tests/SmartPort/Parser/AttachmentAlignmentTests.cs
using System.Numerics;
using HumanoidRetargeter.Editor;
using HumanoidRetargeter.Maths;
using HumanoidRetargeter.Skeleton;
using HumanoidRetargeter.Target;
using HumanoidRetargeterVrf;
using HumanoidRetargeterVrf.ResourceTypes;
using Xunit;
using SkeletonModel = HumanoidRetargeter.Skeleton.Skeleton;
namespace SmartPort.Parser.Tests;
public class AttachmentAlignmentTests
{
static string Document(string bone, bool ignore = false) => VmdlWriter.Kv3Header + """
{ rootNode = { children = [{ _class = "AttachmentList" children = [{
_class = "Attachment" name = "tool_mount"
""" + $"parent_bone = \"{bone}\" ignore_rotation = {ignore.ToString().ToLowerInvariant()} " + """
relative_origin = [1, 2, 3] relative_angles = [0, 0, 0] weight = 1
}] }] } }
""";
static KvObject Attachment(string text) => (KvObject)((KvArray)((KvObject)((KvArray)((KvObject)((KvObject)
Kv3.Parse(text).Root)["rootNode"])["children"]).Items[0])["children"]).Items[0];
[CompiledFixtureFact]
public void SharedSocketAdoptsSourceAxesWithoutMovingItsFittedPositionOrSkin()
{
using var resource = new Resource();
resource.Read(Path.Combine(Environment.GetEnvironmentVariable("HR_SMART_PORT_FIXTURE")!, "models/player/human/frank_mp.vmdl_c"));
var model = Assert.IsType<Model>(resource.DataBlock);
var source = SkeletonModel.Create(model.Skeleton.Bones.Select(b => new BoneDefinition(b.Name, b.Parent?.Name, new(b.Position, b.Angle))).ToArray());
var target = SkeletonModel.Create(source.Bones.Select(b => new BoneDefinition(b.Name,
b.ParentIndex < 0 ? null : source[b.ParentIndex].Name,
b.Name == "hold_R" ? new XForm(b.RestLocal.Pos, Quaternion.CreateFromAxisAngle(Vector3.UnitY, .3f) * b.RestLocal.Rot) : b.RestLocal)).ToArray());
var rig = new SmartPortRig(source, target);
var sourceText = Document("hold_r"); // compiled attachment names can differ in case
var targetText = Document("hold_R");
var aligned = Attachment(SmartPortAttachments.Align(targetText, sourceText, rig));
Assert.True(KvValue.DeepEquals(Attachment(targetText)["relative_origin"], aligned["relative_origin"]));
var values = ((KvArray)aligned["relative_angles"]).Items.Select(v => v is KvDouble d ? (float)d.Value : ((KvLong)v).Value).ToArray();
var local = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, values[1] * MathF.PI / 180)
* Quaternion.CreateFromAxisAngle(Vector3.UnitY, values[0] * MathF.PI / 180)
* Quaternion.CreateFromAxisAngle(Vector3.UnitX, values[2] * MathF.PI / 180);
var pose = rig.Transfer(source.Bones.Select(b => b.RestLocal).ToArray());
var world = new XForm[rig.Target.Count];
foreach (var bone in rig.Target.Bones)
world[bone.Index] = bone.ParentIndex < 0 ? pose[bone.Index] : XForm.Compose(world[bone.ParentIndex], pose[bone.Index]);
Assert.True(MathQ.AngleBetween(world[rig.Target.IndexOf("hold_R")].Rot * local, source.RestWorld[source.IndexOf("hold_R")].Rot) < .001f);
foreach (var bone in target.Bones) Assert.Equal(bone.RestLocal, rig.Target[rig.Target.IndexOf(bone.Name)].RestLocal);
var ignored = Document("hold_R", true);
Assert.True(KvValue.DeepEquals(Attachment(ignored), Attachment(SmartPortAttachments.Align(ignored, sourceText, rig))));
}
[CompiledFixtureFact]
public void RecoveredGraphRetainsEveryNodePositionWhenCopied()
{
var path = Path.Combine(Environment.GetEnvironmentVariable("HR_SMART_PORT_FIXTURE")!, "models/player/mplayer/mplayer_animgraph.vanmgrph_c");
var recovered = CompiledAssetRecovery.Recover(path, "models/player/mplayer/mplayer_animgraph.vanmgrph",
Path.GetTempPath(), "unused", new Dictionary<string, string>(), CancellationToken.None);
using var resource = new Resource(); resource.Read(path);
var original = (KvObject)Kv3.Parse(resource.DataBlock!.ToString()!).Root;
var copied = (KvObject)Kv3.Parse(StockAnimationGraph.CopyForModel(recovered, "custom.vmdl")).Root;
Assert.True(KvValue.DeepEquals(original["m_nodeManager"], copied["m_nodeManager"]));
Assert.Contains("m_vecPosition", recovered);
}
}