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
parent
a8c7f55de2
commit
6c9d908a76
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> ¶meters);
|
||||
|
@ -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;
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#include "bonemark.h"
|
||||
#include "componentlayer.h"
|
||||
|
||||
#define MAX_WEIGHT_NUM 4
|
||||
|
||||
struct ObjectNode
|
||||
{
|
||||
QUuid partId;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
#include "theme.h"
|
||||
#include "bonemark.h"
|
||||
#include "skeletonside.h"
|
||||
#include "rigger.h"
|
||||
#include "rig.h"
|
|
@ -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;
|
|
@ -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 ¤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<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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue