Move utils out, add dissolve mgr
This commit is contained in:
parent
65f7206b46
commit
3a51c82762
3 changed files with 210 additions and 113 deletions
128
Editor/Core.cs
128
Editor/Core.cs
|
|
@ -7,6 +7,7 @@ using VRC.SDK3.Avatars.Components;
|
||||||
using VRC.SDK3.Dynamics.Constraint.Components;
|
using VRC.SDK3.Dynamics.Constraint.Components;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEditor.Animations;
|
using UnityEditor.Animations;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
namespace gay.lilyy.SoldAvatarBootstrap
|
namespace gay.lilyy.SoldAvatarBootstrap
|
||||||
|
|
@ -21,117 +22,7 @@ namespace gay.lilyy.SoldAvatarBootstrap
|
||||||
public AnimatorController fx;
|
public AnimatorController fx;
|
||||||
public AacFlClip emptyClip;
|
public AacFlClip emptyClip;
|
||||||
public bool experimentalEnabled;
|
public bool experimentalEnabled;
|
||||||
public bool isPC;
|
// 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<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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
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<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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -214,8 +105,8 @@ namespace gay.lilyy.SoldAvatarBootstrap
|
||||||
{
|
{
|
||||||
root = root,
|
root = root,
|
||||||
|
|
||||||
isPC = EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows
|
// isPC = EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows
|
||||||
|| EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64,
|
// || EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64,
|
||||||
|
|
||||||
fx = GetController(definition)
|
fx = GetController(definition)
|
||||||
};
|
};
|
||||||
|
|
@ -284,6 +175,17 @@ namespace gay.lilyy.SoldAvatarBootstrap
|
||||||
AvatarLogger.LogInfo($"AAC initialization: {initStopwatch.ElapsedMilliseconds}ms");
|
AvatarLogger.LogInfo($"AAC initialization: {initStopwatch.ElapsedMilliseconds}ms");
|
||||||
AvatarLogger.LogInfo($"Layer groups: {totalLayerGroupTime}ms (processed: {processedCount}, skipped: {skippedCount})");
|
AvatarLogger.LogInfo($"Layer groups: {totalLayerGroupTime}ms (processed: {processedCount}, skipped: {skippedCount})");
|
||||||
AvatarLogger.LogInfo($"Average per layer group: {(processedCount > 0 ? totalLayerGroupTime / processedCount : 0)}ms");
|
AvatarLogger.LogInfo($"Average per layer group: {(processedCount > 0 ? totalLayerGroupTime / processedCount : 0)}ms");
|
||||||
|
|
||||||
|
// replace the animator controller
|
||||||
|
var animatorControllers = root.GetComponent<VRCAvatarDescriptor>().baseAnimationLayers
|
||||||
|
.ToList();
|
||||||
|
animatorControllers.RemoveAll(layer => layer.type == VRCAvatarDescriptor.AnimLayerType.FX);
|
||||||
|
animatorControllers.Add(new VRCAvatarDescriptor.CustomAnimLayer
|
||||||
|
{
|
||||||
|
type = VRCAvatarDescriptor.AnimLayerType.FX,
|
||||||
|
animatorController = assets.fx
|
||||||
|
});
|
||||||
|
root.GetComponent<VRCAvatarDescriptor>().baseAnimationLayers = animatorControllers.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
184
Editor/Utils.cs
Normal file
184
Editor/Utils.cs
Normal file
|
|
@ -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<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);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Editor/Utils.cs.meta
Normal file
11
Editor/Utils.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 95da9d2be3d2ac0409c4cecf148ecfcb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Loading…
Add table
Add a link
Reference in a new issue