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.
master
Jeremy Hu 2018-10-25 17:18:04 +08:00
parent 84607fba01
commit 438c488f5a
7 changed files with 152 additions and 95 deletions

View File

@ -84,8 +84,8 @@ HEADERS += src/snapshotxml.h
SOURCES += src/ds3file.cpp SOURCES += src/ds3file.cpp
HEADERS += src/ds3file.h HEADERS += src/ds3file.h
SOURCES += src/gltffile.cpp SOURCES += src/glbfile.cpp
HEADERS += src/gltffile.h HEADERS += src/glbfile.h
SOURCES += src/theme.cpp SOURCES += src/theme.cpp
HEADERS += src/theme.h HEADERS += src/theme.h

View File

@ -25,7 +25,7 @@
#include "util.h" #include "util.h"
#include "aboutwidget.h" #include "aboutwidget.h"
#include "version.h" #include "version.h"
#include "gltffile.h" #include "glbfile.h"
#include "graphicscontainerwidget.h" #include "graphicscontainerwidget.h"
#include "parttreewidget.h" #include "parttreewidget.h"
#include "rigwidget.h" #include "rigwidget.h"
@ -1232,7 +1232,7 @@ void SkeletonDocumentWindow::showExportPreview()
if (nullptr == m_exportPreviewWidget) { if (nullptr == m_exportPreviewWidget) {
m_exportPreviewWidget = new ExportPreviewWidget(m_document, this); m_exportPreviewWidget = new ExportPreviewWidget(m_document, this);
connect(m_exportPreviewWidget, &ExportPreviewWidget::regenerate, m_document, &Document::regenerateMesh); 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_exportPreviewWidget, &ExportPreviewWidget::saveAsFbx, this, &SkeletonDocumentWindow::exportFbxResult);
connect(m_document, &Document::resultMeshChanged, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); connect(m_document, &Document::resultMeshChanged, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner);
connect(m_document, &Document::exportReady, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); connect(m_document, &Document::exportReady, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner);
@ -1262,10 +1262,10 @@ void SkeletonDocumentWindow::exportFbxResult()
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
} }
void SkeletonDocumentWindow::exportGltfResult() void SkeletonDocumentWindow::exportGlbResult()
{ {
QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), QString filename = QFileDialog::getSaveFileName(this, QString(), QString(),
tr("GL Transmission Format (.gltf)")); tr("glTF Binary Format (.glb)"));
if (filename.isEmpty()) { if (filename.isEmpty()) {
return; return;
} }
@ -1275,10 +1275,10 @@ void SkeletonDocumentWindow::exportGltfResult()
} }
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
Outcome skeletonResult = m_document->currentPostProcessedResultContext(); Outcome skeletonResult = m_document->currentPostProcessedResultContext();
GltfFileWriter gltfFileWriter(skeletonResult, m_document->resultRigBones(), m_document->resultRigWeights(), filename); GlbFileWriter glbFileWriter(skeletonResult, m_document->resultRigBones(), m_document->resultRigWeights(), filename,
gltfFileWriter.save(); m_document->textureImage);
if (m_document->textureImage) glbFileWriter.save();
m_document->textureImage->save(gltfFileWriter.textureFilenameInGltf()); /*
QFileInfo nameInfo(filename); QFileInfo nameInfo(filename);
QString borderFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-BORDER.png"; QString borderFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-BORDER.png";
if (m_document->textureBorderImage) if (m_document->textureBorderImage)
@ -1289,6 +1289,7 @@ void SkeletonDocumentWindow::exportGltfResult()
QString colorFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-COLOR.png"; QString colorFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-COLOR.png";
if (m_document->textureColorImage) if (m_document->textureColorImage)
m_document->textureColorImage->save(colorFilename); m_document->textureColorImage->save(colorFilename);
*/
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
} }

View File

@ -39,7 +39,7 @@ public slots:
void saveTo(const QString &saveAsFilename); void saveTo(const QString &saveAsFilename);
void open(); void open();
void exportObjResult(); void exportObjResult();
void exportGltfResult(); void exportGlbResult();
void exportFbxResult(); void exportFbxResult();
void showExportPreview(); void showExportPreview();
void newWindow(); void newWindow();

View File

@ -30,15 +30,15 @@ ExportPreviewWidget::ExportPreviewWidget(Document *document, QWidget *parent) :
connect(regenerateButton, &QPushButton::clicked, this, &ExportPreviewWidget::regenerate); connect(regenerateButton, &QPushButton::clicked, this, &ExportPreviewWidget::regenerate);
QComboBox *exportFormatSelectBox = new QComboBox; QComboBox *exportFormatSelectBox = new QComboBox;
exportFormatSelectBox->addItem(tr("glTF")); exportFormatSelectBox->addItem(tr(".glb"));
exportFormatSelectBox->addItem(tr("FBX")); exportFormatSelectBox->addItem(tr(".fbx"));
exportFormatSelectBox->setCurrentIndex(0); exportFormatSelectBox->setCurrentIndex(0);
m_saveButton = new QPushButton(tr("Save")); m_saveButton = new QPushButton(tr("Save"));
connect(m_saveButton, &QPushButton::clicked, this, [=]() { connect(m_saveButton, &QPushButton::clicked, this, [=]() {
auto currentIndex = exportFormatSelectBox->currentIndex(); auto currentIndex = exportFormatSelectBox->currentIndex();
if (0 == currentIndex) { if (0 == currentIndex) {
emit saveAsGltf(); emit saveAsGlb();
} else if (1 == currentIndex) { } else if (1 == currentIndex) {
emit saveAsFbx(); emit saveAsFbx();
} }

View File

@ -14,7 +14,7 @@ class ExportPreviewWidget : public QDialog
Q_OBJECT Q_OBJECT
signals: signals:
void regenerate(); void regenerate();
void saveAsGltf(); void saveAsGlb();
void saveAsFbx(); void saveAsFbx();
public: public:
ExportPreviewWidget(Document *document, QWidget *parent=nullptr); ExportPreviewWidget(Document *document, QWidget *parent=nullptr);

View File

@ -4,7 +4,8 @@
#include <QDataStream> #include <QDataStream>
#include <QFileInfo> #include <QFileInfo>
#include <QDir> #include <QDir>
#include "gltffile.h" #include <QBuffer>
#include "glbfile.h"
#include "version.h" #include "version.h"
#include "util.h" #include "util.h"
#include "jointnodetree.h" #include "jointnodetree.h"
@ -19,30 +20,36 @@
// http://quaternions.online/ // http://quaternions.online/
// https://en.m.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions?wprov=sfla1 // 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<AutoRiggerBone> *resultRigBones, const std::vector<AutoRiggerBone> *resultRigBones,
const std::map<int, AutoRiggerVertexWeights> *resultRigWeights, const std::map<int, AutoRiggerVertexWeights> *resultRigWeights,
const QString &filename) : const QString &filename,
QImage *textureImage) :
m_filename(filename), m_filename(filename),
m_outputNormal(false), m_outputNormal(false),
m_outputAnimation(true), m_outputAnimation(true),
m_outputUv(true), m_outputUv(true),
m_testOutputAsWhole(false) m_testOutputAsWhole(false)
{ {
QFileInfo nameInfo(filename); QDataStream binStream(&m_binByteArray, QIODevice::WriteOnly);
QString textureFilenameWithoutPath = nameInfo.completeBaseName() + ".png"; binStream.setFloatingPointPrecision(QDataStream::SinglePrecision);
m_textureFilename = nameInfo.path() + QDir::separator() + textureFilenameWithoutPath; binStream.setByteOrder(QDataStream::LittleEndian);
QByteArray binaries; auto alignBin = [this, &binStream] {
QDataStream stream(&binaries, QIODevice::WriteOnly); while (0 != this->m_binByteArray.size() % 4) {
stream.setFloatingPointPrecision(QDataStream::SinglePrecision); binStream << (quint8)0;
stream.setByteOrder(QDataStream::LittleEndian); }
};
auto alignBinaries = [&binaries, &stream] { QDataStream jsonStream(&m_jsonByteArray, QIODevice::WriteOnly);
while (0 != binaries.size() % 4) { jsonStream.setFloatingPointPrecision(QDataStream::SinglePrecision);
stream << (quint8)0; 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]["skeleton"] = skeletonNodeStartIndex;
m_json["skins"][0]["inverseBindMatrices"] = bufferViewIndex; 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]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
for (auto i = 0u; i < boneNodes.size(); i++) { for (auto i = 0u; i < boneNodes.size(); i++) {
const float *floatArray = boneNodes[i].inverseBindMatrix.constData(); const float *floatArray = boneNodes[i].inverseBindMatrix.constData();
for (auto j = 0u; j < 16; j++) { for (auto j = 0u; j < 16; j++) {
stream << (float)floatArray[j]; binStream << (float)floatArray[j];
} }
} }
m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset;
Q_ASSERT((int)boneNodes.size() * 16 * sizeof(float) == binaries.size() - bufferViewFromOffset); Q_ASSERT((int)boneNodes.size() * 16 * sizeof(float) == m_binByteArray.size() - bufferViewFromOffset);
alignBinaries(); alignBin();
if (m_enableComment) if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: mat").arg(QString::number(bufferViewIndex)).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: mat").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
@ -119,29 +126,8 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
m_json["nodes"][0]["mesh"] = 0; 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<QUuid, ResultPart> *parts = &outcome.parts(); const std::map<QUuid, ResultPart> *parts = &outcome.parts();
std::map<QUuid, ResultPart> 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; int primitiveIndex = 0;
for (const auto &part: *parts) { for (const auto &part: *parts) {
@ -163,16 +149,16 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
primitiveIndex++; primitiveIndex++;
bufferViewFromOffset = (int)binaries.size(); bufferViewFromOffset = (int)m_binByteArray.size();
for (const auto &it: part.second.triangles) { 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]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
m_json["bufferViews"][bufferViewIndex]["byteLength"] = (int)part.second.triangles.size() * 3 * sizeof(quint16); m_json["bufferViews"][bufferViewIndex]["byteLength"] = (int)part.second.triangles.size() * 3 * sizeof(quint16);
m_json["bufferViews"][bufferViewIndex]["target"] = 34963; m_json["bufferViews"][bufferViewIndex]["target"] = 34963;
Q_ASSERT((int)part.second.triangles.size() * 3 * sizeof(quint16) == binaries.size() - bufferViewFromOffset); Q_ASSERT((int)part.second.triangles.size() * 3 * sizeof(quint16) == m_binByteArray.size() - bufferViewFromOffset);
alignBinaries(); alignBin();
if (m_enableComment) if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: triangle indicies").arg(QString::number(bufferViewIndex)).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: triangle indicies").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
@ -182,7 +168,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
m_json["accessors"][bufferViewIndex]["type"] = "SCALAR"; m_json["accessors"][bufferViewIndex]["type"] = "SCALAR";
bufferViewIndex++; bufferViewIndex++;
bufferViewFromOffset = (int)binaries.size(); bufferViewFromOffset = (int)m_binByteArray.size();
m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
float minX = 100; float minX = 100;
@ -204,12 +190,12 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
minZ = it.position.z(); minZ = it.position.z();
if (it.position.z() > maxZ) if (it.position.z() > maxZ)
maxZ = it.position.z(); 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]["byteLength"] = part.second.vertices.size() * 3 * sizeof(float);
m_json["bufferViews"][bufferViewIndex]["target"] = 34962; m_json["bufferViews"][bufferViewIndex]["target"] = 34962;
alignBinaries(); alignBin();
if (m_enableComment) if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: xyz").arg(QString::number(bufferViewIndex)).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: xyz").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
@ -223,7 +209,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
/* /*
if (m_outputNormal) { if (m_outputNormal) {
bufferViewFromOffset = (int)binaries.size(); bufferViewFromOffset = (int)binByteArray.size();
m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
QStringList normalList; QStringList normalList;
@ -232,7 +218,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
if (m_enableComment && m_outputNormal) 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()))); 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]["byteLength"] = part.second.vertices.size() * 3 * sizeof(float);
m_json["bufferViews"][bufferViewIndex]["target"] = 34962; m_json["bufferViews"][bufferViewIndex]["target"] = 34962;
alignBinaries(); alignBinaries();
@ -247,14 +233,14 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
}*/ }*/
if (m_outputUv) { if (m_outputUv) {
bufferViewFromOffset = (int)binaries.size(); bufferViewFromOffset = (int)m_binByteArray.size();
m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
for (const auto &it: part.second.vertexUvs) { 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; m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset;
alignBinaries(); alignBin();
if (m_enableComment) if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: uv").arg(QString::number(bufferViewIndex)).toUtf8().constData(); m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: uv").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
@ -266,7 +252,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
} }
if (resultRigWeights && !resultRigWeights->empty()) { if (resultRigWeights && !resultRigWeights->empty()) {
bufferViewFromOffset = (int)binaries.size(); bufferViewFromOffset = (int)m_binByteArray.size();
m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
QStringList boneList; QStringList boneList;
@ -279,13 +265,13 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
if (findWeight != resultRigWeights->end()) { if (findWeight != resultRigWeights->end()) {
for (; i < MAX_WEIGHT_NUM; i++) { for (; i < MAX_WEIGHT_NUM; i++) {
quint16 nodeIndex = (quint16)findWeight->second.boneIndicies[i]; quint16 nodeIndex = (quint16)findWeight->second.boneIndicies[i];
stream << (quint16)nodeIndex; binStream << (quint16)nodeIndex;
if (m_enableComment) if (m_enableComment)
boneList.append(QString("%1").arg(nodeIndex)); boneList.append(QString("%1").arg(nodeIndex));
} }
} }
for (; i < MAX_WEIGHT_NUM; i++) { for (; i < MAX_WEIGHT_NUM; i++) {
stream << (quint16)0; binStream << (quint16)0;
if (m_enableComment) if (m_enableComment)
boneList.append(QString("%1").arg(0)); boneList.append(QString("%1").arg(0));
} }
@ -293,8 +279,8 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
boneList.append(QString(">")); boneList.append(QString(">"));
weightItIndex++; weightItIndex++;
} }
m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset;
alignBinaries(); alignBin();
if (m_enableComment) 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]["__comment"] = QString("/accessors/%1: bone indicies %2").arg(QString::number(bufferViewIndex)).arg(boneList.join(" ")).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
@ -304,7 +290,7 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
m_json["accessors"][bufferViewIndex]["type"] = "VEC4"; m_json["accessors"][bufferViewIndex]["type"] = "VEC4";
bufferViewIndex++; bufferViewIndex++;
bufferViewFromOffset = (int)binaries.size(); bufferViewFromOffset = (int)m_binByteArray.size();
m_json["bufferViews"][bufferViewIndex]["buffer"] = 0; m_json["bufferViews"][bufferViewIndex]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
QStringList weightList; QStringList weightList;
@ -317,13 +303,13 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
if (findWeight != resultRigWeights->end()) { if (findWeight != resultRigWeights->end()) {
for (; i < MAX_WEIGHT_NUM; i++) { for (; i < MAX_WEIGHT_NUM; i++) {
float weight = (float)findWeight->second.boneWeights[i]; float weight = (float)findWeight->second.boneWeights[i];
stream << (float)weight; binStream << (float)weight;
if (m_enableComment) if (m_enableComment)
weightList.append(QString("%1").arg(QString::number((float)weight))); weightList.append(QString("%1").arg(QString::number((float)weight)));
} }
} }
for (; i < MAX_WEIGHT_NUM; i++) { for (; i < MAX_WEIGHT_NUM; i++) {
stream << (float)0.0; binStream << (float)0.0;
if (m_enableComment) if (m_enableComment)
weightList.append(QString("%1").arg(QString::number(0.0))); weightList.append(QString("%1").arg(QString::number(0.0)));
} }
@ -331,8 +317,8 @@ GltfFileWriter::GltfFileWriter(Outcome &outcome,
weightList.append(QString(">")); weightList.append(QString(">"));
weightItIndex++; weightItIndex++;
} }
m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset; m_json["bufferViews"][bufferViewIndex]["byteLength"] = m_binByteArray.size() - bufferViewFromOffset;
alignBinaries(); alignBin();
if (m_enableComment) 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]["__comment"] = QString("/accessors/%1: bone weights %2").arg(QString::number(bufferViewIndex)).arg(weightList.join(" ")).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex; 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(); if (nullptr != textureImage) {
m_json["buffers"][0]["byteLength"] = binaries.size(); 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++;
} }
const QString &GltfFileWriter::textureFilenameInGltf() m_json["buffers"][0]["byteLength"] = m_binByteArray.size();
{
return m_textureFilename; auto jsonString = m_json.dump(4);
jsonStream.writeRawData(jsonString.data(), jsonString.size());
alignJson();
} }
bool GltfFileWriter::save() bool GlbFileWriter::save()
{ {
QFile file(m_filename); QFile file(m_filename);
if (!file.open(QIODevice::WriteOnly)) { if (!file.open(QIODevice::WriteOnly)) {
return false; 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; return true;
} }

View File

@ -1,32 +1,34 @@
#ifndef DUST3D_GLTF_FILE_H #ifndef DUST3D_GLB_FILE_H
#define DUST3D_GLTF_FILE_H #define DUST3D_GLB_FILE_H
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QByteArray> #include <QByteArray>
#include <QMatrix4x4> #include <QMatrix4x4>
#include <vector> #include <vector>
#include <QQuaternion> #include <QQuaternion>
#include <QImage>
#include "outcome.h" #include "outcome.h"
#include "json.hpp" #include "json.hpp"
#include "document.h" #include "document.h"
class GltfFileWriter : public QObject class GlbFileWriter : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
GltfFileWriter(Outcome &outcome, GlbFileWriter(Outcome &outcome,
const std::vector<AutoRiggerBone> *resultRigBones, const std::vector<AutoRiggerBone> *resultRigBones,
const std::map<int, AutoRiggerVertexWeights> *resultRigWeights, const std::map<int, AutoRiggerVertexWeights> *resultRigWeights,
const QString &filename); const QString &filename,
QImage *textureImage=nullptr);
bool save(); bool save();
const QString &textureFilenameInGltf();
private: private:
QString m_filename; QString m_filename;
QString m_textureFilename;
bool m_outputNormal; bool m_outputNormal;
bool m_outputAnimation; bool m_outputAnimation;
bool m_outputUv; bool m_outputUv;
bool m_testOutputAsWhole; bool m_testOutputAsWhole;
QByteArray m_binByteArray;
QByteArray m_jsonByteArray;
private: private:
nlohmann::json m_json; nlohmann::json m_json;
public: public: