Code/HumanoidRetargeter/Target/CompiledRigSourceSpace.cs
#nullable enable annotations

using System;
using System.Numerics;
using HumanoidRetargeter.Maths;

using NVector3 = System.Numerics.Vector3;

namespace HumanoidRetargeter.Target;

/// <summary>
/// Converts compiled-model bind locals (engine Z-up inches) back into the source space
/// declared by an embedded-FBX target.
/// </summary>
public static class CompiledRigSourceSpace
{
    /// <summary>
    /// Converts one parent-local transform. The unit conversion applies to every bone;
    /// the inverse Z-up-to-Y-up basis applies only to roots of Y-up source rigs. Z-up FBX
    /// targets (including Unreal exports) remain Z-up and therefore are never rotated twice.
    /// </summary>
    public static XForm FromEngineLocal(XForm local, bool isRoot, TargetUpAxis sourceUpAxis)
    {
        var position = local.Pos / RetargetTargetSpec.SboxSourceScale;
        var rotation = local.Rot;
        if (isRoot && sourceUpAxis == TargetUpAxis.YUpCm)
        {
            var inverseUp = Quaternion.CreateFromAxisAngle(NVector3.UnitX, -MathF.PI * 0.5f);
            position = NVector3.Transform(position, inverseUp);
            rotation = Quaternion.Normalize(inverseUp * rotation);
        }

        return new XForm(position, rotation);
    }
}