mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-29 02:35:06 +08:00
chore: port ReactiveComponents to the new NDMF animation API
This commit is contained in:
parent
5c084a8b8a
commit
2ed1402dd4
@ -2,13 +2,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
@ -32,7 +28,6 @@ namespace nadena.dev.modular_avatar.animation
|
||||
private BuildContext _context;
|
||||
private AnimationDatabase _animationDatabase;
|
||||
private PathMappings _pathMappings;
|
||||
private ReadableProperty _readableProperty;
|
||||
|
||||
private Dictionary<GameObject, string> _selfProxies = new();
|
||||
|
||||
@ -45,8 +40,6 @@ namespace nadena.dev.modular_avatar.animation
|
||||
|
||||
_pathMappings = new PathMappings();
|
||||
_pathMappings.OnActivate(context, _animationDatabase);
|
||||
|
||||
_readableProperty = new ReadableProperty(_context, _animationDatabase, this);
|
||||
}
|
||||
|
||||
public void OnDeactivate(BuildContext context)
|
||||
@ -85,41 +78,5 @@ namespace nadena.dev.modular_avatar.animation
|
||||
return _pathMappings;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<(EditorCurveBinding, string)> BoundReadableProperties => _readableProperty.BoundProperties;
|
||||
|
||||
// HACK: This is a temporary crutch until we rework the entire animator services system
|
||||
public void AddPropertyDefinition(AnimatorControllerParameter paramDef)
|
||||
{
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
if (!_context.AvatarDescriptor) return;
|
||||
|
||||
var fx = (AnimatorController)
|
||||
_context.AvatarDescriptor.baseAnimationLayers
|
||||
.First(l => l.type == VRCAvatarDescriptor.AnimLayerType.FX)
|
||||
.animatorController;
|
||||
|
||||
fx.parameters = fx.parameters.Concat(new[] { paramDef }).ToArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
public string GetActiveSelfProxy(GameObject obj)
|
||||
{
|
||||
if (_selfProxies.TryGetValue(obj, out var paramName) && !string.IsNullOrEmpty(paramName)) return paramName;
|
||||
|
||||
var path = PathMappings.GetObjectIdentifier(obj);
|
||||
|
||||
paramName = _readableProperty.ForActiveSelf(path);
|
||||
_selfProxies[obj] = paramName;
|
||||
|
||||
return paramName;
|
||||
}
|
||||
|
||||
public bool ObjectHasAnimations(GameObject obj)
|
||||
{
|
||||
var path = PathMappings.GetObjectIdentifier(obj);
|
||||
var clips = AnimationDatabase.ClipsForPath(path);
|
||||
return clips != null && !clips.IsEmpty;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
@ -18,14 +19,13 @@ namespace nadena.dev.modular_avatar.animation
|
||||
{
|
||||
protected override void Execute(BuildContext context)
|
||||
{
|
||||
var asc = context.Extension<AnimationServicesContext>();
|
||||
if (!asc.BoundReadableProperties.Any()) return;
|
||||
|
||||
var fx = (AnimatorController)context.AvatarDescriptor.baseAnimationLayers
|
||||
.FirstOrDefault(l => l.type == VRCAvatarDescriptor.AnimLayerType.FX).animatorController;
|
||||
var asc = context.Extension<AnimatorServicesContext>();
|
||||
var activeProxies = context.GetState<ReadablePropertyExtension.Retained>().proxyProps;
|
||||
if (activeProxies.Count == 0) return;
|
||||
|
||||
var fx = asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX];
|
||||
if (fx == null) return;
|
||||
|
||||
|
||||
var nullMotion = new AnimationClip();
|
||||
nullMotion.name = "NullMotion";
|
||||
|
||||
@ -33,48 +33,31 @@ namespace nadena.dev.modular_avatar.animation
|
||||
blendTree.blendType = BlendTreeType.Direct;
|
||||
blendTree.useAutomaticThresholds = false;
|
||||
|
||||
blendTree.children = asc.BoundReadableProperties
|
||||
.Select(prop => GenerateDelayChild(nullMotion, prop))
|
||||
blendTree.children = activeProxies
|
||||
.Select(prop => GenerateDelayChild(nullMotion, (prop.Key, prop.Value)))
|
||||
.ToArray();
|
||||
|
||||
var asm = new AnimatorStateMachine();
|
||||
var state = new AnimatorState();
|
||||
state.name = "DelayDisable";
|
||||
state.motion = blendTree;
|
||||
state.writeDefaultValues = true;
|
||||
var layer = fx.AddLayer(LayerPriority.Default, "DelayDisable");
|
||||
var state = layer.StateMachine.AddState("DelayDisable");
|
||||
layer.StateMachine.DefaultState = state;
|
||||
|
||||
asm.defaultState = state;
|
||||
asm.states = new[]
|
||||
{
|
||||
new ChildAnimatorState
|
||||
{
|
||||
state = state,
|
||||
position = Vector3.zero
|
||||
}
|
||||
};
|
||||
|
||||
fx.layers = fx.layers.Append(new AnimatorControllerLayer
|
||||
{
|
||||
name = "DelayDisable",
|
||||
stateMachine = asm,
|
||||
defaultWeight = 1,
|
||||
blendingMode = AnimatorLayerBlendingMode.Override
|
||||
}).ToArray();
|
||||
state.WriteDefaultValues = true;
|
||||
state.Motion = asc.ControllerContext.Clone(blendTree);
|
||||
|
||||
// Ensure the initial state of readable props matches the actual state of the gameobject
|
||||
var parameters = fx.parameters;
|
||||
var paramToIndex = parameters.Select((p, i) => (p, i)).ToDictionary(x => x.p.name, x => x.i);
|
||||
foreach (var (binding, prop) in asc.BoundReadableProperties)
|
||||
foreach (var controller in asc.ControllerContext.GetAllControllers())
|
||||
{
|
||||
var obj = asc.PathMappings.PathToObject(binding.path);
|
||||
|
||||
if (obj != null && paramToIndex.TryGetValue(prop, out var index))
|
||||
foreach (var (binding, prop) in activeProxies)
|
||||
{
|
||||
parameters[index].defaultFloat = obj.activeSelf ? 1 : 0;
|
||||
var obj = asc.ObjectPathRemapper.GetObjectForPath(binding.path);
|
||||
|
||||
if (obj != null && controller.Parameters.TryGetValue(prop, out var p))
|
||||
{
|
||||
p.defaultFloat = obj.activeSelf ? 1 : 0;
|
||||
controller.Parameters = controller.Parameters.SetItem(prop, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fx.parameters = parameters;
|
||||
}
|
||||
|
||||
private ChildMotion GenerateDelayChild(Motion nullMotion, (EditorCurveBinding, string) binding)
|
||||
|
@ -1,147 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace nadena.dev.modular_avatar.animation
|
||||
{
|
||||
internal class ReadableProperty
|
||||
{
|
||||
private readonly BuildContext _context;
|
||||
private readonly AnimationDatabase _animDB;
|
||||
private readonly AnimationServicesContext _asc;
|
||||
private readonly Dictionary<EditorCurveBinding, string> _alreadyBound = new();
|
||||
private long _nextIndex;
|
||||
|
||||
public ReadableProperty(BuildContext context, AnimationDatabase animDB, AnimationServicesContext asc)
|
||||
{
|
||||
_context = context;
|
||||
_animDB = animDB;
|
||||
_asc = asc;
|
||||
}
|
||||
|
||||
public IEnumerable<(EditorCurveBinding, string)> BoundProperties =>
|
||||
_alreadyBound.Select(kv => (kv.Key, kv.Value));
|
||||
|
||||
/// <summary>
|
||||
/// Creates an animator parameter which tracks the effective value of a property on a component. This only
|
||||
/// tracks FX layer properties.
|
||||
/// </summary>
|
||||
/// <param name="ecb"></param>
|
||||
/// <returns></returns>
|
||||
public string ForBinding(string path, Type componentType, string property)
|
||||
{
|
||||
var ecb = new EditorCurveBinding
|
||||
{
|
||||
path = path,
|
||||
type = componentType,
|
||||
propertyName = property
|
||||
};
|
||||
|
||||
if (_alreadyBound.TryGetValue(ecb, out var reader))
|
||||
{
|
||||
return reader;
|
||||
}
|
||||
|
||||
var lastComponent = path.Split("/")[^1];
|
||||
var emuPropName = $"__MA/ReadableProp/{lastComponent}/{componentType}/{property}#{_nextIndex++}";
|
||||
|
||||
float initialValue = 0;
|
||||
var gameObject = _asc.PathMappings.PathToObject(path);
|
||||
Object component = componentType == typeof(GameObject)
|
||||
? gameObject
|
||||
: gameObject?.GetComponent(componentType);
|
||||
if (component != null)
|
||||
{
|
||||
var so = new SerializedObject(component);
|
||||
var prop = so.FindProperty(property);
|
||||
if (prop != null)
|
||||
switch (prop.propertyType)
|
||||
{
|
||||
case SerializedPropertyType.Boolean:
|
||||
initialValue = prop.boolValue ? 1 : 0;
|
||||
break;
|
||||
case SerializedPropertyType.Float:
|
||||
initialValue = prop.floatValue;
|
||||
break;
|
||||
case SerializedPropertyType.Integer:
|
||||
initialValue = prop.intValue;
|
||||
break;
|
||||
default: throw new NotImplementedException($"Property type {prop.type} not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_asc.AddPropertyDefinition(new AnimatorControllerParameter
|
||||
{
|
||||
defaultFloat = initialValue,
|
||||
name = emuPropName,
|
||||
type = AnimatorControllerParameterType.Float
|
||||
});
|
||||
|
||||
BindProperty(ecb, emuPropName);
|
||||
|
||||
_alreadyBound[ecb] = emuPropName;
|
||||
|
||||
return emuPropName;
|
||||
}
|
||||
|
||||
private void BindProperty(EditorCurveBinding ecb, string propertyName)
|
||||
{
|
||||
var boundProp = new EditorCurveBinding
|
||||
{
|
||||
path = "",
|
||||
type = typeof(Animator),
|
||||
propertyName = propertyName
|
||||
};
|
||||
|
||||
foreach (var clip in _animDB.ClipsForPath(ecb.path)) ProcessAnyClip(clip);
|
||||
|
||||
void ProcessBlendTree(BlendTree blendTree)
|
||||
{
|
||||
foreach (var child in blendTree.children)
|
||||
switch (child.motion)
|
||||
{
|
||||
case AnimationClip animationClip:
|
||||
ProcessAnimationClip(animationClip);
|
||||
break;
|
||||
|
||||
case BlendTree subBlendTree:
|
||||
ProcessBlendTree(subBlendTree);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessAnimationClip(AnimationClip animationClip)
|
||||
{
|
||||
var curve = AnimationUtility.GetEditorCurve(animationClip, ecb);
|
||||
if (curve == null) return;
|
||||
|
||||
AnimationUtility.SetEditorCurve(animationClip, boundProp, curve);
|
||||
}
|
||||
|
||||
void ProcessAnyClip(AnimationDatabase.ClipHolder clip)
|
||||
{
|
||||
switch (clip.CurrentClip)
|
||||
{
|
||||
case AnimationClip animationClip:
|
||||
ProcessAnimationClip(animationClip);
|
||||
break;
|
||||
|
||||
case BlendTree blendTree:
|
||||
ProcessBlendTree(blendTree);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ForActiveSelf(string path)
|
||||
{
|
||||
return ForBinding(path, typeof(GameObject), "m_IsActive");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1074339e2a59465ba585cb8cbbc4a88c
|
||||
timeCreated: 1719195449
|
82
Editor/Animation/ReadablePropertyExtension.cs
Normal file
82
Editor/Animation/ReadablePropertyExtension.cs
Normal file
@ -0,0 +1,82 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace nadena.dev.modular_avatar.animation
|
||||
{
|
||||
[DependsOnContext(typeof(AnimatorServicesContext))]
|
||||
internal class ReadablePropertyExtension : IExtensionContext
|
||||
{
|
||||
// This is a temporary hack for GameObjectDelayDisablePass
|
||||
public class Retained
|
||||
{
|
||||
public Dictionary<EditorCurveBinding, string> proxyProps = new();
|
||||
}
|
||||
|
||||
private AnimatorServicesContext? _asc;
|
||||
private Retained _retained;
|
||||
|
||||
private AnimatorServicesContext asc =>
|
||||
_asc ?? throw new InvalidOperationException("ActiveSelfProxyExtension is not active");
|
||||
|
||||
private Dictionary<EditorCurveBinding, string> proxyProps => _retained.proxyProps;
|
||||
private int index;
|
||||
|
||||
public IEnumerable<(EditorCurveBinding, string)> ActiveProxyProps =>
|
||||
proxyProps.Select(kvp => (kvp.Key, kvp.Value));
|
||||
|
||||
public string GetActiveSelfProxy(GameObject obj)
|
||||
{
|
||||
var path = asc.ObjectPathRemapper.GetVirtualPathForObject(obj);
|
||||
var ecb = EditorCurveBinding.FloatCurve(path, typeof(GameObject), "m_IsActive");
|
||||
|
||||
if (proxyProps.TryGetValue(ecb, out var prop)) return prop;
|
||||
|
||||
prop = $"__MA/ActiveSelfProxy/{obj.name}##{index++}";
|
||||
proxyProps[ecb] = prop;
|
||||
|
||||
// Add prop to all animators
|
||||
foreach (var animator in asc.ControllerContext.GetAllControllers())
|
||||
{
|
||||
animator.Parameters = animator.Parameters.SetItem(
|
||||
prop,
|
||||
new AnimatorControllerParameter
|
||||
{
|
||||
name = prop,
|
||||
type = AnimatorControllerParameterType.Float,
|
||||
defaultFloat = obj.activeSelf ? 1 : 0
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void OnActivate(BuildContext context)
|
||||
{
|
||||
_asc = context.Extension<AnimatorServicesContext>();
|
||||
_retained = context.GetState<Retained>();
|
||||
}
|
||||
|
||||
public void OnDeactivate(BuildContext context)
|
||||
{
|
||||
asc.AnimationIndex.EditClipsByBinding(proxyProps.Keys, clip =>
|
||||
{
|
||||
foreach (var b in clip.GetFloatCurveBindings().ToList())
|
||||
{
|
||||
if (proxyProps.TryGetValue(b, out var proxyProp))
|
||||
{
|
||||
var curve = clip.GetFloatCurve(b);
|
||||
clip.SetFloatCurve("", typeof(Animator), proxyProp, curve);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/Animation/ReadablePropertyExtension.cs.meta
Normal file
3
Editor/Animation/ReadablePropertyExtension.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 511cbc0373a2469192e0351e2222a203
|
||||
timeCreated: 1732496091
|
@ -6,6 +6,7 @@ using nadena.dev.modular_avatar.core.ArmatureAwase;
|
||||
using nadena.dev.modular_avatar.core.editor.plugin;
|
||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using nadena.dev.ndmf.fluent;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
@ -55,12 +56,22 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
seq.Run(MergeAnimatorPluginPass.Instance);
|
||||
seq.Run(ApplyAnimatorDefaultValuesPass.Instance);
|
||||
#endif
|
||||
seq.WithRequiredExtension(typeof(AnimatorServicesContext), _s2 =>
|
||||
{
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.WithRequiredExtension(typeof(ReadablePropertyExtension), _s3 =>
|
||||
{
|
||||
seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute())
|
||||
.PreviewingWith(new ShapeChangerPreview(), new ObjectSwitcherPreview(),
|
||||
new MaterialSetterPreview());
|
||||
});
|
||||
seq.Run(GameObjectDelayDisablePass.Instance);
|
||||
#endif
|
||||
});
|
||||
|
||||
seq.WithRequiredExtension(typeof(AnimationServicesContext), _s2 =>
|
||||
{
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute())
|
||||
.PreviewingWith(new ShapeChangerPreview(), new ObjectSwitcherPreview(), new MaterialSetterPreview());
|
||||
|
||||
// TODO: We currently run this above MergeArmaturePlugin, because Merge Armature might destroy
|
||||
// game objects which contain Menu Installers. It'd probably be better however to teach Merge Armature
|
||||
// to retain those objects? maybe?
|
||||
@ -78,7 +89,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
seq.Run(ReplaceObjectPluginPass.Instance);
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.Run(BlendshapeSyncAnimationPluginPass.Instance);
|
||||
seq.Run(GameObjectDelayDisablePass.Instance);
|
||||
// seq.Run(GameObjectDelayDisablePass.Instance); - TODO, move back here
|
||||
#endif
|
||||
seq.Run(ConstraintConverterPass.Instance);
|
||||
});
|
||||
|
@ -31,7 +31,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
if (_asc != null)
|
||||
{
|
||||
return _asc.GetActiveSelfProxy(obj);
|
||||
return _rpe.GetActiveSelfProxy(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4,6 +4,7 @@ using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.core.editor.Simulator;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using nadena.dev.ndmf.preview;
|
||||
using UnityEngine;
|
||||
|
||||
@ -17,7 +18,9 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
private readonly ComputeContext _computeContext;
|
||||
private readonly ndmf.BuildContext _context;
|
||||
private readonly AnimationServicesContext _asc;
|
||||
private readonly AnimatorServicesContext _asc;
|
||||
private readonly ReadablePropertyExtension _rpe;
|
||||
|
||||
private Dictionary<string, float> _simulationInitialStates;
|
||||
|
||||
public const string BlendshapePrefix = "blendShape.";
|
||||
@ -34,7 +37,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
_computeContext = ComputeContext.NullContext;
|
||||
_context = context;
|
||||
_asc = context.Extension<AnimationServicesContext>();
|
||||
_asc = context.Extension<AnimatorServicesContext>();
|
||||
_rpe = context.Extension<ReadablePropertyExtension>();
|
||||
_simulationInitialStates = null;
|
||||
}
|
||||
|
||||
@ -145,7 +149,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
/// <param name="shapes"></param>
|
||||
private void AnalyzeConstants(Dictionary<TargetProp, AnimatedProperty> shapes)
|
||||
{
|
||||
var asc = _context?.Extension<AnimationServicesContext>();
|
||||
var asc = _context?.Extension<AnimatorServicesContext>();
|
||||
HashSet<GameObject> toggledObjects = new();
|
||||
|
||||
if (asc == null) return;
|
||||
@ -160,7 +164,10 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
foreach (var condition in actionGroup.ControllingConditions)
|
||||
if (condition.ReferenceObject != null && !toggledObjects.Contains(condition.ReferenceObject))
|
||||
condition.IsConstant = asc.AnimationDatabase.ClipsForPath(asc.PathMappings.GetObjectIdentifier(condition.ReferenceObject)).IsEmpty;
|
||||
condition.IsConstant = !asc.AnimationIndex.GetClipsForObjectPath(
|
||||
asc.ObjectPathRemapper.GetVirtualPathForObject(condition.ReferenceObject) ??
|
||||
"___NONEXISTENT___"
|
||||
).Any();
|
||||
|
||||
// Remove redundant active conditions.
|
||||
actionGroup.ControllingConditions.RemoveAll(c => c.IsConstant && c.InitiallyActive);
|
||||
@ -187,7 +194,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
/// <param name="groups"></param>
|
||||
private void ResolveToggleInitialStates(Dictionary<TargetProp, AnimatedProperty> groups)
|
||||
{
|
||||
var asc = _context?.Extension<AnimationServicesContext>();
|
||||
var asc = _context?.Extension<AnimatorServicesContext>();
|
||||
|
||||
Dictionary<string, float> propStates = new();
|
||||
Dictionary<string, float> nextPropStates = new();
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
@ -23,8 +24,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
// Properties that are being driven, either by foreign animations or Object Toggles
|
||||
private HashSet<string> activeProps = new();
|
||||
|
||||
private AnimationClip _initialStateClip;
|
||||
|
||||
private VirtualClip _initialStateClip;
|
||||
private bool _writeDefaults;
|
||||
|
||||
public ReactiveObjectPass(ndmf.BuildContext context)
|
||||
@ -60,7 +61,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
private void GenerateActiveSelfProxies(Dictionary<TargetProp, AnimatedProperty> shapes)
|
||||
{
|
||||
var asc = context.Extension<AnimationServicesContext>();
|
||||
var rpe = context.Extension<ReadablePropertyExtension>();
|
||||
|
||||
foreach (var prop in shapes.Keys)
|
||||
{
|
||||
@ -68,7 +69,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
// Ensure a proxy exists for each object we're going to be toggling.
|
||||
// TODO: is this still needed?
|
||||
asc.GetActiveSelfProxy(go);
|
||||
rpe.GetActiveSelfProxy(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,19 +92,19 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
private void ProcessInitialStates(Dictionary<TargetProp, object> initialStates,
|
||||
Dictionary<TargetProp, AnimatedProperty> shapes)
|
||||
{
|
||||
var asc = context.Extension<AnimationServicesContext>();
|
||||
var asc = context.Extension<AnimatorServicesContext>();
|
||||
var rpe = context.Extension<ReadablePropertyExtension>();
|
||||
|
||||
// We need to track _two_ initial states: the initial state we'll apply at build time (which applies
|
||||
// when animations are disabled) and the animation base state. Confusingly, the animation base state
|
||||
// should be the state that is currently applied to the object...
|
||||
|
||||
var clips = context.Extension<AnimationServicesContext>().AnimationDatabase;
|
||||
var initialStateHolder = clips.ClipsForPath(ReactiveObjectPrepass.TAG_PATH).FirstOrDefault();
|
||||
if (initialStateHolder == null) return;
|
||||
|
||||
_initialStateClip = new AnimationClip();
|
||||
_initialStateClip.name = "Reactive Component Defaults";
|
||||
initialStateHolder.CurrentClip = _initialStateClip;
|
||||
var clips = asc.AnimationIndex;
|
||||
_initialStateClip = clips.GetClipsForObjectPath(ReactiveObjectPrepass.TAG_PATH).FirstOrDefault();
|
||||
|
||||
if (_initialStateClip == null) return;
|
||||
|
||||
_initialStateClip.Name = "Reactive Component Defaults";
|
||||
|
||||
foreach (var (key, initialState) in initialStates)
|
||||
{
|
||||
@ -186,17 +187,17 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
curve.AddKey(0, f);
|
||||
curve.AddKey(1, f);
|
||||
|
||||
AnimationUtility.SetEditorCurve(_initialStateClip, binding, curve);
|
||||
_initialStateClip.SetFloatCurve(binding, curve);
|
||||
|
||||
if (componentType == typeof(GameObject) && key.PropertyName == "m_IsActive")
|
||||
{
|
||||
binding = EditorCurveBinding.FloatCurve(
|
||||
"",
|
||||
typeof(Animator),
|
||||
asc.GetActiveSelfProxy((GameObject)key.TargetObject)
|
||||
rpe.GetActiveSelfProxy((GameObject)key.TargetObject)
|
||||
);
|
||||
|
||||
AnimationUtility.SetEditorCurve(_initialStateClip, binding, curve);
|
||||
_initialStateClip.SetFloatCurve(binding, curve);
|
||||
}
|
||||
}
|
||||
else if (animBaseState is Object obj)
|
||||
@ -206,8 +207,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
componentType,
|
||||
key.PropertyName
|
||||
);
|
||||
|
||||
AnimationUtility.SetObjectReferenceCurve(_initialStateClip, binding, new []
|
||||
|
||||
_initialStateClip.SetObjectCurve(binding, new[]
|
||||
{
|
||||
new ObjectReferenceKeyframe()
|
||||
{
|
||||
@ -299,7 +300,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
private AnimatorStateMachine GenerateStateMachine(AnimatedProperty info)
|
||||
{
|
||||
var asc = context.Extension<AnimationServicesContext>();
|
||||
var asc = context.Extension<AnimatorServicesContext>();
|
||||
var asm = new AnimatorStateMachine();
|
||||
|
||||
// Workaround for the warning: "'.' is not allowed in State name"
|
||||
@ -326,7 +327,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
position = new Vector3(x, y),
|
||||
state = initialState
|
||||
});
|
||||
asc.AnimationDatabase.RegisterState(states[^1].state);
|
||||
|
||||
var lastConstant = info.actionGroups.FindLastIndex(agk => agk.IsConstant);
|
||||
var transitionBuffer = new List<(AnimatorState, List<AnimatorStateTransition>)>();
|
||||
@ -350,7 +350,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
clip.name = "Property Overlay controlled by " + group.ControllingConditions[0].DebugName + " " +
|
||||
group.Value;
|
||||
|
||||
var conditions = GetTransitionConditions(asc, group);
|
||||
var conditions = GetTransitionConditions(group);
|
||||
|
||||
foreach (var (st, transitions) in transitionBuffer)
|
||||
{
|
||||
@ -394,7 +394,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
position = new Vector3(x, y),
|
||||
state = state
|
||||
});
|
||||
asc.AnimationDatabase.RegisterState(states[^1].state);
|
||||
|
||||
var transitionList = new List<AnimatorStateTransition>();
|
||||
transitionBuffer.Add((state, transitionList));
|
||||
@ -466,7 +465,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
};
|
||||
}
|
||||
|
||||
private AnimatorCondition[] GetTransitionConditions(AnimationServicesContext asc, ReactionRule group)
|
||||
private AnimatorCondition[] GetTransitionConditions(ReactionRule group)
|
||||
{
|
||||
var conditions = new List<AnimatorCondition>();
|
||||
|
||||
@ -552,8 +551,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
if (key.TargetObject is GameObject targetObject && key.PropertyName == "m_IsActive")
|
||||
{
|
||||
var asc = context.Extension<AnimationServicesContext>();
|
||||
var propName = asc.GetActiveSelfProxy(targetObject);
|
||||
var rpe = context.Extension<ReadablePropertyExtension>();
|
||||
var propName = rpe.GetActiveSelfProxy(targetObject);
|
||||
binding = EditorCurveBinding.FloatCurve("", typeof(Animator), propName);
|
||||
AnimationUtility.SetEditorCurve(clip, binding, curve);
|
||||
}
|
||||
@ -564,47 +563,29 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
private void ApplyController(AnimatorStateMachine asm, string layerName)
|
||||
{
|
||||
var fx = FindFxController();
|
||||
|
||||
if (fx.animatorController == null)
|
||||
var asc = context.Extension<AnimatorServicesContext>();
|
||||
var fx = asc.ControllerContext[
|
||||
VRCAvatarDescriptor.AnimLayerType.FX
|
||||
];
|
||||
|
||||
if (fx == null)
|
||||
{
|
||||
throw new InvalidOperationException("No FX layer found");
|
||||
}
|
||||
|
||||
if (!context.IsTemporaryAsset(fx.animatorController))
|
||||
{
|
||||
throw new InvalidOperationException("FX layer is not a temporary asset");
|
||||
}
|
||||
|
||||
if (!(fx.animatorController is AnimatorController animController))
|
||||
foreach (var paramName in initialValues.Keys.Except(fx.Parameters.Keys))
|
||||
{
|
||||
throw new InvalidOperationException("FX layer is not an animator controller");
|
||||
}
|
||||
|
||||
var paramList = animController.parameters.ToList();
|
||||
var paramSet = paramList.Select(p => p.name).ToHashSet();
|
||||
|
||||
foreach (var paramName in initialValues.Keys.Except(paramSet))
|
||||
{
|
||||
paramList.Add(new AnimatorControllerParameter()
|
||||
var parameter = new AnimatorControllerParameter
|
||||
{
|
||||
name = paramName,
|
||||
type = AnimatorControllerParameterType.Float,
|
||||
defaultFloat = initialValues[paramName], // TODO
|
||||
});
|
||||
paramSet.Add(paramName);
|
||||
};
|
||||
fx.Parameters = fx.Parameters.SetItem(paramName, parameter);
|
||||
}
|
||||
|
||||
animController.parameters = paramList.ToArray();
|
||||
|
||||
animController.layers = animController.layers.Append(
|
||||
new AnimatorControllerLayer
|
||||
{
|
||||
stateMachine = asm,
|
||||
name = "RC " + layerName,
|
||||
defaultWeight = 1
|
||||
}
|
||||
).ToArray();
|
||||
fx.AddLayer(LayerPriority.Default, "RC " + layerName).StateMachine =
|
||||
asc.ControllerContext.Clone(asm);
|
||||
}
|
||||
|
||||
private VRCAvatarDescriptor.CustomAnimLayer FindFxController()
|
||||
|
Loading…
Reference in New Issue
Block a user