Dust3D项目实训八 | 基于skeletondocument的骨骼生成分析

本文详细介绍了Dust3D软件中的骨架模块,包括骨架节点(SkeletonNode)、骨架边(SkeletonEdge)的定义与功能,以及SkeletonDocumentEditMode枚举类型用于编辑操作的说明。骨架节点包含半径、旋转、剪切面等属性,边则记录了节点间的链接关系。此外,还分析了SkeletonComponent类中子对象的管理,如添加、删除、优先级调整等操作。
摘要由CSDN通过智能技术生成

2021SC@SDUSC

目录

分析概述

模块功能

骨架生成方式

skeletondocument.h类分析

SkeletonNode类

SkeletonEdge类

SkeletonDocumentEditMode类

SkeletonComponent类分析


分析概述

模块功能

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]);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值