// 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;
}
}
};