- Ensure backwards compatibility by creating an anomalous clip to be used by default on animators
685 lines
30 KiB
C#
685 lines
30 KiB
C#
using System;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEditor.Animations;
|
|
using UnityEngine;
|
|
using VRC.SDK3.Avatars.Components;
|
|
using Random = UnityEngine.Random;
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace AnimatorAsCode.V0
|
|
{
|
|
public static class AacV0
|
|
{
|
|
public static AacFlBase Create(AacConfiguration configuration)
|
|
{
|
|
return new AacFlBase(configuration);
|
|
}
|
|
|
|
internal static AnimatorController AnimatorOf(VRCAvatarDescriptor ad, VRCAvatarDescriptor.AnimLayerType animLayerType)
|
|
{
|
|
return (AnimatorController) ad.baseAnimationLayers.First(it => it.type == animLayerType).animatorController;
|
|
}
|
|
|
|
internal static AnimationClip NewClip(AacConfiguration component, string suffix)
|
|
{
|
|
return RegisterClip(component, suffix, new AnimationClip());
|
|
}
|
|
|
|
internal static AnimationClip RegisterClip(AacConfiguration component, string suffix, AnimationClip clip)
|
|
{
|
|
clip.name = "zAutogenerated__" + component.AssetKey + "__" + suffix + "_" + Random.Range(0, Int32.MaxValue); // FIXME animation name conflict
|
|
clip.hideFlags = HideFlags.None;
|
|
AssetDatabase.AddObjectToAsset(clip, component.AssetContainer);
|
|
return clip;
|
|
}
|
|
|
|
internal static BlendTree NewBlendTreeAsRaw(AacConfiguration component, string suffix)
|
|
{
|
|
var clip = new BlendTree();
|
|
clip.name = "zAutogenerated__" + component.AssetKey + "__" + suffix + "_" + Random.Range(0, Int32.MaxValue); // FIXME animation name conflict
|
|
clip.hideFlags = HideFlags.None;
|
|
AssetDatabase.AddObjectToAsset(clip, component.AssetContainer);
|
|
return clip;
|
|
}
|
|
|
|
internal static EditorCurveBinding Binding(AacConfiguration component, Type type, Transform transform, string propertyName)
|
|
{
|
|
return new EditorCurveBinding
|
|
{
|
|
path = ResolveRelativePath(component.AnimatorRoot, transform),
|
|
type = type,
|
|
propertyName = propertyName
|
|
};
|
|
}
|
|
|
|
internal static AnimationCurve OneFrame(float desiredValue)
|
|
{
|
|
return AnimationCurve.Constant(0f, 1 / 60f, desiredValue);
|
|
}
|
|
|
|
internal static AnimationCurve ConstantSeconds(float seconds, float desiredValue)
|
|
{
|
|
return AnimationCurve.Constant(0f, seconds, desiredValue);
|
|
}
|
|
|
|
internal static string ResolveRelativePath(Transform avatar, Transform item)
|
|
{
|
|
if (item.parent != avatar && item.parent != null)
|
|
{
|
|
return ResolveRelativePath(avatar, item.parent) + "/" + item.name;
|
|
}
|
|
|
|
return item.name;
|
|
}
|
|
|
|
internal static EditorCurveBinding ToSubBinding(EditorCurveBinding binding, string suffix)
|
|
{
|
|
return new EditorCurveBinding {path = binding.path, type = binding.type, propertyName = binding.propertyName + "." + suffix};
|
|
}
|
|
}
|
|
|
|
public struct AacConfiguration
|
|
{
|
|
public string SystemName;
|
|
public VRCAvatarDescriptor AvatarDescriptor;
|
|
public Transform AnimatorRoot;
|
|
public Transform DefaultValueRoot;
|
|
public AnimatorController AssetContainer;
|
|
public string AssetKey;
|
|
public IAacDefaultsProvider DefaultsProvider;
|
|
}
|
|
|
|
public struct AacFlLayer
|
|
{
|
|
private readonly AnimatorController _animatorController;
|
|
private readonly AacConfiguration _configuration;
|
|
private readonly string _fullLayerName;
|
|
private readonly AacFlStateMachine _stateMachine;
|
|
|
|
internal AacFlLayer(AnimatorController animatorController, AacConfiguration configuration, AacFlStateMachine stateMachine, string fullLayerName)
|
|
{
|
|
_animatorController = animatorController;
|
|
_configuration = configuration;
|
|
_fullLayerName = fullLayerName;
|
|
_stateMachine = stateMachine;
|
|
}
|
|
|
|
public AacFlState NewState(string name)
|
|
{
|
|
var lastState = _stateMachine.LastNodePosition();
|
|
var state = _stateMachine.NewState(name, 0, 0).Shift(lastState, 0, 1);
|
|
return state;
|
|
}
|
|
|
|
public AacFlState NewState(string name, int x, int y)
|
|
{
|
|
return _stateMachine.NewState(name, x, y);
|
|
}
|
|
|
|
public AacFlStateMachine NewSubStateMachine(string name)
|
|
{
|
|
return _stateMachine.NewSubStateMachine(name);
|
|
}
|
|
|
|
public AacFlStateMachine NewSubStateMachine(string name, int x, int y)
|
|
{
|
|
return _stateMachine.NewSubStateMachine(name, x, y);
|
|
}
|
|
|
|
public AacFlTransition AnyTransitionsTo(AacFlState destination)
|
|
{
|
|
return _stateMachine.AnyTransitionsTo(destination);
|
|
}
|
|
|
|
public AacFlEntryTransition EntryTransitionsTo(AacFlState destination)
|
|
{
|
|
return _stateMachine.EntryTransitionsTo(destination);
|
|
}
|
|
|
|
public AacFlEntryTransition EntryTransitionsTo(AacFlStateMachine destination)
|
|
{
|
|
return _stateMachine.EntryTransitionsTo(destination);
|
|
}
|
|
|
|
public AacFlBoolParameter BoolParameter(string parameterName) => _stateMachine.BackingAnimator().BoolParameter(parameterName);
|
|
public AacFlBoolParameter TriggerParameterAsBool(string parameterName) => _stateMachine.BackingAnimator().TriggerParameter(parameterName);
|
|
public AacFlFloatParameter FloatParameter(string parameterName) => _stateMachine.BackingAnimator().FloatParameter(parameterName);
|
|
public AacFlIntParameter IntParameter(string parameterName) => _stateMachine.BackingAnimator().IntParameter(parameterName);
|
|
public AacFlBoolParameterGroup BoolParameters(params string[] parameterNames) => _stateMachine.BackingAnimator().BoolParameters(parameterNames);
|
|
public AacFlBoolParameterGroup TriggerParametersAsBools(params string[] parameterNames) => _stateMachine.BackingAnimator().TriggerParameters(parameterNames);
|
|
public AacFlFloatParameterGroup FloatParameters(params string[] parameterNames) => _stateMachine.BackingAnimator().FloatParameters(parameterNames);
|
|
public AacFlIntParameterGroup IntParameters(params string[] parameterNames) => _stateMachine.BackingAnimator().IntParameters(parameterNames);
|
|
public AacFlBoolParameterGroup BoolParameters(params AacFlBoolParameter[] parameters) => _stateMachine.BackingAnimator().BoolParameters(parameters);
|
|
public AacFlBoolParameterGroup TriggerParametersAsBools(params AacFlBoolParameter[] parameters) => _stateMachine.BackingAnimator().TriggerParameters(parameters);
|
|
public AacFlFloatParameterGroup FloatParameters(params AacFlFloatParameter[] parameters) => _stateMachine.BackingAnimator().FloatParameters(parameters);
|
|
public AacFlIntParameterGroup IntParameters(params AacFlIntParameter[] parameters) => _stateMachine.BackingAnimator().IntParameters(parameters);
|
|
public AacAv3 Av3() => new AacAv3(_stateMachine.BackingAnimator());
|
|
|
|
public AacFlLayer OverrideValue(AacFlBoolParameter toBeForced, bool value)
|
|
{
|
|
var parameters = _animatorController.parameters;
|
|
foreach (var param in parameters)
|
|
{
|
|
if (param.name == toBeForced.Name)
|
|
{
|
|
param.defaultBool = value;
|
|
}
|
|
}
|
|
|
|
_animatorController.parameters = parameters;
|
|
|
|
return this;
|
|
}
|
|
|
|
public AacFlLayer OverrideValue(AacFlFloatParameter toBeForced, float value)
|
|
{
|
|
var parameters = _animatorController.parameters;
|
|
foreach (var param in parameters)
|
|
{
|
|
if (param.name == toBeForced.Name)
|
|
{
|
|
param.defaultFloat = value;
|
|
}
|
|
}
|
|
|
|
_animatorController.parameters = parameters;
|
|
|
|
return this;
|
|
}
|
|
|
|
public AacFlLayer OverrideValue(AacFlIntParameter toBeForced, int value)
|
|
{
|
|
var parameters = _animatorController.parameters;
|
|
foreach (var param in parameters)
|
|
{
|
|
if (param.name == toBeForced.Name)
|
|
{
|
|
param.defaultInt = value;
|
|
}
|
|
}
|
|
|
|
_animatorController.parameters = parameters;
|
|
|
|
return this;
|
|
}
|
|
|
|
public AacFlLayer WithAvatarMask(AvatarMask avatarMask)
|
|
{
|
|
var finalFullLayerName = _fullLayerName;
|
|
_animatorController.layers = _animatorController.layers
|
|
.Select(layer =>
|
|
{
|
|
if (layer.name == finalFullLayerName)
|
|
{
|
|
layer.avatarMask = avatarMask;
|
|
}
|
|
|
|
return layer;
|
|
})
|
|
.ToArray();
|
|
|
|
return this;
|
|
}
|
|
|
|
public AacFlLayer WithAvatarMaskNoTransforms()
|
|
{
|
|
ResolveAvatarMask(new Transform[0]);
|
|
|
|
return this;
|
|
}
|
|
|
|
public AacFlLayer ResolveAvatarMask(Transform[] paths)
|
|
{
|
|
// FIXME: Fragile
|
|
var avatarMask = new AvatarMask();
|
|
avatarMask.name = "zAutogenerated__" + _configuration.AssetKey + "_" + _fullLayerName + "__AvatarMask";
|
|
avatarMask.hideFlags = HideFlags.None;
|
|
|
|
if (paths.Length == 0)
|
|
{
|
|
avatarMask.transformCount = 1;
|
|
avatarMask.SetTransformActive(0, false);
|
|
avatarMask.SetTransformPath(0, "_ignored");
|
|
}
|
|
else
|
|
{
|
|
avatarMask.transformCount = paths.Length;
|
|
for (var index = 0; index < paths.Length; index++)
|
|
{
|
|
var transform = paths[index];
|
|
avatarMask.SetTransformActive(index, true);
|
|
avatarMask.SetTransformPath(index, AacV0.ResolveRelativePath(_configuration.AnimatorRoot, transform));
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < (int) AvatarMaskBodyPart.LastBodyPart; i++)
|
|
{
|
|
avatarMask.SetHumanoidBodyPartActive((AvatarMaskBodyPart) i, false);
|
|
}
|
|
|
|
AssetDatabase.AddObjectToAsset(avatarMask, _animatorController);
|
|
|
|
WithAvatarMask(avatarMask);
|
|
|
|
return this;
|
|
}
|
|
|
|
public AacFlLayer WithDefaultState(AacFlState newDefaultState)
|
|
{
|
|
_stateMachine.WithDefaultState(newDefaultState);
|
|
return this;
|
|
}
|
|
}
|
|
|
|
public class AacFlBase
|
|
{
|
|
private readonly AacConfiguration _configuration;
|
|
|
|
internal AacFlBase(AacConfiguration configuration)
|
|
{
|
|
_configuration = configuration;
|
|
}
|
|
|
|
public AacFlClip NewClip()
|
|
{
|
|
var clip = AacV0.NewClip(_configuration, Guid.NewGuid().ToString());
|
|
return new AacFlClip(_configuration, clip);
|
|
}
|
|
|
|
public AacFlClip CopyClip(AnimationClip originalClip)
|
|
{
|
|
var newClip = UnityEngine.Object.Instantiate(originalClip);
|
|
var clip = AacV0.RegisterClip(_configuration, Guid.NewGuid().ToString(), newClip);
|
|
return new AacFlClip(_configuration, clip);
|
|
}
|
|
|
|
public BlendTree NewBlendTreeAsRaw()
|
|
{
|
|
return AacV0.NewBlendTreeAsRaw(_configuration, Guid.NewGuid().ToString());
|
|
}
|
|
|
|
public AacFlClip NewClip(string name)
|
|
{
|
|
var clip = AacV0.NewClip(_configuration, name);
|
|
return new AacFlClip(_configuration, clip);
|
|
}
|
|
|
|
public AacFlClip DummyClipLasting(float numberOf, AacFlUnit unit)
|
|
{
|
|
var dummyClip = AacV0.NewClip(_configuration, $"D({numberOf} {Enum.GetName(typeof(AacFlUnit), unit)})");
|
|
|
|
return new AacFlClip(_configuration, dummyClip)
|
|
.Animating(clip => clip.Animates("_ignored", typeof(GameObject), "m_IsActive")
|
|
.WithUnit(unit, keyframes => keyframes.Constant(0, 0f).Constant(numberOf, 0f)));
|
|
}
|
|
|
|
// For backwards compatibility, generates an animation with 2 keyframes that are at a distance of 1 / 60 of 1 frame,
|
|
// that is 1 / 3600 seconds.
|
|
// This was due to DummyClipLasting returning a clip with unit conversion applied twice,
|
|
// resulting in an "undesired" animation which was functional anyways.
|
|
// Since this dummy clip is used internally on all uninitalized states,
|
|
// preserve this behaviour so that existing animators don't break due to this change.
|
|
protected AacFlClip AnomalousSingleKeyframeClip()
|
|
{
|
|
var numberOf = 1f;
|
|
var unit = AacFlUnit.Frames;
|
|
|
|
var dummyClip = AacV0.NewClip(_configuration, $"D({numberOf} {Enum.GetName(typeof(AacFlUnit), unit)})");
|
|
|
|
var duration = unit == AacFlUnit.Frames ? numberOf / 60f : numberOf;
|
|
return new AacFlClip(_configuration, dummyClip)
|
|
.Animating(clip => clip.Animates("_ignored", typeof(GameObject), "m_IsActive")
|
|
.WithUnit(unit, keyframes => keyframes.Constant(0, 0f).Constant(duration, 0f)));
|
|
}
|
|
|
|
public void RemoveAllMainLayers()
|
|
{
|
|
var layerName = _configuration.SystemName;
|
|
RemoveLayerOnAllControllers(_configuration.DefaultsProvider.ConvertLayerName(layerName));
|
|
}
|
|
|
|
public void RemoveAllSupportingLayers(string suffix)
|
|
{
|
|
var layerName = _configuration.SystemName;
|
|
RemoveLayerOnAllControllers(_configuration.DefaultsProvider.ConvertLayerNameWithSuffix(layerName, suffix));
|
|
}
|
|
|
|
private void RemoveLayerOnAllControllers(string layerName)
|
|
{
|
|
var layers = _configuration.AvatarDescriptor.baseAnimationLayers.Select(layer => layer.animatorController).Where(layer => layer != null).Distinct().ToList();
|
|
foreach (var customAnimLayer in layers)
|
|
{
|
|
new AacAnimatorRemoval((AnimatorController) customAnimLayer).RemoveLayer(_configuration.DefaultsProvider.ConvertLayerName(layerName));
|
|
}
|
|
}
|
|
|
|
public AacFlLayer CreateMainFxLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.FX);
|
|
public AacFlLayer CreateMainGestureLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.Gesture);
|
|
public AacFlLayer CreateMainActionLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.Action);
|
|
public AacFlLayer CreateMainIdleLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.Additive);
|
|
public AacFlLayer CreateMainLocomotionLayer() => DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType.Base);
|
|
public AacFlLayer CreateMainAv3Layer(VRCAvatarDescriptor.AnimLayerType animLayerType) => DoCreateMainLayerOnController(animLayerType);
|
|
|
|
public AacFlLayer CreateSupportingFxLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.FX, suffix);
|
|
public AacFlLayer CreateSupportingGestureLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.Gesture, suffix);
|
|
public AacFlLayer CreateSupportingActionLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.Action, suffix);
|
|
public AacFlLayer CreateSupportingIdleLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.Additive, suffix);
|
|
public AacFlLayer CreateSupportingLocomotionLayer(string suffix) => DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType.Base, suffix);
|
|
public AacFlLayer CreateSupportingAv3Layer(VRCAvatarDescriptor.AnimLayerType animLayerType, string suffix) => DoCreateSupportingLayerOnController(animLayerType, suffix);
|
|
|
|
public AacFlLayer CreateMainArbitraryControllerLayer(AnimatorController controller) => DoCreateLayer(controller, _configuration.DefaultsProvider.ConvertLayerName(_configuration.SystemName));
|
|
public AacFlLayer CreateSupportingArbitraryControllerLayer(AnimatorController controller, string suffix) => DoCreateLayer(controller, _configuration.DefaultsProvider.ConvertLayerNameWithSuffix(_configuration.SystemName, suffix));
|
|
public AacFlLayer CreateFirstArbitraryControllerLayer(AnimatorController controller) => DoCreateLayer(controller, controller.layers[0].name);
|
|
|
|
private AacFlLayer DoCreateMainLayerOnController(VRCAvatarDescriptor.AnimLayerType animType)
|
|
{
|
|
var animator = AacV0.AnimatorOf(_configuration.AvatarDescriptor, animType);
|
|
var layerName = _configuration.DefaultsProvider.ConvertLayerName(_configuration.SystemName);
|
|
|
|
return DoCreateLayer(animator, layerName);
|
|
}
|
|
|
|
private AacFlLayer DoCreateSupportingLayerOnController(VRCAvatarDescriptor.AnimLayerType animType, string suffix)
|
|
{
|
|
var animator = AacV0.AnimatorOf(_configuration.AvatarDescriptor, animType);
|
|
var layerName = _configuration.DefaultsProvider.ConvertLayerNameWithSuffix(_configuration.SystemName, suffix);
|
|
|
|
return DoCreateLayer(animator, layerName);
|
|
}
|
|
|
|
private AacFlLayer DoCreateLayer(AnimatorController animator, string layerName)
|
|
{
|
|
var ag = new AacAnimatorGenerator(animator, CreateEmptyClip().Clip, _configuration.DefaultsProvider);
|
|
var machine = ag.CreateOrClearLayerAtSameIndex(layerName, 1f);
|
|
|
|
return new AacFlLayer(animator, _configuration, machine, layerName);
|
|
}
|
|
|
|
private AacFlClip CreateEmptyClip()
|
|
{
|
|
var emptyClip = AnomalousSingleKeyframeClip();
|
|
return emptyClip;
|
|
}
|
|
|
|
public AacVrcAssetLibrary VrcAssets()
|
|
{
|
|
return new AacVrcAssetLibrary();
|
|
}
|
|
|
|
public void ClearPreviousAssets()
|
|
{
|
|
var allSubAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(_configuration.AssetContainer));
|
|
foreach (var subAsset in allSubAssets)
|
|
{
|
|
if (subAsset.name.StartsWith($"zAutogenerated__{_configuration.AssetKey}__")
|
|
&& (subAsset is AnimationClip || subAsset is BlendTree || subAsset is AvatarMask))
|
|
{
|
|
AssetDatabase.RemoveObjectFromAsset(subAsset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public class AacAv3
|
|
{
|
|
private readonly AacBackingAnimator _backingAnimator;
|
|
|
|
internal AacAv3(AacBackingAnimator backingAnimator)
|
|
{
|
|
_backingAnimator = backingAnimator;
|
|
}
|
|
|
|
// ReSharper disable InconsistentNaming
|
|
public AacFlBoolParameter IsLocal => _backingAnimator.BoolParameter("IsLocal");
|
|
public AacFlEnumIntParameter<Av3Viseme> Viseme => _backingAnimator.EnumParameter<Av3Viseme>("Viseme");
|
|
public AacFlEnumIntParameter<Av3Gesture> GestureLeft => _backingAnimator.EnumParameter<Av3Gesture>("GestureLeft");
|
|
public AacFlEnumIntParameter<Av3Gesture> GestureRight => _backingAnimator.EnumParameter<Av3Gesture>("GestureRight");
|
|
public AacFlFloatParameter GestureLeftWeight => _backingAnimator.FloatParameter("GestureLeftWeight");
|
|
public AacFlFloatParameter GestureRightWeight => _backingAnimator.FloatParameter("GestureRightWeight");
|
|
public AacFlFloatParameter AngularY => _backingAnimator.FloatParameter("AngularY");
|
|
public AacFlFloatParameter VelocityX => _backingAnimator.FloatParameter("VelocityX");
|
|
public AacFlFloatParameter VelocityY => _backingAnimator.FloatParameter("VelocityY");
|
|
public AacFlFloatParameter VelocityZ => _backingAnimator.FloatParameter("VelocityZ");
|
|
public AacFlFloatParameter Upright => _backingAnimator.FloatParameter("Upright");
|
|
public AacFlBoolParameter Grounded => _backingAnimator.BoolParameter("Grounded");
|
|
public AacFlBoolParameter Seated => _backingAnimator.BoolParameter("Seated");
|
|
public AacFlBoolParameter AFK => _backingAnimator.BoolParameter("AFK");
|
|
public AacFlIntParameter TrackingType => _backingAnimator.IntParameter("TrackingType");
|
|
public AacFlIntParameter VRMode => _backingAnimator.IntParameter("VRMode");
|
|
public AacFlBoolParameter MuteSelf => _backingAnimator.BoolParameter("MuteSelf");
|
|
public AacFlBoolParameter InStation => _backingAnimator.BoolParameter("InStation");
|
|
public AacFlFloatParameter Voice => _backingAnimator.FloatParameter("Voice");
|
|
// ReSharper restore InconsistentNaming
|
|
|
|
public IAacFlCondition ItIsRemote() => IsLocal.IsFalse();
|
|
public IAacFlCondition ItIsLocal() => IsLocal.IsTrue();
|
|
|
|
public enum Av3Gesture
|
|
{
|
|
// Specify all the values explicitly because they should be dictated by VRChat, not enumeration order.
|
|
Neutral = 0,
|
|
Fist = 1,
|
|
HandOpen = 2,
|
|
Fingerpoint = 3,
|
|
Victory = 4,
|
|
RockNRoll = 5,
|
|
HandGun = 6,
|
|
ThumbsUp = 7
|
|
}
|
|
|
|
public enum Av3Viseme
|
|
{
|
|
// Specify all the values explicitly because they should be dictated by VRChat, not enumeration order.
|
|
// ReSharper disable InconsistentNaming
|
|
sil = 0,
|
|
pp = 1,
|
|
ff = 2,
|
|
th = 3,
|
|
dd = 4,
|
|
kk = 5,
|
|
ch = 6,
|
|
ss = 7,
|
|
nn = 8,
|
|
rr = 9,
|
|
aa = 10,
|
|
e = 11,
|
|
ih = 12,
|
|
oh = 13,
|
|
ou = 14
|
|
// ReSharper restore InconsistentNaming
|
|
}
|
|
}
|
|
|
|
public class AacVrcAssetLibrary
|
|
{
|
|
public AvatarMask LeftHandAvatarMask()
|
|
{
|
|
return AssetDatabase.LoadAssetAtPath<AvatarMask>("Assets/VRCSDK/Examples3/Animation/Masks/vrc_Hand Left.mask");
|
|
}
|
|
|
|
public AvatarMask RightHandAvatarMask()
|
|
{
|
|
return AssetDatabase.LoadAssetAtPath<AvatarMask>("Assets/VRCSDK/Examples3/Animation/Masks/vrc_Hand Right.mask");
|
|
}
|
|
|
|
public AnimationClip ProxyForGesture(AacAv3.Av3Gesture gesture, bool masculine)
|
|
{
|
|
return AssetDatabase.LoadAssetAtPath<AnimationClip>("Assets/VRCSDK/Examples3/Animation/ProxyAnim/" + ResolveProxyFilename(gesture, masculine));
|
|
}
|
|
|
|
private static string ResolveProxyFilename(AacAv3.Av3Gesture gesture, bool masculine)
|
|
{
|
|
switch (gesture)
|
|
{
|
|
case AacAv3.Av3Gesture.Neutral: return masculine ? "proxy_hands_idle.anim" : "proxy_hands_idle2.anim";
|
|
case AacAv3.Av3Gesture.Fist: return "proxy_hands_fist.anim";
|
|
case AacAv3.Av3Gesture.HandOpen: return "proxy_hands_open.anim";
|
|
case AacAv3.Av3Gesture.Fingerpoint: return "proxy_hands_point.anim";
|
|
case AacAv3.Av3Gesture.Victory: return "proxy_hands_peace.anim";
|
|
case AacAv3.Av3Gesture.RockNRoll: return "proxy_hands_rock.anim";
|
|
case AacAv3.Av3Gesture.HandGun: return "proxy_hands_gun.anim";
|
|
case AacAv3.Av3Gesture.ThumbsUp: return "proxy_hands_thumbs_up.anim";
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(gesture), gesture, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class AacAnimatorRemoval
|
|
{
|
|
private readonly AnimatorController _animatorController;
|
|
|
|
public AacAnimatorRemoval(AnimatorController animatorController)
|
|
{
|
|
_animatorController = animatorController;
|
|
}
|
|
|
|
public void RemoveLayer(string layerName)
|
|
{
|
|
var index = FindIndexOf(layerName);
|
|
if (index == -1) return;
|
|
|
|
_animatorController.RemoveLayer(index);
|
|
}
|
|
|
|
private int FindIndexOf(string layerName)
|
|
{
|
|
return _animatorController.layers.ToList().FindIndex(layer => layer.name == layerName);
|
|
}
|
|
}
|
|
|
|
public class AacAnimatorGenerator
|
|
{
|
|
private readonly AnimatorController _animatorController;
|
|
private readonly AnimationClip _emptyClip;
|
|
private readonly IAacDefaultsProvider _defaultsProvider;
|
|
|
|
internal AacAnimatorGenerator(AnimatorController animatorController, AnimationClip emptyClip, IAacDefaultsProvider defaultsProvider)
|
|
{
|
|
_animatorController = animatorController;
|
|
_emptyClip = emptyClip;
|
|
_defaultsProvider = defaultsProvider;
|
|
}
|
|
|
|
internal void CreateParamsAsNeeded(params AacFlParameter[] parameters)
|
|
{
|
|
foreach (var parameter in parameters)
|
|
{
|
|
switch (parameter)
|
|
{
|
|
case AacFlIntParameter _:
|
|
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Int);
|
|
break;
|
|
case AacFlFloatParameter _:
|
|
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Float);
|
|
break;
|
|
case AacFlBoolParameter _:
|
|
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Bool);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
internal void CreateTriggerParamsAsNeeded(params AacFlBoolParameter[] parameters)
|
|
{
|
|
foreach (var parameter in parameters)
|
|
{
|
|
CreateParamIfNotExists(parameter.Name, AnimatorControllerParameterType.Trigger);
|
|
}
|
|
}
|
|
|
|
private void CreateParamIfNotExists(string paramName, AnimatorControllerParameterType type)
|
|
{
|
|
if (_animatorController.parameters.FirstOrDefault(param => param.name == paramName) == null)
|
|
{
|
|
_animatorController.AddParameter(paramName, type);
|
|
}
|
|
}
|
|
|
|
// DEPRECATED: This causes the editor window to glitch by deselecting, which is jarring for experimentation
|
|
internal AacFlStateMachine CreateOrRemakeLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null)
|
|
{
|
|
var originalIndexToPreserveOrdering = FindIndexOf(layerName);
|
|
if (originalIndexToPreserveOrdering != -1)
|
|
{
|
|
_animatorController.RemoveLayer(originalIndexToPreserveOrdering);
|
|
}
|
|
|
|
AddLayerWithWeight(layerName, weightWhenCreating, maskWhenCreating);
|
|
if (originalIndexToPreserveOrdering != -1)
|
|
{
|
|
var items = _animatorController.layers.ToList();
|
|
var last = items[items.Count - 1];
|
|
items.RemoveAt(items.Count - 1);
|
|
items.Insert(originalIndexToPreserveOrdering, last);
|
|
_animatorController.layers = items.ToArray();
|
|
}
|
|
|
|
var layer = TryGetLayer(layerName);
|
|
var machinist = new AacFlStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider);
|
|
return machinist
|
|
.WithAnyStatePosition(0, 7)
|
|
.WithEntryPosition(0, -1)
|
|
.WithExitPosition(7, -1);
|
|
}
|
|
|
|
internal AacFlStateMachine CreateOrClearLayerAtSameIndex(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating = null)
|
|
{
|
|
var originalIndexToPreserveOrdering = FindIndexOf(layerName);
|
|
if (originalIndexToPreserveOrdering != -1)
|
|
{
|
|
RecursivelyClearChildrenMachines(_animatorController.layers[originalIndexToPreserveOrdering].stateMachine);
|
|
|
|
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.stateMachines = new ChildAnimatorStateMachine[0];
|
|
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.states = new ChildAnimatorState[0];
|
|
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.entryTransitions = new AnimatorTransition[0];
|
|
_animatorController.layers[originalIndexToPreserveOrdering].stateMachine.anyStateTransitions = new AnimatorStateTransition[0];
|
|
}
|
|
else
|
|
{
|
|
_animatorController.AddLayer(_animatorController.MakeUniqueLayerName(layerName));
|
|
originalIndexToPreserveOrdering = _animatorController.layers.Length - 1;
|
|
}
|
|
|
|
var layers = _animatorController.layers;
|
|
layers[originalIndexToPreserveOrdering].avatarMask = maskWhenCreating;
|
|
layers[originalIndexToPreserveOrdering].defaultWeight = weightWhenCreating;
|
|
_animatorController.layers = layers;
|
|
|
|
var layer = TryGetLayer(layerName);
|
|
var machinist = new AacFlStateMachine(layer.stateMachine, _emptyClip, new AacBackingAnimator(this), _defaultsProvider);
|
|
_defaultsProvider.ConfigureStateMachine(layer.stateMachine);
|
|
return machinist;
|
|
}
|
|
|
|
private void RecursivelyClearChildrenMachines(AnimatorStateMachine parentMachine)
|
|
{
|
|
// TODO: RemoveStateMachine might already be recursive
|
|
foreach (var childStateMachineHolder in parentMachine.stateMachines)
|
|
{
|
|
RecursivelyClearChildrenMachines(childStateMachineHolder.stateMachine);
|
|
parentMachine.RemoveStateMachine(childStateMachineHolder.stateMachine);
|
|
}
|
|
}
|
|
|
|
private int FindIndexOf(string layerName)
|
|
{
|
|
return _animatorController.layers.ToList().FindIndex(layer1 => layer1.name == layerName);
|
|
}
|
|
|
|
private AnimatorControllerLayer TryGetLayer(string layerName)
|
|
{
|
|
return _animatorController.layers.FirstOrDefault(it => it.name == layerName);
|
|
}
|
|
|
|
private void AddLayerWithWeight(string layerName, float weightWhenCreating, AvatarMask maskWhenCreating)
|
|
{
|
|
_animatorController.AddLayer(_animatorController.MakeUniqueLayerName(layerName));
|
|
|
|
var mutatedLayers = _animatorController.layers;
|
|
mutatedLayers[mutatedLayers.Length - 1].defaultWeight = weightWhenCreating;
|
|
mutatedLayers[mutatedLayers.Length - 1].avatarMask = maskWhenCreating;
|
|
_animatorController.layers = mutatedLayers;
|
|
}
|
|
}
|
|
} |