git-svn-id: svn://db.shs.com.ru/libs@43 a8b55f48-bf90-11e4-a774-851b48703e85
This commit is contained in:
424
qglview/loader_dae.cpp
Normal file
424
qglview/loader_dae.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "loader_dae.h"
|
||||
#include <QDomDocument>
|
||||
#include <QUrl>
|
||||
|
||||
|
||||
Material LoaderDAE::materialByName(const QVector<Material> & 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<Material> LoaderDAE::readMaterials(QDomElement le, QDomElement li, bool fbx) {
|
||||
QVector<Material> 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<QPair<QPair<QString, QString>, 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<QPair<QString, QString>, QMatrix4x4>(QPair<QString, QString>(gid, last_name), cm);
|
||||
continue;
|
||||
}
|
||||
//qDebug() << name << m;
|
||||
}
|
||||
ret << QPair<QPair<QString, QString>, QMatrix4x4>(QPair<QString, QString>("", 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<Material> materials = LoaderDAE::readMaterials(maine.firstChildElement("library_effects"),
|
||||
maine.firstChildElement("library_images"), fbx);
|
||||
GLObjectBase * root = new GLObjectBase(), * co = 0;
|
||||
QMap<QString, QVector<GLObjectBase * > > objects;
|
||||
|
||||
QMap<QString, QString> 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<QString, QString> source_names;
|
||||
QMap<QString, QVector<Vector3d> > 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<Vector3d> & 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<GLObjectBase * > ol;
|
||||
for (int j = 0; j < etr.count(); ++j) {
|
||||
QDomElement ds = etr.at(j).toElement();
|
||||
QString matname = mat_names[ds.attribute("material")];
|
||||
QVector<int> 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<GLfloat> & 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<GLfloat> * 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<Vector3d> & 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<GLObjectBase*> ol;
|
||||
ol << lo;
|
||||
objects[gid] = ol;
|
||||
//qDebug() << "light" << name;
|
||||
}
|
||||
//qDebug() << "readed" << objects.size();
|
||||
|
||||
QVector<QPair<QPair<QString, QString>, QMatrix4x4> > scene;
|
||||
readScene(mvse, QMatrix4x4(), scene);
|
||||
for (int i = 0; i < scene.size(); ++i) {
|
||||
QPair<QPair<QString, QString>, QMatrix4x4> so = scene[i];
|
||||
if (so.first.first.isEmpty()) continue;
|
||||
QVector<GLObjectBase * > 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<QPair<QString, QString>, 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<QVector<GLObjectBase * > > 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;
|
||||
}
|
||||
36
qglview/loader_dae.h
Normal file
36
qglview/loader_dae.h
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LOADER_DAE_H
|
||||
#define LOADER_DAE_H
|
||||
|
||||
#include "gltexture_manager.h"
|
||||
#include "globject.h"
|
||||
#include <QGLWidget>
|
||||
#include <QFileInfo>
|
||||
#include <QDateTime>
|
||||
#include <QDomElement>
|
||||
|
||||
namespace LoaderDAE {
|
||||
Material materialByName(const QVector<Material> & materials, const QString & name);
|
||||
QVector<Material> readMaterials(QDomElement le, QDomElement li, bool fbx);
|
||||
}
|
||||
|
||||
GLObjectBase * loadFromDAEFile(const QString & filepath, double scale = 1.0);
|
||||
|
||||
#endif // LOADER_DAE_H
|
||||
270
qglview/loader_obj.cpp
Normal file
270
qglview/loader_obj.cpp
Normal file
@@ -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 <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;
|
||||
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<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.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<Material> & 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<Vector3d> vertices, normals, texcoords;
|
||||
QVector<Material> 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;
|
||||
}
|
||||
43
qglview/loader_obj.h
Normal file
43
qglview/loader_obj.h
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LOADER_OBJ_H
|
||||
#define LOADER_OBJ_H
|
||||
|
||||
#include "gltexture_manager.h"
|
||||
#include "globject.h"
|
||||
#include <QGLWidget>
|
||||
#include <QFileInfo>
|
||||
#include <QDateTime>
|
||||
|
||||
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<Vector3d> & vertices, const QVector<Vector3d> & normals, const QVector<Vector3d> & texcoords);
|
||||
Material materialByName(const QVector<Material> & materials, const QString & name);
|
||||
}
|
||||
|
||||
GLObjectBase * loadFromOBJFile(const QString & filepath, double scale = 1.0);
|
||||
|
||||
#endif // LOADER_3DS_H
|
||||
Reference in New Issue
Block a user