Editor/HumanoidRetargeter/SmartPortIkTargets.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeter.Target;
using HumanoidRetargeterVrf;
using HumanoidRetargeterVrf.ResourceTypes;
using HumanoidRetargeterVrf.Serialization.KeyValues;
namespace HumanoidRetargeter.Editor;
internal static class SmartPortIkTargets
{
internal static IReadOnlyDictionary<string, string> Read(string compiledModel, string graph)
{
using var resource = new Resource();
resource.Read(compiledModel);
var model = (Model)resource.DataBlock!;
var goals = new Dictionary<string, string>(StringComparer.Ordinal);
if (!model.KeyValues.ContainsKey("ikdata")) return goals;
var bones = model.Skeleton.Bones.ToDictionary(b => b.Name, b => b.Name, StringComparer.OrdinalIgnoreCase);
var ends = new Dictionary<string, string>(StringComparer.Ordinal);
void Add(string goal, string end)
{
if (!bones.TryGetValue(goal, out var g) || !bones.TryGetValue(end, out var e)) return;
if (goals.TryGetValue(g, out var previous) && previous != e)
throw new InvalidOperationException("Conflicting IK effectors for goal: " + g);
goals[g] = e;
}
foreach (var chain in model.KeyValues.GetSubCollection("ikdata").GetArray("m_IKChains"))
{
var joints = chain.GetArray("m_Joints");
if (joints.Length == 0) continue;
var end = joints[^1].GetSubCollection("m_Bone").GetStringProperty("m_Name");
ends[chain.GetStringProperty("m_Name")] = end;
var target = chain.GetSubCollection("m_DefaultTargetSettings");
if (target.GetStringProperty("m_TargetSource") == "Bone")
Add(target.GetSubCollection("m_Bone").GetStringProperty("m_Name"), end);
}
Visit(Kv3.Parse(graph).Root);
return goals;
void Visit(KvValue value)
{
if (value is KvObject node)
{
if (node.GetString("m_targetType") == "IkTarget_Bone"
&& node.GetString("m_endEffectorType") == "IkEndEffector_Bone"
&& ends.TryGetValue(node.GetString("m_ikChainName") ?? "", out var end))
Add(node.GetString("m_targetBoneName") ?? "", end);
foreach (var key in node.Keys) Visit(node[key]);
}
else if (value is KvArray array)
foreach (var item in array.Items) Visit(item);
}
}
}