From 6c9d908a76d5748f055af26f3ddb38eaacc4d49e Mon Sep 17 00:00:00 2001 From: huxingyi Date: Sun, 22 Nov 2020 17:12:33 +0930 Subject: [PATCH] Support new export format: .ds3obj ds3obj is the abbreviation of Dust3D Object. It include the result mesh, rig, motions and extra informations such as nodes, edges which doesn't include in the glTF and FBX files. --- dust3d.pro | 7 +- src/document.cpp | 8 +-- src/document.h | 8 +-- src/documentwindow.cpp | 126 ++++++++++++++++++++++++++++++++++-- src/documentwindow.h | 3 + src/fbxfile.cpp | 6 +- src/fbxfile.h | 4 +- src/glbfile.cpp | 4 +- src/glbfile.h | 4 +- src/jointnodetree.cpp | 2 +- src/jointnodetree.h | 4 +- src/motioneditwidget.cpp | 8 +-- src/motioneditwidget.h | 10 +-- src/motionsgenerator.cpp | 8 +-- src/motionsgenerator.h | 10 +-- src/object.h | 2 - src/objectxml.cpp | 2 +- src/{rigger.cpp => rig.cpp} | 2 +- src/{rigger.h => rig.h} | 21 +++--- src/riggenerator.cpp | 32 ++++----- src/riggenerator.h | 10 +-- src/rigxml.cpp | 73 +++++++++++++++++++++ src/rigxml.h | 12 ++++ src/skinnedmeshcreator.cpp | 4 +- src/skinnedmeshcreator.h | 4 +- 25 files changed, 287 insertions(+), 87 deletions(-) rename src/{rigger.cpp => rig.cpp} (83%) rename src/{rigger.h => rig.h} (83%) create mode 100644 src/rigxml.cpp create mode 100644 src/rigxml.h diff --git a/dust3d.pro b/dust3d.pro index fdf3f9b2..74a9df47 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -260,8 +260,8 @@ HEADERS += src/skeletonside.h SOURCES += src/meshsplitter.cpp HEADERS += src/meshsplitter.h -SOURCES += src/rigger.cpp -HEADERS += src/rigger.h +SOURCES += src/rig.cpp +HEADERS += src/rig.h SOURCES += src/rigtype.cpp HEADERS += src/rigtype.h @@ -526,6 +526,9 @@ HEADERS += src/vertebratamotionparameterswidget.h SOURCES += src/objectxml.cpp HEADERS += src/objectxml.h +SOURCES += src/rigxml.cpp +HEADERS += src/rigxml.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/document.cpp b/src/document.cpp index 38ced8ef..1990d7de 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -3579,12 +3579,12 @@ void Document::rigReady() } } -const std::vector *Document::resultRigBones() const +const std::vector *Document::resultRigBones() const { return m_resultRigBones; } -const std::map *Document::resultRigWeights() const +const std::map *Document::resultRigWeights() const { return m_resultRigWeights; } @@ -3643,8 +3643,8 @@ void Document::generateMotions() return; } - const std::vector *rigBones = resultRigBones(); - const std::map *rigWeights = resultRigWeights(); + const std::vector *rigBones = resultRigBones(); + const std::map *rigWeights = resultRigWeights(); if (nullptr == rigBones || nullptr == rigWeights) { return; diff --git a/src/document.h b/src/document.h index 7cb2c634..a99d7d1a 100644 --- a/src/document.h +++ b/src/document.h @@ -470,8 +470,8 @@ public: bool isMeshGenerationSucceed(); Model *takeResultTextureMesh(); Model *takeResultRigWeightMesh(); - const std::vector *resultRigBones() const; - const std::map *resultRigWeights() const; + const std::vector *resultRigBones() const; + const std::map *resultRigWeights() const; void updateTurnaround(const QImage &image); void updateTextureImage(QImage *image); void updateTextureNormalImage(QImage *image); @@ -684,8 +684,8 @@ private: // need initialize bool m_smoothNormal; RigGenerator *m_rigGenerator; Model *m_resultRigWeightMesh; - std::vector *m_resultRigBones; - std::map *m_resultRigWeights; + std::vector *m_resultRigBones; + std::map *m_resultRigWeights; bool m_isRigObsolete; Object *m_riggedObject; bool m_currentRigSucceed; diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp index 3ac34ec0..3f0ec6c6 100644 --- a/src/documentwindow.cpp +++ b/src/documentwindow.cpp @@ -49,6 +49,7 @@ #include "fileforever.h" #include "documentsaver.h" #include "objectxml.h" +#include "rigxml.h" int DocumentWindow::m_autoRecovered = false; @@ -584,6 +585,10 @@ DocumentWindow::DocumentWindow() : connect(m_exportTexturesAction, &QAction::triggered, this, &DocumentWindow::exportTextures, Qt::QueuedConnection); m_fileMenu->addAction(m_exportTexturesAction); + m_exportDs3objAction = new QAction(tr("Export DS3OBJ..."), this); + connect(m_exportDs3objAction, &QAction::triggered, this, &DocumentWindow::exportDs3objResult, Qt::QueuedConnection); + m_fileMenu->addAction(m_exportDs3objAction); + m_exportRenderedAsImageAction = new QAction(tr("Export as Image..."), this); connect(m_exportRenderedAsImageAction, &QAction::triggered, this, &DocumentWindow::exportRenderedResult, Qt::QueuedConnection); m_fileMenu->addAction(m_exportRenderedAsImageAction); @@ -605,6 +610,7 @@ DocumentWindow::DocumentWindow() : m_exportAsGlbAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady()); m_exportAsFbxAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady()); m_exportTexturesAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady()); + m_exportDs3objAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady()); m_exportRenderedAsImageAction->setEnabled(m_graphicsWidget->hasItems()); }); @@ -1871,12 +1877,14 @@ void DocumentWindow::openPathAs(const QString &path, const QString &asName) for (int i = 0; i < ds3Reader.items().size(); ++i) { Ds3ReaderItem item = ds3Reader.items().at(i); if (item.type == "object") { - QByteArray data; - ds3Reader.loadItem(item.name, &data); - QXmlStreamReader stream(data); - Object *object = new Object; - loadObjectFromXmlStream(object, stream); - m_document->updateObject(object); + if (item.name == "object.xml") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + QXmlStreamReader stream(data); + Object *object = new Object; + loadObjectFromXmlStream(object, stream); + m_document->updateObject(object); + } } } } @@ -2045,6 +2053,17 @@ void DocumentWindow::exportGlbResult() exportGlbToFilename(filename); } +void DocumentWindow::exportDs3objResult() +{ + QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), + tr("Dust3D Object (.ds3obj)")); + if (filename.isEmpty()) { + return; + } + ensureFileExtension(&filename, ".ds3obj"); + exportDs3objToFilename(filename); +} + void DocumentWindow::exportGlbToFilename(const QString &filename) { if (!m_document->isExportReady()) { @@ -2068,6 +2087,101 @@ void DocumentWindow::exportGlbToFilename(const QString &filename) QApplication::restoreOverrideCursor(); } +void DocumentWindow::exportDs3objToFilename(const QString &filename) +{ + if (!m_document->isExportReady()) { + qDebug() << "Export but document is not export ready"; + return; + } + QApplication::setOverrideCursor(Qt::WaitCursor); + Ds3FileWriter ds3Writer; + + { + QByteArray objectXml; + QXmlStreamWriter stream(&objectXml); + saveObjectToXmlStream(&m_document->currentPostProcessedObject(), &stream); + if (objectXml.size() > 0) + ds3Writer.add("object.xml", "object", &objectXml); + } + + const std::vector *rigBones = m_document->resultRigBones(); + const std::map *rigWeights = m_document->resultRigWeights(); + if (nullptr != rigBones && nullptr != rigWeights) { + QByteArray rigXml; + QXmlStreamWriter stream(&rigXml); + saveRigToXmlStream(&m_document->currentPostProcessedObject(), rigBones, rigWeights, &stream); + if (rigXml.size() > 0) + ds3Writer.add("rig.xml", "rig", &rigXml); + } + + { + QByteArray motionsXml; + { + QXmlStreamWriter stream(&motionsXml); + QXmlStreamWriter *writer = &stream; + writer->setAutoFormatting(true); + writer->writeStartDocument(); + writer->writeStartElement("motions"); + for (const auto &motionIt: m_document->motionMap) { + writer->writeStartElement("motion"); + writer->writeAttribute("name", motionIt.second.name); + writer->writeStartElement("frames"); + for (const auto &it: motionIt.second.jointNodeTrees) { + writer->writeStartElement("frame"); + writer->writeAttribute("duration", QString::number(it.first)); + writer->writeStartElement("bones"); + const auto &nodes = it.second.nodes(); + for (size_t boneIndex = 0; boneIndex < nodes.size(); ++boneIndex) { + const auto &node = nodes[boneIndex]; + writer->writeStartElement("bone"); + writer->writeAttribute("index", QString::number(boneIndex)); + QMatrix4x4 translationMatrix; + translationMatrix.translate(node.translation); + QMatrix4x4 rotationMatrix; + rotationMatrix.rotate(node.rotation); + QMatrix4x4 matrix = translationMatrix * rotationMatrix; + const float *floatArray = matrix.constData(); + QStringList matrixItemList; + for (auto j = 0u; j < 16; j++) { + matrixItemList += QString::number(floatArray[j]); + } + writer->writeAttribute("matrix", matrixItemList.join(",")); + writer->writeEndElement(); + } + writer->writeEndElement(); + writer->writeEndElement(); + } + writer->writeEndElement(); + writer->writeEndElement(); + } + writer->writeEndElement(); + writer->writeEndDocument(); + } + if (motionsXml.size() > 0) + ds3Writer.add("motions.xml", "motions", &motionsXml); + } + + auto saveTexture = [&](const QString &filename, const QImage *image) { + if (nullptr == image) + return; + QByteArray byteArray; + QBuffer pngBuffer(&byteArray); + pngBuffer.open(QIODevice::WriteOnly); + image->save(&pngBuffer, "PNG"); + ds3Writer.add(filename, "asset", &byteArray); + }; + + saveTexture("object_color.png", m_document->textureImage); + saveTexture("object_normal.png", m_document->textureNormalImage); + saveTexture("object_metallic.png", m_document->textureMetalnessImage); + saveTexture("object_roughness.png", m_document->textureRoughnessImage); + saveTexture("object_ao.png", m_document->textureAmbientOcclusionImage); + + ds3Writer.save(filename); + + QApplication::restoreOverrideCursor(); +} + void DocumentWindow::updateXlockButtonState() { if (m_document->xlocked) diff --git a/src/documentwindow.h b/src/documentwindow.h index 41674679..ba52e07b 100644 --- a/src/documentwindow.h +++ b/src/documentwindow.h @@ -61,6 +61,7 @@ public slots: void exportGlbResult(); void exportFbxResult(); void exportTextures(); + void exportDs3objResult(); void newWindow(); void newDocument(); void saveAs(); @@ -92,6 +93,7 @@ public slots: void exportFbxToFilename(const QString &filename); void exportGlbToFilename(const QString &filename); void exportTexturesToDirectory(const QString &directory); + void exportDs3objToFilename(const QString &filename); void toggleRotation(); //void updateInfoWidgetPosition(); void generateNormalAndDepthMaps(); @@ -146,6 +148,7 @@ private: QAction *m_exportAsGlbAction; QAction *m_exportAsFbxAction; QAction *m_exportTexturesAction; + QAction *m_exportDs3objAction; QAction *m_exportRenderedAsImageAction; QMenu *m_editMenu; diff --git a/src/fbxfile.cpp b/src/fbxfile.cpp index cd5fbdd0..7f849b58 100644 --- a/src/fbxfile.cpp +++ b/src/fbxfile.cpp @@ -2201,8 +2201,8 @@ void FbxFileWriter::createDefinitions(size_t deformerCount, } FbxFileWriter::FbxFileWriter(Object &object, - const std::vector *resultRigBones, - const std::map *resultRigWeights, + const std::vector *resultRigBones, + const std::map *resultRigWeights, const QString &filename, QImage *textureImage, QImage *normalImage, @@ -2415,7 +2415,7 @@ FbxFileWriter::FbxFileWriter(Object &object, std::vector, std::vector>> bindPerBone(resultRigBones->size()); if (resultRigWeights && !resultRigWeights->empty()) { for (const auto &item: *resultRigWeights) { - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < MAX_WEIGHT_NUM; ++i) { const auto &boneIndex = item.second.boneIndices[i]; Q_ASSERT(boneIndex < bindPerBone.size()); if (0 == boneIndex) diff --git a/src/fbxfile.h b/src/fbxfile.h index f7cd9d79..40d536d7 100644 --- a/src/fbxfile.h +++ b/src/fbxfile.h @@ -14,8 +14,8 @@ class FbxFileWriter : public QObject Q_OBJECT public: FbxFileWriter(Object &object, - const std::vector *resultRigBones, - const std::map *resultRigWeights, + const std::vector *resultRigBones, + const std::map *resultRigWeights, const QString &filename, QImage *textureImage=nullptr, QImage *normalImage=nullptr, diff --git a/src/glbfile.cpp b/src/glbfile.cpp index 41bc4408..792fbcfa 100644 --- a/src/glbfile.cpp +++ b/src/glbfile.cpp @@ -23,8 +23,8 @@ bool GlbFileWriter::m_enableComment = false; GlbFileWriter::GlbFileWriter(Object &object, - const std::vector *resultRigBones, - const std::map *resultRigWeights, + const std::vector *resultRigBones, + const std::map *resultRigWeights, const QString &filename, QImage *textureImage, QImage *normalImage, diff --git a/src/glbfile.h b/src/glbfile.h index 457ea79a..eed0fcd7 100644 --- a/src/glbfile.h +++ b/src/glbfile.h @@ -16,8 +16,8 @@ class GlbFileWriter : public QObject Q_OBJECT public: GlbFileWriter(Object &object, - const std::vector *resultRigBones, - const std::map *resultRigWeights, + const std::vector *resultRigBones, + const std::map *resultRigWeights, const QString &filename, QImage *textureImage=nullptr, QImage *normalImage=nullptr, diff --git a/src/jointnodetree.cpp b/src/jointnodetree.cpp index 4a7ba934..7852ca8d 100644 --- a/src/jointnodetree.cpp +++ b/src/jointnodetree.cpp @@ -36,7 +36,7 @@ void JointNodeTree::updateMatrix(int index, const QMatrix4x4 &matrix) QQuaternion(scalar / length, x / length, y / length, z / length)); } -JointNodeTree::JointNodeTree(const std::vector *resultRigBones) +JointNodeTree::JointNodeTree(const std::vector *resultRigBones) { if (nullptr == resultRigBones || resultRigBones->empty()) return; diff --git a/src/jointnodetree.h b/src/jointnodetree.h index 4b5f1c0a..85d01ddb 100644 --- a/src/jointnodetree.h +++ b/src/jointnodetree.h @@ -3,7 +3,7 @@ #include #include #include -#include "rigger.h" +#include "rig.h" struct JointNode { @@ -22,7 +22,7 @@ class JointNodeTree { public: const std::vector &nodes() const; - JointNodeTree(const std::vector *resultRigBones); + JointNodeTree(const std::vector *resultRigBones); void updateRotation(int index, const QQuaternion &rotation); void updateTranslation(int index, const QVector3D &translation); void updateMatrix(int index, const QMatrix4x4 &matrix); diff --git a/src/motioneditwidget.cpp b/src/motioneditwidget.cpp index 55714dcc..f4799a36 100644 --- a/src/motioneditwidget.cpp +++ b/src/motioneditwidget.cpp @@ -187,8 +187,8 @@ void MotionEditWidget::save() } void MotionEditWidget::updateBones(RigType rigType, - const std::vector *rigBones, - const std::map *rigWeights, + const std::vector *rigBones, + const std::map *rigWeights, const Object *object) { m_rigType = rigType; @@ -205,8 +205,8 @@ void MotionEditWidget::updateBones(RigType rigType, if (nullptr != rigBones && nullptr != rigWeights && nullptr != object) { - m_bones = new std::vector(*rigBones); - m_rigWeights = new std::map(*rigWeights); + m_bones = new std::vector(*rigBones); + m_rigWeights = new std::map(*rigWeights); m_object = new Object(*object); generatePreview(); diff --git a/src/motioneditwidget.h b/src/motioneditwidget.h index def0e4e7..251bcc1c 100644 --- a/src/motioneditwidget.h +++ b/src/motioneditwidget.h @@ -6,7 +6,7 @@ #include #include #include "vertebratamotion.h" -#include "rigger.h" +#include "rig.h" #include "object.h" class SimpleShaderWidget; @@ -31,8 +31,8 @@ public slots: void generatePreview(); void previewReady(); void updateBones(RigType rigType, - const std::vector *rigBones, - const std::map *rigWeights, + const std::vector *rigBones, + const std::map *rigWeights, const Object *object); void setEditMotionName(const QString &name); void setEditMotionId(const QUuid &motionId); @@ -55,8 +55,8 @@ private: std::vector m_frames; size_t m_frameIndex = 0; RigType m_rigType = RigType::None; - std::vector *m_bones = nullptr; - std::map *m_rigWeights = nullptr; + std::vector *m_bones = nullptr; + std::map *m_rigWeights = nullptr; Object *m_object = nullptr; QLineEdit *m_nameEdit = nullptr; bool m_unsaved = false; diff --git a/src/motionsgenerator.cpp b/src/motionsgenerator.cpp index 733a2360..e6ab3b52 100644 --- a/src/motionsgenerator.cpp +++ b/src/motionsgenerator.cpp @@ -10,8 +10,8 @@ #include "util.h" MotionsGenerator::MotionsGenerator(RigType rigType, - const std::vector &bones, - const std::map &rigWeights, + const std::vector &bones, + const std::map &rigWeights, const Object &object) : m_rigType(rigType), m_bones(bones), @@ -252,7 +252,7 @@ void MotionsGenerator::generateMotion(const QUuid &motionId) const auto &vertebrataMotionFrames = vertebrataMotion->frames(); for (size_t frameIndex = 0; frameIndex < vertebrataMotionFrames.size(); ++frameIndex) { const auto &frame = vertebrataMotionFrames[frameIndex]; - std::vector transformedBones = m_bones; + std::vector transformedBones = m_bones; for (const auto &node: frame) { if (-1 == node.boneIndex) continue; @@ -326,7 +326,7 @@ void MotionsGenerator::generateMotion(const QUuid &motionId) std::vector transformedVertices(m_object.vertices.size()); for (size_t i = 0; i < m_object.vertices.size(); ++i) { const auto &weight = m_rigWeights[i]; - for (int x = 0; x < 4; x++) { + for (int x = 0; x < MAX_WEIGHT_NUM; x++) { float factor = weight.boneWeights[x]; if (factor > 0) { transformedVertices[i] += jointNodeMatrices[weight.boneIndices[x]] * m_object.vertices[i] * factor; diff --git a/src/motionsgenerator.h b/src/motionsgenerator.h index f2dcf566..1431d29e 100644 --- a/src/motionsgenerator.h +++ b/src/motionsgenerator.h @@ -6,7 +6,7 @@ #include #include "model.h" #include "simpleshadermesh.h" -#include "rigger.h" +#include "rig.h" #include "jointnodetree.h" #include "document.h" @@ -15,8 +15,8 @@ class MotionsGenerator : public QObject Q_OBJECT public: MotionsGenerator(RigType rigType, - const std::vector &bones, - const std::map &rigWeights, + const std::vector &bones, + const std::map &rigWeights, const Object &object); ~MotionsGenerator(); void addMotion(const QUuid &motionId, const std::map ¶meters); @@ -35,8 +35,8 @@ public slots: private: RigType m_rigType = RigType::None; - std::vector m_bones; - std::map m_rigWeights; + std::vector m_bones; + std::map m_rigWeights; Object m_object; std::map> m_motions; std::set m_generatedMotionIds; diff --git a/src/object.h b/src/object.h index 611b003a..e883caef 100644 --- a/src/object.h +++ b/src/object.h @@ -10,8 +10,6 @@ #include "bonemark.h" #include "componentlayer.h" -#define MAX_WEIGHT_NUM 4 - struct ObjectNode { QUuid partId; diff --git a/src/objectxml.cpp b/src/objectxml.cpp index 59758edd..05ddc039 100644 --- a/src/objectxml.cpp +++ b/src/objectxml.cpp @@ -273,7 +273,7 @@ void loadObjectFromXmlStream(Object *object, QXmlStreamReader &reader) } } } else if (reader.isEndElement()) { - if (fullName.startsWith("object.uvAreas")) { + if (fullName == "object.uvAreas") { object->setPartUvRects(partUvRects); } } else if (reader.isCharacters()) { diff --git a/src/rigger.cpp b/src/rig.cpp similarity index 83% rename from src/rigger.cpp rename to src/rig.cpp index 47a71769..f1ce344b 100644 --- a/src/rigger.cpp +++ b/src/rig.cpp @@ -3,4 +3,4 @@ #include "theme.h" #include "bonemark.h" #include "skeletonside.h" -#include "rigger.h" +#include "rig.h" diff --git a/src/rigger.h b/src/rig.h similarity index 83% rename from src/rigger.h rename to src/rig.h index 10b26f65..aa2571d8 100644 --- a/src/rigger.h +++ b/src/rig.h @@ -1,5 +1,5 @@ -#ifndef DUST3D_RIGGER_H -#define DUST3D_RIGGER_H +#ifndef DUST3D_RIG_H +#define DUST3D_RIG_H #include #include #include @@ -11,12 +11,9 @@ #include "rigtype.h" #include "skeletonside.h" -namespace Rigger -{ - const QString rootBoneName = "Body"; -}; +#define MAX_WEIGHT_NUM 4 -class RiggerBone +class RigBone { public: QString name; @@ -31,11 +28,11 @@ public: std::vector children; }; -class RiggerVertexWeights +class RigVertexWeights { public: - int boneIndices[4] = {0, 0, 0, 0}; - float boneWeights[4] = {0, 0, 0, 0}; + int boneIndices[MAX_WEIGHT_NUM] = {0}; + float boneWeights[MAX_WEIGHT_NUM] = {0}; void addBone(int boneIndex, float weight) { for (auto &it: m_boneRawWeights) { @@ -53,12 +50,12 @@ public: return a.second > b.second; }); float totalWeight = 0; - for (size_t i = 0; i < m_boneRawWeights.size() && i < 4; i++) { + for (size_t i = 0; i < m_boneRawWeights.size() && i < MAX_WEIGHT_NUM; i++) { const auto &item = m_boneRawWeights[i]; totalWeight += item.second; } if (totalWeight > 0) { - for (size_t i = 0; i < m_boneRawWeights.size() && i < 4; i++) { + for (size_t i = 0; i < m_boneRawWeights.size() && i < MAX_WEIGHT_NUM; i++) { const auto &item = m_boneRawWeights[i]; boneIndices[i] = item.first; boneWeights[i] = item.second / totalWeight; diff --git a/src/riggenerator.cpp b/src/riggenerator.cpp index c92f3cb5..9b8ab68d 100644 --- a/src/riggenerator.cpp +++ b/src/riggenerator.cpp @@ -80,16 +80,16 @@ Object *RigGenerator::takeObject() return object; } -std::vector *RigGenerator::takeResultBones() +std::vector *RigGenerator::takeResultBones() { - std::vector *resultBones = m_resultBones; + std::vector *resultBones = m_resultBones; m_resultBones = nullptr; return resultBones; } -std::map *RigGenerator::takeResultWeights() +std::map *RigGenerator::takeResultWeights() { - std::map *resultWeights = m_resultWeights; + std::map *resultWeights = m_resultWeights; m_resultWeights = nullptr; return resultWeights; } @@ -408,12 +408,12 @@ void RigGenerator::buildSkeleton() m_rootSpineJointIndex = m_attachLimbsToSpineJointIndices[0]; m_lastSpineJointIndex = m_spineJoints.size() - 1; - m_resultBones = new std::vector; - m_resultWeights = new std::map; + m_resultBones = new std::vector; + m_resultWeights = new std::map; { const auto &firstSpineNode = m_object->nodes[m_spineJoints[m_rootSpineJointIndex]]; - RiggerBone bone; + RigBone bone; bone.headPosition = QVector3D(0.0, 0.0, 0.0); bone.tailPosition = firstSpineNode.origin; bone.headRadius = 0; @@ -432,7 +432,7 @@ void RigGenerator::buildSkeleton() ++spineJointIndex) { const auto ¤tNode = m_object->nodes[m_spineJoints[spineJointIndex]]; const auto &nextNode = m_object->nodes[m_spineJoints[spineJointIndex + 1]]; - RiggerBone bone; + RigBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; bone.headRadius = currentNode.radius; @@ -454,7 +454,7 @@ void RigGenerator::buildSkeleton() const auto &spineNode = m_object->nodes[m_spineJoints[spineJointIndex]]; const auto &limbFirstNode = m_object->nodes[limbJoints[limbIndex][0]]; const auto &parentIndex = attachedBoneIndex(spineJointIndex); - RiggerBone bone; + RigBone bone; bone.headPosition = spineNode.origin; bone.tailPosition = limbFirstNode.origin; bone.headRadius = spineNode.radius; @@ -478,7 +478,7 @@ void RigGenerator::buildSkeleton() ++limbJointIndex) { const auto ¤tNode = m_object->nodes[joints[limbJointIndex]]; const auto &nextNode = m_object->nodes[joints[limbJointIndex + 1]]; - RiggerBone bone; + RigBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; bone.headRadius = currentNode.radius; @@ -516,7 +516,7 @@ void RigGenerator::buildSkeleton() ++neckJointIndex) { const auto ¤tNode = m_object->nodes[m_neckJoints[neckJointIndex]]; const auto &nextNode = m_object->nodes[m_neckJoints[neckJointIndex + 1]]; - RiggerBone bone; + RigBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; bone.headRadius = currentNode.radius; @@ -548,7 +548,7 @@ void RigGenerator::buildSkeleton() const auto &nextNode = spineJointIndex > 0 ? m_object->nodes[m_spineJoints[spineJointIndex - 1]] : m_object->nodes[m_tailJoints[0]]; - RiggerBone bone; + RigBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; bone.headRadius = currentNode.radius; @@ -574,7 +574,7 @@ void RigGenerator::buildSkeleton() ++tailJointIndex) { const auto ¤tNode = m_object->nodes[m_tailJoints[tailJointIndex]]; const auto &nextNode = m_object->nodes[m_tailJoints[tailJointIndex + 1]]; - RiggerBone bone; + RigBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; bone.headRadius = currentNode.radius; @@ -1190,17 +1190,17 @@ void RigGenerator::buildDemoMesh() const auto &resultWeights = *m_resultWeights; const auto &resultBones = *m_resultBones; - m_resultWeights = new std::map; + m_resultWeights = new std::map; *m_resultWeights = resultWeights; - m_resultBones = new std::vector; + m_resultBones = new std::vector; *m_resultBones = resultBones; for (const auto &weightItem: resultWeights) { size_t vertexIndex = weightItem.first; const auto &weight = weightItem.second; int blendR = 0, blendG = 0, blendB = 0; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < MAX_WEIGHT_NUM; i++) { int boneIndex = weight.boneIndices[i]; const auto &bone = resultBones[boneIndex]; blendR += bone.color.red() * weight.boneWeights[i]; diff --git a/src/riggenerator.h b/src/riggenerator.h index 1b06fe06..d800d1e9 100644 --- a/src/riggenerator.h +++ b/src/riggenerator.h @@ -6,7 +6,7 @@ #include #include "object.h" #include "model.h" -#include "rigger.h" +#include "rig.h" #include "rigtype.h" class RigGenerator : public QObject @@ -16,8 +16,8 @@ public: RigGenerator(RigType rigType, const Object &object); ~RigGenerator(); Model *takeResultMesh(); - std::vector *takeResultBones(); - std::map *takeResultWeights(); + std::vector *takeResultBones(); + std::map *takeResultWeights(); const std::vector> &messages(); Object *takeObject(); bool isSuccessful(); @@ -38,8 +38,8 @@ private: RigType m_rigType = RigType::None; Object *m_object = nullptr; Model *m_resultMesh = nullptr; - std::vector *m_resultBones = nullptr; - std::map *m_resultWeights = nullptr; + std::vector *m_resultBones = nullptr; + std::map *m_resultWeights = nullptr; std::vector> m_messages; std::map> m_neighborMap; std::vector m_boneNodeChain; diff --git a/src/rigxml.cpp b/src/rigxml.cpp new file mode 100644 index 00000000..c6b68f8b --- /dev/null +++ b/src/rigxml.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include "rigxml.h" +#include "util.h" +#include "jointnodetree.h" + +void saveRigToXmlStream(const Object *object, + const std::vector *rigBones, + const std::map *rigWeights, + QXmlStreamWriter *writer) +{ + JointNodeTree jointNodeTree(rigBones); + const auto &boneNodes = jointNodeTree.nodes(); + + writer->setAutoFormatting(true); + writer->writeStartDocument(); + + writer->writeStartElement("rig"); + writer->writeStartElement("bones"); + for (size_t boneIndex = 0; boneIndex < rigBones->size(); ++boneIndex) { + const auto &bone = (*rigBones)[boneIndex]; + const auto &node = boneNodes[boneIndex]; + writer->writeStartElement("bone"); + writer->writeAttribute("name", bone.name); + writer->writeAttribute("parentIndex", QString::number(bone.parent)); + writer->writeAttribute("headX", QString::number(bone.headPosition.x())); + writer->writeAttribute("headY", QString::number(bone.headPosition.y())); + writer->writeAttribute("headZ", QString::number(bone.headPosition.z())); + writer->writeAttribute("tailX", QString::number(bone.tailPosition.x())); + writer->writeAttribute("tailY", QString::number(bone.tailPosition.y())); + writer->writeAttribute("tailZ", QString::number(bone.tailPosition.z())); + writer->writeAttribute("headRadius", QString::number(bone.headRadius)); + writer->writeAttribute("tailRadius", QString::number(bone.tailRadius)); + writer->writeAttribute("color", bone.color.name(QColor::HexArgb)); + for (const auto &attribute: bone.attributes) + writer->writeAttribute(attribute.first, attribute.second); + const float *floatArray = node.inverseBindMatrix.constData(); + QStringList matrixItemList; + for (auto j = 0u; j < 16; j++) { + matrixItemList += QString::number(floatArray[j]); + } + writer->writeAttribute("inverseBindMatrix", matrixItemList.join(",")); + writer->writeEndElement(); + } + writer->writeEndElement(); + + writer->writeStartElement("weights"); + QStringList weightsList; + for (size_t vertexIndex = 0; vertexIndex < object->vertices.size(); ++vertexIndex) { + auto findWeights = rigWeights->find(vertexIndex); + if (findWeights == rigWeights->end()) { + QStringList vertexWeightsList; + for (size_t i = 0; i < MAX_WEIGHT_NUM; ++i) { + vertexWeightsList += "0,0"; + } + weightsList += vertexWeightsList.join(","); + } else { + QStringList vertexWeightsList; + for (size_t i = 0; i < MAX_WEIGHT_NUM; ++i) { + vertexWeightsList += QString::number(findWeights->second.boneIndices[i]) + "," + QString::number(findWeights->second.boneWeights[i]); + } + weightsList += vertexWeightsList.join(","); + } + } + writer->writeCharacters(weightsList.join(" ")); + writer->writeEndElement(); + + writer->writeEndElement(); + writer->writeEndDocument(); +} diff --git a/src/rigxml.h b/src/rigxml.h new file mode 100644 index 00000000..907118d4 --- /dev/null +++ b/src/rigxml.h @@ -0,0 +1,12 @@ +#ifndef DUST3D_RIG_XML_H +#define DUST3D_RIG_XML_H +#include +#include "rig.h" +#include "object.h" + +void saveRigToXmlStream(const Object *object, + const std::vector *rigBones, + const std::map *rigWeights, + QXmlStreamWriter *writer); + +#endif diff --git a/src/skinnedmeshcreator.cpp b/src/skinnedmeshcreator.cpp index 9b798ca4..912d38a2 100644 --- a/src/skinnedmeshcreator.cpp +++ b/src/skinnedmeshcreator.cpp @@ -2,7 +2,7 @@ #include "theme.h" SkinnedMeshCreator::SkinnedMeshCreator(const Object &object, - const std::map &resultWeights) : + const std::map &resultWeights) : m_object(object), m_resultWeights(resultWeights) { @@ -48,7 +48,7 @@ Model *SkinnedMeshCreator::createMeshFromTransform(const std::vector QMatrix4x4 mixedMatrix; transformedPositions[i][j] = QVector3D(); transformedPoseNormals[i][j] = QVector3D(); - for (int x = 0; x < 4; x++) { + for (int x = 0; x < MAX_WEIGHT_NUM; x++) { float factor = weight.boneWeights[x]; if (factor > 0) { transformedPositions[i][j] += matricies[weight.boneIndices[x]] * m_verticesBindPositions[i][j] * factor; diff --git a/src/skinnedmeshcreator.h b/src/skinnedmeshcreator.h index 77aa6f80..b098dc2b 100644 --- a/src/skinnedmeshcreator.h +++ b/src/skinnedmeshcreator.h @@ -12,11 +12,11 @@ class SkinnedMeshCreator { public: SkinnedMeshCreator(const Object &object, - const std::map &resultWeights); + const std::map &resultWeights); Model *createMeshFromTransform(const std::vector &matricies); private: Object m_object; - std::map m_resultWeights; + std::map m_resultWeights; std::vector> m_verticesOldIndices; std::vector> m_verticesBindPositions; std::vector> m_verticesBindNormals;