/* QGLView Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru 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_assimp.h" #include "glscene.h" #include "glmesh.h" #include "glmaterial.h" #include "globject.h" #include #include #include #include #include QVector3D fromAiVector3D(const aiVector3D & v) {return QVector3D(v.x, v.y, v.z);} Vector3i fromAiFace (const aiFace & v) {return Vector3i(v.mIndices[0], v.mIndices[1], v.mIndices[2]);} QMatrix4x4 fromAiMatrix4D(const aiMatrix4x4 & v) {return QMatrix4x4(v.a1, v.a2, v.a3, v.a4, v.b1, v.b2, v.b3, v.b4, v.c1, v.c2, v.c3, v.c4, v.d1, v.d2, v.d3, v.d4);} bool isAiMeshTriangles(const aiMesh * m) {return (m->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) == aiPrimitiveType_TRIANGLE;} Mesh * assimpMesh(const aiMesh * m) { if (!m) return 0; if (!isAiMeshTriangles(m)) return 0; Mesh * ret = new Mesh(); int vcnt = m->mNumVertices, tcnt = m->mNumFaces; QVector & v(ret->vertices()); v.resize(vcnt); QVector & t(ret->texcoords()); t.resize(vcnt); QVector< Vector3i> & ind(ret->indicesTriangles()); for (int i = 0; i < vcnt; ++i) v[i] = fromAiVector3D(m->mVertices[i]); if (m->HasNormals()) { QVector & n(ret->normals()); n.resize(vcnt); for (int i = 0; i < vcnt; ++i) n[i] = fromAiVector3D(m->mNormals[i]); } if (m->HasTextureCoords(0)) { for (int i = 0; i < vcnt; ++i) t[i] = fromAiVector3D(m->mTextureCoords[0][i]).toVector2D(); } if (m->HasFaces()) { ind.resize(tcnt); for (int i = 0; i < tcnt; ++i) ind[i] = fromAiFace(m->mFaces[i]); } else { tcnt = vcnt / 3; ind.resize(tcnt); int si = 0; for (int i = 0; i < tcnt; ++i) { si = i+i+i; ind[i] = Vector3i(si, si+1, si+2); } } //qDebug() << "add mesh" << v.size() << ret->normals().size() << ret->texcoords().size() << ret->indices().size(); return ret; } QColor aiMatColor(const aiMaterial * m, const char * key, uint s0, uint s1, const QColor & def = Qt::white) { aiColor4D col; aiReturn r = m->Get(key, s0, s1, col); //qDebug() << key << r << col.r << col.g << col.b; if (r != aiReturn_SUCCESS) return def; return QColor::fromRgbF(col.r, col.g, col.b); } float aiMatFloat(const aiMaterial * m, const char * key, uint s0, uint s1, float def = 0.f) { float ret; aiReturn r = m->Get(key, s0, s1, ret); if (r != aiReturn_SUCCESS) return def; return ret; } QString aiMatMap(const aiMaterial * m, aiTextureType texture) { aiString p; aiReturn r = const_cast(m)->GetTexture(texture, 0, &p); if (r != aiReturn_SUCCESS) return QString(p.C_Str()); return QString(); } Material * assimpMaterial(const aiMaterial * m) { if (!m) return 0; Material * ret = new Material(); ret->name = const_cast(m)->GetName().C_Str(); //qDebug() << "mat" << ret->name; //for (int i = 0; i < m->mNumProperties; ++i) // qDebug()<< m->mProperties[i]->mKey.C_Str(); ret->color_diffuse = aiMatColor(m, AI_MATKEY_COLOR_DIFFUSE); ret->color_specular = aiMatColor(m, AI_MATKEY_COLOR_SPECULAR); ret->color_emission = aiMatColor(m, AI_MATKEY_COLOR_EMISSIVE); ret->map_diffuse .bitmap_path = aiMatMap(m, aiTextureType_DIFFUSE); ret->map_normal .bitmap_path = aiMatMap(m, aiTextureType_NORMALS); ret->map_specular .bitmap_path = aiMatMap(m, aiTextureType_SPECULAR); ret->map_roughness.bitmap_path = aiMatMap(m, aiTextureType_DIFFUSE_ROUGHNESS); ret->map_emission .bitmap_path = aiMatMap(m, aiTextureType_EMISSIVE); ret->transparency = 1.f - aiMatFloat(m, AI_MATKEY_OPACITY, 1.f); ret->setTypes(); return ret; } Light * assimpLight(const aiLight * l) { if (!l) return 0; Light * ret = new Light(); ret->setName(l->mName.C_Str()); ret->setPos(fromAiVector3D(l->mPosition)); ret->setDirection(fromAiVector3D(l->mDirection)); ret->decay_const = l->mAttenuationConstant ; ret->decay_linear = l->mAttenuationLinear ; ret->decay_quadratic = l->mAttenuationQuadratic; ret->angle_start = l->mAngleInnerCone * rad2deg; ret->angle_end = l->mAngleOuterCone * rad2deg; if (l->mType == aiLightSource_SPOT) ret->light_type = Light::Cone; QVector3D col(l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b); ret->intensity = col.length(); col /= ret->intensity; ret->setColor(QColor::fromRgbF(col[0], col[1], col[2])); return ret; } ObjectBase * assimpObject(const aiNode * n, const QVector & meshes, aiMesh ** ai_meshes, const QVector & materials, const QMap & light_by_name, QVector & out_lights) { if (!n) return 0; ObjectBase * ret = 0; QString name = n->mName.C_Str(); Light * light = light_by_name.value(name, 0); if (light) { ret = light->clone(); out_lights << (Light*)ret; } else ret = new ObjectBase(); ret->setName(name); ret->setMatrix(fromAiMatrix4D(n->mTransformation)); //qDebug() << "add object" << ret << ret->name(); if (!light) { //qDebug() << name << "has" << n->mNumMeshes << "meshes"; for (uint i = 0; i < n->mNumMeshes; ++i) { int mi = n->mMeshes[i]; if (meshes[mi]) { if (!ret->mesh()) { ret->setMesh(new Mesh()); int mati = ai_meshes[mi]->mMaterialIndex; if (mati >= 0 || mati < materials.size()) ret->setMaterial(materials[mati]); } ret->mesh()->append(meshes[mi]); //ret->setMesh(meshes[mi]); //qDebug() << "set mesh" << mi << ret->mesh(); //break; } } } for (uint i = 0; i < n->mNumChildren; ++i) { ObjectBase * co = assimpObject(n->mChildren[i], meshes, ai_meshes, materials, light_by_name, out_lights); if (co) ret->addChild(co); } return ret; } Scene * loadScene(const QString & filepath) { if (filepath.isEmpty()) return 0; qDebug() << "[Loader Assimp] Import" << filepath << "..."; Assimp::Importer importer; const aiScene * ais = importer.ReadFile(filepath.toUtf8(), aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_GenUVCoords | aiProcess_TransformUVCoords); if (!ais) { qDebug() << "[Loader Assimp] Error: \"" + QString(importer.GetErrorString()) + "\""; return 0; } qDebug() << "[Loader Assimp] Imported" << ais->mNumMeshes << "meshes"; QVector meshes; for (uint i = 0; i < ais->mNumMeshes; ++i) meshes << assimpMesh(ais->mMeshes[i]); QVector materials; for (uint i = 0; i < ais->mNumMaterials; ++i) materials << assimpMaterial(ais->mMaterials[i]); QVector lights; for (uint i = 0; i < ais->mNumLights; ++i) lights << assimpLight(ais->mLights[i]); QMap light_by_name; foreach (Light * l, lights) light_by_name[l->name()] = l; QVector out_lights; ObjectBase * root = assimpObject(ais->mRootNode, meshes, ais->mMeshes, materials, light_by_name, out_lights); if (!root) return 0; ObjectBaseList rcl = root->children(true); foreach (ObjectBase * c, rcl) { foreach (Light * l, out_lights) { if (c->name() == (l->name() + ".Target")) { l->setAim((l->worldTransform().inverted() * QVector4D(c->worldPos(), 1)).toVector3D()); delete c; break; } } } Scene * scene = new Scene(); scene->setName(root->name()); foreach (ObjectBase * o, root->children()) scene->addObject(o); qDeleteAll(lights); return scene; }