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)
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<int>(std::floorf(poseTime * currClip->GetTicksPerSecond()));
updatePoseWithClip(CLOSEST_FRAME_IDX, poseTime, rig->GetRootNode(), SHMatrix::Identity);
}
void SHAnimatorComponent::updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRigNode> 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<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;
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<int>(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);
}
}
}

View File

@ -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<SHMatrix> boneMatrices;
// Caches
std::unordered_map<std::string, const SHAnimationClip::Channel*> channelMap;
/*---------------------------------------------------------------------------------*/
/* Helper Functions */
/*---------------------------------------------------------------------------------*/
void updatePoseWithClip(float poseTime);
void updatePoseWithClip(int closestFrameIndex, float poseTime, Handle<SHRigNode> node, const SHMatrix& parentMatrix);
template<typename T>
T getInterpolatedValue(const std::vector<SHAnimationKeyFrame<T>>& keyframes, int closestFrameIndex, float poseTime);

View File

@ -39,7 +39,7 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/
/* 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 = "";
@ -49,7 +49,7 @@ namespace SHADE
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))
return nodesByName.at(name);
@ -62,7 +62,7 @@ namespace SHADE
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))
{
@ -75,14 +75,14 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/
/* 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
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())

View File

@ -28,23 +28,32 @@ namespace SHADE
/* Forward Declarations */
/*-----------------------------------------------------------------------------------*/
struct SHRigAsset;
struct SHRigNode;
struct SHRigNodeAsset;
/*-----------------------------------------------------------------------------------*/
/* 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
{
public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
struct Node
{
SHMatrix Transform;
std::vector<Handle<Node>> 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.
/// </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>
/// Retrieves a node via name.
/// </summary>
@ -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.
/// </returns>
Handle<Node> GetNode(const std::string& name) const noexcept;
Handle<SHRigNode> GetNode(const std::string& name) const noexcept;
/// <summary>
/// Returns the number of nodes in the rig. This matches the number of bone matrices
/// needed.
@ -83,22 +97,22 @@ namespace SHADE
/// <summary>
/// Retrieves the index in the node storage.
/// </summary>
int GetNodeIndex(Handle<Node> node) const noexcept;
int GetNodeIndex(Handle<SHRigNode> node) const noexcept;
private:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
Handle<Node> rootNode;
std::unordered_map<Handle<Node>, std::string> nodeNames;
std::unordered_map<std::string, Handle<Node>> nodesByName;
std::vector<Handle<Node>> nodes;
std::unordered_map<Handle<Node>, int> nodeIndexMap;
SHResourceLibrary<Node> nodeStore;
Handle<SHRigNode> rootNode;
std::unordered_map<Handle<SHRigNode>, std::string> nodeNames;
std::unordered_map<std::string, Handle<SHRigNode>> nodesByName;
std::vector<Handle<SHRigNode>> nodes;
std::unordered_map<Handle<SHRigNode>, int> nodeIndexMap;
SHResourceLibrary<SHRigNode> nodeStore;
/*---------------------------------------------------------------------------------*/
/* 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;
};
struct SHRigNode
struct SHRigNodeAsset
{
uint32_t idRef;
std::vector<SHRigNode*> children;
std::vector<SHRigNodeAsset*> children;
};
struct SH_API SHRigAsset : SHAssetData
@ -41,6 +41,6 @@ namespace SHADE
SHRigDataHeader header;
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
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<std::pair<SHRigNode*, NodeTemp*>> nodeQueue;
std::queue<std::pair<SHRigNodeAsset*, NodeTemp*>> nodeQueue;
nodeQueue.emplace(std::make_pair(nodePool, dst));
SHRigNode* depthPtr = nodePool + 1;
SHRigNodeAsset* depthPtr = nodePool + 1;
NodeTemp* depthTempPtr = dst + 1;
while(!nodeQueue.empty())

View File

@ -24,7 +24,7 @@ namespace SHADE
void ReadRigHeader(FileReference file, SHRigDataHeader& header);
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 ReadAnimData(FileReference file, std::vector<SHAnimDataHeader> const& headers, std::vector<SHAnimAsset*>& anims);