From 3a51c82762c41c68b1a30d38f710f37f2c9b6e4f Mon Sep 17 00:00:00 2001 From: Lillith Rose Date: Tue, 19 May 2026 13:56:11 -0400 Subject: [PATCH] Move utils out, add dissolve mgr --- Editor/Core.cs | 128 ++++-------------------------- Editor/Utils.cs | 184 +++++++++++++++++++++++++++++++++++++++++++ Editor/Utils.cs.meta | 11 +++ 3 files changed, 210 insertions(+), 113 deletions(-) create mode 100644 Editor/Utils.cs create mode 100644 Editor/Utils.cs.meta diff --git a/Editor/Core.cs b/Editor/Core.cs index 0c4ce1d..1e437da 100644 --- a/Editor/Core.cs +++ b/Editor/Core.cs @@ -7,6 +7,7 @@ using VRC.SDK3.Avatars.Components; using VRC.SDK3.Dynamics.Constraint.Components; using System.Collections.Generic; using UnityEditor.Animations; +using System; namespace gay.lilyy.SoldAvatarBootstrap @@ -21,117 +22,7 @@ namespace gay.lilyy.SoldAvatarBootstrap public AnimatorController fx; public AacFlClip emptyClip; public bool experimentalEnabled; - public bool isPC; - } - - public static class AvatarUtils - { - public static GameObject FindChildRecursive(GameObject parent, string name) - { - return FindChildRecursive(parent.transform, name)?.gameObject; - } - public static Transform FindChildRecursive(Transform parent, string name) - { - return parent.GetComponentsInChildren(true).FirstOrDefault(t => t.gameObject.name == name); - } - public static Transform[] FindChildrenRecursive(Transform parent, string name) - { - return parent.GetComponentsInChildren(true).Where(t => t.gameObject.name == name).ToArray(); - } - - public static AacFlClip CreateConstraintWeightClip(AvatarAssets assets, VRCParentConstraint constraint, int index, int indexCount) - { - return assets.aac.NewClip() - .Animating(action => - { - action.Animates(constraint, $"Sources.source{index}.Weight").WithOneFrame(1f); - for (int i = 0; i < indexCount; i++) - { - if (i == index) continue; - action.Animates(constraint, $"Sources.source{i}.Weight").WithOneFrame(0f); - } - }); - } - - public static AacFlClip CreateEmptyClipWithFrames(AvatarAssets assets, int frames) - { - if (frames == 0) return assets.emptyClip; - var emptyGo = new GameObject(); - emptyGo.name = "_EmptyClipInstant"; - emptyGo.transform.SetParent(assets.root.transform); - var clip = assets.aac.NewClip().Animating(action => - { - action.Animates(emptyGo.transform, "m_LocalScale.z").WithFrameCountUnit(unit => - { - unit.Constant(0, 0); - unit.Constant(frames, 1); - }); - }); - Object.DestroyImmediate(emptyGo); - return clip; - } - - public static AacFlClip CreateEmptyClipWithSeconds(AvatarAssets assets, float seconds) - { - if (seconds == 0) return assets.emptyClip; - var emptyGo = new GameObject(); - emptyGo.name = "_EmptyClipInstant"; - emptyGo.transform.SetParent(assets.root.transform); - var clip = assets.aac.NewClip().Animating(action => - { - action.Animates(emptyGo.transform, "m_LocalScale.z").WithFrameCountUnit(unit => - { - unit.Constant(0, 0); - unit.Constant(seconds, 1); - }); - }); - Object.DestroyImmediate(emptyGo); - return clip; - } - - public static List FindWithBlendshape(AvatarAssets assets, string shapeName) - { - var list = new List(); - foreach (var smr in assets.root.GetComponentsInChildren(true)) - { - var mesh = smr.sharedMesh; - if (!mesh) continue; - - int count = mesh.blendShapeCount; - for (int i = 0; i < count; i++) - { - if (mesh.GetBlendShapeName(i) == shapeName) - { - list.Add(smr); - break; - } - } - } - return list; - } - - public static List FindNamedComponents(Transform parent, string name) where T : Component - { - var list = new List(); - foreach (var component in parent.GetComponentsInChildren(true)) - { - if (component.gameObject.name == name) - { - list.Add(component); - } - } - UnityEngine.Debug.Log($"Found {list.Count} {name} components"); - foreach (var component in list) - { - UnityEngine.Debug.Log($"Component: {component.gameObject.name}"); - } - return list; - } - - public static List FindNamedComponents(AvatarAssets assets, string name) where T : Component - { - return FindNamedComponents(assets.root.transform, name); - } + // public bool isPC; } @@ -214,8 +105,8 @@ namespace gay.lilyy.SoldAvatarBootstrap { root = root, - isPC = EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows - || EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64, + // isPC = EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows + // || EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64, fx = GetController(definition) }; @@ -284,6 +175,17 @@ namespace gay.lilyy.SoldAvatarBootstrap AvatarLogger.LogInfo($"AAC initialization: {initStopwatch.ElapsedMilliseconds}ms"); AvatarLogger.LogInfo($"Layer groups: {totalLayerGroupTime}ms (processed: {processedCount}, skipped: {skippedCount})"); AvatarLogger.LogInfo($"Average per layer group: {(processedCount > 0 ? totalLayerGroupTime / processedCount : 0)}ms"); + + // replace the animator controller + var animatorControllers = root.GetComponent().baseAnimationLayers + .ToList(); + animatorControllers.RemoveAll(layer => layer.type == VRCAvatarDescriptor.AnimLayerType.FX); + animatorControllers.Add(new VRCAvatarDescriptor.CustomAnimLayer + { + type = VRCAvatarDescriptor.AnimLayerType.FX, + animatorController = assets.fx + }); + root.GetComponent().baseAnimationLayers = animatorControllers.ToArray(); } } } \ No newline at end of file diff --git a/Editor/Utils.cs b/Editor/Utils.cs new file mode 100644 index 0000000..e8e6f4b --- /dev/null +++ b/Editor/Utils.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AnimatorAsCode.V1; +using UnityEngine; +using VRC.SDK3.Dynamics.Constraint.Components; + +namespace gay.lilyy.SoldAvatarBootstrap +{ + public static class AvatarUtils + { + public static GameObject FindChildRecursive(GameObject parent, string name) + { + return FindChildRecursive(parent.transform, name)?.gameObject; + } + public static Transform FindChildRecursive(Transform parent, string name) + { + return parent.GetComponentsInChildren(true).FirstOrDefault(t => t.gameObject.name == name); + } + public static Transform[] FindChildrenRecursive(Transform parent, string name) + { + return parent.GetComponentsInChildren(true).Where(t => t.gameObject.name == name).ToArray(); + } + + public static AacFlClip CreateConstraintWeightClip(AvatarAssets assets, VRCParentConstraint constraint, int index, int indexCount) + { + return assets.aac.NewClip() + .Animating(action => + { + action.Animates(constraint, $"Sources.source{index}.Weight").WithOneFrame(1f); + for (int i = 0; i < indexCount; i++) + { + if (i == index) continue; + action.Animates(constraint, $"Sources.source{i}.Weight").WithOneFrame(0f); + } + }); + } + + public static AacFlClip CreateEmptyClipWithFrames(AvatarAssets assets, int frames) + { + if (frames == 0) return assets.emptyClip; + var emptyGo = new GameObject(); + emptyGo.name = "_EmptyClipInstant"; + emptyGo.transform.SetParent(assets.root.transform); + var clip = assets.aac.NewClip().Animating(action => + { + action.Animates(emptyGo.transform, "m_LocalScale.z").WithFrameCountUnit(unit => + { + unit.Constant(0, 0); + unit.Constant(frames, 1); + }); + }); + UnityEngine.Object.DestroyImmediate(emptyGo); + return clip; + } + + public static AacFlClip CreateEmptyClipWithSeconds(AvatarAssets assets, float seconds) + { + if (seconds == 0) return assets.emptyClip; + var emptyGo = new GameObject(); + emptyGo.name = "_EmptyClipInstant"; + emptyGo.transform.SetParent(assets.root.transform); + var clip = assets.aac.NewClip().Animating(action => + { + action.Animates(emptyGo.transform, "m_LocalScale.z").WithFrameCountUnit(unit => + { + unit.Constant(0, 0); + unit.Constant(seconds, 1); + }); + }); + UnityEngine.Object.DestroyImmediate(emptyGo); + return clip; + } + + public static List FindWithBlendshape(AvatarAssets assets, string shapeName) + { + var list = new List(); + foreach (var smr in assets.root.GetComponentsInChildren(true)) + { + var mesh = smr.sharedMesh; + if (!mesh) continue; + + int count = mesh.blendShapeCount; + for (int i = 0; i < count; i++) + { + if (mesh.GetBlendShapeName(i) == shapeName) + { + list.Add(smr); + break; + } + } + } + return list; + } + + public static List FindNamedComponents(Transform parent, string name) where T : Component + { + var list = new List(); + foreach (var component in parent.GetComponentsInChildren(true)) + { + if (component.gameObject.name == name) + { + list.Add(component); + } + } + UnityEngine.Debug.Log($"Found {list.Count} {name} components"); + foreach (var component in list) + { + UnityEngine.Debug.Log($"Component: {component.gameObject.name}"); + } + return list; + } + + public static List FindNamedComponents(AvatarAssets assets, string name) where T : Component + { + return FindNamedComponents(assets.root.transform, name); + } + public static class Dissolve + { + + public class DissolveHooks + { +#nullable enable + public Action? TransitionAnim { get; set; } + public Action? TransitionClip { get; set; } + public Action? OffAnim { get; set; } + public Action? OffClip { get; set; } + public Action? OnAnim { get; set; } + public Action? OnClip { get; set; } +#nullable disable + } + + public static void CreateDissolveLayer(Transform item, string paramStr, AvatarAssets assets, bool isDefault = true, float dissolveSeconds = 1f, DissolveHooks hooks = null) + { + var layer = assets.aac.CreateSupportingArbitraryControllerLayer(assets.fx, paramStr); + var param = layer.BoolParameter(paramStr); + + var mesh = item.GetComponent(); + + var itemGo = item.gameObject; + + var offClip = assets.aac.NewClip().Toggling(itemGo, false); + if (hooks != null && hooks.OffAnim != null) offClip.Animating(hooks.OffAnim); + if (hooks != null && hooks.OffClip != null) hooks.OffClip(offClip); + var onClip = assets.aac.NewClip().Toggling(itemGo, true); + if (hooks != null && hooks.OnAnim != null) onClip.Animating(hooks.OnAnim); + if (hooks != null && hooks.OnClip != null) hooks.OnClip(onClip); + + var off = layer.NewState("Off").WithAnimation(offClip); + var on = layer.NewState("On").WithAnimation(onClip); + + // if (assets.isPC) + // { + var transitionClip = assets.aac.NewClip().Animating(anim => + { + if (hooks != null && hooks.TransitionAnim != null) hooks.TransitionAnim(anim); + anim.Animates(itemGo).WithOneFrame(1f); + anim.Animates(mesh, "material._DissolveAlpha").WithSecondsUnit(dissolve => + { + dissolve.Easing(0f, 0f); + dissolve.Easing(dissolveSeconds, 1f); + }); + }); + if (hooks != null && hooks.TransitionClip != null) hooks.TransitionClip(transitionClip); + + var onToOff = layer.NewState("On To Off").WithAnimation(transitionClip).WithSpeedSetTo(1); + var offToOn = layer.NewState("Off To On").WithAnimation(transitionClip).WithSpeedSetTo(-1); + + on.TransitionsTo(onToOff).When(param.IsFalse()); + onToOff.TransitionsTo(off).AfterAnimationFinishes(); + off.TransitionsTo(offToOn).When(param.IsTrue()); + offToOn.TransitionsTo(on).AfterAnimationFinishes(); + } + // else + // { + // on.TransitionsTo(off).When(param.IsFalse()); + // off.TransitionsTo(on).When(param.IsTrue()); + // } + + // layer.WithDefaultState(isDefault ? on : off); + // } + } + } +} \ No newline at end of file diff --git a/Editor/Utils.cs.meta b/Editor/Utils.cs.meta new file mode 100644 index 0000000..9c9dc96 --- /dev/null +++ b/Editor/Utils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95da9d2be3d2ac0409c4cecf148ecfcb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: