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。