Editor helper class that stores UniMate's canonical mapping of raw bone names to labels and per-rig facing info. It lazy-loads a large built-in table from embedded Entries and RigFaceEntries strings, exposes Lookup(name) to get UniMate's label, and RigFacing(...) to identify per-rig left/right facing joints using a SHA1 signature of the rig's normalized names.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace TextToAnimation.Editor.Inference.UniMate;
/// <summary>
/// UniMate's own labels for raw bone names it has seen: every raw name in its published data (UniML3D: 6149 Objaverse
/// rigs, 74 Truebones animals) that its annotation - rule and language model - labelled consistently. A rig whose
/// bones carry such names (Bip01_*, mixamorig:*, Blender .L/.R conventions and the like) gets exactly the label
/// UniMate gave them; the rest go through the rule and <see cref="UniMateNaming"/>. Generated by
/// dev/tools/upstream_prep/make_name_table.py.
/// </summary>
public static partial class UniMateNameTable
{
static Dictionary<string, string> _table;
/// <summary>Replaces the table (tests score the naming on rigs the table was built without).</summary>
public static void Use( Dictionary<string, string> table ) => _table = table;
static Dictionary<string, string> Table
{
get
{
if ( _table is not null ) return _table;
var table = new Dictionary<string, string>( 40000, StringComparer.Ordinal );
foreach ( var line in Entries.Split( '\n' ) )
{
var tab = line.IndexOf( '\t' );
if ( tab > 0 ) table[line[..tab]] = Labels[int.Parse( line[(tab + 1)..] )];
}
return _table = table;
}
}
static Dictionary<string, (string Right, string Left, bool BodyAxis)> _rigFaces;
/// <summary>Turns the per-rig facing off (tests scoring the rules on rigs the table contains).</summary>
public static void NoRigFaces() => _rigFaces = new Dictionary<string, (string, string, bool)>();
static Dictionary<string, (string Right, string Left, bool BodyAxis)> RigFaces
{
get
{
if ( _rigFaces is not null ) return _rigFaces;
var faces = new Dictionary<string, (string, string, bool)>( StringComparer.Ordinal );
foreach ( var line in RigFaceEntries.Split( '\n' ) )
{
var parts = line.Split( '\t' );
if ( parts.Length == 4 ) faces[parts[0]] = (parts[1], parts[2], parts[3] == "1");
}
return _rigFaces = faces;
}
}
/// <summary>
/// When the prepared rig is one of UniMate's own (the same set of joint names), the facing UniMate gave it, as
/// indices into <paramref name="raw"/> (-1, -1 = none); null for a rig UniMate doesn't have.
/// </summary>
public static (int Right, int Left, bool BodyAxis)? RigFacing( IReadOnlyList<string> raw )
{
// the engine's form of each name (its compiler turns symbols other than '_' into '_'); the rig is identified by
// them with index suffixes stripped, its facing joints by their full names
var engine = raw.Select( name => string.Concat( (name ?? "").Select( c => char.IsLetterOrDigit( c ) || c == '_' ? c : '_' ) ) ).ToList();
var keys = engine.Select( name => IndexSuffix.Replace( name, "" ) ).ToList();
var joined = string.Join( "\n", new SortedSet<string>( keys, StringComparer.Ordinal ) );
var hash = System.Security.Cryptography.SHA1.HashData( System.Text.Encoding.UTF8.GetBytes( joined ) );
var signature = Convert.ToHexString( hash ).ToLowerInvariant()[..16];
if ( !RigFaces.TryGetValue( signature, out var face ) ) return null;
if ( face.Right.Length == 0 || face.Left.Length == 0 ) return (-1, -1, false);
int r = engine.IndexOf( face.Right ), l = engine.IndexOf( face.Left );
return r >= 0 && l >= 0 ? (r, l, face.BodyAxis) : null;
}
static readonly Regex IndexSuffix = new( @"(_\d+)+$", RegexOptions.Compiled );
/// <summary>The label UniMate gave this raw bone name, or null when it never saw it (or labelled it inconsistently).</summary>
public static string Lookup( string raw )
{
var key = string.IsNullOrEmpty( raw ) ? "" : IndexSuffix.Replace( raw, "" );
return key.Length > 0 && Table.TryGetValue( key, out var label ) ? label : null;
}
}