/**************************************************************************** ** ** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "assimpimporter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE using namespace Qt3DCore; using namespace Qt3DExtras; namespace Qt3DRender { /*! \class Qt3DRender::AssimpImporter \inmodule Qt3DRender \since 5.5 \internal \brief Provides a generic way of loading various 3D assets format into a Qt3D scene. It should be noted that Assimp aiString is explicitly defined to be UTF-8. */ Q_LOGGING_CATEGORY(AssimpImporterLog, "Qt3D.AssimpImporter", QtWarningMsg) namespace { const QString ASSIMP_MATERIAL_DIFFUSE_COLOR = QLatin1String("kd"); const QString ASSIMP_MATERIAL_SPECULAR_COLOR = QLatin1String("ks"); const QString ASSIMP_MATERIAL_AMBIENT_COLOR = QLatin1String("ka"); const QString ASSIMP_MATERIAL_EMISSIVE_COLOR = QLatin1String("emissive"); const QString ASSIMP_MATERIAL_TRANSPARENT_COLOR = QLatin1String("transparent"); const QString ASSIMP_MATERIAL_REFLECTIVE_COLOR = QLatin1String("reflective"); const QString ASSIMP_MATERIAL_DIFFUSE_TEXTURE = QLatin1String("diffuseTexture"); const QString ASSIMP_MATERIAL_AMBIENT_TEXTURE = QLatin1String("ambientTex"); const QString ASSIMP_MATERIAL_SPECULAR_TEXTURE = QLatin1String("specularTexture"); const QString ASSIMP_MATERIAL_EMISSIVE_TEXTURE = QLatin1String("emissiveTex"); const QString ASSIMP_MATERIAL_NORMALS_TEXTURE = QLatin1String("normalsTex"); const QString ASSIMP_MATERIAL_OPACITY_TEXTURE = QLatin1String("opacityTex"); const QString ASSIMP_MATERIAL_REFLECTION_TEXTURE = QLatin1String("reflectionTex"); const QString ASSIMP_MATERIAL_HEIGHT_TEXTURE = QLatin1String("heightTex"); const QString ASSIMP_MATERIAL_LIGHTMAP_TEXTURE = QLatin1String("opacityTex"); const QString ASSIMP_MATERIAL_DISPLACEMENT_TEXTURE = QLatin1String("displacementTex"); const QString ASSIMP_MATERIAL_SHININESS_TEXTURE = QLatin1String("shininessTex"); const QString ASSIMP_MATERIAL_IS_TWOSIDED = QLatin1String("twosided"); const QString ASSIMP_MATERIAL_IS_WIREFRAME = QLatin1String("wireframe"); const QString ASSIMP_MATERIAL_OPACITY = QLatin1String("opacity"); const QString ASSIMP_MATERIAL_SHININESS = QLatin1String("shininess"); const QString ASSIMP_MATERIAL_SHININESS_STRENGTH = QLatin1String("shininess_strength"); const QString ASSIMP_MATERIAL_REFRACTI = QLatin1String("refracti"); const QString ASSIMP_MATERIAL_REFLECTIVITY = QLatin1String("reflectivity"); const QString ASSIMP_MATERIAL_NAME = QLatin1String("name"); const QString VERTICES_ATTRIBUTE_NAME = QAttribute::defaultPositionAttributeName(); const QString NORMAL_ATTRIBUTE_NAME = QAttribute::defaultNormalAttributeName(); const QString TANGENT_ATTRIBUTE_NAME = QAttribute::defaultTangentAttributeName(); const QString TEXTCOORD_ATTRIBUTE_NAME = QAttribute::defaultTextureCoordinateAttributeName(); const QString COLOR_ATTRIBUTE_NAME = QAttribute::defaultColorAttributeName(); /* * Returns a QMatrix4x4 from \a matrix; */ QMatrix4x4 aiMatrix4x4ToQMatrix4x4(const aiMatrix4x4 &matrix) Q_DECL_NOTHROW { return QMatrix4x4(matrix.a1, matrix.a2, matrix.a3, matrix.a4, matrix.b1, matrix.b2, matrix.b3, matrix.b4, matrix.c1, matrix.c2, matrix.c3, matrix.c4, matrix.d1, matrix.d2, matrix.d3, matrix.d4); } /* * Returns a QString from \a str; */ inline QString aiStringToQString(const aiString &str) { return QString::fromUtf8(str.data, int(str.length)); } QMaterial *createBestApproachingMaterial(const aiMaterial *assimpMaterial) { aiString path; // unused but necessary const bool hasDiffuseTexture = (assimpMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS); const bool hasSpecularTexture = (assimpMaterial->GetTexture(aiTextureType_SPECULAR, 0, &path) == AI_SUCCESS); if (hasDiffuseTexture && hasSpecularTexture) return QAbstractNodeFactory::createNode("QDiffuseSpecularMapMaterial"); if (hasDiffuseTexture) return QAbstractNodeFactory::createNode("QDiffuseMapMaterial"); return QAbstractNodeFactory::createNode("QPhongMaterial"); } QString texturePath(const aiString &path) { QString p = aiStringToQString(path); if (p.startsWith('/')) p.remove(0, 1); return p; } /* * Returns the Qt3DRender::QParameter with named \a name if contained by the material * \a material. If the material doesn't contain the named parameter, a new * Qt3DRender::QParameter is created and inserted into the material. */ QParameter *findNamedParameter(const QString &name, QMaterial *material) { // Does the material contain the parameter ? const auto params = material->parameters(); for (QParameter *p : params) { if (p->name() == name) return p; } // Does the material's effect contain the parameter ? if (material->effect()) { const QEffect *e = material->effect(); const auto params = e->parameters(); for (QParameter *p : params) { if (p->name() == name) return p; } } // Create and add parameter to material QParameter *p = QAbstractNodeFactory::createNode("QParameter"); p->setParent(material); p->setName(name); material->addParameter(p); return p; } void setParameterValue(const QString &name, QMaterial *material, const QVariant &value) { QParameter *p = findNamedParameter(name, material); p->setValue(value); } QAttribute *createAttribute(QBuffer *buffer, const QString &name, QAttribute::VertexBaseType vertexBaseType, uint vertexSize, uint count, uint byteOffset = 0, uint byteStride = 0, QNode *parent = nullptr) { QAttribute *attribute = QAbstractNodeFactory::createNode("QAttribute"); attribute->setBuffer(buffer); attribute->setName(name); attribute->setVertexBaseType(vertexBaseType); attribute->setVertexSize(vertexSize); attribute->setCount(count); attribute->setByteOffset(byteOffset); attribute->setByteStride(byteStride); attribute->setParent(parent); return attribute; } QAttribute *createIndexAttribute(QBuffer *buffer, QAttribute::VertexBaseType vertexBaseType, uint vertexSize, uint count, uint byteOffset = 0, uint byteStride = 0, QNode *parent = nullptr) { QAttribute *attribute = QAbstractNodeFactory::createNode("QAttribute"); attribute->setBuffer(buffer); attribute->setVertexBaseType(vertexBaseType); attribute->setVertexSize(vertexSize); attribute->setCount(count); attribute->setByteOffset(byteOffset); attribute->setByteStride(byteStride); attribute->setParent(parent); return attribute; } QTextureWrapMode::WrapMode wrapModeFromaiTextureMapMode(int mode) { switch (mode) { case aiTextureMapMode_Wrap: return QTextureWrapMode::Repeat; case aiTextureMapMode_Mirror: return QTextureWrapMode::MirroredRepeat; case aiTextureMapMode_Decal: return QTextureWrapMode::ClampToBorder; case aiTextureMapMode_Clamp: default: return QTextureWrapMode::ClampToEdge; } } } // anonymous QStringList AssimpImporter::assimpSupportedFormatsList = AssimpImporter::assimpSupportedFormats(); /*! * Returns a QStringlist with the suffixes of the various supported asset formats. */ QStringList AssimpImporter::assimpSupportedFormats() { QStringList formats; formats.reserve(60); formats.append(QStringLiteral("3d")); formats.append(QStringLiteral("3ds")); formats.append(QStringLiteral("ac")); formats.append(QStringLiteral("ac3d")); formats.append(QStringLiteral("acc")); formats.append(QStringLiteral("ase")); formats.append(QStringLiteral("ask")); formats.append(QStringLiteral("assbin")); formats.append(QStringLiteral("b3d")); formats.append(QStringLiteral("blend")); formats.append(QStringLiteral("bvh")); formats.append(QStringLiteral("cob")); formats.append(QStringLiteral("csm")); formats.append(QStringLiteral("dae")); formats.append(QStringLiteral("dxf")); formats.append(QStringLiteral("enff")); formats.append(QStringLiteral("fbx")); formats.append(QStringLiteral("hmp")); formats.append(QStringLiteral("irr")); formats.append(QStringLiteral("irrmesh")); formats.append(QStringLiteral("lwo")); formats.append(QStringLiteral("lws")); formats.append(QStringLiteral("lxo")); formats.append(QStringLiteral("md2")); formats.append(QStringLiteral("md3")); formats.append(QStringLiteral("md5anim")); formats.append(QStringLiteral("md5camera")); formats.append(QStringLiteral("md5mesh")); formats.append(QStringLiteral("mdc")); formats.append(QStringLiteral("mdl")); formats.append(QStringLiteral("mesh.xml")); formats.append(QStringLiteral("mot")); formats.append(QStringLiteral("ms3d")); formats.append(QStringLiteral("ndo")); formats.append(QStringLiteral("nff")); formats.append(QStringLiteral("obj")); formats.append(QStringLiteral("off")); formats.append(QStringLiteral("ogex")); formats.append(QStringLiteral("pk3")); formats.append(QStringLiteral("ply")); formats.append(QStringLiteral("prj")); formats.append(QStringLiteral("q3o")); formats.append(QStringLiteral("q3s")); formats.append(QStringLiteral("raw")); formats.append(QStringLiteral("scn")); formats.append(QStringLiteral("sib")); formats.append(QStringLiteral("smd")); formats.append(QStringLiteral("stl")); formats.append(QStringLiteral("ter")); formats.append(QStringLiteral("uc")); formats.append(QStringLiteral("vta")); formats.append(QStringLiteral("x")); formats.append(QStringLiteral("xml")); return formats; } class AssimpRawTextureImage : public QAbstractTextureImage { Q_OBJECT public: explicit AssimpRawTextureImage(QNode *parent = 0); QTextureImageDataGeneratorPtr dataGenerator() const final; void setData(const QByteArray &data); private: QByteArray m_data; class AssimpRawTextureImageFunctor : public QTextureImageDataGenerator { public: explicit AssimpRawTextureImageFunctor(const QByteArray &data); QTextureImageDataPtr operator()() final; bool operator ==(const QTextureImageDataGenerator &other) const final; QT3D_FUNCTOR(AssimpRawTextureImageFunctor) private: QByteArray m_data; }; }; /*! * Constructor. Initializes a new instance of AssimpImporter. */ AssimpImporter::AssimpImporter() : QSceneImporter(), m_sceneParsed(false), m_scene(nullptr) { } /*! * Destructor. Cleans the parser properly before destroying it. */ AssimpImporter::~AssimpImporter() { cleanup(); } /*! * Returns \c true if the extensions are supported * by the Assimp Assets importer. */ bool AssimpImporter::areAssimpExtensions(const QStringList &extensions) { for (const auto &ext : qAsConst(extensions)) if (AssimpImporter::assimpSupportedFormatsList.contains(ext.toLower())) return true; return false; } /*! * Sets the \a source used by the parser to load the asset file. * If the file is valid, this will trigger parsing of the file. */ void AssimpImporter::setSource(const QUrl &source) { const QString path = QUrlHelper::urlToLocalFileOrQrc(source); QFileInfo file(path); m_sceneDir = file.absoluteDir(); if (!file.exists()) { qCWarning(AssimpImporterLog) << "File missing " << path; return ; } readSceneFile(path); } /*! * Sets the \a basePath used by the parser to load the asset file. * If the file specified in \a data is valid, this will trigger parsing of the file. */ void AssimpImporter::setData(const QByteArray &data, const QString &basePath) { readSceneData(data, basePath); } /*! * Returns \c true if the extension in QStringList \a extensions is supported by * the assimp parser. */ bool AssimpImporter::areFileTypesSupported(const QStringList &extensions) const { return AssimpImporter::areAssimpExtensions(extensions); } /*! * Returns a Entity node which is the root node of the scene * node specified by \a id. If \a id is empty, the scene is assumed to be * the root node of the scene. * * Returns \c nullptr if \a id was specified but no node matching it was found. */ Qt3DCore::QEntity *AssimpImporter::scene(const QString &id) { // m_aiScene shouldn't be null. // If it is either, the file failed to be imported or // setFilePath was not called if (m_scene == nullptr || m_scene->m_aiScene == nullptr) return nullptr; aiNode *rootNode = m_scene->m_aiScene->mRootNode; // if id specified, tries to find node if (!id.isEmpty() && !(rootNode = rootNode->FindNode(id.toUtf8().constData()))) { qCDebug(AssimpImporterLog) << Q_FUNC_INFO << " Couldn't find requested scene node"; return nullptr; } // Builds the Qt3D scene using the Assimp aiScene // and the various dicts filled previously by parse Qt3DCore::QEntity *n = node(rootNode); if (m_scene->m_animations.size() > 0) { qWarning() << "No target found for " << m_scene->m_animations.size() << " animations!"; for (Qt3DAnimation::QKeyframeAnimation *anim : qAsConst(m_scene->m_animations)) delete anim; m_scene->m_animations.clear(); } return n; } /*! * Returns a Node from the scene identified by \a id. * Returns \c nullptr if the node was not found. */ Qt3DCore::QEntity *AssimpImporter::node(const QString &id) { if (m_scene == nullptr || m_scene->m_aiScene == nullptr) return nullptr; parse(); aiNode *n = m_scene->m_aiScene->mRootNode->FindNode(id.toUtf8().constData()); return node(n); } template void findAnimationsForNode(QVector &animations, QVector &result, const QString &name) { for (T *anim : animations) { if (anim->targetName() == name) { result.push_back(anim); animations.removeAll(anim); } } } /*! * Returns a Node from an Assimp aiNode \a node. */ Qt3DCore::QEntity *AssimpImporter::node(aiNode *node) { if (node == nullptr) return nullptr; QEntity *entityNode = QAbstractNodeFactory::createNode("QEntity"); entityNode->setObjectName(aiStringToQString(node->mName)); // Add Meshes to the node for (uint i = 0; i < node->mNumMeshes; i++) { uint meshIndex = node->mMeshes[i]; QGeometryRenderer *mesh = loadMesh(meshIndex); // mesh material QMaterial *material; QList morphingAnimations = mesh->findChildren(); if (morphingAnimations.size() > 0) { material = new Qt3DExtras::QMorphPhongMaterial(entityNode); QVector animations; findAnimationsForNode(m_scene->m_morphAnimations, animations, aiStringToQString(node->mName)); const auto morphTargetList = morphingAnimations.at(0)->morphTargetList(); for (Qt3DAnimation::QMorphingAnimation *anim : qAsConst(animations)) { anim->setParent(entityNode); anim->setTarget(mesh); anim->setMorphTargets(morphTargetList); } for (int j = 0; j < animations.size(); ++j) { QObject::connect(animations[j], &Qt3DAnimation::QMorphingAnimation::interpolatorChanged, (Qt3DExtras::QMorphPhongMaterial *)material, &Qt3DExtras::QMorphPhongMaterial::setInterpolator); } morphingAnimations[0]->deleteLater(); } else { uint materialIndex = m_scene->m_aiScene->mMeshes[meshIndex]->mMaterialIndex; material = loadMaterial(materialIndex); } if (node->mNumMeshes == 1) { if (material) entityNode->addComponent(material); // mesh entityNode->addComponent(mesh); } else { QEntity *childEntity = QAbstractNodeFactory::createNode("QEntity"); childEntity->setObjectName(entityNode->objectName() + QLatin1String("_Child") + QString::number(i)); if (material) childEntity->addComponent(material); childEntity->addComponent(mesh); childEntity->setParent(entityNode); Qt3DCore::QTransform *transform = QAbstractNodeFactory::createNode("QTransform"); childEntity->addComponent(transform); } } // Add Children to Node for (uint i = 0; i < node->mNumChildren; i++) { // this-> is necessary here otherwise // it conflicts with the variable node QEntity *child = this->node(node->mChildren[i]); // Are we sure each child are unique ??? if (child != nullptr) child->setParent(entityNode); } // Add Transformations const QMatrix4x4 qTransformMatrix = aiMatrix4x4ToQMatrix4x4(node->mTransformation); Qt3DCore::QTransform *transform = QAbstractNodeFactory::createNode("QTransform"); transform->setMatrix(qTransformMatrix); entityNode->addComponent(transform); QVector animations; findAnimationsForNode(m_scene->m_animations, animations, aiStringToQString(node->mName)); for (Qt3DAnimation::QKeyframeAnimation *anim : qAsConst(animations)) { anim->setTarget(transform); anim->setParent(entityNode); } // Add Camera auto camera = loadCamera(node); if (camera) camera->setParent(entityNode); // TO DO : Add lights .... return entityNode; } /*! * Reads the scene file pointed by \a path and launches the parsing of * the scene using Assimp, after having cleaned up previously saved values * from eventual previous parsings. */ void AssimpImporter::readSceneFile(const QString &path) { cleanup(); m_scene = new SceneImporter(); // SET THIS TO REMOVE POINTS AND LINES -> HAVE ONLY TRIANGLES m_scene->m_importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE|aiPrimitiveType_POINT); // SET CUSTOM FILE HANDLER TO HANDLE FILE READING THROUGH QT (RESOURCES, SOCKET ...) m_scene->m_importer->SetIOHandler(new AssimpHelper::AssimpIOSystem()); // type and aiProcess_Triangulate discompose polygons with more than 3 points in triangles // aiProcess_SortByPType makes sure that meshes data are triangles m_scene->m_aiScene = m_scene->m_importer->ReadFile(path.toUtf8().constData(), aiProcess_SortByPType| aiProcess_Triangulate| aiProcess_GenSmoothNormals| aiProcess_FlipUVs); if (m_scene->m_aiScene == nullptr) { qCWarning(AssimpImporterLog) << "Assimp scene import failed" << m_scene->m_importer->GetErrorString(); QSceneImporter::logError(QString::fromUtf8(m_scene->m_importer->GetErrorString())); return ; } parse(); } /*! * Reads the scene file pointed by \a path and launches the parsing of * the scene using Assimp, after having cleaned up previously saved values * from eventual previous parsings. */ void AssimpImporter::readSceneData(const QByteArray& data, const QString &basePath) { Q_UNUSED(basePath); cleanup(); m_scene = new SceneImporter(); // SET THIS TO REMOVE POINTS AND LINES -> HAVE ONLY TRIANGLES m_scene->m_importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE|aiPrimitiveType_POINT); // SET CUSTOM FILE HANDLER TO HANDLE FILE READING THROUGH QT (RESOURCES, SOCKET ...) m_scene->m_importer->SetIOHandler(new AssimpHelper::AssimpIOSystem()); // type and aiProcess_Triangulate discompose polygons with more than 3 points in triangles // aiProcess_SortByPType makes sure that meshes data are triangles m_scene->m_aiScene = m_scene->m_importer->ReadFileFromMemory(data.data(), data.size(), aiProcess_SortByPType| aiProcess_Triangulate| aiProcess_GenSmoothNormals| aiProcess_FlipUVs); if (m_scene->m_aiScene == nullptr) { qCWarning(AssimpImporterLog) << "Assimp scene import failed"; return ; } parse(); } /*! * Cleans the various dictionaries holding the scene's information. */ void AssimpImporter::cleanup() { m_sceneParsed = false; delete m_scene; m_scene = nullptr; } /*! * Parses the aiScene provided py Assimp and converts Assimp * values to Qt3D values. */ void AssimpImporter::parse() { if (!m_sceneParsed) { // Set parsed flags m_sceneParsed = !m_sceneParsed; for (uint i = 0; i < m_scene->m_aiScene->mNumAnimations; i++) loadAnimation(i); } } /*! * Converts the provided Assimp aiMaterial identified by \a materialIndex to a * Qt3D material * \sa Material */ QMaterial *AssimpImporter::loadMaterial(uint materialIndex) { // Generates default material based on what the assimp material contains aiMaterial *assimpMaterial = m_scene->m_aiScene->mMaterials[materialIndex]; QMaterial *material = createBestApproachingMaterial(assimpMaterial); // Material Name copyMaterialName(material, assimpMaterial); copyMaterialColorProperties(material, assimpMaterial); copyMaterialBoolProperties(material, assimpMaterial); copyMaterialFloatProperties(material, assimpMaterial); // Add textures to materials dict copyMaterialTextures(material, assimpMaterial); return material; } /*! * Converts the Assimp aiMesh mesh identified by \a meshIndex to a QGeometryRenderer * \sa QGeometryRenderer */ QGeometryRenderer *AssimpImporter::loadMesh(uint meshIndex) { aiMesh *mesh = m_scene->m_aiScene->mMeshes[meshIndex]; QGeometryRenderer *geometryRenderer = QAbstractNodeFactory::createNode("QGeometryRenderer"); QGeometry *meshGeometry = QAbstractNodeFactory::createNode("QGeometry"); meshGeometry->setParent(geometryRenderer); Qt3DRender::QBuffer *vertexBuffer = QAbstractNodeFactory::createNode("QBuffer"); vertexBuffer->setParent(meshGeometry); vertexBuffer->setType(Qt3DRender::QBuffer::VertexBuffer); Qt3DRender::QBuffer *indexBuffer = QAbstractNodeFactory::createNode("QBuffer"); indexBuffer->setParent(meshGeometry); indexBuffer->setType(Qt3DRender::QBuffer::IndexBuffer); geometryRenderer->setGeometry(meshGeometry); // Primitive are always triangles with the current Assimp's configuration // Vertices and Normals always present with the current Assimp's configuration aiVector3D *vertices = mesh->mVertices; aiVector3D *normals = mesh->mNormals; aiColor4D *colors = mesh->mColors[0]; // Tangents and TextureCoord not always present bool hasTangent = mesh->HasTangentsAndBitangents(); bool hasTexture = mesh->HasTextureCoords(0); bool hasColor = (colors != NULL); // NULL defined by Assimp aiVector3D *tangents = hasTangent ? mesh->mTangents : nullptr; aiVector3D *textureCoord = hasTexture ? mesh->mTextureCoords[0] : nullptr; // Add values in raw float array ushort chunkSize = 6 + (hasTangent ? 3 : 0) + (hasTexture ? 2 : 0) + (hasColor ? 4 : 0); QByteArray bufferArray; bufferArray.resize(chunkSize * mesh->mNumVertices * sizeof(float)); float *vbufferContent = reinterpret_cast(bufferArray.data()); for (uint i = 0; i < mesh->mNumVertices; i++) { uint idx = i * chunkSize; // position vbufferContent[idx] = vertices[i].x; vbufferContent[idx + 1] = vertices[i].y; vbufferContent[idx + 2] = vertices[i].z; // normals vbufferContent[idx + 3] = normals[i].x; vbufferContent[idx + 4] = normals[i].y; vbufferContent[idx + 5] = normals[i].z; if (hasTangent) { vbufferContent[idx + 6] = tangents[i].x; vbufferContent[idx + 7] = tangents[i].y; vbufferContent[idx + 8] = tangents[i].z; } if (hasTexture) { char offset = (hasTangent ? 9 : 6); vbufferContent[idx + offset] = textureCoord[i].x; vbufferContent[idx + offset + 1] = textureCoord[i].y; } if (hasColor) { char offset = 6 + (hasTangent ? 3 : 0) + (hasTexture ? 2 : 0); vbufferContent[idx + offset] = colors[i].r; vbufferContent[idx + offset + 1] = colors[i].g; vbufferContent[idx + offset + 2] = colors[i].b; vbufferContent[idx + offset + 3] = colors[i].a; } } vertexBuffer->setData(bufferArray); // Add vertex attributes to the mesh with the right array QAttribute *positionAttribute = createAttribute(vertexBuffer, VERTICES_ATTRIBUTE_NAME, QAttribute::Float, 3, mesh->mNumVertices, 0, chunkSize * sizeof(float)); QAttribute *normalAttribute = createAttribute(vertexBuffer, NORMAL_ATTRIBUTE_NAME, QAttribute::Float, 3, mesh->mNumVertices, 3 * sizeof(float), chunkSize * sizeof(float)); meshGeometry->addAttribute(positionAttribute); meshGeometry->addAttribute(normalAttribute); if (hasTangent) { QAttribute *tangentsAttribute = createAttribute(vertexBuffer, TANGENT_ATTRIBUTE_NAME, QAttribute::Float, 3, mesh->mNumVertices, 6 * sizeof(float), chunkSize * sizeof(float)); meshGeometry->addAttribute(tangentsAttribute); } if (hasTexture) { QAttribute *textureCoordAttribute = createAttribute(vertexBuffer, TEXTCOORD_ATTRIBUTE_NAME, QAttribute::Float, 2, mesh->mNumVertices, (hasTangent ? 9 : 6) * sizeof(float), chunkSize * sizeof(float)); meshGeometry->addAttribute(textureCoordAttribute); } if (hasColor) { QAttribute *colorAttribute = createAttribute(vertexBuffer, COLOR_ATTRIBUTE_NAME, QAttribute::Float, 4, mesh->mNumVertices, (6 + (hasTangent ? 3 : 0) + (hasTexture ? 2 : 0)) * sizeof(float), chunkSize * sizeof(float)); meshGeometry->addAttribute(colorAttribute); } QAttribute::VertexBaseType indiceType; QByteArray ibufferContent; uint indices = mesh->mNumFaces * 3; // If there are less than 65535 indices, indices can then fit in ushort // which saves video memory if (indices >= USHRT_MAX) { indiceType = QAttribute::UnsignedInt; ibufferContent.resize(indices * sizeof(quint32)); for (uint i = 0; i < mesh->mNumFaces; i++) { aiFace face = mesh->mFaces[i]; Q_ASSERT(face.mNumIndices == 3); memcpy(&reinterpret_cast(ibufferContent.data())[i * 3], face.mIndices, 3 * sizeof(uint)); } } else { indiceType = QAttribute::UnsignedShort; ibufferContent.resize(indices * sizeof(quint16)); for (uint i = 0; i < mesh->mNumFaces; i++) { aiFace face = mesh->mFaces[i]; Q_ASSERT(face.mNumIndices == 3); for (ushort j = 0; j < face.mNumIndices; j++) reinterpret_cast(ibufferContent.data())[i * 3 + j] = face.mIndices[j]; } } indexBuffer->setData(ibufferContent); // Add indices attributes QAttribute *indexAttribute = createIndexAttribute(indexBuffer, indiceType, 1, indices); indexAttribute->setAttributeType(QAttribute::IndexAttribute); meshGeometry->addAttribute(indexAttribute); if (mesh->mNumAnimMeshes > 0) { aiAnimMesh *animesh = mesh->mAnimMeshes[0]; if (animesh->mNumVertices != mesh->mNumVertices) return geometryRenderer; Qt3DAnimation::QMorphingAnimation *morphingAnimation = new Qt3DAnimation::QMorphingAnimation(geometryRenderer); QVector names; QVector targets; uint voff = 0; uint noff = 0; uint tanoff = 0; uint texoff = 0; uint coloff = 0; uint offset = 0; if (animesh->mVertices) { names.push_back(VERTICES_ATTRIBUTE_NAME); offset += 3; } if (animesh->mNormals) { names.push_back(NORMAL_ATTRIBUTE_NAME); noff = offset; offset += 3; } if (animesh->mTangents) { names.push_back(TANGENT_ATTRIBUTE_NAME); tanoff = offset; offset += 3; } if (animesh->mTextureCoords[0]) { names.push_back(TEXTCOORD_ATTRIBUTE_NAME); texoff = offset; offset += 2; } if (animesh->mColors[0]) { names.push_back(COLOR_ATTRIBUTE_NAME); coloff = offset; } ushort clumpSize = (animesh->mVertices ? 3 : 0) + (animesh->mNormals ? 3 : 0) + (animesh->mTangents ? 3 : 0) + (animesh->mColors[0] ? 4 : 0) + (animesh->mTextureCoords[0] ? 2 : 0); for (uint i = 0; i < mesh->mNumAnimMeshes; i++) { aiAnimMesh *animesh = mesh->mAnimMeshes[i]; Qt3DAnimation::QMorphTarget *target = new Qt3DAnimation::QMorphTarget(geometryRenderer); targets.push_back(target); QVector attributes; QByteArray targetBufferArray; targetBufferArray.resize(clumpSize * mesh->mNumVertices * sizeof(float)); float *dst = reinterpret_cast(targetBufferArray.data()); for (uint j = 0; j < mesh->mNumVertices; j++) { if (animesh->mVertices) { *dst++ = animesh->mVertices[j].x; *dst++ = animesh->mVertices[j].y; *dst++ = animesh->mVertices[j].z; } if (animesh->mNormals) { *dst++ = animesh->mNormals[j].x; *dst++ = animesh->mNormals[j].y; *dst++ = animesh->mNormals[j].z; } if (animesh->mTangents) { *dst++ = animesh->mTangents[j].x; *dst++ = animesh->mTangents[j].y; *dst++ = animesh->mTangents[j].z; } if (animesh->mTextureCoords[0]) { *dst++ = animesh->mTextureCoords[0][j].x; *dst++ = animesh->mTextureCoords[0][j].y; } if (animesh->mColors[0]) { *dst++ = animesh->mColors[0][j].r; *dst++ = animesh->mColors[0][j].g; *dst++ = animesh->mColors[0][j].b; *dst++ = animesh->mColors[0][j].a; } } Qt3DRender::QBuffer *targetBuffer = QAbstractNodeFactory::createNode("QBuffer"); targetBuffer->setData(targetBufferArray); targetBuffer->setParent(meshGeometry); if (animesh->mVertices) { attributes.push_back(createAttribute(targetBuffer, VERTICES_ATTRIBUTE_NAME, QAttribute::Float, 3, animesh->mNumVertices, voff * sizeof(float), clumpSize * sizeof(float), meshGeometry)); } if (animesh->mNormals) { attributes.push_back(createAttribute(targetBuffer, NORMAL_ATTRIBUTE_NAME, QAttribute::Float, 3, animesh->mNumVertices, noff * sizeof(float), clumpSize * sizeof(float), meshGeometry)); } if (animesh->mTangents) { attributes.push_back(createAttribute(targetBuffer, TANGENT_ATTRIBUTE_NAME, QAttribute::Float, 3, animesh->mNumVertices, tanoff * sizeof(float), clumpSize * sizeof(float), meshGeometry)); } if (animesh->mTextureCoords[0]) { attributes.push_back(createAttribute(targetBuffer, TEXTCOORD_ATTRIBUTE_NAME, QAttribute::Float, 2, animesh->mNumVertices, texoff * sizeof(float), clumpSize * sizeof(float), meshGeometry)); } if (animesh->mColors[0]) { attributes.push_back(createAttribute(targetBuffer, COLOR_ATTRIBUTE_NAME, QAttribute::Float, 4, animesh->mNumVertices, coloff * sizeof(float), clumpSize * sizeof(float), meshGeometry)); } target->setAttributes(attributes); } morphingAnimation->setMorphTargets(targets); morphingAnimation->setTargetName(aiStringToQString(mesh->mName)); morphingAnimation->setTarget(geometryRenderer); } qCDebug(AssimpImporterLog) << Q_FUNC_INFO << " Mesh " << aiStringToQString(mesh->mName) << " Vertices " << mesh->mNumVertices << " Faces " << mesh->mNumFaces << " Indices " << indices; return geometryRenderer; } /*! * Converts the provided Assimp aiTexture at \a textureIndex to a Texture * \sa Texture */ QAbstractTexture *AssimpImporter::loadEmbeddedTexture(uint textureIndex) { aiTexture *assimpTexture = m_scene->m_aiScene->mTextures[textureIndex]; QAbstractTexture *texture = QAbstractNodeFactory::createNode("QTexture2D"); AssimpRawTextureImage *imageData = new AssimpRawTextureImage(); bool isCompressed = assimpTexture->mHeight == 0; uint textureSize = assimpTexture->mWidth * (isCompressed ? 1 : assimpTexture->mHeight); // Set texture to RGBA8888 QByteArray textureContent; textureContent.reserve(textureSize * 4); for (uint i = 0; i < textureSize; i++) { uint idx = i * 4; aiTexel texel = assimpTexture->pcData[i]; textureContent[idx] = texel.r; textureContent[idx + 1] = texel.g; textureContent[idx + 2] = texel.b; textureContent[idx + 3] = texel.a; } imageData->setData(textureContent); texture->addTextureImage(imageData); return texture; } /*! * Loads the light in the current scene located at \a lightIndex. */ QAbstractLight *AssimpImporter::loadLight(uint lightIndex) { aiLight *light = m_scene->m_aiScene->mLights[lightIndex]; // TODO: Implement me! Q_UNUSED(light); return nullptr; } /*! * Converts the provided Assimp aiCamera in a node to a camera entity */ Qt3DCore::QEntity *AssimpImporter::loadCamera(aiNode *node) { aiCamera *assimpCamera = nullptr; for (uint i = 0; i < m_scene->m_aiScene->mNumCameras; ++i) { auto camera = m_scene->m_aiScene->mCameras[i]; if (camera->mName == node->mName) { assimpCamera = camera; break; } } if (assimpCamera == nullptr) return nullptr; QEntity *camera = QAbstractNodeFactory::createNode("QEntity"); QCameraLens *lens = QAbstractNodeFactory::createNode("QCameraLens"); lens->setObjectName(aiStringToQString(assimpCamera->mName)); lens->setPerspectiveProjection(qRadiansToDegrees(assimpCamera->mHorizontalFOV), qMax(assimpCamera->mAspect, 1.0f), assimpCamera->mClipPlaneNear, assimpCamera->mClipPlaneFar); camera->addComponent(lens); QMatrix4x4 m; m.lookAt(QVector3D(assimpCamera->mPosition.x, assimpCamera->mPosition.y, assimpCamera->mPosition.z), QVector3D(assimpCamera->mLookAt.x, assimpCamera->mLookAt.y, assimpCamera->mLookAt.z), QVector3D(assimpCamera->mUp.x, assimpCamera->mUp.y, assimpCamera->mUp.z)); Qt3DCore::QTransform *transform = QAbstractNodeFactory::createNode("QTransform"); transform->setMatrix(m); camera->addComponent(transform); return camera; } int findTimeIndex(const QVector ×, float time) { for (int i = 0; i < times.size(); i++) { if (qFuzzyCompare(times[i], time)) return i; } return -1; } void insertAtTime(QVector &positions, QVector &tranforms, Qt3DCore::QTransform *t, float time) { if (positions.size() == 0) { positions.push_back(time); tranforms.push_back(t); } else if (time < positions.first()) { positions.push_front(time); tranforms.push_front(t); } else if (time > positions.last()) { positions.push_back(time); tranforms.push_back(t); } else { qWarning() << "Insert new key in the middle of the keyframe not implemented."; } } // OPTIONAL void AssimpImporter::loadAnimation(uint animationIndex) { aiAnimation *assimpAnim = m_scene->m_aiScene->mAnimations[animationIndex]; qCDebug(AssimpImporterLog) << "load Animation: "<< aiStringToQString(assimpAnim->mName); double tickScale = 1.0; if (!qFuzzyIsNull(assimpAnim->mTicksPerSecond)) tickScale = 1.0 / assimpAnim->mTicksPerSecond; /* keyframe animations */ for (uint i = 0; i < assimpAnim->mNumChannels; ++i) { aiNodeAnim *nodeAnim = assimpAnim->mChannels[i]; aiNode *targetNode = m_scene->m_aiScene->mRootNode->FindNode(nodeAnim->mNodeName); Qt3DAnimation::QKeyframeAnimation *kfa = new Qt3DAnimation::QKeyframeAnimation(); QVector positions; QVector transforms; if ((nodeAnim->mNumPositionKeys > 1) || !(nodeAnim->mNumPositionKeys == 1 && nodeAnim->mPositionKeys[0].mValue.x == 0 && nodeAnim->mPositionKeys[0].mValue.y == 0 && nodeAnim->mPositionKeys[0].mValue.z == 0)) { for (uint j = 0; j < nodeAnim->mNumPositionKeys; j++) { positions.push_back(nodeAnim->mPositionKeys[j].mTime); Qt3DCore::QTransform *t = new Qt3DCore::QTransform(); t->setTranslation(QVector3D(nodeAnim->mPositionKeys[j].mValue.x, nodeAnim->mPositionKeys[j].mValue.y, nodeAnim->mPositionKeys[j].mValue.z)); transforms.push_back(t); } } if ((nodeAnim->mNumRotationKeys > 1) || !(nodeAnim->mNumRotationKeys == 1 && nodeAnim->mRotationKeys[0].mValue.x == 0 && nodeAnim->mRotationKeys[0].mValue.y == 0 && nodeAnim->mRotationKeys[0].mValue.z == 0 && nodeAnim->mRotationKeys[0].mValue.w == 1)) { for (uint j = 0; j < nodeAnim->mNumRotationKeys; j++) { int index = findTimeIndex(positions, nodeAnim->mRotationKeys[j].mTime); if (index >= 0) { Qt3DCore::QTransform *t = transforms[index]; t->setRotation(QQuaternion(nodeAnim->mRotationKeys[j].mValue.w, nodeAnim->mRotationKeys[j].mValue.x, nodeAnim->mRotationKeys[j].mValue.y, nodeAnim->mRotationKeys[j].mValue.z)); } else { Qt3DCore::QTransform *t = new Qt3DCore::QTransform(); t->setRotation(QQuaternion(nodeAnim->mRotationKeys[j].mValue.w, nodeAnim->mRotationKeys[j].mValue.x, nodeAnim->mRotationKeys[j].mValue.y, nodeAnim->mRotationKeys[j].mValue.z)); insertAtTime(positions, transforms, t, nodeAnim->mRotationKeys[j].mTime); } } } if ((nodeAnim->mNumScalingKeys > 1) || !(nodeAnim->mNumScalingKeys == 1 && nodeAnim->mScalingKeys[0].mValue.x == 1 && nodeAnim->mScalingKeys[0].mValue.y == 1 && nodeAnim->mScalingKeys[0].mValue.z == 1)) { for (uint j = 0; j < nodeAnim->mNumScalingKeys; j++) { int index = findTimeIndex(positions, nodeAnim->mScalingKeys[j].mTime); if (index >= 0) { Qt3DCore::QTransform *t = transforms[index]; t->setScale3D(QVector3D(nodeAnim->mScalingKeys[j].mValue.x, nodeAnim->mScalingKeys[j].mValue.y, nodeAnim->mScalingKeys[j].mValue.z)); } else { Qt3DCore::QTransform *t = new Qt3DCore::QTransform(); t->setScale3D(QVector3D(nodeAnim->mScalingKeys[j].mValue.x, nodeAnim->mScalingKeys[j].mValue.y, nodeAnim->mScalingKeys[j].mValue.z)); insertAtTime(positions, transforms, t, nodeAnim->mScalingKeys[j].mTime); } } } for (int j = 0; j < positions.size(); ++j) positions[j] = positions[j] * tickScale; kfa->setFramePositions(positions); kfa->setKeyframes(transforms); kfa->setAnimationName(QString(assimpAnim->mName.C_Str())); kfa->setTargetName(QString(targetNode->mName.C_Str())); m_scene->m_animations.push_back(kfa); } /* mesh morph animations */ for (uint i = 0; i < assimpAnim->mNumMorphMeshChannels; ++i) { aiMeshMorphAnim *morphAnim = assimpAnim->mMorphMeshChannels[i]; aiNode *targetNode = m_scene->m_aiScene->mRootNode->FindNode(morphAnim->mName); aiMesh *mesh = m_scene->m_aiScene->mMeshes[targetNode->mMeshes[0]]; Qt3DAnimation::QMorphingAnimation *morphingAnimation = new Qt3DAnimation::QMorphingAnimation; QVector positions; positions.resize(morphAnim->mNumKeys); // set so that weights array is allocated to correct size in morphingAnimation morphingAnimation->setTargetPositions(positions); for (unsigned int j = 0; j < morphAnim->mNumKeys; ++j) { aiMeshMorphKey &key = morphAnim->mKeys[j]; positions[j] = key.mTime * tickScale; QVector weights; weights.resize(key.mNumValuesAndWeights); for (int k = 0; k < weights.size(); k++) { const unsigned int value = key.mValues[k]; if (value < key.mNumValuesAndWeights) weights[value] = key.mWeights[k]; } morphingAnimation->setWeights(j, weights); } morphingAnimation->setTargetPositions(positions); morphingAnimation->setAnimationName(QString(assimpAnim->mName.C_Str())); morphingAnimation->setTargetName(QString(targetNode->mName.C_Str())); morphingAnimation->setMethod((mesh->mMethod == aiMorphingMethod_MORPH_NORMALIZED) ? Qt3DAnimation::QMorphingAnimation::Normalized : Qt3DAnimation::QMorphingAnimation::Relative); m_scene->m_morphAnimations.push_back(morphingAnimation); } } /*! * Sets the object name of \a material to the name of \a assimpMaterial. */ void AssimpImporter::copyMaterialName(QMaterial *material, aiMaterial *assimpMaterial) { aiString name; if (assimpMaterial->Get(AI_MATKEY_NAME, name) == aiReturn_SUCCESS) { // May not be necessary // Kept for debug purposes at the moment material->setObjectName(aiStringToQString(name)); qCDebug(AssimpImporterLog) << Q_FUNC_INFO << "Assimp Material " << material->objectName(); } } /*! * Fills \a material color properties with \a assimpMaterial color properties. */ void AssimpImporter::copyMaterialColorProperties(QMaterial *material, aiMaterial *assimpMaterial) { aiColor3D color; if (assimpMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, color) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_DIFFUSE_COLOR, material, QColor::fromRgbF(color.r, color.g, color.b)); if (assimpMaterial->Get(AI_MATKEY_COLOR_SPECULAR, color) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_SPECULAR_COLOR, material, QColor::fromRgbF(color.r, color.g, color.b)); if (assimpMaterial->Get(AI_MATKEY_COLOR_AMBIENT, color) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_AMBIENT_COLOR, material, QColor::fromRgbF(color.r, color.g, color.b)); if (assimpMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, color) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_EMISSIVE_COLOR, material, QColor::fromRgbF(color.r, color.g, color.b)); if (assimpMaterial->Get(AI_MATKEY_COLOR_TRANSPARENT, color) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_TRANSPARENT_COLOR, material, QColor::fromRgbF(color.r, color.g, color.b)); if (assimpMaterial->Get(AI_MATKEY_COLOR_REFLECTIVE, color) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_REFLECTIVE_COLOR, material, QColor::fromRgbF(color.r, color.g, color.b)); } /*! * Retrieves a \a material bool property. */ void AssimpImporter::copyMaterialBoolProperties(QMaterial *material, aiMaterial *assimpMaterial) { int value; if (assimpMaterial->Get(AI_MATKEY_TWOSIDED, value) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_IS_TWOSIDED, material, (value == 0) ? false : true); if (assimpMaterial->Get(AI_MATKEY_ENABLE_WIREFRAME, value) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_IS_WIREFRAME, material, (value == 0) ? false : true); } void AssimpImporter::copyMaterialShadingModel(QMaterial *material, aiMaterial *assimpMaterial) { Q_UNUSED(material); Q_UNUSED(assimpMaterial); // TODO // Match each shading function with a default shader // AssimpIO::assimpMaterialAttributesMap[AI_MATKEY_SHADING_MODEL] = &AssimpIO::getMaterialShadingModel; // AssimpIO::assimpMaterialAttributesMap[AI_MATKEY_BLEND_FUNC] = &AssimpIO::getMaterialBlendingFunction; } void AssimpImporter::copyMaterialBlendingFunction(QMaterial *material, aiMaterial *assimpMaterial) { Q_UNUSED(material); Q_UNUSED(assimpMaterial); // TO DO } /*! * */ void AssimpImporter::copyMaterialTextures(QMaterial *material, aiMaterial *assimpMaterial) { static const aiTextureType textureType[] = {aiTextureType_AMBIENT, aiTextureType_DIFFUSE, aiTextureType_DISPLACEMENT, aiTextureType_EMISSIVE, aiTextureType_HEIGHT, aiTextureType_LIGHTMAP, aiTextureType_NORMALS, aiTextureType_OPACITY, aiTextureType_REFLECTION, aiTextureType_SHININESS, aiTextureType_SPECULAR}; if (m_scene->m_textureToParameterName.isEmpty()) { m_scene->m_textureToParameterName.insert(aiTextureType_AMBIENT, ASSIMP_MATERIAL_AMBIENT_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_DIFFUSE, ASSIMP_MATERIAL_DIFFUSE_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_DISPLACEMENT, ASSIMP_MATERIAL_DISPLACEMENT_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_EMISSIVE, ASSIMP_MATERIAL_EMISSIVE_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_HEIGHT, ASSIMP_MATERIAL_HEIGHT_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_LIGHTMAP, ASSIMP_MATERIAL_LIGHTMAP_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_NORMALS, ASSIMP_MATERIAL_NORMALS_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_OPACITY, ASSIMP_MATERIAL_OPACITY_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_REFLECTION, ASSIMP_MATERIAL_REFLECTION_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_SHININESS, ASSIMP_MATERIAL_SHININESS_TEXTURE); m_scene->m_textureToParameterName.insert(aiTextureType_SPECULAR, ASSIMP_MATERIAL_SPECULAR_TEXTURE); } for (unsigned int i = 0; i < sizeof(textureType)/sizeof(textureType[0]); i++) { aiString path; if (assimpMaterial->GetTexture(textureType[i], 0, &path) == AI_SUCCESS) { const QString fullPath = m_sceneDir.absoluteFilePath(texturePath(path)); // Load texture if not already loaded QAbstractTexture *tex = QAbstractNodeFactory::createNode("QTexture2D"); QTextureImage *texImage = QAbstractNodeFactory::createNode("QTextureImage"); texImage->setSource(QUrl::fromLocalFile(fullPath)); texImage->setMirrored(false); tex->addTextureImage(texImage); // Set proper wrapping mode QTextureWrapMode wrapMode(QTextureWrapMode::Repeat); int xMode = 0; int yMode = 0; if (assimpMaterial->Get(AI_MATKEY_MAPPINGMODE_U(textureType[i], 0), xMode) == aiReturn_SUCCESS) wrapMode.setX(wrapModeFromaiTextureMapMode(xMode)); if (assimpMaterial->Get(AI_MATKEY_MAPPINGMODE_V(textureType[i], 0), yMode) == aiReturn_SUCCESS) wrapMode.setY(wrapModeFromaiTextureMapMode(yMode)); tex->setWrapMode(wrapMode); qCDebug(AssimpImporterLog) << Q_FUNC_INFO << " Loaded Texture " << fullPath; setParameterValue(m_scene->m_textureToParameterName[textureType[i]], material, QVariant::fromValue(tex)); } } } /*! * Retrieves a \a material float property. */ void AssimpImporter::copyMaterialFloatProperties(QMaterial *material, aiMaterial *assimpMaterial) { float value = 0; if (assimpMaterial->Get(AI_MATKEY_OPACITY, value) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_OPACITY, material, value); if (assimpMaterial->Get(AI_MATKEY_SHININESS, value) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_SHININESS, material, value); if (assimpMaterial->Get(AI_MATKEY_SHININESS_STRENGTH, value) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_SHININESS_STRENGTH, material, value); if (assimpMaterial->Get(AI_MATKEY_REFRACTI, value) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_REFRACTI, material, value); if (assimpMaterial->Get(AI_MATKEY_REFLECTIVITY, value) == aiReturn_SUCCESS) setParameterValue(ASSIMP_MATERIAL_REFLECTIVITY, material, value); } AssimpRawTextureImage::AssimpRawTextureImage(QNode *parent) : QAbstractTextureImage(parent) { } QTextureImageDataGeneratorPtr AssimpRawTextureImage::dataGenerator() const { return QTextureImageDataGeneratorPtr(new AssimpRawTextureImageFunctor(m_data)); } void AssimpRawTextureImage::setData(const QByteArray &data) { if (data != m_data) { m_data = data; notifyDataGeneratorChanged(); } } AssimpRawTextureImage::AssimpRawTextureImageFunctor::AssimpRawTextureImageFunctor(const QByteArray &data) : QTextureImageDataGenerator() , m_data(data) { } QTextureImageDataPtr AssimpRawTextureImage::AssimpRawTextureImageFunctor::operator()() { QTextureImageDataPtr dataPtr = QTextureImageDataPtr::create(); // Note: we assume 4 components per pixel and not compressed for now dataPtr->setData(m_data, 4, false); return dataPtr; } bool AssimpRawTextureImage::AssimpRawTextureImageFunctor::operator ==(const QTextureImageDataGenerator &other) const { const AssimpRawTextureImageFunctor *otherFunctor = functor_cast(&other); return (otherFunctor != nullptr && otherFunctor->m_data == m_data); } AssimpImporter::SceneImporter::SceneImporter() : m_importer(new Assimp::Importer()) , m_aiScene(nullptr) { // The Assimp::Importer manages the lifetime of the aiScene object } AssimpImporter::SceneImporter::~SceneImporter() { delete m_importer; } } // namespace Qt3DRender QT_END_NAMESPACE #include "assimpimporter.moc"