Files
FlaxEngine/Source/Engine/Graphics/Models/SkeletonMapping.h
2024-02-26 19:00:48 +01:00

98 lines
3.1 KiB
C++

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Collections/ArrayExtensions.h"
#include "Engine/Core/Types/String.h"
/// <summary>
/// Helper class used to map model nodes/bones from one skeleton into another. Useful for animation retargeting.
/// </summary>
template<typename T>
class SkeletonMapping
{
public:
typedef Array<T> Items;
public:
/// <summary>
/// The amount of the nodes (from the source skeleton).
/// </summary>
int32 Size;
/// <summary>
/// The node mapping from source to target skeletons.
/// </summary>
Array<int32> SourceToTarget;
/// <summary>
/// A round-trip through TargetToSource[SourceToTarget[i]] so that we know easily what nodes are remapped in source skeleton side.
/// </summary>
Array<int32> SourceToSource;
public:
/// <summary>
/// Initializes a new instance of the <see cref="SkeletonMapping"/> class.
/// </summary>
/// <param name="sourceSkeleton">The source model skeleton.</param>
/// <param name="targetSkeleton">The target skeleton. May be null to disable nodes mapping.</param>
SkeletonMapping(const Items& sourceSkeleton, const Items* targetSkeleton)
{
Size = sourceSkeleton.Count();
SourceToTarget.Resize(Size); // model => skeleton mapping
SourceToSource.Resize(Size); // model => model mapping
if (targetSkeleton == nullptr)
{
// No skeleton, we can compact everything
for (int i = 0; i < Size; i++)
{
// Map everything to the root node
SourceToTarget[i] = 0;
SourceToSource[i] = 0;
}
return;
}
Array<int32> targetToSource;
targetToSource.Resize(Size); // skeleton => model mapping
for (int32 i = 0; i < Size; i++)
targetToSource[i] = -1;
// Build mapping from model to actual skeleton
for (int32 modelIndex = 0; modelIndex < Size; modelIndex++)
{
auto node = sourceSkeleton[modelIndex];
const auto parentModelIndex = node.ParentIndex;
// Find matching node in skeleton (or map to best parent)
const Function<bool(const T&)> f = [node](const T& x) -> bool
{
return x.Name == node.Name;
};
const auto skeletonIndex = ArrayExtensions::IndexOf(*targetSkeleton, f);
if (skeletonIndex == -1)
{
// Nothing match, remap to parent node
SourceToTarget[modelIndex] = parentModelIndex != -1 ? SourceToTarget[parentModelIndex] : 0;
continue;
}
// Name match
SourceToTarget[modelIndex] = skeletonIndex;
targetToSource[skeletonIndex] = modelIndex;
}
for (int32 modelIndex = 0; modelIndex < Size; modelIndex++)
{
SourceToSource[modelIndex] = targetToSource[SourceToTarget[modelIndex]];
}
}
/// <summary>
/// Finalizes an instance of the <see cref="SkeletonMapping"/> class.
/// </summary>
~SkeletonMapping()
{
}
};