Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: port ReactiveComponents to the new NDMF animation API #1371

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions Editor/ActiveAnimationRetargeter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using nadena.dev.modular_avatar.animation;
using nadena.dev.ndmf.animator;
using UnityEditor;
using UnityEngine;
using EditorCurveBinding = UnityEditor.EditorCurveBinding;
Expand All @@ -16,7 +17,7 @@ internal class ActiveAnimationRetargeter
{
private readonly BuildContext _context;
private readonly BoneDatabase _boneDatabase;
private readonly PathMappings _pathMappings;
private readonly AnimatorServicesContext _asc;
private readonly List<IntermediateObj> _intermediateObjs = new List<IntermediateObj>();

/// <summary>
Expand Down Expand Up @@ -55,15 +56,15 @@ Transform root
{
_context = context;
_boneDatabase = boneDatabase;
_pathMappings = context.PluginBuildContext.Extension<AnimationServicesContext>().PathMappings;
_asc = context.PluginBuildContext.Extension<AnimatorServicesContext>();

while (root != null && !RuntimeUtil.IsAvatarRoot(root))
{
var originalPath = RuntimeUtil.AvatarRootPath(root.gameObject);
System.Diagnostics.Debug.Assert(originalPath != null);

if (context.AnimationDatabase.ClipsForPath(originalPath).Any(clip =>
GetActiveBinding(clip.CurrentClip as AnimationClip, originalPath) != null
if (_asc.AnimationIndex.GetClipsForObjectPath(originalPath).Any(clip =>
GetActiveBinding(clip, originalPath) != null
))
{
_intermediateObjs.Add(new IntermediateObj
Expand Down Expand Up @@ -118,7 +119,6 @@ public GameObject CreateIntermediateObjects(GameObject sourceBone)
// Ensure mesh retargeting looks through this
_boneDatabase.AddMergedBone(sourceBone.transform);
_boneDatabase.RetainMergedBone(sourceBone.transform);
_pathMappings.MarkTransformLookthrough(sourceBone);
}

return sourceBone;
Expand All @@ -130,33 +130,24 @@ public void FixupAnimations()
{
var path = intermediate.OriginalPath;

foreach (var holder in _context.AnimationDatabase.ClipsForPath(path))
foreach (var clip in _asc.AnimationIndex.GetClipsForObjectPath(path))
{
if (!_context.PluginBuildContext.IsTemporaryAsset(holder.CurrentClip))
{
holder.CurrentClip = Object.Instantiate(holder.CurrentClip);
}

var clip = holder.CurrentClip as AnimationClip;
if (clip == null) continue;

var curve = GetActiveBinding(clip, path);
if (curve != null)
{
foreach (var mapping in intermediate.Created)
{
clip.SetCurve(_pathMappings.GetObjectIdentifier(mapping), typeof(GameObject), "m_IsActive",
clip.SetFloatCurve(_asc.ObjectPathRemapper.GetVirtualPathForObject(mapping), typeof(GameObject), "m_IsActive",
curve);
}
}
}
}
}

private AnimationCurve GetActiveBinding(AnimationClip clip, string path)
private AnimationCurve GetActiveBinding(VirtualClip clip, string path)
{
return AnimationUtility.GetEditorCurve(clip,
EditorCurveBinding.FloatCurve(path, typeof(GameObject), "m_IsActive"));
return clip.GetFloatCurve(EditorCurveBinding.FloatCurve(path, typeof(GameObject), "m_IsActive"));
}
}
}
43 changes: 0 additions & 43 deletions Editor/Animation/AnimationServicesContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,7 +28,6 @@ internal sealed class AnimationServicesContext : IExtensionContext
private BuildContext _context;
private AnimationDatabase _animationDatabase;
private PathMappings _pathMappings;
private ReadableProperty _readableProperty;

private Dictionary<GameObject, string> _selfProxies = new();

Expand All @@ -45,8 +40,6 @@ public void OnActivate(BuildContext context)

_pathMappings = new PathMappings();
_pathMappings.OnActivate(context, _animationDatabase);

_readableProperty = new ReadableProperty(_context, _animationDatabase, this);
}

public void OnDeactivate(BuildContext context)
Expand Down Expand Up @@ -85,41 +78,5 @@ public PathMappings PathMappings
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;
}
}
}
61 changes: 22 additions & 39 deletions Editor/Animation/GameObjectDisableDelayPass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,63 +19,45 @@ internal class GameObjectDelayDisablePass : Pass<GameObjectDelayDisablePass>
{
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";

var blendTree = new BlendTree();
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;

asm.defaultState = state;
asm.states = new[]
{
new ChildAnimatorState
{
state = state,
position = Vector3.zero
}
};
var layer = fx.AddLayer(LayerPriority.Default, "DelayDisable");
var state = layer.StateMachine.AddState("DelayDisable");
layer.StateMachine.DefaultState = state;

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)
Expand Down
147 changes: 0 additions & 147 deletions Editor/Animation/ReadableProperty.cs

This file was deleted.

Loading
Loading