diff options
author | Mike Krus <mike.krus@kdab.com> | 2020-06-16 12:51:28 +0100 |
---|---|---|
committer | Mike Krus <mike.krus@kdab.com> | 2020-06-17 16:59:31 +0100 |
commit | b9994cd88925ca012d66e52d033cc9a3a909fc7a (patch) | |
tree | 2666e92b68e4d427e5d9fce5631856fc79ba5e2f /tools | |
parent | 66c088a25dbb3518d568143231c1771f20d7b0b3 (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.txt | 5 | ||||
-rw-r--r-- | tools/qgltf/CMakeLists.txt | 22 | ||||
-rw-r--r-- | tools/qgltf/qgltf.cpp | 2589 | ||||
-rw-r--r-- | tools/qgltf/qgltf.pro | 7 | ||||
-rw-r--r-- | tools/tools.pro | 9 |
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> ¶ms); - 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> ¶ms) -{ - for (const ProgramInfo::Param ¶m : 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 ¶m : 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 ¶m : 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 ¶m : prog->attributes) { - progAttrs[param.nameInShader] = param.name; - } - technique["attributes"] = progAttrs; - QJsonObject progUniforms; - for (const ProgramInfo::Param ¶m : 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 -} |