Modified how SHAnimatorComponent computes the bone matrices

This commit is contained in:
Kah Wei 2023-01-18 19:16:45 +08:00
parent 35b7ac0178
commit b9fcdc43d4
7 changed files with 99 additions and 61 deletions

View File

@ -83,10 +83,21 @@ namespace SHADE
if (currClip == newClip) if (currClip == newClip)
return; return;
// Set parameters
currClip = newClip; currClip = newClip;
secsPerTick = 1.0f / currClip->GetTicksPerSecond(); secsPerTick = 1.0f / currClip->GetTicksPerSecond();
if (rig) // Build channel map
channelMap.clear();
if (currClip)
{
for (const auto& channel : currClip->GetChannels())
{
channelMap.emplace(channel.Name, &channel);
}
}
if (rig && currClip)
{ {
updatePoseWithClip(0.0f); updatePoseWithClip(0.0f);
} }
@ -123,36 +134,45 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
void SHAnimatorComponent::updatePoseWithClip(float poseTime) void SHAnimatorComponent::updatePoseWithClip(float poseTime)
{ {
for (const auto& channel : currClip->GetChannels())
{
// Get the bone
std::queue<Handle<SHRig::Node>> bones;
bones.push(rig->GetNode(channel.Name));
while (!bones.empty())
{
// Select bone at front of the queue
auto bone = bones.front(); bones.pop();
if (!bone)
continue;
// Add any children to the queue
for (auto child : bone->Children)
{
bones.push(child);
}
// Get closest frame index // Get closest frame index
const int CLOSEST_FRAME_IDX = static_cast<int>(std::floorf(poseTime * currClip->GetTicksPerSecond())); const int CLOSEST_FRAME_IDX = static_cast<int>(std::floorf(poseTime * currClip->GetTicksPerSecond()));
updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, rig->GetRootNode(), SHMatrix::Identity);
}
// Calculate the matrix from interpolated values void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRigNode> node, const SHMatrix& parentMatrix)
const int BONE_MTX_IDX = rig->GetNodeIndex(bone); {
boneMatrices[BONE_MTX_IDX] = boneMatrices[BONE_MTX_IDX] * SHMatrix::Transform // Check if there is a channel for this node
const std::string& BONE_NAME = rig->GetName(node);
SHMatrix transformMatrix = SHMatrix::Identity;
if (channelMap.contains(BONE_NAME))
{
const auto CHANNEL = channelMap[BONE_NAME];
transformMatrix = SHMatrix::Transform
( (
getInterpolatedValue(channel.PositionKeyFrames, CLOSEST_FRAME_IDX, poseTime), getInterpolatedValue(CHANNEL->PositionKeyFrames, closestFrameIndex, poseTime),
getInterpolatedValue(channel.RotationKeyFrames, CLOSEST_FRAME_IDX, poseTime), getInterpolatedValue(CHANNEL->RotationKeyFrames, closestFrameIndex, poseTime),
getInterpolatedValue(channel.ScaleKeyFrames , CLOSEST_FRAME_IDX, poseTime) getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime)
); );
} }
else
{
transformMatrix = SHMatrix::Inverse(node->OffsetMatrix); // TODO: Use TransformMatrix for the bone
}
// Apply parent's transformation
transformMatrix = parentMatrix * transformMatrix;
// Apply transformations to this node
const int BONE_MTX_IDX = rig->GetNodeIndex(node);
if (BONE_MTX_IDX >= 0)
{
boneMatrices[BONE_MTX_IDX] = boneMatrices[BONE_MTX_IDX] * transformMatrix * node->OffsetMatrix;
}
// Apply pose to children
for (auto& child : node->Children)
{
updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix);
} }
} }
} }

View File

@ -30,6 +30,7 @@ namespace SHADE
/* Forward Declarations */ /* Forward Declarations */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
class SHRig; class SHRig;
struct SHRigNode;
class SHAnimationClip; class SHAnimationClip;
class SHVkBuffer; class SHVkBuffer;
@ -133,11 +134,14 @@ namespace SHADE
float secsPerTick = 0.0f; float secsPerTick = 0.0f;
// Buffer // Buffer
std::vector<SHMatrix> boneMatrices; std::vector<SHMatrix> boneMatrices;
// Caches
std::unordered_map<std::string, const SHAnimationClip::Channel*> channelMap;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
void updatePoseWithClip(float poseTime); void updatePoseWithClip(float poseTime);
void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRigNode> node, const SHMatrix& parentMatrix);
template<typename T> template<typename T>
T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, int closestFrameIndex, float poseTime); T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, int closestFrameIndex, float poseTime);

View File

@ -39,7 +39,7 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Usage Functions */ /* Usage Functions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
const std::string& SHRig::GetName(Handle<Node> node) const noexcept const std::string& SHRig::GetName(Handle<SHRigNode> node) const noexcept
{ {
static const std::string EMPTY_STRING = ""; static const std::string EMPTY_STRING = "";
@ -49,7 +49,7 @@ namespace SHADE
return EMPTY_STRING; return EMPTY_STRING;
} }
Handle<SHRig::Node> SHRig::GetNode(const std::string& name) const noexcept Handle<SHRigNode> SHRig::GetNode(const std::string& name) const noexcept
{ {
if (nodesByName.contains(name)) if (nodesByName.contains(name))
return nodesByName.at(name); return nodesByName.at(name);
@ -62,7 +62,7 @@ namespace SHADE
return static_cast<int>(nodes.size()); return static_cast<int>(nodes.size());
} }
int SHRig::GetNodeIndex(Handle<Node> node) const noexcept int SHRig::GetNodeIndex(Handle<SHRigNode> node) const noexcept
{ {
if (nodeIndexMap.contains(node)) if (nodeIndexMap.contains(node))
{ {
@ -75,14 +75,14 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
Handle<SHRig::Node> SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNode* sourceNode) Handle<SHRigNode> SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode)
{ {
// Construct the node // Construct the node
auto newNode = nodeStore.Create(); auto newNode = nodeStore.Create();
// Fill the node with data // Fill the node with data
const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef); const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef);
newNode->Transform = NODE_DATA.transform; newNode->OffsetMatrix = NODE_DATA.transform;
// Populate maps // Populate maps
if (!NODE_DATA.name.empty()) if (!NODE_DATA.name.empty())

View File

@ -28,23 +28,32 @@ namespace SHADE
/* Forward Declarations */ /* Forward Declarations */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
struct SHRigAsset; struct SHRigAsset;
struct SHRigNode; struct SHRigNodeAsset;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Type Definitions */ /* Type Definitions */
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/// <summary>
///
/// </summary>
struct SHRigNode
{
/// <summary>
/// Matrix that performs a transformation from local space to bone (node) space.
/// </summary>
SHMatrix OffsetMatrix;
/// <summary>
/// Child nodes of this node.
/// </summary>
std::vector<Handle<SHRigNode>> Children;
};
/// <summary>
///
/// </summary>
class SH_API SHRig class SH_API SHRig
{ {
public: public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
struct Node
{
SHMatrix Transform;
std::vector<Handle<Node>> Children;
};
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Constructors */ /* Constructors */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -65,7 +74,12 @@ namespace SHADE
/// Name of the node. If it does not have a name or is invalid, an empty string will /// Name of the node. If it does not have a name or is invalid, an empty string will
/// be provided. /// be provided.
/// </returns> /// </returns>
const std::string& GetName(Handle<Node> node) const noexcept; const std::string& GetName(Handle<SHRigNode> node) const noexcept;
/// <summary>
/// Retrieves the root node of the rig.
/// </summary>
/// <returns>Handle to the root node of the rig.</returns>
Handle<SHRigNode> GetRootNode() const noexcept { return rootNode; }
/// <summary> /// <summary>
/// Retrieves a node via name. /// Retrieves a node via name.
/// </summary> /// </summary>
@ -74,7 +88,7 @@ namespace SHADE
/// Node with the specified name. If it does not have a name or is invalid, an empty /// Node with the specified name. If it does not have a name or is invalid, an empty
/// handle will be provided. /// handle will be provided.
/// </returns> /// </returns>
Handle<Node> GetNode(const std::string& name) const noexcept; Handle<SHRigNode> GetNode(const std::string& name) const noexcept;
/// <summary> /// <summary>
/// Returns the number of nodes in the rig. This matches the number of bone matrices /// Returns the number of nodes in the rig. This matches the number of bone matrices
/// needed. /// needed.
@ -83,22 +97,22 @@ namespace SHADE
/// <summary> /// <summary>
/// Retrieves the index in the node storage. /// Retrieves the index in the node storage.
/// </summary> /// </summary>
int GetNodeIndex(Handle<Node> node) const noexcept; int GetNodeIndex(Handle<SHRigNode> node) const noexcept;
private: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Data Members */ /* Data Members */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
Handle<Node> rootNode; Handle<SHRigNode> rootNode;
std::unordered_map<Handle<Node>, std::string> nodeNames; std::unordered_map<Handle<SHRigNode>, std::string> nodeNames;
std::unordered_map<std::string, Handle<Node>> nodesByName; std::unordered_map<std::string, Handle<SHRigNode>> nodesByName;
std::vector<Handle<Node>> nodes; std::vector<Handle<SHRigNode>> nodes;
std::unordered_map<Handle<Node>, int> nodeIndexMap; std::unordered_map<Handle<SHRigNode>, int> nodeIndexMap;
SHResourceLibrary<Node> nodeStore; SHResourceLibrary<SHRigNode> nodeStore;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Helper Functions */ /* Helper Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
Handle<Node> recurseCreateNode(const SHRigAsset& asset, const SHRigNode* sourceNode); Handle<SHRigNode> recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode);
}; };
} }

View File

@ -29,10 +29,10 @@ namespace SHADE
SHMatrix transform; SHMatrix transform;
}; };
struct SHRigNode struct SHRigNodeAsset
{ {
uint32_t idRef; uint32_t idRef;
std::vector<SHRigNode*> children; std::vector<SHRigNodeAsset*> children;
}; };
struct SH_API SHRigAsset : SHAssetData struct SH_API SHRigAsset : SHAssetData
@ -41,6 +41,6 @@ namespace SHADE
SHRigDataHeader header; SHRigDataHeader header;
std::vector<SHRigNodeData> nodeDataCollection; std::vector<SHRigNodeData> nodeDataCollection;
SHRigNode* root; SHRigNodeAsset* root;
}; };
} }

View File

@ -154,7 +154,7 @@ namespace SHADE
} }
} }
void SHModelLoader::ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode*& root) void SHModelLoader::ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNodeAsset*& root)
{ {
// Read All nodes into one contiguous data block // Read All nodes into one contiguous data block
struct NodeTemp struct NodeTemp
@ -170,13 +170,13 @@ namespace SHADE
); );
// Build and populate tree // Build and populate tree
SHRigNode* nodePool = new SHRigNode[header.nodeCount]; SHRigNodeAsset* nodePool = new SHRigNodeAsset[header.nodeCount];
root = nodePool; root = nodePool;
std::queue<std::pair<SHRigNode*, NodeTemp*>> nodeQueue; std::queue<std::pair<SHRigNodeAsset*, NodeTemp*>> nodeQueue;
nodeQueue.emplace(std::make_pair(nodePool, dst)); nodeQueue.emplace(std::make_pair(nodePool, dst));
SHRigNode* depthPtr = nodePool + 1; SHRigNodeAsset* depthPtr = nodePool + 1;
NodeTemp* depthTempPtr = dst + 1; NodeTemp* depthTempPtr = dst + 1;
while(!nodeQueue.empty()) while(!nodeQueue.empty())

View File

@ -24,7 +24,7 @@ namespace SHADE
void ReadRigHeader(FileReference file, SHRigDataHeader& header); void ReadRigHeader(FileReference file, SHRigDataHeader& header);
void ReadRigData(FileReference file, SHRigDataHeader const& header, std::vector<SHRigNodeData>& data); void ReadRigData(FileReference file, SHRigDataHeader const& header, std::vector<SHRigNodeData>& data);
void ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNode*& root); void ReadRigTree(FileReference file, SHRigDataHeader const& header, SHRigNodeAsset*& root);
void ReadMeshData(FileReference file, std::vector<SHMeshDataHeader> const& headers, std::vector<SHMeshAsset*>& meshes); void ReadMeshData(FileReference file, std::vector<SHMeshDataHeader> const& headers, std::vector<SHMeshAsset*>& meshes);
void ReadAnimData(FileReference file, std::vector<SHAnimDataHeader> const& headers, std::vector<SHAnimAsset*>& anims); void ReadAnimData(FileReference file, std::vector<SHAnimDataHeader> const& headers, std::vector<SHAnimAsset*>& anims);