// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Collections/ArrayExtensions.h"
#include "Engine/Core/Types/String.h"
///
/// Helper class used to map model nodes/bones from one skeleton into another. Useful for animation retargeting.
///
template
class SkeletonMapping
{
public:
typedef Array Items;
public:
///
/// The amount of the nodes (from the source skeleton).
///
int32 Size;
///
/// The node mapping from source to target skeletons.
///
Array SourceToTarget;
///
/// A round-trip through TargetToSource[SourceToTarget[i]] so that we know easily what nodes are remapped in source skeleton side.
///
Array SourceToSource;
public:
///
/// Initializes a new instance of the class.
///
/// The source model skeleton.
/// The target skeleton. May be null to disable nodes mapping.
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 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 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]];
}
}
///
/// Finalizes an instance of the class.
///
~SkeletonMapping()
{
}
};