From e0127987aff3758fcccde1e855561b3d6177c87b Mon Sep 17 00:00:00 2001 From: yum Date: Tue, 3 Mar 2026 19:13:06 -0800 Subject: Names are now resolved at build time Lets modules reference each other's meshes without knowing the module's top-level name. --- Scripts/YOTSCore.cs | 7 +++---- Scripts/YOTSNDMFGenerator.cs | 48 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) (limited to 'Scripts') diff --git a/Scripts/YOTSCore.cs b/Scripts/YOTSCore.cs index 0fc8ccb..4421607 100644 --- a/Scripts/YOTSCore.cs +++ b/Scripts/YOTSCore.cs @@ -59,11 +59,10 @@ namespace YOTS [SerializeField] public List externalAnimations = new List(); - // Where to put the toggle in the menu. All toggles are placed under - // /YOTS. So if you put "Clothes" here, it'll be placed under - // /YOTS/Clothes. + // Where to put the toggle in the menu. Defaults to the top-level menu. + // For example, if you put "Clothes" here, it'll be placed under /Clothes. [SerializeField] - public string menuPath = "/YOTS"; + public string menuPath = "/"; // The default value of the toggle. Range from 0-1. // For example, if you want a gimmick to start toggled off, set this to diff --git a/Scripts/YOTSNDMFGenerator.cs b/Scripts/YOTSNDMFGenerator.cs index 4471fa1..8a2faac 100644 --- a/Scripts/YOTSNDMFGenerator.cs +++ b/Scripts/YOTSNDMFGenerator.cs @@ -104,11 +104,14 @@ namespace YOTS descriptor.expressionsMenu = menu; descriptor.expressionParameters = parameters; + // Resolve bare mesh names to full hierarchy paths. + var resolvedJson = ResolveMeshNames(config.jsonConfig, ctx.AvatarRootObject.transform); + // Generate the YOTS animator. RuntimeAnimatorController generatedAnimator = null; try { generatedAnimator = YOTSCore.GenerateAnimator( - config.jsonConfig, + resolvedJson, parameters, menu ); @@ -172,6 +175,49 @@ namespace YOTS public bool skipGeneration; } + // Resolve bare mesh names (no '/') in meshToggles/inverseMeshToggles to all + // matching hierarchy paths. Names containing '/' are kept as explicit paths. + private static string ResolveMeshNames(string jsonConfig, Transform avatarRoot) { + var config = JsonUtility.FromJson(jsonConfig); + var nameToPathsMap = BuildNameToPathsMap(avatarRoot); + foreach (var toggle in config.toggles) { + toggle.meshToggles = ExpandMeshNames(toggle.meshToggles, nameToPathsMap); + toggle.inverseMeshToggles = ExpandMeshNames(toggle.inverseMeshToggles, nameToPathsMap); + } + return JsonUtility.ToJson(config); + } + + private static List ExpandMeshNames(List names, Dictionary> nameToPathsMap) { + if (names == null) return null; + var resolved = new List(); + foreach (var name in names) { + if (name.Contains('/')) { + resolved.Add(name); + } else if (nameToPathsMap.TryGetValue(name, out var paths)) { + resolved.AddRange(paths); + } else { + resolved.Add(name); + } + } + return resolved; + } + + private static Dictionary> BuildNameToPathsMap(Transform root) { + var map = new Dictionary>(); + CollectPaths(root, "", map); + return map; + } + + private static void CollectPaths(Transform current, string currentPath, Dictionary> map) { + foreach (Transform child in current) { + string childPath = currentPath == "" ? child.name : currentPath + "/" + child.name; + if (!map.ContainsKey(child.name)) + map[child.name] = new List(); + map[child.name].Add(childPath); + CollectPaths(child, childPath, map); + } + } + private static VRCExpressionsMenu DeepCopyMenu(VRCExpressionsMenu sourceMenu) { var copiedMenu = UnityEngine.Object.Instantiate(sourceMenu); // Deep copy all submenu references -- cgit v1.2.3