1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "assimpimporter.h"
5
6#include <Qt3DCore/qentity.h>
7#include <Qt3DCore/qtransform.h>
8#include <Qt3DCore/qattribute.h>
9#include <Qt3DCore/qbuffer.h>
10#include <Qt3DCore/qgeometry.h>
11#include <Qt3DRender/qcameralens.h>
12#include <Qt3DRender/qeffect.h>
13#include <Qt3DRender/qgeometryrenderer.h>
14#include <Qt3DRender/qmaterial.h>
15#include <Qt3DRender/qmesh.h>
16#include <Qt3DRender/qparameter.h>
17#include <Qt3DRender/qtexture.h>
18#include <Qt3DRender/qtextureimagedatagenerator.h>
19#include <Qt3DExtras/qdiffusemapmaterial.h>
20#include <Qt3DExtras/qdiffusespecularmapmaterial.h>
21#include <Qt3DExtras/qphongmaterial.h>
22#include <Qt3DExtras/qmorphphongmaterial.h>
23#include <Qt3DExtras/qnormaldiffusemapmaterial.h>
24#include <Qt3DExtras/qnormaldiffusespecularmapmaterial.h>
25#include <Qt3DAnimation/qkeyframeanimation.h>
26#include <Qt3DAnimation/qmorphinganimation.h>
27#include <QtCore/QFileInfo>
28#include <QtGui/QColor>
29
30#include <qmath.h>
31
32#include <Qt3DCore/private/qabstractnodefactory_p.h>
33#include <Qt3DCore/private/qurlhelper_p.h>
34#include <Qt3DRender/private/renderlogging_p.h>
35
36QT_BEGIN_NAMESPACE
37
38
39namespace Qt3DRender {
40
41using namespace Qt3DCore;
42using namespace Qt3DExtras;
43
44/*!
45 \class Qt3DRender::AssimpImporter
46 \inmodule Qt3DRender
47 \since 5.5
48 \internal
49
50 \brief Provides a generic way of loading various 3D assets
51 format into a Qt3D scene.
52
53 It should be noted that Assimp aiString is explicitly defined to be UTF-8.
54*/
55
56Q_LOGGING_CATEGORY(AssimpImporterLog, "Qt3D.AssimpImporter", QtWarningMsg)
57
58namespace {
59
60const QString ASSIMP_MATERIAL_DIFFUSE_COLOR = QLatin1String("kd");
61const QString ASSIMP_MATERIAL_SPECULAR_COLOR = QLatin1String("ks");
62const QString ASSIMP_MATERIAL_AMBIENT_COLOR = QLatin1String("ka");
63const QString ASSIMP_MATERIAL_EMISSIVE_COLOR = QLatin1String("emissive");
64const QString ASSIMP_MATERIAL_TRANSPARENT_COLOR = QLatin1String("transparent");
65const QString ASSIMP_MATERIAL_REFLECTIVE_COLOR = QLatin1String("reflective");
66
67const QString ASSIMP_MATERIAL_DIFFUSE_TEXTURE = QLatin1String("diffuseTexture");
68const QString ASSIMP_MATERIAL_AMBIENT_TEXTURE = QLatin1String("ambientTex");
69const QString ASSIMP_MATERIAL_SPECULAR_TEXTURE = QLatin1String("specularTexture");
70const QString ASSIMP_MATERIAL_EMISSIVE_TEXTURE = QLatin1String("emissiveTex");
71const QString ASSIMP_MATERIAL_NORMALS_TEXTURE = QLatin1String("normalsTex");
72// Keep the old "normalsTex" parameter name to keep backwards compatibility, add "normalTexture" as a new one
73const QString ASSIMP_MATERIAL_NORMALS_TEXTURE2 = QLatin1String("normalTexture");
74const QString ASSIMP_MATERIAL_OPACITY_TEXTURE = QLatin1String("opacityTex");
75const QString ASSIMP_MATERIAL_REFLECTION_TEXTURE = QLatin1String("reflectionTex");
76const QString ASSIMP_MATERIAL_HEIGHT_TEXTURE = QLatin1String("heightTex");
77const QString ASSIMP_MATERIAL_LIGHTMAP_TEXTURE = QLatin1String("opacityTex");
78const QString ASSIMP_MATERIAL_DISPLACEMENT_TEXTURE = QLatin1String("displacementTex");
79const QString ASSIMP_MATERIAL_SHININESS_TEXTURE = QLatin1String("shininessTex");
80
81const QString ASSIMP_MATERIAL_IS_TWOSIDED = QLatin1String("twosided");
82const QString ASSIMP_MATERIAL_IS_WIREFRAME = QLatin1String("wireframe");
83
84const QString ASSIMP_MATERIAL_OPACITY = QLatin1String("opacity");
85const QString ASSIMP_MATERIAL_SHININESS = QLatin1String("shininess");
86const QString ASSIMP_MATERIAL_SHININESS_STRENGTH = QLatin1String("shininess_strength");
87const QString ASSIMP_MATERIAL_REFRACTI = QLatin1String("refracti");
88const QString ASSIMP_MATERIAL_REFLECTIVITY = QLatin1String("reflectivity");
89
90const QString ASSIMP_MATERIAL_NAME = QLatin1String("name");
91
92const QString VERTICES_ATTRIBUTE_NAME = QAttribute::defaultPositionAttributeName();
93const QString NORMAL_ATTRIBUTE_NAME = QAttribute::defaultNormalAttributeName();
94const QString TANGENT_ATTRIBUTE_NAME = QAttribute::defaultTangentAttributeName();
95const QString TEXTCOORD_ATTRIBUTE_NAME = QAttribute::defaultTextureCoordinateAttributeName();
96const QString COLOR_ATTRIBUTE_NAME = QAttribute::defaultColorAttributeName();
97
98/*
99 * Returns a QMatrix4x4 from \a matrix;
100 */
101QMatrix4x4 aiMatrix4x4ToQMatrix4x4(const aiMatrix4x4 &matrix) noexcept
102{
103 return QMatrix4x4(matrix.a1, matrix.a2, matrix.a3, matrix.a4,
104 matrix.b1, matrix.b2, matrix.b3, matrix.b4,
105 matrix.c1, matrix.c2, matrix.c3, matrix.c4,
106 matrix.d1, matrix.d2, matrix.d3, matrix.d4);
107}
108
109/*
110 * Returns a QString from \a str;
111 */
112inline QString aiStringToQString(const aiString &str)
113{
114 return QString::fromUtf8(utf8: str.data, size: int(str.length));
115}
116
117QMaterial *createBestApproachingMaterial(const aiMaterial *assimpMaterial)
118{
119 aiString path; // unused but necessary
120 const bool hasNormalTexture = (assimpMaterial->GetTexture(type: aiTextureType_NORMALS, index: 0, path: &path) == AI_SUCCESS);
121 const bool hasDiffuseTexture = (assimpMaterial->GetTexture(type: aiTextureType_DIFFUSE, index: 0, path: &path) == AI_SUCCESS);
122 const bool hasSpecularTexture = (assimpMaterial->GetTexture(type: aiTextureType_SPECULAR, index: 0, path: &path) == AI_SUCCESS);
123
124 if (hasNormalTexture && hasDiffuseTexture && hasSpecularTexture)
125 return QAbstractNodeFactory::createNode<QNormalDiffuseSpecularMapMaterial>(type: "QNormalDiffuseSpecularMapMaterial");
126 if (hasNormalTexture && hasDiffuseTexture)
127 return QAbstractNodeFactory::createNode<QNormalDiffuseMapMaterial>(type: "QNormalDiffuseMapMaterial");
128 if (hasDiffuseTexture && hasSpecularTexture)
129 return QAbstractNodeFactory::createNode<QDiffuseSpecularMapMaterial>(type: "QDiffuseSpecularMapMaterial");
130 if (hasDiffuseTexture)
131 return QAbstractNodeFactory::createNode<QDiffuseMapMaterial>(type: "QDiffuseMapMaterial");
132 return QAbstractNodeFactory::createNode<QPhongMaterial>(type: "QPhongMaterial");
133}
134
135QString texturePath(const aiString &path)
136{
137 QString p = aiStringToQString(str: path);
138 p.replace(before: QLatin1String("\\"), after: QLatin1String("/"));
139 if (p.startsWith(c: '/'))
140 p.remove(i: 0, len: 1);
141 return p;
142}
143
144/*
145 * Returns the Qt3DRender::QParameter with named \a name if contained by the material
146 * \a material. If the material doesn't contain the named parameter, a new
147 * Qt3DRender::QParameter is created and inserted into the material.
148 */
149QParameter *findNamedParameter(const QString &name, QMaterial *material)
150{
151 // Does the material contain the parameter ?
152 const auto params = material->parameters();
153 for (QParameter *p : params) {
154 if (p->name() == name)
155 return p;
156 }
157
158 // Does the material's effect contain the parameter ?
159 if (material->effect()) {
160 const QEffect *e = material->effect();
161 const auto params = e->parameters();
162 for (QParameter *p : params) {
163 if (p->name() == name)
164 return p;
165 }
166 }
167
168 // Create and add parameter to material
169 QParameter *p = QAbstractNodeFactory::createNode<QParameter>(type: "QParameter");
170 p->setParent(material);
171 p->setName(name);
172 material->addParameter(parameter: p);
173 return p;
174}
175
176void setParameterValue(const QString &name, QMaterial *material, const QVariant &value)
177{
178 QParameter *p = findNamedParameter(name, material);
179 p->setValue(value);
180}
181
182QAttribute *createAttribute(Qt3DCore::QBuffer *buffer,
183 const QString &name,
184 QAttribute::VertexBaseType vertexBaseType,
185 uint vertexSize,
186 uint count,
187 uint byteOffset = 0,
188 uint byteStride = 0,
189 QNode *parent = nullptr)
190{
191 QAttribute *attribute = QAbstractNodeFactory::createNode<QAttribute>(type: "QAttribute");
192 attribute->setBuffer(buffer);
193 attribute->setName(name);
194 attribute->setVertexBaseType(vertexBaseType);
195 attribute->setVertexSize(vertexSize);
196 attribute->setCount(count);
197 attribute->setByteOffset(byteOffset);
198 attribute->setByteStride(byteStride);
199 attribute->setParent(parent);
200 return attribute;
201}
202
203QAttribute *createIndexAttribute(Qt3DCore::QBuffer *buffer,
204 QAttribute::VertexBaseType vertexBaseType,
205 uint vertexSize,
206 uint count,
207 uint byteOffset = 0,
208 uint byteStride = 0,
209 QNode *parent = nullptr)
210{
211 QAttribute *attribute = QAbstractNodeFactory::createNode<QAttribute>(type: "QAttribute");
212 attribute->setBuffer(buffer);
213 attribute->setVertexBaseType(vertexBaseType);
214 attribute->setVertexSize(vertexSize);
215 attribute->setCount(count);
216 attribute->setByteOffset(byteOffset);
217 attribute->setByteStride(byteStride);
218 attribute->setParent(parent);
219 return attribute;
220}
221
222QTextureWrapMode::WrapMode wrapModeFromaiTextureMapMode(int mode)
223{
224 switch (mode) {
225 case aiTextureMapMode_Wrap:
226 return QTextureWrapMode::Repeat;
227 case aiTextureMapMode_Mirror:
228 return QTextureWrapMode::MirroredRepeat;
229 case aiTextureMapMode_Decal:
230 return QTextureWrapMode::ClampToBorder;
231 case aiTextureMapMode_Clamp:
232 default:
233 return QTextureWrapMode::ClampToEdge;
234 }
235}
236
237} // anonymous
238
239QStringList AssimpImporter::assimpSupportedFormatsList = AssimpImporter::assimpSupportedFormats();
240
241/*!
242 * Returns a QStringlist with the suffixes of the various supported asset formats.
243 */
244QStringList AssimpImporter::assimpSupportedFormats()
245{
246 QStringList formats;
247
248 formats.reserve(asize: 60);
249 formats.append(QStringLiteral("3d"));
250 formats.append(QStringLiteral("3ds"));
251 formats.append(QStringLiteral("ac"));
252 formats.append(QStringLiteral("ac3d"));
253 formats.append(QStringLiteral("acc"));
254 formats.append(QStringLiteral("ase"));
255 formats.append(QStringLiteral("ask"));
256 formats.append(QStringLiteral("assbin"));
257 formats.append(QStringLiteral("b3d"));
258 formats.append(QStringLiteral("blend"));
259 formats.append(QStringLiteral("bvh"));
260 formats.append(QStringLiteral("cob"));
261 formats.append(QStringLiteral("csm"));
262 formats.append(QStringLiteral("dae"));
263 formats.append(QStringLiteral("dxf"));
264 formats.append(QStringLiteral("enff"));
265 formats.append(QStringLiteral("fbx"));
266 formats.append(QStringLiteral("hmp"));
267 formats.append(QStringLiteral("irr"));
268 formats.append(QStringLiteral("irrmesh"));
269 formats.append(QStringLiteral("lwo"));
270 formats.append(QStringLiteral("lws"));
271 formats.append(QStringLiteral("lxo"));
272 formats.append(QStringLiteral("md2"));
273 formats.append(QStringLiteral("md3"));
274 formats.append(QStringLiteral("md5anim"));
275 formats.append(QStringLiteral("md5camera"));
276 formats.append(QStringLiteral("md5mesh"));
277 formats.append(QStringLiteral("mdc"));
278 formats.append(QStringLiteral("mdl"));
279 formats.append(QStringLiteral("mesh.xml"));
280 formats.append(QStringLiteral("mot"));
281 formats.append(QStringLiteral("ms3d"));
282 formats.append(QStringLiteral("ndo"));
283 formats.append(QStringLiteral("nff"));
284 formats.append(QStringLiteral("obj"));
285 formats.append(QStringLiteral("off"));
286 formats.append(QStringLiteral("ogex"));
287 formats.append(QStringLiteral("pk3"));
288 formats.append(QStringLiteral("ply"));
289 formats.append(QStringLiteral("prj"));
290 formats.append(QStringLiteral("q3o"));
291 formats.append(QStringLiteral("q3s"));
292 formats.append(QStringLiteral("raw"));
293 formats.append(QStringLiteral("scn"));
294 formats.append(QStringLiteral("sib"));
295 formats.append(QStringLiteral("smd"));
296 formats.append(QStringLiteral("stl"));
297 formats.append(QStringLiteral("ter"));
298 formats.append(QStringLiteral("uc"));
299 formats.append(QStringLiteral("vta"));
300 formats.append(QStringLiteral("x"));
301 formats.append(QStringLiteral("xml"));
302
303 return formats;
304}
305
306class AssimpRawTextureImage : public QAbstractTextureImage
307{
308 Q_OBJECT
309public:
310 explicit AssimpRawTextureImage(QNode *parent = 0);
311
312 QTextureImageDataGeneratorPtr dataGenerator() const final;
313
314 void setData(const QByteArray &data);
315
316private:
317 QByteArray m_data;
318
319 class AssimpRawTextureImageFunctor : public QTextureImageDataGenerator
320 {
321 public:
322 explicit AssimpRawTextureImageFunctor(const QByteArray &data);
323
324 QTextureImageDataPtr operator()() final;
325 bool operator ==(const QTextureImageDataGenerator &other) const final;
326
327 QT3D_FUNCTOR(AssimpRawTextureImageFunctor)
328 private:
329 QByteArray m_data;
330 };
331};
332
333/*!
334 * Constructor. Initializes a new instance of AssimpImporter.
335 */
336AssimpImporter::AssimpImporter() : QSceneImporter(),
337 m_sceneParsed(false),
338 m_scene(nullptr)
339{
340}
341
342/*!
343 * Destructor. Cleans the parser properly before destroying it.
344 */
345AssimpImporter::~AssimpImporter()
346{
347 cleanup();
348}
349
350/*!
351 * Returns \c true if the extensions are supported
352 * by the Assimp Assets importer.
353 */
354bool AssimpImporter::areAssimpExtensions(const QStringList &extensions)
355{
356 for (const auto &ext : std::as_const(t: extensions))
357 if (AssimpImporter::assimpSupportedFormatsList.contains(str: ext.toLower()))
358 return true;
359 return false;
360}
361
362/*!
363 * Sets the \a source used by the parser to load the asset file.
364 * If the file is valid, this will trigger parsing of the file.
365 */
366void AssimpImporter::setSource(const QUrl &source)
367{
368 const QString path = Qt3DCore::QUrlHelper::urlToLocalFileOrQrc(url: source);
369 QFileInfo file(path);
370 m_sceneDir = file.absoluteDir();
371 if (!file.exists()) {
372 qCWarning(AssimpImporterLog) << "File missing " << path;
373 return ;
374 }
375 readSceneFile(file: path);
376}
377
378/*!
379 * Sets the \a basePath used by the parser to load the asset file.
380 * If the file specified in \a data is valid, this will trigger parsing of the file.
381 */
382void AssimpImporter::setData(const QByteArray &data, const QString &basePath)
383{
384 readSceneData(data, basePath);
385}
386
387/*!
388 * Returns \c true if the extension in QStringList \a extensions is supported by
389 * the assimp parser.
390 */
391bool AssimpImporter::areFileTypesSupported(const QStringList &extensions) const
392{
393 return AssimpImporter::areAssimpExtensions(extensions);
394}
395
396/*!
397 * Returns a Entity node which is the root node of the scene
398 * node specified by \a id. If \a id is empty, the scene is assumed to be
399 * the root node of the scene.
400 *
401 * Returns \c nullptr if \a id was specified but no node matching it was found.
402 */
403Qt3DCore::QEntity *AssimpImporter::scene(const QString &id)
404{
405 // m_aiScene shouldn't be null.
406 // If it is either, the file failed to be imported or
407 // setFilePath was not called
408 if (m_scene == nullptr || m_scene->m_aiScene == nullptr)
409 return nullptr;
410
411 aiNode *rootNode = m_scene->m_aiScene->mRootNode;
412 // if id specified, tries to find node
413 if (!id.isEmpty() &&
414 !(rootNode = rootNode->FindNode(name: id.toUtf8().constData()))) {
415 qCDebug(AssimpImporterLog) << Q_FUNC_INFO << " Couldn't find requested scene node";
416 return nullptr;
417 }
418
419 // Builds the Qt3D scene using the Assimp aiScene
420 // and the various dicts filled previously by parse
421 Qt3DCore::QEntity *n = node(node: rootNode);
422 if (m_scene->m_animations.size() > 0) {
423 qWarning() << "No target found for " << m_scene->m_animations.size() << " animations!";
424
425 for (Qt3DAnimation::QKeyframeAnimation *anim : std::as_const(t&: m_scene->m_animations))
426 delete anim;
427 m_scene->m_animations.clear();
428 }
429 return n;
430}
431
432/*!
433 * Returns a Node from the scene identified by \a id.
434 * Returns \c nullptr if the node was not found.
435 */
436Qt3DCore::QEntity *AssimpImporter::node(const QString &id)
437{
438 if (m_scene == nullptr || m_scene->m_aiScene == nullptr)
439 return nullptr;
440 parse();
441 aiNode *n = m_scene->m_aiScene->mRootNode->FindNode(name: id.toUtf8().constData());
442 return node(node: n);
443}
444
445template <typename T>
446void findAnimationsForNode(QList<T *> &animations, QList<T *> &result, const QString &name)
447{
448 for (T *anim : animations) {
449 if (anim->targetName() == name) {
450 result.push_back(anim);
451 animations.removeAll(anim);
452 }
453 }
454}
455
456/*!
457 * Returns a Node from an Assimp aiNode \a node.
458 */
459Qt3DCore::QEntity *AssimpImporter::node(aiNode *node)
460{
461 if (node == nullptr)
462 return nullptr;
463 QEntity *entityNode = QAbstractNodeFactory::createNode<Qt3DCore::QEntity>(type: "QEntity");
464 entityNode->setObjectName(aiStringToQString(str: node->mName));
465
466 // Add Meshes to the node
467 for (uint i = 0; i < node->mNumMeshes; i++) {
468 uint meshIndex = node->mMeshes[i];
469 QGeometryRenderer *mesh = loadMesh(meshIndex);
470
471 // mesh material
472 QMaterial *material;
473
474 QList<Qt3DAnimation::QMorphingAnimation *> morphingAnimations
475 = mesh->findChildren<Qt3DAnimation::QMorphingAnimation *>();
476 if (morphingAnimations.size() > 0) {
477 material = new Qt3DExtras::QMorphPhongMaterial(entityNode);
478
479 QList<Qt3DAnimation::QMorphingAnimation *> animations;
480 findAnimationsForNode<Qt3DAnimation::QMorphingAnimation>(animations&: m_scene->m_morphAnimations,
481 result&: animations,
482 name: aiStringToQString(str: node->mName));
483 const auto morphTargetList = morphingAnimations.at(i: 0)->morphTargetList();
484 for (Qt3DAnimation::QMorphingAnimation *anim : std::as_const(t&: animations)) {
485 anim->setParent(entityNode);
486 anim->setTarget(mesh);
487 anim->setMorphTargets(morphTargetList);
488 }
489
490 for (int j = 0; j < animations.size(); ++j) {
491 QObject::connect(sender: animations[j], signal: &Qt3DAnimation::QMorphingAnimation::interpolatorChanged,
492 context: (Qt3DExtras::QMorphPhongMaterial *)material,
493 slot: &Qt3DExtras::QMorphPhongMaterial::setInterpolator);
494 }
495 morphingAnimations[0]->deleteLater();
496 } else {
497 uint materialIndex = m_scene->m_aiScene->mMeshes[meshIndex]->mMaterialIndex;
498 material = loadMaterial(materialIndex);
499 }
500
501 if (node->mNumMeshes == 1) {
502 if (material)
503 entityNode->addComponent(comp: material);
504 // mesh
505 entityNode->addComponent(comp: mesh);
506 } else {
507 QEntity *childEntity = QAbstractNodeFactory::createNode<Qt3DCore::QEntity>(type: "QEntity");
508 childEntity->setObjectName(entityNode->objectName() + QLatin1String("_Child") + QString::number(i));
509 if (material)
510 childEntity->addComponent(comp: material);
511 childEntity->addComponent(comp: mesh);
512 childEntity->setParent(entityNode);
513
514 Qt3DCore::QTransform *transform
515 = QAbstractNodeFactory::createNode<Qt3DCore::QTransform>(type: "QTransform");
516 childEntity->addComponent(comp: transform);
517 }
518 }
519
520 // Add Children to Node
521 for (uint i = 0; i < node->mNumChildren; i++) {
522 // this-> is necessary here otherwise
523 // it conflicts with the variable node
524 QEntity *child = this->node(node: node->mChildren[i]);
525 // Are we sure each child are unique ???
526 if (child != nullptr)
527 child->setParent(entityNode);
528 }
529
530 // Add Transformations
531 const QMatrix4x4 qTransformMatrix = aiMatrix4x4ToQMatrix4x4(matrix: node->mTransformation);
532 Qt3DCore::QTransform *transform = QAbstractNodeFactory::createNode<Qt3DCore::QTransform>(type: "QTransform");
533 transform->setMatrix(qTransformMatrix);
534 entityNode->addComponent(comp: transform);
535
536 QList<Qt3DAnimation::QKeyframeAnimation *> animations;
537 findAnimationsForNode<Qt3DAnimation::QKeyframeAnimation>(animations&: m_scene->m_animations,
538 result&: animations,
539 name: aiStringToQString(str: node->mName));
540
541 for (Qt3DAnimation::QKeyframeAnimation *anim : std::as_const(t&: animations)) {
542 anim->setTarget(transform);
543 anim->setParent(entityNode);
544 }
545
546 // Add Camera
547 auto camera = loadCamera(node);
548 if (camera)
549 camera->setParent(entityNode);
550
551 // TO DO : Add lights ....
552
553 return entityNode;
554}
555
556/*!
557 * Reads the scene file pointed by \a path and launches the parsing of
558 * the scene using Assimp, after having cleaned up previously saved values
559 * from eventual previous parsings.
560 */
561void AssimpImporter::readSceneFile(const QString &path)
562{
563 cleanup();
564
565 m_scene = new SceneImporter();
566
567 // SET THIS TO REMOVE POINTS AND LINES -> HAVE ONLY TRIANGLES
568 m_scene->m_importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, iValue: aiPrimitiveType_LINE|aiPrimitiveType_POINT);
569 // SET CUSTOM FILE HANDLER TO HANDLE FILE READING THROUGH QT (RESOURCES, SOCKET ...)
570 m_scene->m_importer->SetIOHandler(new AssimpHelper::AssimpIOSystem());
571
572 // type and aiProcess_Triangulate discompose polygons with more than 3 points in triangles
573 // aiProcess_SortByPType makes sure that meshes data are triangles
574 m_scene->m_aiScene = m_scene->m_importer->ReadFile(pFile: path.toUtf8().constData(),
575 pFlags: aiProcess_SortByPType|
576 aiProcess_Triangulate|
577 aiProcess_GenSmoothNormals|
578 aiProcess_FlipUVs);
579 if (m_scene->m_aiScene == nullptr) {
580 qCWarning(AssimpImporterLog) << "Assimp scene import failed" << m_scene->m_importer->GetErrorString();
581 QSceneImporter::logError(error: QString::fromUtf8(utf8: m_scene->m_importer->GetErrorString()));
582 return ;
583 }
584 parse();
585}
586
587/*!
588 * Reads the scene file pointed by \a path and launches the parsing of
589 * the scene using Assimp, after having cleaned up previously saved values
590 * from eventual previous parsings.
591 */
592void AssimpImporter::readSceneData(const QByteArray& data, const QString &basePath)
593{
594 Q_UNUSED(basePath);
595 cleanup();
596
597 m_scene = new SceneImporter();
598
599 // SET THIS TO REMOVE POINTS AND LINES -> HAVE ONLY TRIANGLES
600 m_scene->m_importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, iValue: aiPrimitiveType_LINE|aiPrimitiveType_POINT);
601 // SET CUSTOM FILE HANDLER TO HANDLE FILE READING THROUGH QT (RESOURCES, SOCKET ...)
602 m_scene->m_importer->SetIOHandler(new AssimpHelper::AssimpIOSystem());
603
604 // type and aiProcess_Triangulate discompose polygons with more than 3 points in triangles
605 // aiProcess_SortByPType makes sure that meshes data are triangles
606 m_scene->m_aiScene = m_scene->m_importer->ReadFileFromMemory(pBuffer: data.data(), pLength: data.size(),
607 pFlags: aiProcess_SortByPType|
608 aiProcess_Triangulate|
609 aiProcess_GenSmoothNormals|
610 aiProcess_FlipUVs);
611 if (m_scene->m_aiScene == nullptr) {
612 qCWarning(AssimpImporterLog) << "Assimp scene import failed";
613 return ;
614 }
615 parse();
616}
617
618/*!
619 * Cleans the various dictionaries holding the scene's information.
620 */
621void AssimpImporter::cleanup()
622{
623 m_sceneParsed = false;
624 delete m_scene;
625 m_scene = nullptr;
626}
627
628/*!
629 * Parses the aiScene provided py Assimp and converts Assimp
630 * values to Qt3D values.
631 */
632void AssimpImporter::parse()
633{
634 if (!m_sceneParsed) {
635 // Set parsed flags
636 m_sceneParsed = !m_sceneParsed;
637
638 for (uint i = 0; i < m_scene->m_aiScene->mNumAnimations; i++)
639 loadAnimation(animationIndex: i);
640 }
641}
642
643/*!
644 * Converts the provided Assimp aiMaterial identified by \a materialIndex to a
645 * Qt3D material
646 * \sa Material
647 */
648QMaterial *AssimpImporter::loadMaterial(uint materialIndex)
649{
650 // Generates default material based on what the assimp material contains
651 aiMaterial *assimpMaterial = m_scene->m_aiScene->mMaterials[materialIndex];
652 QMaterial *material = createBestApproachingMaterial(assimpMaterial);
653 // Material Name
654 copyMaterialName(material, assimpMaterial);
655 copyMaterialColorProperties(material, assimpMaterial);
656 copyMaterialBoolProperties(material, assimpMaterial);
657 copyMaterialFloatProperties(material, assimpMaterial);
658
659 // Add textures to materials dict
660 copyMaterialTextures(material, assimpMaterial);
661
662 return material;
663}
664
665/*!
666 * Converts the Assimp aiMesh mesh identified by \a meshIndex to a QGeometryRenderer
667 * \sa QGeometryRenderer
668 */
669QGeometryRenderer *AssimpImporter::loadMesh(uint meshIndex)
670{
671 aiMesh *mesh = m_scene->m_aiScene->mMeshes[meshIndex];
672
673 QGeometryRenderer *geometryRenderer = QAbstractNodeFactory::createNode<QGeometryRenderer>(type: "QGeometryRenderer");
674 QGeometry *meshGeometry = QAbstractNodeFactory::createNode<QGeometry>(type: "QGeometry");
675 meshGeometry->setParent(geometryRenderer);
676 Qt3DCore::QBuffer *vertexBuffer = QAbstractNodeFactory::createNode<Qt3DCore::QBuffer>(type: "QBuffer");
677 vertexBuffer->setParent(meshGeometry);
678 Qt3DCore::QBuffer *indexBuffer = QAbstractNodeFactory::createNode<Qt3DCore::QBuffer>(type: "QBuffer");
679 indexBuffer->setParent(meshGeometry);
680
681 geometryRenderer->setGeometry(meshGeometry);
682
683 // Primitive are always triangles with the current Assimp's configuration
684
685 // Vertices and Normals always present with the current Assimp's configuration
686 aiVector3D *vertices = mesh->mVertices;
687 aiVector3D *normals = mesh->mNormals;
688 aiColor4D *colors = mesh->mColors[0];
689 // Tangents and TextureCoord not always present
690 bool hasTangent = mesh->HasTangentsAndBitangents();
691 bool hasTexture = mesh->HasTextureCoords(index: 0);
692 bool hasColor = (colors != NULL); // NULL defined by Assimp
693 aiVector3D *tangents = hasTangent ? mesh->mTangents : nullptr;
694 aiVector3D *textureCoord = hasTexture ? mesh->mTextureCoords[0] : nullptr;
695
696 // Add values in raw float array
697 ushort chunkSize = 6 + (hasTangent ? 3 : 0) + (hasTexture ? 2 : 0) + (hasColor ? 4 : 0);
698 QByteArray bufferArray;
699 bufferArray.resize(size: chunkSize * mesh->mNumVertices * sizeof(float));
700 float *vbufferContent = reinterpret_cast<float*>(bufferArray.data());
701 for (uint i = 0; i < mesh->mNumVertices; i++) {
702 uint idx = i * chunkSize;
703 // position
704 vbufferContent[idx] = vertices[i].x;
705 vbufferContent[idx + 1] = vertices[i].y;
706 vbufferContent[idx + 2] = vertices[i].z;
707 // normals
708 vbufferContent[idx + 3] = normals[i].x;
709 vbufferContent[idx + 4] = normals[i].y;
710 vbufferContent[idx + 5] = normals[i].z;
711
712 if (hasTangent) {
713 vbufferContent[idx + 6] = tangents[i].x;
714 vbufferContent[idx + 7] = tangents[i].y;
715 vbufferContent[idx + 8] = tangents[i].z;
716 }
717 if (hasTexture) {
718 char offset = (hasTangent ? 9 : 6);
719 vbufferContent[idx + offset] = textureCoord[i].x;
720 vbufferContent[idx + offset + 1] = textureCoord[i].y;
721 }
722 if (hasColor) {
723 char offset = 6 + (hasTangent ? 3 : 0) + (hasTexture ? 2 : 0);
724 vbufferContent[idx + offset] = colors[i].r;
725 vbufferContent[idx + offset + 1] = colors[i].g;
726 vbufferContent[idx + offset + 2] = colors[i].b;
727 vbufferContent[idx + offset + 3] = colors[i].a;
728 }
729 }
730
731 vertexBuffer->setData(bufferArray);
732
733 // Add vertex attributes to the mesh with the right array
734 QAttribute *positionAttribute = createAttribute(buffer: vertexBuffer, name: VERTICES_ATTRIBUTE_NAME,
735 vertexBaseType: QAttribute::Float, vertexSize: 3,
736 count: mesh->mNumVertices,
737 byteOffset: 0,
738 byteStride: chunkSize * sizeof(float));
739
740 QAttribute *normalAttribute = createAttribute(buffer: vertexBuffer, name: NORMAL_ATTRIBUTE_NAME,
741 vertexBaseType: QAttribute::Float, vertexSize: 3,
742 count: mesh->mNumVertices,
743 byteOffset: 3 * sizeof(float),
744 byteStride: chunkSize * sizeof(float));
745
746 meshGeometry->addAttribute(attribute: positionAttribute);
747 meshGeometry->addAttribute(attribute: normalAttribute);
748
749 if (hasTangent) {
750 QAttribute *tangentsAttribute = createAttribute(buffer: vertexBuffer, name: TANGENT_ATTRIBUTE_NAME,
751 vertexBaseType: QAttribute::Float, vertexSize: 3,
752 count: mesh->mNumVertices,
753 byteOffset: 6 * sizeof(float),
754 byteStride: chunkSize * sizeof(float));
755 meshGeometry->addAttribute(attribute: tangentsAttribute);
756 }
757
758 if (hasTexture) {
759 QAttribute *textureCoordAttribute = createAttribute(buffer: vertexBuffer, name: TEXTCOORD_ATTRIBUTE_NAME,
760 vertexBaseType: QAttribute::Float, vertexSize: 2,
761 count: mesh->mNumVertices,
762 byteOffset: (hasTangent ? 9 : 6) * sizeof(float),
763 byteStride: chunkSize * sizeof(float));
764 meshGeometry->addAttribute(attribute: textureCoordAttribute);
765 }
766
767 if (hasColor) {
768 QAttribute *colorAttribute = createAttribute(buffer: vertexBuffer, name: COLOR_ATTRIBUTE_NAME,
769 vertexBaseType: QAttribute::Float, vertexSize: 4,
770 count: mesh->mNumVertices,
771 byteOffset: (6 + (hasTangent ? 3 : 0) + (hasTexture ? 2 : 0)) * sizeof(float),
772 byteStride: chunkSize * sizeof(float));
773 meshGeometry->addAttribute(attribute: colorAttribute);
774 }
775
776 QAttribute::VertexBaseType indiceType;
777 QByteArray ibufferContent;
778 uint indices = mesh->mNumFaces * 3;
779 // If there are less than 65535 indices, indices can then fit in ushort
780 // which saves video memory
781 if (indices >= USHRT_MAX) {
782 indiceType = QAttribute::UnsignedInt;
783 ibufferContent.resize(size: indices * sizeof(quint32));
784 for (uint i = 0; i < mesh->mNumFaces; i++) {
785 aiFace face = mesh->mFaces[i];
786 Q_ASSERT(face.mNumIndices == 3);
787 memcpy(dest: &reinterpret_cast<quint32*>(ibufferContent.data())[i * 3], src: face.mIndices, n: 3 * sizeof(uint));
788 }
789 }
790 else {
791 indiceType = QAttribute::UnsignedShort;
792 ibufferContent.resize(size: indices * sizeof(quint16));
793 for (uint i = 0; i < mesh->mNumFaces; i++) {
794 aiFace face = mesh->mFaces[i];
795 Q_ASSERT(face.mNumIndices == 3);
796 for (uint j = 0; j < face.mNumIndices; j++)
797 reinterpret_cast<quint16*>(ibufferContent.data())[i * 3 + j] = face.mIndices[j];
798 }
799 }
800
801 indexBuffer->setData(ibufferContent);
802
803 // Add indices attributes
804 QAttribute *indexAttribute = createIndexAttribute(buffer: indexBuffer, vertexBaseType: indiceType, vertexSize: 1, count: indices);
805 indexAttribute->setAttributeType(QAttribute::IndexAttribute);
806
807 meshGeometry->addAttribute(attribute: indexAttribute);
808
809 if (mesh->mNumAnimMeshes > 0) {
810
811 aiAnimMesh *animesh = mesh->mAnimMeshes[0];
812
813 if (animesh->mNumVertices != mesh->mNumVertices)
814 return geometryRenderer;
815
816 Qt3DAnimation::QMorphingAnimation *morphingAnimation
817 = new Qt3DAnimation::QMorphingAnimation(geometryRenderer);
818 QList<QString> names;
819
820 QList<Qt3DAnimation::QMorphTarget *> targets;
821 uint voff = 0;
822 uint noff = 0;
823 uint tanoff = 0;
824 uint texoff = 0;
825 uint coloff = 0;
826 uint offset = 0;
827 if (animesh->mVertices) {
828 names.push_back(t: VERTICES_ATTRIBUTE_NAME);
829 offset += 3;
830 }
831 if (animesh->mNormals) {
832 names.push_back(t: NORMAL_ATTRIBUTE_NAME);
833 noff = offset;
834 offset += 3;
835 }
836 if (animesh->mTangents) {
837 names.push_back(t: TANGENT_ATTRIBUTE_NAME);
838 tanoff = offset;
839 offset += 3;
840 }
841 if (animesh->mTextureCoords[0]) {
842 names.push_back(t: TEXTCOORD_ATTRIBUTE_NAME);
843 texoff = offset;
844 offset += 2;
845 }
846 if (animesh->mColors[0]) {
847 names.push_back(t: COLOR_ATTRIBUTE_NAME);
848 coloff = offset;
849 }
850
851 ushort clumpSize = (animesh->mVertices ? 3 : 0)
852 + (animesh->mNormals ? 3 : 0)
853 + (animesh->mTangents ? 3 : 0)
854 + (animesh->mColors[0] ? 4 : 0)
855 + (animesh->mTextureCoords[0] ? 2 : 0);
856
857
858 for (uint i = 0; i < mesh->mNumAnimMeshes; i++) {
859 aiAnimMesh *animesh = mesh->mAnimMeshes[i];
860 Qt3DAnimation::QMorphTarget *target = new Qt3DAnimation::QMorphTarget(geometryRenderer);
861 targets.push_back(t: target);
862 QList<QAttribute *> attributes;
863 QByteArray targetBufferArray;
864 targetBufferArray.resize(size: clumpSize * mesh->mNumVertices * sizeof(float));
865 float *dst = reinterpret_cast<float *>(targetBufferArray.data());
866
867 for (uint j = 0; j < mesh->mNumVertices; j++) {
868 if (animesh->mVertices) {
869 *dst++ = animesh->mVertices[j].x;
870 *dst++ = animesh->mVertices[j].y;
871 *dst++ = animesh->mVertices[j].z;
872 }
873 if (animesh->mNormals) {
874 *dst++ = animesh->mNormals[j].x;
875 *dst++ = animesh->mNormals[j].y;
876 *dst++ = animesh->mNormals[j].z;
877 }
878 if (animesh->mTangents) {
879 *dst++ = animesh->mTangents[j].x;
880 *dst++ = animesh->mTangents[j].y;
881 *dst++ = animesh->mTangents[j].z;
882 }
883 if (animesh->mTextureCoords[0]) {
884 *dst++ = animesh->mTextureCoords[0][j].x;
885 *dst++ = animesh->mTextureCoords[0][j].y;
886 }
887 if (animesh->mColors[0]) {
888 *dst++ = animesh->mColors[0][j].r;
889 *dst++ = animesh->mColors[0][j].g;
890 *dst++ = animesh->mColors[0][j].b;
891 *dst++ = animesh->mColors[0][j].a;
892 }
893 }
894
895 Qt3DCore::QBuffer *targetBuffer
896 = QAbstractNodeFactory::createNode<Qt3DCore::QBuffer>(type: "QBuffer");
897 targetBuffer->setData(targetBufferArray);
898 targetBuffer->setParent(meshGeometry);
899
900 if (animesh->mVertices) {
901 attributes.push_back(t: createAttribute(buffer: targetBuffer, name: VERTICES_ATTRIBUTE_NAME,
902 vertexBaseType: QAttribute::Float, vertexSize: 3,
903 count: animesh->mNumVertices, byteOffset: voff * sizeof(float),
904 byteStride: clumpSize * sizeof(float), parent: meshGeometry));
905 }
906 if (animesh->mNormals) {
907 attributes.push_back(t: createAttribute(buffer: targetBuffer, name: NORMAL_ATTRIBUTE_NAME,
908 vertexBaseType: QAttribute::Float, vertexSize: 3,
909 count: animesh->mNumVertices, byteOffset: noff * sizeof(float),
910 byteStride: clumpSize * sizeof(float), parent: meshGeometry));
911 }
912 if (animesh->mTangents) {
913 attributes.push_back(t: createAttribute(buffer: targetBuffer, name: TANGENT_ATTRIBUTE_NAME,
914 vertexBaseType: QAttribute::Float, vertexSize: 3,
915 count: animesh->mNumVertices, byteOffset: tanoff * sizeof(float),
916 byteStride: clumpSize * sizeof(float), parent: meshGeometry));
917 }
918 if (animesh->mTextureCoords[0]) {
919 attributes.push_back(t: createAttribute(buffer: targetBuffer, name: TEXTCOORD_ATTRIBUTE_NAME,
920 vertexBaseType: QAttribute::Float, vertexSize: 2,
921 count: animesh->mNumVertices, byteOffset: texoff * sizeof(float),
922 byteStride: clumpSize * sizeof(float), parent: meshGeometry));
923 }
924 if (animesh->mColors[0]) {
925 attributes.push_back(t: createAttribute(buffer: targetBuffer, name: COLOR_ATTRIBUTE_NAME,
926 vertexBaseType: QAttribute::Float, vertexSize: 4,
927 count: animesh->mNumVertices, byteOffset: coloff * sizeof(float),
928 byteStride: clumpSize * sizeof(float), parent: meshGeometry));
929 }
930 target->setAttributes(attributes);
931 }
932 morphingAnimation->setMorphTargets(targets);
933 morphingAnimation->setTargetName(aiStringToQString(str: mesh->mName));
934 morphingAnimation->setTarget(geometryRenderer);
935 }
936
937 qCDebug(AssimpImporterLog) << Q_FUNC_INFO << " Mesh " << aiStringToQString(str: mesh->mName)
938 << " Vertices " << mesh->mNumVertices << " Faces "
939 << mesh->mNumFaces << " Indices " << indices;
940
941 return geometryRenderer;
942}
943
944/*!
945 * Converts the provided Assimp aiTexture at \a textureIndex to a Texture
946 * \sa Texture
947 */
948QAbstractTexture *AssimpImporter::loadEmbeddedTexture(uint textureIndex)
949{
950 aiTexture *assimpTexture = m_scene->m_aiScene->mTextures[textureIndex];
951 QAbstractTexture *texture = QAbstractNodeFactory::createNode<QTexture2D>(type: "QTexture2D");
952 AssimpRawTextureImage *imageData = new AssimpRawTextureImage();
953
954 bool isCompressed = assimpTexture->mHeight == 0;
955 uint textureSize = assimpTexture->mWidth *
956 (isCompressed ? 1 : assimpTexture->mHeight);
957 // Set texture to RGBA8888
958 QByteArray textureContent;
959 textureContent.reserve(asize: textureSize * 4);
960 for (uint i = 0; i < textureSize; i++) {
961 uint idx = i * 4;
962 aiTexel texel = assimpTexture->pcData[i];
963 textureContent[idx] = texel.r;
964 textureContent[idx + 1] = texel.g;
965 textureContent[idx + 2] = texel.b;
966 textureContent[idx + 3] = texel.a;
967 }
968 imageData->setData(textureContent);
969 texture->addTextureImage(textureImage: imageData);
970
971 return texture;
972}
973
974/*!
975 * Loads the light in the current scene located at \a lightIndex.
976 */
977QAbstractLight *AssimpImporter::loadLight(uint lightIndex)
978{
979 aiLight *light = m_scene->m_aiScene->mLights[lightIndex];
980 // TODO: Implement me!
981 Q_UNUSED(light);
982 return nullptr;
983}
984
985/*!
986 * Converts the provided Assimp aiCamera in a node to a camera entity
987 */
988Qt3DCore::QEntity *AssimpImporter::loadCamera(aiNode *node)
989{
990 aiCamera *assimpCamera = nullptr;
991
992 for (uint i = 0; i < m_scene->m_aiScene->mNumCameras; ++i) {
993 auto camera = m_scene->m_aiScene->mCameras[i];
994 if (camera->mName == node->mName) {
995 assimpCamera = camera;
996 break;
997 }
998 }
999
1000 if (assimpCamera == nullptr)
1001 return nullptr;
1002
1003 QEntity *camera = QAbstractNodeFactory::createNode<Qt3DCore::QEntity>(type: "QEntity");
1004 QCameraLens *lens = QAbstractNodeFactory::createNode<QCameraLens>(type: "QCameraLens");
1005
1006 lens->setObjectName(aiStringToQString(str: assimpCamera->mName));
1007 lens->setPerspectiveProjection(fieldOfView: qRadiansToDegrees(radians: assimpCamera->mHorizontalFOV),
1008 aspect: qMax(a: assimpCamera->mAspect, b: 1.0f),
1009 nearPlane: assimpCamera->mClipPlaneNear,
1010 farPlane: assimpCamera->mClipPlaneFar);
1011 camera->addComponent(comp: lens);
1012
1013 QMatrix4x4 m;
1014 m.lookAt(eye: QVector3D(assimpCamera->mPosition.x, assimpCamera->mPosition.y, assimpCamera->mPosition.z),
1015 center: QVector3D(assimpCamera->mLookAt.x, assimpCamera->mLookAt.y, assimpCamera->mLookAt.z),
1016 up: QVector3D(assimpCamera->mUp.x, assimpCamera->mUp.y, assimpCamera->mUp.z));
1017 Qt3DCore::QTransform *transform = QAbstractNodeFactory::createNode<Qt3DCore::QTransform>(type: "QTransform");
1018 transform->setMatrix(m);
1019 camera->addComponent(comp: transform);
1020
1021 return camera;
1022}
1023
1024int findTimeIndex(const QList<float> &times, float time) {
1025 for (int i = 0; i < times.size(); i++) {
1026 if (qFuzzyCompare(p1: times[i], p2: time))
1027 return i;
1028 }
1029 return -1;
1030}
1031
1032void insertAtTime(QList<float> &positions, QList<Qt3DCore::QTransform *> &tranforms,
1033 Qt3DCore::QTransform *t, float time)
1034{
1035 if (positions.size() == 0) {
1036 positions.push_back(t: time);
1037 tranforms.push_back(t);
1038 } else if (time < positions.first()) {
1039 positions.push_front(t: time);
1040 tranforms.push_front(t);
1041 } else if (time > positions.last()) {
1042 positions.push_back(t: time);
1043 tranforms.push_back(t);
1044 } else {
1045 qWarning() << "Insert new key in the middle of the keyframe not implemented.";
1046 }
1047}
1048
1049// OPTIONAL
1050void AssimpImporter::loadAnimation(uint animationIndex)
1051{
1052 aiAnimation *assimpAnim = m_scene->m_aiScene->mAnimations[animationIndex];
1053 qCDebug(AssimpImporterLog) << "load Animation: "<< aiStringToQString(str: assimpAnim->mName);
1054 double tickScale = 1.0;
1055 if (!qFuzzyIsNull(d: assimpAnim->mTicksPerSecond))
1056 tickScale = 1.0 / assimpAnim->mTicksPerSecond;
1057
1058 /* keyframe animations */
1059 for (uint i = 0; i < assimpAnim->mNumChannels; ++i) {
1060 aiNodeAnim *nodeAnim = assimpAnim->mChannels[i];
1061 aiNode *targetNode = m_scene->m_aiScene->mRootNode->FindNode(name: nodeAnim->mNodeName);
1062
1063 Qt3DAnimation::QKeyframeAnimation *kfa = new Qt3DAnimation::QKeyframeAnimation();
1064 QList<float> positions;
1065 QList<Qt3DCore::QTransform *> transforms;
1066 if ((nodeAnim->mNumPositionKeys > 1)
1067 || !(nodeAnim->mNumPositionKeys == 1 && nodeAnim->mPositionKeys[0].mValue.x == 0
1068 && nodeAnim->mPositionKeys[0].mValue.y == 0
1069 && nodeAnim->mPositionKeys[0].mValue.z == 0)) {
1070 for (uint j = 0; j < nodeAnim->mNumPositionKeys; j++) {
1071 positions.push_back(t: nodeAnim->mPositionKeys[j].mTime);
1072 Qt3DCore::QTransform *t = new Qt3DCore::QTransform();
1073 t->setTranslation(QVector3D(nodeAnim->mPositionKeys[j].mValue.x,
1074 nodeAnim->mPositionKeys[j].mValue.y,
1075 nodeAnim->mPositionKeys[j].mValue.z));
1076 transforms.push_back(t);
1077 }
1078 }
1079 if ((nodeAnim->mNumRotationKeys > 1) ||
1080 !(nodeAnim->mNumRotationKeys == 1 && nodeAnim->mRotationKeys[0].mValue.x == 0
1081 && nodeAnim->mRotationKeys[0].mValue.y == 0
1082 && nodeAnim->mRotationKeys[0].mValue.z == 0
1083 && nodeAnim->mRotationKeys[0].mValue.w == 1)) {
1084 for (uint j = 0; j < nodeAnim->mNumRotationKeys; j++) {
1085 int index = findTimeIndex(times: positions, time: nodeAnim->mRotationKeys[j].mTime);
1086 if (index >= 0) {
1087 Qt3DCore::QTransform *t = transforms[index];
1088 t->setRotation(QQuaternion(nodeAnim->mRotationKeys[j].mValue.w,
1089 nodeAnim->mRotationKeys[j].mValue.x,
1090 nodeAnim->mRotationKeys[j].mValue.y,
1091 nodeAnim->mRotationKeys[j].mValue.z));
1092 } else {
1093 Qt3DCore::QTransform *t = new Qt3DCore::QTransform();
1094 t->setRotation(QQuaternion(nodeAnim->mRotationKeys[j].mValue.w,
1095 nodeAnim->mRotationKeys[j].mValue.x,
1096 nodeAnim->mRotationKeys[j].mValue.y,
1097 nodeAnim->mRotationKeys[j].mValue.z));
1098 insertAtTime(positions, tranforms&: transforms, t, time: nodeAnim->mRotationKeys[j].mTime);
1099 }
1100 }
1101 }
1102 if ((nodeAnim->mNumScalingKeys > 1)
1103 || !(nodeAnim->mNumScalingKeys == 1 && nodeAnim->mScalingKeys[0].mValue.x == 1
1104 && nodeAnim->mScalingKeys[0].mValue.y == 1
1105 && nodeAnim->mScalingKeys[0].mValue.z == 1)) {
1106 for (uint j = 0; j < nodeAnim->mNumScalingKeys; j++) {
1107 int index = findTimeIndex(times: positions, time: nodeAnim->mScalingKeys[j].mTime);
1108 if (index >= 0) {
1109 Qt3DCore::QTransform *t = transforms[index];
1110 t->setScale3D(QVector3D(nodeAnim->mScalingKeys[j].mValue.x,
1111 nodeAnim->mScalingKeys[j].mValue.y,
1112 nodeAnim->mScalingKeys[j].mValue.z));
1113 } else {
1114 Qt3DCore::QTransform *t = new Qt3DCore::QTransform();
1115 t->setScale3D(QVector3D(nodeAnim->mScalingKeys[j].mValue.x,
1116 nodeAnim->mScalingKeys[j].mValue.y,
1117 nodeAnim->mScalingKeys[j].mValue.z));
1118 insertAtTime(positions, tranforms&: transforms, t, time: nodeAnim->mScalingKeys[j].mTime);
1119 }
1120 }
1121 }
1122 for (int j = 0; j < positions.size(); ++j)
1123 positions[j] = positions[j] * tickScale;
1124 kfa->setFramePositions(positions);
1125 kfa->setKeyframes(transforms);
1126 kfa->setAnimationName(QString(assimpAnim->mName.C_Str()));
1127 kfa->setTargetName(QString(targetNode->mName.C_Str()));
1128 m_scene->m_animations.push_back(t: kfa);
1129 }
1130 /* mesh morph animations */
1131 for (uint i = 0; i < assimpAnim->mNumMorphMeshChannels; ++i) {
1132 aiMeshMorphAnim *morphAnim = assimpAnim->mMorphMeshChannels[i];
1133 aiNode *targetNode = m_scene->m_aiScene->mRootNode->FindNode(name: morphAnim->mName);
1134 aiMesh *mesh = m_scene->m_aiScene->mMeshes[targetNode->mMeshes[0]];
1135
1136 Qt3DAnimation::QMorphingAnimation *morphingAnimation = new Qt3DAnimation::QMorphingAnimation;
1137 QList<float> positions;
1138 positions.resize(size: morphAnim->mNumKeys);
1139 // set so that weights array is allocated to correct size in morphingAnimation
1140 morphingAnimation->setTargetPositions(positions);
1141 for (unsigned int j = 0; j < morphAnim->mNumKeys; ++j) {
1142 aiMeshMorphKey &key = morphAnim->mKeys[j];
1143 positions[j] = key.mTime * tickScale;
1144
1145 QList<float> weights;
1146 weights.resize(size: key.mNumValuesAndWeights);
1147 for (int k = 0; k < weights.size(); k++) {
1148 const unsigned int value = key.mValues[k];
1149 if (value < key.mNumValuesAndWeights)
1150 weights[value] = key.mWeights[k];
1151 }
1152 morphingAnimation->setWeights(positionIndex: j, weights);
1153 }
1154
1155 morphingAnimation->setTargetPositions(positions);
1156 morphingAnimation->setAnimationName(QString(assimpAnim->mName.C_Str()));
1157 morphingAnimation->setTargetName(QString(targetNode->mName.C_Str()));
1158 morphingAnimation->setMethod((mesh->mMethod == aiMorphingMethod_MORPH_NORMALIZED)
1159 ? Qt3DAnimation::QMorphingAnimation::Normalized
1160 : Qt3DAnimation::QMorphingAnimation::Relative);
1161 m_scene->m_morphAnimations.push_back(t: morphingAnimation);
1162 }
1163}
1164
1165/*!
1166 * Sets the object name of \a material to the name of \a assimpMaterial.
1167 */
1168void AssimpImporter::copyMaterialName(QMaterial *material, aiMaterial *assimpMaterial)
1169{
1170 aiString name;
1171 if (assimpMaterial->Get(AI_MATKEY_NAME, pOut&: name) == aiReturn_SUCCESS) {
1172 // May not be necessary
1173 // Kept for debug purposes at the moment
1174 material->setObjectName(aiStringToQString(str: name));
1175 qCDebug(AssimpImporterLog) << Q_FUNC_INFO << "Assimp Material " << material->objectName();
1176 }
1177}
1178
1179/*!
1180 * Fills \a material color properties with \a assimpMaterial color properties.
1181 */
1182void AssimpImporter::copyMaterialColorProperties(QMaterial *material, aiMaterial *assimpMaterial)
1183{
1184 aiColor3D color;
1185 if (assimpMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, pOut&: color) == aiReturn_SUCCESS)
1186 setParameterValue(name: ASSIMP_MATERIAL_DIFFUSE_COLOR, material, value: QColor::fromRgbF(r: color.r, g: color.g, b: color.b));
1187 if (assimpMaterial->Get(AI_MATKEY_COLOR_SPECULAR, pOut&: color) == aiReturn_SUCCESS)
1188 setParameterValue(name: ASSIMP_MATERIAL_SPECULAR_COLOR, material, value: QColor::fromRgbF(r: color.r, g: color.g, b: color.b));
1189 if (assimpMaterial->Get(AI_MATKEY_COLOR_AMBIENT, pOut&: color) == aiReturn_SUCCESS)
1190 setParameterValue(name: ASSIMP_MATERIAL_AMBIENT_COLOR, material, value: QColor::fromRgbF(r: color.r, g: color.g, b: color.b));
1191 if (assimpMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, pOut&: color) == aiReturn_SUCCESS)
1192 setParameterValue(name: ASSIMP_MATERIAL_EMISSIVE_COLOR, material, value: QColor::fromRgbF(r: color.r, g: color.g, b: color.b));
1193 if (assimpMaterial->Get(AI_MATKEY_COLOR_TRANSPARENT, pOut&: color) == aiReturn_SUCCESS)
1194 setParameterValue(name: ASSIMP_MATERIAL_TRANSPARENT_COLOR, material, value: QColor::fromRgbF(r: color.r, g: color.g, b: color.b));
1195 if (assimpMaterial->Get(AI_MATKEY_COLOR_REFLECTIVE, pOut&: color) == aiReturn_SUCCESS)
1196 setParameterValue(name: ASSIMP_MATERIAL_REFLECTIVE_COLOR, material, value: QColor::fromRgbF(r: color.r, g: color.g, b: color.b));
1197}
1198
1199/*!
1200 * Retrieves a \a material bool property.
1201 */
1202void AssimpImporter::copyMaterialBoolProperties(QMaterial *material, aiMaterial *assimpMaterial)
1203{
1204 int value;
1205 if (assimpMaterial->Get(AI_MATKEY_TWOSIDED, pOut&: value) == aiReturn_SUCCESS)
1206 setParameterValue(name: ASSIMP_MATERIAL_IS_TWOSIDED, material, value: (value == 0) ? false : true);
1207 if (assimpMaterial->Get(AI_MATKEY_ENABLE_WIREFRAME, pOut&: value) == aiReturn_SUCCESS)
1208 setParameterValue(name: ASSIMP_MATERIAL_IS_WIREFRAME, material, value: (value == 0) ? false : true);
1209}
1210
1211void AssimpImporter::copyMaterialShadingModel(QMaterial *material, aiMaterial *assimpMaterial)
1212{
1213 Q_UNUSED(material);
1214 Q_UNUSED(assimpMaterial);
1215 // TODO
1216 // Match each shading function with a default shader
1217
1218 // AssimpIO::assimpMaterialAttributesMap[AI_MATKEY_SHADING_MODEL] = &AssimpIO::getMaterialShadingModel;
1219 // AssimpIO::assimpMaterialAttributesMap[AI_MATKEY_BLEND_FUNC] = &AssimpIO::getMaterialBlendingFunction;
1220}
1221
1222void AssimpImporter::copyMaterialBlendingFunction(QMaterial *material, aiMaterial *assimpMaterial)
1223{
1224 Q_UNUSED(material);
1225 Q_UNUSED(assimpMaterial);
1226 // TO DO
1227}
1228
1229/*!
1230 *
1231 */
1232void AssimpImporter::copyMaterialTextures(QMaterial *material, aiMaterial *assimpMaterial)
1233{
1234 static const aiTextureType textureType[] = {aiTextureType_AMBIENT,
1235 aiTextureType_DIFFUSE,
1236 aiTextureType_DISPLACEMENT,
1237 aiTextureType_EMISSIVE,
1238 aiTextureType_HEIGHT,
1239 aiTextureType_LIGHTMAP,
1240 aiTextureType_NORMALS,
1241 aiTextureType_OPACITY,
1242 aiTextureType_REFLECTION,
1243 aiTextureType_SHININESS,
1244 aiTextureType_SPECULAR};
1245
1246 if (m_scene->m_textureToParameterName.isEmpty()) {
1247 m_scene->m_textureToParameterName.insert(key: aiTextureType_AMBIENT, value: ASSIMP_MATERIAL_AMBIENT_TEXTURE);
1248 m_scene->m_textureToParameterName.insert(key: aiTextureType_DIFFUSE, value: ASSIMP_MATERIAL_DIFFUSE_TEXTURE);
1249 m_scene->m_textureToParameterName.insert(key: aiTextureType_DISPLACEMENT, value: ASSIMP_MATERIAL_DISPLACEMENT_TEXTURE);
1250 m_scene->m_textureToParameterName.insert(key: aiTextureType_EMISSIVE, value: ASSIMP_MATERIAL_EMISSIVE_TEXTURE);
1251 m_scene->m_textureToParameterName.insert(key: aiTextureType_HEIGHT, value: ASSIMP_MATERIAL_HEIGHT_TEXTURE);
1252 m_scene->m_textureToParameterName.insert(key: aiTextureType_LIGHTMAP, value: ASSIMP_MATERIAL_LIGHTMAP_TEXTURE);
1253 m_scene->m_textureToParameterName.insert(key: aiTextureType_NORMALS, value: ASSIMP_MATERIAL_NORMALS_TEXTURE);
1254 m_scene->m_textureToParameterName.insert(key: aiTextureType_OPACITY, value: ASSIMP_MATERIAL_OPACITY_TEXTURE);
1255 m_scene->m_textureToParameterName.insert(key: aiTextureType_REFLECTION, value: ASSIMP_MATERIAL_REFLECTION_TEXTURE);
1256 m_scene->m_textureToParameterName.insert(key: aiTextureType_SHININESS, value: ASSIMP_MATERIAL_SHININESS_TEXTURE);
1257 m_scene->m_textureToParameterName.insert(key: aiTextureType_SPECULAR, value: ASSIMP_MATERIAL_SPECULAR_TEXTURE);
1258 }
1259
1260 for (unsigned int i = 0; i < sizeof(textureType)/sizeof(textureType[0]); i++) {
1261 aiString path;
1262 if (assimpMaterial->GetTexture(type: textureType[i], index: 0, path: &path) == AI_SUCCESS) {
1263 const QString fullPath = m_sceneDir.absoluteFilePath(fileName: texturePath(path));
1264 // Load texture if not already loaded
1265 QAbstractTexture *tex = QAbstractNodeFactory::createNode<QTexture2D>(type: "QTexture2D");
1266 QTextureImage *texImage = QAbstractNodeFactory::createNode<QTextureImage>(type: "QTextureImage");
1267 texImage->setSource(QUrl::fromLocalFile(localfile: fullPath));
1268 texImage->setMirrored(false);
1269 tex->addTextureImage(textureImage: texImage);
1270
1271 // Set proper wrapping mode
1272 QTextureWrapMode wrapMode(QTextureWrapMode::Repeat);
1273 int xMode = 0;
1274 int yMode = 0;
1275
1276 if (assimpMaterial->Get(AI_MATKEY_MAPPINGMODE_U(textureType[i], 0), pOut&: xMode) == aiReturn_SUCCESS)
1277 wrapMode.setX(wrapModeFromaiTextureMapMode(mode: xMode));
1278 if (assimpMaterial->Get(AI_MATKEY_MAPPINGMODE_V(textureType[i], 0), pOut&: yMode) == aiReturn_SUCCESS)
1279 wrapMode.setY(wrapModeFromaiTextureMapMode(mode: yMode));
1280
1281 tex->setWrapMode(wrapMode);
1282
1283 qCDebug(AssimpImporterLog) << Q_FUNC_INFO << " Loaded Texture " << fullPath;
1284 const QString parameterName = m_scene->m_textureToParameterName[textureType[i]];
1285 setParameterValue(name: parameterName, material, value: QVariant::fromValue(value: tex));
1286
1287 if (parameterName == ASSIMP_MATERIAL_NORMALS_TEXTURE) {
1288 setParameterValue(name: ASSIMP_MATERIAL_NORMALS_TEXTURE2, material, value: QVariant::fromValue(value: tex));
1289 }
1290 }
1291 }
1292}
1293
1294/*!
1295 * Retrieves a \a material float property.
1296 */
1297void AssimpImporter::copyMaterialFloatProperties(QMaterial *material, aiMaterial *assimpMaterial)
1298{
1299 float value = 0;
1300 if (assimpMaterial->Get(AI_MATKEY_OPACITY, pOut&: value) == aiReturn_SUCCESS)
1301 setParameterValue(name: ASSIMP_MATERIAL_OPACITY, material, value);
1302 if (assimpMaterial->Get(AI_MATKEY_SHININESS, pOut&: value) == aiReturn_SUCCESS)
1303 setParameterValue(name: ASSIMP_MATERIAL_SHININESS, material, value);
1304 if (assimpMaterial->Get(AI_MATKEY_SHININESS_STRENGTH, pOut&: value) == aiReturn_SUCCESS)
1305 setParameterValue(name: ASSIMP_MATERIAL_SHININESS_STRENGTH, material, value);
1306 if (assimpMaterial->Get(AI_MATKEY_REFRACTI, pOut&: value) == aiReturn_SUCCESS)
1307 setParameterValue(name: ASSIMP_MATERIAL_REFRACTI, material, value);
1308 if (assimpMaterial->Get(AI_MATKEY_REFLECTIVITY, pOut&: value) == aiReturn_SUCCESS)
1309 setParameterValue(name: ASSIMP_MATERIAL_REFLECTIVITY, material, value);
1310}
1311
1312AssimpRawTextureImage::AssimpRawTextureImage(QNode *parent)
1313 : QAbstractTextureImage(parent)
1314{
1315}
1316
1317QTextureImageDataGeneratorPtr AssimpRawTextureImage::dataGenerator() const
1318{
1319 return QTextureImageDataGeneratorPtr(new AssimpRawTextureImageFunctor(m_data));
1320}
1321
1322void AssimpRawTextureImage::setData(const QByteArray &data)
1323{
1324 if (data != m_data) {
1325 m_data = data;
1326 notifyDataGeneratorChanged();
1327 }
1328}
1329
1330AssimpRawTextureImage::AssimpRawTextureImageFunctor::AssimpRawTextureImageFunctor(const QByteArray &data)
1331 : QTextureImageDataGenerator()
1332 , m_data(data)
1333{
1334}
1335
1336QTextureImageDataPtr AssimpRawTextureImage::AssimpRawTextureImageFunctor::operator()()
1337{
1338 QTextureImageDataPtr dataPtr = QTextureImageDataPtr::create();
1339 // Note: we assume 4 components per pixel and not compressed for now
1340 dataPtr->setData(data: m_data, blockSize: 4, isCompressed: false);
1341 return dataPtr;
1342}
1343
1344bool AssimpRawTextureImage::AssimpRawTextureImageFunctor::operator ==(const QTextureImageDataGenerator &other) const
1345{
1346 const AssimpRawTextureImageFunctor *otherFunctor = functor_cast<AssimpRawTextureImageFunctor>(other: &other);
1347 return (otherFunctor != nullptr && otherFunctor->m_data == m_data);
1348}
1349
1350AssimpImporter::SceneImporter::SceneImporter()
1351 : m_importer(new Assimp::Importer())
1352 , m_aiScene(nullptr)
1353{
1354 // The Assimp::Importer manages the lifetime of the aiScene object
1355}
1356
1357
1358
1359AssimpImporter::SceneImporter::~SceneImporter()
1360{
1361 delete m_importer;
1362}
1363
1364} // namespace Qt3DRender
1365
1366QT_END_NAMESPACE
1367
1368#include "moc_assimpimporter.cpp"
1369
1370#include "assimpimporter.moc"
1371

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qt3d/src/plugins/sceneparsers/assimp/assimpimporter.cpp