Code/Target/HandModelFactory.cs
#nullable enable
namespace HumanoidHandRetargeter.Target;
/// <summary>Minimal hand ModelDoc using the audited VmdlWriter mesh and scaling node shapes.</summary>
public static class HandModelFactory
{
public const string Header = "<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:modeldoc30:version{8c2d7a91-9c42-4bf0-883a-5a3b1762d4f1} -->";
public static string Create(string baseModel = "", string mesh = "", float meshUnitScaleCm = 1, IReadOnlyDictionary<string,string>? materialRemaps = null, IReadOnlyList<string>? meshNames = null)
{
if (!float.IsFinite(meshUnitScaleCm) || meshUnitScaleCm <= 0) throw new ArgumentOutOfRangeException(nameof(meshUnitScaleCm));
if (string.IsNullOrWhiteSpace(baseModel) == string.IsNullOrWhiteSpace(mesh)) throw new ArgumentException("Choose one base model or mesh source.");
var children = new KvArray();
if (mesh.Length > 0)
{
mesh = VmdlSetupService.NormalizeAssetPath(mesh, ".fbx");
var meshes = new KvArray();
meshes.Items.Add(new KvObject { ["_class"] = new KvString("RenderMeshFile"), ["name"] = new KvString("hands_mesh"),
["filename"] = new KvString(mesh), ["import_scale"] = new KvDouble(meshUnitScaleCm),
["import_translation"] = Vector(0,0,0), ["import_rotation"] = Vector(0,0,0),
["align_origin_x_type"] = new KvString("None"), ["align_origin_y_type"] = new KvString("None"), ["align_origin_z_type"] = new KvString("None") });
if(meshNames is {Count:>0})
{
var names=new KvArray();foreach(var name in meshNames)names.Items.Add(new KvString(name));
((KvObject)meshes.Items[0])["import_filter"]=new KvObject
{["exclude_by_default"]=new KvBool(true),["exception_list"]=names};
}
children.Items.Add(new KvObject { ["_class"] = new KvString("RenderMeshList"), ["children"] = meshes });
children.Items.Add(new KvObject { ["_class"] = new KvString("BoneMarkupList"), ["bone_cull_type"] = new KvString("None"), ["children"] = new KvArray() });
var remaps=new KvArray();
if(materialRemaps is not null)foreach(var pair in materialRemaps)remaps.Items.Add(new KvObject{["from"]=new KvString(pair.Key),["to"]=new KvString(pair.Value)});
var materials = new KvArray();
materials.Items.Add(new KvObject { ["_class"] = new KvString("DefaultMaterialGroup"), ["use_global_default"] = new KvBool(materialRemaps is null || materialRemaps.Count==0),
["global_default_material"] = new KvString("materials/dev/reflectivity_50.vmat"), ["remaps"] = remaps });
children.Items.Add(new KvObject { ["_class"] = new KvString("MaterialGroupList"), ["children"] = materials });
}
else baseModel = VmdlSetupService.NormalizeAssetPath(baseModel, ".vmdl");
var modifiers = new KvArray();
modifiers.Items.Add(new KvObject { ["_class"] = new KvString("ModelModifier_ScaleAndMirror"), ["scale"] = new KvDouble(1.0 / 2.54),
["mirror_x"] = new KvBool(false), ["mirror_y"] = new KvBool(false), ["mirror_z"] = new KvBool(false),
["flip_bone_forward"] = new KvBool(false), ["swap_left_and_right_bones"] = new KvBool(false) });
children.Items.Add(new KvObject { ["_class"] = new KvString("ModelModifierList"), ["children"] = modifiers });
var root = new KvObject { ["_class"] = new KvString("RootNode"), ["children"] = children, ["base_model_name"] = new KvString(baseModel),
["anim_graph_name"] = new KvString(""), ["model_archetype"] = new KvString(""), ["primary_associated_entity"] = new KvString("") };
return Kv3.Serialize(new Kv3Document(Header, new KvObject { ["rootNode"] = root }));
}
private static KvArray Vector(params double[] values) { var array = new KvArray(); foreach (var value in values) array.Items.Add(new KvDouble(value)); return array; }
}