From 0675266fac2d1b9b1af9b89b5830bc7efedfed21 Mon Sep 17 00:00:00 2001 From: yum Date: Wed, 19 Feb 2025 02:24:48 -0800 Subject: Fix upload Scripts must be wrapped in `#if UNITY_EDITOR` or build shits the bed. Also document config structs. --- YOTSCore.cs | 33 +++++++++++++++++++++++++++++++-- YOTSNDMFConfig.cs | 11 +++++++++++ YOTSNDMFGenerator.cs | 40 ++++++++++++++++++++++++++++++++++------ 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/YOTSCore.cs b/YOTSCore.cs index 4e0e6ee..a7e275a 100644 --- a/YOTSCore.cs +++ b/YOTSCore.cs @@ -1,3 +1,5 @@ +#if UNITY_EDITOR + using System; using System.Collections.Generic; using System.IO; @@ -13,23 +15,42 @@ namespace YOTS [System.Serializable] public class ToggleSpec { + // The name of the toggle. This is plumbed into the menu, the VRChat + // parameters, and the animator parameters. [SerializeField] public string name; + // The type of toggle. + // Accepted values: + // "toggle" - A boolean toggle. Creates a boolean sync param. + // "radial" - A radial puppet. Creates a float sync param. [SerializeField] public string type = "toggle"; - // Dependencies are evaluated before this one. They must share one or - // more attributes with this spec. + // Dependencies are toggles that will be evaluated before this one. If + // you have two toggles which animate the same thing, one must depend + // on the other. [SerializeField] public List dependencies = new List(); + + // The name of meshes to toggle. + // For example, "Body" or "Shirt". [SerializeField] public List meshToggles = new List(); + + // Blendshapes to animate. [SerializeField] public List blendShapes = 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. [SerializeField] public string menuPath = "/"; + // The default value of the toggle. + // For example, if you want a gimmick to start toggled off, set this to + // 0.0f. [SerializeField] public float defaultValue = 1.0f; @@ -57,15 +78,21 @@ namespace YOTS [System.Serializable] public class BlendShapeSpec { + // The path to the mesh renderer to apply the blend shape to. + // For example, "Body" or "Shirt". [SerializeField] public string path; + // The name of the blend shape to apply. + // For example, "Chest_Hide" or "Boobs+". [SerializeField] public string blendShape; + // The value of the blendshape when the toggle is off. Range from 0-100. [SerializeField] public float offValue = 0.0f; + // The value of the blendshape when the toggle is on. Range from 0-100. [SerializeField] public float onValue = 100.0f; @@ -1028,3 +1055,5 @@ namespace YOTS } } } + +#endif // UNITY_EDITOR diff --git a/YOTSNDMFConfig.cs b/YOTSNDMFConfig.cs index baf92b8..0609d18 100644 --- a/YOTSNDMFConfig.cs +++ b/YOTSNDMFConfig.cs @@ -1,11 +1,22 @@ +#if UNITY_EDITOR + using UnityEngine; +using nadena.dev.ndmf; using VRC.SDK3.Avatars.ScriptableObjects; namespace YOTS { + [DisallowMultipleComponent] public class YOTSNDMFConfig : MonoBehaviour { [Tooltip("The JSON configuration file.")] public TextAsset jsonConfig; + + void OnValidate() + { + gameObject.tag = "EditorOnly"; + } } } + +#endif // UNITY_EDITOR diff --git a/YOTSNDMFGenerator.cs b/YOTSNDMFGenerator.cs index d8dba78..e6e0585 100644 --- a/YOTSNDMFGenerator.cs +++ b/YOTSNDMFGenerator.cs @@ -1,7 +1,10 @@ +#if UNITY_EDITOR + using UnityEngine; using nadena.dev.ndmf; -using nadena.dev.ndmf.VRChat; +using nadena.dev.ndmf.builtin; using nadena.dev.ndmf.localization; +using nadena.dev.ndmf.VRChat; using VRC.SDK3.Avatars.ScriptableObjects; using UnityEditor; using System; @@ -22,13 +25,31 @@ namespace YOTS protected override void Configure() { - // Use a different pass name in play mode to indicate temporary processing. + // First pass: Store config data + InPhase(BuildPhase.Resolving) + .Run("Cache YOTS Config", ctx => { + var config = ctx.AvatarRootObject.GetComponentInChildren(); + if (config == null || config.jsonConfig == null) { + ErrorReport.ReportError(localizer, ErrorSeverity.Error, "yots.error.no_config", + "No YOTS config found on the avatar."); + return; + } + ctx.GetState().jsonConfig = config.jsonConfig.text; + }) + // Shoutsout anatawa12/AvatarOptimizer + .BeforePass(RemoveEditorOnlyPass.Instance); + + // Second pass: Generate animator InPhase(BuildPhase.Transforming) .Run("Generate YOTS Animator", ctx => { - // ctx is a BuildContext - // https://ndmf.nadena.dev/api/nadena.dev.ndmf.BuildContext.html + var state = ctx.GetState(); + if (string.IsNullOrEmpty(state.jsonConfig)) { + ErrorReport.ReportError(localizer, ErrorSeverity.Error, "yots.error.no_config", + "No YOTS config found on the avatar."); + return; + } // Get config - var config = ctx.AvatarRootObject.GetComponentInChildren(); + var config = ctx.GetState(); if (config == null) { ErrorReport.ReportError(localizer, ErrorSeverity.Error, "yots.error.no_config", "No YOTS config component found on the avatar."); @@ -65,7 +86,7 @@ namespace YOTS descriptor.expressionsMenu = menu; descriptor.expressionParameters = parameters; RuntimeAnimatorController generatedAnimator = YOTSCore.GenerateAnimator( - config.jsonConfig.text, + state.jsonConfig, parameters, menu ); @@ -79,5 +100,12 @@ namespace YOTS } ); } + + private class YOTSBuildState + { + public string jsonConfig; + } } } + +#endif // UNITY_EDITOR -- cgit v1.2.3