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.
master
huxingyi 2020-11-22 17:12:33 +09:30
parent a8c7f55de2
commit 6c9d908a76
25 changed files with 287 additions and 87 deletions

View File

@ -260,8 +260,8 @@ HEADERS += src/skeletonside.h
SOURCES += src/meshsplitter.cpp SOURCES += src/meshsplitter.cpp
HEADERS += src/meshsplitter.h HEADERS += src/meshsplitter.h
SOURCES += src/rigger.cpp SOURCES += src/rig.cpp
HEADERS += src/rigger.h HEADERS += src/rig.h
SOURCES += src/rigtype.cpp SOURCES += src/rigtype.cpp
HEADERS += src/rigtype.h HEADERS += src/rigtype.h
@ -526,6 +526,9 @@ HEADERS += src/vertebratamotionparameterswidget.h
SOURCES += src/objectxml.cpp SOURCES += src/objectxml.cpp
HEADERS += src/objectxml.h HEADERS += src/objectxml.h
SOURCES += src/rigxml.cpp
HEADERS += src/rigxml.h
SOURCES += src/main.cpp SOURCES += src/main.cpp
HEADERS += src/version.h HEADERS += src/version.h

View File

@ -3579,12 +3579,12 @@ void Document::rigReady()
} }
} }
const std::vector<RiggerBone> *Document::resultRigBones() const const std::vector<RigBone> *Document::resultRigBones() const
{ {
return m_resultRigBones; return m_resultRigBones;
} }
const std::map<int, RiggerVertexWeights> *Document::resultRigWeights() const const std::map<int, RigVertexWeights> *Document::resultRigWeights() const
{ {
return m_resultRigWeights; return m_resultRigWeights;
} }
@ -3643,8 +3643,8 @@ void Document::generateMotions()
return; return;
} }
const std::vector<RiggerBone> *rigBones = resultRigBones(); const std::vector<RigBone> *rigBones = resultRigBones();
const std::map<int, RiggerVertexWeights> *rigWeights = resultRigWeights(); const std::map<int, RigVertexWeights> *rigWeights = resultRigWeights();
if (nullptr == rigBones || nullptr == rigWeights) { if (nullptr == rigBones || nullptr == rigWeights) {
return; return;

View File

@ -470,8 +470,8 @@ public:
bool isMeshGenerationSucceed(); bool isMeshGenerationSucceed();
Model *takeResultTextureMesh(); Model *takeResultTextureMesh();
Model *takeResultRigWeightMesh(); Model *takeResultRigWeightMesh();
const std::vector<RiggerBone> *resultRigBones() const; const std::vector<RigBone> *resultRigBones() const;
const std::map<int, RiggerVertexWeights> *resultRigWeights() const; const std::map<int, RigVertexWeights> *resultRigWeights() const;
void updateTurnaround(const QImage &image); void updateTurnaround(const QImage &image);
void updateTextureImage(QImage *image); void updateTextureImage(QImage *image);
void updateTextureNormalImage(QImage *image); void updateTextureNormalImage(QImage *image);
@ -684,8 +684,8 @@ private: // need initialize
bool m_smoothNormal; bool m_smoothNormal;
RigGenerator *m_rigGenerator; RigGenerator *m_rigGenerator;
Model *m_resultRigWeightMesh; Model *m_resultRigWeightMesh;
std::vector<RiggerBone> *m_resultRigBones; std::vector<RigBone> *m_resultRigBones;
std::map<int, RiggerVertexWeights> *m_resultRigWeights; std::map<int, RigVertexWeights> *m_resultRigWeights;
bool m_isRigObsolete; bool m_isRigObsolete;
Object *m_riggedObject; Object *m_riggedObject;
bool m_currentRigSucceed; bool m_currentRigSucceed;

View File

@ -49,6 +49,7 @@
#include "fileforever.h" #include "fileforever.h"
#include "documentsaver.h" #include "documentsaver.h"
#include "objectxml.h" #include "objectxml.h"
#include "rigxml.h"
int DocumentWindow::m_autoRecovered = false; int DocumentWindow::m_autoRecovered = false;
@ -584,6 +585,10 @@ DocumentWindow::DocumentWindow() :
connect(m_exportTexturesAction, &QAction::triggered, this, &DocumentWindow::exportTextures, Qt::QueuedConnection); connect(m_exportTexturesAction, &QAction::triggered, this, &DocumentWindow::exportTextures, Qt::QueuedConnection);
m_fileMenu->addAction(m_exportTexturesAction); 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); m_exportRenderedAsImageAction = new QAction(tr("Export as Image..."), this);
connect(m_exportRenderedAsImageAction, &QAction::triggered, this, &DocumentWindow::exportRenderedResult, Qt::QueuedConnection); connect(m_exportRenderedAsImageAction, &QAction::triggered, this, &DocumentWindow::exportRenderedResult, Qt::QueuedConnection);
m_fileMenu->addAction(m_exportRenderedAsImageAction); m_fileMenu->addAction(m_exportRenderedAsImageAction);
@ -605,6 +610,7 @@ DocumentWindow::DocumentWindow() :
m_exportAsGlbAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady()); m_exportAsGlbAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady());
m_exportAsFbxAction->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_exportTexturesAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady());
m_exportDs3objAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady());
m_exportRenderedAsImageAction->setEnabled(m_graphicsWidget->hasItems()); m_exportRenderedAsImageAction->setEnabled(m_graphicsWidget->hasItems());
}); });
@ -1871,6 +1877,7 @@ void DocumentWindow::openPathAs(const QString &path, const QString &asName)
for (int i = 0; i < ds3Reader.items().size(); ++i) { for (int i = 0; i < ds3Reader.items().size(); ++i) {
Ds3ReaderItem item = ds3Reader.items().at(i); Ds3ReaderItem item = ds3Reader.items().at(i);
if (item.type == "object") { if (item.type == "object") {
if (item.name == "object.xml") {
QByteArray data; QByteArray data;
ds3Reader.loadItem(item.name, &data); ds3Reader.loadItem(item.name, &data);
QXmlStreamReader stream(data); QXmlStreamReader stream(data);
@ -1880,6 +1887,7 @@ void DocumentWindow::openPathAs(const QString &path, const QString &asName)
} }
} }
} }
}
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
@ -2045,6 +2053,17 @@ void DocumentWindow::exportGlbResult()
exportGlbToFilename(filename); 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) void DocumentWindow::exportGlbToFilename(const QString &filename)
{ {
if (!m_document->isExportReady()) { if (!m_document->isExportReady()) {
@ -2068,6 +2087,101 @@ void DocumentWindow::exportGlbToFilename(const QString &filename)
QApplication::restoreOverrideCursor(); 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<RigBone> *rigBones = m_document->resultRigBones();
const std::map<int, RigVertexWeights> *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() void DocumentWindow::updateXlockButtonState()
{ {
if (m_document->xlocked) if (m_document->xlocked)

View File

@ -61,6 +61,7 @@ public slots:
void exportGlbResult(); void exportGlbResult();
void exportFbxResult(); void exportFbxResult();
void exportTextures(); void exportTextures();
void exportDs3objResult();
void newWindow(); void newWindow();
void newDocument(); void newDocument();
void saveAs(); void saveAs();
@ -92,6 +93,7 @@ public slots:
void exportFbxToFilename(const QString &filename); void exportFbxToFilename(const QString &filename);
void exportGlbToFilename(const QString &filename); void exportGlbToFilename(const QString &filename);
void exportTexturesToDirectory(const QString &directory); void exportTexturesToDirectory(const QString &directory);
void exportDs3objToFilename(const QString &filename);
void toggleRotation(); void toggleRotation();
//void updateInfoWidgetPosition(); //void updateInfoWidgetPosition();
void generateNormalAndDepthMaps(); void generateNormalAndDepthMaps();
@ -146,6 +148,7 @@ private:
QAction *m_exportAsGlbAction; QAction *m_exportAsGlbAction;
QAction *m_exportAsFbxAction; QAction *m_exportAsFbxAction;
QAction *m_exportTexturesAction; QAction *m_exportTexturesAction;
QAction *m_exportDs3objAction;
QAction *m_exportRenderedAsImageAction; QAction *m_exportRenderedAsImageAction;
QMenu *m_editMenu; QMenu *m_editMenu;

View File

@ -2201,8 +2201,8 @@ void FbxFileWriter::createDefinitions(size_t deformerCount,
} }
FbxFileWriter::FbxFileWriter(Object &object, FbxFileWriter::FbxFileWriter(Object &object,
const std::vector<RiggerBone> *resultRigBones, const std::vector<RigBone> *resultRigBones,
const std::map<int, RiggerVertexWeights> *resultRigWeights, const std::map<int, RigVertexWeights> *resultRigWeights,
const QString &filename, const QString &filename,
QImage *textureImage, QImage *textureImage,
QImage *normalImage, QImage *normalImage,
@ -2415,7 +2415,7 @@ FbxFileWriter::FbxFileWriter(Object &object,
std::vector<std::pair<std::vector<int32_t>, std::vector<double>>> bindPerBone(resultRigBones->size()); std::vector<std::pair<std::vector<int32_t>, std::vector<double>>> bindPerBone(resultRigBones->size());
if (resultRigWeights && !resultRigWeights->empty()) { if (resultRigWeights && !resultRigWeights->empty()) {
for (const auto &item: *resultRigWeights) { 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]; const auto &boneIndex = item.second.boneIndices[i];
Q_ASSERT(boneIndex < bindPerBone.size()); Q_ASSERT(boneIndex < bindPerBone.size());
if (0 == boneIndex) if (0 == boneIndex)

View File

@ -14,8 +14,8 @@ class FbxFileWriter : public QObject
Q_OBJECT Q_OBJECT
public: public:
FbxFileWriter(Object &object, FbxFileWriter(Object &object,
const std::vector<RiggerBone> *resultRigBones, const std::vector<RigBone> *resultRigBones,
const std::map<int, RiggerVertexWeights> *resultRigWeights, const std::map<int, RigVertexWeights> *resultRigWeights,
const QString &filename, const QString &filename,
QImage *textureImage=nullptr, QImage *textureImage=nullptr,
QImage *normalImage=nullptr, QImage *normalImage=nullptr,

View File

@ -23,8 +23,8 @@
bool GlbFileWriter::m_enableComment = false; bool GlbFileWriter::m_enableComment = false;
GlbFileWriter::GlbFileWriter(Object &object, GlbFileWriter::GlbFileWriter(Object &object,
const std::vector<RiggerBone> *resultRigBones, const std::vector<RigBone> *resultRigBones,
const std::map<int, RiggerVertexWeights> *resultRigWeights, const std::map<int, RigVertexWeights> *resultRigWeights,
const QString &filename, const QString &filename,
QImage *textureImage, QImage *textureImage,
QImage *normalImage, QImage *normalImage,

View File

@ -16,8 +16,8 @@ class GlbFileWriter : public QObject
Q_OBJECT Q_OBJECT
public: public:
GlbFileWriter(Object &object, GlbFileWriter(Object &object,
const std::vector<RiggerBone> *resultRigBones, const std::vector<RigBone> *resultRigBones,
const std::map<int, RiggerVertexWeights> *resultRigWeights, const std::map<int, RigVertexWeights> *resultRigWeights,
const QString &filename, const QString &filename,
QImage *textureImage=nullptr, QImage *textureImage=nullptr,
QImage *normalImage=nullptr, QImage *normalImage=nullptr,

View File

@ -36,7 +36,7 @@ void JointNodeTree::updateMatrix(int index, const QMatrix4x4 &matrix)
QQuaternion(scalar / length, x / length, y / length, z / length)); QQuaternion(scalar / length, x / length, y / length, z / length));
} }
JointNodeTree::JointNodeTree(const std::vector<RiggerBone> *resultRigBones) JointNodeTree::JointNodeTree(const std::vector<RigBone> *resultRigBones)
{ {
if (nullptr == resultRigBones || resultRigBones->empty()) if (nullptr == resultRigBones || resultRigBones->empty())
return; return;

View File

@ -3,7 +3,7 @@
#include <QMatrix4x4> #include <QMatrix4x4>
#include <vector> #include <vector>
#include <QQuaternion> #include <QQuaternion>
#include "rigger.h" #include "rig.h"
struct JointNode struct JointNode
{ {
@ -22,7 +22,7 @@ class JointNodeTree
{ {
public: public:
const std::vector<JointNode> &nodes() const; const std::vector<JointNode> &nodes() const;
JointNodeTree(const std::vector<RiggerBone> *resultRigBones); JointNodeTree(const std::vector<RigBone> *resultRigBones);
void updateRotation(int index, const QQuaternion &rotation); void updateRotation(int index, const QQuaternion &rotation);
void updateTranslation(int index, const QVector3D &translation); void updateTranslation(int index, const QVector3D &translation);
void updateMatrix(int index, const QMatrix4x4 &matrix); void updateMatrix(int index, const QMatrix4x4 &matrix);

View File

@ -187,8 +187,8 @@ void MotionEditWidget::save()
} }
void MotionEditWidget::updateBones(RigType rigType, void MotionEditWidget::updateBones(RigType rigType,
const std::vector<RiggerBone> *rigBones, const std::vector<RigBone> *rigBones,
const std::map<int, RiggerVertexWeights> *rigWeights, const std::map<int, RigVertexWeights> *rigWeights,
const Object *object) const Object *object)
{ {
m_rigType = rigType; m_rigType = rigType;
@ -205,8 +205,8 @@ void MotionEditWidget::updateBones(RigType rigType,
if (nullptr != rigBones && if (nullptr != rigBones &&
nullptr != rigWeights && nullptr != rigWeights &&
nullptr != object) { nullptr != object) {
m_bones = new std::vector<RiggerBone>(*rigBones); m_bones = new std::vector<RigBone>(*rigBones);
m_rigWeights = new std::map<int, RiggerVertexWeights>(*rigWeights); m_rigWeights = new std::map<int, RigVertexWeights>(*rigWeights);
m_object = new Object(*object); m_object = new Object(*object);
generatePreview(); generatePreview();

View File

@ -6,7 +6,7 @@
#include <QLineEdit> #include <QLineEdit>
#include <QUuid> #include <QUuid>
#include "vertebratamotion.h" #include "vertebratamotion.h"
#include "rigger.h" #include "rig.h"
#include "object.h" #include "object.h"
class SimpleShaderWidget; class SimpleShaderWidget;
@ -31,8 +31,8 @@ public slots:
void generatePreview(); void generatePreview();
void previewReady(); void previewReady();
void updateBones(RigType rigType, void updateBones(RigType rigType,
const std::vector<RiggerBone> *rigBones, const std::vector<RigBone> *rigBones,
const std::map<int, RiggerVertexWeights> *rigWeights, const std::map<int, RigVertexWeights> *rigWeights,
const Object *object); const Object *object);
void setEditMotionName(const QString &name); void setEditMotionName(const QString &name);
void setEditMotionId(const QUuid &motionId); void setEditMotionId(const QUuid &motionId);
@ -55,8 +55,8 @@ private:
std::vector<SimpleShaderMesh *> m_frames; std::vector<SimpleShaderMesh *> m_frames;
size_t m_frameIndex = 0; size_t m_frameIndex = 0;
RigType m_rigType = RigType::None; RigType m_rigType = RigType::None;
std::vector<RiggerBone> *m_bones = nullptr; std::vector<RigBone> *m_bones = nullptr;
std::map<int, RiggerVertexWeights> *m_rigWeights = nullptr; std::map<int, RigVertexWeights> *m_rigWeights = nullptr;
Object *m_object = nullptr; Object *m_object = nullptr;
QLineEdit *m_nameEdit = nullptr; QLineEdit *m_nameEdit = nullptr;
bool m_unsaved = false; bool m_unsaved = false;

View File

@ -10,8 +10,8 @@
#include "util.h" #include "util.h"
MotionsGenerator::MotionsGenerator(RigType rigType, MotionsGenerator::MotionsGenerator(RigType rigType,
const std::vector<RiggerBone> &bones, const std::vector<RigBone> &bones,
const std::map<int, RiggerVertexWeights> &rigWeights, const std::map<int, RigVertexWeights> &rigWeights,
const Object &object) : const Object &object) :
m_rigType(rigType), m_rigType(rigType),
m_bones(bones), m_bones(bones),
@ -252,7 +252,7 @@ void MotionsGenerator::generateMotion(const QUuid &motionId)
const auto &vertebrataMotionFrames = vertebrataMotion->frames(); const auto &vertebrataMotionFrames = vertebrataMotion->frames();
for (size_t frameIndex = 0; frameIndex < vertebrataMotionFrames.size(); ++frameIndex) { for (size_t frameIndex = 0; frameIndex < vertebrataMotionFrames.size(); ++frameIndex) {
const auto &frame = vertebrataMotionFrames[frameIndex]; const auto &frame = vertebrataMotionFrames[frameIndex];
std::vector<RiggerBone> transformedBones = m_bones; std::vector<RigBone> transformedBones = m_bones;
for (const auto &node: frame) { for (const auto &node: frame) {
if (-1 == node.boneIndex) if (-1 == node.boneIndex)
continue; continue;
@ -326,7 +326,7 @@ void MotionsGenerator::generateMotion(const QUuid &motionId)
std::vector<QVector3D> transformedVertices(m_object.vertices.size()); std::vector<QVector3D> transformedVertices(m_object.vertices.size());
for (size_t i = 0; i < m_object.vertices.size(); ++i) { for (size_t i = 0; i < m_object.vertices.size(); ++i) {
const auto &weight = m_rigWeights[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]; float factor = weight.boneWeights[x];
if (factor > 0) { if (factor > 0) {
transformedVertices[i] += jointNodeMatrices[weight.boneIndices[x]] * m_object.vertices[i] * factor; transformedVertices[i] += jointNodeMatrices[weight.boneIndices[x]] * m_object.vertices[i] * factor;

View File

@ -6,7 +6,7 @@
#include <set> #include <set>
#include "model.h" #include "model.h"
#include "simpleshadermesh.h" #include "simpleshadermesh.h"
#include "rigger.h" #include "rig.h"
#include "jointnodetree.h" #include "jointnodetree.h"
#include "document.h" #include "document.h"
@ -15,8 +15,8 @@ class MotionsGenerator : public QObject
Q_OBJECT Q_OBJECT
public: public:
MotionsGenerator(RigType rigType, MotionsGenerator(RigType rigType,
const std::vector<RiggerBone> &bones, const std::vector<RigBone> &bones,
const std::map<int, RiggerVertexWeights> &rigWeights, const std::map<int, RigVertexWeights> &rigWeights,
const Object &object); const Object &object);
~MotionsGenerator(); ~MotionsGenerator();
void addMotion(const QUuid &motionId, const std::map<QString, QString> &parameters); void addMotion(const QUuid &motionId, const std::map<QString, QString> &parameters);
@ -35,8 +35,8 @@ public slots:
private: private:
RigType m_rigType = RigType::None; RigType m_rigType = RigType::None;
std::vector<RiggerBone> m_bones; std::vector<RigBone> m_bones;
std::map<int, RiggerVertexWeights> m_rigWeights; std::map<int, RigVertexWeights> m_rigWeights;
Object m_object; Object m_object;
std::map<QUuid, std::map<QString, QString>> m_motions; std::map<QUuid, std::map<QString, QString>> m_motions;
std::set<QUuid> m_generatedMotionIds; std::set<QUuid> m_generatedMotionIds;

View File

@ -10,8 +10,6 @@
#include "bonemark.h" #include "bonemark.h"
#include "componentlayer.h" #include "componentlayer.h"
#define MAX_WEIGHT_NUM 4
struct ObjectNode struct ObjectNode
{ {
QUuid partId; QUuid partId;

View File

@ -273,7 +273,7 @@ void loadObjectFromXmlStream(Object *object, QXmlStreamReader &reader)
} }
} }
} else if (reader.isEndElement()) { } else if (reader.isEndElement()) {
if (fullName.startsWith("object.uvAreas")) { if (fullName == "object.uvAreas") {
object->setPartUvRects(partUvRects); object->setPartUvRects(partUvRects);
} }
} else if (reader.isCharacters()) { } else if (reader.isCharacters()) {

View File

@ -3,4 +3,4 @@
#include "theme.h" #include "theme.h"
#include "bonemark.h" #include "bonemark.h"
#include "skeletonside.h" #include "skeletonside.h"
#include "rigger.h" #include "rig.h"

View File

@ -1,5 +1,5 @@
#ifndef DUST3D_RIGGER_H #ifndef DUST3D_RIG_H
#define DUST3D_RIGGER_H #define DUST3D_RIG_H
#include <QtGlobal> #include <QtGlobal>
#include <QVector3D> #include <QVector3D>
#include <QObject> #include <QObject>
@ -11,12 +11,9 @@
#include "rigtype.h" #include "rigtype.h"
#include "skeletonside.h" #include "skeletonside.h"
namespace Rigger #define MAX_WEIGHT_NUM 4
{
const QString rootBoneName = "Body";
};
class RiggerBone class RigBone
{ {
public: public:
QString name; QString name;
@ -31,11 +28,11 @@ public:
std::vector<int> children; std::vector<int> children;
}; };
class RiggerVertexWeights class RigVertexWeights
{ {
public: public:
int boneIndices[4] = {0, 0, 0, 0}; int boneIndices[MAX_WEIGHT_NUM] = {0};
float boneWeights[4] = {0, 0, 0, 0}; float boneWeights[MAX_WEIGHT_NUM] = {0};
void addBone(int boneIndex, float weight) void addBone(int boneIndex, float weight)
{ {
for (auto &it: m_boneRawWeights) { for (auto &it: m_boneRawWeights) {
@ -53,12 +50,12 @@ public:
return a.second > b.second; return a.second > b.second;
}); });
float totalWeight = 0; 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]; const auto &item = m_boneRawWeights[i];
totalWeight += item.second; totalWeight += item.second;
} }
if (totalWeight > 0) { 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]; const auto &item = m_boneRawWeights[i];
boneIndices[i] = item.first; boneIndices[i] = item.first;
boneWeights[i] = item.second / totalWeight; boneWeights[i] = item.second / totalWeight;

View File

@ -80,16 +80,16 @@ Object *RigGenerator::takeObject()
return object; return object;
} }
std::vector<RiggerBone> *RigGenerator::takeResultBones() std::vector<RigBone> *RigGenerator::takeResultBones()
{ {
std::vector<RiggerBone> *resultBones = m_resultBones; std::vector<RigBone> *resultBones = m_resultBones;
m_resultBones = nullptr; m_resultBones = nullptr;
return resultBones; return resultBones;
} }
std::map<int, RiggerVertexWeights> *RigGenerator::takeResultWeights() std::map<int, RigVertexWeights> *RigGenerator::takeResultWeights()
{ {
std::map<int, RiggerVertexWeights> *resultWeights = m_resultWeights; std::map<int, RigVertexWeights> *resultWeights = m_resultWeights;
m_resultWeights = nullptr; m_resultWeights = nullptr;
return resultWeights; return resultWeights;
} }
@ -408,12 +408,12 @@ void RigGenerator::buildSkeleton()
m_rootSpineJointIndex = m_attachLimbsToSpineJointIndices[0]; m_rootSpineJointIndex = m_attachLimbsToSpineJointIndices[0];
m_lastSpineJointIndex = m_spineJoints.size() - 1; m_lastSpineJointIndex = m_spineJoints.size() - 1;
m_resultBones = new std::vector<RiggerBone>; m_resultBones = new std::vector<RigBone>;
m_resultWeights = new std::map<int, RiggerVertexWeights>; m_resultWeights = new std::map<int, RigVertexWeights>;
{ {
const auto &firstSpineNode = m_object->nodes[m_spineJoints[m_rootSpineJointIndex]]; const auto &firstSpineNode = m_object->nodes[m_spineJoints[m_rootSpineJointIndex]];
RiggerBone bone; RigBone bone;
bone.headPosition = QVector3D(0.0, 0.0, 0.0); bone.headPosition = QVector3D(0.0, 0.0, 0.0);
bone.tailPosition = firstSpineNode.origin; bone.tailPosition = firstSpineNode.origin;
bone.headRadius = 0; bone.headRadius = 0;
@ -432,7 +432,7 @@ void RigGenerator::buildSkeleton()
++spineJointIndex) { ++spineJointIndex) {
const auto &currentNode = m_object->nodes[m_spineJoints[spineJointIndex]]; const auto &currentNode = m_object->nodes[m_spineJoints[spineJointIndex]];
const auto &nextNode = m_object->nodes[m_spineJoints[spineJointIndex + 1]]; const auto &nextNode = m_object->nodes[m_spineJoints[spineJointIndex + 1]];
RiggerBone bone; RigBone bone;
bone.headPosition = currentNode.origin; bone.headPosition = currentNode.origin;
bone.tailPosition = nextNode.origin; bone.tailPosition = nextNode.origin;
bone.headRadius = currentNode.radius; bone.headRadius = currentNode.radius;
@ -454,7 +454,7 @@ void RigGenerator::buildSkeleton()
const auto &spineNode = m_object->nodes[m_spineJoints[spineJointIndex]]; const auto &spineNode = m_object->nodes[m_spineJoints[spineJointIndex]];
const auto &limbFirstNode = m_object->nodes[limbJoints[limbIndex][0]]; const auto &limbFirstNode = m_object->nodes[limbJoints[limbIndex][0]];
const auto &parentIndex = attachedBoneIndex(spineJointIndex); const auto &parentIndex = attachedBoneIndex(spineJointIndex);
RiggerBone bone; RigBone bone;
bone.headPosition = spineNode.origin; bone.headPosition = spineNode.origin;
bone.tailPosition = limbFirstNode.origin; bone.tailPosition = limbFirstNode.origin;
bone.headRadius = spineNode.radius; bone.headRadius = spineNode.radius;
@ -478,7 +478,7 @@ void RigGenerator::buildSkeleton()
++limbJointIndex) { ++limbJointIndex) {
const auto &currentNode = m_object->nodes[joints[limbJointIndex]]; const auto &currentNode = m_object->nodes[joints[limbJointIndex]];
const auto &nextNode = m_object->nodes[joints[limbJointIndex + 1]]; const auto &nextNode = m_object->nodes[joints[limbJointIndex + 1]];
RiggerBone bone; RigBone bone;
bone.headPosition = currentNode.origin; bone.headPosition = currentNode.origin;
bone.tailPosition = nextNode.origin; bone.tailPosition = nextNode.origin;
bone.headRadius = currentNode.radius; bone.headRadius = currentNode.radius;
@ -516,7 +516,7 @@ void RigGenerator::buildSkeleton()
++neckJointIndex) { ++neckJointIndex) {
const auto &currentNode = m_object->nodes[m_neckJoints[neckJointIndex]]; const auto &currentNode = m_object->nodes[m_neckJoints[neckJointIndex]];
const auto &nextNode = m_object->nodes[m_neckJoints[neckJointIndex + 1]]; const auto &nextNode = m_object->nodes[m_neckJoints[neckJointIndex + 1]];
RiggerBone bone; RigBone bone;
bone.headPosition = currentNode.origin; bone.headPosition = currentNode.origin;
bone.tailPosition = nextNode.origin; bone.tailPosition = nextNode.origin;
bone.headRadius = currentNode.radius; bone.headRadius = currentNode.radius;
@ -548,7 +548,7 @@ void RigGenerator::buildSkeleton()
const auto &nextNode = spineJointIndex > 0 ? const auto &nextNode = spineJointIndex > 0 ?
m_object->nodes[m_spineJoints[spineJointIndex - 1]] : m_object->nodes[m_spineJoints[spineJointIndex - 1]] :
m_object->nodes[m_tailJoints[0]]; m_object->nodes[m_tailJoints[0]];
RiggerBone bone; RigBone bone;
bone.headPosition = currentNode.origin; bone.headPosition = currentNode.origin;
bone.tailPosition = nextNode.origin; bone.tailPosition = nextNode.origin;
bone.headRadius = currentNode.radius; bone.headRadius = currentNode.radius;
@ -574,7 +574,7 @@ void RigGenerator::buildSkeleton()
++tailJointIndex) { ++tailJointIndex) {
const auto &currentNode = m_object->nodes[m_tailJoints[tailJointIndex]]; const auto &currentNode = m_object->nodes[m_tailJoints[tailJointIndex]];
const auto &nextNode = m_object->nodes[m_tailJoints[tailJointIndex + 1]]; const auto &nextNode = m_object->nodes[m_tailJoints[tailJointIndex + 1]];
RiggerBone bone; RigBone bone;
bone.headPosition = currentNode.origin; bone.headPosition = currentNode.origin;
bone.tailPosition = nextNode.origin; bone.tailPosition = nextNode.origin;
bone.headRadius = currentNode.radius; bone.headRadius = currentNode.radius;
@ -1190,17 +1190,17 @@ void RigGenerator::buildDemoMesh()
const auto &resultWeights = *m_resultWeights; const auto &resultWeights = *m_resultWeights;
const auto &resultBones = *m_resultBones; const auto &resultBones = *m_resultBones;
m_resultWeights = new std::map<int, RiggerVertexWeights>; m_resultWeights = new std::map<int, RigVertexWeights>;
*m_resultWeights = resultWeights; *m_resultWeights = resultWeights;
m_resultBones = new std::vector<RiggerBone>; m_resultBones = new std::vector<RigBone>;
*m_resultBones = resultBones; *m_resultBones = resultBones;
for (const auto &weightItem: resultWeights) { for (const auto &weightItem: resultWeights) {
size_t vertexIndex = weightItem.first; size_t vertexIndex = weightItem.first;
const auto &weight = weightItem.second; const auto &weight = weightItem.second;
int blendR = 0, blendG = 0, blendB = 0; 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]; int boneIndex = weight.boneIndices[i];
const auto &bone = resultBones[boneIndex]; const auto &bone = resultBones[boneIndex];
blendR += bone.color.red() * weight.boneWeights[i]; blendR += bone.color.red() * weight.boneWeights[i];

View File

@ -6,7 +6,7 @@
#include <unordered_set> #include <unordered_set>
#include "object.h" #include "object.h"
#include "model.h" #include "model.h"
#include "rigger.h" #include "rig.h"
#include "rigtype.h" #include "rigtype.h"
class RigGenerator : public QObject class RigGenerator : public QObject
@ -16,8 +16,8 @@ public:
RigGenerator(RigType rigType, const Object &object); RigGenerator(RigType rigType, const Object &object);
~RigGenerator(); ~RigGenerator();
Model *takeResultMesh(); Model *takeResultMesh();
std::vector<RiggerBone> *takeResultBones(); std::vector<RigBone> *takeResultBones();
std::map<int, RiggerVertexWeights> *takeResultWeights(); std::map<int, RigVertexWeights> *takeResultWeights();
const std::vector<std::pair<QtMsgType, QString>> &messages(); const std::vector<std::pair<QtMsgType, QString>> &messages();
Object *takeObject(); Object *takeObject();
bool isSuccessful(); bool isSuccessful();
@ -38,8 +38,8 @@ private:
RigType m_rigType = RigType::None; RigType m_rigType = RigType::None;
Object *m_object = nullptr; Object *m_object = nullptr;
Model *m_resultMesh = nullptr; Model *m_resultMesh = nullptr;
std::vector<RiggerBone> *m_resultBones = nullptr; std::vector<RigBone> *m_resultBones = nullptr;
std::map<int, RiggerVertexWeights> *m_resultWeights = nullptr; std::map<int, RigVertexWeights> *m_resultWeights = nullptr;
std::vector<std::pair<QtMsgType, QString>> m_messages; std::vector<std::pair<QtMsgType, QString>> m_messages;
std::map<size_t, std::unordered_set<size_t>> m_neighborMap; std::map<size_t, std::unordered_set<size_t>> m_neighborMap;
std::vector<BoneNodeChain> m_boneNodeChain; std::vector<BoneNodeChain> m_boneNodeChain;

73
src/rigxml.cpp Normal file
View File

@ -0,0 +1,73 @@
#include <stack>
#include <QUuid>
#include <QDebug>
#include <QStringList>
#include <QRegExp>
#include "rigxml.h"
#include "util.h"
#include "jointnodetree.h"
void saveRigToXmlStream(const Object *object,
const std::vector<RigBone> *rigBones,
const std::map<int, RigVertexWeights> *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();
}

12
src/rigxml.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef DUST3D_RIG_XML_H
#define DUST3D_RIG_XML_H
#include <QXmlStreamWriter>
#include "rig.h"
#include "object.h"
void saveRigToXmlStream(const Object *object,
const std::vector<RigBone> *rigBones,
const std::map<int, RigVertexWeights> *rigWeights,
QXmlStreamWriter *writer);
#endif

View File

@ -2,7 +2,7 @@
#include "theme.h" #include "theme.h"
SkinnedMeshCreator::SkinnedMeshCreator(const Object &object, SkinnedMeshCreator::SkinnedMeshCreator(const Object &object,
const std::map<int, RiggerVertexWeights> &resultWeights) : const std::map<int, RigVertexWeights> &resultWeights) :
m_object(object), m_object(object),
m_resultWeights(resultWeights) m_resultWeights(resultWeights)
{ {
@ -48,7 +48,7 @@ Model *SkinnedMeshCreator::createMeshFromTransform(const std::vector<QMatrix4x4>
QMatrix4x4 mixedMatrix; QMatrix4x4 mixedMatrix;
transformedPositions[i][j] = QVector3D(); transformedPositions[i][j] = QVector3D();
transformedPoseNormals[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]; float factor = weight.boneWeights[x];
if (factor > 0) { if (factor > 0) {
transformedPositions[i][j] += matricies[weight.boneIndices[x]] * m_verticesBindPositions[i][j] * factor; transformedPositions[i][j] += matricies[weight.boneIndices[x]] * m_verticesBindPositions[i][j] * factor;

View File

@ -12,11 +12,11 @@ class SkinnedMeshCreator
{ {
public: public:
SkinnedMeshCreator(const Object &object, SkinnedMeshCreator(const Object &object,
const std::map<int, RiggerVertexWeights> &resultWeights); const std::map<int, RigVertexWeights> &resultWeights);
Model *createMeshFromTransform(const std::vector<QMatrix4x4> &matricies); Model *createMeshFromTransform(const std::vector<QMatrix4x4> &matricies);
private: private:
Object m_object; Object m_object;
std::map<int, RiggerVertexWeights> m_resultWeights; std::map<int, RigVertexWeights> m_resultWeights;
std::vector<std::vector<int>> m_verticesOldIndices; std::vector<std::vector<int>> m_verticesOldIndices;
std::vector<std::vector<QVector3D>> m_verticesBindPositions; std::vector<std::vector<QVector3D>> m_verticesBindPositions;
std::vector<std::vector<QVector3D>> m_verticesBindNormals; std::vector<std::vector<QVector3D>> m_verticesBindNormals;