Editor/Core/Rigging/RigPoseEditing.cs
using System.Numerics;
namespace HumanoidRigger;
using Vector3=System.Numerics.Vector3;
/// <summary>Converts a world-space bone drag to the existing bind-space pose rotations.</summary>
public static class RigPoseEditing
{
public static IReadOnlyDictionary<string,Quaternion> Rotate(GeneratedRig rig,IReadOnlyDictionary<string,Quaternion> pose,string role,Vector3 from,Vector3 to)
{
int index=Array.FindIndex(rig.Bones,b=>b.Role==role);
if(index<0)throw new ArgumentException("Unknown pose bone.",nameof(role));
var result=new Dictionary<string,Quaternion>(pose);
if(!Geometry.Finite(from)||!Geometry.Finite(to)||from.LengthSquared()<1e-10f||to.LengthSquared()<1e-10f)return result;
from=Vector3.Normalize(from);to=Vector3.Normalize(to);float dot=Math.Clamp(Vector3.Dot(from,to),-1,1);
if(dot>1-1e-7f)return result;
Quaternion delta;
if(dot< -1+1e-6f)
{
var basis=Math.Abs(from.X)<.8f?Vector3.UnitX:Vector3.UnitY;
delta=Quaternion.CreateFromAxisAngle(Vector3.Normalize(Vector3.Cross(from,basis)),MathF.PI);
}
else delta=Quaternion.Normalize(new Quaternion(Vector3.Cross(from,to),1+dot));
var transforms=Deformation.BoneTransforms(rig,pose);int parent=rig.Bones[index].Parent;
var inherited=parent<0?Quaternion.Identity:transforms.Rotations[parent];
// Parent rotation is already applied by Deformation.BoneTransforms.
// Conjugate the drag into that parent's frame instead of applying it twice.
result[role]=Quaternion.Normalize(Quaternion.Inverse(inherited)*delta*inherited*pose.GetValueOrDefault(role,Quaternion.Identity));
return result;
}
}