Optimize animations retargeting between skeletons

Cuts down `RetargetSkeletonPose` time down by over 80%.

#3827
This commit is contained in:
Wojtek Figat
2026-02-09 15:03:42 +01:00
parent 9c32f978fb
commit 3d66316716
4 changed files with 116 additions and 66 deletions

View File

@@ -154,6 +154,8 @@ void SkeletonData::Swap(SkeletonData& other)
{
Nodes.Swap(other.Nodes);
Bones.Swap(other.Bones);
Dirty();
other.Dirty();
}
Transform SkeletonData::GetNodeTransform(int32 nodeIndex) const
@@ -171,6 +173,7 @@ Transform SkeletonData::GetNodeTransform(int32 nodeIndex) const
void SkeletonData::SetNodeTransform(int32 nodeIndex, const Transform& value)
{
CHECK(Nodes.IsValidIndex(nodeIndex));
Dirty();
const int32 parentIndex = Nodes[nodeIndex].ParentIndex;
if (parentIndex == -1)
{
@@ -201,6 +204,39 @@ int32 SkeletonData::FindBone(int32 nodeIndex) const
return -1;
}
const Array<Matrix>& SkeletonData::GetNodesPose() const
{
// Guard with a simple atomic flag to avoid locking if the pose is up to date
if (Platform::AtomicRead(&_dirty))
{
ScopeLock lock(RenderContext::GPULocker);
if (Platform::AtomicRead(&_dirty))
{
Platform::AtomicStore(&_dirty, 0);
const SkeletonNode* nodes = Nodes.Get();
const int32 nodesCount = Nodes.Count();
_cachedPose.Resize(nodesCount);
Matrix* posePtr = _cachedPose.Get();
for (int32 nodeIndex = 0; nodeIndex < nodesCount; nodeIndex++)
{
const SkeletonNode& node = nodes[nodeIndex];
Matrix local;
Matrix::Transformation(node.LocalTransform.Scale, node.LocalTransform.Orientation, node.LocalTransform.Translation, local);
if (node.ParentIndex != -1)
Matrix::Multiply(local, posePtr[node.ParentIndex], posePtr[nodeIndex]);
else
posePtr[nodeIndex] = local;
}
}
}
return _cachedPose;
}
void SkeletonData::Dirty()
{
Platform::AtomicStore(&_dirty, 1);
}
uint64 SkeletonData::GetMemoryUsage() const
{
uint64 result = Nodes.Capacity() * sizeof(SkeletonNode) + Bones.Capacity() * sizeof(SkeletonBone);