diff --git a/qglview/loader_dae.cpp b/qglview/loader_dae.cpp
new file mode 100644
index 0000000..8e73a32
--- /dev/null
+++ b/qglview/loader_dae.cpp
@@ -0,0 +1,424 @@
+/*
+ QGLView
+ Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com
+
+ 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_dae.h"
+#include
+#include
+
+
+Material LoaderDAE::materialByName(const QVector & materials, const QString & name) {
+ foreach (const Material & m, materials) {
+ //qDebug() << m.name << " ??? " << name;
+ if (m.name == name)
+ return m;
+ }
+ return Material();
+}
+
+
+QColor readXMLColor(QDomElement n) {
+ QStringList sl(n.firstChildElement("color").firstChild().nodeValue().trimmed().split(" "));
+ sl.removeAll("");
+ sl.removeAll(" ");
+ if (sl.size() >= 3)
+ return QColor::fromRgbF(sl[0].toDouble(), sl[1].toDouble(), sl[2].toDouble(), sl.size() >= 4 ? sl[3].toDouble() : 1.);
+ return QColor();
+}
+
+
+QVector4D readXMLVector(QDomElement n) {
+ QStringList sl(n.firstChild().nodeValue().trimmed().split(" "));
+ sl.removeAll("");
+ sl.removeAll(" ");
+ if (sl.size() == 3) return QVector4D(sl[0].toDouble(), sl[1].toDouble(), sl[2].toDouble(), 0.);
+ if (sl.size() == 4) return QVector4D(sl[0].toDouble(), sl[1].toDouble(), sl[2].toDouble(), sl[3].toDouble());
+ return QVector4D();
+}
+
+
+QMatrix4x4 readXMLMatrix(QDomElement n) {
+ QStringList sl(n.firstChild().nodeValue().trimmed().split(" "));
+ sl.removeAll("");
+ sl.removeAll(" ");
+ if (sl.size() != 16) return QMatrix4x4();
+ QMatrix4x4 m;
+ int ind = -1;
+ for (int r = 0; r < 4; ++r)
+ for (int c = 0; c < 4; ++c)
+ m(r, c) = sl[++ind].toDouble();
+ return m;
+}
+
+
+double readXMLFloat(QDomElement n) {
+ return n.firstChildElement("float").firstChild().nodeValue().toDouble();
+}
+
+
+QString readXMLTexture(QDomElement n, QDomElement prof, QDomElement li) {
+ QString tex = n.firstChildElement("texture").attribute("texture");
+ if (tex.isEmpty()) return QString();
+ QString tag;
+ QDomNodeList elist = prof.elementsByTagName("newparam");
+ if (elist.isEmpty()) {
+ tag = tex;
+ } else {
+ bool found = false;
+ int cnt = 0;
+ while (!tex.isEmpty() && !found && cnt < 10) {
+ found = false;
+ cnt++;
+ for (int i = 0; i < elist.count(); ++i) {
+ QDomNode dn = elist.at(i);
+ if (dn.attributes().namedItem("sid").nodeValue() == tex) {
+ //qDebug() << "found!";
+ if (dn.firstChild().nodeName() == "sampler2D") {
+ tex = dn.firstChildElement("sampler2D").firstChildElement("source").firstChild().nodeValue();
+ break;
+ }
+ if (dn.firstChild().nodeName() == "surface") {
+ tag = dn.firstChildElement("surface").firstChildElement("init_from").firstChild().nodeValue();
+ //qDebug() << tex << "->" << tag;
+ tex.clear();
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ if (cnt == 10) return QString();
+ }
+ //qDebug() << tag;
+ if (tag.isEmpty()) return QString();
+ elist = li.elementsByTagName("image");
+ for (int i = 0; i < elist.count(); ++i) {
+ QDomElement dn = elist.at(i).toElement();
+ if (dn.attribute("id") == tag) {
+ tex = dn.firstChildElement("init_from").firstChild().nodeValue();
+ tex.replace("\\", "/");
+ if (tex.startsWith("file:") && tex.mid(5, 3) != "///") tex.insert(6, "/");
+ //qDebug() << "found" << tex << QUrl(tex).toLocalFile();
+ tex = QUrl(tex).toLocalFile();
+ if (tex == "/") tex.clear();
+ return tex;
+ }
+ }
+ return QString();
+}
+
+
+QVector LoaderDAE::readMaterials(QDomElement le, QDomElement li, bool fbx) {
+ QVector ret;
+ QDomNodeList elist = le.elementsByTagName("effect");
+ for (int i = 0; i < elist.count(); ++i) {
+ QDomNode dn = elist.at(i);
+ Material mat;
+ mat.name = dn.attributes().namedItem("id").nodeValue();
+ QDomElement prof = dn.firstChildElement("profile_COMMON");
+ QDomNode pn = prof.firstChildElement("technique").firstChild();
+ QColor col;
+ QString text;
+
+ col = readXMLColor(pn.firstChildElement("emission"));
+ if (col.isValid()) mat.color_self_illumination = col;
+ col = readXMLColor(pn.firstChildElement("diffuse"));
+ if (col.isValid()) mat.color_diffuse = col;
+ col = readXMLColor(pn.firstChildElement("specular"));
+ if (col.isValid()) mat.color_specular = col;
+ mat.shine = 2. / exp(readXMLFloat(pn.firstChildElement("shininess")));
+ mat.transparency = readXMLFloat(pn.firstChildElement("transparency"));
+ if (!fbx) mat.transparency = 1. - mat.transparency;
+ text = readXMLTexture(pn.firstChildElement("diffuse"), prof, li);
+ if (!text.isEmpty()) mat.diffuse.bitmap_path = text;
+ text = readXMLTexture(pn.firstChildElement("diffuse"), prof, li);
+ if (!text.isEmpty()) mat.diffuse.bitmap_path = text;
+
+ pn = prof.firstChildElement("technique").firstChildElement("extra").firstChild();
+ text = readXMLTexture(pn.firstChildElement("bump"), prof, li);
+ if (!text.isEmpty()) mat.bump.bitmap_path = text;
+
+ ret << mat;
+ /*
+ qDebug() << "** Material" << mat.name;
+ qDebug() << " emission" << mat.color_self_illumination;
+ qDebug() << " diffuse" << mat.color_diffuse;
+ qDebug() << " diffuse" << mat.diffuse.bitmap_path;
+ qDebug() << " specular" << mat.color_specular;
+ qDebug() << " bump" << mat.bump.bitmap_path;
+ qDebug() << " transparency" << mat.transparency;
+ */
+ }
+ return ret;
+}
+
+
+QMatrix4x4 readXMLTransformations(QDomElement n) {
+ QMatrix4x4 tm;
+ QDomNodeList trl = n.childNodes();
+ for (int i = 0; i < trl.count(); ++i) {
+ QDomElement dt = trl.at(i).toElement();
+ }
+ return tm;
+}
+
+
+void readScene(QDomElement n, QMatrix4x4 cm, QVector, QMatrix4x4> > & ret, QString last_name = QString()) {
+ QDomNodeList evsl = n.childNodes();
+ if (n.hasAttribute("name")) last_name = n.attribute("name");
+ for (int i = 0; i < evsl.count(); ++i) {
+ QDomElement dt = evsl.at(i).toElement();
+ QVector4D v;
+//qDebug() << dt.nodeName();
+ if (dt.nodeName() == "translate") {
+ v = readXMLVector(dt);
+ cm.translate(v.toVector3D());
+ continue;
+ }
+ if (dt.nodeName() == "rotate") {
+ v = readXMLVector(dt);
+ cm.rotate(v.w(), v.toVector3D());
+ continue;
+ }
+ if (dt.nodeName() == "scale") {
+ v = readXMLVector(dt);
+ cm.scale(v.toVector3D());
+ continue;
+ }
+ if (dt.nodeName() == "matrix") {
+ QMatrix4x4 m = readXMLMatrix(dt);
+ cm *= m;
+ continue;
+ }
+ if (dt.nodeName() == "node") {
+ readScene(dt, cm, ret, last_name);
+ continue;
+ }
+ if (dt.nodeName() == "instance_geometry" || dt.nodeName() == "instance_light") {
+ QString gid = dt.attribute("url");
+ if (gid.startsWith("#")) gid.remove(0, 1);
+//qDebug() << "matrix" << gid << cm;
+ ret << QPair, QMatrix4x4>(QPair(gid, last_name), cm);
+ continue;
+ }
+ //qDebug() << name << m;
+ }
+ ret << QPair, QMatrix4x4>(QPair("", last_name), cm);
+}
+
+
+GLObjectBase * loadFromDAEFile(const QString & filepath, double scale) {
+ QFile f(filepath);
+ if (!f.exists()) {
+ qDebug() << "[Loader DAE] Error: can`t open \"" + filepath + "\"";
+ return 0;
+ }
+ QTime tm;
+ tm.restart();
+ QDomDocument dom(filepath);
+ if (!dom.setContent(&f)) {
+ qDebug() << "[Loader DAE] Error: can`t parse \"" + filepath + "\"";
+ return 0;
+ }
+ //qDebug() << "parse" << tm.elapsed();
+ tm.restart();
+ QDomElement maine = dom.firstChildElement("COLLADA");
+ bool fbx = maine.firstChildElement("asset").firstChildElement("contributor").firstChildElement("authoring_tool").firstChild().nodeValue().startsWith("FBX");
+ QVector materials = LoaderDAE::readMaterials(maine.firstChildElement("library_effects"),
+ maine.firstChildElement("library_images"), fbx);
+ GLObjectBase * root = new GLObjectBase(), * co = 0;
+ QMap > objects;
+
+ QMap mat_names;
+ QDomElement mvse = maine.firstChildElement("library_visual_scenes").firstChildElement("visual_scene");
+ QDomNodeList evsl = mvse.elementsByTagName("instance_material");
+ QDomNodeList matl = maine.firstChildElement("library_materials").elementsByTagName("material");
+ for (int i = 0; i < evsl.count(); ++i) {
+ QDomElement dn = evsl.at(i).toElement();
+ QString tn = dn.attribute("target");
+ if (tn.startsWith("#")) tn.remove(0, 1);
+ for (int j = 0; j < matl.count(); ++j) {
+ QDomElement dm = matl.at(j).toElement();
+ if (dm.attribute("id") == tn) {
+ QString en = dm.firstChildElement("instance_effect").attribute("url");
+ if (en.startsWith("#")) en.remove(0, 1);
+ mat_names[dn.attribute("symbol")] = en;
+ //qDebug() << dn.attribute("symbol") << "->" << en;
+ }
+ }
+ }
+
+ QDomNodeList elist = maine.firstChildElement("library_geometries").elementsByTagName("geometry");
+ for (int i = 0; i < elist.count(); ++i) {
+ QDomNode dn = elist.at(i);
+ QString name = dn.attributes().namedItem("name").nodeValue();
+ QString gid = dn.attributes().namedItem("id").nodeValue();
+ if (name.isEmpty()) continue;
+ dn = dn.firstChildElement("mesh");
+ QMap source_names;
+ QMap > source_data;
+ QDomNodeList esrc = dn.toElement().elementsByTagName("source");
+ for (int j = 0; j < esrc.count(); ++j) {
+ QDomNode ds = esrc.at(j);
+ QString id = ds.attributes().namedItem("id").nodeValue();
+ QDomNodeList evert = dn.toElement().elementsByTagName("vertices");
+ for (int k = 0; k < evert.count(); ++k) {
+ QDomNode dv = evert.at(k);
+ QString vid = dv.attributes().namedItem("id").nodeValue();
+ if (dv.firstChildElement("input").attribute("source") == ("#" + id))
+ source_names[vid] = id;
+//qDebug() << " found source sin" << vid;
+ }
+ QVector & sd(source_data[id]);
+ int stride = ds.firstChildElement("technique_common").firstChildElement("accessor").attribute("stride").toInt();
+ QString astr = ds.firstChildElement("float_array").firstChild().nodeValue().trimmed();
+ astr.replace("\n", " ");
+ astr.remove("\r");
+ QStringList sl = astr.split(" ");
+ sl.removeAll("");
+ sl.removeAll(" ");
+ for (int c = 0; c < sl.size(); c += stride) {
+ Vector3d v;
+ if (stride >= 1) v.x = sl[c].toDouble();
+ if (stride >= 2) v.y = sl[c + 1].toDouble();
+ if (stride >= 3) v.z = sl[c + 2].toDouble();
+ sd << v;
+ }
+//qDebug() << " found source" << id << "stride =" << stride << ":" << sd;
+//qDebug() << " readed" << sd.size();
+ }
+ QDomNodeList etr = dn.toElement().elementsByTagName("triangles");
+ //QMatrix4x4 m = matrices.value(gid);
+//qDebug() << "found geom" << name;
+ QVector ol;
+ for (int j = 0; j < etr.count(); ++j) {
+ QDomElement ds = etr.at(j).toElement();
+ QString matname = mat_names[ds.attribute("material")];
+ QVector p;
+ QStringList psl = ds.firstChildElement("p").firstChild().nodeValue().trimmed().split(" ");
+ foreach (const QString & s, psl)
+ p << s.toInt();
+ QDomNodeList einp = ds.elementsByTagName("input");
+ int pbv = einp.count();//, tc = qMin(ds.attribute("count").toInt(), p.size() / pbv);
+ co = new GLObjectBase();
+ co->setName(name + "_" + QString::number(j));
+ //co->setTransform(m);
+ co->material() = LoaderDAE::materialByName(materials, matname);
+//qDebug() << " tri" << co->material().color_diffuse << matname;
+ QVector & vertices(co->VBO().vertices()), & normals(co->VBO().normals()), & uvs(co->VBO().texcoords());
+ for (int k = 0; k < einp.count(); ++k) {
+ QDomElement di = einp.at(k).toElement();
+ QString src = di.attribute("source"), sem = di.attribute("semantic").toLower();
+ int offset = di.attribute("offset").toInt();
+ QVector * curv = 0;
+ int pccnt = 0;
+ if (sem == "vertex") {curv = &vertices; pccnt = 3;}
+ if (sem == "normal") {curv = &normals; pccnt = 3;}
+ if (sem == "texcoord") {curv = &uvs; pccnt = 2;}
+ if (curv == 0) continue;
+ if (src.startsWith("#")) src.remove(0, 1);
+ QVector & data(source_data[source_names.value(src, src)]);
+ for (int ii = offset; ii < p.size(); ii += pbv) {
+ if ((p[ii] >= 0) && (p[ii] < data.size())) {
+ Vector3d v = data[p[ii]];
+ (*curv) << v.x << v.y;
+ if (pccnt == 3) (*curv) << v.z;
+ }
+ }
+ //qDebug() << " input" << sem << "from" << data.size() << "->" << (*curv) << pbv;
+ }
+ //qDebug() << "geom" << gid << co;
+ ol << co;
+ }
+ objects[gid] = ol;
+ }
+
+ elist = maine.firstChildElement("library_lights").elementsByTagName("light");
+ for (int i = 0; i < elist.count(); ++i) {
+ QDomElement dn = elist.at(i).toElement();
+ QString name = dn.attributes().namedItem("name").nodeValue();
+ QString gid = dn.attributes().namedItem("id").nodeValue();
+ if (name.isEmpty() || name == "EnvironmentAmbientLight") continue;
+ QDomElement dl = dn.firstChildElement("technique_common").firstChild().toElement();
+ Light * lo = new Light();
+ if (dl.nodeName() == "point") lo->light_type = Light::Omni;
+ else if (dl.nodeName() == "spot") lo->light_type = Light::Cone;
+ else {
+ delete lo;
+ continue;
+ }
+ lo->setColor(readXMLColor(dl));
+ QDomNodeList ml = dn.elementsByTagName("multiplier");
+ if (!ml.isEmpty()) lo->intensity = ml.at(0).firstChild().nodeValue().toDouble();
+ ml = dn.elementsByTagName("intensity");
+ if (!ml.isEmpty()) lo->intensity = ml.at(0).firstChild().nodeValue().toDouble();
+ QString sv;
+ sv = dl.firstChildElement("constant_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_const = sv.toDouble();
+ sv = dl.firstChildElement("linear_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_linear = sv.toDouble();
+ sv = dl.firstChildElement("quadratic_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_quadratic = sv.toDouble();
+ ///lo->setTransform(matrices.value(name));
+ QVector ol;
+ ol << lo;
+ objects[gid] = ol;
+ //qDebug() << "light" << name;
+ }
+ //qDebug() << "readed" << objects.size();
+
+ QVector, QMatrix4x4> > scene;
+ readScene(mvse, QMatrix4x4(), scene);
+ for (int i = 0; i < scene.size(); ++i) {
+ QPair, QMatrix4x4> so = scene[i];
+ if (so.first.first.isEmpty()) continue;
+ QVector ol = objects.value(so.first.first);
+ foreach (GLObjectBase * o, ol) {
+ o = o->clone();
+ o->setName(so.first.second);
+ o->setTransform(so.second);
+ root->addChild(o);
+ //qDebug() << " add" << so.first.second << o->name();
+ }
+ //qDebug() << "add" << so.first << ol.size();
+ }
+
+ for (int i = 0; i < root->childCount(); ++i) {
+ GLObjectBase * o = root->child(i);
+ if (o->type() == GLObjectBase::Light) {
+ Light * l = (Light*)o;
+ if (l->light_type == Light::Directional || l->light_type == Light::Cone) {
+ QString tn = l->name() + ".Target";
+ //qDebug() << "search target" << tn;
+ for (int s = 0; s < scene.size(); ++s) {
+ QPair, QMatrix4x4> so = scene[s];
+ if (so.first.second == tn) {
+ //qDebug() << "found target" << tn;
+ QVector3D tp = so.second.column(3).toVector3D();
+ l->direction = (tp - l->pos()).normalized();
+ //qDebug() << "dir" << l->direction;
+ }
+ }
+ }
+ }
+ }
+
+ QList > dol = objects.values();
+ for (int i = 0; i < dol.size(); ++i)
+ for (int j = 0; j < dol[i].size(); ++j)
+ delete dol[i][j];
+
+ qDebug() << "[Loader DAE] Loaded" << root->childCount() << "objects from" << filepath;
+ return root;
+}
diff --git a/qglview/loader_dae.h b/qglview/loader_dae.h
new file mode 100644
index 0000000..7b25db1
--- /dev/null
+++ b/qglview/loader_dae.h
@@ -0,0 +1,36 @@
+/*
+ QGLView
+ Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com
+
+ 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 .
+*/
+
+#ifndef LOADER_DAE_H
+#define LOADER_DAE_H
+
+#include "gltexture_manager.h"
+#include "globject.h"
+#include
+#include
+#include
+#include
+
+namespace LoaderDAE {
+ Material materialByName(const QVector & materials, const QString & name);
+ QVector readMaterials(QDomElement le, QDomElement li, bool fbx);
+}
+
+GLObjectBase * loadFromDAEFile(const QString & filepath, double scale = 1.0);
+
+#endif // LOADER_DAE_H
diff --git a/qglview/loader_obj.cpp b/qglview/loader_obj.cpp
new file mode 100644
index 0000000..2196966
--- /dev/null
+++ b/qglview/loader_obj.cpp
@@ -0,0 +1,270 @@
+/*
+ QGLView
+ Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com
+
+ 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;
+ uint fcnt = faces.size() * 3;
+ vertices.resize(fcnt * 3);
+ normals.resize(vertices.size());
+ if (has_uv) uvs.resize(fcnt * 2);
+ uint 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].toDouble();
+ if (sl.size() > 1) ret.y = sl[1].toDouble();
+ if (sl.size() > 2) ret.z = sl[2].toDouble();
+ 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].toDouble();
+ if (sl.size() > 1) ret.y = sl[1].toDouble();
+ 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.shine_strength = v.length();
+ double mc = qMax(v.x, qMax(v.y, v.z));
+ if (mc > 0.) 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.shine = line.mid(2).trimmed().toDouble();
+ continue;
+ }
+ if (line.startsWith("d")) {
+ mat.transparency = 1. - line.mid(1).trimmed().toDouble();
+ continue;
+ }
+ if (line.startsWith("map_Kd")) {
+ mat.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.bump_scale = sv.toDouble();
+ }
+ mat.bump.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, double scale) {
+ QFile f(filepath);
+ if (!f.exists()) {
+ qDebug() << "[Loader OBJ] Error: can`t open \"" + filepath + "\"";
+ return 0;
+ }
+ f.open(QIODevice::ReadOnly);
+ QTextStream stream(&f);
+ QVector vertices, normals, texcoords;
+ QVector materials;
+ GLObjectBase * root = new GLObjectBase(), * co = 0;
+ QString name;
+ root->setName(QFileInfo(f).baseName());
+ int cnt = 0;
+ while (!stream.atEnd()) {
+ QString 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 ")) {
+ name = line.mid(1).trimmed();
+ if (co != 0) {
+ 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 != 0) {
+ LoaderOBJ::initOBJMesh(co, vertices, normals, texcoords);
+ root->addChild(co);
+ }
+ qDebug() << "[Loader OBJ] Loaded" << root->childCount() << "objects from" << filepath;
+ return root;
+}
diff --git a/qglview/loader_obj.h b/qglview/loader_obj.h
new file mode 100644
index 0000000..77a5927
--- /dev/null
+++ b/qglview/loader_obj.h
@@ -0,0 +1,43 @@
+/*
+ QGLView
+ Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com
+
+ 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 .
+*/
+
+#ifndef LOADER_OBJ_H
+#define LOADER_OBJ_H
+
+#include "gltexture_manager.h"
+#include "globject.h"
+#include
+#include
+#include
+
+namespace LoaderOBJ {
+#pragma pack(push, 1)
+ struct Face {
+ ushort v0;
+ ushort v1;
+ ushort v2;
+ ushort flags;
+ };
+#pragma pack(pop)
+ void initOBJMesh(GLObjectBase * o, const QVector & vertices, const QVector & normals, const QVector & texcoords);
+ Material materialByName(const QVector & materials, const QString & name);
+}
+
+GLObjectBase * loadFromOBJFile(const QString & filepath, double scale = 1.0);
+
+#endif // LOADER_3DS_H