summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.tests/fbx/fbx.pro1
-rw-r--r--config.tests/fbx/main.cpp39
-rw-r--r--configure.json3
-rw-r--r--src/plugins/geometryloaders/configure.json34
-rw-r--r--src/plugins/geometryloaders/configure.pri30
-rw-r--r--src/plugins/geometryloaders/fbx/fbx.json3
-rw-r--r--src/plugins/geometryloaders/fbx/fbx.pro22
-rw-r--r--src/plugins/geometryloaders/fbx/fbxgeometryloader.cpp507
-rw-r--r--src/plugins/geometryloaders/fbx/fbxgeometryloader.h93
-rw-r--r--src/plugins/geometryloaders/fbx/main.cpp67
-rw-r--r--src/plugins/geometryloaders/geometryloaders.pro1
-rw-r--r--tests/auto/render/geometryloaders/cube.fbxbin0 -> 26028 bytes
-rw-r--r--tests/auto/render/geometryloaders/geometryloaders.pro1
-rw-r--r--tests/auto/render/geometryloaders/geometryloaders.qrc1
-rw-r--r--tests/auto/render/geometryloaders/tst_geometryloaders.cpp46
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
new file mode 100644
index 000000000..6501e6eaf
--- /dev/null
+++ b/tests/auto/render/geometryloaders/cube.fbx
Binary files differ
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"