Editor/UI/CitizenProfiles.cs
using Editor;
using Sandbox;
using Vec=System.Numerics.Vector3;
using Quat=System.Numerics.Quaternion;
namespace HumanoidRigger.Editor;
/// <summary>Only asset locations are known ahead of time. Hierarchy, bind frames,
/// deform membership and rig settings come from this installation of s&box.</summary>
internal static class CitizenProfiles
{
internal static readonly (string Name,string Path)[] References=[
("Citizen (Complete)","models/citizen/citizen.vmdl"),
("Human Citizen Male (Complete)","models/citizen_human/citizen_human_male.vmdl"),
("Human Citizen Female (Complete)","models/citizen_human/citizen_human_female.vmdl")];
internal static RigProfile Load(string path,string name)
{
var asset=AssetSystem.FindByPath(path)??throw new IOException("Install the Citizen content to use this reference.");
var model=Model.Load(path);if(model.IsError)throw new IOException("The installed reference model could not be loaded.");
string source=File.ReadAllText(asset.AbsolutePath);
string settings=ReferenceModelDoc.Read(source,p=>File.ReadAllText(AssetSystem.FindByPath(p)?.AbsolutePath??throw new IOException("Missing reference prefab: "+p)));
string fbx=Path.Combine(Path.GetDirectoryName(asset.AbsolutePath),Path.GetFileNameWithoutExtension(path)+"_REF.fbx");
var weighted=ReferenceSkin.WeightedBones(File.ReadAllBytes(fbx));
var order=new List<int>();
void Visit(int index)
{
if(order.Contains(index))return;
int parent=model.GetBoneParent(index);if(parent>=0)Visit(parent);
order.Add(index);
}
for(int i=0;i<model.BoneCount;i++)Visit(i);
Quat inverseBasis=new(-.5f,-.5f,-.5f,.5f);
var bones=order.Select(i=>
{
var t=model.GetBoneTransform(i);var q=t.Rotation;string boneName=model.GetBoneName(i);
if(Vector3.DistanceBetween(t.Scale,Vector3.One)>.001f)throw new IOException("Reference bone scale is unsupported: "+boneName);
return new RigBone("",boneName,order.IndexOf(model.GetBoneParent(i)),new Vec(t.Position.y,t.Position.z,t.Position.x)*2.54f,
Quat.Normalize(inverseBasis*new Quat(q.x,q.y,q.z,q.w)),weighted.Contains(boneName));
}).ToArray();
return ReferenceArmature.Create("reference:"+path,name,path,bones,settings,ReferenceModelDoc.Scale(settings));
}
}