Code/Target/HandAnimGraph.cs
#nullable enable
namespace HumanoidHandRetargeter.Target;
/// <summary>Minimal standalone idle/bind graph using the shipped sequence-to-root graph shape.
/// Weapon reload/fire state machines remain owned by the weapon graph.</summary>
public static class HandAnimGraph
{
public static string Create(string sequenceName, string modelPath)
{
ArgumentException.ThrowIfNullOrWhiteSpace(sequenceName);
modelPath = VmdlSetupService.NormalizeAssetPath(modelPath, ".vmdl");
var doc = Kv3.Parse(Template);
var root = (KvObject)doc.Root;
var nodes = (KvArray)((KvObject)root["m_nodeManager"])["m_nodes"];
var sequence = nodes.Items.Cast<KvObject>().Select(n => (KvObject)n["value"])
.Single(n => n.GetString("_class") == "CSequenceAnimNode");
sequence["m_sequenceName"] = new KvString(sequenceName);
var models = new KvArray();
models.Items.Add(new KvString(modelPath));
root["m_previewModels"] = models;
return Kv3.Serialize(doc);
}
// Schema inspected from Facepunch's shipped easter_bonnet_anim.vanmgrph.
private const string Template = """
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:animgraph2:version{0f7898b8-5471-45c4-9867-cd9c46bcfdb5} -->
{
_class = "CAnimationGraph"
m_nodeManager =
{
_class = "CAnimNodeManager"
m_nodes =
[
{
key =
{
m_id = 1120153906
}
value =
{
_class = "CRootAnimNode"
m_sName = "Unnamed"
m_vecPosition = [ 124.0, -136.0 ]
m_nNodeID =
{
m_id = 1120153906
}
m_sNote = ""
m_inputConnection =
{
m_nodeID =
{
m_id = 1270187183
}
m_outputID =
{
m_id = 4294967295
}
}
}
},
{
key =
{
m_id = 1270187183
}
value =
{
_class = "CSequenceAnimNode"
m_sName = "Unnamed"
m_vecPosition = [ -157.0, -148.0 ]
m_nNodeID =
{
m_id = 1270187183
}
m_sNote = ""
m_tagSpans = [ ]
m_sequenceName = "bindPose"
m_playbackSpeed = 1.0
m_bLoop = true
}
},
]
}
m_pParameterList =
{
_class = "CAnimParameterList"
m_Parameters = [ ]
}
m_pTagManager =
{
_class = "CAnimTagManager"
m_tags = [ ]
}
m_pMovementManager =
{
_class = "CAnimMovementManager"
m_MotorList =
{
_class = "CAnimMotorList"
m_motors = [ ]
}
m_MovementSettings =
{
_class = "CAnimMovementSettings"
m_bShouldCalculateSlope = false
}
}
m_pSettingsManager =
{
_class = "CAnimGraphSettingsManager"
m_settingsGroups =
[
{
_class = "CAnimGraphGeneralSettings"
m_iGridSnap = 16
},
]
}
m_pActivityValuesList =
{
_class = "CActivityValueList"
m_activities = [ ]
}
m_previewModels =
[
"models/hands.vmdl",
]
m_boneMergeModels = [ ]
m_cameraSettings =
{
m_flFov = 60.0
m_sLockBoneName = ""
m_bLockCamera = false
m_bViewModelCamera = false
}
}
""";
}