/* QGLView Copyright (C) 2020 Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "loader_obj.h" void LoaderOBJ::initOBJMesh(GLObjectBase * o, const QVector & src_vertices, const QVector & src_normals, const QVector & src_texcoords) { QVector & vertices(o->VBO().vertices()), & normals(o->VBO().normals()), & uvs(o->VBO().texcoords()); QVector & 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 readMTL(QString obj_path, QString path) { QVector 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 & 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 vertices, normals, texcoords; QVector 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; }