/* 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_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].toFloat(), sl[1].toFloat(), sl[2].toFloat(), 0.); if (sl.size() == 4) return QVector4D(sl[0].toFloat(), sl[1].toFloat(), sl[2].toFloat(), sl[3].toFloat()); 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].toFloat(); return m; } float readXMLFloat(QDomElement n) { return n.firstChildElement("float").firstChild().nodeValue().toFloat(); } 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.map_specularity.color_amount = 2.f / expf(readXMLFloat(pn.firstChildElement("shininess"))); mat.transparency = readXMLFloat(pn.firstChildElement("transparency")); if (!fbx) mat.transparency = 1.f - mat.transparency; text = readXMLTexture(pn.firstChildElement("diffuse"), prof, li); if (!text.isEmpty()) mat.map_diffuse.bitmap_path = text; text = readXMLTexture(pn.firstChildElement("diffuse"), prof, li); if (!text.isEmpty()) mat.map_diffuse.bitmap_path = text; pn = prof.firstChildElement("technique").firstChildElement("extra").firstChild(); text = readXMLTexture(pn.firstChildElement("bump"), prof, li); if (!text.isEmpty()) mat.map_normal.bitmap_path = text; ret << mat; qDebug() << "** Material" << mat.name; qDebug() << " emission" << mat.color_self_illumination; qDebug() << " diffuse" << mat.color_diffuse; qDebug() << " specular" << mat.color_specular; 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, float scale) { QFile f(filepath); if (!f.exists()) { qDebug() << "[Loader DAE] Error: can`t open \"" + filepath + "\""; return nullptr; } QTime tm; tm.restart(); QDomDocument dom(filepath); if (!dom.setContent(&f)) { qDebug() << "[Loader DAE] Error: can`t parse \"" + filepath + "\""; return nullptr; } //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 = nullptr; 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].toFloat(); if (stride >= 2) v.y = sl[c + 1].toFloat(); if (stride >= 3) v.z = sl[c + 2].toFloat(); 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().name << 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 = nullptr; 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 == nullptr) 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().toFloat(); ml = dn.elementsByTagName("intensity"); if (!ml.isEmpty()) lo->intensity = ml.at(0).firstChild().nodeValue().toFloat(); lo->setColor(lo->color() / lo->intensity); QString sv; sv = dl.firstChildElement("constant_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_const = sv.toFloat(); sv = dl.firstChildElement("linear_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_linear = sv.toFloat(); sv = dl.firstChildElement("quadratic_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_quadratic = sv.toFloat(); ///lo->setTransform(matrices.value(name)); if (lo->light_type == Light::Cone) { ml = dn.elementsByTagName("decay_falloff"); if (!ml.isEmpty()) lo->angle_end = ml.at(0).firstChild().nodeValue().toFloat(); ml = dn.elementsByTagName("hotspot_beam"); if (!ml.isEmpty()) lo->angle_start = ml.at(0).firstChild().nodeValue().toFloat(); } 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::glLight) { 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]; root->setScale(0.001f); qDebug() << "[Loader DAE] Loaded" << root->childCount() << "objects from" << filepath; return root; }