From b9fcdc43d43954b9157b7d64ab69dea3affb6aa8 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Wed, 18 Jan 2023 19:16:45 +0800 Subject: [PATCH] Modified how SHAnimatorComponent computes the bone matrices --- .../src/Animation/SHAnimatorComponent.cpp | 76 ++++++++++++------- .../src/Animation/SHAnimatorComponent.h | 4 + SHADE_Engine/src/Animation/SHRig.cpp | 10 +-- SHADE_Engine/src/Animation/SHRig.h | 54 ++++++++----- .../Assets/Asset Types/Models/SHRigAsset.h | 6 +- .../Libraries/Loaders/SHModelLoader.cpp | 8 +- .../Assets/Libraries/Loaders/SHModelLoader.h | 2 +- 7 files changed, 99 insertions(+), 61 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index 5eff2c45..6ef2d47e 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -83,10 +83,21 @@ namespace SHADE if (currClip == newClip) return; + // Set parameters currClip = newClip; 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); } @@ -123,36 +134,45 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ void SHAnimatorComponent::updatePoseWithClip(float poseTime) { - for (const auto& channel : currClip->GetChannels()) + // Get closest frame index + const int CLOSEST_FRAME_IDX = static_cast(std::floorf(poseTime * currClip->GetTicksPerSecond())); + updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, rig->GetRootNode(), SHMatrix::Identity); + } + + void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix) + { + // 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)) { - // Get the bone - std::queue> 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; + const auto CHANNEL = channelMap[BONE_NAME]; + transformMatrix = SHMatrix::Transform + ( + getInterpolatedValue(CHANNEL->PositionKeyFrames, closestFrameIndex, poseTime), + getInterpolatedValue(CHANNEL->RotationKeyFrames, closestFrameIndex, poseTime), + getInterpolatedValue(CHANNEL->ScaleKeyFrames, closestFrameIndex, poseTime) + ); + } + else + { + transformMatrix = SHMatrix::Inverse(node->OffsetMatrix); // TODO: Use TransformMatrix for the bone + } - // Add any children to the queue - for (auto child : bone->Children) - { - bones.push(child); - } + // Apply parent's transformation + transformMatrix = parentMatrix * transformMatrix; - // Get closest frame index - const int CLOSEST_FRAME_IDX = static_cast(std::floorf(poseTime * currClip->GetTicksPerSecond())); - - // Calculate the matrix from interpolated values - const int BONE_MTX_IDX = rig->GetNodeIndex(bone); - boneMatrices[BONE_MTX_IDX] = boneMatrices[BONE_MTX_IDX] * SHMatrix::Transform - ( - getInterpolatedValue(channel.PositionKeyFrames, CLOSEST_FRAME_IDX, poseTime), - getInterpolatedValue(channel.RotationKeyFrames, CLOSEST_FRAME_IDX, poseTime), - getInterpolatedValue(channel.ScaleKeyFrames , CLOSEST_FRAME_IDX, poseTime) - ); - } + // 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); } } } diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 43a9f044..b47106f8 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -30,6 +30,7 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ class SHRig; + struct SHRigNode; class SHAnimationClip; class SHVkBuffer; @@ -133,11 +134,14 @@ namespace SHADE float secsPerTick = 0.0f; // Buffer std::vector boneMatrices; + // Caches + std::unordered_map channelMap; /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ void updatePoseWithClip(float poseTime); + void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle node, const SHMatrix& parentMatrix); template T getInterpolatedValue(const std::vector>& keyframes, int closestFrameIndex, float poseTime); diff --git a/SHADE_Engine/src/Animation/SHRig.cpp b/SHADE_Engine/src/Animation/SHRig.cpp index d86eee82..6f9b2b85 100644 --- a/SHADE_Engine/src/Animation/SHRig.cpp +++ b/SHADE_Engine/src/Animation/SHRig.cpp @@ -39,7 +39,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Usage Functions */ /*-----------------------------------------------------------------------------------*/ - const std::string& SHRig::GetName(Handle node) const noexcept + const std::string& SHRig::GetName(Handle node) const noexcept { static const std::string EMPTY_STRING = ""; @@ -49,7 +49,7 @@ namespace SHADE return EMPTY_STRING; } - Handle SHRig::GetNode(const std::string& name) const noexcept + Handle SHRig::GetNode(const std::string& name) const noexcept { if (nodesByName.contains(name)) return nodesByName.at(name); @@ -62,7 +62,7 @@ namespace SHADE return static_cast(nodes.size()); } - int SHRig::GetNodeIndex(Handle node) const noexcept + int SHRig::GetNodeIndex(Handle node) const noexcept { if (nodeIndexMap.contains(node)) { @@ -75,14 +75,14 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ - Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNode* sourceNode) + Handle SHRig::recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode) { // Construct the node auto newNode = nodeStore.Create(); // Fill the node with data const auto& NODE_DATA = asset.nodeDataCollection.at(sourceNode->idRef); - newNode->Transform = NODE_DATA.transform; + newNode->OffsetMatrix = NODE_DATA.transform; // Populate maps if (!NODE_DATA.name.empty()) diff --git a/SHADE_Engine/src/Animation/SHRig.h b/SHADE_Engine/src/Animation/SHRig.h index 53a201f4..3d2b14c2 100644 --- a/SHADE_Engine/src/Animation/SHRig.h +++ b/SHADE_Engine/src/Animation/SHRig.h @@ -28,23 +28,32 @@ namespace SHADE /* Forward Declarations */ /*-----------------------------------------------------------------------------------*/ struct SHRigAsset; - struct SHRigNode; + struct SHRigNodeAsset; /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ + /// + /// + /// + struct SHRigNode + { + /// + /// Matrix that performs a transformation from local space to bone (node) space. + /// + SHMatrix OffsetMatrix; + /// + /// Child nodes of this node. + /// + std::vector> Children; + }; + + /// + /// + /// class SH_API SHRig { public: - /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ - /*---------------------------------------------------------------------------------*/ - struct Node - { - SHMatrix Transform; - std::vector> Children; - }; - /*---------------------------------------------------------------------------------*/ /* 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 /// be provided. /// - const std::string& GetName(Handle node) const noexcept; + const std::string& GetName(Handle node) const noexcept; + /// + /// Retrieves the root node of the rig. + /// + /// Handle to the root node of the rig. + Handle GetRootNode() const noexcept { return rootNode; } /// /// Retrieves a node via name. /// @@ -74,7 +88,7 @@ namespace SHADE /// Node with the specified name. If it does not have a name or is invalid, an empty /// handle will be provided. /// - Handle GetNode(const std::string& name) const noexcept; + Handle GetNode(const std::string& name) const noexcept; /// /// Returns the number of nodes in the rig. This matches the number of bone matrices /// needed. @@ -83,22 +97,22 @@ namespace SHADE /// /// Retrieves the index in the node storage. /// - int GetNodeIndex(Handle node) const noexcept; + int GetNodeIndex(Handle node) const noexcept; private: /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ - Handle rootNode; - std::unordered_map, std::string> nodeNames; - std::unordered_map> nodesByName; - std::vector> nodes; - std::unordered_map, int> nodeIndexMap; - SHResourceLibrary nodeStore; + Handle rootNode; + std::unordered_map, std::string> nodeNames; + std::unordered_map> nodesByName; + std::vector> nodes; + std::unordered_map, int> nodeIndexMap; + SHResourceLibrary nodeStore; /*---------------------------------------------------------------------------------*/ /* Helper Functions */ /*---------------------------------------------------------------------------------*/ - Handle recurseCreateNode(const SHRigAsset& asset, const SHRigNode* sourceNode); + Handle recurseCreateNode(const SHRigAsset& asset, const SHRigNodeAsset* sourceNode); }; } \ No newline at end of file diff --git a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h index 86b497ae..424c5468 100644 --- a/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h +++ b/SHADE_Engine/src/Assets/Asset Types/Models/SHRigAsset.h @@ -29,10 +29,10 @@ namespace SHADE SHMatrix transform; }; - struct SHRigNode + struct SHRigNodeAsset { uint32_t idRef; - std::vector children; + std::vector children; }; struct SH_API SHRigAsset : SHAssetData @@ -41,6 +41,6 @@ namespace SHADE SHRigDataHeader header; std::vector nodeDataCollection; - SHRigNode* root; + SHRigNodeAsset* root; }; } diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp index aea46add..0d3160bc 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.cpp @@ -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 struct NodeTemp @@ -170,13 +170,13 @@ namespace SHADE ); // Build and populate tree - SHRigNode* nodePool = new SHRigNode[header.nodeCount]; + SHRigNodeAsset* nodePool = new SHRigNodeAsset[header.nodeCount]; root = nodePool; - std::queue> nodeQueue; + std::queue> nodeQueue; nodeQueue.emplace(std::make_pair(nodePool, dst)); - SHRigNode* depthPtr = nodePool + 1; + SHRigNodeAsset* depthPtr = nodePool + 1; NodeTemp* depthTempPtr = dst + 1; while(!nodeQueue.empty()) diff --git a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h index 93db8534..4320727f 100644 --- a/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h +++ b/SHADE_Engine/src/Assets/Libraries/Loaders/SHModelLoader.h @@ -24,7 +24,7 @@ namespace SHADE void ReadRigHeader(FileReference file, SHRigDataHeader& header); void ReadRigData(FileReference file, SHRigDataHeader const& header, std::vector& 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 const& headers, std::vector& meshes); void ReadAnimData(FileReference file, std::vector const& headers, std::vector& anims);