2021-11-18 14:58:01 +00:00
|
|
|
#ifndef DUST3D_APPLICATION_SKELETON_DOCUMENT_H_
|
|
|
|
#define DUST3D_APPLICATION_SKELETON_DOCUMENT_H_
|
|
|
|
|
2018-11-03 08:09:42 +00:00
|
|
|
#include <QObject>
|
|
|
|
#include <QString>
|
|
|
|
#include <cmath>
|
|
|
|
#include <QImage>
|
2018-11-11 13:08:13 +00:00
|
|
|
#include <QByteArray>
|
2020-12-19 05:37:44 +00:00
|
|
|
#include <QDebug>
|
2021-11-18 14:58:01 +00:00
|
|
|
#include <QVector3D>
|
|
|
|
#include <QPixmap>
|
|
|
|
#include <dust3d/base/uuid.h>
|
|
|
|
#include <dust3d/base/cut_face.h>
|
|
|
|
#include <dust3d/base/part_target.h>
|
|
|
|
#include <dust3d/base/part_base.h>
|
|
|
|
#include <dust3d/base/combine_mode.h>
|
2018-11-03 08:09:42 +00:00
|
|
|
#include "theme.h"
|
2022-09-23 15:54:49 +00:00
|
|
|
#include "model_mesh.h"
|
2021-11-18 14:58:01 +00:00
|
|
|
#include "debug.h"
|
2022-10-01 18:35:55 +00:00
|
|
|
#include "mesh_generator.h"
|
2018-11-03 08:09:42 +00:00
|
|
|
|
|
|
|
class SkeletonNode
|
|
|
|
{
|
|
|
|
public:
|
2021-11-18 14:58:01 +00:00
|
|
|
SkeletonNode(const dust3d::Uuid &withId=dust3d::Uuid()) :
|
2018-11-03 08:09:42 +00:00
|
|
|
radius(0),
|
2019-07-08 22:36:07 +00:00
|
|
|
cutRotation(0.0),
|
2021-11-18 14:58:01 +00:00
|
|
|
cutFace(dust3d::CutFace::Quad),
|
2019-10-19 13:14:36 +00:00
|
|
|
hasCutFaceSettings(false),
|
|
|
|
m_x(0),
|
|
|
|
m_y(0),
|
|
|
|
m_z(0)
|
2018-11-03 08:09:42 +00:00
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
id = withId.isNull() ? dust3d::Uuid::createUuid() : withId;
|
2018-11-03 08:09:42 +00:00
|
|
|
}
|
|
|
|
void setRadius(float toRadius)
|
|
|
|
{
|
2022-10-01 18:35:55 +00:00
|
|
|
if (toRadius < MeshGenerator::m_minimalRadius)
|
|
|
|
toRadius = MeshGenerator::m_minimalRadius;
|
2018-11-03 08:09:42 +00:00
|
|
|
else if (toRadius > 1)
|
|
|
|
toRadius = 1;
|
|
|
|
radius = toRadius;
|
|
|
|
}
|
2019-07-08 22:36:07 +00:00
|
|
|
void setCutRotation(float toRotation)
|
|
|
|
{
|
|
|
|
if (toRotation < -1)
|
|
|
|
toRotation = -1;
|
|
|
|
else if (toRotation > 1)
|
|
|
|
toRotation = 1;
|
|
|
|
cutRotation = toRotation;
|
|
|
|
hasCutFaceSettings = true;
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void setCutFace(dust3d::CutFace face)
|
2019-07-08 22:36:07 +00:00
|
|
|
{
|
|
|
|
cutFace = face;
|
2021-11-18 14:58:01 +00:00
|
|
|
cutFaceLinkedId = dust3d::Uuid();
|
2019-07-08 22:36:07 +00:00
|
|
|
hasCutFaceSettings = true;
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void setCutFaceLinkedId(const dust3d::Uuid &linkedId)
|
2019-07-08 22:36:07 +00:00
|
|
|
{
|
|
|
|
if (linkedId.isNull()) {
|
|
|
|
clearCutFaceSettings();
|
|
|
|
return;
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
cutFace = dust3d::CutFace::UserDefined;
|
2019-07-08 22:36:07 +00:00
|
|
|
cutFaceLinkedId = linkedId;
|
|
|
|
hasCutFaceSettings = true;
|
|
|
|
}
|
|
|
|
void clearCutFaceSettings()
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
cutFace = dust3d::CutFace::Quad;
|
|
|
|
cutFaceLinkedId = dust3d::Uuid();
|
2019-07-08 22:36:07 +00:00
|
|
|
cutRotation = 0;
|
|
|
|
hasCutFaceSettings = false;
|
|
|
|
}
|
2019-10-19 13:14:36 +00:00
|
|
|
float getX(bool rotated=false) const
|
|
|
|
{
|
|
|
|
if (rotated)
|
|
|
|
return m_y;
|
|
|
|
return m_x;
|
|
|
|
}
|
|
|
|
float getY(bool rotated=false) const
|
|
|
|
{
|
|
|
|
if (rotated)
|
|
|
|
return m_x;
|
|
|
|
return m_y;
|
|
|
|
}
|
|
|
|
float getZ(bool rotated=false) const
|
|
|
|
{
|
2019-12-30 15:21:59 +00:00
|
|
|
(void) rotated;
|
2019-10-19 13:14:36 +00:00
|
|
|
return m_z;
|
|
|
|
}
|
|
|
|
void setX(float x)
|
|
|
|
{
|
|
|
|
m_x = x;
|
|
|
|
}
|
|
|
|
void setY(float y)
|
|
|
|
{
|
|
|
|
m_y = y;
|
|
|
|
}
|
|
|
|
void setZ(float z)
|
|
|
|
{
|
|
|
|
m_z = z;
|
|
|
|
}
|
|
|
|
void addX(float x)
|
|
|
|
{
|
|
|
|
m_x += x;
|
|
|
|
}
|
|
|
|
void addY(float y)
|
|
|
|
{
|
|
|
|
m_y += y;
|
|
|
|
}
|
|
|
|
void addZ(float z)
|
|
|
|
{
|
|
|
|
m_z += z;
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::Uuid id;
|
|
|
|
dust3d::Uuid partId;
|
2018-11-03 08:09:42 +00:00
|
|
|
QString name;
|
|
|
|
float radius;
|
2019-07-08 22:36:07 +00:00
|
|
|
float cutRotation;
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::CutFace cutFace;
|
|
|
|
dust3d::Uuid cutFaceLinkedId;
|
2019-07-08 22:36:07 +00:00
|
|
|
bool hasCutFaceSettings;
|
2021-11-18 14:58:01 +00:00
|
|
|
std::vector<dust3d::Uuid> edgeIds;
|
2019-10-19 13:14:36 +00:00
|
|
|
private:
|
|
|
|
float m_x;
|
|
|
|
float m_y;
|
|
|
|
float m_z;
|
2018-11-03 08:09:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SkeletonEdge
|
|
|
|
{
|
|
|
|
public:
|
2021-11-18 14:58:01 +00:00
|
|
|
SkeletonEdge(const dust3d::Uuid &withId=dust3d::Uuid())
|
2018-11-03 08:09:42 +00:00
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
id = withId.isNull() ? dust3d::Uuid::createUuid() : withId;
|
2018-11-03 08:09:42 +00:00
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::Uuid id;
|
|
|
|
dust3d::Uuid partId;
|
2018-11-03 08:09:42 +00:00
|
|
|
QString name;
|
2021-11-18 14:58:01 +00:00
|
|
|
std::vector<dust3d::Uuid> nodeIds;
|
|
|
|
dust3d::Uuid neighborOf(dust3d::Uuid nodeId) const
|
2018-11-03 08:09:42 +00:00
|
|
|
{
|
|
|
|
if (nodeIds.size() != 2)
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::Uuid();
|
2018-11-03 08:09:42 +00:00
|
|
|
return nodeIds[0] == nodeId ? nodeIds[1] : nodeIds[0];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class SkeletonPart
|
|
|
|
{
|
|
|
|
public:
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::Uuid id;
|
2018-11-03 08:09:42 +00:00
|
|
|
QString name;
|
|
|
|
bool visible;
|
|
|
|
bool locked;
|
|
|
|
bool subdived;
|
|
|
|
bool disabled;
|
|
|
|
bool xMirrored;
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::PartBase base;
|
2018-11-03 08:09:42 +00:00
|
|
|
float deformThickness;
|
|
|
|
float deformWidth;
|
2020-10-18 10:31:40 +00:00
|
|
|
bool deformUnified;
|
2018-11-03 08:09:42 +00:00
|
|
|
bool rounded;
|
2019-03-09 22:10:39 +00:00
|
|
|
bool chamfered;
|
2018-11-03 08:09:42 +00:00
|
|
|
QColor color;
|
|
|
|
bool hasColor;
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::Uuid componentId;
|
|
|
|
std::vector<dust3d::Uuid> nodeIds;
|
2018-11-03 08:09:42 +00:00
|
|
|
bool dirty;
|
2019-02-21 22:48:15 +00:00
|
|
|
float cutRotation;
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::CutFace cutFace;
|
|
|
|
dust3d::Uuid cutFaceLinkedId;
|
|
|
|
dust3d::Uuid materialId;
|
|
|
|
dust3d::PartTarget target;
|
2019-05-25 23:24:24 +00:00
|
|
|
float colorSolubility;
|
2020-10-18 05:03:15 +00:00
|
|
|
float metalness;
|
|
|
|
float roughness;
|
2019-08-18 12:02:39 +00:00
|
|
|
float hollowThickness;
|
2019-11-07 12:08:47 +00:00
|
|
|
bool countershaded;
|
2020-10-20 11:18:08 +00:00
|
|
|
bool smooth;
|
2021-11-18 14:58:01 +00:00
|
|
|
SkeletonPart(const dust3d::Uuid &withId=dust3d::Uuid()) :
|
2018-11-03 08:09:42 +00:00
|
|
|
visible(true),
|
|
|
|
locked(false),
|
|
|
|
subdived(false),
|
|
|
|
disabled(false),
|
|
|
|
xMirrored(false),
|
2021-11-18 14:58:01 +00:00
|
|
|
base(dust3d::PartBase::Average),
|
2018-11-03 08:09:42 +00:00
|
|
|
deformThickness(1.0),
|
|
|
|
deformWidth(1.0),
|
2020-10-18 10:31:40 +00:00
|
|
|
deformUnified(false),
|
2018-11-03 08:09:42 +00:00
|
|
|
rounded(false),
|
2019-03-09 22:10:39 +00:00
|
|
|
chamfered(false),
|
2021-11-18 14:58:01 +00:00
|
|
|
color(Qt::white),
|
2018-11-03 08:09:42 +00:00
|
|
|
hasColor(false),
|
|
|
|
dirty(true),
|
2019-02-24 13:42:23 +00:00
|
|
|
cutRotation(0.0),
|
2021-11-18 14:58:01 +00:00
|
|
|
cutFace(dust3d::CutFace::Quad),
|
|
|
|
target(dust3d::PartTarget::Model),
|
2019-08-03 10:21:27 +00:00
|
|
|
colorSolubility(0.0),
|
2020-10-18 05:03:15 +00:00
|
|
|
metalness(0.0),
|
|
|
|
roughness(1.0),
|
2019-11-07 12:08:47 +00:00
|
|
|
hollowThickness(0.0),
|
2019-12-14 13:28:14 +00:00
|
|
|
countershaded(false),
|
2021-11-18 14:58:01 +00:00
|
|
|
smooth(false)
|
2018-11-03 08:09:42 +00:00
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
id = withId.isNull() ? dust3d::Uuid::createUuid() : withId;
|
2018-11-03 08:09:42 +00:00
|
|
|
}
|
2020-04-08 23:25:37 +00:00
|
|
|
bool hasPolyFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
2020-12-15 14:28:20 +00:00
|
|
|
bool hasSmoothFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-12-15 14:28:20 +00:00
|
|
|
}
|
2020-04-08 23:25:37 +00:00
|
|
|
bool hasSubdivFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasRoundEndFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasMirrorFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasChamferFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasRotationFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasHollowFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasCutFaceFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasLayerFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasTargetFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return true;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasBaseFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasCombineModeFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasDeformFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
|
|
|
bool hasColorFunction() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return dust3d::PartTarget::Model == target;
|
2020-04-08 23:25:37 +00:00
|
|
|
}
|
2018-11-03 08:09:42 +00:00
|
|
|
void setDeformThickness(float toThickness)
|
|
|
|
{
|
|
|
|
if (toThickness < 0)
|
|
|
|
toThickness = 0;
|
|
|
|
else if (toThickness > 2)
|
|
|
|
toThickness = 2;
|
|
|
|
deformThickness = toThickness;
|
|
|
|
}
|
|
|
|
void setDeformWidth(float toWidth)
|
|
|
|
{
|
|
|
|
if (toWidth < 0)
|
|
|
|
toWidth = 0;
|
|
|
|
else if (toWidth > 2)
|
|
|
|
toWidth = 2;
|
|
|
|
deformWidth = toWidth;
|
|
|
|
}
|
2019-02-21 22:48:15 +00:00
|
|
|
void setCutRotation(float toRotation)
|
|
|
|
{
|
|
|
|
if (toRotation < -1)
|
|
|
|
toRotation = -1;
|
|
|
|
else if (toRotation > 1)
|
|
|
|
toRotation = 1;
|
|
|
|
cutRotation = toRotation;
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void setCutFace(dust3d::CutFace face)
|
2019-02-24 13:42:23 +00:00
|
|
|
{
|
2019-05-05 12:49:26 +00:00
|
|
|
cutFace = face;
|
2021-11-18 14:58:01 +00:00
|
|
|
cutFaceLinkedId = dust3d::Uuid();
|
2019-05-19 03:21:38 +00:00
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void setCutFaceLinkedId(const dust3d::Uuid &linkedId)
|
2019-05-19 03:21:38 +00:00
|
|
|
{
|
|
|
|
if (linkedId.isNull()) {
|
2021-11-18 14:58:01 +00:00
|
|
|
setCutFace(dust3d::CutFace::Quad);
|
2019-05-19 03:21:38 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
cutFace = dust3d::CutFace::UserDefined;
|
2019-05-19 03:21:38 +00:00
|
|
|
cutFaceLinkedId = linkedId;
|
2019-02-24 13:42:23 +00:00
|
|
|
}
|
2018-11-03 08:09:42 +00:00
|
|
|
bool deformThicknessAdjusted() const
|
|
|
|
{
|
|
|
|
return fabs(deformThickness - 1.0) >= 0.01;
|
|
|
|
}
|
|
|
|
bool deformWidthAdjusted() const
|
|
|
|
{
|
|
|
|
return fabs(deformWidth - 1.0) >= 0.01;
|
|
|
|
}
|
|
|
|
bool deformAdjusted() const
|
|
|
|
{
|
2020-10-18 10:31:40 +00:00
|
|
|
return deformThicknessAdjusted() || deformWidthAdjusted() || deformUnified;
|
2018-11-03 08:09:42 +00:00
|
|
|
}
|
2019-05-25 23:24:24 +00:00
|
|
|
bool colorSolubilityAdjusted() const
|
|
|
|
{
|
|
|
|
return fabs(colorSolubility - 0.0) >= 0.01;
|
|
|
|
}
|
2020-10-18 05:03:15 +00:00
|
|
|
bool metalnessAdjusted() const
|
|
|
|
{
|
|
|
|
return fabs(metalness - 0.0) >= 0.01;
|
|
|
|
}
|
|
|
|
bool roughnessAdjusted() const
|
|
|
|
{
|
|
|
|
return fabs(roughness - 1.0) >= 0.01;
|
|
|
|
}
|
2019-02-21 22:48:15 +00:00
|
|
|
bool cutRotationAdjusted() const
|
|
|
|
{
|
|
|
|
return fabs(cutRotation - 0.0) >= 0.01;
|
|
|
|
}
|
2019-08-18 12:02:39 +00:00
|
|
|
bool hollowThicknessAdjusted() const
|
|
|
|
{
|
|
|
|
return fabs(hollowThickness - 0.0) >= 0.01;
|
|
|
|
}
|
2019-05-05 12:49:26 +00:00
|
|
|
bool cutFaceAdjusted() const
|
2019-02-24 13:42:23 +00:00
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return cutFace != dust3d::CutFace::Quad;
|
2019-05-05 12:49:26 +00:00
|
|
|
}
|
|
|
|
bool cutAdjusted() const
|
|
|
|
{
|
2019-08-18 13:18:55 +00:00
|
|
|
return cutRotationAdjusted() || cutFaceAdjusted() || hollowThicknessAdjusted();
|
2019-02-24 13:42:23 +00:00
|
|
|
}
|
2018-11-03 08:09:42 +00:00
|
|
|
bool materialAdjusted() const
|
|
|
|
{
|
|
|
|
return !materialId.isNull();
|
|
|
|
}
|
|
|
|
bool isEditVisible() const
|
|
|
|
{
|
|
|
|
return visible && !disabled;
|
|
|
|
}
|
|
|
|
void copyAttributes(const SkeletonPart &other)
|
|
|
|
{
|
|
|
|
visible = other.visible;
|
|
|
|
locked = other.locked;
|
|
|
|
subdived = other.subdived;
|
|
|
|
disabled = other.disabled;
|
|
|
|
xMirrored = other.xMirrored;
|
2019-05-20 13:38:01 +00:00
|
|
|
base = other.base;
|
2018-11-03 08:09:42 +00:00
|
|
|
deformThickness = other.deformThickness;
|
|
|
|
deformWidth = other.deformWidth;
|
|
|
|
rounded = other.rounded;
|
2019-03-09 22:10:39 +00:00
|
|
|
chamfered = other.chamfered;
|
2018-11-03 08:09:42 +00:00
|
|
|
color = other.color;
|
|
|
|
hasColor = other.hasColor;
|
2019-02-21 22:48:15 +00:00
|
|
|
cutRotation = other.cutRotation;
|
2019-05-05 12:49:26 +00:00
|
|
|
cutFace = other.cutFace;
|
2019-05-19 03:21:38 +00:00
|
|
|
cutFaceLinkedId = other.cutFaceLinkedId;
|
2018-11-03 08:09:42 +00:00
|
|
|
componentId = other.componentId;
|
|
|
|
dirty = other.dirty;
|
|
|
|
materialId = other.materialId;
|
2019-05-19 03:21:38 +00:00
|
|
|
target = other.target;
|
2019-05-25 23:24:24 +00:00
|
|
|
colorSolubility = other.colorSolubility;
|
2019-11-07 12:08:47 +00:00
|
|
|
countershaded = other.countershaded;
|
2020-10-20 11:18:08 +00:00
|
|
|
metalness = other.metalness;
|
|
|
|
roughness = other.roughness;
|
|
|
|
deformUnified = other.deformUnified;
|
|
|
|
smooth = other.smooth;
|
2020-10-20 21:42:29 +00:00
|
|
|
hollowThickness = other.hollowThickness;
|
2018-11-03 08:09:42 +00:00
|
|
|
}
|
|
|
|
private:
|
|
|
|
Q_DISABLE_COPY(SkeletonPart);
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class SkeletonDocumentEditMode
|
|
|
|
{
|
|
|
|
Add = 0,
|
|
|
|
Select,
|
2019-08-08 11:24:33 +00:00
|
|
|
Paint,
|
2018-11-03 08:09:42 +00:00
|
|
|
Drag,
|
|
|
|
ZoomIn,
|
|
|
|
ZoomOut
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class SkeletonProfile
|
|
|
|
{
|
|
|
|
Unknown = 0,
|
|
|
|
Main,
|
|
|
|
Side
|
|
|
|
};
|
|
|
|
|
2020-12-19 05:37:44 +00:00
|
|
|
class SkeletonComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SkeletonComponent()
|
|
|
|
{
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
SkeletonComponent(const dust3d::Uuid &withId, const QString &linkData=QString(), const QString &linkDataType=QString())
|
2020-12-19 05:37:44 +00:00
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
id = withId.isNull() ? dust3d::Uuid::createUuid() : withId;
|
2020-12-19 05:37:44 +00:00
|
|
|
if (!linkData.isEmpty()) {
|
|
|
|
if ("partId" == linkDataType) {
|
2021-11-18 14:58:01 +00:00
|
|
|
linkToPartId = dust3d::Uuid(linkData.toUtf8().constData());
|
2020-12-19 05:37:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::Uuid id;
|
2020-12-19 05:37:44 +00:00
|
|
|
QString name;
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::Uuid linkToPartId;
|
|
|
|
dust3d::Uuid parentId;
|
2020-12-19 05:37:44 +00:00
|
|
|
bool expanded = true;
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::CombineMode combineMode = dust3d::CombineMode::Normal;
|
2020-12-19 05:37:44 +00:00
|
|
|
bool dirty = true;
|
2021-11-18 14:58:01 +00:00
|
|
|
std::vector<dust3d::Uuid> childrenIds;
|
2022-10-02 19:45:46 +00:00
|
|
|
bool isPreviewMeshObsolete = false;
|
2022-10-05 11:34:56 +00:00
|
|
|
std::unique_ptr<QImage> previewImage;
|
|
|
|
bool isPreviewImageDecorationObsolete = false;
|
2022-10-02 19:45:46 +00:00
|
|
|
QPixmap previewPixmap;
|
2020-12-19 05:37:44 +00:00
|
|
|
QString linkData() const
|
|
|
|
{
|
2021-11-18 14:58:01 +00:00
|
|
|
return linkToPartId.isNull() ? QString() : QString(linkToPartId.toString().c_str());
|
2020-12-19 05:37:44 +00:00
|
|
|
}
|
|
|
|
QString linkDataType() const
|
|
|
|
{
|
|
|
|
return linkToPartId.isNull() ? QString() : QString("partId");
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void addChild(dust3d::Uuid childId)
|
2020-12-19 05:37:44 +00:00
|
|
|
{
|
|
|
|
if (m_childrenIdSet.find(childId) != m_childrenIdSet.end())
|
|
|
|
return;
|
|
|
|
m_childrenIdSet.insert(childId);
|
|
|
|
childrenIds.push_back(childId);
|
|
|
|
}
|
2022-10-10 11:05:16 +00:00
|
|
|
void replaceChildWithOthers(const dust3d::Uuid &childId, const std::vector<dust3d::Uuid> &others)
|
|
|
|
{
|
|
|
|
if (m_childrenIdSet.find(childId) == m_childrenIdSet.end())
|
|
|
|
return;
|
|
|
|
m_childrenIdSet.erase(childId);
|
|
|
|
std::vector<dust3d::Uuid> candidates;
|
|
|
|
for (const auto &it: others) {
|
|
|
|
if (m_childrenIdSet.find(it) == m_childrenIdSet.end()) {
|
|
|
|
m_childrenIdSet.insert(it);
|
|
|
|
candidates.emplace_back(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < childrenIds.size(); ++i) {
|
|
|
|
if (childId == childrenIds[i]) {
|
|
|
|
size_t newAddSize = candidates.size() - 1;
|
|
|
|
if (newAddSize > 0) {
|
|
|
|
size_t oldSize = childrenIds.size();
|
|
|
|
childrenIds.resize(childrenIds.size() + newAddSize);
|
|
|
|
for (int j = (int)oldSize - 1; j > (int)i; --j) {
|
|
|
|
childrenIds[j + newAddSize] = childrenIds[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (size_t k = 0; k < candidates.size(); ++k)
|
|
|
|
childrenIds[i + k] = candidates[k];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void removeChild(dust3d::Uuid childId)
|
2020-12-19 05:37:44 +00:00
|
|
|
{
|
|
|
|
if (m_childrenIdSet.find(childId) == m_childrenIdSet.end())
|
|
|
|
return;
|
|
|
|
m_childrenIdSet.erase(childId);
|
|
|
|
auto findResult = std::find(childrenIds.begin(), childrenIds.end(), childId);
|
|
|
|
if (findResult != childrenIds.end())
|
|
|
|
childrenIds.erase(findResult);
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void replaceChild(dust3d::Uuid childId, dust3d::Uuid newId)
|
2020-12-19 05:37:44 +00:00
|
|
|
{
|
|
|
|
if (m_childrenIdSet.find(childId) == m_childrenIdSet.end())
|
|
|
|
return;
|
|
|
|
if (m_childrenIdSet.find(newId) != m_childrenIdSet.end())
|
|
|
|
return;
|
|
|
|
m_childrenIdSet.erase(childId);
|
|
|
|
m_childrenIdSet.insert(newId);
|
|
|
|
auto findResult = std::find(childrenIds.begin(), childrenIds.end(), childId);
|
|
|
|
if (findResult != childrenIds.end())
|
|
|
|
*findResult = newId;
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void moveChildUp(dust3d::Uuid childId)
|
2020-12-19 05:37:44 +00:00
|
|
|
{
|
|
|
|
auto it = std::find(childrenIds.begin(), childrenIds.end(), childId);
|
|
|
|
if (it == childrenIds.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto index = std::distance(childrenIds.begin(), it);
|
|
|
|
if (index == 0)
|
|
|
|
return;
|
|
|
|
std::swap(childrenIds[index - 1], childrenIds[index]);
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void moveChildDown(dust3d::Uuid childId)
|
2020-12-19 05:37:44 +00:00
|
|
|
{
|
|
|
|
auto it = std::find(childrenIds.begin(), childrenIds.end(), childId);
|
|
|
|
if (it == childrenIds.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto index = std::distance(childrenIds.begin(), it);
|
|
|
|
if (index == (int)childrenIds.size() - 1)
|
|
|
|
return;
|
|
|
|
std::swap(childrenIds[index], childrenIds[index + 1]);
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void moveChildToTop(dust3d::Uuid childId)
|
2020-12-19 05:37:44 +00:00
|
|
|
{
|
|
|
|
auto it = std::find(childrenIds.begin(), childrenIds.end(), childId);
|
|
|
|
if (it == childrenIds.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto index = std::distance(childrenIds.begin(), it);
|
|
|
|
if (index == 0)
|
|
|
|
return;
|
|
|
|
for (int i = index; i >= 1; i--)
|
|
|
|
std::swap(childrenIds[i - 1], childrenIds[i]);
|
|
|
|
}
|
2021-11-18 14:58:01 +00:00
|
|
|
void moveChildToBottom(dust3d::Uuid childId)
|
2020-12-19 05:37:44 +00:00
|
|
|
{
|
|
|
|
auto it = std::find(childrenIds.begin(), childrenIds.end(), childId);
|
|
|
|
if (it == childrenIds.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto index = std::distance(childrenIds.begin(), it);
|
|
|
|
if (index == (int)childrenIds.size() - 1)
|
|
|
|
return;
|
|
|
|
for (int i = index; i <= (int)childrenIds.size() - 2; i++)
|
|
|
|
std::swap(childrenIds[i], childrenIds[i + 1]);
|
|
|
|
}
|
2022-10-02 19:45:46 +00:00
|
|
|
void updatePreviewMesh(std::unique_ptr<ModelMesh> mesh)
|
|
|
|
{
|
|
|
|
m_previewMesh = std::move(mesh);
|
|
|
|
isPreviewMeshObsolete = true;
|
|
|
|
}
|
|
|
|
ModelMesh *takePreviewMesh() const
|
|
|
|
{
|
|
|
|
if (nullptr == m_previewMesh)
|
|
|
|
return nullptr;
|
|
|
|
return new ModelMesh(*m_previewMesh);
|
|
|
|
}
|
2020-12-19 05:37:44 +00:00
|
|
|
private:
|
2022-10-02 19:45:46 +00:00
|
|
|
std::unique_ptr<ModelMesh> m_previewMesh;
|
2021-11-18 14:58:01 +00:00
|
|
|
std::set<dust3d::Uuid> m_childrenIdSet;
|
2020-12-19 05:37:44 +00:00
|
|
|
};
|
|
|
|
|
2018-11-03 08:09:42 +00:00
|
|
|
class SkeletonDocument : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
2020-12-19 05:37:44 +00:00
|
|
|
signals:
|
2021-11-18 14:58:01 +00:00
|
|
|
void partAdded(dust3d::Uuid partId);
|
|
|
|
void nodeAdded(dust3d::Uuid nodeId);
|
|
|
|
void edgeAdded(dust3d::Uuid edgeId);
|
|
|
|
void partRemoved(dust3d::Uuid partId);
|
|
|
|
void partLockStateChanged(dust3d::Uuid partId);
|
|
|
|
void partVisibleStateChanged(dust3d::Uuid partId);
|
|
|
|
void partDisableStateChanged(dust3d::Uuid partId);
|
|
|
|
void componentNameChanged(dust3d::Uuid componentId);
|
|
|
|
void componentChildrenChanged(dust3d::Uuid componentId);
|
|
|
|
void componentRemoved(dust3d::Uuid componentId);
|
|
|
|
void componentAdded(dust3d::Uuid componentId);
|
|
|
|
void componentExpandStateChanged(dust3d::Uuid componentId);
|
2022-10-02 19:45:46 +00:00
|
|
|
void componentPreviewMeshChanged(const dust3d::Uuid &componentId);
|
2022-10-05 11:34:56 +00:00
|
|
|
void componentPreviewPixmapChanged(const dust3d::Uuid &componentId);
|
2021-11-18 14:58:01 +00:00
|
|
|
void nodeRemoved(dust3d::Uuid nodeId);
|
|
|
|
void edgeRemoved(dust3d::Uuid edgeId);
|
|
|
|
void nodeRadiusChanged(dust3d::Uuid nodeId);
|
|
|
|
void nodeOriginChanged(dust3d::Uuid nodeId);
|
|
|
|
void edgeReversed(dust3d::Uuid edgeId);
|
2020-12-19 05:37:44 +00:00
|
|
|
void originChanged();
|
|
|
|
void skeletonChanged();
|
|
|
|
void optionsChanged();
|
2020-12-19 08:12:50 +00:00
|
|
|
void xlockStateChanged();
|
|
|
|
void ylockStateChanged();
|
|
|
|
void zlockStateChanged();
|
|
|
|
void radiusLockStateChanged();
|
2018-11-03 08:09:42 +00:00
|
|
|
public:
|
|
|
|
SkeletonDocumentEditMode editMode = SkeletonDocumentEditMode::Select;
|
|
|
|
bool xlocked = false;
|
|
|
|
bool ylocked = false;
|
|
|
|
bool zlocked = false;
|
|
|
|
bool radiusLocked = false;
|
|
|
|
QImage turnaround;
|
2018-11-11 13:08:13 +00:00
|
|
|
QByteArray turnaroundPngByteArray;
|
2021-11-18 14:58:01 +00:00
|
|
|
std::map<dust3d::Uuid, SkeletonPart> partMap;
|
|
|
|
std::map<dust3d::Uuid, SkeletonNode> nodeMap;
|
|
|
|
std::map<dust3d::Uuid, SkeletonEdge> edgeMap;
|
|
|
|
std::map<dust3d::Uuid, SkeletonComponent> componentMap;
|
2020-12-19 05:37:44 +00:00
|
|
|
SkeletonComponent rootComponent;
|
2018-11-03 08:09:42 +00:00
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
const SkeletonNode *findNode(dust3d::Uuid nodeId) const;
|
|
|
|
const SkeletonEdge *findEdge(dust3d::Uuid edgeId) const;
|
|
|
|
const SkeletonPart *findPart(dust3d::Uuid partId) const;
|
|
|
|
const SkeletonEdge *findEdgeByNodes(dust3d::Uuid firstNodeId, dust3d::Uuid secondNodeId) const;
|
|
|
|
void findAllNeighbors(dust3d::Uuid nodeId, std::set<dust3d::Uuid> &neighbors) const;
|
|
|
|
bool isNodeConnectable(dust3d::Uuid nodeId) const;
|
|
|
|
const SkeletonComponent *findComponent(dust3d::Uuid componentId) const;
|
|
|
|
const SkeletonComponent *findComponentParent(dust3d::Uuid componentId) const;
|
|
|
|
dust3d::Uuid findComponentParentId(dust3d::Uuid componentId) const;
|
|
|
|
void collectComponentDescendantParts(dust3d::Uuid componentId, std::vector<dust3d::Uuid> &partIds) const;
|
|
|
|
void collectComponentDescendantComponents(dust3d::Uuid componentId, std::vector<dust3d::Uuid> &componentIds) const;
|
2022-10-02 19:45:46 +00:00
|
|
|
void setComponentPreviewMesh(const dust3d::Uuid &componentId, std::unique_ptr<ModelMesh> mesh);
|
2022-10-05 11:34:56 +00:00
|
|
|
void setComponentPreviewImage(const dust3d::Uuid &componentId, std::unique_ptr<QImage> image);
|
2020-12-19 05:37:44 +00:00
|
|
|
void resetDirtyFlags();
|
|
|
|
void markAllDirty();
|
2018-11-05 15:47:21 +00:00
|
|
|
|
2018-11-03 08:09:42 +00:00
|
|
|
virtual bool undoable() const = 0;
|
|
|
|
virtual bool redoable() const = 0;
|
|
|
|
virtual bool hasPastableNodesInClipboard() const = 0;
|
|
|
|
virtual bool originSettled() const = 0;
|
2021-11-18 14:58:01 +00:00
|
|
|
virtual bool isNodeEditable(dust3d::Uuid nodeId) const = 0;
|
|
|
|
virtual bool isEdgeEditable(dust3d::Uuid edgeId) const = 0;
|
|
|
|
virtual bool isNodeDeactivated(dust3d::Uuid nodeId) const
|
2019-03-03 00:05:13 +00:00
|
|
|
{
|
2019-12-30 15:21:59 +00:00
|
|
|
(void) nodeId;
|
2019-03-03 00:05:13 +00:00
|
|
|
return false;
|
|
|
|
};
|
2021-11-18 14:58:01 +00:00
|
|
|
virtual bool isEdgeDeactivated(dust3d::Uuid edgeId) const
|
2019-03-03 00:05:13 +00:00
|
|
|
{
|
2019-12-30 15:21:59 +00:00
|
|
|
(void) edgeId;
|
2019-03-03 00:05:13 +00:00
|
|
|
return false;
|
|
|
|
};
|
2021-11-18 14:58:01 +00:00
|
|
|
virtual void copyNodes(std::set<dust3d::Uuid> nodeIdSet) const = 0;
|
2018-11-03 08:09:42 +00:00
|
|
|
|
2019-10-19 13:14:36 +00:00
|
|
|
float getOriginX(bool rotated=false) const
|
|
|
|
{
|
|
|
|
if (rotated)
|
|
|
|
return m_originY;
|
|
|
|
return m_originX;
|
|
|
|
}
|
|
|
|
float getOriginY(bool rotated=false) const
|
|
|
|
{
|
|
|
|
if (rotated)
|
|
|
|
return m_originX;
|
|
|
|
return m_originY;
|
|
|
|
}
|
|
|
|
float getOriginZ(bool rotated=false) const
|
|
|
|
{
|
2019-12-30 15:21:59 +00:00
|
|
|
(void) rotated;
|
2019-10-19 13:14:36 +00:00
|
|
|
return m_originZ;
|
|
|
|
}
|
|
|
|
void setOriginX(float originX)
|
|
|
|
{
|
|
|
|
m_originX = originX;
|
|
|
|
}
|
|
|
|
void setOriginY(float originY)
|
|
|
|
{
|
|
|
|
m_originY = originY;
|
|
|
|
}
|
|
|
|
void setOriginZ(float originZ)
|
|
|
|
{
|
|
|
|
m_originZ = originZ;
|
|
|
|
}
|
|
|
|
void addOriginX(float originX)
|
|
|
|
{
|
|
|
|
m_originX += originX;
|
|
|
|
}
|
|
|
|
void addOriginY(float originY)
|
|
|
|
{
|
|
|
|
m_originY += originY;
|
|
|
|
}
|
|
|
|
void addOriginZ(float originZ)
|
|
|
|
{
|
|
|
|
m_originZ += originZ;
|
|
|
|
}
|
|
|
|
|
2018-11-03 08:09:42 +00:00
|
|
|
public slots:
|
|
|
|
virtual void undo() = 0;
|
|
|
|
virtual void redo() = 0;
|
|
|
|
virtual void paste() = 0;
|
2020-12-19 05:37:44 +00:00
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
void removeNode(dust3d::Uuid nodeId);
|
|
|
|
void removeEdge(dust3d::Uuid edgeId);
|
|
|
|
void removePart(dust3d::Uuid partId);
|
|
|
|
void addNodeWithId(dust3d::Uuid nodeId, float x, float y, float z, float radius, dust3d::Uuid fromNodeId);
|
|
|
|
void addNode(float x, float y, float z, float radius, dust3d::Uuid fromNodeId);
|
|
|
|
void scaleNodeByAddRadius(dust3d::Uuid nodeId, float amount);
|
|
|
|
void moveNodeBy(dust3d::Uuid nodeId, float x, float y, float z);
|
|
|
|
void setNodeOrigin(dust3d::Uuid nodeId, float x, float y, float z);
|
|
|
|
void setNodeRadius(dust3d::Uuid nodeId, float radius);
|
|
|
|
void switchNodeXZ(dust3d::Uuid nodeId);
|
2020-12-19 05:37:44 +00:00
|
|
|
void moveOriginBy(float x, float y, float z);
|
2021-11-18 14:58:01 +00:00
|
|
|
void addEdge(dust3d::Uuid fromNodeId, dust3d::Uuid toNodeId);
|
|
|
|
void moveComponentUp(dust3d::Uuid componentId);
|
|
|
|
void moveComponentDown(dust3d::Uuid componentId);
|
|
|
|
void moveComponentToTop(dust3d::Uuid componentId);
|
|
|
|
void moveComponentToBottom(dust3d::Uuid componentId);
|
|
|
|
void renameComponent(dust3d::Uuid componentId, QString name);
|
|
|
|
void removeComponent(dust3d::Uuid componentId);
|
|
|
|
void addComponent(dust3d::Uuid parentId);
|
|
|
|
void moveComponent(dust3d::Uuid componentId, dust3d::Uuid toParentId);
|
|
|
|
void setCurrentCanvasComponentId(dust3d::Uuid componentId);
|
2022-10-10 11:05:16 +00:00
|
|
|
void groupComponents(const std::vector<dust3d::Uuid> &componentIds);
|
|
|
|
void ungroupComponent(const dust3d::Uuid &componentId);
|
2021-11-18 14:58:01 +00:00
|
|
|
void createNewChildComponent(dust3d::Uuid parentComponentId);
|
|
|
|
void setComponentExpandState(dust3d::Uuid componentId, bool expanded);
|
|
|
|
void hideOtherComponents(dust3d::Uuid componentId);
|
|
|
|
void lockOtherComponents(dust3d::Uuid componentId);
|
2020-12-19 05:37:44 +00:00
|
|
|
void hideAllComponents();
|
|
|
|
void showAllComponents();
|
|
|
|
void showOrHideAllComponents();
|
|
|
|
void collapseAllComponents();
|
|
|
|
void expandAllComponents();
|
|
|
|
void lockAllComponents();
|
|
|
|
void unlockAllComponents();
|
2021-11-18 14:58:01 +00:00
|
|
|
void hideDescendantComponents(dust3d::Uuid componentId);
|
|
|
|
void showDescendantComponents(dust3d::Uuid componentId);
|
|
|
|
void lockDescendantComponents(dust3d::Uuid componentId);
|
|
|
|
void unlockDescendantComponents(dust3d::Uuid componentId);
|
2022-10-05 11:34:56 +00:00
|
|
|
void setComponentPreviewPixmap(const dust3d::Uuid &componentId, const QPixmap &pixmap);
|
2021-11-18 14:58:01 +00:00
|
|
|
void setPartLockState(dust3d::Uuid partId, bool locked);
|
|
|
|
void setPartVisibleState(dust3d::Uuid partId, bool visible);
|
|
|
|
void setPartDisableState(dust3d::Uuid partId, bool disabled);
|
2020-12-19 05:37:44 +00:00
|
|
|
void enableAllPositionRelatedLocks();
|
|
|
|
void disableAllPositionRelatedLocks();
|
2021-11-18 14:58:01 +00:00
|
|
|
bool isPartReadonly(dust3d::Uuid partId) const;
|
|
|
|
void breakEdge(dust3d::Uuid edgeId);
|
|
|
|
void reduceNode(dust3d::Uuid nodeId);
|
|
|
|
void reverseEdge(dust3d::Uuid edgeId);
|
2020-12-19 08:12:50 +00:00
|
|
|
void setXlockState(bool locked);
|
|
|
|
void setYlockState(bool locked);
|
|
|
|
void setZlockState(bool locked);
|
|
|
|
void setRadiusLockState(bool locked);
|
2019-10-19 13:14:36 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
float m_originX = 0;
|
|
|
|
float m_originY = 0;
|
|
|
|
float m_originZ = 0;
|
2020-12-19 05:37:44 +00:00
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
dust3d::Uuid m_currentCanvasComponentId;
|
2020-12-19 05:37:44 +00:00
|
|
|
bool m_allPositionRelatedLocksEnabled = true;
|
|
|
|
|
2021-11-18 14:58:01 +00:00
|
|
|
void splitPartByNode(std::vector<std::vector<dust3d::Uuid>> *groups, dust3d::Uuid nodeId);
|
|
|
|
void joinNodeAndNeiborsToGroup(std::vector<dust3d::Uuid> *group, dust3d::Uuid nodeId, std::set<dust3d::Uuid> *visitMap, dust3d::Uuid noUseEdgeId=dust3d::Uuid());
|
|
|
|
void splitPartByEdge(std::vector<std::vector<dust3d::Uuid>> *groups, dust3d::Uuid edgeId);
|
|
|
|
void removePartDontCareComponent(dust3d::Uuid partId);
|
|
|
|
void addPartToComponent(dust3d::Uuid partId, dust3d::Uuid componentId);
|
|
|
|
bool isDescendantComponent(dust3d::Uuid componentId, dust3d::Uuid suspiciousId);
|
|
|
|
void removeComponentRecursively(dust3d::Uuid componentId);
|
|
|
|
void updateLinkedPart(dust3d::Uuid oldPartId, dust3d::Uuid newPartId);
|
|
|
|
dust3d::Uuid createNode(dust3d::Uuid nodeId, float x, float y, float z, float radius, dust3d::Uuid fromNodeId);
|
2018-11-03 08:09:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|