mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-04-11 23:19:00 +08:00
port remaining MA passes to anim-api
This commit is contained in:
parent
dca6ad36ba
commit
fda9878a48
@ -1,12 +1,14 @@
|
|||||||
#if MA_VRCSDK3_AVATARS
|
#if MA_VRCSDK3_AVATARS
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Animations;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using VRC.SDK3.Avatars.Components;
|
|
||||||
using Object = UnityEngine.Object;
|
|
||||||
|
|
||||||
namespace nadena.dev.modular_avatar.core.editor
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
{
|
{
|
||||||
@ -17,11 +19,16 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
*/
|
*/
|
||||||
internal class BlendshapeSyncAnimationProcessor
|
internal class BlendshapeSyncAnimationProcessor
|
||||||
{
|
{
|
||||||
private BuildContext _context;
|
private readonly ndmf.BuildContext _context;
|
||||||
private Dictionary<Motion, Motion> _motionCache;
|
|
||||||
private Dictionary<SummaryBinding, List<SummaryBinding>> _bindingMappings;
|
private Dictionary<SummaryBinding, List<SummaryBinding>> _bindingMappings;
|
||||||
|
|
||||||
private struct SummaryBinding
|
internal BlendshapeSyncAnimationProcessor(ndmf.BuildContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_bindingMappings = new Dictionary<SummaryBinding, List<SummaryBinding>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct SummaryBinding : IEquatable<SummaryBinding>
|
||||||
{
|
{
|
||||||
private const string PREFIX = "blendShape.";
|
private const string PREFIX = "blendShape.";
|
||||||
public string path;
|
public string path;
|
||||||
@ -33,71 +40,76 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
this.propertyName = PREFIX + blendShape;
|
this.propertyName = PREFIX + blendShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SummaryBinding FromEditorBinding(EditorCurveBinding binding)
|
public static SummaryBinding? FromEditorBinding(EditorCurveBinding binding)
|
||||||
{
|
{
|
||||||
if (binding.type != typeof(SkinnedMeshRenderer) || !binding.propertyName.StartsWith(PREFIX))
|
if (binding.type != typeof(SkinnedMeshRenderer) || !binding.propertyName.StartsWith(PREFIX))
|
||||||
{
|
{
|
||||||
return new SummaryBinding();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SummaryBinding(binding.path, binding.propertyName.Substring(PREFIX.Length));
|
return new SummaryBinding(binding.path, binding.propertyName.Substring(PREFIX.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EditorCurveBinding ToEditorCurveBinding()
|
||||||
|
{
|
||||||
|
return EditorCurveBinding.FloatCurve(
|
||||||
|
path,
|
||||||
|
typeof(SkinnedMeshRenderer),
|
||||||
|
propertyName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(SummaryBinding other)
|
||||||
|
{
|
||||||
|
return path == other.path && propertyName == other.propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is SummaryBinding other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(path, propertyName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnPreprocessAvatar(BuildContext context)
|
public void OnPreprocessAvatar()
|
||||||
{
|
{
|
||||||
_context = context;
|
var avatarGameObject = _context.AvatarRootObject;
|
||||||
var avatarGameObject = context.AvatarRootObject;
|
var animDb = _context.Extension<AnimatorServicesContext>().AnimationIndex;
|
||||||
var animDb = _context.AnimationDatabase;
|
|
||||||
|
|
||||||
var avatarDescriptor = context.AvatarDescriptor;
|
|
||||||
_bindingMappings = new Dictionary<SummaryBinding, List<SummaryBinding>>();
|
_bindingMappings = new Dictionary<SummaryBinding, List<SummaryBinding>>();
|
||||||
_motionCache = new Dictionary<Motion, Motion>();
|
|
||||||
|
|
||||||
var components = avatarGameObject.GetComponentsInChildren<ModularAvatarBlendshapeSync>(true);
|
var components = avatarGameObject.GetComponentsInChildren<ModularAvatarBlendshapeSync>(true);
|
||||||
if (components.Length == 0) return;
|
if (components.Length == 0) return;
|
||||||
|
|
||||||
var layers = avatarDescriptor.baseAnimationLayers;
|
|
||||||
var fxIndex = -1;
|
|
||||||
AnimatorController controller = null;
|
|
||||||
for (int i = 0; i < layers.Length; i++)
|
|
||||||
{
|
|
||||||
if (layers[i].type == VRCAvatarDescriptor.AnimLayerType.FX && !layers[i].isDefault)
|
|
||||||
{
|
|
||||||
if (layers[i].animatorController is AnimatorController c && c != null)
|
|
||||||
{
|
|
||||||
fxIndex = i;
|
|
||||||
controller = c;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controller == null)
|
|
||||||
{
|
|
||||||
// Nothing to do, return
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var component in components)
|
foreach (var component in components)
|
||||||
{
|
{
|
||||||
BuildReport.ReportingObject(component, () => ProcessComponent(avatarGameObject, component));
|
BuildReport.ReportingObject(component, () => ProcessComponent(avatarGameObject, component));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk and transform all clips
|
var clips = new HashSet<VirtualClip>();
|
||||||
animDb.ForeachClip(clip =>
|
foreach (var key in _bindingMappings.Keys)
|
||||||
{
|
{
|
||||||
if (clip.CurrentClip is AnimationClip anim)
|
var ecb = key.ToEditorCurveBinding();
|
||||||
{
|
clips.UnionWith(animDb.GetClipsForBinding(ecb));
|
||||||
BuildReport.ReportingObject(clip.CurrentClip,
|
}
|
||||||
() => { clip.CurrentClip = TransformMotion(anim); });
|
|
||||||
}
|
// Walk and transform all clips
|
||||||
});
|
foreach (var clip in clips)
|
||||||
|
{
|
||||||
|
ProcessClip(clip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessComponent(GameObject avatarGameObject, ModularAvatarBlendshapeSync component)
|
private void ProcessComponent(GameObject avatarGameObject, ModularAvatarBlendshapeSync component)
|
||||||
{
|
{
|
||||||
var targetObj = RuntimeUtil.RelativePath(avatarGameObject, component.gameObject);
|
var targetObj = RuntimeUtil.RelativePath(avatarGameObject, component.gameObject);
|
||||||
|
|
||||||
|
if (targetObj == null) return;
|
||||||
|
|
||||||
foreach (var binding in component.Bindings)
|
foreach (var binding in component.Bindings)
|
||||||
{
|
{
|
||||||
var refObj = binding.ReferenceMesh.Get(component);
|
var refObj = binding.ReferenceMesh.Get(component);
|
||||||
@ -106,6 +118,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
if (refSmr == null) continue;
|
if (refSmr == null) continue;
|
||||||
|
|
||||||
var refPath = RuntimeUtil.RelativePath(avatarGameObject, refObj);
|
var refPath = RuntimeUtil.RelativePath(avatarGameObject, refObj);
|
||||||
|
if (refPath == null) continue;
|
||||||
|
|
||||||
var srcBinding = new SummaryBinding(refPath, binding.Blendshape);
|
var srcBinding = new SummaryBinding(refPath, binding.Blendshape);
|
||||||
|
|
||||||
@ -123,108 +136,20 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Motion TransformMotion(Motion motion)
|
private void ProcessClip(VirtualClip clip)
|
||||||
{
|
{
|
||||||
if (motion == null) return null;
|
foreach (var binding in clip.GetFloatCurveBindings().ToList())
|
||||||
if (_motionCache.TryGetValue(motion, out var cached)) return cached;
|
|
||||||
|
|
||||||
switch (motion)
|
|
||||||
{
|
{
|
||||||
case AnimationClip clip:
|
var srcBinding = SummaryBinding.FromEditorBinding(binding);
|
||||||
{
|
if (srcBinding == null || !_bindingMappings.TryGetValue(srcBinding.Value, out var dstBindings))
|
||||||
motion = ProcessClip(clip);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BlendTree tree:
|
|
||||||
{
|
|
||||||
bool anyChanged = false;
|
|
||||||
var children = tree.children;
|
|
||||||
|
|
||||||
for (int i = 0; i < children.Length; i++)
|
|
||||||
{
|
|
||||||
var newM = TransformMotion(children[i].motion);
|
|
||||||
if (newM != children[i].motion)
|
|
||||||
{
|
|
||||||
anyChanged = true;
|
|
||||||
children[i].motion = newM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (anyChanged)
|
|
||||||
{
|
|
||||||
var newTree = new BlendTree();
|
|
||||||
EditorUtility.CopySerialized(tree, newTree);
|
|
||||||
_context.SaveAsset(newTree);
|
|
||||||
|
|
||||||
newTree.children = children;
|
|
||||||
motion = newTree;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
Debug.LogWarning($"Ignoring unsupported motion type {motion.GetType()}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_motionCache[motion] = motion;
|
|
||||||
return motion;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimationClip ProcessClip(AnimationClip origClip)
|
|
||||||
{
|
|
||||||
var clip = origClip;
|
|
||||||
EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(clip);
|
|
||||||
|
|
||||||
foreach (var binding in bindings)
|
|
||||||
{
|
|
||||||
if (!_bindingMappings.TryGetValue(SummaryBinding.FromEditorBinding(binding), out var dstBindings))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clip == origClip)
|
var curve = clip.GetFloatCurve(binding);
|
||||||
{
|
|
||||||
clip = Object.Instantiate(clip);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var dst in dstBindings)
|
foreach (var dst in dstBindings)
|
||||||
{
|
{
|
||||||
clip.SetCurve(dst.path, typeof(SkinnedMeshRenderer), dst.propertyName,
|
clip.SetFloatCurve(dst.ToEditorCurveBinding(), curve);
|
||||||
AnimationUtility.GetEditorCurve(origClip, binding));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clip;
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerable<AnimatorState> AllStates(AnimatorController controller)
|
|
||||||
{
|
|
||||||
HashSet<AnimatorStateMachine> visitedStateMachines = new HashSet<AnimatorStateMachine>();
|
|
||||||
Queue<AnimatorStateMachine> stateMachines = new Queue<AnimatorStateMachine>();
|
|
||||||
|
|
||||||
foreach (var layer in controller.layers)
|
|
||||||
{
|
|
||||||
if (layer.stateMachine != null)
|
|
||||||
stateMachines.Enqueue(layer.stateMachine);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (stateMachines.Count > 0)
|
|
||||||
{
|
|
||||||
var next = stateMachines.Dequeue();
|
|
||||||
if (visitedStateMachines.Contains(next)) continue;
|
|
||||||
visitedStateMachines.Add(next);
|
|
||||||
|
|
||||||
foreach (var state in next.states)
|
|
||||||
{
|
|
||||||
yield return state.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var sm in next.stateMachines)
|
|
||||||
{
|
|
||||||
stateMachines.Enqueue(sm.stateMachine);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
#nullable enable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using nadena.dev.ndmf;
|
using nadena.dev.ndmf;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
#if MA_VRCSDK3_AVATARS_3_7_0_OR_NEWER
|
#if MA_VRCSDK3_AVATARS_3_7_0_OR_NEWER
|
||||||
@ -6,7 +8,7 @@ using UnityEngine;
|
|||||||
using UnityEngine.Animations;
|
using UnityEngine.Animations;
|
||||||
using VRC.SDK3.Avatars;
|
using VRC.SDK3.Avatars;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.ndmf.animator;
|
||||||
using VRC.Dynamics;
|
using VRC.Dynamics;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -60,7 +62,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
AvatarDynamicsSetup.DoConvertUnityConstraints(targetConstraintComponents, null, false);
|
AvatarDynamicsSetup.DoConvertUnityConstraints(targetConstraintComponents, null, false);
|
||||||
|
|
||||||
var asc = context.Extension<AnimationServicesContext>();
|
var asc = context.Extension<AnimatorServicesContext>();
|
||||||
|
|
||||||
// Also look for preexisting VRCConstraints so we can go fix up any broken animation clips from people who
|
// Also look for preexisting VRCConstraints so we can go fix up any broken animation clips from people who
|
||||||
// clicked auto fix :(
|
// clicked auto fix :(
|
||||||
@ -70,24 +72,20 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
var targetPaths = constraintGameObjects
|
var targetPaths = constraintGameObjects
|
||||||
.Union(existingVRCConstraints)
|
.Union(existingVRCConstraints)
|
||||||
.Select(c => asc.PathMappings.GetObjectIdentifier(c))
|
.Select(c => asc.ObjectPathRemapper.GetVirtualPathForObject(c))
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
|
|
||||||
// Update animation clips
|
// Update animation clips
|
||||||
var clips = targetPaths.SelectMany(tp => asc.AnimationDatabase.ClipsForPath(tp))
|
|
||||||
|
var clips = targetPaths.SelectMany(tp => asc.AnimationIndex.GetClipsForObjectPath(tp))
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
|
|
||||||
foreach (var clip in clips) RemapSingleClip(clip, targetPaths);
|
foreach (var clip in clips) RemapSingleClip(clip, targetPaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemapSingleClip(AnimationDatabase.ClipHolder clip, HashSet<string> targetPaths)
|
private void RemapSingleClip(VirtualClip clip, HashSet<string> targetPaths)
|
||||||
{
|
{
|
||||||
var motion = clip.CurrentClip as AnimationClip;
|
var bindings = clip.GetFloatCurveBindings().ToList();
|
||||||
if (motion == null) return;
|
|
||||||
|
|
||||||
var bindings = AnimationUtility.GetCurveBindings(motion);
|
|
||||||
var toUpdateBindings = new List<EditorCurveBinding>();
|
|
||||||
var toUpdateCurves = new List<AnimationCurve>();
|
|
||||||
|
|
||||||
foreach (var ecb in bindings)
|
foreach (var ecb in bindings)
|
||||||
{
|
{
|
||||||
@ -102,20 +100,11 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
type = newType,
|
type = newType,
|
||||||
propertyName = newProp
|
propertyName = newProp
|
||||||
};
|
};
|
||||||
var curve = AnimationUtility.GetEditorCurve(motion, ecb);
|
var curve = clip.GetFloatCurve(ecb);
|
||||||
if (curve != null)
|
clip.SetFloatCurve(newBinding, curve);
|
||||||
{
|
clip.SetFloatCurve(newBinding, null);
|
||||||
toUpdateBindings.Add(newBinding);
|
|
||||||
toUpdateCurves.Add(curve);
|
|
||||||
|
|
||||||
toUpdateBindings.Add(ecb);
|
|
||||||
toUpdateCurves.Add(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toUpdateBindings.Count == 0) return;
|
|
||||||
AnimationUtility.SetEditorCurves(motion, toUpdateBindings.ToArray(), toUpdateCurves.ToArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -77,28 +77,29 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
|||||||
#endif
|
#endif
|
||||||
seq.Run(MergeArmaturePluginPass.Instance);
|
seq.Run(MergeArmaturePluginPass.Instance);
|
||||||
|
|
||||||
|
seq.Run(BoneProxyPluginPass.Instance);
|
||||||
|
|
||||||
|
#if MA_VRCSDK3_AVATARS
|
||||||
|
seq.Run(VisibleHeadAccessoryPluginPass.Instance);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
seq.Run("World Fixed Object",
|
||||||
|
ctx => new WorldFixedObjectProcessor().Process(ctx)
|
||||||
|
);
|
||||||
|
|
||||||
|
seq.Run(ReplaceObjectPluginPass.Instance);
|
||||||
|
|
||||||
|
#if MA_VRCSDK3_AVATARS
|
||||||
|
seq.Run(BlendshapeSyncAnimationPluginPass.Instance);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
seq.Run(ConstraintConverterPass.Instance);
|
||||||
|
|
||||||
seq.Run("Prune empty animator layers",
|
seq.Run("Prune empty animator layers",
|
||||||
ctx => { ctx.Extension<AnimatorServicesContext>().RemoveEmptyLayers(); });
|
ctx => { ctx.Extension<AnimatorServicesContext>().RemoveEmptyLayers(); });
|
||||||
seq.Run("Harmonize animator parameter types",
|
seq.Run("Harmonize animator parameter types",
|
||||||
ctx => { ctx.Extension<AnimatorServicesContext>().HarmonizeParameterTypes(); });
|
ctx => { ctx.Extension<AnimatorServicesContext>().HarmonizeParameterTypes(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
seq.WithRequiredExtension(typeof(AnimationServicesContext), _s2 =>
|
|
||||||
{
|
|
||||||
seq.Run(BoneProxyPluginPass.Instance);
|
|
||||||
#if MA_VRCSDK3_AVATARS
|
|
||||||
seq.Run(VisibleHeadAccessoryPluginPass.Instance);
|
|
||||||
#endif
|
|
||||||
seq.Run("World Fixed Object",
|
|
||||||
ctx => new WorldFixedObjectProcessor().Process(ctx)
|
|
||||||
);
|
|
||||||
seq.Run(ReplaceObjectPluginPass.Instance);
|
|
||||||
#if MA_VRCSDK3_AVATARS
|
|
||||||
seq.Run(BlendshapeSyncAnimationPluginPass.Instance);
|
|
||||||
// seq.Run(GameObjectDelayDisablePass.Instance); - TODO, move back here
|
|
||||||
#endif
|
|
||||||
seq.Run(ConstraintConverterPass.Instance);
|
|
||||||
});
|
|
||||||
#if MA_VRCSDK3_AVATARS
|
#if MA_VRCSDK3_AVATARS
|
||||||
seq.Run(PhysbonesBlockerPluginPass.Instance);
|
seq.Run(PhysbonesBlockerPluginPass.Instance);
|
||||||
seq.Run("Fixup Expressions Menu", ctx =>
|
seq.Run("Fixup Expressions Menu", ctx =>
|
||||||
@ -258,7 +259,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
|||||||
{
|
{
|
||||||
protected override void Execute(ndmf.BuildContext context)
|
protected override void Execute(ndmf.BuildContext context)
|
||||||
{
|
{
|
||||||
new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(MAContext(context));
|
new BlendshapeSyncAnimationProcessor(context).OnPreprocessAvatar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using VRC.SDK3.Avatars.Components;
|
using VRC.SDK3.Avatars.Components;
|
||||||
@ -164,33 +165,34 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
private void ProcessAnimations()
|
private void ProcessAnimations()
|
||||||
{
|
{
|
||||||
var animdb = _context.AnimationDatabase;
|
var animdb = _context.PluginBuildContext.Extension<AnimatorServicesContext>();
|
||||||
var paths = _context.PathMappings;
|
var paths = animdb.ObjectPathRemapper;
|
||||||
Dictionary<string, string> pathMappings = new Dictionary<string, string>();
|
Dictionary<string, string> pathMappings = new Dictionary<string, string>();
|
||||||
|
HashSet<VirtualClip> clips = new();
|
||||||
|
|
||||||
foreach (var kvp in _boneShims)
|
foreach (var kvp in _boneShims)
|
||||||
{
|
{
|
||||||
var orig = paths.GetObjectIdentifier(kvp.Key.gameObject);
|
var orig = paths.GetVirtualPathForObject(kvp.Key.gameObject);
|
||||||
var shim = paths.GetObjectIdentifier(kvp.Value.gameObject);
|
var shim = paths.GetVirtualPathForObject(kvp.Value.gameObject);
|
||||||
|
|
||||||
pathMappings[orig] = shim;
|
pathMappings[orig] = shim;
|
||||||
|
|
||||||
|
clips.UnionWith(animdb.AnimationIndex.GetClipsForObjectPath(orig));
|
||||||
}
|
}
|
||||||
|
|
||||||
animdb.ForeachClip(motion =>
|
foreach (var clip in clips)
|
||||||
{
|
{
|
||||||
if (!(motion.CurrentClip is AnimationClip clip)) return;
|
foreach (var binding in clip.GetFloatCurveBindings())
|
||||||
|
|
||||||
var bindings = AnimationUtility.GetCurveBindings(clip);
|
|
||||||
foreach (var binding in bindings)
|
|
||||||
{
|
{
|
||||||
if (binding.type != typeof(Transform)) continue;
|
if (binding.type == typeof(Transform) && pathMappings.TryGetValue(binding.path, out var newPath))
|
||||||
if (!pathMappings.TryGetValue(binding.path, out var newPath)) continue;
|
{
|
||||||
|
clip.SetFloatCurve(
|
||||||
var newBinding = binding;
|
EditorCurveBinding.FloatCurve(newPath, typeof(Transform), binding.propertyName),
|
||||||
newBinding.path = newPath;
|
clip.GetFloatCurve(binding)
|
||||||
AnimationUtility.SetEditorCurve(clip, newBinding, AnimationUtility.GetEditorCurve(clip, binding));
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Transform CreateShim(Transform target)
|
private Transform CreateShim(Transform target)
|
||||||
|
@ -5,6 +5,7 @@ using nadena.dev.modular_avatar.core;
|
|||||||
using nadena.dev.modular_avatar.core.editor;
|
using nadena.dev.modular_avatar.core.editor;
|
||||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||||
using nadena.dev.ndmf;
|
using nadena.dev.ndmf;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -12,12 +13,12 @@ namespace modular_avatar_tests.ReplaceObject
|
|||||||
{
|
{
|
||||||
public class ReplaceObjectTests : TestBase
|
public class ReplaceObjectTests : TestBase
|
||||||
{
|
{
|
||||||
private PathMappings pathMappings;
|
private ObjectPathRemapper pathMappings;
|
||||||
|
|
||||||
void Process(GameObject root)
|
void Process(GameObject root)
|
||||||
{
|
{
|
||||||
var buildContext = new nadena.dev.ndmf.BuildContext(root, null);
|
var buildContext = new nadena.dev.ndmf.BuildContext(root, null);
|
||||||
pathMappings = buildContext.ActivateExtensionContext<AnimationServicesContext>().PathMappings;
|
pathMappings = buildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>().ObjectPathRemapper;
|
||||||
new ReplaceObjectPass(buildContext).Process();
|
new ReplaceObjectPass(buildContext).Process();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ namespace modular_avatar_tests.ReplaceObject
|
|||||||
|
|
||||||
Process(root);
|
Process(root);
|
||||||
|
|
||||||
Assert.AreEqual("replacement", pathMappings.MapPath("replacee"));
|
Assert.AreEqual("replacement", pathMappings.GetVirtualPathForObject(pathMappings.GetObjectForPath("replacee")!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user