// 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(Items& sourceSkeleton, 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 std::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() { } };