tests/CitizenAnimationSetup/SmartPortHelperChainTests.cs
using System.Numerics;
using HumanoidRetargeter.Maths;
using HumanoidRetargeter.Skeleton;
using HumanoidRetargeter.Target;
using Xunit;
using SkeletonModel = HumanoidRetargeter.Skeleton.Skeleton;
namespace HumanoidRetargeter.Tests.Target;
public class SmartPortHelperChainTests
{
static SkeletonModel Rig(bool driver)
{
var bones = new List<BoneDefinition>();
void Add(string name, string? parent, Vector3 pos) => bones.Add(new(name, parent, new(pos, Quaternion.Identity)));
Add("pelvis", null, new(0, 0, 40));
Add("spine_0", "pelvis", new(0, 0, 8));
Add("spine_1", "spine_0", new(0, 0, 8));
Add("neck_0", "spine_1", new(0, 0, 5));
Add("head", "neck_0", new(0, 0, 5));
foreach (var side in new[] { "L", "R" })
{
var y = side == "L" ? 1 : -1;
Add("clavicle_" + side, "spine_1", new(0, y * 2, 0));
Add("arm_upper_" + side, "clavicle_" + side, new(0, y * 4, 0));
Add("arm_lower_" + side, "arm_upper_" + side, new(0, y * 10, 0));
Add("hand_" + side, "arm_lower_" + side, new(0, y * 9, 0));
Add("leg_upper_" + side, "pelvis", new(0, y * 4, 0));
Add("leg_lower_" + side, "leg_upper_" + side, new(0, 0, -18));
Add("ankle_" + side, "leg_lower_" + side, new(0, 0, -18));
Add("ball_" + side, "ankle_" + side, new(5, 0, -3));
}
if (driver) Add("accessory_driver", "hand_R", Vector3.Zero);
Add("accessory_socket", driver ? "accessory_driver" : "hand_R", new(2, 0, 0));
return SkeletonModel.Create(bones);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void SkippedAnimatedParentStillDrivesSocket(bool additive)
{
var source = Rig(true);
var target = Rig(false);
var rig = new SmartPortRig(source, target);
var pose = source.Bones.Select(b => additive ? XForm.Identity : b.RestLocal).ToArray();
var rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, .25f);
pose[source.IndexOf("accessory_driver")].Rot = rotation;
var output = rig.Transfer(pose, additive);
var socket = rig.Target.IndexOf("accessory_socket");
Assert.True(MathQ.AngleBetween(rotation, output[socket].Rot) < .001f);
var expectedPosition = Vector3.Transform(new Vector3(2, 0, 0), rotation);
if (additive) expectedPosition -= new Vector3(2, 0, 0);
Assert.True(Vector3.Distance(expectedPosition, output[socket].Pos) < .001f);
foreach (var bone in target.Bones)
Assert.Equal(bone.RestLocal, rig.Target[rig.Target.IndexOf(bone.Name)].RestLocal);
}
}