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
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

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

View File

@ -470,8 +470,8 @@ public:
bool isMeshGenerationSucceed();
Model *takeResultTextureMesh();
Model *takeResultRigWeightMesh();
const std::vector<RiggerBone> *resultRigBones() const;
const std::map<int, RiggerVertexWeights> *resultRigWeights() const;
const std::vector<RigBone> *resultRigBones() const;
const std::map<int, RigVertexWeights> *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<RiggerBone> *m_resultRigBones;
std::map<int, RiggerVertexWeights> *m_resultRigWeights;
std::vector<RigBone> *m_resultRigBones;
std::map<int, RigVertexWeights> *m_resultRigWeights;
bool m_isRigObsolete;
Object *m_riggedObject;
bool m_currentRigSucceed;

View File

@ -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,6 +1877,7 @@ 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") {
if (item.name == "object.xml") {
QByteArray data;
ds3Reader.loadItem(item.name, &data);
QXmlStreamReader stream(data);
@ -1880,6 +1887,7 @@ void DocumentWindow::openPathAs(const QString &path, const QString &asName)
}
}
}
}
QApplication::restoreOverrideCursor();
@ -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<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()
{
if (m_document->xlocked)

View File

@ -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;

View File

@ -2201,8 +2201,8 @@ void FbxFileWriter::createDefinitions(size_t deformerCount,
}
FbxFileWriter::FbxFileWriter(Object &object,
const std::vector<RiggerBone> *resultRigBones,
const std::map<int, RiggerVertexWeights> *resultRigWeights,
const std::vector<RigBone> *resultRigBones,
const std::map<int, RigVertexWeights> *resultRigWeights,
const QString &filename,
QImage *textureImage,
QImage *normalImage,
@ -2415,7 +2415,7 @@ FbxFileWriter::FbxFileWriter(Object &object,
std::vector<std::pair<std::vector<int32_t>, std::vector<double>>> 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)

View File

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

View File

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

View File

@ -16,8 +16,8 @@ class GlbFileWriter : public QObject
Q_OBJECT
public:
GlbFileWriter(Object &object,
const std::vector<RiggerBone> *resultRigBones,
const std::map<int, RiggerVertexWeights> *resultRigWeights,
const std::vector<RigBone> *resultRigBones,
const std::map<int, RigVertexWeights> *resultRigWeights,
const QString &filename,
QImage *textureImage=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));
}
JointNodeTree::JointNodeTree(const std::vector<RiggerBone> *resultRigBones)
JointNodeTree::JointNodeTree(const std::vector<RigBone> *resultRigBones)
{
if (nullptr == resultRigBones || resultRigBones->empty())
return;

View File

@ -3,7 +3,7 @@
#include <QMatrix4x4>
#include <vector>
#include <QQuaternion>
#include "rigger.h"
#include "rig.h"
struct JointNode
{
@ -22,7 +22,7 @@ class JointNodeTree
{
public:
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 updateTranslation(int index, const QVector3D &translation);
void updateMatrix(int index, const QMatrix4x4 &matrix);

View File

@ -187,8 +187,8 @@ void MotionEditWidget::save()
}
void MotionEditWidget::updateBones(RigType rigType,
const std::vector<RiggerBone> *rigBones,
const std::map<int, RiggerVertexWeights> *rigWeights,
const std::vector<RigBone> *rigBones,
const std::map<int, RigVertexWeights> *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<RiggerBone>(*rigBones);
m_rigWeights = new std::map<int, RiggerVertexWeights>(*rigWeights);
m_bones = new std::vector<RigBone>(*rigBones);
m_rigWeights = new std::map<int, RigVertexWeights>(*rigWeights);
m_object = new Object(*object);
generatePreview();

View File

@ -6,7 +6,7 @@
#include <QLineEdit>
#include <QUuid>
#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<RiggerBone> *rigBones,
const std::map<int, RiggerVertexWeights> *rigWeights,
const std::vector<RigBone> *rigBones,
const std::map<int, RigVertexWeights> *rigWeights,
const Object *object);
void setEditMotionName(const QString &name);
void setEditMotionId(const QUuid &motionId);
@ -55,8 +55,8 @@ private:
std::vector<SimpleShaderMesh *> m_frames;
size_t m_frameIndex = 0;
RigType m_rigType = RigType::None;
std::vector<RiggerBone> *m_bones = nullptr;
std::map<int, RiggerVertexWeights> *m_rigWeights = nullptr;
std::vector<RigBone> *m_bones = nullptr;
std::map<int, RigVertexWeights> *m_rigWeights = nullptr;
Object *m_object = nullptr;
QLineEdit *m_nameEdit = nullptr;
bool m_unsaved = false;

View File

@ -10,8 +10,8 @@
#include "util.h"
MotionsGenerator::MotionsGenerator(RigType rigType,
const std::vector<RiggerBone> &bones,
const std::map<int, RiggerVertexWeights> &rigWeights,
const std::vector<RigBone> &bones,
const std::map<int, RigVertexWeights> &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<RiggerBone> transformedBones = m_bones;
std::vector<RigBone> 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<QVector3D> 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;

View File

@ -6,7 +6,7 @@
#include <set>
#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<RiggerBone> &bones,
const std::map<int, RiggerVertexWeights> &rigWeights,
const std::vector<RigBone> &bones,
const std::map<int, RigVertexWeights> &rigWeights,
const Object &object);
~MotionsGenerator();
void addMotion(const QUuid &motionId, const std::map<QString, QString> &parameters);
@ -35,8 +35,8 @@ public slots:
private:
RigType m_rigType = RigType::None;
std::vector<RiggerBone> m_bones;
std::map<int, RiggerVertexWeights> m_rigWeights;
std::vector<RigBone> m_bones;
std::map<int, RigVertexWeights> m_rigWeights;
Object m_object;
std::map<QUuid, std::map<QString, QString>> m_motions;
std::set<QUuid> m_generatedMotionIds;

View File

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

View File

@ -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()) {

View File

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

View File

@ -1,5 +1,5 @@
#ifndef DUST3D_RIGGER_H
#define DUST3D_RIGGER_H
#ifndef DUST3D_RIG_H
#define DUST3D_RIG_H
#include <QtGlobal>
#include <QVector3D>
#include <QObject>
@ -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<int> 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;

View File

@ -80,16 +80,16 @@ Object *RigGenerator::takeObject()
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;
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;
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<RiggerBone>;
m_resultWeights = new std::map<int, RiggerVertexWeights>;
m_resultBones = new std::vector<RigBone>;
m_resultWeights = new std::map<int, RigVertexWeights>;
{
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 &currentNode = 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 &currentNode = 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 &currentNode = 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 &currentNode = 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<int, RiggerVertexWeights>;
m_resultWeights = new std::map<int, RigVertexWeights>;
*m_resultWeights = resultWeights;
m_resultBones = new std::vector<RiggerBone>;
m_resultBones = new std::vector<RigBone>;
*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];

View File

@ -6,7 +6,7 @@
#include <unordered_set>
#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<RiggerBone> *takeResultBones();
std::map<int, RiggerVertexWeights> *takeResultWeights();
std::vector<RigBone> *takeResultBones();
std::map<int, RigVertexWeights> *takeResultWeights();
const std::vector<std::pair<QtMsgType, QString>> &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<RiggerBone> *m_resultBones = nullptr;
std::map<int, RiggerVertexWeights> *m_resultWeights = nullptr;
std::vector<RigBone> *m_resultBones = nullptr;
std::map<int, RigVertexWeights> *m_resultWeights = nullptr;
std::vector<std::pair<QtMsgType, QString>> m_messages;
std::map<size_t, std::unordered_set<size_t>> m_neighborMap;
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"
SkinnedMeshCreator::SkinnedMeshCreator(const Object &object,
const std::map<int, RiggerVertexWeights> &resultWeights) :
const std::map<int, RigVertexWeights> &resultWeights) :
m_object(object),
m_resultWeights(resultWeights)
{
@ -48,7 +48,7 @@ Model *SkinnedMeshCreator::createMeshFromTransform(const std::vector<QMatrix4x4>
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;

View File

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