Animation WIP merge #321
|
@ -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()));
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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 pose to children
|
||||
for (auto& child : node->Children)
|
||||
{
|
||||
updatePoseWithClip(closestFrameIndex, poseTime, child, transformMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue