summaryrefslogtreecommitdiffstats
path: root/src/plugins/geometryloaders/fbx/fbxgeometryloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/geometryloaders/fbx/fbxgeometryloader.cpp')
-rw-r--r--src/plugins/geometryloaders/fbx/fbxgeometryloader.cpp507
1 files changed, 507 insertions, 0 deletions
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