/*
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 .
*/
#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;
}