diff options
Diffstat (limited to 'src/plugins/sceneformats/assimp')
-rw-r--r-- | src/plugins/sceneformats/assimp/README.txt | 12 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/ailoaderiostream.cpp | 121 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/ailoaderiostream.h | 67 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/ailoaderiosystem.cpp | 156 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/ailoaderiosystem.h | 67 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/assimp.pro | 32 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/main.cpp | 100 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/qailoader.cpp | 540 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/qailoader.h | 89 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/qaimesh.cpp | 143 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/qaimesh.h | 67 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/qaiscene.cpp | 112 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/qaiscene.h | 77 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/qaiscenehandler.cpp | 286 | ||||
-rw-r--r-- | src/plugins/sceneformats/assimp/qaiscenehandler.h | 108 |
15 files changed, 1977 insertions, 0 deletions
diff --git a/src/plugins/sceneformats/assimp/README.txt b/src/plugins/sceneformats/assimp/README.txt new file mode 100644 index 000000000..cabf1bdd8 --- /dev/null +++ b/src/plugins/sceneformats/assimp/README.txt @@ -0,0 +1,12 @@ +This importer uses the Asset Importer library available from: + +http://assimp.sourceforge.net/ + +Copyright (c) 2008-2010 ASSIMP Development Team +All rights reserved. + +For convenience a stripped down source tree is included in this distribution. See the license +included at $QT3D_SOURCE/3rdparty/assimp/LICENCE, or at http://assimp.sourceforge.net/main_license.html + +To use your own build of AssImp, modify the assimp.pro file and/or export the environment variables +specified there. diff --git a/src/plugins/sceneformats/assimp/ailoaderiostream.cpp b/src/plugins/sceneformats/assimp/ailoaderiostream.cpp new file mode 100644 index 000000000..0ee44863f --- /dev/null +++ b/src/plugins/sceneformats/assimp/ailoaderiostream.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ailoaderiostream.h" + +#include <QtCore/qiodevice.h> + +#include <QtCore/qdebug.h> + +AiLoaderIOStream::AiLoaderIOStream(QIODevice *device) + : m_device(device) + , m_errorState(false) +{ + Q_ASSERT(device); +} + +AiLoaderIOStream::~AiLoaderIOStream() +{ + // Nothing to do here +} + +size_t AiLoaderIOStream::Read( void* pvBuffer, size_t pSize, size_t pCount) +{ + qint64 result = m_device->read((char*)pvBuffer, pSize * pCount); + size_t res = result; + m_errorState = (result == -1); + if (m_errorState) + fprintf(stderr, "AI read error: %s\n", qPrintable(m_device->errorString())); + if ((pSize * pCount) != res) // AI will treat as error + { + fprintf(stderr, "read mismatch requested size: %lu x count: %lu = %lu != %lu actuall read\n", + pSize, pCount, (pSize * pCount), res); + } + return res; +} + +size_t AiLoaderIOStream::Write( const void* pvBuffer, size_t pSize, size_t pCount) +{ + qint64 result = m_device->write((char*)pvBuffer, pSize * pCount); + m_errorState = (result == -1); + if (m_errorState) + fprintf(stderr, "AI write error: %s\n", qPrintable(m_device->errorString())); + return result; +} + +aiReturn AiLoaderIOStream::Seek(size_t pOffset, aiOrigin pOrigin) +{ + // cannot deal with sockets right now + Q_ASSERT(!m_device->isSequential()); + switch (pOrigin) + { + case aiOrigin_SET: + m_errorState = m_device->seek(pOffset); + break; + case aiOrigin_CUR: + m_errorState = m_device->seek(m_device->pos() + pOffset); + break; + case aiOrigin_END: + m_errorState = m_device->seek(m_device->size() + pOffset); + break; + default: + Q_ASSERT(0); + return(aiReturn_FAILURE); + } + if (m_errorState) + return aiReturn_FAILURE; + return aiReturn_SUCCESS; +} + +size_t AiLoaderIOStream::Tell() const +{ + return m_device->pos(); +} + +size_t AiLoaderIOStream::FileSize() const +{ + return m_device->size(); +} + +void AiLoaderIOStream::Flush() +{ + // do nothing +} diff --git a/src/plugins/sceneformats/assimp/ailoaderiostream.h b/src/plugins/sceneformats/assimp/ailoaderiostream.h new file mode 100644 index 000000000..be3172bb2 --- /dev/null +++ b/src/plugins/sceneformats/assimp/ailoaderiostream.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AILOADERIOSTREAM_H +#define AILOADERIOSTREAM_H + +#include "IOStream.h" +#include "IOSystem.h" + +class QIODevice; + +class AiLoaderIOStream : public Assimp::IOStream +{ +public: + AiLoaderIOStream(QIODevice *device); + ~AiLoaderIOStream(); + size_t Read( void* pvBuffer, size_t pSize, size_t pCount); + size_t Write( const void* pvBuffer, size_t pSize, size_t pCount); + aiReturn Seek( size_t pOffset, aiOrigin pOrigin); + size_t Tell() const; + size_t FileSize() const; + void Flush(); + QIODevice *device() const { return m_device; } +private: + QIODevice *m_device; + bool m_errorState; +}; + +#endif // AILOADERIOSTREAM_H diff --git a/src/plugins/sceneformats/assimp/ailoaderiosystem.cpp b/src/plugins/sceneformats/assimp/ailoaderiosystem.cpp new file mode 100644 index 000000000..b31f275d0 --- /dev/null +++ b/src/plugins/sceneformats/assimp/ailoaderiosystem.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ailoaderiosystem.h" +#include "ailoaderiostream.h" +#include "DefaultLogger.h" + +#include <QtCore/qfile.h> +#include <QtCore/qdir.h> + +AiLoaderIOSystem::AiLoaderIOSystem(QIODevice *device, QUrl url) + : m_device(device) + , m_url(url) +{ +} + +AiLoaderIOSystem::~AiLoaderIOSystem() +{ + // we don't own m_device + qDeleteAll(m_sub); +} + +bool AiLoaderIOSystem::Exists(const char* path) const +{ + return QFile::exists(QLatin1String(path)); +} + +char AiLoaderIOSystem::getOsSeparator() const +{ + return QDir::separator().toLatin1(); +} + +/*! + \internal + Open the pFile with the pMode, where mode is given by "man fopen" +*/ +Assimp::IOStream* AiLoaderIOSystem::Open(const char* pFile, const char* pMode) +{ + // This is just the file already opened on the device + if (m_url.toEncoded().endsWith(pFile)) + return new AiLoaderIOStream(m_device); + + // New relative file + QUrl rel; + rel.setScheme(m_url.scheme()); + rel.setPath(QLatin1String(pFile)); + QUrl url = m_url.resolved(rel); + + // TODO: handle network case + if (url.scheme() != QLatin1String("file")) + { + qWarning("Opening %s url not supported", qPrintable(url.scheme())); + return 0; + } + + char mode_str[4]; + qMemSet(mode_str, '\0', 4); + int i = 0; + for (const char *ptr = pMode; i < 4 && *ptr; ++ptr) + { + if (*ptr != 'b') // ignore the binary attribute + mode_str[i++] = *ptr; + } + QIODevice::OpenMode mode = QIODevice::NotOpen; + if (::strncmp("r", mode_str, 1) == 0) + { + mode = QIODevice::ReadOnly; + } + else if (::strncmp("r+", mode_str, 2) == 0) + { + mode = QIODevice::ReadWrite; + } + else if (::strncmp("w", mode_str, 1) == 0) + { + mode = QIODevice::WriteOnly | QIODevice::Truncate; + } + else if (::strncmp("w+", mode_str, 2) == 0) + { + mode = QIODevice::ReadWrite | QIODevice::Truncate; + } + else if (::strncmp("a", mode_str, 1) == 0) + { + mode = QIODevice::WriteOnly | QIODevice::Append; + } + else if (::strncmp("a+", mode_str, 2) == 0) + { + mode = QIODevice::ReadWrite | QIODevice::Append; + } + else + { + std::string err("Error: invalid mode flag:"); + err.append(mode_str).append(" when opening ").append(pFile); + Assimp::DefaultLogger::get()->warn(err); + return 0; + } + + QFile *f = new QFile(url.toLocalFile()); + bool res = f->open(mode); + if (!res) + { + std::string err("Error: could not open subsequent file:"); + err.append(pFile).append("--").append(f->errorString().toStdString()); + Assimp::DefaultLogger::get()->warn(err); + delete f; + return 0; + } + m_sub.append(f); + AiLoaderIOStream *s = new AiLoaderIOStream(f); + return s; +} + +void AiLoaderIOSystem::Close(Assimp::IOStream* stream) +{ + AiLoaderIOStream *s = static_cast<AiLoaderIOStream*>(stream); + Q_ASSERT(s); + s->device()->close(); + delete stream; +} diff --git a/src/plugins/sceneformats/assimp/ailoaderiosystem.h b/src/plugins/sceneformats/assimp/ailoaderiosystem.h new file mode 100644 index 000000000..8df0a3c75 --- /dev/null +++ b/src/plugins/sceneformats/assimp/ailoaderiosystem.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AILOADERIOSYSTEM_H +#define AILOADERIOSYSTEM_H + +#include "IOSystem.h" + +#include <QtCore/qurl.h> + +class QIODevice; +class IOStream; + +class AiLoaderIOSystem : public Assimp::IOSystem +{ +public: + AiLoaderIOSystem(QIODevice *device, QUrl url); + ~AiLoaderIOSystem(); + virtual bool Exists( const char* path) const; + virtual char getOsSeparator() const; + virtual Assimp::IOStream* Open(const char* pFile, const char* pMode = "rb"); + virtual void Close(Assimp::IOStream* pFile); +private: + QIODevice *m_device; + QList<QIODevice*> m_sub; + QUrl m_url; +}; + +#endif // AILOADERIOSYSTEM_H diff --git a/src/plugins/sceneformats/assimp/assimp.pro b/src/plugins/sceneformats/assimp/assimp.pro new file mode 100644 index 000000000..43b74363e --- /dev/null +++ b/src/plugins/sceneformats/assimp/assimp.pro @@ -0,0 +1,32 @@ +TARGET = qsceneai +include(../../qpluginbase.pri) +HEADERS += qailoader.h \ + qaiscene.h \ + qaiscenehandler.h \ + qaimesh.h \ + ailoaderiostream.h \ + ailoaderiosystem.h +SOURCES += main.cpp \ + qailoader.cpp \ + qaiscene.cpp \ + qaiscenehandler.cpp \ + qaimesh.cpp \ + ailoaderiostream.cpp \ + ailoaderiosystem.cpp +CONFIG += qt3d +system_ai { + !isEmpty(QMAKE_INCDIR_AI):INCLUDEPATH += $$QMAKE_INCDIR_AI + !isEmpty(QMAKE_LIBDIR_AI):LIBS += -L$$QMAKE_LIBDIR_AI + + !isEmpty(QMAKE_LIBS_AI):LIBS += -l$$QMAKE_LIBS_AI + else { + win32:LIBS += -llibai-1_3 + else:LIBS += -lai + } +} else { + include(../../../../3rdparty/assimp/assimp.pri) +} + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/sceneformats +target.path += $$[QT_INSTALL_PLUGINS]/sceneformats +INSTALLS += target diff --git a/src/plugins/sceneformats/assimp/main.cpp b/src/plugins/sceneformats/assimp/main.cpp new file mode 100644 index 000000000..7251e4627 --- /dev/null +++ b/src/plugins/sceneformats/assimp/main.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglsceneformatplugin.h" +#include "qaiscenehandler.h" + +#include "assimp.hpp" + +#include <QtCore/qmap.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +class QAiScenePlugin : public QGLSceneFormatPlugin +{ +public: + QStringList keys() const; + virtual QGLSceneFormatHandler *create(QIODevice *device, const QUrl& url, const QString &format) const; +}; + +QStringList QAiScenePlugin::keys() const +{ + static QMultiMap<QString, QString> mimetypes; + if (mimetypes.size() == 0) + { + mimetypes.insertMulti("3ds", "application/x-3ds"); + mimetypes.insertMulti("3ds", "image/x-3ds"); + mimetypes.insertMulti("dae", "model/x3d+binary"); + mimetypes.insertMulti("dxf", "application/x-dxf"); + } + QStringList result; + Assimp::Importer importer; + aiString extns; + importer.GetExtensionList(extns); + QString qextns = QString::fromUtf8(extns.data, extns.length); + QStringList extnList = qextns.split(';'); + for (int i = 0; i < extnList.size(); ++i) + { + QString xt = extnList.at(i); + xt = xt.simplified(); + if (xt.startsWith(QLatin1String("*."))) + xt = xt.mid(2); + result << xt; + QMap<QString, QString>::const_iterator it = mimetypes.constFind(xt); + for ( ; it != mimetypes.constEnd(); ++it) + result << it.value(); + } + return result; +} + +QGLSceneFormatHandler *QAiScenePlugin::create(QIODevice *device, const QUrl& url, const QString &format) const +{ + Q_UNUSED(device); + Q_UNUSED(url); + Q_UNUSED(format); + return new QAiSceneHandler; +} + +Q_EXPORT_STATIC_PLUGIN(QAiScenePlugin) +Q_EXPORT_PLUGIN2(qsceneai, QAiScenePlugin) + +QT_END_NAMESPACE diff --git a/src/plugins/sceneformats/assimp/qailoader.cpp b/src/plugins/sceneformats/assimp/qailoader.cpp new file mode 100644 index 000000000..18ba1e172 --- /dev/null +++ b/src/plugins/sceneformats/assimp/qailoader.cpp @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qailoader.h" +#include "qaiscenehandler.h" +#include "qaiscene.h" +#include "qaimesh.h" + +#include "qgeometrydata.h" +#include "qgltwosidedmaterial.h" +#include "qglmaterial.h" +#include "qglmaterialcollection.h" +#include "qglpainter.h" +#include "qgltexture2d.h" +#include "qglscenenode.h" +#include "qlogicalvertex.h" + +#include "aiScene.h" +#include "aiMaterial.h" +#include "aiMesh.h" +#include "DefaultLogger.h" + +#include <QtCore/qdir.h> +#include <QtCore/qobject.h> +#include <QtCore/qfileinfo.h> + +QAiLoader::QAiLoader(const aiScene *scene, QAiSceneHandler* handler) + : m_scene(scene) + , m_root(0) + , m_handler(handler) + , m_hasTextures(false) + , m_hasLitMaterials(false) + , m_builder(new QGLMaterialCollection(m_root)) +{ +} + +QAiLoader::~QAiLoader() +{ + // nothing to do here - m_rootNode is taken ownership of by caller of + // rootNode() method +} + +static inline void assertOnePrimitiveType(aiMesh *mesh) +{ +#ifndef QT_NO_DEBUG + int k = 0; // count the number of bits set in the primitives + unsigned int msk = 0x01; + for (unsigned int p = mesh->mPrimitiveTypes; p; p >>= 1) + if (p & msk) + ++k; + Q_ASSERT(k == 1); // Assimp SortByPType promises this +#else + Q_UNUSED(mesh); +#endif +} + +static inline bool qHasTextures(const QGLSceneNode *node) +{ + QGLMaterial *mat = node->material(); + for (int i = 0; i < mat->textureLayerCount(); ++i) + if (node->material()->texture(i)) + return true; + return false; +} + +void QAiLoader::loadMesh(aiMesh *mesh) +{ + QString name = QString::fromUtf8(mesh->mName.data, mesh->mName.length); + // qDebug() << "loadMesh" << name << "with" << mesh->mNumVertices << "vertices" + // << "and" << mesh->mNumFaces << "faces"; + + assertOnePrimitiveType(mesh); + + if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) + { + m_builder.newSection(); + QGLSceneNode *node = m_builder.currentNode(); + node->setObjectName(name); + QAiMesh m(mesh); + m.build(m_builder, m_handler->showWarnings()); + m_meshes.append(node); + if (qHasTextures(node)) + m_hasTextures = true; + else + m_hasLitMaterials = true; + } + else + { + // TODO: Implement other types in qaimesh.cpp + if (m_handler->showWarnings()) + { + QString error = QLatin1String("Bad primitive type in mesh %1 : %2"); + error = error.arg(name).arg(mesh->mPrimitiveTypes); + Assimp::DefaultLogger::get()->warn(error.toStdString()); + } + } + + if (m_handler->showWarnings()) + { + if (!(m_handler->removeComponentFlags() & aiComponent_COLORS)) + { + // TODO: Implement models with per vertex colors. Ok, the + // vertex colors were wanted but are not supported yet. + for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) + { + if (mesh->HasVertexColors(i)) + { + QString error = QLatin1String( + "Found color information in mesh %1, channel %2" + "- per vertex color not yet supported"); + error = error.arg(name).arg(i); + Assimp::DefaultLogger::get()->warn(error.toStdString()); + break; + } + } + } + if (mesh->HasBones()) + { + // TODO: Implement skeletal animation + QString error = QLatin1String("Bones in mesh %1 not yet supported"); + error.arg(name); + Assimp::DefaultLogger::get()->warn(error.toStdString()); + } + if (mesh->HasTangentsAndBitangents()) + { + // TODO: Implement normal maps - here and in the texture import + QString error = QLatin1String("Tangents for normal map in mesh %1 not yet supported"); + error.arg(name); + Assimp::DefaultLogger::get()->warn(error.toStdString()); + } + } +} + +inline static QMatrix4x4 getNodeMatrix(aiNode *node) +{ + QMatrix4x4 nodeMatrix; + if (node->mTransformation.IsIdentity()) + return nodeMatrix; + aiQuaternion rotation; + aiVector3D position; + aiVector3D scale; + node->mTransformation.Decompose(scale, rotation, position); + QVector3D qscale(scale.x,scale.y, scale.z); + QVector3D qposition(position.x, position.y, position.z); + QQuaternion qrotation(rotation.w, rotation.x, rotation.y, rotation.z); + if (!qscale.isNull()) + nodeMatrix.scale(qscale); + if (!qposition.isNull()) + nodeMatrix.translate(qposition); + if (!qrotation.isNull()) + nodeMatrix.rotate(qrotation); + return nodeMatrix; +} + +void QAiLoader::loadNodes(aiNode *nodeList, QGLSceneNode *parentNode) +{ + QMap<aiNode *, QGLSceneNode *>::const_iterator it = m_nodeMap.constFind(nodeList); + QGLSceneNode *node = 0; + if (it == m_nodeMap.constEnd()) // not found + { + node = new QGLSceneNode(parentNode); + m_nodes.append(node); + QString name = QString::fromUtf8(nodeList->mName.data, nodeList->mName.length); + if (name.isEmpty()) + name = QString(QLatin1String("aiNode %1")).arg(m_nodes.size()); + node->setObjectName(name); + QMatrix4x4 mat = getNodeMatrix(nodeList); + if (!mat.isIdentity()) + node->setLocalTransform(mat); + for (unsigned int i = 0; i < nodeList->mNumChildren; ++i) + loadNodes(nodeList->mChildren[i], node); + for (unsigned int i = 0; i < nodeList->mNumMeshes; ++i) + { + int n = nodeList->mMeshes[i]; + if (n < m_meshes.size()) + node->addNode(m_meshes.at(n)); + } + } + else + { + node = it.value(); + parentNode->addNode(node); + } +} + +/*! + \internal + Loads all the geometry, materials, and texture associations from the assigned + file, and returns the root node of the resulting scene graph. + + The caller must take ownership of the root node returned, and delete it + when its no longer required. +*/ +QGLSceneNode *QAiLoader::loadMeshes() +{ + Q_ASSERT(m_scene); + for (unsigned int i = 0; i < m_scene->mNumMaterials; ++i) + loadMaterial(m_scene->mMaterials[i]); + + // builds a naive scene heierarchy with all meshes under the root node + for (unsigned int i = 0; i < m_scene->mNumMeshes; ++i) + loadMesh(m_scene->mMeshes[i]); + + // fetch the naive scene heierarchy from the builder + m_root = m_builder.finalizedSceneNode(); + + QString name = m_handler->url().path(); + int pos = name.lastIndexOf("/"); + if (pos == -1) + pos = name.lastIndexOf("\\"); + if (pos != -1) + name = name.mid(pos+1); + m_root->setObjectName(name); + + // if scene has a node heierarchy replace the naive heierarchy with that + if (m_scene->mRootNode->mNumChildren > 0 && m_scene->mRootNode->mChildren) + { + m_root->removeNodes(m_root->children()); + loadNodes(m_scene->mRootNode, m_root); + } + + if (m_hasTextures) // make textures the default + { + m_root->setEffect(QGL::LitModulateTexture2D); + if (m_hasLitMaterials) + { + for (int i = 0; i < m_meshes.size(); ++i) + if (!qHasTextures(m_meshes.at(i))) + m_meshes.at(i)->setEffect(QGL::LitMaterial); + } + } + else + { + m_root->setEffect(QGL::LitMaterial); + } + + if (m_handler->showWarnings()) + { + QString message = QLatin1String("AssetImporter loader %1 -- " + "Mesh count: %2 -- Node count: %3 -- " + "Material count: %4"); + QUrl url = m_handler->url(); + message = message.arg(url.toString()).arg(m_meshes.size()) + .arg(m_nodes.size()).arg(m_root->palette()->size()); + Assimp::DefaultLogger::get()->warn(message.toStdString()); + } + +//#define DEBUG_ME +#ifdef DEBUG_ME + qDumpScene(m_root); + + QList<QGLSceneNode*> c = m_root->allChildren(); + QSet<quint64> debugged; + for (int i = 0; i < c.size(); ++i) + { + if (c.at(i)->geometry().count() > 0) + { + QGeometryData g = c.at(i)->geometry(); + qDebug() << "geometry for:" << c.at(i) << "is:" << g.id(); + if (!debugged.contains(g.id())) + { + qDebug() << g; + debugged.insert(g.id()); + } + } + } +#endif + + return m_root; +} + +/*! + \internal + Search for a resource based on the given \a path. + + If the URL for the currently loading mesh has a scheme other than + "file" then a URL with the path relative to that URL is returned. + + If the URL for the currently loading mesh has a "file" scheme, then + first a case-sensitive search is done of all of the current directory, + and the :/ resource directory, and the directory of the current mesh + file. + + If the file is not found in any of those locations then they are + searched again case-insensitively. If the file is found, then a + URL based on the absolute file path of the matching file is returned. + + Otherwise an empty string is returned. +*/ +QUrl QAiLoader::ensureResource(const QString &path) +{ + QUrl res; + QUrl base = m_handler->url(); + if (base.scheme() == QLatin1String("file")) + { + res = base.resolved(path); + //qDebug() << "ensureResource - base:" << base + // << " -- path:" << path << "-- resolved:" << res; + if (QFile::exists(res.path())) // shortcut common case + return res; + QStringList paths; + paths << QLatin1String(".") << QLatin1String(":/"); // current directory and aliased/root resource file + if (!base.isEmpty()) + { + QFileInfo fi(base.path()); + paths.prepend(fi.absoluteDir().absolutePath()); + } + bool caseInsensitive = false; + do { + QStringList::const_iterator it(paths.begin()); + for ( ; it != paths.end(); ++it) + { + QDir resDir(*it); + QStringList fileList = resDir.entryList(QDir::Files); + if (caseInsensitive) + { + QStringList::const_iterator fit(fileList.begin()); + for ( ; fit != fileList.end(); ++fit) + { + if (fit->toLower() == path.toLower()) + { + res.setScheme(QLatin1String("file")); + res.setPath(resDir.absoluteFilePath(*fit)); + break; + } + } + } + else + { + if (fileList.contains(path)) + { + //return resDir.absoluteFilePath(path); + res.setScheme(QLatin1String("file")); + res.setPath(resDir.absoluteFilePath(path)); + break; + } + } + } + if (caseInsensitive) + break; + caseInsensitive = true; + } while (true); + } + else + { + // non-file url + res = base.resolved(path); + } + return res; +} + +void QAiLoader::loadTextures(aiMaterial *ma, QGLMaterial *mq) +{ + int texCount; + aiTextureType texType; + if (m_handler->showWarnings()) + { + // TODO: AssImp has a very rich set of texture related functionality + // but until things in this list get implemented, all are just going + // to get ignored, tho' they will generate a warning in verbose mode. + // So for now, its just inside this conditional - move it out once + // implementation of these items begins. + static const char * typeNames[] = { + "None", + "Diffuse", + "Specular", + "Ambient", + "Emissive", + "Height", + "Normals", + "Shininess", + "Opacity", + "Displacement", + "Lightmap", + "Reflection", + "Unknown", + 0 + }; + + for (unsigned int i = 0; i <= aiTextureType_UNKNOWN; ++i) + { + texType = static_cast<aiTextureType>(i); + texCount = ma->GetTextureCount(texType); + if (texCount && texType != aiTextureType_DIFFUSE) + { + QString error = QLatin1String("Unsupported texture type \"%1\" in material \"%2\"."); + error.arg(typeNames[i]).arg(mq->objectName()); + Assimp::DefaultLogger::get()->warn(error.toStdString()); + } + } + } + // TODO: For now assume the only texture we care about is the diffuse one + // + texCount = ma->GetTextureCount(aiTextureType_DIFFUSE); + if (texCount > 0) + { + if (texCount > 1 && m_handler->showWarnings()) + { + QString error = QLatin1String("Multi-textures not supported: \"%1\" has %2"); + error.arg(mq->objectName()).arg(texCount); + Assimp::DefaultLogger::get()->warn(error.toStdString()); + } + else + { + aiString path; + ma->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), path); + QString qpath = QString::fromUtf8(path.data, path.length); + QUrl url = ensureResource(qpath); + if (url.isEmpty()) + { + if (m_handler->showWarnings()) + { + QString error = QLatin1String("Could not load texture: %1 for material %2"); + error.arg(url.toString()).arg(mq->objectName()); + Assimp::DefaultLogger::get()->warn(error.toStdString()); + } + } + else + { + mq->setTextureUrl(url); + } + } + } +} + +/*! + Load a material +*/ +void QAiLoader::loadMaterial(aiMaterial *ma) +{ + QGLMaterialCollection *palette = m_builder.palette(); + QGLMaterial *mq = new QGLMaterial; + mq->setObjectName("___DEFAULT_NAME___"); + + bool isTwoSided = false; + bool isWireframe = false; + struct aiColor4D clr; + float shininess; + float amplitude; + + aiReturn r; + + // In AssImp if the material is two sided then it means cull face + // should be turned off: http://assimp.sourceforge.net/lib_html/materials.html + r = ma->Get(AI_MATKEY_TWOSIDED, isTwoSided); + if (r == aiReturn_SUCCESS && isTwoSided) + mq->setProperty("isTwoSided", isTwoSided); + + aiString aName; + r = ma->Get(AI_MATKEY_NAME, aName); + if (r == aiReturn_SUCCESS && aName.length > 0) + mq->setObjectName(QLatin1String(aName.data)); + + r = ma->Get(AI_MATKEY_COLOR_AMBIENT, clr); + if (r == aiReturn_SUCCESS) + mq->setAmbientColor(QColor::fromRgbF(clr.r, clr.g, clr.b, clr.a)); + + r = ma->Get(AI_MATKEY_COLOR_DIFFUSE, clr); + if (r == aiReturn_SUCCESS) + mq->setDiffuseColor(QColor::fromRgbF(clr.r, clr.g, clr.b, clr.a)); + + r = ma->Get(AI_MATKEY_COLOR_SPECULAR, clr); + if (r == aiReturn_SUCCESS) + { + QColor spec = QColor::fromRgbF(clr.r, clr.g, clr.b, clr.a); + mq->setSpecularColor(spec); + + // By default the specular color is black - very dark. + // If the specular color is bright be careful with shininess - + // a shininess of 0 will blow everything out to full white. So in + // the case of bad materials with this problem, set shiness low. + mq->setShininess(64.0); + } + + r = ma->Get(AI_MATKEY_SHININESS, shininess); + if (r == aiReturn_SUCCESS) + mq->setShininess(shininess); + + r = ma->Get(AI_MATKEY_SHININESS_STRENGTH, amplitude); + if (r == aiReturn_SUCCESS) + mq->setShininess(shininess * amplitude); + + r = ma->Get(AI_MATKEY_ENABLE_WIREFRAME, isWireframe); + if (r == aiReturn_SUCCESS && isWireframe) + mq->setProperty("isWireFrame", isWireframe); + + loadTextures(ma, mq); + + // INVARIANT: since we create the palette newly in this class, and this + // function is the only place we add materials to this palette, the index + // values (the positions in the palette generated by this call to addMaterial) + // will exactly match the index values of the materials traversed in the + // for loop in loadMeshes() - so therefore AI's index values and the ones in + // the palette will be the same. + // + // executive summary: don't muck around with the palettte outside of this call + + int k = palette->addMaterial(mq); + + Q_UNUSED(k); + //qDebug() << "loaded material" << k << mq; +} diff --git a/src/plugins/sceneformats/assimp/qailoader.h b/src/plugins/sceneformats/assimp/qailoader.h new file mode 100644 index 000000000..cb646295a --- /dev/null +++ b/src/plugins/sceneformats/assimp/qailoader.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGL3DSLOADER_H +#define QGL3DSLOADER_H + +#include <QtCore/qurl.h> +#include <QtCore/qstring.h> +#include <QtCore/qmap.h> + +#include "aiScene.h" + +#include "qglbuilder.h" + +class QAiMesh; +class QGLSceneNode; +class QAiSceneHandler; +class QGLMaterial; +class aiMaterial; + +class QAiLoader +{ +public: + QAiLoader(const aiScene *scene, QAiSceneHandler* handler); + ~QAiLoader(); + QGLSceneNode *loadMeshes(); + +private: + void loadMesh(aiMesh *); + void loadNodes(aiNode *, QGLSceneNode *); + void loadMaterial(aiMaterial *); + void loadTextures(aiMaterial *, QGLMaterial *); + QUrl ensureResource(const QString &); + void optimizeData(); + void optimizeNodes(QGLSceneNode *node = 0, QGLSceneNode *parent = 0); + void countChildNodeReferences(); + + const aiScene *m_scene; + QGLSceneNode *m_root; + QAiSceneHandler *m_handler; + QList<QGLSceneNode *> m_nodes; + QList<QGLMaterial *> m_materials; + QList<QGLSceneNode *> m_meshes; + QMap<aiNode *, QGLSceneNode *> m_nodeMap; + QMap<QGLSceneNode *, int> m_refCounts; + bool m_hasTextures; + bool m_hasLitMaterials; + QGLBuilder m_builder; +}; + +#endif // QGL3DSLOADER_H diff --git a/src/plugins/sceneformats/assimp/qaimesh.cpp b/src/plugins/sceneformats/assimp/qaimesh.cpp new file mode 100644 index 000000000..e7d801dc3 --- /dev/null +++ b/src/plugins/sceneformats/assimp/qaimesh.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaimesh.h" +#include "qglscenenode.h" +#include "qglmaterialcollection.h" +#include "qglbuilder.h" + +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qmath.h> + +#include "aiMesh.h" +#include "DefaultLogger.h" + +QAiMesh::QAiMesh(aiMesh *mesh) + : m_mesh(mesh) +{ +} + +QAiMesh::~QAiMesh() +{ +} + +static inline QVector2D qv2d(const aiVector3D &v) +{ + return QVector2D(v.x, v.y); +} + +static inline QVector2D qv2d_inv(const aiVector3D &v) +{ + // invert the v coord because Qt is upside-down + return QVector2D(v.x, (1.0 - v.y)); +} + +static inline QVector3D qv3d(const aiVector3D &v) +{ + return QVector3D(v.x, v.y, v.z); +} + +void QAiMesh::loadTriangles(QGLBuilder &builder) +{ + QGeometryData data; + for (unsigned int i = 0; i < m_mesh->mNumVertices; ++i) + data.appendVertex(qv3d(m_mesh->mVertices[i])); + if (m_mesh->HasNormals()) + for (unsigned int i = 0; i < m_mesh->mNumVertices; ++i) + data.appendNormal(qv3d(m_mesh->mNormals[i])); + int k = m_mesh->GetNumUVChannels(); + for (int t = 0; t < k; ++t) + { + if (m_mesh->mNumUVComponents[t] != 2) + Assimp::DefaultLogger::get()->warn("Tex co-ords only supports U & V"); + QGLMaterial *m = builder.currentNode()->material(); + if (m && m->textureUrl().path().endsWith(".dds", Qt::CaseInsensitive)) + { + for (unsigned int i = 0; i < m_mesh->mNumVertices; ++i) + data.appendTexCoord(qv2d_inv(m_mesh->mTextureCoords[t][i]), static_cast<QGL::VertexAttribute>(QGL::TextureCoord0 + t)); + } + else + { + for (unsigned int i = 0; i < m_mesh->mNumVertices; ++i) + data.appendTexCoord(qv2d(m_mesh->mTextureCoords[t][i]), static_cast<QGL::VertexAttribute>(QGL::TextureCoord0 + t)); + } + } + + for (unsigned int i = 0; i < m_mesh->mNumFaces; ++i) + { + aiFace *face = &m_mesh->mFaces[i]; + data.appendIndices(face->mIndices[0], face->mIndices[1], face->mIndices[2]); + } + + // raw triangle mode + builder.addTriangles(data); +} + +void QAiMesh::build(QGLBuilder &builder, bool showWarnings) +{ + QGLSceneNode *node = builder.currentNode(); + QString name = node->objectName(); + + if (!m_mesh->HasFaces() || !m_mesh->HasPositions()) + { + if (showWarnings) + { + QString error = QLatin1String("Mesh %1 has zero vertex/face count"); + error.arg(name.isEmpty() ? QString(QLatin1String("<unnamed mesh>")) : name); + Assimp::DefaultLogger::get()->warn(error.toStdString()); + } + return; + } + + node->setMaterialIndex(m_mesh->mMaterialIndex); + node->palette()->markMaterialAsUsed(m_mesh->mMaterialIndex); + + if (m_mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) + loadTriangles(builder); + else + return; // TODO - lines, points, quads, polygons + + QGLMaterial * mat = node->palette()->material(m_mesh->mMaterialIndex); + if (mat->property("isTwoSided").isValid() && mat->property("isTwoSided").toBool()) + node->setBackMaterialIndex(m_mesh->mMaterialIndex); + if (mat->property("isWireFrame").isValid() && mat->property("isWireFrame").toBool()) + node->setDrawingMode(QGL::Lines); +} diff --git a/src/plugins/sceneformats/assimp/qaimesh.h b/src/plugins/sceneformats/assimp/qaimesh.h new file mode 100644 index 000000000..2a1efc2ed --- /dev/null +++ b/src/plugins/sceneformats/assimp/qaimesh.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGL3DSMESH_H +#define QGL3DSMESH_H + +#include "qaiscenehandler.h" +#include "qgeometrydata.h" + +class QGLMaterialCollection; +class QGLSceneNode; +class QGLBuilder; +class QAiLoader; +class aiMesh; + +class QAiMesh +{ +public: + QAiMesh(aiMesh *mesh); + virtual ~QAiMesh(); + + void build(QGLBuilder &builder, bool showWarnings = false); +private: + void loadTriangles(QGLBuilder &builder); + + aiMesh *m_mesh; +}; + +#endif // QGL3DSMESH_H diff --git a/src/plugins/sceneformats/assimp/qaiscene.cpp b/src/plugins/sceneformats/assimp/qaiscene.cpp new file mode 100644 index 000000000..cb67fe99d --- /dev/null +++ b/src/plugins/sceneformats/assimp/qaiscene.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaiscene.h" +#include "qaimesh.h" +#include "qailoader.h" + +#include "qglscenenode.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QAiScene + \brief The QAiScene class manages and contains an asset importer scene. + The scene consists of a QGLSceneNode object which is the top-level node + and references the geometry imported from a file. +*/ + +/*! + \internal + Construct a new QAiScene object using the data in the \a scene, + and setting the given \a parent. + + The QAiScene object takes ownership of the \a file. +*/ +QAiScene::QAiScene(const aiScene *scene, QAiSceneHandler *handler) + : QGLAbstractScene(0) +{ + Q_ASSERT(handler); + Q_ASSERT(scene); + QAiLoader loader(scene, handler); + m_root = loader.loadMeshes(); +} + +/*! + \reimp + Destroy this QAiScene, recovering all resources. + + This method destroys the Lib3dsFile object the scene is based + on by calling the appropriate lib3ds function. +*/ +QAiScene::~QAiScene() +{ + // nothing to do here +} + +/*! + \internal + \reimp +*/ +QList<QObject *> QAiScene::objects() const +{ + QList<QObject *> objs; + if (!m_root) + return objs; + objs.append(m_root); + QList<QGLSceneNode*> children = m_root->allChildren(); + QList<QGLSceneNode*>::const_iterator it = children.constBegin(); + for ( ; it != children.constEnd(); ++it) + objs.append(*it); + return objs; +} + +/*! + \internal + \reimp +*/ +QGLSceneNode *QAiScene::mainNode() const +{ + return m_root; +} + +QT_END_NAMESPACE diff --git a/src/plugins/sceneformats/assimp/qaiscene.h b/src/plugins/sceneformats/assimp/qaiscene.h new file mode 100644 index 000000000..5b9aac58c --- /dev/null +++ b/src/plugins/sceneformats/assimp/qaiscene.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGL3DSSCENE_H +#define QGL3DSSCENE_H + +#include "qglabstractscene.h" + +#include "aiScene.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +#include <QtCore/qurl.h> + +class QGLSceneNode; +class QAiSceneHandler; + +class QAiScene : public QGLAbstractScene +{ + Q_OBJECT +public: + explicit QAiScene(const aiScene *scene, QAiSceneHandler *handler); + virtual ~QAiScene(); + + QList<QObject *> objects() const; + QGLSceneNode *mainNode() const; +private: + QGLSceneNode *m_root; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/sceneformats/assimp/qaiscenehandler.cpp b/src/plugins/sceneformats/assimp/qaiscenehandler.cpp new file mode 100644 index 000000000..259bf9021 --- /dev/null +++ b/src/plugins/sceneformats/assimp/qaiscenehandler.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaiscenehandler.h" +#include "qaiscene.h" +#include "ailoaderiosystem.h" + +#include "aiScene.h" +#include "aiPostProcess.h" +#include "DefaultLogger.h" + +#include <QtCore/qdir.h> +#include <QtCore/qdebug.h> + + +#define qAiPostProcessPreset ( \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenSmoothNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_ImproveCacheLocality | \ + aiProcess_LimitBoneWeights | \ + aiProcess_RemoveRedundantMaterials | \ + aiProcess_SplitLargeMeshes | \ + aiProcess_Triangulate | \ + aiProcess_GenUVCoords | \ + aiProcess_SortByPType | \ + aiProcess_FindDegenerates | \ + aiProcess_FindInvalidData ) + +QT_BEGIN_NAMESPACE + +QAiSceneHandler::QAiSceneHandler() + : m_options(qAiPostProcessPreset) + , m_showWarnings(false) + , m_mayHaveLinesPoints(false) + , m_meshSplitVertexLimit(2000) + , m_meshSplitTriangleLimit(2000) + , m_removeComponentFlags(0) + , m_removeSortFlags(0) +{ + // by default remove per vertex colors from the data - no-one uses that in + // models - if they need it it can be turned on with UseVertexColors + m_removeComponentFlags |= aiComponent_COLORS; + + // by default remove points and lines from the model, since these are usually + // degenerate structures from bad modelling or bad import/export. if they + // are needed it can be turned on with IncludeLinesPoints + m_removeSortFlags |= aiPrimitiveType_POINT | aiPrimitiveType_LINE; +} + +QAiSceneHandler::~QAiSceneHandler() +{ + // nothing to do +} + +void QAiSceneHandler::decodeOptions(const QString &options) +{ + if (options.isEmpty()) + return; + + // See aiPostProcess.h for aiProcessPreset_TargetRealtime_Quality + // - a useful default set of values - its exactly what we want but + // wont compile with flags, so redefined with the above macro. + // Also, allow the user to override some settings + m_options = qAiPostProcessPreset; + + // Has to match the enum + static const char *validOptions[] = { + "NoOptions", + "ShowWarnings", + "CalculateNormals", + "ForceFaceted", + "ForceSmooth", + "IncludeAllMaterials", + "IncludeLinesPoints", + "FixNormals", + "DeDupMeshes", + "Optimize", + "FlipUVs", + "FlipWinding", + "UseVertexColors", + "VertexSplitLimitx2", + "TriangleSplitLimitx2", + 0 + }; + + QStringList opList = options.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts); + + for (int i = 0; i < opList.count(); ++i) + { + QString op = opList.at(i); + op = op.trimmed(); + int k = 0; + for ( ; validOptions[k]; ++k) + if (op == QString::fromLatin1(validOptions[k])) + break; + if (validOptions[k]) // found + { + Options o = static_cast<Options>(k); + switch (o) + { + case NoOptions: + break; + case ShowWarnings: + if (m_showWarnings) + { + m_importer.SetExtraVerbose(true); + } + else + { + m_showWarnings = true; + m_options |= aiProcess_ValidateDataStructure; + } + break; + case CalculateNormals: + m_removeComponentFlags |= aiComponent_NORMALS; + m_options |= aiProcess_GenSmoothNormals; + m_options &= ~aiProcess_GenNormals; + break; + case ForceFaceted: + m_removeComponentFlags |= aiComponent_NORMALS; + m_options |= aiProcess_GenNormals; + m_options &= ~aiProcess_GenSmoothNormals; + m_options &= ~aiProcess_JoinIdenticalVertices; + break; + case ForceSmooth: + Assimp::DefaultLogger::get()->warn("ForceSmooth is deprecated - ignoring (meshes now smooth by default)"); + break; + case IncludeAllMaterials: + m_options &= ~aiProcess_RemoveRedundantMaterials; + break; + case IncludeLinesPoints: + m_removeSortFlags &= ~(aiPrimitiveType_LINE | aiPrimitiveType_POINT); + m_mayHaveLinesPoints = true; + // leave it with the FindDegenerates option turned on - we want zero + // area triangles to display as proper GL lines or points + break; + case FixNormals: + m_options |= aiProcess_FixInfacingNormals; + break; + case DeDupMeshes: + m_options |= aiProcess_FindInstances; + break; + case Optimize: + m_options |= aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes; + break; + case FlipUVs: + m_options |= aiProcess_FlipUVs; + break; + case FlipWinding: + m_options |= aiProcess_FlipWindingOrder; + break; + case UseVertexColors: + m_removeComponentFlags &= ~aiComponent_COLORS; + break; + case VertexSplitLimitx2: + m_meshSplitVertexLimit <<= 1; + // repeating this in the option string more than once works... + break; + case TriangleSplitLimitx2: + // ....and we're OK with that, just don't overdo it + m_meshSplitTriangleLimit <<= 1; + break; + } + } + else + { + qWarning("Bad option: \"%s\" in %s", qPrintable(op), + qPrintable(options)); + } + } +} + +QGLAbstractScene *QAiSceneHandler::read() +{ + AiLoaderIOSystem *ios = new AiLoaderIOSystem(device(), url()); + m_importer.SetIOHandler(ios); + + Assimp::Logger *log = 0; + Assimp::Logger::LogSeverity severity = Assimp::Logger::NORMAL; + if (m_showWarnings) + { + severity = Assimp::Logger::VERBOSE; + int streams = aiDefaultLogStream_FILE | +#ifdef Q_CC_MSVC + aiDefaultLogStream_DEBUGGER +#else + aiDefaultLogStream_STDERR +#endif + ; + log = Assimp::DefaultLogger::create("AssimpLog.txt", severity, streams); + } + + QString path; + QUrl u = url(); + if (u.scheme() != QLatin1String("file")) + { + qWarning("Non-file URL's not yet supported"); + return 0; + } + path = u.toLocalFile(); + + if (m_removeComponentFlags) + m_options |= aiProcess_RemoveComponent; + else + m_options &= ~aiProcess_RemoveComponent; + + m_importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, m_removeComponentFlags); + m_importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, m_removeSortFlags); + m_importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT, m_meshSplitVertexLimit); + m_importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, m_meshSplitTriangleLimit); + + // force this on, and provide no way to turn it off. Its set by the + // aiProcessPreset_TargetRealtime_Quality option in the constructor. + // Guarantees that all meshes only have one primitive type + Q_ASSERT(m_options & aiProcess_SortByPType); + + // the importer owns the scene, so when the it goes out of scope on exiting + // this function the scene will get destroyed + const aiScene* scene = m_importer.ReadFile(path.toStdString(), m_options); + + if (!scene) + { + // Notes on import success flags - according to assimp doco if validation + // is requested the flags AI_SCENE_FLAGS_VALIDATION_WARNING will be set + // if there's a warning, and AI_SCENE_FLAGS_VALIDATED is set on success. + // This does not happen. Also AI_SCENE_FLAGS_INCOMPLETE can be set on a + // valid model, so checking for that is no use either. Best way to proceed + // is that if ShowWarnings is turned on above, then any pertinent warnings + // will be shown; and if a NULL result is returned here, then its a fatal + // error and a message is shown here. If a non-NULL result is returned + // just go ahead and try to load it. + QString c = QDir::current().absolutePath(); + qWarning("Asset importer error: %s\n", m_importer.GetErrorString()); + if (log) + qWarning("For details check log: %s/AssimpLog.txt\n", qPrintable(c)); + return 0; + } + + + QAiScene *qscene = new QAiScene(scene, this); + + Assimp::DefaultLogger::kill(); + + return qscene; +} + +QT_END_NAMESPACE diff --git a/src/plugins/sceneformats/assimp/qaiscenehandler.h b/src/plugins/sceneformats/assimp/qaiscenehandler.h new file mode 100644 index 000000000..95b800025 --- /dev/null +++ b/src/plugins/sceneformats/assimp/qaiscenehandler.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGL3DSSCENEHANDLER_H +#define QGL3DSSCENEHANDLER_H + +#include "qglsceneformatplugin.h" + +#include "aiPostProcess.h" +#include "assimp.hpp" + +#include <QtCore/qurl.h> + +Q_DECLARE_FLAGS(aiPostProcessFlags, aiPostProcessSteps); +Q_DECLARE_OPERATORS_FOR_FLAGS(aiPostProcessFlags); + + +QT_BEGIN_NAMESPACE + +class QIODevice; + +class QAiSceneHandler : public QGLSceneFormatHandler +{ +public: + enum Options { + NoOptions, + ShowWarnings, // show any warnings while loading the file + CalculateNormals, // replace normals from the file with smooth generated ones + ForceFaceted, // generate non-smooth normals (implies CalculateNormals) + ForceSmooth, // deprecated - retained only for backward compatibility + IncludeAllMaterials, // include even redundant (unused) materials + IncludeLinesPoints, // include even collapsed triangles (lines or points) + FixNormals, // try to fix incorrect (in facing) normals + DeDupMeshes, // replace copied meshes with refs to a single instance + Optimize, // collapse meshes, nodes & scene heierarchies + FlipUVs, // flips UV's on the y-axis (for upside-down textures) + FlipWinding, // makes faces CW instead of CCW + UseVertexColors, // use vertex colors that are in a model + VertexSplitLimitx2, // double the vertex count which will split a large mesh + TriangleSplitLimitx2 // double the triangle count which will split a large mesh + }; + + QAiSceneHandler(); + ~QAiSceneHandler(); + + QGLAbstractScene *read(); + + void decodeOptions(const QString &options); + + bool showWarnings() const { return m_showWarnings; } + bool mayHaveLinesPoints() const { return m_mayHaveLinesPoints; } + + aiPostProcessFlags options() const { return m_options; } + quint32 removeComponentFlags() const { return m_removeComponentFlags; } + quint32 removeSortFlags() const { return m_removeSortFlags; } + +private: + aiPostProcessFlags m_options; + bool m_showWarnings; + bool m_mayHaveLinesPoints; + int m_meshSplitVertexLimit; + int m_meshSplitTriangleLimit; + Assimp::Importer m_importer; + quint32 m_removeComponentFlags; + quint32 m_removeSortFlags; +}; + +QT_END_NAMESPACE + +#endif |