// Copyright (c) Wojciech Figat. All rights reserved. #pragma once #include "SkeletonData.h" #include "Engine/Core/Collections/Array.h" /// /// Performs hierarchical updates for skeleton nodes. /// template class SkeletonUpdater { public: typedef Array Items; /// /// Represents skeleton node transformation data. /// struct Node { /// /// The parent node index. The root node uses value -1. /// int32 ParentIndex; /// /// The local transform. /// Transform Transform; /// /// The local transformation matrix (from parent local space to node local space). /// Matrix LocalMatrix; /// /// The absolute world transformation matrix (from world space to node local space). /// Matrix WorldMatrix; }; public: /// /// The cached node transformations. /// Array NodeTransformations; public: /// /// Initializes a new instance of the class. /// /// The skeleton. SkeletonUpdater(const Items& skeleton) { Initialize(skeleton); } /// /// Initializes the updater using the specified skeleton. /// /// The skeleton. void Initialize(const Items& skeleton) { const int32 count = skeleton.Count(); NodeTransformations.Resize(count, false); for (int32 i = 0; i < count; i++) { auto& n1 = NodeTransformations[i]; auto& n2 = skeleton[i]; n1.ParentIndex = n2.ParentIndex; n1.Transform = n2.LocalTransform; n1.WorldMatrix = Matrix::Identity; n1.Transform.GetWorld(n1.LocalMatrix); } } /// /// For each node, updates the world matrices from local matrices. /// void UpdateMatrices() { for (int32 i = 0; i < NodeTransformations.Count(); i++) { UpdateNode(NodeTransformations[i]); } } /// /// Gets the transformation matrix to go from rootIndex to index. /// /// The root index. /// The current index. /// The matrix at this index. Matrix CombineMatricesFromNodeIndices(int32 rootIndex, int32 index) { if (index == -1) return Matrix::Identity; auto result = NodeTransformations[index].LocalMatrix; if (index != rootIndex) { const auto topMatrix = CombineMatricesFromNodeIndices(rootIndex, NodeTransformations[index].ParentIndex); result = Matrix::Multiply(result, topMatrix); } return result; } /// /// Gets the world matrix of the node. /// /// The node index. /// The result matrix. FORCE_INLINE void GetWorldMatrix(int32 index, Matrix* matrix) { *matrix = NodeTransformations[index].WorldMatrix; } /// /// Gets the local matrix of the node. /// /// The node index. /// The result matrix. FORCE_INLINE void GetLocalMatrix(int32 index, Matrix* matrix) { *matrix = NodeTransformations[index].LocalMatrix; } public: /// /// Gets the default root node. /// /// The node. static Node GetDefaultNode() { Node node; node.ParentIndex = -1; node.Transform = Transform::Identity; node.LocalMatrix = Matrix::Identity; node.WorldMatrix = Matrix::Identity; return node; } private: void UpdateNode(Node& node) { // Compute local matrix node.Transform.GetWorld(node.LocalMatrix); // Compute world matrix if (node.ParentIndex != -1) { Matrix::Multiply(node.LocalMatrix, NodeTransformations[node.ParentIndex].WorldMatrix, node.WorldMatrix); } else { node.WorldMatrix = node.LocalMatrix; } } };