2021SC@SDUSC
目录
分析概述
模块功能
skeletondocument模块主要用于几何体骨骼的生成,例如在长颈鹿的模型实例中形成长颈鹿其对应的骨骼,在Dust3D中,骨骼的关节点也可以看作是二维圆中的核心圆圆心,即二维平面内具有颜色标记的圆心
骨架生成方式
骨架由一系列具有层次关系的关节(骨骼)和关节链组成,选择其中一个是根关节,其它关节是根关节的子孙,可以通过平移和旋转根关节移动并确定整个骨架在世界空间中的位置和方向。骨架是利用树状结构来存储信息的,通过层级结构存储不同的节点信息。在Dust3D中,几何体的将完整骨骼分为了不同的构件,在构件之间划分主次、链接关系,再将不同的构件继续划分为关节、节点信息,因此在进行类的构造时根据不同的层次进行了类的构造。
skeletondocument.h类分析
SkeletonNode类
SkeletonNode类定义了组成骨架的各关键节点信息,其中包括:骨骼点所生成的圆的半径、节点id以及其父节点id、节点的旋转属性、拖拽属性、以及剪切面的属性设置
class SkeletonNode
{//骨架节点
public:
SkeletonNode(const QUuid &withId=QUuid()) :
radius(0),//节点半径
boneMark(BoneMark::None),//节点标记
cutRotation(0.0),//旋转
cutFace(CutFace::Quad),
hasCutFaceSettings(false),//是否具有剪切面设置
m_x(0),
m_y(0),
m_z(0)
{
id = withId.isNull() ? QUuid::createUuid() : withId;
}
void setRadius(float toRadius)
{//设置半径在0.005f到1之间
if (toRadius < 0.005f)
toRadius = 0.005f;
else if (toRadius > 1)
toRadius = 1;
radius = toRadius;
}
void setCutRotation(float toRotation)
{//设置旋转在-1到1之间
if (toRotation < -1)
toRotation = -1;
else if (toRotation > 1)
toRotation = 1;
cutRotation = toRotation;
hasCutFaceSettings = true;
}
void setCutFace(CutFace face)
{
cutFace = face;
cutFaceLinkedId = QUuid();//为切割面生成唯一码
hasCutFaceSettings = true;
}
void setCutFaceLinkedId(const QUuid &linkedId)
{//设置切割面链接Id
if (linkedId.isNull()) {
clearCutFaceSettings();
return;
}
cutFace = CutFace::UserDefined;
cutFaceLinkedId = linkedId;
hasCutFaceSettings = true;
}
void clearCutFaceSettings()
{//清空有剪切面的各项属性
cutFace = CutFace::Quad;
cutFaceLinkedId = QUuid();
cutRotation = 0;
hasCutFaceSettings = false;
}
//返回节点x,y,z坐标值
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{(void) rotated;return m_z;}
//设置节点x\y\z坐标值
void setX(float x){m_x = x;}
void setY(float y){m_y = y;}
void setZ(float z){m_z = z;
//添加的节点的x\y\z坐标值
void addX(float x){m_x += x;}
void addY(float y){ m_y += y;}
void addZ(float z){m_z += z;}
QUuid id;//节点id
QUuid partId;//节点属于的骨架部分的id
QString name;//节点名字
float radius;//节点所在圆半径
BoneMark boneMark;
float cutRotation;
CutFace cutFace;
QUuid cutFaceLinkedId;
bool hasCutFaceSettings;
std::vector<QUuid> edgeIds;//边缘id
private:
float m_x;
float m_y;
float m_z;
};
SkeletonEdge类
骨架中的任意一边是由两点构成的,在生成骨架节点后,需要考虑各个节点之间的链接关系,将相关的节点连接成骨架边,而SkeletonEdge类存储的就是骨架的边信息
class SkeletonEdge
{//骨架边
public:
SkeletonEdge(const QUuid &withId=QUuid())
{//构造函数
id = withId.isNull() ? QUuid::createUuid() : withId;
}
QUuid id;//骨架边id
QUuid partId;//骨架边隶属的部分id
QString name;//骨架边名字
std::vector<QUuid> nodeIds;//边所含有的点的命名
QUuid neighborOf(QUuid nodeId) const
{//返回骨架边的邻居节点
if (nodeIds.size() != 2)
return QUuid();
return nodeIds[0] == nodeId ? nodeIds[1] : nodeIds[0];
}
};
SkeletonDocumentEditMode类
用于对骨架中不同部分的编辑控制,包括选择、绘制、拖拽、放缩等操作
enum class SkeletonDocumentEditMode
{
Add = 0,
Select,//选择
Paint,//绘制
Drag,//拖拽
ZoomIn,//放大
ZoomOut//缩小
};
SkeletonComponent类分析
SkeletonComponent类存储了子对象的属性特征以及控制子对象变换的函数。在类内函数中,主要功能在于子对象在树状图中优先级的设置,包括子对象的添加、删除、树状结构中优先级的上升下降等操作,在上下移操作中采用了冒泡
{//子对象节点
if (m_childrenIdSet.find(childId) != m_childrenIdSet.end())
return;
m_childrenIdSet.insert(childId);//将输入子对象的id存储至m_childrenIdSet中
childrenIds.push_back(childId);
}
void removeChild(QUuid childId)
{//子对象的移除
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);
}
void replaceChild(QUuid childId, QUuid newId)
{//子对象的替代
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;
}
void moveChildUp(QUuid childId)
{//目标子对象与上一级子对象交换(优先级上移操作)
auto it = std::find(childrenIds.begin(), childrenIds.end(), childId);
if (it == childrenIds.end()) {
qDebug() << "Child not found in list:" << childId;
return;
}
auto index = std::distance(childrenIds.begin(), it);
if (index == 0)
return;
std::swap(childrenIds[index - 1], childrenIds[index]);//目标子对象与上一级子对象交换
}
void moveChildDown(QUuid childId)
{//目标子对象与下一级子对象交换(优先级下移操作)
auto it = std::find(childrenIds.begin(), childrenIds.end(), childId);
if (it == childrenIds.end()) {
qDebug() << "Child not found in list:" << childId;
return;
}
auto index = std::distance(childrenIds.begin(), it);
if (index == (int)childrenIds.size() - 1)
return;
std::swap(childrenIds[index], childrenIds[index + 1]);
}
void moveChildToTop(QUuid childId)
{//目标子对象置于层级顶端
auto it = std::find(childrenIds.begin(), childrenIds.end(), childId);
if (it == childrenIds.end()) {
qDebug() << "Child not found in list:" << childId;
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]);
}
void moveChildToBottom(QUuid childId)
{//目标子对象置于层级底端
auto it = std::find(childrenIds.begin(), childrenIds.end(), childId);
if (it == childrenIds.end()) {
qDebug() << "Child not found in list:" << childId;
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]);
}