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
parent
84607fba01
commit
438c488f5a
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
bool GlbFileWriter::save()
|
||||||
{
|
|
||||||
return m_textureFilename;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GltfFileWriter::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;
|
||||||
}
|
}
|
|
@ -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:
|
Loading…
Reference in New Issue