Dust3D开源项目分析——骨骼与动画部分 | 关节树

 2021SC@SDUSC

这一部分文件主要负责构建储存骨骼层级关系的数据结构,是骨骼动画的基础。

头文件部分 jointnodetree.h 

#ifndef DUST3D_JOINT_NODE_TREE_H
#define DUST3D_JOINT_NODE_TREE_H
#include <QMatrix4x4>
#include <vector>
#include <QQuaternion>
#include "rig.h"

struct JointNode
{
    int parentIndex;
    QString name;
    QVector3D position;
    QVector3D bindTranslation;
    QVector3D translation;
    QQuaternion rotation;
    QMatrix4x4 bindMatrix;
    QMatrix4x4 inverseBindMatrix;
    std::vector<int> children;
};

class JointNodeTree
{
public:
    const std::vector<JointNode> &nodes() const;
    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);
private:
    std::vector<JointNode> m_boneNodes;
};

#endif

JointNode结构体有以下成员:

parentIndex标记当前关节父节点的标号。在反向运动学(IK)系统中,每个关节可以有多个子节点,但是只有一个父节点。position存储关节节点的位置。bindTranslation存储绑定位移。translation存储关节自身的位移值。rotation存储节点的旋转值,这一数值在实现正向运动学(FK)功能时可能会需要用到。bindMatrix和inverseBindMatrix存储绑定矩阵和它的逆,用于关机运动的计算。children存储当前节点所有的子级节点。

jointnodetree.cpp

#include <QMatrix3x3>
#include "jointnodetree.h"
#include "util.h"

const std::vector<JointNode> &JointNodeTree::nodes() const
{
    return m_boneNodes;
}

void JointNodeTree::updateRotation(int index, const QQuaternion &rotation)
{
    m_boneNodes[index].rotation = rotation;
}

void JointNodeTree::updateTranslation(int index, const QVector3D &translation)
{
    m_boneNodes[index].translation = translation;
}

void JointNodeTree::updateMatrix(int index, const QMatrix4x4 &matrix)
{
    const QMatrix4x4 &localMatrix = matrix;
    
    updateTranslation(index, 
        QVector3D(localMatrix(0, 3), localMatrix(1, 3), localMatrix(2, 3)));
    
    float scalar = std::sqrt(std::max(0.0f, 1.0f + localMatrix(0, 0) + localMatrix(1, 1) + localMatrix(2, 2))) / 2.0f;
    float x = std::sqrt(std::max(0.0f, 1.0f + localMatrix(0, 0) - localMatrix(1, 1) - localMatrix(2, 2))) / 2.0f;
    float y = std::sqrt(std::max(0.0f, 1.0f - localMatrix(0, 0) + localMatrix(1, 1) - localMatrix(2, 2))) / 2.0f;
    float z = std::sqrt(std::max(0.0f, 1.0f - localMatrix(0, 0) - localMatrix(1, 1) + localMatrix(2, 2))) / 2.0f;
    x *= x * (localMatrix(2, 1) - localMatrix(1, 2)) > 0 ? 1 : -1;
    y *= y * (localMatrix(0, 2) - localMatrix(2, 0)) > 0 ? 1 : -1;
    z *= z * (localMatrix(1, 0) - localMatrix(0, 1)) > 0 ? 1 : -1;
    float length = std::sqrt(scalar * scalar + x * x + y * y + z * z);
    updateRotation(index, 
        QQuaternion(scalar / length, x / length, y / length, z / length));
}

JointNodeTree::JointNodeTree(const std::vector<RigBone> *resultRigBones)
{
    if (nullptr == resultRigBones || resultRigBones->empty())
        return;
    
    m_boneNodes.resize(resultRigBones->size());
    
    m_boneNodes[0].parentIndex = -1;
    for (decltype(resultRigBones->size()) i = 0; i < resultRigBones->size(); i++) {
        const auto &bone = (*resultRigBones)[i];
        auto &node = m_boneNodes[i];
        node.name = bone.name;
        node.position = bone.headPosition;
        QMatrix4x4 parentMatrix;
        if (-1 == node.parentIndex) {
            node.bindTranslation = node.position;
        } else {
            const auto &parentNode = m_boneNodes[node.parentIndex];
            node.bindTranslation = node.position - parentNode.position;
            parentMatrix = parentNode.bindMatrix;
        }
        QMatrix4x4 translationMatrix;
        translationMatrix.translate(node.bindTranslation);
        node.bindMatrix = parentMatrix * translationMatrix;
        node.inverseBindMatrix = node.bindMatrix.inverted();
        node.children = bone.children;
        for (const auto &childIndex: bone.children)
            m_boneNodes[childIndex].parentIndex = i;
    }
}

方法实现部分。前部分是一些更新函数,用于运动是计算并更新关节节点的数据,最后是关节节点树的构造函数,根据绑定数据构建关节节点的层级关系(Hierarchy),并且根据上一级的matrix计算下一级的matrix。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值