summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMike Krus <mike.krus@kdab.com>2020-06-16 12:51:28 +0100
committerMike Krus <mike.krus@kdab.com>2020-06-17 16:59:31 +0100
commitb9994cd88925ca012d66e52d033cc9a3a909fc7a (patch)
tree2666e92b68e4d427e5d9fce5631856fc79ba5e2f /tools
parent66c088a25dbb3518d568143231c1771f20d7b0b3 (diff)
Remove custom gltf tool
Change-Id: I36ae2a043af155d2f2408dce5fcfae7f6e98823d Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt5
-rw-r--r--tools/qgltf/CMakeLists.txt22
-rw-r--r--tools/qgltf/qgltf.cpp2589
-rw-r--r--tools/qgltf/qgltf.pro7
-rw-r--r--tools/tools.pro9
5 files changed, 0 insertions, 2632 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
deleted file mode 100644
index d9e4ecdcd..000000000
--- a/tools/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Generated from tools.pro.
-
-#if(QT_FEATURE_assimp AND QT_FEATURE_commandlineparser AND NOT CMAKE_CROSSCOMPILING)
-# add_subdirectory(qgltf)
-#endif()
diff --git a/tools/qgltf/CMakeLists.txt b/tools/qgltf/CMakeLists.txt
deleted file mode 100644
index be90beda8..000000000
--- a/tools/qgltf/CMakeLists.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated from qgltf.pro.
-
-#####################################################################
-## qgltf Tool:
-#####################################################################
-
-qt_add_tool(qgltf
- TOOLS_TARGET 3DRender # special case
- SOURCES
- qgltf.cpp
- EXCEPTIONS # special case
- PUBLIC_LIBRARIES
- Qt::Gui
-)
-
-#### Keys ignored in scope 1:.:.:qgltf.pro:<TRUE>:
-# _OPTION = "host_build"
-
-## Scopes:
-#####################################################################
-include(../../src/3rdparty/assimp/assimp.cmake)
-qt3d_extend_target_for_assimp(qgltf)
diff --git a/tools/qgltf/qgltf.cpp b/tools/qgltf/qgltf.cpp
deleted file mode 100644
index 559ad7691..000000000
--- a/tools/qgltf/qgltf.cpp
+++ /dev/null
@@ -1,2589 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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 <assimp/Importer.hpp>
-#include <assimp/IOStream.hpp>
-#include <assimp/IOSystem.hpp>
-#include <assimp/scene.h>
-#include <assimp/postprocess.h>
-
-#include <qiodevice.h>
-#include <qfile.h>
-#include <qfileinfo.h>
-#include <qdir.h>
-#include <qhash.h>
-#include <qdebug.h>
-#include <qcoreapplication.h>
-#include <qcommandlineparser.h>
-#include <qjsondocument.h>
-#include <qjsonobject.h>
-#include <qjsonarray.h>
-#include <qcborvalue.h>
-#include <qmath.h>
-
-#define GLT_UNSIGNED_SHORT 0x1403
-#define GLT_UNSIGNED_INT 0x1405
-#define GLT_FLOAT 0x1406
-
-#define GLT_FLOAT_VEC2 0x8B50
-#define GLT_FLOAT_VEC3 0x8B51
-#define GLT_FLOAT_VEC4 0x8B52
-#define GLT_FLOAT_MAT3 0x8B5B
-#define GLT_FLOAT_MAT4 0x8B5C
-#define GLT_SAMPLER_2D 0x8B5E
-
-#define GLT_ARRAY_BUFFER 0x8892
-#define GLT_ELEMENT_ARRAY_BUFFER 0x8893
-
-#define GLT_DEPTH_TEST 0x0B71
-#define GLT_CULL_FACE 0x0B44
-#define GLT_BLEND 0x0BE2
-
-class AssimpIOStream : public Assimp::IOStream
-{
-public:
- AssimpIOStream(QIODevice *device);
- ~AssimpIOStream();
-
- size_t Read(void *pvBuffer, size_t pSize, size_t pCount) override;
- size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override;
- aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override;
- size_t Tell() const override;
- size_t FileSize() const override;
- void Flush() override;
-
-private:
- QIODevice *m_device;
-};
-
-class AssimpIOSystem : public Assimp::IOSystem
-{
-public:
- bool Exists(const char *pFile) const override;
- char getOsSeparator() const override;
- Assimp::IOStream *Open(const char *pFile, const char *pMode) override;
- void Close(Assimp::IOStream *pFile) override;
-};
-
-AssimpIOStream::AssimpIOStream(QIODevice *device) :
- m_device(device)
-{
- Q_ASSERT(m_device);
-}
-
-AssimpIOStream::~AssimpIOStream()
-{
- delete m_device;
-}
-
-size_t AssimpIOStream::Read(void *pvBuffer, size_t pSize, size_t pCount)
-{
- qint64 readBytes = m_device->read((char *)pvBuffer, pSize * pCount);
- if (readBytes < 0)
- qWarning() << Q_FUNC_INFO << " read failed";
- return readBytes;
-}
-
-size_t AssimpIOStream::Write(const void *pvBuffer, size_t pSize, size_t pCount)
-{
- qint64 writtenBytes = m_device->write((char *)pvBuffer, pSize * pCount);
- if (writtenBytes < 0)
- qWarning() << Q_FUNC_INFO << " write failed";
- return writtenBytes;
-}
-
-aiReturn AssimpIOStream::Seek(size_t pOffset, aiOrigin pOrigin)
-{
- qint64 seekPos = pOffset;
-
- if (pOrigin == aiOrigin_CUR)
- seekPos += m_device->pos();
- else if (pOrigin == aiOrigin_END)
- seekPos += m_device->size();
-
- if (!m_device->seek(seekPos)) {
- qWarning() << Q_FUNC_INFO << " seek failed";
- return aiReturn_FAILURE;
- }
- return aiReturn_SUCCESS;
-}
-
-size_t AssimpIOStream::Tell() const
-{
- return m_device->pos();
-}
-
-size_t AssimpIOStream::FileSize() const
-{
- return m_device->size();
-}
-
-void AssimpIOStream::Flush()
-{
- // we don't write via assimp
-}
-
-static QIODevice::OpenMode openModeFromText(const char *name) noexcept
-{
- static const struct OpenModeMapping {
- char name[2];
- int mode;
- } openModeMapping[] = {
- { { 'r', 0 }, QIODevice::ReadOnly },
- { { 'r', '+' }, QIODevice::ReadWrite },
- { { 'w', 0 }, QIODevice::WriteOnly | QIODevice::Truncate },
- { { 'w', '+' }, QIODevice::ReadWrite | QIODevice::Truncate },
- { { 'a', 0 }, QIODevice::WriteOnly | QIODevice::Append },
- { { 'a', '+' }, QIODevice::ReadWrite | QIODevice::Append },
- { { 'w', 'b' }, QIODevice::WriteOnly },
- { { 'w', 't' }, QIODevice::WriteOnly | QIODevice::Text },
- { { 'r', 'b' }, QIODevice::ReadOnly },
- { { 'r', 't' }, QIODevice::ReadOnly | QIODevice::Text },
- };
-
- for (auto e : openModeMapping) {
- if (qstrncmp(e.name, name, sizeof(OpenModeMapping::name)) == 0)
- return static_cast<QIODevice::OpenMode>(e.mode);
- }
- return QIODevice::NotOpen;
-}
-
-bool AssimpIOSystem::Exists(const char *pFile) const
-{
- return QFileInfo::exists(QString::fromUtf8(pFile));
-}
-
-char AssimpIOSystem::getOsSeparator() const
-{
- return QDir::separator().toLatin1();
-}
-
-Assimp::IOStream *AssimpIOSystem::Open(const char *pFile, const char *pMode)
-{
- const QString fileName(QString::fromUtf8(pFile));
- const QLatin1String cleanedMode = QLatin1String{pMode}.trimmed();
-
- if (const QIODevice::OpenMode openMode = openModeFromText(cleanedMode.data())) {
- QScopedPointer<QFile> file(new QFile(fileName));
- if (file->open(openMode))
- return new AssimpIOStream(file.take());
- }
-
- return nullptr;
-}
-
-void AssimpIOSystem::Close(Assimp::IOStream *pFile)
-{
- delete pFile;
-}
-
-static inline QString ai2qt(const aiString &str)
-{
- return QString::fromUtf8(str.data, int(str.length));
-}
-
-static inline QVector<float> ai2qt(const aiMatrix4x4 &matrix)
-{
- return QVector<float>() << matrix.a1 << matrix.b1 << matrix.c1 << matrix.d1
- << matrix.a2 << matrix.b2 << matrix.c2 << matrix.d2
- << matrix.a3 << matrix.b3 << matrix.c3 << matrix.d3
- << matrix.a4 << matrix.b4 << matrix.c4 << matrix.d4;
-}
-
-struct Options {
- QString outDir;
-#if QT_CONFIG(cborstreamwriter)
- bool genBin;
-#endif
- bool compact;
- bool compress;
- bool genTangents;
- bool interleave;
- float scale;
- bool genCore;
- enum TextureCompression {
- NoTextureCompression,
- ETC1
- };
- TextureCompression texComp;
- bool commonMat;
- bool shaders;
- bool showLog;
-} opts;
-
-class Importer
-{
-public:
- Importer();
- virtual ~Importer();
-
- virtual bool load(const QString &filename) = 0;
-
- struct BufferInfo {
- QString name;
- QByteArray data;
- };
- QVector<BufferInfo> buffers() const;
-
- struct MeshInfo {
- struct BufferView {
- BufferView() : bufIndex(0), offset(0), length(0), componentType(0), target(0) { }
- QString name;
- uint bufIndex;
- uint offset;
- uint length;
- uint componentType;
- uint target;
- };
- QVector<BufferView> views;
- struct Accessor {
- Accessor() : offset(0), stride(0), count(0), componentType(0) { }
- QString name;
- QString usage;
- QString bufferView;
- uint offset;
- uint stride;
- uint count;
- uint componentType;
- QString type;
- QVector<float> minVal;
- QVector<float> maxVal;
- };
- QVector<Accessor> accessors;
- QString name; // generated
- QString originalName; // may be empty
- uint materialIndex;
- };
-
- QVector<MeshInfo::BufferView> bufferViews() const;
- QVector<MeshInfo::Accessor> accessors() const;
- uint meshCount() const;
- MeshInfo meshInfo(uint meshIndex) const;
-
- struct MaterialInfo {
- QString name;
- QString originalName;
- QHash<QByteArray, QVector<float> > m_colors;
- QHash<QByteArray, float> m_values;
- QHash<QByteArray, QString> m_textures;
- };
- uint materialCount() const;
- MaterialInfo materialInfo(uint materialIndex) const;
-
- QSet<QString> externalTextures() const;
-
- struct CameraInfo {
- QString name; // suffixed
- float aspectRatio;
- float yfov;
- float zfar;
- float znear;
- };
- QHash<QString, CameraInfo> cameraInfo() const;
-
- struct EmbeddedTextureInfo {
- EmbeddedTextureInfo() { }
- QString name;
-#ifdef HAS_QIMAGE
- EmbeddedTextureInfo(const QString &name, const QImage &image) : name(name), image(image) { }
- QImage image;
-#endif
- };
- QHash<QString, EmbeddedTextureInfo> embeddedTextures() const;
-
- struct Node {
- QString name;
- QString uniqueName; // generated
- QVector<float> transformation;
- QVector<Node *> children;
- QVector<uint> meshes;
- };
- const Node *rootNode() const;
-
- struct KeyFrame {
- KeyFrame() : t(0), transValid(false), rotValid(false), scaleValid(false) { }
- float t;
- bool transValid;
- QVector<float> trans;
- bool rotValid;
- QVector<float> rot;
- bool scaleValid;
- QVector<float> scale;
- };
- struct AnimationInfo {
- AnimationInfo() : hasTranslation(false), hasRotation(false), hasScale(false) { }
- QString name;
- QString targetNode;
- bool hasTranslation;
- bool hasRotation;
- bool hasScale;
- QVector<KeyFrame> keyFrames;
- };
- QVector<AnimationInfo> animations() const;
-
- bool allMeshesForMaterialHaveTangents(uint materialIndex) const;
-
- const Node *findNode(const Node *root, const QString &originalName) const;
-
-protected:
- void delNode(Importer::Node *n);
-
- QByteArray m_buffer;
- QHash<uint, MeshInfo> m_meshInfo;
- QHash<uint, MaterialInfo> m_materialInfo;
- QHash<QString, EmbeddedTextureInfo> m_embeddedTextures;
- QSet<QString> m_externalTextures;
- QHash<QString, CameraInfo> m_cameraInfo;
- Node *m_rootNode;
- QVector<AnimationInfo> m_animations;
-};
-QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(Importer::BufferInfo, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(Importer::MeshInfo::BufferView, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(Importer::MeshInfo::Accessor, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(Importer::MaterialInfo, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(Importer::CameraInfo, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(Importer::EmbeddedTextureInfo, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(Importer::Node, Q_COMPLEX_TYPE); // uses address as identity
-Q_DECLARE_TYPEINFO(Importer::KeyFrame, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(Importer::AnimationInfo, Q_MOVABLE_TYPE);
-QT_END_NAMESPACE
-
-Importer::Importer()
- : m_rootNode(nullptr)
-{
-}
-
-void Importer::delNode(Importer::Node *n)
-{
- if (!n)
- return;
- for (Importer::Node *c : qAsConst(n->children))
- delNode(c);
- delete n;
-}
-
-Importer::~Importer()
-{
- delNode(m_rootNode);
-}
-
-QVector<Importer::BufferInfo> Importer::buffers() const
-{
- BufferInfo b;
- b.name = QStringLiteral("buf");
- b.data = m_buffer;
- return QVector<BufferInfo>() << b;
-}
-
-const Importer::Node *Importer::rootNode() const
-{
- return m_rootNode;
-}
-
-bool Importer::allMeshesForMaterialHaveTangents(uint materialIndex) const
-{
- for (const MeshInfo &mi : m_meshInfo) {
- if (mi.materialIndex == materialIndex) {
- bool hasTangents = false;
- for (const MeshInfo::Accessor &acc : mi.accessors) {
- if (acc.usage == QStringLiteral("TANGENT")) {
- hasTangents = true;
- break;
- }
- }
- if (!hasTangents)
- return false;
- }
- }
- return true;
-}
-
-QVector<Importer::MeshInfo::BufferView> Importer::bufferViews() const
-{
- QVector<Importer::MeshInfo::BufferView> bv;
- for (const MeshInfo &mi : m_meshInfo) {
- for (const MeshInfo::BufferView &v : mi.views)
- bv << v;
- }
- return bv;
-}
-
-QVector<Importer::MeshInfo::Accessor> Importer::accessors() const
-{
- QVector<Importer::MeshInfo::Accessor> acc;
- for (const MeshInfo &mi : m_meshInfo) {
- for (const MeshInfo::Accessor &a : mi.accessors)
- acc << a;
- }
- return acc;
-}
-
-uint Importer::meshCount() const
-{
- return m_meshInfo.count();
-}
-
-Importer::MeshInfo Importer::meshInfo(uint meshIndex) const
-{
- return m_meshInfo[meshIndex];
-}
-
-uint Importer::materialCount() const
-{
- return m_materialInfo.count();
-}
-
-Importer::MaterialInfo Importer::materialInfo(uint materialIndex) const
-{
- return m_materialInfo[materialIndex];
-}
-
-QHash<QString, Importer::CameraInfo> Importer::cameraInfo() const
-{
- return m_cameraInfo;
-}
-
-QSet<QString> Importer::externalTextures() const
-{
- return m_externalTextures;
-}
-
-QHash<QString, Importer::EmbeddedTextureInfo> Importer::embeddedTextures() const
-{
- return m_embeddedTextures;
-}
-
-QVector<Importer::AnimationInfo> Importer::animations() const
-{
- return m_animations;
-}
-
-const Importer::Node *Importer::findNode(const Node *root, const QString &originalName) const
-{
- for (const Node *c : root->children) {
- if (c->name == originalName)
- return c;
- const Node *cn = findNode(c, originalName);
- if (cn)
- return cn;
- }
- return nullptr;
-}
-
-class AssimpImporter : public Importer
-{
-public:
- AssimpImporter();
-
- bool load(const QString &filename) override;
-
-private:
- const aiScene *scene() const;
- void printNodes(const aiNode *node, int level = 1);
- void buildBuffer();
- void parseEmbeddedTextures();
- void parseMaterials();
- void parseCameras();
- void parseNode(Importer::Node *dst, const aiNode *src);
- void parseScene();
- void parseAnimations();
- void addKeyFrame(QVector<KeyFrame> &keyFrames, float t, aiVector3D *vt, aiQuaternion *vr, aiVector3D *vs);
-
- QScopedPointer<Assimp::Importer> m_importer;
-};
-
-AssimpImporter::AssimpImporter() :
- m_importer(new Assimp::Importer)
-{
- m_importer->SetIOHandler(new AssimpIOSystem);
- m_importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT);
-}
-
-bool AssimpImporter::load(const QString &filename)
-{
- uint flags = aiProcess_Triangulate | aiProcess_SortByPType
- | aiProcess_JoinIdenticalVertices
- | aiProcess_GenSmoothNormals
- | aiProcess_GenUVCoords
- | aiProcess_FlipUVs
- | aiProcess_FindDegenerates;
-
- if (opts.genTangents)
- flags |= aiProcess_CalcTangentSpace;
-
- const aiScene *scene = m_importer->ReadFile(filename.toUtf8().constData(), flags);
- if (!scene)
- return false;
-
- if (opts.showLog) {
- qDebug().noquote() << filename
- << scene->mNumMeshes << "meshes,"
- << scene->mNumMaterials << "materials,"
- << scene->mNumTextures << "embedded textures,"
- << scene->mNumCameras << "cameras,"
- << scene->mNumLights << "lights,"
- << scene->mNumAnimations << "animations";
- qDebug() << "Scene:";
- printNodes(scene->mRootNode);
- }
-
- buildBuffer();
- parseEmbeddedTextures();
- parseMaterials();
- parseCameras();
- parseScene();
- parseAnimations();
-
- return true;
-}
-
-void AssimpImporter::printNodes(const aiNode *node, int level)
-{
- qDebug().noquote() << QString().fill('-', level * 4) << ai2qt(node->mName) << node->mNumMeshes << "mesh refs";
- for (uint i = 0; i < node->mNumChildren; ++i)
- printNodes(node->mChildren[i], level + 1);
-}
-
-template<class T> void copyIndexBuf(T *dst, const aiMesh *src)
-{
- for (uint j = 0; j < src->mNumFaces; ++j) {
- const aiFace *f = &src->mFaces[j];
- if (f->mNumIndices != 3)
- qFatal("Face %d is not a triangle (index count %d instead of 3)", j, f->mNumIndices);
- *dst++ = f->mIndices[0];
- *dst++ = f->mIndices[1];
- *dst++ = f->mIndices[2];
- }
-}
-
-static QString newBufferViewName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("bufferView_%1")).arg(++cnt);
-}
-
-static QString newAccessorName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("accessor_%1")).arg(++cnt);
-}
-
-static QString newMeshName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("mesh_%1")).arg(++cnt);
-}
-
-static QString newMaterialName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("material_%1")).arg(++cnt);
-}
-
-static QString newTechniqueName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("technique_%1")).arg(++cnt);
-}
-
-static QString newTextureName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("texture_%1")).arg(++cnt);
-}
-
-static QString newImageName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("image_%1")).arg(++cnt);
-}
-
-static QString newShaderName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("shader_%1")).arg(++cnt);
-}
-
-static QString newProgramName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("program_%1")).arg(++cnt);
-}
-
-static QString newNodeName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("node_%1")).arg(++cnt);
-}
-
-static QString newAnimationName()
-{
- static int cnt = 0;
- return QString(QStringLiteral("animation_%1")).arg(++cnt);
-}
-
-template<class T> void calcBB(QVector<float> &minVal, QVector<float> &maxVal, T *data, int vertexCount, int compCount)
-{
- minVal.resize(compCount);
- maxVal.resize(compCount);
- for (int i = 0; i < vertexCount; ++i) {
- for (int j = 0; j < compCount; ++j) {
- if (i == 0) {
- minVal[j] = maxVal[j] = data[i][j];
- } else {
- if (data[i][j] < minVal[j])
- minVal[j] = data[i][j];
- if (data[i][j] > maxVal[j])
- maxVal[j] = data[i][j];
- }
- }
- }
-}
-
-// One buffer per importer (scene).
-// Two buffer views (array, index) + three or more accessors per mesh.
-
-void AssimpImporter::buildBuffer()
-{
- m_buffer.clear();
- m_meshInfo.clear();
-
- if (opts.showLog)
- qDebug() << "Meshes:";
-
- const aiScene *sc = scene();
- for (uint i = 0; i < sc->mNumMeshes; ++i) {
- aiMesh *m = sc->mMeshes[i];
- MeshInfo meshInfo;
- meshInfo.originalName = ai2qt(m->mName);
- meshInfo.name = newMeshName();
- meshInfo.materialIndex = m->mMaterialIndex;
-
- aiVector3D *vertices = m->mVertices;
- aiVector3D *normals = m->mNormals;
- aiVector3D *textureCoords = m->mTextureCoords[0];
- aiColor4D *colors = m->mColors[0];
- aiVector3D *tangents = m->mTangents;
-
- if (opts.scale != 1) {
- for (uint j = 0; j < m->mNumVertices; ++j) {
- vertices[j].x *= opts.scale;
- vertices[j].y *= opts.scale;
- vertices[j].z *= opts.scale;
- }
- }
-
- // Vertex (3), Normal (3), Coord? (2), Color? (4), Tangent? (3)
- uint stride = 3 + 3 + (textureCoords ? 2 : 0) + (colors ? 4 : 0) + (tangents ? 3 : 0);
- QByteArray vertexBuf;
- vertexBuf.resize(stride * m->mNumVertices * sizeof(float));
- float *p = reinterpret_cast<float *>(vertexBuf.data());
-
- if (opts.interleave) {
- for (uint j = 0; j < m->mNumVertices; ++j) {
- // Vertex
- *p++ = vertices[j].x;
- *p++ = vertices[j].y;
- *p++ = vertices[j].z;
-
- // Normal
- *p++ = normals[j].x;
- *p++ = normals[j].y;
- *p++ = normals[j].z;
-
- // Coord
- if (textureCoords) {
- *p++ = textureCoords[j].x;
- *p++ = textureCoords[j].y;
- }
-
- // Color
- if (colors) {
- *p++ = colors[j].r;
- *p++ = colors[j].g;
- *p++ = colors[j].b;
- *p++ = colors[j].a;
- }
-
- // Tangent
- if (tangents) {
- *p++ = tangents[j].x;
- *p++ = tangents[j].y;
- *p++ = tangents[j].z;
- }
- }
- } else {
- // Vertex
- for (uint j = 0; j < m->mNumVertices; ++j) {
- *p++ = vertices[j].x;
- *p++ = vertices[j].y;
- *p++ = vertices[j].z;
- }
-
- // Normal
- for (uint j = 0; j < m->mNumVertices; ++j) {
- *p++ = normals[j].x;
- *p++ = normals[j].y;
- *p++ = normals[j].z;
- }
-
- // Coord
- if (textureCoords) {
- for (uint j = 0; j < m->mNumVertices; ++j) {
- *p++ = textureCoords[j].x;
- *p++ = textureCoords[j].y;
- }
- }
-
- // Color
- if (colors) {
- for (uint j = 0; j < m->mNumVertices; ++j) {
- *p++ = colors[j].r;
- *p++ = colors[j].g;
- *p++ = colors[j].b;
- *p++ = colors[j].a;
- }
- }
-
- // Tangent
- if (tangents) {
- for (uint j = 0; j < m->mNumVertices; ++j) {
- *p++ = tangents[j].x;
- *p++ = tangents[j].y;
- *p++ = tangents[j].z;
- }
- }
- }
-
- MeshInfo::BufferView vertexBufView;
- vertexBufView.name = newBufferViewName();
- vertexBufView.length = vertexBuf.size();
- vertexBufView.offset = m_buffer.size();
- vertexBufView.componentType = GLT_FLOAT;
- vertexBufView.target = GLT_ARRAY_BUFFER;
- meshInfo.views.append(vertexBufView);
-
- QByteArray indexBuf;
- uint indexCount = m->mNumFaces * 3;
- if (indexCount >= USHRT_MAX) {
- indexBuf.resize(indexCount * sizeof(quint32));
- quint32 *p = reinterpret_cast<quint32 *>(indexBuf.data());
- copyIndexBuf(p, m);
- } else {
- indexBuf.resize(indexCount * sizeof(quint16));
- quint16 *p = reinterpret_cast<quint16 *>(indexBuf.data());
- copyIndexBuf(p, m);
- }
-
- MeshInfo::BufferView indexBufView;
- indexBufView.name = newBufferViewName();
- indexBufView.length = indexBuf.size();
- indexBufView.offset = vertexBufView.offset + vertexBufView.length;
- indexBufView.componentType = indexCount >= USHRT_MAX ? GLT_UNSIGNED_INT : GLT_UNSIGNED_SHORT;
- indexBufView.target = GLT_ELEMENT_ARRAY_BUFFER;
- meshInfo.views.append(indexBufView);
-
- MeshInfo::Accessor acc;
- uint startOffset = 0;
- // Vertex
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("POSITION");
- acc.bufferView = vertexBufView.name;
- acc.offset = 0;
- acc.stride = opts.interleave ? stride * sizeof(float) : 3 * sizeof(float);
- acc.count = m->mNumVertices;
- acc.componentType = vertexBufView.componentType;
- acc.type = QStringLiteral("VEC3");
- calcBB(acc.minVal, acc.maxVal, vertices, m->mNumVertices, 3);
- meshInfo.accessors.append(acc);
- startOffset += opts.interleave ? 3 : 3 * m->mNumVertices;
- // Normal
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("NORMAL");
- acc.offset = startOffset * sizeof(float);
- if (!opts.interleave)
- acc.stride = 3 * sizeof(float);
- calcBB(acc.minVal, acc.maxVal, normals, m->mNumVertices, 3);
- meshInfo.accessors.append(acc);
- startOffset += opts.interleave ? 3 : 3 * m->mNumVertices;
- // Coord
- if (textureCoords) {
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("TEXCOORD_0");
- acc.offset = startOffset * sizeof(float);
- if (!opts.interleave)
- acc.stride = 2 * sizeof(float);
- acc.type = QStringLiteral("VEC2");
- calcBB(acc.minVal, acc.maxVal, textureCoords, m->mNumVertices, 2);
- meshInfo.accessors.append(acc);
- startOffset += opts.interleave ? 2 : 2 * m->mNumVertices;
- }
- // Color
- if (colors) {
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("COLOR");
- acc.offset = startOffset * sizeof(float);
- if (!opts.interleave)
- acc.stride = 4 * sizeof(float);
- acc.type = QStringLiteral("VEC4");
- calcBB(acc.minVal, acc.maxVal, colors, m->mNumVertices, 4);
- meshInfo.accessors.append(acc);
- startOffset += opts.interleave ? 4 : 4 * m->mNumVertices;
- }
- // Tangent
- if (tangents) {
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("TANGENT");
- acc.offset = startOffset * sizeof(float);
- if (!opts.interleave)
- acc.stride = 3 * sizeof(float);
- acc.type = QStringLiteral("VEC3");
- calcBB(acc.minVal, acc.maxVal, tangents, m->mNumVertices, 3);
- meshInfo.accessors.append(acc);
- startOffset += opts.interleave ? 3 : 3 * m->mNumVertices;
- }
-
- // Index
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("INDEX");
- acc.bufferView = indexBufView.name;
- acc.offset = 0;
- acc.stride = 0;
- acc.count = indexCount;
- acc.componentType = indexBufView.componentType;
- acc.type = QStringLiteral("SCALAR");
- acc.minVal = acc.maxVal = QVector<float>();
- meshInfo.accessors.append(acc);
-
- if (opts.showLog) {
- qDebug().noquote() << "#" << i << "(" << meshInfo.name << "/" << meshInfo.originalName << ")"
- << m->mNumVertices << "vertices,"
- << m->mNumFaces << "faces," << stride << "bytes per vertex,"
- << vertexBuf.size() << "vertex bytes," << indexBuf.size() << "index bytes";
- if (opts.scale != 1)
- qDebug() << " scaled by" << opts.scale;
- if (!opts.interleave)
- qDebug() << " non-interleaved layout";
- QStringList sl;
- for (const MeshInfo::BufferView &bv : qAsConst(meshInfo.views)) sl << bv.name;
- qDebug() << " buffer views:" << sl;
- sl.clear();
- for (const MeshInfo::Accessor &acc : qAsConst(meshInfo.accessors)) sl << acc.name;
- qDebug() << " accessors:" << sl;
- qDebug() << " material: #" << meshInfo.materialIndex;
- }
-
- m_buffer.append(vertexBuf);
- m_buffer.append(indexBuf);
-
- m_meshInfo.insert(i, meshInfo);
- }
-
- if (opts.showLog)
- qDebug().noquote() << "Total buffer size" << m_buffer.size();
-}
-
-void AssimpImporter::parseEmbeddedTextures()
-{
-#ifdef HAS_QIMAGE
- m_embeddedTextures.clear();
-
- const aiScene *sc = scene();
- if (opts.showLog && sc->mNumTextures)
- qDebug() << "Embedded textures:";
-
- for (uint i = 0; i < sc->mNumTextures; ++i) {
- aiTexture *t = sc->mTextures[i];
- QImage img;
- if (t->mHeight == 0) {
- img = QImage::fromData(reinterpret_cast<uchar *>(t->pcData), t->mWidth);
- } else {
- uint sz = t->mWidth * t->mHeight;
- QByteArray data;
- data.resize(sz * 4);
- uchar *p = reinterpret_cast<uchar *>(data.data());
- for (uint j = 0; j < sz; ++j) {
- *p++ = t->pcData[j].r;
- *p++ = t->pcData[j].g;
- *p++ = t->pcData[j].b;
- *p++ = t->pcData[j].a;
- }
- img = QImage(reinterpret_cast<const uchar *>(data.constData()), t->mWidth, t->mHeight, QImage::Format_RGBA8888);
- img.detach();
- }
- QString name;
- static int imgCnt = 0;
- name = QString(QStringLiteral("texture_%1.png")).arg(++imgCnt);
- QString embeddedTextureRef = QStringLiteral("*") + QString::number(i); // see AI_MAKE_EMBEDDED_TEXNAME
- m_embeddedTextures.insert(embeddedTextureRef, EmbeddedTextureInfo(name, img));
- if (opts.showLog)
- qDebug().noquote() << "#" << i << name << img;
- }
-#else
- if (scene()->mNumTextures)
- qWarning() << "WARNING: No image support, ignoring" << scene()->mNumTextures << "embedded textures";
-#endif
-}
-
-void AssimpImporter::parseMaterials()
-{
- m_materialInfo.clear();
- m_externalTextures.clear();
-
- if (opts.showLog)
- qDebug() << "Materials:";
-
- const aiScene *sc = scene();
- for (uint i = 0; i < sc->mNumMaterials; ++i) {
- const aiMaterial *mat = sc->mMaterials[i];
- MaterialInfo matInfo;
- matInfo.name = newMaterialName();
-
- aiString s;
- if (mat->Get(AI_MATKEY_NAME, s) == aiReturn_SUCCESS)
- matInfo.originalName = ai2qt(s);
-
- aiColor4D color;
- if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, color) == aiReturn_SUCCESS)
- matInfo.m_colors.insert("diffuse", QVector<float>() << color.r << color.g << color.b << color.a);
- if (mat->Get(AI_MATKEY_COLOR_SPECULAR, color) == aiReturn_SUCCESS)
- matInfo.m_colors.insert("specular", QVector<float>() << color.r << color.g << color.b);
- if (mat->Get(AI_MATKEY_COLOR_AMBIENT, color) == aiReturn_SUCCESS)
- matInfo.m_colors.insert("ambient", QVector<float>() << color.r << color.g << color.b);
-
- float f;
- if (mat->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS)
- matInfo.m_values.insert("shininess", f);
-
- if (mat->GetTexture(aiTextureType_DIFFUSE, 0, &s) == aiReturn_SUCCESS)
- matInfo.m_textures.insert("diffuse", ai2qt(s));
- if (mat->GetTexture(aiTextureType_SPECULAR, 0, &s) == aiReturn_SUCCESS)
- matInfo.m_textures.insert("specular", ai2qt(s));
- if (mat->GetTexture(aiTextureType_NORMALS, 0, &s) == aiReturn_SUCCESS)
- matInfo.m_textures.insert("normal", ai2qt(s));
-
- QHash<QByteArray, QString>::iterator texIt = matInfo.m_textures.begin();
- while (texIt != matInfo.m_textures.end()) {
- // Map embedded texture references to real files.
- if (texIt->startsWith('*'))
- *texIt = m_embeddedTextures[*texIt].name;
- else
- m_externalTextures.insert(*texIt);
- ++texIt;
- }
-
- m_materialInfo.insert(i, matInfo);
-
- if (opts.showLog) {
- qDebug().noquote() << "#" << i << "(" << matInfo.name << "/" << matInfo.originalName << ")";
- qDebug() << " colors:" << matInfo.m_colors;
- qDebug() << " values:" << matInfo.m_values;
- qDebug() << " textures:" << matInfo.m_textures;
- }
- }
-}
-
-void AssimpImporter::parseCameras()
-{
- m_cameraInfo.clear();
-
- if (opts.showLog)
- qDebug() << "Cameras:";
-
- const aiScene *sc = scene();
- for (uint i = 0; i < sc->mNumCameras; ++i) {
- const aiCamera *cam = sc->mCameras[i];
- QString name = ai2qt(cam->mName);
- CameraInfo c;
-
- c.name = name + QStringLiteral("_cam");
- c.aspectRatio = qFuzzyIsNull(cam->mAspect) ? 1.5f : cam->mAspect;
- c.yfov = cam->mHorizontalFOV;
- if (c.yfov < (M_PI / 10.0)) // this can't be right (probably orthographic source camera)
- c.yfov = float(M_PI / 4.0);
- c.znear = cam->mClipPlaneNear;
- c.zfar = cam->mClipPlaneFar;
-
- // Collada / glTF cameras point in -Z by default, the rest is in the
- // node matrix, no separate look-at params given here.
-
- m_cameraInfo.insert(name, c);
-
- if (opts.showLog)
- qDebug().noquote() << "#" << i << "(" << name << ")" << c.aspectRatio << c.yfov << c.znear << c.zfar;
- }
-}
-
-void AssimpImporter::parseNode(Importer::Node *dst, const aiNode *src)
-{
- dst->name = ai2qt(src->mName);
- dst->uniqueName = newNodeName();
- for (uint j = 0; j < src->mNumChildren; ++j) {
- Node *c = new Node;
- parseNode(c, src->mChildren[j]);
- dst->children << c;
- }
- dst->transformation = ai2qt(src->mTransformation);
- for (uint j = 0; j < src->mNumMeshes; ++j)
- dst->meshes << src->mMeshes[j];
-}
-
-void AssimpImporter::parseScene()
-{
- delNode(m_rootNode);
- const aiScene *sc = scene();
- m_rootNode = new Node;
- parseNode(m_rootNode, sc->mRootNode);
-}
-
-void AssimpImporter::addKeyFrame(QVector<KeyFrame> &keyFrames, float t, aiVector3D *vt, aiQuaternion *vr, aiVector3D *vs)
-{
- KeyFrame kf;
- int idx = -1;
- for (int i = 0; i < keyFrames.count(); ++i) {
- if (qFuzzyCompare(keyFrames[i].t, t)) {
- kf = keyFrames[i];
- idx = i;
- break;
- }
- }
-
- kf.t = t;
- if (vt) {
- kf.transValid = true;
- kf.trans = QVector<float>() << vt->x << vt->y << vt->z;
- }
- if (vr) {
- kf.rotValid = true;
- kf.rot = QVector<float>() << vr->w << vr->x << vr->y << vr->z;
- }
- if (vs) {
- kf.scaleValid = true;
- kf.scale = QVector<float>() << vs->x << vs->y << vs->z;
- }
-
- if (idx >= 0)
- keyFrames[idx] = kf;
- else
- keyFrames.append(kf);
-}
-
-void AssimpImporter::parseAnimations()
-{
- const aiScene *sc = scene();
- if (opts.showLog && sc->mNumAnimations)
- qDebug() << "Animations:";
-
- for (uint i = 0; i < sc->mNumAnimations; ++i) {
- const aiAnimation *anim = sc->mAnimations[i];
-
- // Only care about node animations.
- for (uint j = 0; j < anim->mNumChannels; ++j) {
- const aiNodeAnim *a = anim->mChannels[j];
- AnimationInfo animInfo;
- QVector<KeyFrame> keyFrames;
-
- if (opts.showLog)
- qDebug().noquote() << ai2qt(anim->mName) << "->" << ai2qt(a->mNodeName);
-
- // Target values in the keyframes are local absolute (relative to parent, like node.matrix).
- for (uint kf = 0; kf < a->mNumPositionKeys; ++kf) {
- float t = float(a->mPositionKeys[kf].mTime);
- aiVector3D v = a->mPositionKeys[kf].mValue;
- animInfo.hasTranslation = true;
- addKeyFrame(keyFrames, t, &v, nullptr, nullptr);
- }
- for (uint kf = 0; kf < a->mNumRotationKeys; ++kf) {
- float t = float(a->mRotationKeys[kf].mTime);
- aiQuaternion v = a->mRotationKeys[kf].mValue;
- animInfo.hasRotation = true;
- addKeyFrame(keyFrames, t, nullptr, &v, nullptr);
- }
- for (uint kf = 0; kf < a->mNumScalingKeys; ++kf) {
- float t = float(a->mScalingKeys[kf].mTime);
- aiVector3D v = a->mScalingKeys[kf].mValue;
- animInfo.hasScale = true;
- addKeyFrame(keyFrames, t, nullptr, nullptr, &v);
- }
-
- // Here we should ideally get rid of non-animated properties (that
- // just set the t-r-s value from node.matrix in every frame) but
- // let's leave that as a future exercise.
-
- if (!keyFrames.isEmpty()) {
- animInfo.name = ai2qt(anim->mName);
- QString nodeName = ai2qt(a->mNodeName); // have to map to our generated, unique node names
- const Node *targetNode = findNode(m_rootNode, nodeName);
- if (targetNode)
- animInfo.targetNode = targetNode->uniqueName;
- else
- qWarning().noquote() << "ERROR: Cannot find target node" << nodeName << "for animation" << animInfo.name;
- animInfo.keyFrames = keyFrames;
- m_animations << animInfo;
-
- if (opts.showLog) {
- for (const KeyFrame &kf : qAsConst(keyFrames)) {
- QString msg;
- QTextStream s(&msg);
- s << " @ " << kf.t;
- if (kf.transValid)
- s << " T=(" << kf.trans[0] << ", " << kf.trans[1] << ", " << kf.trans[2] << ")";
- if (kf.rotValid)
- s << " R=(w=" << kf.rot[0] << ", " << kf.rot[1] << ", " << kf.rot[2] << ", " << kf.rot[3] << ")";
- if (kf.scaleValid)
- s << " S=(" << kf.scale[0] << ", " << kf.scale[1] << ", " << kf.scale[2] << ")";
- qDebug().noquote() << msg;
- }
- }
- }
- }
- }
-}
-
-const aiScene *AssimpImporter::scene() const
-{
- return m_importer->GetScene();
-}
-
-class Exporter
-{
-public:
- Exporter(Importer *importer) : m_importer(importer) { }
- virtual ~Exporter() { }
-
- virtual void save(const QString &inputFilename) = 0;
-
-protected:
- bool nodeIsUseful(const Importer::Node *n) const;
- void copyExternalTextures(const QString &inputFilename);
- void exportEmbeddedTextures();
- void compressTextures();
-
- Importer *m_importer;
- QSet<QString> m_files;
- QHash<QString, QString> m_compressedTextures;
-};
-
-bool Exporter::nodeIsUseful(const Importer::Node *n) const
-{
- if (!n->meshes.isEmpty() || m_importer->cameraInfo().contains(n->name))
- return true;
-
- for (const Importer::Node *c : n->children) {
- if (nodeIsUseful(c))
- return true;
- }
-
- return false;
-}
-
-void Exporter::copyExternalTextures(const QString &inputFilename)
-{
- const auto textureFilenames = m_importer->externalTextures();
- for (const QString &textureFilename : textureFilenames) {
- const QString dst = opts.outDir + textureFilename;
- m_files.insert(QFileInfo(dst).fileName());
- // External textures need copying only when output dir was specified.
- if (!opts.outDir.isEmpty()) {
- const QString src = QFileInfo(inputFilename).path() + QStringLiteral("/") + textureFilename;
- if (QFileInfo(src).absolutePath() != QFileInfo(dst).absolutePath()) {
- if (opts.showLog)
- qDebug().noquote() << "Copying" << src << "to" << dst;
- QFile(src).copy(dst);
- }
- }
- }
-}
-
-void Exporter::exportEmbeddedTextures()
-{
-#ifdef HAS_QIMAGE
- const auto embeddedTextures = m_importer->embeddedTextures();
- for (const Importer::EmbeddedTextureInfo &embTex : embeddedTextures) {
- QString fn = opts.outDir + embTex.name;
- m_files.insert(QFileInfo(fn).fileName());
- if (opts.showLog)
- qDebug().noquote() << "Writing" << fn;
- embTex.image.save(fn);
- }
-#endif
-}
-
-void Exporter::compressTextures()
-{
- if (opts.texComp != Options::ETC1)
- return;
-
- const auto textureFilenames = m_importer->externalTextures();
- const auto embeddedTextures = m_importer->embeddedTextures();
- QStringList imageList;
- imageList.reserve(textureFilenames.size() + embeddedTextures.size());
- for (const QString &textureFilename : textureFilenames)
- imageList << opts.outDir + textureFilename;
- for (const Importer::EmbeddedTextureInfo &embTex : embeddedTextures)
- imageList << opts.outDir + embTex.name;
-
- for (const QString &filename : qAsConst(imageList)) {
- if (QFileInfo(filename).suffix().toLower() != QStringLiteral("png"))
- continue;
- QByteArray cmd = QByteArrayLiteral("etc1tool ");
- cmd += filename.toUtf8();
- qDebug().noquote() << "Invoking" << cmd;
- // No QProcess in bootstrap
- if (system(cmd.constData()) == -1) {
- qWarning() << "ERROR: Failed to launch etc1tool";
- } else {
- QString src = QFileInfo(filename).fileName();
- QString dst = QFileInfo(src).baseName() + QStringLiteral(".pkm");
- m_compressedTextures.insert(src, dst);
- m_files.remove(src);
- m_files.insert(dst);
- }
- }
-}
-
-class GltfExporter : public Exporter
-{
-public:
- GltfExporter(Importer *importer);
- void save(const QString &inputFilename) override;
-
-private:
- struct ProgramInfo {
- struct Param {
- Param() : type(0) { }
- Param(QString name, QString nameInShader, QString semantic, uint type)
- : name(name), nameInShader(nameInShader), semantic(semantic), type(type) { }
- QString name;
- QString nameInShader;
- QString semantic;
- uint type;
- };
- QString commonTechniqueName;
- QString vertShader;
- QString fragShader;
- QVector<Param> attributes;
- QVector<Param> uniforms;
- };
- friend class QTypeInfo<ProgramInfo>;
- friend class QTypeInfo<ProgramInfo::Param>;
-
- void writeShader(const QString &src, const QString &dst, const QVector<QPair<QByteArray, QByteArray> > &substTab);
- QString exportNode(const Importer::Node *n, QJsonObject &nodes);
- void exportMaterials(QJsonObject &materials, QHash<QString, QString> *textureNameMap);
- void exportParameter(QJsonObject &dst, const QVector<ProgramInfo::Param> &params);
- void exportTechniques(QJsonObject &obj, const QString &basename);
- void exportAnimations(QJsonObject &obj, QVector<Importer::BufferInfo> &bufList,
- QVector<Importer::MeshInfo::BufferView> &bvList,
- QVector<Importer::MeshInfo::Accessor> &accList);
- void initShaderInfo();
- ProgramInfo *chooseProgram(uint materialIndex);
-
- QJsonObject m_obj;
- QVector<ProgramInfo> m_progs;
-
- struct TechniqueInfo {
- TechniqueInfo() : opaque(true), prog(nullptr) { }
- TechniqueInfo(const QString &name, bool opaque, ProgramInfo *prog)
- : name(name)
- , opaque(opaque)
- , prog(prog)
- {
- coreName = name + QStringLiteral("_core");
- gl2Name = name + QStringLiteral("_gl2");
- }
- QString name;
- QString coreName;
- QString gl2Name;
- bool opaque;
- ProgramInfo *prog;
- };
- friend class QTypeInfo<TechniqueInfo>;
- QVector<TechniqueInfo> m_techniques;
- QSet<ProgramInfo *> m_usedPrograms;
-
- QVector<QPair<QByteArray, QByteArray> > m_subst_es2;
- QVector<QPair<QByteArray, QByteArray> > m_subst_core;
-
- QHash<QString, bool> m_imageHasAlpha;
-};
-QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(GltfExporter::ProgramInfo, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(GltfExporter::ProgramInfo::Param, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(GltfExporter::TechniqueInfo, Q_MOVABLE_TYPE);
-QT_END_NAMESPACE
-
-GltfExporter::GltfExporter(Importer *importer)
- : Exporter(importer)
-{
- initShaderInfo();
-}
-
-struct Shader {
- const char *name;
- const char *text;
-} shaders[] = {
- {
- "color.vert",
-"$VERSION\n"
-"$ATTRIBUTE vec3 vertexPosition;\n"
-"$ATTRIBUTE vec3 vertexNormal;\n"
-"$VVARYING vec3 vPosition;\n"
-"$VVARYING vec3 vNormal;\n"
-"uniform mat4 projection;\n"
-"uniform mat4 modelView;\n"
-"uniform mat3 modelViewNormal;\n"
-"void main()\n"
-"{\n"
-" vNormal = normalize( modelViewNormal * vertexNormal );\n"
-" vPosition = vec3( modelView * vec4( vertexPosition, 1.0 ) );\n"
-" gl_Position = projection * modelView * vec4( vertexPosition, 1.0 );\n"
-"}\n"
- },
- {
- "color.frag",
-"$VERSION\n"
-"uniform $HIGHP vec4 lightPosition;\n"
-"uniform $HIGHP vec3 lightIntensity;\n"
-"uniform $HIGHP vec3 ka;\n"
-"uniform $HIGHP vec4 kd;\n"
-"uniform $HIGHP vec3 ks;\n"
-"uniform $HIGHP float shininess;\n"
-"$FVARYING $HIGHP vec3 vPosition;\n"
-"$FVARYING $HIGHP vec3 vNormal;\n"
-"$DECL_FRAGCOLOR\n"
-"$HIGHP vec3 adsModel( const $HIGHP vec3 pos, const $HIGHP vec3 n )\n"
-"{\n"
-" $HIGHP vec3 s = normalize( vec3( lightPosition ) - pos );\n"
-" $HIGHP vec3 v = normalize( -pos );\n"
-" $HIGHP vec3 r = reflect( -s, n );\n"
-" $HIGHP float diffuse = max( dot( s, n ), 0.0 );\n"
-" $HIGHP float specular = 0.0;\n"
-" if ( dot( s, n ) > 0.0 )\n"
-" specular = pow( max( dot( r, v ), 0.0 ), shininess );\n"
-" return lightIntensity * ( ka + kd.rgb * diffuse + ks * specular );\n"
-"}\n"
-"void main()\n"
-"{\n"
-" $FRAGCOLOR = vec4( adsModel( vPosition, normalize( vNormal ) ) * kd.a, kd.a );\n"
-"}\n"
- },
- {
- "diffusemap.vert",
-"$VERSION\n"
-"$ATTRIBUTE vec3 vertexPosition;\n"
-"$ATTRIBUTE vec3 vertexNormal;\n"
-"$ATTRIBUTE vec2 vertexTexCoord;\n"
-"$VVARYING vec3 vPosition;\n"
-"$VVARYING vec3 vNormal;\n"
-"$VVARYING vec2 vTexCoord;\n"
-"uniform mat4 projection;\n"
-"uniform mat4 modelView;\n"
-"uniform mat3 modelViewNormal;\n"
-"void main()\n"
-"{\n"
-" vTexCoord = vertexTexCoord;\n"
-" vNormal = normalize( modelViewNormal * vertexNormal );\n"
-" vPosition = vec3( modelView * vec4( vertexPosition, 1.0 ) );\n"
-" gl_Position = projection * modelView * vec4( vertexPosition, 1.0 );\n"
-"}\n"
- },
- {
- "diffusemap.frag",
-"$VERSION\n"
-"uniform $HIGHP vec4 lightPosition;\n"
-"uniform $HIGHP vec3 lightIntensity;\n"
-"uniform $HIGHP vec3 ka;\n"
-"uniform $HIGHP vec3 ks;\n"
-"uniform $HIGHP float shininess;\n"
-"uniform sampler2D diffuseTexture;\n"
-"$FVARYING $HIGHP vec3 vPosition;\n"
-"$FVARYING $HIGHP vec3 vNormal;\n"
-"$FVARYING $HIGHP vec2 vTexCoord;\n"
-"$DECL_FRAGCOLOR\n"
-"$HIGHP vec4 adsModel( const $HIGHP vec3 pos, const $HIGHP vec3 n )\n"
-"{\n"
-" $HIGHP vec3 s = normalize( vec3( lightPosition ) - pos );\n"
-" $HIGHP vec3 v = normalize( -pos );\n"
-" $HIGHP vec3 r = reflect( -s, n );\n"
-" $HIGHP float diffuse = max( dot( s, n ), 0.0 );\n"
-" $HIGHP float specular = 0.0;\n"
-" if ( dot( s, n ) > 0.0 )\n"
-" specular = pow( max( dot( r, v ), 0.0 ), shininess );\n"
-" $HIGHP vec4 kd = $TEXTURE2D( diffuseTexture, vTexCoord );\n"
-" return vec4( lightIntensity * ( ka + kd.rgb * diffuse + ks * specular ) * kd.a, kd.a );\n"
-"}\n"
-"void main()\n"
-"{\n"
-" $FRAGCOLOR = adsModel( vPosition, normalize( vNormal ) );\n"
-"}\n"
- },
- {
- "diffusespecularmap.frag",
-"$VERSION\n"
-"uniform $HIGHP vec4 lightPosition;\n"
-"uniform $HIGHP vec3 lightIntensity;\n"
-"uniform $HIGHP vec3 ka;\n"
-"uniform $HIGHP float shininess;\n"
-"uniform sampler2D diffuseTexture;\n"
-"uniform sampler2D specularTexture;\n"
-"$FVARYING $HIGHP vec3 vPosition;\n"
-"$FVARYING $HIGHP vec3 vNormal;\n"
-"$FVARYING $HIGHP vec2 vTexCoord;\n"
-"$DECL_FRAGCOLOR\n"
-"$HIGHP vec4 adsModel( const in $HIGHP vec3 pos, const in $HIGHP vec3 n )\n"
-"{\n"
-" $HIGHP vec3 s = normalize( vec3( lightPosition ) - pos );\n"
-" $HIGHP vec3 v = normalize( -pos );\n"
-" $HIGHP vec3 r = reflect( -s, n );\n"
-" $HIGHP float diffuse = max( dot( s, n ), 0.0 );\n"
-" $HIGHP float specular = 0.0;\n"
-" if ( dot( s, n ) > 0.0 )\n"
-" specular = ( shininess / ( 8.0 * 3.14 ) ) * pow( max( dot( r, v ), 0.0 ), shininess );\n"
-" $HIGHP vec4 kd = $TEXTURE2D( diffuseTexture, vTexCoord );\n"
-" $HIGHP vec3 ks = $TEXTURE2D( specularTexture, vTexCoord );\n"
-" return vec4( lightIntensity * ( ka + kd.rgb * diffuse + ks * specular ) * kd.a, kd.a );\n"
-"}\n"
-"void main()\n"
-"{\n"
-" $FRAGCOLOR = vec4( adsModel( vPosition, normalize( vNormal ) ), 1.0 );\n"
-"}\n"
- },
- {
- "normaldiffusemap.vert",
-"$VERSION\n"
-"$ATTRIBUTE vec3 vertexPosition;\n"
-"$ATTRIBUTE vec3 vertexNormal;\n"
-"$ATTRIBUTE vec2 vertexTexCoord;\n"
-"$ATTRIBUTE vec4 vertexTangent;\n"
-"$VVARYING vec3 lightDir;\n"
-"$VVARYING vec3 viewDir;\n"
-"$VVARYING vec2 texCoord;\n"
-"uniform mat4 projection;\n"
-"uniform mat4 modelView;\n"
-"uniform mat3 modelViewNormal;\n"
-"uniform vec4 lightPosition;\n"
-"void main()\n"
-"{\n"
-" texCoord = vertexTexCoord;\n"
-" vec3 normal = normalize( modelViewNormal * vertexNormal );\n"
-" vec3 tangent = normalize( modelViewNormal * vertexTangent.xyz );\n"
-" vec3 position = vec3( modelView * vec4( vertexPosition, 1.0 ) );\n"
-" vec3 binormal = normalize( cross( normal, tangent ) );\n"
-" mat3 tangentMatrix = mat3 (\n"
-" tangent.x, binormal.x, normal.x,\n"
-" tangent.y, binormal.y, normal.y,\n"
-" tangent.z, binormal.z, normal.z );\n"
-" vec3 s = vec3( lightPosition ) - position;\n"
-" lightDir = normalize( tangentMatrix * s );\n"
-" vec3 v = -position;\n"
-" viewDir = normalize( tangentMatrix * v );\n"
-" gl_Position = projection * modelView * vec4( vertexPosition, 1.0 );\n"
-"}\n"
- },
- {
- "normaldiffusemap.frag",
-"$VERSION\n"
-"uniform $HIGHP vec3 lightIntensity;\n"
-"uniform $HIGHP vec3 ka;\n"
-"uniform $HIGHP vec3 ks;\n"
-"uniform $HIGHP float shininess;\n"
-"uniform sampler2D diffuseTexture;\n"
-"uniform sampler2D normalTexture;\n"
-"$FVARYING $HIGHP vec3 lightDir;\n"
-"$FVARYING $HIGHP vec3 viewDir;\n"
-"$FVARYING $HIGHP vec2 texCoord;\n"
-"$DECL_FRAGCOLOR\n"
-"$HIGHP vec3 adsModel( const $HIGHP vec3 norm, const $HIGHP vec3 diffuseReflect)\n"
-"{\n"
-" $HIGHP vec3 r = reflect( -lightDir, norm );\n"
-" $HIGHP vec3 ambient = lightIntensity * ka;\n"
-" $HIGHP float sDotN = max( dot( lightDir, norm ), 0.0 );\n"
-" $HIGHP vec3 diffuse = lightIntensity * diffuseReflect * sDotN;\n"
-" $HIGHP vec3 ambientAndDiff = ambient + diffuse;\n"
-" $HIGHP vec3 spec = vec3( 0.0 );\n"
-" if ( sDotN > 0.0 )\n"
-" spec = lightIntensity * ks * pow( max( dot( r, viewDir ), 0.0 ), shininess );\n"
-" return ambientAndDiff + spec;\n"
-"}\n"
-"void main()\n"
-"{\n"
-" $HIGHP vec4 kd = $TEXTURE2D( diffuseTexture, texCoord );\n"
-" $HIGHP vec4 normal = 2.0 * $TEXTURE2D( normalTexture, texCoord ) - vec4( 1.0 );\n"
-" $FRAGCOLOR = vec4( adsModel( normalize( normal.xyz ), kd.rgb) * kd.a, kd.a );\n"
-"}\n"
- },
- {
- "normaldiffusespecularmap.frag",
-"$VERSION\n"
-"uniform $HIGHP vec3 lightIntensity;\n"
-"uniform $HIGHP vec3 ka;\n"
-"uniform $HIGHP float shininess;\n"
-"uniform sampler2D diffuseTexture;\n"
-"uniform sampler2D specularTexture;\n"
-"uniform sampler2D normalTexture;\n"
-"$FVARYING $HIGHP vec3 lightDir;\n"
-"$FVARYING $HIGHP vec3 viewDir;\n"
-"$FVARYING $HIGHP vec2 texCoord;\n"
-"$DECL_FRAGCOLOR\n"
-"$HIGHP vec3 adsModel( const $HIGHP vec3 norm, const $HIGHP vec3 diffuseReflect, const $HIGHP vec3 specular )\n"
-"{\n"
-" $HIGHP vec3 r = reflect( -lightDir, norm );\n"
-" $HIGHP vec3 ambient = lightIntensity * ka;\n"
-" $HIGHP float sDotN = max( dot( lightDir, norm ), 0.0 );\n"
-" $HIGHP vec3 diffuse = lightIntensity * diffuseReflect * sDotN;\n"
-" $HIGHP vec3 ambientAndDiff = ambient + diffuse;\n"
-" $HIGHP vec3 spec = vec3( 0.0 );\n"
-" if ( sDotN > 0.0 )\n"
-" spec = lightIntensity * ( shininess / ( 8.0 * 3.14 ) ) * pow( max( dot( r, viewDir ), 0.0 ), shininess );\n"
-" return (ambientAndDiff + spec * specular.rgb);\n"
-"}\n"
-"void main()\n"
-"{\n"
-" $HIGHP vec4 kd = $TEXTURE2D( diffuseTexture, texCoord );\n"
-" $HIGHP vec3 ks = $TEXTURE2D( specularTexture, texCoord );\n"
-" $HIGHP vec4 normal = 2.0 * $TEXTURE2D( normalTexture, texCoord ) - vec4( 1.0 );\n"
-" $FRAGCOLOR = vec4( adsModel( normalize( normal.xyz ), kd.rgb, ks ) * kd.a, kd.a );\n"
-"}\n"
- }
-};
-
-void GltfExporter::initShaderInfo()
-{
- ProgramInfo p;
-
- p = ProgramInfo();
- p.commonTechniqueName = "PHONG"; // diffuse RGBA, specular RGBA
- p.vertShader = "color.vert";
- p.fragShader = "color.frag";
- p.attributes << ProgramInfo::Param("position", "vertexPosition", "POSITION", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("normal", "vertexNormal", "NORMAL", GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("projection", "projection", "PROJECTION", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("modelView", "modelView", "MODELVIEW", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("normalMatrix", "modelViewNormal", "MODELVIEWINVERSETRANSPOSE", GLT_FLOAT_MAT3);
- p.uniforms << ProgramInfo::Param("lightPosition", "lightPosition", QString(), GLT_FLOAT_VEC4);
- p.uniforms << ProgramInfo::Param("lightIntensity", "lightIntensity", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("ambient", "ka", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("diffuse", "kd", QString(), GLT_FLOAT_VEC4);
- p.uniforms << ProgramInfo::Param("specular", "ks", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("shininess", "shininess", QString(), GLT_FLOAT);
- m_progs << p;
-
- p = ProgramInfo();
- p.commonTechniqueName = "PHONG"; // diffuse texture, specular RGBA
- p.vertShader = "diffusemap.vert";
- p.fragShader = "diffusemap.frag";
- p.attributes << ProgramInfo::Param("position", "vertexPosition", "POSITION", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("normal", "vertexNormal", "NORMAL", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("texcoord0", "vertexTexCoord", "TEXCOORD_0", GLT_FLOAT_VEC2);
- p.uniforms << ProgramInfo::Param("projection", "projection", "PROJECTION", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("modelView", "modelView", "MODELVIEW", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("normalMatrix", "modelViewNormal", "MODELVIEWINVERSETRANSPOSE", GLT_FLOAT_MAT3);
- p.uniforms << ProgramInfo::Param("lightPosition", "lightPosition", QString(), GLT_FLOAT_VEC4);
- p.uniforms << ProgramInfo::Param("lightIntensity", "lightIntensity", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("ambient", "ka", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("specular", "ks", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("shininess", "shininess", QString(), GLT_FLOAT);
- p.uniforms << ProgramInfo::Param("diffuse", "diffuseTexture", QString(), GLT_SAMPLER_2D);
- m_progs << p;
-
- p = ProgramInfo();
- p.commonTechniqueName = "PHONG"; // diffuse texture, specular texture
- p.vertShader = "diffusemap.vert";
- p.fragShader = "diffusespecularmap.frag";
- p.attributes << ProgramInfo::Param("position", "vertexPosition", "POSITION", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("normal", "vertexNormal", "NORMAL", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("texcoord0", "vertexTexCoord", "TEXCOORD_0", GLT_FLOAT_VEC2);
- p.uniforms << ProgramInfo::Param("projection", "projection", "PROJECTION", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("modelView", "modelView", "MODELVIEW", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("normalMatrix", "modelViewNormal", "MODELVIEWINVERSETRANSPOSE", GLT_FLOAT_MAT3);
- p.uniforms << ProgramInfo::Param("lightPosition", "lightPosition", QString(), GLT_FLOAT_VEC4);
- p.uniforms << ProgramInfo::Param("lightIntensity", "lightIntensity", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("ambient", "ka", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("shininess", "shininess", QString(), GLT_FLOAT);
- p.uniforms << ProgramInfo::Param("diffuse", "diffuseTexture", QString(), GLT_SAMPLER_2D);
- p.uniforms << ProgramInfo::Param("specular", "specularTexture", QString(), GLT_SAMPLER_2D);
- m_progs << p;
-
- p = ProgramInfo();
- p.commonTechniqueName = "PHONG"; // diffuse texture, specular RGBA, normalmap texture
- p.vertShader = "normaldiffusemap.vert";
- p.fragShader = "normaldiffusemap.frag";
- p.attributes << ProgramInfo::Param("position", "vertexPosition", "POSITION", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("normal", "vertexNormal", "NORMAL", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("texcoord0", "vertexTexCoord", "TEXCOORD_0", GLT_FLOAT_VEC2);
- p.attributes << ProgramInfo::Param("tangent", "vertexTangent", "TANGENT", GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("projection", "projection", "PROJECTION", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("modelView", "modelView", "MODELVIEW", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("normalMatrix", "modelViewNormal", "MODELVIEWINVERSETRANSPOSE", GLT_FLOAT_MAT3);
- p.uniforms << ProgramInfo::Param("lightPosition", "lightPosition", QString(), GLT_FLOAT_VEC4);
- p.uniforms << ProgramInfo::Param("lightIntensity", "lightIntensity", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("ambient", "ka", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("specular", "ks", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("shininess", "shininess", QString(), GLT_FLOAT);
- p.uniforms << ProgramInfo::Param("diffuse", "diffuseTexture", QString(), GLT_SAMPLER_2D);
- p.uniforms << ProgramInfo::Param("normalmap", "normalTexture", QString(), GLT_SAMPLER_2D);
- m_progs << p;
-
- p = ProgramInfo();
- p.commonTechniqueName = "PHONG"; // diffuse texture, specular texture, normalmap texture
- p.vertShader = "normaldiffusemap.vert";
- p.fragShader = "normaldiffusespecularmap.frag";
- p.attributes << ProgramInfo::Param("position", "vertexPosition", "POSITION", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("normal", "vertexNormal", "NORMAL", GLT_FLOAT_VEC3);
- p.attributes << ProgramInfo::Param("texcoord0", "vertexTexCoord", "TEXCOORD_0", GLT_FLOAT_VEC2);
- p.attributes << ProgramInfo::Param("tangent", "vertexTangent", "TANGENT", GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("projection", "projection", "PROJECTION", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("modelView", "modelView", "MODELVIEW", GLT_FLOAT_MAT4);
- p.uniforms << ProgramInfo::Param("normalMatrix", "modelViewNormal", "MODELVIEWINVERSETRANSPOSE", GLT_FLOAT_MAT3);
- p.uniforms << ProgramInfo::Param("lightPosition", "lightPosition", QString(), GLT_FLOAT_VEC4);
- p.uniforms << ProgramInfo::Param("lightIntensity", "lightIntensity", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("ambient", "ka", QString(), GLT_FLOAT_VEC3);
- p.uniforms << ProgramInfo::Param("shininess", "shininess", QString(), GLT_FLOAT);
- p.uniforms << ProgramInfo::Param("diffuse", "diffuseTexture", QString(), GLT_SAMPLER_2D);
- p.uniforms << ProgramInfo::Param("specular", "specularTexture", QString(), GLT_SAMPLER_2D);
- p.uniforms << ProgramInfo::Param("normalmap", "normalTexture", QString(), GLT_SAMPLER_2D);
- m_progs << p;
-
- m_subst_es2 << qMakePair(QByteArrayLiteral("$VERSION"), QByteArray());
- m_subst_es2 << qMakePair(QByteArrayLiteral("$ATTRIBUTE"), QByteArrayLiteral("attribute"));
- m_subst_es2 << qMakePair(QByteArrayLiteral("$VVARYING"), QByteArrayLiteral("varying"));
- m_subst_es2 << qMakePair(QByteArrayLiteral("$FVARYING"), QByteArrayLiteral("varying"));
- m_subst_es2 << qMakePair(QByteArrayLiteral("$TEXTURE2D"), QByteArrayLiteral("texture2D"));
- m_subst_es2 << qMakePair(QByteArrayLiteral("$DECL_FRAGCOLOR"), QByteArray());
- m_subst_es2 << qMakePair(QByteArrayLiteral("$FRAGCOLOR"), QByteArrayLiteral("gl_FragColor"));
- m_subst_es2 << qMakePair(QByteArrayLiteral("$HIGHP"), QByteArrayLiteral("highp"));
-
- m_subst_core << qMakePair(QByteArrayLiteral("$VERSION"), QByteArrayLiteral("#version 150 core"));
- m_subst_core << qMakePair(QByteArrayLiteral("$ATTRIBUTE"), QByteArrayLiteral("in"));
- m_subst_core << qMakePair(QByteArrayLiteral("$VVARYING"), QByteArrayLiteral("out"));
- m_subst_core << qMakePair(QByteArrayLiteral("$FVARYING"), QByteArrayLiteral("in"));
- m_subst_core << qMakePair(QByteArrayLiteral("$TEXTURE2D"), QByteArrayLiteral("texture"));
- m_subst_core << qMakePair(QByteArrayLiteral("$DECL_FRAGCOLOR"), QByteArrayLiteral("out vec4 fragColor;"));
- m_subst_core << qMakePair(QByteArrayLiteral("$FRAGCOLOR"), QByteArrayLiteral("fragColor"));
- m_subst_core << qMakePair(QByteArrayLiteral("$HIGHP "), QByteArray());
-}
-
-GltfExporter::ProgramInfo *GltfExporter::chooseProgram(uint materialIndex)
-{
- Importer::MaterialInfo matInfo = m_importer->materialInfo(materialIndex);
- const bool hasNormalTexture = matInfo.m_textures.contains("normal");
- const bool hasSpecularTexture = matInfo.m_textures.contains("specular");
- const bool hasDiffuseTexture = matInfo.m_textures.contains("diffuse");
-
- if (hasNormalTexture && !m_importer->allMeshesForMaterialHaveTangents(materialIndex))
- qWarning() << "WARNING: Tangent vectors not exported while the material requires it. (hint: try -t)";
-
- if (hasNormalTexture && hasSpecularTexture && hasDiffuseTexture) {
- if (opts.showLog)
- qDebug() << "Using program taking diffuse, specular, normal textures";
- return &m_progs[4];
- }
-
- if (hasNormalTexture && hasDiffuseTexture) {
- if (opts.showLog)
- qDebug() << "Using program taking diffuse, normal textures";
- return &m_progs[3];
- }
-
- if (hasSpecularTexture && hasDiffuseTexture) {
- if (opts.showLog)
- qDebug() << "Using program taking diffuse, specular textures";
- return &m_progs[2];
- }
-
- if (hasDiffuseTexture) {
- if (opts.showLog)
- qDebug() << "Using program taking diffuse texture";
- return &m_progs[1];
- }
-
- if (opts.showLog)
- qDebug() << "Using program without textures";
- return &m_progs[0];
-}
-
-QString GltfExporter::exportNode(const Importer::Node *n, QJsonObject &nodes)
-{
- QJsonObject node;
- node["name"] = n->name;
- QJsonArray children;
- for (const Importer::Node *c : n->children) {
- if (nodeIsUseful(c))
- children << exportNode(c, nodes);
- }
- node["children"] = children;
- QJsonArray matrix;
- const float *mtxp = n->transformation.constData();
- for (int j = 0; j < 16; ++j)
- matrix.append(*mtxp++);
- node["matrix"] = matrix;
- QJsonArray meshList;
- for (int j = 0; j < n->meshes.count(); ++j)
- meshList.append(m_importer->meshInfo(n->meshes[j]).name);
- if (!meshList.isEmpty()) {
- node["meshes"] = meshList;
- } else {
- QHash<QString, Importer::CameraInfo> cam = m_importer->cameraInfo();
- if (cam.contains(n->name))
- node["camera"] = cam[n->name].name;
- }
-
- nodes[n->uniqueName] = node;
- return n->uniqueName;
-}
-
-static inline QJsonArray col2jsvec(const QVector<float> &color, bool alpha = false)
-{
- QJsonArray arr;
- arr << color[0] << color[1] << color[2];
- if (alpha)
- arr << color[3];
- return arr;
-}
-
-static inline QJsonArray vec2jsvec(const QVector<float> &v)
-{
- QJsonArray arr;
- for (int i = 0; i < v.count(); ++i)
- arr << v[i];
- return arr;
-}
-
-static inline void promoteColorsToRGBA(QJsonObject *obj)
-{
- QJsonObject::iterator it = obj->begin(), itEnd = obj->end();
- while (it != itEnd) {
- QJsonArray arr = it.value().toArray();
- if (arr.count() == 3) {
- const QString key = it.key();
- if (key == QStringLiteral("ambient")
- || key == QStringLiteral("diffuse")
- || key == QStringLiteral("specular")) {
- arr.append(1);
- *it = arr;
- }
- }
- ++it;
- }
-}
-
-void GltfExporter::exportMaterials(QJsonObject &materials, QHash<QString, QString> *textureNameMap)
-{
- for (uint i = 0; i < m_importer->materialCount(); ++i) {
- Importer::MaterialInfo matInfo = m_importer->materialInfo(i);
- QJsonObject material;
- material["name"] = matInfo.originalName;
-
- bool opaque = true;
- QJsonObject vals;
- for (QHash<QByteArray, QString>::const_iterator it = matInfo.m_textures.constBegin(); it != matInfo.m_textures.constEnd(); ++it) {
- if (!textureNameMap->contains(it.value()))
- textureNameMap->insert(it.value(), newTextureName());
- QByteArray key = it.key();
- if (key == QByteArrayLiteral("normal")) // avoid clashing with the vertex normals
- key = QByteArrayLiteral("normalmap");
- // alpha is supported for diffuse textures, but have to check the image data to decide if blending is needed
- if (key == QByteArrayLiteral("diffuse")) {
- QString imgFn = opts.outDir + it.value();
- if (m_imageHasAlpha.contains(imgFn)) {
- if (m_imageHasAlpha[imgFn])
- opaque = false;
- } else {
-#ifdef HAS_QIMAGE
- QImage img(imgFn);
- if (!img.isNull()) {
- if (img.hasAlphaChannel()) {
- for (int y = 0; opaque && y < img.height(); ++y)
- for (int x = 0; opaque && x < img.width(); ++x)
- if (qAlpha(img.pixel(x, y)) < 255)
- opaque = false;
- }
- m_imageHasAlpha[imgFn] = !opaque;
- } else {
- qWarning() << "WARNING: Cannot determine presence of alpha for" << imgFn;
- }
-#else
- qWarning() << "WARNING: No image support, assuming all textures are opaque";
-#endif
- }
- }
- vals[key] = textureNameMap->value(it.value());
- }
- for (QHash<QByteArray, float>::const_iterator it = matInfo.m_values.constBegin();
- it != matInfo.m_values.constEnd(); ++it) {
- if (vals.contains(it.key()))
- continue;
- vals[it.key()] = it.value();
- }
- for (QHash<QByteArray, QVector<float> >::const_iterator it = matInfo.m_colors.constBegin();
- it != matInfo.m_colors.constEnd(); ++it) {
- if (vals.contains(it.key()))
- continue;
- // alpha is supported for the diffuse color. < 1 will enable blending.
- const bool alpha = it.key() == QStringLiteral("diffuse");
- if (alpha && it.value()[3] < 1.0f)
- opaque = false;
- vals[it.key()] = col2jsvec(it.value(), alpha);
- }
- if (opts.shaders)
- material["values"] = vals;
-
- ProgramInfo *prog = chooseProgram(i);
- TechniqueInfo techniqueInfo;
- bool needsNewTechnique = true;
- for (int j = 0; j < m_techniques.count(); ++j) {
- if (m_techniques[j].prog == prog) {
- techniqueInfo = m_techniques[j];
- needsNewTechnique = opaque != techniqueInfo.opaque;
- }
- if (!needsNewTechnique)
- break;
- }
- if (needsNewTechnique) {
- QString techniqueName = newTechniqueName();
- techniqueInfo = TechniqueInfo(techniqueName, opaque, prog);
- m_techniques.append(techniqueInfo);
- m_usedPrograms.insert(prog);
- }
-
- if (opts.shaders) {
- if (opts.showLog)
- qDebug().noquote() << "Material #" << i << "->" << techniqueInfo.name;
-
- material["technique"] = techniqueInfo.name;
- if (opts.genCore) {
- material["techniqueCore"] = techniqueInfo.coreName;
- material["techniqueGL2"] = techniqueInfo.gl2Name;
- }
- }
-
- if (opts.commonMat) {
- // The built-in shaders we output are of little use in practice.
- // Ideally we want Qt3D's own standard materials in order to have our
- // models participate in lighting for example. To achieve this, output
- // a KHR_materials_common block which Qt3D's loader will recognize and
- // prefer over the shader-based techniques.
- if (!prog->commonTechniqueName.isEmpty()) {
- QJsonObject commonMat;
- commonMat["technique"] = prog->commonTechniqueName;
- // Set the values as-is. "normalmap" is our own extension, not in the spec.
- // However, RGB colors have to be promoted to RGBA since the spec uses
- // vec4, and all types are pre-defined for common material values.
- promoteColorsToRGBA(&vals);
- commonMat["values"] = vals;
- if (!opaque)
- commonMat["transparent"] = true;
- QJsonObject extensions;
- extensions["KHR_materials_common"] = commonMat;
- material["extensions"] = extensions;
- }
- }
-
- materials[matInfo.name] = material;
- }
-}
-
-void GltfExporter::writeShader(const QString &src, const QString &dst, const QVector<QPair<QByteArray, QByteArray> > &substTab)
-{
- for (const Shader shader : shaders) {
- QByteArray name = src.toUtf8();
- if (!qstrcmp(shader.name, name.constData())) {
- QString outfn = opts.outDir + dst;
- QFile outf(outfn);
- if (outf.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- m_files.insert(QFileInfo(outf.fileName()).fileName());
- if (opts.showLog)
- qDebug() << "Writing" << outfn;
- const auto lines = QString::fromUtf8(shader.text).split('\n');
- for (QString line : lines) {
- for (const auto &subst : substTab)
- line.replace(subst.first, subst.second);
- line += QStringLiteral("\n");
- outf.write(line.toUtf8());
- }
- }
- return;
- }
- }
- qWarning() << "ERROR: No shader found for" << src;
-}
-
-void GltfExporter::exportParameter(QJsonObject &dst, const QVector<ProgramInfo::Param> &params)
-{
- for (const ProgramInfo::Param &param : params) {
- QJsonObject parameter;
- parameter["type"] = int(param.type);
- if (!param.semantic.isEmpty())
- parameter["semantic"] = param.semantic;
- if (param.name == QStringLiteral("lightIntensity"))
- parameter["value"] = QJsonArray() << 1 << 1 << 1;
- if (param.name == QStringLiteral("lightPosition"))
- parameter["value"] = QJsonArray() << 0 << 0 << 0 << 1;
- dst[param.name] = parameter;
- }
-}
-
-namespace {
-struct ProgramNames
-{
- QString name;
- QString coreName;
-};
-}
-
-void GltfExporter::exportTechniques(QJsonObject &obj, const QString &basename)
-{
- if (!opts.shaders)
- return;
-
- QJsonObject shaders;
- QHash<QString, QString> shaderMap;
- for (ProgramInfo *prog : qAsConst(m_usedPrograms)) {
- QString newName;
- if (!shaderMap.contains(prog->vertShader)) {
- QJsonObject vertexShader;
- vertexShader["type"] = 35633;
- if (newName.isEmpty())
- newName = newShaderName();
- QString key = basename + QStringLiteral("_") + newName + QStringLiteral("_v");
- QString fn = QString(QStringLiteral("%1.vert")).arg(key);
- vertexShader["uri"] = fn;
- writeShader(prog->vertShader, fn, m_subst_es2);
- if (opts.genCore) {
- QJsonObject coreVertexShader;
- QString coreKey = QString(QStringLiteral("%1_core").arg(key));
- fn = QString(QStringLiteral("%1.vert")).arg(coreKey);
- coreVertexShader["type"] = 35633;
- coreVertexShader["uri"] = fn;
- writeShader(prog->vertShader, fn, m_subst_core);
- shaders[coreKey] = coreVertexShader;
- shaderMap.insert(QString(prog->vertShader + QStringLiteral("_core")), coreKey);
- }
- shaders[key] = vertexShader;
- shaderMap.insert(prog->vertShader, key);
- }
- if (!shaderMap.contains(prog->fragShader)) {
- QJsonObject fragmentShader;
- fragmentShader["type"] = 35632;
- if (newName.isEmpty())
- newName = newShaderName();
- QString key = basename + QStringLiteral("_") + newName + QStringLiteral("_f");
- QString fn = QString(QStringLiteral("%1.frag")).arg(key);
- fragmentShader["uri"] = fn;
- writeShader(prog->fragShader, fn, m_subst_es2);
- if (opts.genCore) {
- QJsonObject coreFragmentShader;
- QString coreKey = QString(QStringLiteral("%1_core").arg(key));
- fn = QString(QStringLiteral("%1.frag")).arg(coreKey);
- coreFragmentShader["type"] = 35632;
- coreFragmentShader["uri"] = fn;
- writeShader(prog->fragShader, fn, m_subst_core);
- shaders[coreKey] = coreFragmentShader;
- shaderMap.insert(QString(prog->fragShader + QStringLiteral("_core")), coreKey);
- }
- shaders[key] = fragmentShader;
- shaderMap.insert(prog->fragShader, key);
- }
- }
- obj["shaders"] = shaders;
-
- QJsonObject programs;
- QHash<const ProgramInfo *, ProgramNames> programMap;
- for (const ProgramInfo *prog : qAsConst(m_usedPrograms)) {
- QJsonObject program;
- program["vertexShader"] = shaderMap[prog->vertShader];
- program["fragmentShader"] = shaderMap[prog->fragShader];
- QJsonArray attrs;
- for (const ProgramInfo::Param &param : prog->attributes) {
- attrs << param.nameInShader;
- }
- program["attributes"] = attrs;
- QString programName = newProgramName();
- programMap[prog].name = programName;
- programs[programMap[prog].name] = program;
- if (opts.genCore) {
- program["vertexShader"] = shaderMap[QString(prog->vertShader + QLatin1String("_core"))];
- program["fragmentShader"] = shaderMap[QString(prog->fragShader + QLatin1String("_core"))];
- QJsonArray attrs;
- for (const ProgramInfo::Param &param : prog->attributes) {
- attrs << param.nameInShader;
- }
- program["attributes"] = attrs;
- programMap[prog].coreName = programName + QLatin1String("_core");
- programs[programMap[prog].coreName] = program;
- }
- }
- obj["programs"] = programs;
-
- QJsonObject techniques;
- for (const TechniqueInfo &techniqueInfo : qAsConst(m_techniques)) {
- QJsonObject technique;
- QJsonObject parameters;
- const ProgramInfo *prog = techniqueInfo.prog;
- exportParameter(parameters, prog->attributes);
- exportParameter(parameters, prog->uniforms);
- technique["parameters"] = parameters;
- technique["program"] = programMap[prog].name;
- QJsonObject progAttrs;
- for (const ProgramInfo::Param &param : prog->attributes) {
- progAttrs[param.nameInShader] = param.name;
- }
- technique["attributes"] = progAttrs;
- QJsonObject progUniforms;
- for (const ProgramInfo::Param &param : prog->uniforms) {
- progUniforms[param.nameInShader] = param.name;
- }
- technique["uniforms"] = progUniforms;
- QJsonObject states;
- QJsonArray enabledStates;
- enabledStates << GLT_DEPTH_TEST << GLT_CULL_FACE;
- if (!techniqueInfo.opaque) {
- enabledStates << GLT_BLEND;
- QJsonObject funcs;
- // GL_ONE, GL_ONE_MINUS_SRC_ALPHA
- funcs["blendFuncSeparate"] = QJsonArray() << 1 << 771 << 1 << 771;
- states["functions"] = funcs;
- }
- states["enable"] = enabledStates;
- technique["states"] = states;
- techniques[techniqueInfo.name] = technique;
-
- if (opts.genCore) {
- //GL2 (same as ES2)
- techniques[techniqueInfo.gl2Name] = technique;
-
- //Core
- technique["program"] = programMap[prog].coreName;
- techniques[techniqueInfo.coreName] = technique;
- }
- }
- obj["techniques"] = techniques;
-}
-
-void GltfExporter::exportAnimations(QJsonObject &obj,
- QVector<Importer::BufferInfo> &bufList,
- QVector<Importer::MeshInfo::BufferView> &bvList,
- QVector<Importer::MeshInfo::Accessor> &accList)
-{
- const auto animationInfos = m_importer->animations();
- if (animationInfos.empty()) {
- obj["animations"] = QJsonObject();
- return;
- }
-
- QString bvName = newBufferViewName();
- QByteArray extraData;
-
- int sz = 0;
- for (const Importer::AnimationInfo &ai : animationInfos)
- sz += ai.keyFrames.count() * (1 + 3 + 4 + 3) * sizeof(float);
- extraData.resize(sz);
-
- float *base = reinterpret_cast<float *>(extraData.data());
- float *p = base;
-
- QJsonObject animations;
- for (const Importer::AnimationInfo &ai : animationInfos) {
- QJsonObject animation;
- animation["name"] = ai.name;
- animation["count"] = ai.keyFrames.count();
- QJsonObject samplers;
- QJsonArray channels;
-
- if (ai.hasTranslation) {
- QJsonObject sampler;
- sampler["input"] = QStringLiteral("TIME");
- sampler["interpolation"] = QStringLiteral("LINEAR");
- sampler["output"] = QStringLiteral("translation");
- samplers["sampler_translation"] = sampler;
- QJsonObject channel;
- channel["sampler"] = QStringLiteral("sampler_translation");
- QJsonObject target;
- target["id"] = ai.targetNode;
- target["path"] = QStringLiteral("translation");
- channel["target"] = target;
- channels << channel;
- }
- if (ai.hasRotation) {
- QJsonObject sampler;
- sampler["input"] = QStringLiteral("TIME");
- sampler["interpolation"] = QStringLiteral("LINEAR");
- sampler["output"] = QStringLiteral("rotation");
- samplers["sampler_rotation"] = sampler;
- QJsonObject channel;
- channel["sampler"] = QStringLiteral("sampler_rotation");
- QJsonObject target;
- target["id"] = ai.targetNode;
- target["path"] = QStringLiteral("rotation");
- channel["target"] = target;
- channels << channel;
- }
- if (ai.hasScale) {
- QJsonObject sampler;
- sampler["input"] = QStringLiteral("TIME");
- sampler["interpolation"] = QStringLiteral("LINEAR");
- sampler["output"] = QStringLiteral("scale");
- samplers["sampler_scale"] = sampler;
- QJsonObject channel;
- channel["sampler"] = QStringLiteral("sampler_scale");
- QJsonObject target;
- target["id"] = ai.targetNode;
- target["path"] = QStringLiteral("scale");
- channel["target"] = target;
- channels << channel;
- }
-
- animation["samplers"] = samplers;
- animation["channels"] = channels;
- QJsonObject parameters;
-
- // Multiple animations sharing the same data should ideally use the
- // same accessors. This we unfortunately cannot do due to assimp's/our
- // own data structures so everything will get its own accessor and data
- // for now.
-
- Importer::MeshInfo::Accessor acc;
- acc.name = newAccessorName();
- acc.bufferView = bvName;
- acc.count = ai.keyFrames.count();
- acc.componentType = GLT_FLOAT;
- acc.type = QStringLiteral("SCALAR");
- acc.offset = uint((p - base) * sizeof(float));
- for (const Importer::KeyFrame &kf : ai.keyFrames)
- *p++ = kf.t;
- parameters["TIME"] = acc.name;
- accList << acc;
-
- if (ai.hasTranslation) {
- acc.name = newAccessorName();
- acc.componentType = GLT_FLOAT;
- acc.type = QStringLiteral("VEC3");
- acc.offset = uint((p - base) * sizeof(float));
- QVector<float> lastV;
- for (const Importer::KeyFrame &kf : ai.keyFrames) {
- const QVector<float> *v = kf.transValid ? &kf.trans : &lastV;
- *p++ = v->at(0);
- *p++ = v->at(1);
- *p++ = v->at(2);
- if (kf.transValid)
- lastV = *v;
- }
- parameters["translation"] = acc.name;
- accList << acc;
- }
- if (ai.hasRotation) {
- acc.name = newAccessorName();
- acc.componentType = GLT_FLOAT;
- acc.type = QStringLiteral("VEC4");
- acc.offset = uint((p - base) * sizeof(float));
- QVector<float> lastV;
- for (const Importer::KeyFrame &kf : ai.keyFrames) {
- const QVector<float> *v = kf.rotValid ? &kf.rot : &lastV;
- *p++ = v->at(1); // x
- *p++ = v->at(2); // y
- *p++ = v->at(3); // z
- *p++ = v->at(0); // w
- if (kf.rotValid)
- lastV = *v;
- }
- parameters["rotation"] = acc.name;
- accList << acc;
- }
- if (ai.hasScale) {
- acc.name = newAccessorName();
- acc.componentType = GLT_FLOAT;
- acc.type = QStringLiteral("VEC3");
- acc.offset = uint((p - base) * sizeof(float));
- QVector<float> lastV;
- for (const Importer::KeyFrame &kf : ai.keyFrames) {
- const QVector<float> *v = kf.scaleValid ? &kf.scale : &lastV;
- *p++ = v->at(0);
- *p++ = v->at(1);
- *p++ = v->at(2);
- if (kf.scaleValid)
- lastV = *v;
- }
- parameters["scale"] = acc.name;
- accList << acc;
- }
- animation["parameters"] = parameters;
-
- animations[newAnimationName()] = animation;
- }
- obj["animations"] = animations;
-
- // Now all the key frame data is in extraData. Append it to the first buffer
- // and create a single buffer view for it.
- if (!extraData.isEmpty()) {
- if (bufList.isEmpty()) {
- Importer::BufferInfo b;
- b.name = QStringLiteral("buf");
- bufList << b;
- }
- Importer::BufferInfo &buf(bufList[0]);
- Importer::MeshInfo::BufferView bv;
- bv.name = bvName;
- bv.offset = buf.data.size();
- bv.length = uint((p - base) * sizeof(float));
- bv.componentType = GLT_FLOAT;
- bvList << bv;
- extraData.resize(bv.length);
- buf.data += extraData;
- if (opts.showLog)
- qDebug().noquote() << "Animation data in buffer uses" << extraData.size() << "bytes";
- }
-}
-
-void GltfExporter::save(const QString &inputFilename)
-{
- if (opts.showLog)
- qDebug() << "Exporting";
-
- m_files.clear();
- m_techniques.clear();
- m_usedPrograms.clear();
-
- QFile f;
- QString basename = QFileInfo(inputFilename).baseName();
- QString bufNameTempl = basename + QStringLiteral("_%1.bin");
-
- copyExternalTextures(inputFilename);
- exportEmbeddedTextures();
- compressTextures();
-
- m_obj = QJsonObject();
-
- QVector<Importer::BufferInfo> bufList = m_importer->buffers();
- QVector<Importer::MeshInfo::BufferView> bvList = m_importer->bufferViews();
- QVector<Importer::MeshInfo::Accessor> accList = m_importer->accessors();
-
- // Animations add data to the buffer so process them first.
- exportAnimations(m_obj, bufList, bvList, accList);
-
- QJsonObject asset;
- asset["generator"] = QString(QStringLiteral("qgltf %1")).arg(QCoreApplication::applicationVersion());
- asset["version"] = QStringLiteral("1.0");
- asset["premultipliedAlpha"] = true;
- m_obj["asset"] = asset;
-
- for (int i = 0; i < bufList.count(); ++i) {
- QString bufName = bufNameTempl.arg(i + 1);
- f.setFileName(opts.outDir + bufName);
- if (opts.showLog)
- qDebug().noquote() << (opts.compress ? "Writing (compressed)" : "Writing") << (opts.outDir + bufName);
- if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- m_files.insert(QFileInfo(f.fileName()).fileName());
- QByteArray data = bufList[i].data;
- if (opts.compress)
- data = qCompress(data);
- f.write(data);
- f.close();
- }
- }
-
- QJsonObject buffers;
- for (int i = 0; i < bufList.count(); ++i) {
- QJsonObject buffer;
- buffer["byteLength"] = bufList[i].data.size();
- buffer["type"] = QStringLiteral("arraybuffer");
- buffer["uri"] = bufNameTempl.arg(i + 1);
- if (opts.compress)
- buffer["compression"] = QStringLiteral("Qt");
- buffers[bufList[i].name] = buffer;
- }
- m_obj["buffers"] = buffers;
-
- QJsonObject bufferViews;
- for (const Importer::MeshInfo::BufferView &bv : qAsConst(bvList)) {
- QJsonObject bufferView;
- bufferView["buffer"] = bufList[bv.bufIndex].name;
- bufferView["byteLength"] = int(bv.length);
- bufferView["byteOffset"] = int(bv.offset);
- if (bv.target)
- bufferView["target"] = int(bv.target);
- bufferViews[bv.name] = bufferView;
- }
- m_obj["bufferViews"] = bufferViews;
-
- QJsonObject accessors;
- for (const Importer::MeshInfo::Accessor &acc : qAsConst(accList)) {
- QJsonObject accessor;
- accessor["bufferView"] = acc.bufferView;
- accessor["byteOffset"] = int(acc.offset);
- accessor["byteStride"] = int(acc.stride);
- accessor["count"] = int(acc.count);
- accessor["componentType"] = int(acc.componentType);
- accessor["type"] = acc.type;
- if (!acc.minVal.isEmpty() && !acc.maxVal.isEmpty()) {
- accessor["min"] = vec2jsvec(acc.minVal);
- accessor["max"] = vec2jsvec(acc.maxVal);
- }
- accessors[acc.name] = accessor;
- }
- m_obj["accessors"] = accessors;
-
- QJsonObject meshes;
- for (uint i = 0; i < m_importer->meshCount(); ++i) {
- const Importer::MeshInfo meshInfo = m_importer->meshInfo(i);
- QJsonObject mesh;
- mesh["name"] = meshInfo.originalName;
- QJsonArray prims;
- QJsonObject prim;
- prim["mode"] = 4; // triangles
- QJsonObject attrs;
- for (const Importer::MeshInfo::Accessor &acc : meshInfo.accessors) {
- if (acc.usage != QStringLiteral("INDEX"))
- attrs[acc.usage] = acc.name;
- else
- prim["indices"] = acc.name;
- }
- prim["attributes"] = attrs;
- prim["material"] = m_importer->materialInfo(meshInfo.materialIndex).name;
- prims.append(prim);
- mesh["primitives"] = prims;
- meshes[meshInfo.name] = mesh;
- }
- m_obj["meshes"] = meshes;
-
- QJsonObject cameras;
- const auto cameraInfos = m_importer->cameraInfo();
- for (const Importer::CameraInfo &camInfo : cameraInfos) {
- QJsonObject camera;
- QJsonObject persp;
- persp["aspect_ratio"] = camInfo.aspectRatio;
- persp["yfov"] = camInfo.yfov;
- persp["znear"] = camInfo.znear;
- persp["zfar"] = camInfo.zfar;
- camera["perspective"] = persp;
- camera["type"] = QStringLiteral("perspective");
- cameras[camInfo.name] = camera;
- }
- m_obj["cameras"] = cameras;
-
- QJsonArray sceneNodes;
- QJsonObject nodes;
- for (const Importer::Node *n : qAsConst(m_importer->rootNode()->children)) {
- if (nodeIsUseful(n))
- sceneNodes << exportNode(n, nodes);
- }
- m_obj["nodes"] = nodes;
-
- QJsonObject scenes;
- QJsonObject defaultScene;
- defaultScene["nodes"] = sceneNodes;
- scenes["defaultScene"] = defaultScene;
- m_obj["scenes"] = scenes;
- m_obj["scene"] = QStringLiteral("defaultScene");
-
- QJsonObject materials;
- QHash<QString, QString> textureNameMap;
- exportMaterials(materials, &textureNameMap);
- m_obj["materials"] = materials;
-
- QJsonObject textures;
- QHash<QString, QString> imageMap; // uri -> key
- for (QHash<QString, QString>::const_iterator it = textureNameMap.constBegin(); it != textureNameMap.constEnd(); ++it) {
- QJsonObject texture;
- if (!imageMap.contains(it.key()))
- imageMap[it.key()] = newImageName();
- texture["source"] = imageMap[it.key()];
- texture["format"] = 0x1908; // RGBA
- const bool compressed = m_compressedTextures.contains(it.key());
- texture["internalFormat"] = !compressed ? 0x1908 : 0x8D64; // RGBA / ETC1
- texture["sampler"] = !compressed ? QStringLiteral("sampler_mip_rep") : QStringLiteral("sampler_nonmip_rep");
- texture["target"] = 3553; // TEXTURE_2D
- texture["type"] = 5121; // UNSIGNED_BYTE
- textures[it.value()] = texture;
- }
- m_obj["textures"] = textures;
-
- QJsonObject images;
- for (QHash<QString, QString>::const_iterator it = imageMap.constBegin(); it != imageMap.constEnd(); ++it) {
- QJsonObject image;
- image["uri"] = m_compressedTextures.contains(it.key()) ? m_compressedTextures[it.key()] : it.key();
- images[it.value()] = image;
- }
- m_obj["images"] = images;
-
- QJsonObject samplers;
- QJsonObject sampler;
- sampler["magFilter"] = 9729; // LINEAR
- sampler["minFilter"] = 9987; // LINEAR_MIPMAP_LINEAR
- sampler["wrapS"] = 10497; // REPEAT
- sampler["wrapT"] = 10497;
- samplers["sampler_mip_rep"] = sampler;
- // Compressed textures may not support mipmapping with GLES.
- if (!m_compressedTextures.isEmpty()) {
- sampler["minFilter"] = 9729; // LINEAR
- samplers["sampler_nonmip_rep"] = sampler;
- }
- m_obj["samplers"] = samplers;
-
- exportTechniques(m_obj, basename);
-
- QString gltfName = opts.outDir + basename + QStringLiteral(".qgltf");
- f.setFileName(gltfName);
-
-#if QT_CONFIG(cborstreamwriter)
- if (opts.showLog)
- qDebug().noquote() << (opts.genBin ? "Writing (CBOR)" : "Writing") << gltfName;
-
- const QIODevice::OpenMode openMode = opts.genBin
- ? (QIODevice::WriteOnly | QIODevice::Truncate)
- : (QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
- const QByteArray data = opts.genBin
- ? QCborValue::fromJsonValue(m_obj).toCbor()
- : QJsonDocument(m_obj).toJson(opts.compact ? QJsonDocument::Compact
- : QJsonDocument::Indented);
-#else
- if (opts.showLog)
- qDebug().noquote() << "Writing" << gltfName;
-
- const QIODevice::OpenMode openMode
- = QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text;
- const QByteArray data
- = QJsonDocument(m_obj).toJson(opts.compact ? QJsonDocument::Compact
- : QJsonDocument::Indented);
-#endif
-
- if (f.open(openMode)) {
- m_files.insert(QFileInfo(f.fileName()).fileName());
- f.write(data);
- f.close();
- }
-
- QString qrcName = opts.outDir + basename + QStringLiteral(".qrc");
- f.setFileName(qrcName);
- if (opts.showLog)
- qDebug().noquote() << "Writing" << qrcName;
- if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
- QByteArray pre = "<RCC><qresource prefix=\"/models\">\n";
- QByteArray post = "</qresource></RCC>\n";
- f.write(pre);
- for (const QString &file : qAsConst(m_files)) {
- QString line = QString(QStringLiteral(" <file>%1</file>\n")).arg(file);
- f.write(line.toUtf8());
- }
- f.write(post);
- f.close();
- }
-
- if (opts.showLog)
- qDebug() << "Done\n";
-}
-
-static const char *description =
- "qgltf uses Assimp to import a variety of 3D model formats "
- "and export it into fast-to-load, optimized glTF "
- "assets embedded into Qt resource files.\n\n"
- "Note: this tool should typically not be invoked directly. Instead, "
- "let qmake manage it based on QT3D_MODELS in the .pro file.\n\n"
- "For standard Qt 3D usage the recommended options are -b -S.";
-
-int main(int argc, char **argv)
-{
- QCoreApplication app(argc, argv);
- app.setApplicationVersion(QStringLiteral("0.2"));
- app.setApplicationName(QStringLiteral("Qt glTF converter"));
-
- QCommandLineParser cmdLine;
- cmdLine.addHelpOption();
- cmdLine.addVersionOption();
- cmdLine.setApplicationDescription(QString::fromUtf8(description));
- QCommandLineOption outDirOpt(QStringLiteral("d"), QStringLiteral("Place all output data into <dir>"), QStringLiteral("dir"));
- cmdLine.addOption(outDirOpt);
-#if QT_CONFIG(cborstreamwriter)
- QCommandLineOption binOpt(QStringLiteral("b"), QStringLiteral("Store CBOR data in the .qgltf file"));
- cmdLine.addOption(binOpt);
-#endif
- QCommandLineOption compactOpt(QStringLiteral("m"), QStringLiteral("Store compact JSON in the .qgltf file"));
- cmdLine.addOption(compactOpt);
- QCommandLineOption compOpt(QStringLiteral("c"), QStringLiteral("qCompress() vertex/index data in the .bin file"));
- cmdLine.addOption(compOpt);
- QCommandLineOption tangentOpt(QStringLiteral("t"), QStringLiteral("Generate tangent vectors"));
- cmdLine.addOption(tangentOpt);
- QCommandLineOption nonInterleavedOpt(QStringLiteral("n"), QStringLiteral("Use non-interleaved buffer layout"));
- cmdLine.addOption(nonInterleavedOpt);
- QCommandLineOption scaleOpt(QStringLiteral("e"), QStringLiteral("Scale vertices by the float scale factor <factor>"), QStringLiteral("factor"));
- cmdLine.addOption(scaleOpt);
- QCommandLineOption coreOpt(QStringLiteral("g"), QStringLiteral("Generate OpenGL 3.2+ core profile shaders too"));
- cmdLine.addOption(coreOpt);
- QCommandLineOption etc1Opt(QStringLiteral("1"), QStringLiteral("Generate ETC1 compressed textures by invoking etc1tool (PNG only)"));
- cmdLine.addOption(etc1Opt);
- QCommandLineOption noCommonMatOpt(QStringLiteral("T"), QStringLiteral("Do not generate KHR_materials_common block"));
- cmdLine.addOption(noCommonMatOpt);
- QCommandLineOption noShadersOpt(QStringLiteral("S"), QStringLiteral("Do not generate shaders/programs/techniques"));
- cmdLine.addOption(noShadersOpt);
- QCommandLineOption silentOpt(QStringLiteral("s"), QStringLiteral("Silence debug output"));
- cmdLine.addOption(silentOpt);
- cmdLine.process(app);
- opts.outDir = cmdLine.value(outDirOpt);
-#if QT_CONFIG(cborstreamwriter)
- opts.genBin = cmdLine.isSet(binOpt);
-#endif
- opts.compact = cmdLine.isSet(compactOpt);
- opts.compress = cmdLine.isSet(compOpt);
- opts.genTangents = cmdLine.isSet(tangentOpt);
- opts.interleave = !cmdLine.isSet(nonInterleavedOpt);
- opts.scale = 1;
- if (cmdLine.isSet(scaleOpt)) {
- bool ok = false;
- float v;
- v = cmdLine.value(scaleOpt).toFloat(&ok);
- if (ok)
- opts.scale = v;
- }
- opts.genCore = cmdLine.isSet(coreOpt);
- opts.texComp = cmdLine.isSet(etc1Opt) ? Options::ETC1 : Options::NoTextureCompression;
- opts.commonMat = !cmdLine.isSet(noCommonMatOpt);
- opts.shaders = !cmdLine.isSet(noShadersOpt);
- opts.showLog = !cmdLine.isSet(silentOpt);
- if (!opts.outDir.isEmpty()) {
- if (!opts.outDir.endsWith('/'))
- opts.outDir.append('/');
- QDir().mkpath(opts.outDir);
- }
-
- const auto fileNames = cmdLine.positionalArguments();
- if (fileNames.isEmpty())
- cmdLine.showHelp();
-
- AssimpImporter importer;
- GltfExporter exporter(&importer);
- for (const QString &fn : fileNames) {
- if (!importer.load(fn)) {
- qWarning() << "Failed to import" << fn;
- continue;
- }
- exporter.save(fn);
- }
-
- return 0;
-}
diff --git a/tools/qgltf/qgltf.pro b/tools/qgltf/qgltf.pro
deleted file mode 100644
index c40016015..000000000
--- a/tools/qgltf/qgltf.pro
+++ /dev/null
@@ -1,7 +0,0 @@
-option(host_build)
-
-SOURCES = qgltf.cpp
-
-include(../../src/3rdparty/assimp/assimp_dependency.pri)
-
-load(qt_tool)
diff --git a/tools/tools.pro b/tools/tools.pro
deleted file mode 100644
index 3b457ed50..000000000
--- a/tools/tools.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-TEMPLATE = subdirs
-
-!qtHaveModule(3dcore): \
- return()
-
-QT_FOR_CONFIG += 3dcore-private
-qtConfig(assimp):qtConfig(commandlineparser): {
- SUBDIRS += qgltf
-}