Editor/Core/Rigging/RigPosePreview.cs
using System.Numerics;
namespace HumanoidRigger;
using Vector3=System.Numerics.Vector3;
/// <summary>Absolute preview poses, always evaluated from the imported bind pose.</summary>
public static class RigPosePreview
{
public static Vector3[][] Apply(ImportedCharacter character,GeneratedRig rig,CharacterPose pose)
{
if(pose==CharacterPose.Auto)return character.Meshes.Select(m=>m.Vertices).ToArray();
return Deformation.ApplyRotations(character,rig,Rotations(rig,pose));
}
public static Vector3[] BonePositions(GeneratedRig rig,CharacterPose pose)=>Deformation.BoneTransforms(rig,Rotations(rig,pose)).Positions;
public static (Vector3[] Positions,Quaternion[] Rotations) Transforms(GeneratedRig rig,CharacterPose pose)=>Deformation.BoneTransforms(rig,Rotations(rig,pose));
public static IReadOnlyDictionary<string,Quaternion> Rotations(GeneratedRig rig,CharacterPose pose)
{
if(pose==CharacterPose.Auto)return new Dictionary<string,Quaternion>();
float degrees=pose switch
{
CharacterPose.TPose=>0,
CharacterPose.APose1=>30,
CharacterPose.APose2=>45,
_=>throw new ArgumentException("Unsupported preview pose.",nameof(pose))
};
var rotations=new Dictionary<string,Quaternion>();
foreach(var (side,sign) in new[]{("L",1f),("R",-1f)})
{
var shoulder=rig.Bones.FirstOrDefault(b=>b.Role=="UpperArm."+side);
var elbow=rig.Bones.FirstOrDefault(b=>b.Role=="LowerArm."+side);
if(shoulder is null||elbow is null)throw new InvalidOperationException("The rig needs both arm chains to preview this pose.");
var direction=elbow.Position-shoulder.Position;
if(direction.LengthSquared()<1e-8f)throw new InvalidOperationException("An arm has no usable length.");
direction=Vector3.Normalize(direction);
float radians=degrees*MathF.PI/180;
var target=new Vector3(sign*MathF.Cos(radians),-MathF.Sin(radians),0);
var axis=Vector3.Cross(direction,target);float dot=Math.Clamp(Vector3.Dot(direction,target),-1,1);
var rotation=dot<-.99999f?Quaternion.CreateFromAxisAngle(Vector3.UnitZ,MathF.PI)
:Quaternion.Normalize(new Quaternion(axis,1+dot));
rotations[shoulder.Role]=rotation;
}
return rotations;
}
}