273 lines
8.4 KiB
C++
273 lines
8.4 KiB
C++
/*
|
|
QGLView
|
|
Ivan Pelipenko peri4ko@yandex.ru
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "loader_obj.h"
|
|
|
|
|
|
void LoaderOBJ::initOBJMesh(GLObjectBase * o, const QVector<Vector3d> & src_vertices, const QVector<Vector3d> & src_normals, const QVector<Vector3d> & src_texcoords) {
|
|
QVector<GLfloat> & vertices(o->VBO().vertices()), & normals(o->VBO().normals()), & uvs(o->VBO().texcoords());
|
|
QVector<Vector3i> & faces(o->faces), & uvws(o->uvws), & norms(o->norms);
|
|
//Vector3d pos = Vector3d(o->pos());
|
|
bool has_uv = !uvws.isEmpty();
|
|
Vector3i cf, ct, cn;
|
|
Vector3d v[3], t[3], n[3];
|
|
//for (int i = 0; i < points.size(); ++i)
|
|
// points[i] -= pos;
|
|
int fcnt = faces.size() * 3;
|
|
vertices.resize(fcnt * 3);
|
|
normals.resize(vertices.size());
|
|
if (has_uv) uvs.resize(fcnt * 2);
|
|
int ind = 0, induv = 0;
|
|
//qDebug() << "initOBJMesh" << faces.size();
|
|
for (int i = 0; i < faces.size(); ++i) {
|
|
cf = faces[i];
|
|
ct = uvws[i];
|
|
cn = norms[i];
|
|
v[0] = src_vertices[cf.p0];
|
|
v[1] = src_vertices[cf.p1];
|
|
v[2] = src_vertices[cf.p2];
|
|
n[0] = src_normals[cn.p0];
|
|
n[1] = src_normals[cn.p1];
|
|
n[2] = src_normals[cn.p2];
|
|
vertices[ind] = v[0].x; normals[ind] = n[0].x; ++ind;
|
|
vertices[ind] = v[0].y; normals[ind] = n[0].y; ++ind;
|
|
vertices[ind] = v[0].z; normals[ind] = n[0].z; ++ind;
|
|
vertices[ind] = v[1].x; normals[ind] = n[1].x; ++ind;
|
|
vertices[ind] = v[1].y; normals[ind] = n[1].y; ++ind;
|
|
vertices[ind] = v[1].z; normals[ind] = n[1].z; ++ind;
|
|
vertices[ind] = v[2].x; normals[ind] = n[2].x; ++ind;
|
|
vertices[ind] = v[2].y; normals[ind] = n[2].y; ++ind;
|
|
vertices[ind] = v[2].z; normals[ind] = n[2].z; ++ind;
|
|
if (has_uv) {
|
|
if ((ct.p0 >= 0) && (ct.p1 >= 0) && (ct.p2 >= 0)) {
|
|
t[0] = src_texcoords[ct.p0];
|
|
t[1] = src_texcoords[ct.p1];
|
|
t[2] = src_texcoords[ct.p2];
|
|
uvs[induv] = t[0].x; ++induv;
|
|
uvs[induv] = t[0].y; ++induv;
|
|
uvs[induv] = t[1].x; ++induv;
|
|
uvs[induv] = t[1].y; ++induv;
|
|
uvs[induv] = t[2].x; ++induv;
|
|
uvs[induv] = t[2].y; ++induv;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Vector3d readVector3d(QString s) {
|
|
Vector3d ret;
|
|
QStringList sl(s.trimmed().split(" "));
|
|
sl.removeAll(""); sl.removeAll(" ");
|
|
if (sl.size() > 0) ret.x = sl[0].toFloat();
|
|
if (sl.size() > 1) ret.y = sl[1].toFloat();
|
|
if (sl.size() > 2) ret.z = sl[2].toFloat();
|
|
return ret;
|
|
}
|
|
|
|
|
|
Vector2d readVector2d(QString s) {
|
|
Vector2d ret;
|
|
QStringList sl(s.trimmed().split(" "));
|
|
sl.removeAll(""); sl.removeAll(" ");
|
|
if (sl.size() > 0) ret.x = sl[0].toFloat();
|
|
if (sl.size() > 1) ret.y = sl[1].toFloat();
|
|
return ret;
|
|
}
|
|
|
|
|
|
QColor readColor(QString s) {
|
|
Vector3d c = readVector3d(s);
|
|
return QColor::fromRgbF(c.x, c.y, c.z);
|
|
}
|
|
|
|
|
|
void readFaces(QString s, GLObjectBase * co) {
|
|
QStringList sl(s.trimmed().split(" "));
|
|
sl.removeAll(""); sl.removeAll(" ");
|
|
static Vector3i inds[4];
|
|
for (int i = 0; i < sl.size(); ++i) {
|
|
inds[i].p0 = inds[i].p1 = inds[i].p2 = 0;
|
|
QStringList sl2(sl[i].split("/"));
|
|
if (sl2.size() > 4) continue;
|
|
inds[i].p0 = sl2[0].toInt();
|
|
inds[i].p1 = sl2[1].toInt();
|
|
inds[i].p2 = sl2[2].toInt();
|
|
}
|
|
if (sl.size() == 3) {
|
|
co->faces << Vector3i(inds[0].p0 - 1, inds[1].p0 - 1, inds[2].p0 - 1);
|
|
co->uvws << Vector3i(inds[0].p1 - 1, inds[1].p1 - 1, inds[2].p1 - 1);
|
|
co->norms << Vector3i(inds[0].p2 - 1, inds[1].p2 - 1, inds[2].p2 - 1);
|
|
}
|
|
if (sl.size() == 4) {
|
|
co->faces << Vector3i(inds[0].p0 - 1, inds[1].p0 - 1, inds[2].p0 - 1);
|
|
co->uvws << Vector3i(inds[0].p1 - 1, inds[1].p1 - 1, inds[2].p1 - 1);
|
|
co->norms << Vector3i(inds[0].p2 - 1, inds[1].p2 - 1, inds[2].p2 - 1);
|
|
co->faces << Vector3i(inds[0].p0 - 1, inds[2].p0 - 1, inds[3].p0 - 1);
|
|
co->uvws << Vector3i(inds[0].p1 - 1, inds[2].p1 - 1, inds[3].p1 - 1);
|
|
co->norms << Vector3i(inds[0].p2 - 1, inds[2].p2 - 1, inds[3].p2 - 1);
|
|
}
|
|
}
|
|
|
|
|
|
QVector<Material> readMTL(QString obj_path, QString path) {
|
|
QVector<Material> ret;
|
|
QStringList sp = GLTextureManagerBase::searchPathes();
|
|
sp.prepend(QFileInfo(obj_path).absoluteDir().path());
|
|
QFile f(findFile(path, sp));
|
|
if (!f.open(QIODevice::ReadOnly)) {
|
|
qDebug() << "[Loader OBJ] Warning: can`t open \"" + path + "\"";
|
|
return ret;
|
|
}
|
|
QTextStream stream(&f);
|
|
QString name;
|
|
Material mat;
|
|
while (!stream.atEnd()) {
|
|
QString line = stream.readLine().trimmed();
|
|
if (line.startsWith("newmtl")) {
|
|
if (!mat.name.isEmpty())
|
|
ret << mat;
|
|
mat = Material();
|
|
mat.name = line.mid(6).trimmed();
|
|
continue;
|
|
}
|
|
if (line.startsWith("Kd")) {
|
|
mat.color_diffuse = readColor(line.mid(2).trimmed());
|
|
continue;
|
|
}
|
|
if (line.startsWith("Ke")) {
|
|
mat.color_self_illumination = readColor(line.mid(2).trimmed());
|
|
continue;
|
|
}
|
|
if (line.startsWith("Ks")) {
|
|
Vector3d v = readVector3d(line.mid(2).trimmed());
|
|
mat.map_specular.color_amount = v.length();
|
|
float mc = qMax(v.x, qMax(v.y, v.z));
|
|
if (mc > 0.f) v /= mc;
|
|
mat.color_specular = QColor::fromRgbF(v.x, v.y, v.z);
|
|
//qDebug() << mat.shine_strength << mat.color_specular;
|
|
continue;
|
|
}
|
|
if (line.startsWith("Ns")) {
|
|
mat.map_specularity.color_amount = 2.f / expf(line.mid(2).trimmed().toFloat());
|
|
continue;
|
|
}
|
|
if (line.startsWith("d")) {
|
|
mat.transparency = 1.f - line.mid(1).trimmed().toFloat();
|
|
continue;
|
|
}
|
|
if (line.startsWith("map_Kd")) {
|
|
mat.map_diffuse.bitmap_path = findFile(line.mid(6).trimmed(), sp);
|
|
continue;
|
|
}
|
|
if (line.startsWith("map_bump")) {
|
|
line = line.mid(8).trimmed();
|
|
if (line.startsWith("-bm")) {
|
|
line = line.mid(3).trimmed();
|
|
QString sv = line.left(line.indexOf(" "));
|
|
line = line.mid(sv.size()).trimmed();
|
|
mat.map_normal.color_amount = sv.toFloat();
|
|
}
|
|
mat.map_normal.bitmap_path = findFile(line, sp);
|
|
//qDebug() << "BUMP" << mat.name << mat.bump_scale << mat.bump.bitmap_path;
|
|
continue;
|
|
}
|
|
}
|
|
if (!mat.name.isEmpty())
|
|
ret << mat;
|
|
qDebug() << "load from MTL" << f.fileName() << ret.size() << "materials";
|
|
return ret;
|
|
}
|
|
|
|
|
|
Material LoaderOBJ::materialByName(const QVector<Material> & materials, const QString & name) {
|
|
foreach (const Material & m, materials)
|
|
if (m.name == name)
|
|
return m;
|
|
return Material();
|
|
}
|
|
|
|
|
|
GLObjectBase * loadFromOBJFile(const QString & filepath, float scale) {
|
|
QFile f(filepath);
|
|
if (!f.exists()) {
|
|
qDebug() << "[Loader OBJ] Error: can`t open \"" + filepath + "\"";
|
|
return nullptr;
|
|
}
|
|
f.open(QIODevice::ReadOnly);
|
|
QTextStream stream(&f);
|
|
QVector<Vector3d> vertices, normals, texcoords;
|
|
QVector<Material> materials;
|
|
GLObjectBase * root = new GLObjectBase(), * co = nullptr;
|
|
QString name, line, pline;
|
|
root->setName(QFileInfo(f).baseName());
|
|
int cnt = 0;
|
|
while (!stream.atEnd()) {
|
|
pline = line;
|
|
line = stream.readLine().trimmed();
|
|
if (line.startsWith("mtllib")) {
|
|
materials = readMTL(filepath, line.mid(6).trimmed());
|
|
continue;
|
|
}
|
|
if (line.startsWith("v ")) {
|
|
vertices << readVector3d(line.mid(1));
|
|
continue;
|
|
}
|
|
if (line.startsWith("vt ")) {
|
|
texcoords << readVector3d(line.mid(2));
|
|
continue;
|
|
}
|
|
if (line.startsWith("vn ")) {
|
|
normals << readVector3d(line.mid(2));
|
|
continue;
|
|
}
|
|
if (line.startsWith("g ") || line.startsWith("o ")) {
|
|
if (pline.mid(1) == line.mid(1)) continue;
|
|
name = line.mid(1).trimmed();
|
|
if (co != nullptr) {
|
|
LoaderOBJ::initOBJMesh(co, vertices, normals, texcoords);
|
|
root->addChild(co);
|
|
}
|
|
co = new GLObjectBase();
|
|
co->setName(name);
|
|
//qDebug() << "new object" << co->name();
|
|
continue;
|
|
}
|
|
if (line.startsWith("f ")) {
|
|
readFaces(line.mid(2), co);
|
|
continue;
|
|
}
|
|
if (line.startsWith("usemtl")) {
|
|
if (!co->faces.isEmpty()) {
|
|
LoaderOBJ::initOBJMesh(co, vertices, normals, texcoords);
|
|
root->addChild(co);
|
|
co = new GLObjectBase();
|
|
co->setName(name + "_" + QString::number(cnt++));
|
|
}
|
|
co->material() = LoaderOBJ::materialByName(materials, line.mid(6).trimmed());
|
|
continue;
|
|
}
|
|
}
|
|
if (co != nullptr) {
|
|
LoaderOBJ::initOBJMesh(co, vertices, normals, texcoords);
|
|
root->addChild(co);
|
|
}
|
|
qDebug() << "[Loader OBJ] Loaded" << root->childCount() << "objects from" << filepath;
|
|
return root;
|
|
}
|