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