diff options
-rw-r--r-- | config.tests/fbx/fbx.pro | 1 | ||||
-rw-r--r-- | config.tests/fbx/main.cpp | 39 | ||||
-rw-r--r-- | configure.json | 3 | ||||
-rw-r--r-- | src/plugins/geometryloaders/configure.json | 34 | ||||
-rw-r--r-- | src/plugins/geometryloaders/configure.pri | 30 | ||||
-rw-r--r-- | src/plugins/geometryloaders/fbx/fbx.json | 3 | ||||
-rw-r--r-- | src/plugins/geometryloaders/fbx/fbx.pro | 22 | ||||
-rw-r--r-- | src/plugins/geometryloaders/fbx/fbxgeometryloader.cpp | 507 | ||||
-rw-r--r-- | src/plugins/geometryloaders/fbx/fbxgeometryloader.h | 93 | ||||
-rw-r--r-- | src/plugins/geometryloaders/fbx/main.cpp | 67 | ||||
-rw-r--r-- | src/plugins/geometryloaders/geometryloaders.pro | 1 | ||||
-rw-r--r-- | tests/auto/render/geometryloaders/cube.fbx | bin | 0 -> 26028 bytes | |||
-rw-r--r-- | tests/auto/render/geometryloaders/geometryloaders.pro | 1 | ||||
-rw-r--r-- | tests/auto/render/geometryloaders/geometryloaders.qrc | 1 | ||||
-rw-r--r-- | tests/auto/render/geometryloaders/tst_geometryloaders.cpp | 46 |
15 files changed, 847 insertions, 1 deletions
diff --git a/config.tests/fbx/fbx.pro b/config.tests/fbx/fbx.pro new file mode 100644 index 000000000..28dcadcbf --- /dev/null +++ b/config.tests/fbx/fbx.pro @@ -0,0 +1 @@ +SOURCES += main.cpp diff --git a/config.tests/fbx/main.cpp b/config.tests/fbx/main.cpp new file mode 100644 index 000000000..56304e7cb --- /dev/null +++ b/config.tests/fbx/main.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <fbxsdk.h> + +int main(int, char **) +{ + FbxManager *manager = FbxManager::Create(); + FbxScene::Create(manager, "scene"); + manager->Destroy(); + + return 0; +} + diff --git a/configure.json b/configure.json index 40d6bfe56..7f7137cd1 100644 --- a/configure.json +++ b/configure.json @@ -1,5 +1,6 @@ { "subconfigs": [ - "src/core" + "src/core", + "src/plugins/geometryloaders" ] } diff --git a/src/plugins/geometryloaders/configure.json b/src/plugins/geometryloaders/configure.json new file mode 100644 index 000000000..42cc57736 --- /dev/null +++ b/src/plugins/geometryloaders/configure.json @@ -0,0 +1,34 @@ +{ + "module": "geometryloaders", + "testDir": "../../../config.tests", + + "libraries": { + "fbx": { + "label": "Autodesk FBX", + "test": "fbx", + "sources": [ + { "type": "fbx", "libs": "-lfbxsdk" } + ] + } + }, + + "features": { + "qt3d-fbxsdk": { + "label": "Autodesk FBX", + "condition": "libs.fbx", + "output": [ + "privateFeature", + { "type": "define", "name": "QT_3DGEOMETRYLOADERS_FBX" } + ] + } + }, + + "summary": [ + { + "section": "Qt 3D GeometryLoaders", + "entries": [ + "qt3d-fbxsdk" + ] + } + ] +} diff --git a/src/plugins/geometryloaders/configure.pri b/src/plugins/geometryloaders/configure.pri new file mode 100644 index 000000000..c49c1be51 --- /dev/null +++ b/src/plugins/geometryloaders/configure.pri @@ -0,0 +1,30 @@ +defineTest(qtConfLibrary_fbx) { + libs = $$eval($${1}.libs) + includedir = + + libs_override = $$getenv(FBXSDK_LIBS) + !isEmpty(libs_override) { + libs = $${libs_override} + } + + prefix = $$getenv(FBXSDK) + + unix:isEmpty(prefix) { + libs += "-L/usr/local/lib" + libs += "-L/usr/lib" + } + + !isEmpty(prefix) { + includedir += $${prefix}/include + !win32:libs += -L$${prefix}/lib + } + + $${1}.libs = $$val_escape(libs) + $${1}.includedir = $$val_escape(includedir) + + export($${1}.libs) + export($${1}.includedir) + + return(true) +} + diff --git a/src/plugins/geometryloaders/fbx/fbx.json b/src/plugins/geometryloaders/fbx/fbx.json new file mode 100644 index 000000000..4e0cb9d98 --- /dev/null +++ b/src/plugins/geometryloaders/fbx/fbx.json @@ -0,0 +1,3 @@ +{ + "Keys": ["fbx"] +} diff --git a/src/plugins/geometryloaders/fbx/fbx.pro b/src/plugins/geometryloaders/fbx/fbx.pro new file mode 100644 index 000000000..a35035fad --- /dev/null +++ b/src/plugins/geometryloaders/fbx/fbx.pro @@ -0,0 +1,22 @@ +TARGET = fbxgeometryloader +QT += core-private 3dcore 3dcore-private 3drender 3drender-private + +# Qt3D is free of Q_FOREACH - make sure it stays that way: +DEFINES += QT_NO_FOREACH + +HEADERS += \ + fbxgeometryloader.h \ + +SOURCES += \ + main.cpp \ + fbxgeometryloader.cpp \ + +DISTFILES += \ + fbx.json + +include($$OUT_PWD/../qtgeometryloaders-config.pri) +QMAKE_USE += fbx + +PLUGIN_TYPE = geometryloaders +PLUGIN_CLASS_NAME = fbxGeometryLoaderPlugin +load(qt_plugin) diff --git a/src/plugins/geometryloaders/fbx/fbxgeometryloader.cpp b/src/plugins/geometryloaders/fbx/fbxgeometryloader.cpp new file mode 100644 index 000000000..cb0b57d50 --- /dev/null +++ b/src/plugins/geometryloaders/fbx/fbxgeometryloader.cpp @@ -0,0 +1,507 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "fbxgeometryloader.h" + +#include <QtCore/QFileDevice> + +#include <Qt3DRender/QAttribute> +#include <Qt3DRender/QBuffer> +#include <Qt3DRender/QGeometry> +#include <Qt3DRender/private/renderlogging_p.h> + +#include <fbxsdk.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +Q_LOGGING_CATEGORY(FbxGeometryLoaderLog, "Qt3D.FbxGeometryLoader") + +class FbxStreamWrapper : public FbxStream +{ + FbxManager *m_manager; + QIODevice *m_device; +public: + FbxStreamWrapper(FbxManager *manager) + : m_manager(manager) + , m_device(nullptr) + { + } + + EState GetState() + { + if (!m_device) + return eEmpty; + return m_device->isOpen() ? eOpen : eClosed; + } + + bool Open(void *pStreamData) + { + m_device = reinterpret_cast<QIODevice *>(pStreamData); + + if (!m_device->isOpen()) + m_device->open(QIODevice::ReadOnly); + + return m_device && m_device->isOpen(); + } + + bool Close() + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return false; + + m_device->close(); + + return !m_device->isOpen(); + } + + bool Flush() + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return false; + + QFileDevice *device = qobject_cast<QFileDevice*>(m_device); + if (Q_LIKELY(device)) + return device->flush(); + + return false; + } + + int Write(const void *pData, int pSize) + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return -1; + + return m_device->write(reinterpret_cast<const char *>(pData), pSize); + } + + int Read(void *pData, int pSize) const + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return -1; + + return m_device->read(reinterpret_cast<char *>(pData), pSize); + } + + char *ReadString(char *pBuffer, int pMaxSize, bool pStopAtFirstWhiteSpace = false) + { + Q_UNUSED(pBuffer); + Q_UNUSED(pMaxSize); + Q_UNUSED(pStopAtFirstWhiteSpace); + return nullptr; + } + + int GetReaderID() const + { + return m_manager->GetIOPluginRegistry()-> + FindReaderIDByExtension(qPrintable(FBXGEOMETRYLOADER_EXT)); + } + + int GetWriterID() const + { + return m_manager->GetIOPluginRegistry()-> + FindReaderIDByExtension(qPrintable(FBXGEOMETRYLOADER_EXT)); + } + + void Seek(const FbxInt64 &pOffset, const FbxFile::ESeekPos &pSeekPos) + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return; + + switch (pSeekPos) { + case FbxFile::eBegin: + m_device->seek(pOffset); + break; + case FbxFile::eCurrent: + m_device->seek(m_device->pos() + pOffset); + break; + case FbxFile::eEnd: + m_device->seek(m_device->size() - pOffset); + break; + } + } + + long GetPosition() const + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return -1; + + return m_device->pos(); + } + + void SetPosition(long pPosition) + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return; + + m_device->seek(pPosition); + } + + int GetError() const + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return 1; + + QFileDevice *device = qobject_cast<QFileDevice *>(m_device); + if (Q_LIKELY(device)) + return device->error() == QFileDevice::NoError ? 0 : 1; + + return 0; + } + + void ClearError() + { + Q_ASSERT(m_device); + + if (Q_UNLIKELY(!m_device)) + return; + + QFileDevice *device = qobject_cast<QFileDevice *>(m_device); + if (Q_LIKELY(device)) + device->unsetError(); + } +}; + +FbxGeometryLoader::FbxGeometryLoader() + : m_manager(nullptr) + , m_scene(nullptr) + , m_geometry(nullptr) +{ + m_manager = FbxManager::Create(); + + if (Q_LIKELY(m_manager)) + qInfo(FbxGeometryLoaderLog, "Autodesk FBX SDK version %s", m_manager->GetVersion()); + else + qWarning(FbxGeometryLoaderLog, "Failed to create FBX Manager."); +} + +FbxGeometryLoader::~FbxGeometryLoader() +{ + if (m_manager) + m_manager->Destroy(); +} + +QGeometry *FbxGeometryLoader::geometry() const +{ + return m_geometry; +} + +bool FbxGeometryLoader::load(QIODevice *ioDev, const QString &subMesh) +{ + if (m_scene) + m_scene->Destroy(); + + m_scene = FbxScene::Create(m_manager, "scene"); + if (!m_scene) + qWarning(FbxGeometryLoaderLog, "Unable to create FBX scene!"); + + QScopedPointer<FbxStreamWrapper> fbxStream(new FbxStreamWrapper(m_manager)); + + FbxImporter *importer = FbxImporter::Create(m_manager, ""); + + const bool hasInitialized = importer->Initialize(fbxStream.data(), ioDev); + + int fileVersion[3]; + + importer->GetFileVersion(fileVersion[0], fileVersion[1], fileVersion[2]); + + if (!hasInitialized) { + FbxString error = importer->GetStatus().GetErrorString(); + qWarning(FbxGeometryLoaderLog, "Call to FbxImporter::Initialize() failed."); + qWarning(FbxGeometryLoaderLog, "Error returned: %s", error.Buffer()); + + if (importer->GetStatus().GetCode() == FbxStatus::eInvalidFileVersion) { + int SdkVersion[3]; + FbxManager::GetFileFormatVersion(SdkVersion[0], SdkVersion[1], SdkVersion[2]); + qWarning(FbxGeometryLoaderLog, "FBX file format version for this FBX SDK is %d.%d.%d", + SdkVersion[0], SdkVersion[1], SdkVersion[2]); + qWarning(FbxGeometryLoaderLog, "FBX file format version for the file is %d.%d.%d", + fileVersion[0], fileVersion[1], fileVersion[2]); + } + + return false; + } + + if (importer->IsFBX()) { + qInfo(FbxGeometryLoaderLog, "FBX file format version for file is %d.%d.%d", + fileVersion[0], fileVersion[1], fileVersion[2]); + + const int stackCount = importer->GetAnimStackCount(); + for (int i = 0; i < stackCount; ++i) { + FbxTakeInfo *lTakeInfo = importer->GetTakeInfo(i); + qInfo(FbxGeometryLoaderLog, " Animation Stack %d", i); + qInfo(FbxGeometryLoaderLog, " Name: \"%s\"", lTakeInfo->mName.Buffer()); + qInfo(FbxGeometryLoaderLog, " Description: \"%s\"", lTakeInfo->mDescription.Buffer()); + qInfo(FbxGeometryLoaderLog, " Import Name: \"%s\"", lTakeInfo->mImportName.Buffer()); + qInfo(FbxGeometryLoaderLog, " Import State: %s", lTakeInfo->mSelect ? "true" : "false"); + } + + auto settings = importer->GetIOSettings(); + settings->SetBoolProp(IMP_FBX_MATERIAL, false); + settings->SetBoolProp(IMP_FBX_TEXTURE, false); + settings->SetBoolProp(IMP_FBX_LINK, false); + settings->SetBoolProp(IMP_FBX_SHAPE, false); + settings->SetBoolProp(IMP_FBX_GOBO, false); + settings->SetBoolProp(IMP_FBX_ANIMATION, false); + settings->SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, false); + } + + const bool wasImported = importer->Import(m_scene); + importer->Destroy(); + + m_mesh = subMesh; + + if (wasImported) + recurseNodes(); + + return wasImported; +} + +void FbxGeometryLoader::recurseNodes() +{ + Q_ASSERT(m_scene); + + if (!m_scene) + return; + + FbxNode *node = m_scene->GetRootNode(); + if (node) { + for (int i = 0; i < node->GetChildCount() && !m_geometry; ++i) + processNode(node->GetChild(i)); + } +} + +void FbxGeometryLoader::processNode(FbxNode *node) +{ + auto attr = node->GetNodeAttribute(); + if (!attr) + return; + + switch (attr->GetAttributeType()) { + case FbxNodeAttribute::eMesh: + if (m_mesh.isEmpty() || m_mesh.compare(node->GetName(), Qt::CaseInsensitive) == 0) { + qDebug(FbxGeometryLoaderLog, "Found mesh: %s", node->GetName()); + processMesh(node->GetMesh()); + } + break; + default: + break; + } + + if (m_geometry) + return; + + for (int i = 0; i < node->GetChildCount(); ++i) + processNode(node->GetChild(i)); +} + +void FbxGeometryLoader::processMesh(FbxMesh *mesh) +{ + const int normalCount = mesh->GetElementNormalCount(); + const int polygonCount = mesh->GetPolygonCount(); + const int tangentCount = mesh->GetElementTangentCount(); + const int uvCount = mesh->GetElementUVCount(); + + const bool hasNormal = (normalCount > 0); + const bool hasTangent = (tangentCount > 0); + const bool hasUV = (uvCount > 0); + + const unsigned int elementSize = 3 + (hasUV ? 2 : 0) + (hasNormal ? 3 : 0) + (hasTangent ? 4 : 0); + const unsigned int elementBytes = elementSize * sizeof(double); + + int vertexCount = 0; + for (int polygonIndex = 0; polygonIndex < polygonCount; ++polygonIndex) + vertexCount += mesh->GetPolygonSize(polygonIndex); + const int indexCount = (polygonCount * 3) + ((vertexCount - (polygonCount * 3)) * 3); + + QByteArray indexPayload; + indexPayload.resize(indexCount * sizeof(quint32)); + quint32 *indexData = reinterpret_cast<quint32 *>(indexPayload.data()); + + QByteArray vertexPayload; + vertexPayload.resize(vertexCount * elementBytes); + double *vertexData = reinterpret_cast<double *>(vertexPayload.data()); + + const FbxVector4 *controlPoints = mesh->GetControlPoints(); + + int vertexIndex = 0; + for (int polygonIndex = 0; polygonIndex < polygonCount; ++polygonIndex) { + const int polygonSize = mesh->GetPolygonSize(polygonIndex); + for (int pVertexIndex = 0; pVertexIndex < polygonSize; ++pVertexIndex) { + const int controlPointIndex = mesh->GetPolygonVertex(polygonIndex, pVertexIndex); + const FbxVector4 *vertex = (controlPoints + controlPointIndex); + *vertexData++ = (*vertex)[0]; + *vertexData++ = (*vertex)[1]; + *vertexData++ = (*vertex)[2]; + + if (pVertexIndex >= 1 && pVertexIndex < (polygonSize - 1)) { + *indexData++ = vertexIndex - pVertexIndex; + *indexData++ = vertexIndex; + *indexData++ = vertexIndex + 1; + } + + if (hasUV) { + FbxVector2 vector; + bool unmapped; + + if (mesh->GetPolygonVertexUV(polygonIndex, pVertexIndex, NULL, vector, unmapped)) { + *vertexData++ = unmapped ? 0 : vector[0]; + *vertexData++ = unmapped ? 0 : vector[1]; + } else { + qWarning(FbxGeometryLoaderLog, + "Irregularity encountered while parsing UV element."); + *vertexData++ = 0; + *vertexData++ = 0; + } + } + + if (hasNormal) { + FbxVector4 vector; + if (mesh->GetPolygonVertexNormal(polygonIndex, pVertexIndex, vector)) { + *vertexData++ = vector[0]; + *vertexData++ = vector[1]; + *vertexData++ = vector[2]; + } else { + qWarning(FbxGeometryLoaderLog, + "Irregularity encountered while parsing Normal element."); + *vertexData++ = 0; + *vertexData++ = 0; + *vertexData++ = 0; + } + } + + if (hasTangent) { + int index = -1; + + for (int tangentIndex = 0; tangentIndex < tangentCount && index == -1; ++tangentIndex) { + const FbxGeometryElementTangent *tangent = mesh->GetElementTangent(tangentIndex); + + if (tangent->GetMappingMode() == FbxGeometryElement::eByPolygonVertex) { + switch (tangent->GetReferenceMode()) { + case FbxGeometryElement::eDirect: + index = vertexIndex; + break; + case FbxGeometryElement::eIndexToDirect: + index = tangent->GetIndexArray().GetAt(vertexIndex); + break; + default: break; + } + } + + if (index != -1) { + const FbxVector4 vector = tangent->GetDirectArray().GetAt(index); + *vertexData++ = vector[0]; + *vertexData++ = vector[1]; + *vertexData++ = vector[2]; + *vertexData++ = vector[3]; + } + } + + if (index == -1) { + qWarning(FbxGeometryLoaderLog, + "Irregularity encountered while parsing Tangent element."); + *vertexData++ = 0; + *vertexData++ = 0; + *vertexData++ = 0; + *vertexData++ = 0; + } + } + ++vertexIndex; + } + } + + /* + * QGeometry Generation + */ + m_geometry = new QGeometry(); + + unsigned int offset = 0; + + QBuffer *vertexBuffer = new QBuffer(QBuffer::VertexBuffer); + vertexBuffer->setData(vertexPayload); + + QBuffer *indexBuffer = new QBuffer(QBuffer::IndexBuffer); + indexBuffer->setData(indexPayload); + + QAttribute *positionAttribute = new QAttribute(vertexBuffer, QAttribute::defaultPositionAttributeName(), QAttribute::Double, 3, vertexCount, offset, elementBytes); + m_geometry->addAttribute(positionAttribute); + offset += sizeof(double) * 3; + + if (hasUV) { + QAttribute *attribute = new QAttribute(vertexBuffer, QAttribute::defaultTextureCoordinateAttributeName(), QAttribute::Double, 2, vertexCount, offset, elementBytes); + m_geometry->addAttribute(attribute); + offset += sizeof(double) * 2; + } + + if (hasNormal) { + QAttribute *attribute = new QAttribute(vertexBuffer, QAttribute::defaultNormalAttributeName(), QAttribute::Double, 3, vertexCount, offset, elementBytes); + m_geometry->addAttribute(attribute); + offset += sizeof(double) * 3; + } + + if (hasTangent) { + QAttribute *attribute = new QAttribute(vertexBuffer, QAttribute::defaultTangentAttributeName(),QAttribute::Double, 4, vertexCount, offset, elementBytes); + m_geometry->addAttribute(attribute); + } + + QAttribute *indexAttribute = new QAttribute(indexBuffer, QAttribute::UnsignedInt, 1, indexCount); + indexAttribute->setAttributeType(QAttribute::IndexAttribute); + m_geometry->addAttribute(indexAttribute); +} + +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/plugins/geometryloaders/fbx/fbxgeometryloader.h b/src/plugins/geometryloaders/fbx/fbxgeometryloader.h new file mode 100644 index 000000000..69d9670ae --- /dev/null +++ b/src/plugins/geometryloaders/fbx/fbxgeometryloader.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +#ifndef FBXGEOMETRYLOADER_H +#define FBXGEOMETRYLOADER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/private/qgeometryloaderinterface_p.h> + +#include <private/qlocale_tools_p.h> + +#include <fbxsdk.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +#define FBXGEOMETRYLOADER_EXT QLatin1String("fbx") + +class FbxGeometryLoader : public QGeometryLoaderInterface +{ + Q_OBJECT +public: + FbxGeometryLoader(); + ~FbxGeometryLoader(); + + QGeometry *geometry() const Q_DECL_FINAL; + + bool load(QIODevice *ioDev, const QString &subMesh = QString()) Q_DECL_FINAL; + +protected: + void recurseNodes(); + void processNode(FbxNode *node); + void processMesh(FbxMesh *mesh); + +private: + FbxManager *m_manager; + FbxScene *m_scene; + QGeometry *m_geometry; + QString m_mesh; +}; + +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // FBXGEOMETRYLOADER_H diff --git a/src/plugins/geometryloaders/fbx/main.cpp b/src/plugins/geometryloaders/fbx/main.cpp new file mode 100644 index 000000000..a3bcb3b2a --- /dev/null +++ b/src/plugins/geometryloaders/fbx/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). +** 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 <Qt3DRender/private/qgeometryloaderfactory_p.h> + +#include "fbxgeometryloader.h" + +QT_BEGIN_NAMESPACE + +class FBXGeometryLoaderPlugin : public Qt3DRender::QGeometryLoaderFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QGeometryLoaderFactory_iid FILE "fbx.json") +public: + + QStringList keys() const Q_DECL_OVERRIDE + { + return QStringList() << FBXGEOMETRYLOADER_EXT; + } + + Qt3DRender::QGeometryLoaderInterface* create(const QString& ext) Q_DECL_OVERRIDE + { + if ((ext.compare(FBXGEOMETRYLOADER_EXT, Qt::CaseInsensitive) == 0)) + return new Qt3DRender::FbxGeometryLoader; + return nullptr; + } +}; + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/geometryloaders/geometryloaders.pro b/src/plugins/geometryloaders/geometryloaders.pro index 85ab48d01..8dd99deb5 100644 --- a/src/plugins/geometryloaders/geometryloaders.pro +++ b/src/plugins/geometryloaders/geometryloaders.pro @@ -1,3 +1,4 @@ TEMPLATE = subdirs SUBDIRS += default SUBDIRS += gltf +qtConfig(qt3d-fbxsdk) : SUBDIRS += fbx diff --git a/tests/auto/render/geometryloaders/cube.fbx b/tests/auto/render/geometryloaders/cube.fbx Binary files differnew file mode 100644 index 000000000..6501e6eaf --- /dev/null +++ b/tests/auto/render/geometryloaders/cube.fbx diff --git a/tests/auto/render/geometryloaders/geometryloaders.pro b/tests/auto/render/geometryloaders/geometryloaders.pro index 6231c896d..fcf362055 100644 --- a/tests/auto/render/geometryloaders/geometryloaders.pro +++ b/tests/auto/render/geometryloaders/geometryloaders.pro @@ -10,3 +10,4 @@ SOURCES += tst_geometryloaders.cpp RESOURCES += \ geometryloaders.qrc + diff --git a/tests/auto/render/geometryloaders/geometryloaders.qrc b/tests/auto/render/geometryloaders/geometryloaders.qrc index f052420d5..730a0c452 100644 --- a/tests/auto/render/geometryloaders/geometryloaders.qrc +++ b/tests/auto/render/geometryloaders/geometryloaders.qrc @@ -5,5 +5,6 @@ <file>cube.stl</file> <file>cube.gltf</file> <file>cube_buffer.bin</file> + <file>cube.fbx</file> </qresource> </RCC> diff --git a/tests/auto/render/geometryloaders/tst_geometryloaders.cpp b/tests/auto/render/geometryloaders/tst_geometryloaders.cpp index 03c3abc31..15600dc75 100644 --- a/tests/auto/render/geometryloaders/tst_geometryloaders.cpp +++ b/tests/auto/render/geometryloaders/tst_geometryloaders.cpp @@ -48,6 +48,8 @@ #include <Qt3DRender/private/qgeometryloaderfactory_p.h> #include <Qt3DRender/private/qgeometryloaderinterface_p.h> +#include "../../../../src/plugins/geometryloaders/qtgeometryloaders-config.h" + using namespace Qt3DRender; Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, geometryLoader, @@ -62,6 +64,9 @@ private Q_SLOTS: void testPLYLoader(); void testSTLLoader(); void testGLTFLoader(); +#ifdef QT_3DGEOMETRYLOADERS_FBX + void testFBXLoader(); +#endif }; void tst_geometryloaders::testOBJLoader() @@ -213,6 +218,47 @@ void tst_geometryloaders::testGLTFLoader() file.close(); } +#ifdef QT_3DGEOMETRYLOADERS_FBX +void tst_geometryloaders::testFBXLoader() +{ + QScopedPointer<QGeometryLoaderInterface> loader; + loader.reset(qLoadPlugin<QGeometryLoaderInterface, QGeometryLoaderFactory>(geometryLoader(), QStringLiteral("fbx"))); + QVERIFY(loader); + if (!loader) + return; + + QFile file(QStringLiteral(":/cube.fbx")); + if (!file.open(QIODevice::ReadOnly)) { + qDebug("Could not open test file for reading"); + return; + } + + bool loaded = loader->load(&file, QStringLiteral("Cube")); + QVERIFY(loaded); + if (!loaded) + return; + + QGeometry *geometry = loader->geometry(); + QVERIFY(geometry); + if (!geometry) + return; + + QCOMPARE(geometry->attributes().count(), 3); + for (QAttribute *attr : geometry->attributes()) { + switch (attr->attributeType()) { + case QAttribute::IndexAttribute: + QCOMPARE(attr->count(), 36u); + break; + case QAttribute::VertexAttribute: + QCOMPARE(attr->count(), 24u); + break; + } + } + + file.close(); +} +#endif + QTEST_MAIN(tst_geometryloaders) #include "tst_geometryloaders.moc" |