From 438c488f5a8b45f5e580b5a97c79a42a0609b2ac Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Thu, 25 Oct 2018 17:18:04 +0800 Subject: [PATCH] Support glTF binary format export The normals haven't fix yet, this commit is mainly preparing for the next coming commits about animation export. The glTF text format abandoned for the binary format. --- dust3d.pro | 4 +- src/documentwindow.cpp | 17 +-- src/documentwindow.h | 2 +- src/exportpreviewwidget.cpp | 6 +- src/exportpreviewwidget.h | 2 +- src/{gltffile.cpp => glbfile.cpp} | 200 +++++++++++++++++++----------- src/{gltffile.h => glbfile.h} | 16 +-- 7 files changed, 152 insertions(+), 95 deletions(-) rename src/{gltffile.cpp => glbfile.cpp} (74%) rename src/{gltffile.h => glbfile.h} (67%) diff --git a/dust3d.pro b/dust3d.pro index 47d80b8d..cd1d3a92 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -84,8 +84,8 @@ HEADERS += src/snapshotxml.h SOURCES += src/ds3file.cpp HEADERS += src/ds3file.h -SOURCES += src/gltffile.cpp -HEADERS += src/gltffile.h +SOURCES += src/glbfile.cpp +HEADERS += src/glbfile.h SOURCES += src/theme.cpp HEADERS += src/theme.h diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp index 3a047421..c738be43 100644 --- a/src/documentwindow.cpp +++ b/src/documentwindow.cpp @@ -25,7 +25,7 @@ #include "util.h" #include "aboutwidget.h" #include "version.h" -#include "gltffile.h" +#include "glbfile.h" #include "graphicscontainerwidget.h" #include "parttreewidget.h" #include "rigwidget.h" @@ -1232,7 +1232,7 @@ void SkeletonDocumentWindow::showExportPreview() if (nullptr == m_exportPreviewWidget) { m_exportPreviewWidget = new ExportPreviewWidget(m_document, this); connect(m_exportPreviewWidget, &ExportPreviewWidget::regenerate, m_document, &Document::regenerateMesh); - connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsGltf, this, &SkeletonDocumentWindow::exportGltfResult); + connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsGlb, this, &SkeletonDocumentWindow::exportGlbResult); connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsFbx, this, &SkeletonDocumentWindow::exportFbxResult); connect(m_document, &Document::resultMeshChanged, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); connect(m_document, &Document::exportReady, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); @@ -1262,10 +1262,10 @@ void SkeletonDocumentWindow::exportFbxResult() QApplication::restoreOverrideCursor(); } -void SkeletonDocumentWindow::exportGltfResult() +void SkeletonDocumentWindow::exportGlbResult() { QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), - tr("GL Transmission Format (.gltf)")); + tr("glTF Binary Format (.glb)")); if (filename.isEmpty()) { return; } @@ -1275,10 +1275,10 @@ void SkeletonDocumentWindow::exportGltfResult() } QApplication::setOverrideCursor(Qt::WaitCursor); Outcome skeletonResult = m_document->currentPostProcessedResultContext(); - GltfFileWriter gltfFileWriter(skeletonResult, m_document->resultRigBones(), m_document->resultRigWeights(), filename); - gltfFileWriter.save(); - if (m_document->textureImage) - m_document->textureImage->save(gltfFileWriter.textureFilenameInGltf()); + GlbFileWriter glbFileWriter(skeletonResult, m_document->resultRigBones(), m_document->resultRigWeights(), filename, + m_document->textureImage); + glbFileWriter.save(); + /* QFileInfo nameInfo(filename); QString borderFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-BORDER.png"; if (m_document->textureBorderImage) @@ -1289,6 +1289,7 @@ void SkeletonDocumentWindow::exportGltfResult() QString colorFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-COLOR.png"; if (m_document->textureColorImage) m_document->textureColorImage->save(colorFilename); + */ QApplication::restoreOverrideCursor(); } diff --git a/src/documentwindow.h b/src/documentwindow.h index a4cff61b..b9310aae 100644 --- a/src/documentwindow.h +++ b/src/documentwindow.h @@ -39,7 +39,7 @@ public slots: void saveTo(const QString &saveAsFilename); void open(); void exportObjResult(); - void exportGltfResult(); + void exportGlbResult(); void exportFbxResult(); void showExportPreview(); void newWindow(); diff --git a/src/exportpreviewwidget.cpp b/src/exportpreviewwidget.cpp index 029c8026..b6a40722 100644 --- a/src/exportpreviewwidget.cpp +++ b/src/exportpreviewwidget.cpp @@ -30,15 +30,15 @@ ExportPreviewWidget::ExportPreviewWidget(Document *document, QWidget *parent) : connect(regenerateButton, &QPushButton::clicked, this, &ExportPreviewWidget::regenerate); QComboBox *exportFormatSelectBox = new QComboBox; - exportFormatSelectBox->addItem(tr("glTF")); - exportFormatSelectBox->addItem(tr("FBX")); + exportFormatSelectBox->addItem(tr(".glb")); + exportFormatSelectBox->addItem(tr(".fbx")); exportFormatSelectBox->setCurrentIndex(0); m_saveButton = new QPushButton(tr("Save")); connect(m_saveButton, &QPushButton::clicked, this, [=]() { auto currentIndex = exportFormatSelectBox->currentIndex(); if (0 == currentIndex) { - emit saveAsGltf(); + emit saveAsGlb(); } else if (1 == currentIndex) { emit saveAsFbx(); } diff --git a/src/exportpreviewwidget.h b/src/exportpreviewwidget.h index 08e3a709..77d881b0 100644 --- a/src/exportpreviewwidget.h +++ b/src/exportpreviewwidget.h @@ -14,7 +14,7 @@ class ExportPreviewWidget : public QDialog Q_OBJECT signals: void regenerate(); - void saveAsGltf(); + void saveAsGlb(); void saveAsFbx(); public: ExportPreviewWidget(Document *document, QWidget *parent=nullptr); diff --git a/src/gltffile.cpp b/src/glbfile.cpp similarity index 74% rename from src/gltffile.cpp rename to src/glbfile.cpp index 722a3309..3c2fd49f 100644 --- a/src/gltffile.cpp +++ b/src/glbfile.cpp @@ -4,7 +4,8 @@ #include #include #include -#include "gltffile.h" +#include +#include "glbfile.h" #include "version.h" #include "util.h" #include "jointnodetree.h" @@ -19,30 +20,36 @@ // http://quaternions.online/ // https://en.m.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions?wprov=sfla1 -bool GltfFileWriter::m_enableComment = true; +bool GlbFileWriter::m_enableComment = true; -GltfFileWriter::GltfFileWriter(Outcome &outcome, +GlbFileWriter::GlbFileWriter(Outcome &outcome, const std::vector *resultRigBones, const std::map *resultRigWeights, - const QString &filename) : + const QString &filename, + QImage *textureImage) : m_filename(filename), m_outputNormal(false), m_outputAnimation(true), m_outputUv(true), m_testOutputAsWhole(false) { - QFileInfo nameInfo(filename); - QString textureFilenameWithoutPath = nameInfo.completeBaseName() + ".png"; - m_textureFilename = nameInfo.path() + QDir::separator() + textureFilenameWithoutPath; + QDataStream binStream(&m_binByteArray, QIODevice::WriteOnly); + binStream.setFloatingPointPrecision(QDataStream::SinglePrecision); + binStream.setByteOrder(QDataStream::LittleEndian); - QByteArray binaries; - QDataStream stream(&binaries, QIODevice::WriteOnly); - stream.setFloatingPointPrecision(QDataStream::SinglePrecision); - stream.setByteOrder(QDataStream::LittleEndian); + auto alignBin = [this, &binStream] { + while (0 != this->m_binByteArray.size() % 4) { + binStream << (quint8)0; + } + }; - auto alignBinaries = [&binaries, &stream] { - while (0 != binaries.size() % 4) { - stream << (quint8)0; + QDataStream jsonStream(&m_jsonByteArray, QIODevice::WriteOnly); + jsonStream.setFloatingPointPrecision(QDataStream::SinglePrecision); + jsonStream.setByteOrder(QDataStream::LittleEndian); + + auto alignJson = [this, &jsonStream] { + while (0 != this->m_jsonByteArray.size() % 4) { + jsonStream << (quint8)' '; } }; @@ -95,18 +102,18 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, m_json["skins"][0]["skeleton"] = skeletonNodeStartIndex; m_json["skins"][0]["inverseBindMatrices"] = bufferViewIndex; - bufferViewFromOffset = (int)binaries.size(); + bufferViewFromOffset = (int)m_binByteArray.size(); m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; for (auto i = 0u; i < boneNodes.size(); i++) { const float *floatArray = boneNodes[i].inverseBindMatrix.constData(); for (auto j = 0u; j < 16; j++) { - stream << (float)floatArray[j]; + binStream << (float)floatArray[j]; } } - m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset; - Q_ASSERT((int)boneNodes.size() * 16 * sizeof(float) == binaries.size() - bufferViewFromOffset); - alignBinaries(); + m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset; + Q_ASSERT((int)boneNodes.size() * 16 * sizeof(float) == m_binByteArray.size() - bufferViewFromOffset); + alignBin(); if (m_enableComment) m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: mat").arg(QString::number(bufferViewIndex)).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; @@ -119,28 +126,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, m_json["nodes"][0]["mesh"] = 0; } - m_json["textures"][0]["sampler"] = 0; - m_json["textures"][0]["source"] = 0; - - m_json["images"][0]["uri"] = textureFilenameWithoutPath.toUtf8().constData(); - - m_json["samplers"][0]["magFilter"] = 9729; - m_json["samplers"][0]["minFilter"] = 9987; - m_json["samplers"][0]["wrapS"] = 33648; - m_json["samplers"][0]["wrapT"] = 33648; - const std::map *parts = &outcome.parts(); - - std::map testParts; - if (m_testOutputAsWhole) { - testParts[0].vertices = outcome.vertices; - testParts[0].triangles = outcome.triangles; - - m_outputNormal = false; - m_outputUv = false; - - parts = &testParts; - } int primitiveIndex = 0; for (const auto &part: *parts) { @@ -163,16 +149,16 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, primitiveIndex++; - bufferViewFromOffset = (int)binaries.size(); + bufferViewFromOffset = (int)m_binByteArray.size(); for (const auto &it: part.second.triangles) { - stream << (quint16)it.indicies[0] << (quint16)it.indicies[1] << (quint16)it.indicies[2]; + binStream << (quint16)it.indicies[0] << (quint16)it.indicies[1] << (quint16)it.indicies[2]; } m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteLength"] = (int)part.second.triangles.size() * 3 * sizeof(quint16); m_json["bufferViews"][bufferViewIndex]["target"] = 34963; - Q_ASSERT((int)part.second.triangles.size() * 3 * sizeof(quint16) == binaries.size() - bufferViewFromOffset); - alignBinaries(); + Q_ASSERT((int)part.second.triangles.size() * 3 * sizeof(quint16) == m_binByteArray.size() - bufferViewFromOffset); + alignBin(); if (m_enableComment) m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: triangle indicies").arg(QString::number(bufferViewIndex)).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; @@ -182,7 +168,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, m_json["accessors"][bufferViewIndex]["type"] = "SCALAR"; bufferViewIndex++; - bufferViewFromOffset = (int)binaries.size(); + bufferViewFromOffset = (int)m_binByteArray.size(); m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; float minX = 100; @@ -204,12 +190,12 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, minZ = it.position.z(); if (it.position.z() > maxZ) maxZ = it.position.z(); - stream << (float)it.position.x() << (float)it.position.y() << (float)it.position.z(); + binStream << (float)it.position.x() << (float)it.position.y() << (float)it.position.z(); } - Q_ASSERT((int)part.second.vertices.size() * 3 * sizeof(float) == binaries.size() - bufferViewFromOffset); + Q_ASSERT((int)part.second.vertices.size() * 3 * sizeof(float) == m_binByteArray.size() - bufferViewFromOffset); m_json["bufferViews"][bufferViewIndex]["byteLength"] = part.second.vertices.size() * 3 * sizeof(float); m_json["bufferViews"][bufferViewIndex]["target"] = 34962; - alignBinaries(); + alignBin(); if (m_enableComment) m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: xyz").arg(QString::number(bufferViewIndex)).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; @@ -223,7 +209,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, /* if (m_outputNormal) { - bufferViewFromOffset = (int)binaries.size(); + bufferViewFromOffset = (int)binByteArray.size(); m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; QStringList normalList; @@ -232,7 +218,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, if (m_enableComment && m_outputNormal) normalList.append(QString("<%1,%2,%3>").arg(QString::number(it.x())).arg(QString::number(it.y())).arg(QString::number(it.z()))); } - Q_ASSERT((int)part.second.interpolatedVertexNormals.size() * 3 * sizeof(float) == binaries.size() - bufferViewFromOffset); + Q_ASSERT((int)part.second.interpolatedVertexNormals.size() * 3 * sizeof(float) == binByteArray.size() - bufferViewFromOffset); m_json["bufferViews"][bufferViewIndex]["byteLength"] = part.second.vertices.size() * 3 * sizeof(float); m_json["bufferViews"][bufferViewIndex]["target"] = 34962; alignBinaries(); @@ -247,14 +233,14 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, }*/ if (m_outputUv) { - bufferViewFromOffset = (int)binaries.size(); + bufferViewFromOffset = (int)m_binByteArray.size(); m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; for (const auto &it: part.second.vertexUvs) { - stream << (float)it.uv[0] << (float)it.uv[1]; + binStream << (float)it.uv[0] << (float)it.uv[1]; } - m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset; - alignBinaries(); + m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset; + alignBin(); if (m_enableComment) m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: uv").arg(QString::number(bufferViewIndex)).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; @@ -266,7 +252,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, } if (resultRigWeights && !resultRigWeights->empty()) { - bufferViewFromOffset = (int)binaries.size(); + bufferViewFromOffset = (int)m_binByteArray.size(); m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; QStringList boneList; @@ -279,13 +265,13 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, if (findWeight != resultRigWeights->end()) { for (; i < MAX_WEIGHT_NUM; i++) { quint16 nodeIndex = (quint16)findWeight->second.boneIndicies[i]; - stream << (quint16)nodeIndex; + binStream << (quint16)nodeIndex; if (m_enableComment) boneList.append(QString("%1").arg(nodeIndex)); } } for (; i < MAX_WEIGHT_NUM; i++) { - stream << (quint16)0; + binStream << (quint16)0; if (m_enableComment) boneList.append(QString("%1").arg(0)); } @@ -293,8 +279,8 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, boneList.append(QString(">")); weightItIndex++; } - m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset; - alignBinaries(); + m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset; + alignBin(); if (m_enableComment) m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: bone indicies %2").arg(QString::number(bufferViewIndex)).arg(boneList.join(" ")).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; @@ -304,7 +290,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, m_json["accessors"][bufferViewIndex]["type"] = "VEC4"; bufferViewIndex++; - bufferViewFromOffset = (int)binaries.size(); + bufferViewFromOffset = (int)m_binByteArray.size(); m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; QStringList weightList; @@ -317,13 +303,13 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, if (findWeight != resultRigWeights->end()) { for (; i < MAX_WEIGHT_NUM; i++) { float weight = (float)findWeight->second.boneWeights[i]; - stream << (float)weight; + binStream << (float)weight; if (m_enableComment) weightList.append(QString("%1").arg(QString::number((float)weight))); } } for (; i < MAX_WEIGHT_NUM; i++) { - stream << (float)0.0; + binStream << (float)0.0; if (m_enableComment) weightList.append(QString("%1").arg(QString::number(0.0))); } @@ -331,8 +317,8 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, weightList.append(QString(">")); weightItIndex++; } - m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset; - alignBinaries(); + m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset; + alignBin(); if (m_enableComment) m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: bone weights %2").arg(QString::number(bufferViewIndex)).arg(weightList.join(" ")).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; @@ -344,21 +330,89 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome, } } - m_json["buffers"][0]["uri"] = QString("data:application/octet-stream;base64," + binaries.toBase64()).toUtf8().constData(); - m_json["buffers"][0]["byteLength"] = binaries.size(); + if (nullptr != textureImage) { + m_json["textures"][0]["sampler"] = 0; + m_json["textures"][0]["source"] = 0; + + m_json["samplers"][0]["magFilter"] = 9729; + m_json["samplers"][0]["minFilter"] = 9987; + m_json["samplers"][0]["wrapS"] = 33648; + m_json["samplers"][0]["wrapT"] = 33648; + + bufferViewFromOffset = (int)m_binByteArray.size(); + m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; + m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; + QByteArray pngByteArray; + QBuffer buffer(&pngByteArray); + textureImage->save(&buffer, "PNG"); + binStream.writeRawData(pngByteArray.data(), pngByteArray.size()); + alignBin(); + m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset; + m_json["images"][0]["bufferView"] = bufferViewIndex; + m_json["images"][0]["mimeType"] = "image/png"; + bufferViewIndex++; + } + + m_json["buffers"][0]["byteLength"] = m_binByteArray.size(); + + auto jsonString = m_json.dump(4); + jsonStream.writeRawData(jsonString.data(), jsonString.size()); + alignJson(); } -const QString &GltfFileWriter::textureFilenameInGltf() -{ - return m_textureFilename; -} - -bool GltfFileWriter::save() +bool GlbFileWriter::save() { QFile file(m_filename); if (!file.open(QIODevice::WriteOnly)) { return false; } - file.write(QString::fromStdString(m_json.dump(4)).toUtf8()); + QDataStream output(&file); + output.setFloatingPointPrecision(QDataStream::SinglePrecision); + output.setByteOrder(QDataStream::LittleEndian); + + uint32_t headerSize = 12; + uint32_t chunk0DescriptionSize = 8; + uint32_t chunk1DescriptionSize = 8; + uint32_t fileSize = headerSize + + chunk0DescriptionSize + m_jsonByteArray.size() + + chunk1DescriptionSize + m_binByteArray.size(); + + qDebug() << "Chunk 0 data size:" << m_jsonByteArray.size(); + qDebug() << "Chunk 1 data size:" << m_binByteArray.size(); + qDebug() << "File size:" << fileSize; + + //////////// Header //////////// + + // magic + output << (uint32_t)0x46546C67; + + // version + output << (uint32_t)0x00000002; + + // length + output << (uint32_t)fileSize; + + //////////// Chunk 0 (Json) //////////// + + // length + output << (uint32_t)m_jsonByteArray.size(); + + // type + output << (uint32_t)0x4E4F534A; + + // data + output.writeRawData(m_jsonByteArray.data(), m_jsonByteArray.size()); + + //////////// Chunk 1 (Binary Buffer) /// + + // length + output << (uint32_t)m_binByteArray.size(); + + // type + output << (uint32_t)0x004E4942; + + // data + output.writeRawData(m_binByteArray.data(), m_binByteArray.size()); + return true; } diff --git a/src/gltffile.h b/src/glbfile.h similarity index 67% rename from src/gltffile.h rename to src/glbfile.h index 6772d427..46c03cc9 100644 --- a/src/gltffile.h +++ b/src/glbfile.h @@ -1,32 +1,34 @@ -#ifndef DUST3D_GLTF_FILE_H -#define DUST3D_GLTF_FILE_H +#ifndef DUST3D_GLB_FILE_H +#define DUST3D_GLB_FILE_H #include #include #include #include #include #include +#include #include "outcome.h" #include "json.hpp" #include "document.h" -class GltfFileWriter : public QObject +class GlbFileWriter : public QObject { Q_OBJECT public: - GltfFileWriter(Outcome &outcome, + GlbFileWriter(Outcome &outcome, const std::vector *resultRigBones, const std::map *resultRigWeights, - const QString &filename); + const QString &filename, + QImage *textureImage=nullptr); bool save(); - const QString &textureFilenameInGltf(); private: QString m_filename; - QString m_textureFilename; bool m_outputNormal; bool m_outputAnimation; bool m_outputUv; bool m_testOutputAsWhole; + QByteArray m_binByteArray; + QByteArray m_jsonByteArray; private: nlohmann::json m_json; public: