SoldAvatarBootstrap/Editor/Utils.cs
2026-05-19 13:56:11 -04:00

184 lines
No EOL
6.5 KiB
C#

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<Transform>(true).FirstOrDefault(t => t.gameObject.name == name);
}
public static Transform[] FindChildrenRecursive(Transform parent, string name)
{
return parent.GetComponentsInChildren<Transform>(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<SkinnedMeshRenderer> FindWithBlendshape(AvatarAssets assets, string shapeName)
{
var list = new List<SkinnedMeshRenderer>();
foreach (var smr in assets.root.GetComponentsInChildren<SkinnedMeshRenderer>(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<T> FindNamedComponents<T>(Transform parent, string name) where T : Component
{
var list = new List<T>();
foreach (var component in parent.GetComponentsInChildren<T>(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<T> FindNamedComponents<T>(AvatarAssets assets, string name) where T : Component
{
return FindNamedComponents<T>(assets.root.transform, name);
}
public static class Dissolve
{
public class DissolveHooks
{
#nullable enable
public Action<AacFlEditClip>? TransitionAnim { get; set; }
public Action<AacFlClip>? TransitionClip { get; set; }
public Action<AacFlEditClip>? OffAnim { get; set; }
public Action<AacFlClip>? OffClip { get; set; }
public Action<AacFlEditClip>? OnAnim { get; set; }
public Action<AacFlClip>? 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<SkinnedMeshRenderer>();
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);
// }
}
}
}