mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-28 10:15:06 +08:00
wip
This commit is contained in:
parent
2ed1402dd4
commit
567cad4edd
@ -2,6 +2,7 @@ using System;
|
||||
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;
|
||||
@ -16,7 +17,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
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>
|
||||
@ -55,15 +56,15 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
_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
|
||||
@ -118,7 +119,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
// Ensure mesh retargeting looks through this
|
||||
_boneDatabase.AddMergedBone(sourceBone.transform);
|
||||
_boneDatabase.RetainMergedBone(sourceBone.transform);
|
||||
_pathMappings.MarkTransformLookthrough(sourceBone);
|
||||
}
|
||||
|
||||
return sourceBone;
|
||||
@ -130,22 +130,14 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -153,10 +145,9 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ namespace nadena.dev.modular_avatar.animation
|
||||
}
|
||||
|
||||
private AnimatorServicesContext? _asc;
|
||||
private Retained _retained;
|
||||
private Retained _retained = null!;
|
||||
|
||||
private AnimatorServicesContext asc =>
|
||||
_asc ?? throw new InvalidOperationException("ActiveSelfProxyExtension is not active");
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
|
||||
@ -21,40 +22,34 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
var values = context.GetState<DefaultValues>()?.InitialValueOverrides
|
||||
?? ImmutableDictionary<string, float>.Empty;
|
||||
|
||||
foreach (var layer in context.AvatarDescriptor.baseAnimationLayers
|
||||
.Concat(context.AvatarDescriptor.specialAnimationLayers))
|
||||
var asc = context.Extension<AnimatorServicesContext>();
|
||||
|
||||
foreach (var controller in asc.ControllerContext.GetAllControllers())
|
||||
{
|
||||
if (layer.isDefault || layer.animatorController == null) continue;
|
||||
|
||||
// We should have converted anything that's not an AnimationController by now
|
||||
var controller = layer.animatorController as AnimatorController;
|
||||
if (controller == null || !context.IsTemporaryAsset(controller))
|
||||
var parameters = controller.Parameters;
|
||||
foreach (var (name, parameter) in parameters)
|
||||
{
|
||||
throw new Exception("Leaked unexpected controller: " + layer.animatorController + " (type " + layer.animatorController?.GetType() + ")");
|
||||
}
|
||||
if (!values.TryGetValue(name, out var defaultValue)) continue;
|
||||
|
||||
var parameters = controller.parameters;
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (!values.TryGetValue(parameters[i].name, out var defaultValue)) continue;
|
||||
|
||||
switch (parameters[i].type)
|
||||
switch (parameter.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
parameters[i].defaultBool = defaultValue != 0.0f;
|
||||
parameter.defaultBool = defaultValue != 0.0f;
|
||||
break;
|
||||
case AnimatorControllerParameterType.Int:
|
||||
parameters[i].defaultInt = Mathf.RoundToInt(defaultValue);
|
||||
parameter.defaultInt = Mathf.RoundToInt(defaultValue);
|
||||
break;
|
||||
case AnimatorControllerParameterType.Float:
|
||||
parameters[i].defaultFloat = defaultValue;
|
||||
parameter.defaultFloat = defaultValue;
|
||||
break;
|
||||
default:
|
||||
continue; // unhandled type, e.g. trigger
|
||||
}
|
||||
|
||||
parameters = parameters.SetItem(name, parameter);
|
||||
}
|
||||
|
||||
controller.parameters = parameters;
|
||||
controller.Parameters = parameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,52 +24,23 @@
|
||||
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
using VRC.SDKBase;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class MergeAnimatorProcessor
|
||||
{
|
||||
private const string SAMPLE_PATH_PACKAGE =
|
||||
"Packages/com.vrchat.avatars/Samples/AV3 Demo Assets/Animation/Controllers";
|
||||
|
||||
private const string SAMPLE_PATH_LEGACY = "Assets/VRCSDK/Examples3/Animation/Controllers";
|
||||
|
||||
private const string GUID_GESTURE_HANDSONLY_MASK = "b2b8bad9583e56a46a3e21795e96ad92";
|
||||
|
||||
private BuildContext _context;
|
||||
|
||||
private Dictionary<VRCAvatarDescriptor.AnimLayerType, AnimatorController> defaultControllers_ =
|
||||
new Dictionary<VRCAvatarDescriptor.AnimLayerType, AnimatorController>();
|
||||
|
||||
private Dictionary<VRCAvatarDescriptor.AnimLayerType, bool?> writeDefaults_ =
|
||||
new Dictionary<VRCAvatarDescriptor.AnimLayerType, bool?>();
|
||||
|
||||
Dictionary<VRCAvatarDescriptor.AnimLayerType, AnimatorCombiner> mergeSessions =
|
||||
new Dictionary<VRCAvatarDescriptor.AnimLayerType, AnimatorCombiner>();
|
||||
private AnimatorServicesContext _asc;
|
||||
|
||||
internal void OnPreprocessAvatar(GameObject avatarGameObject, BuildContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
defaultControllers_.Clear();
|
||||
mergeSessions.Clear();
|
||||
|
||||
var descriptor = avatarGameObject.GetComponent<VRCAvatarDescriptor>();
|
||||
if (!descriptor) return;
|
||||
|
||||
if (descriptor.baseAnimationLayers != null) InitSessions(descriptor.baseAnimationLayers);
|
||||
if (descriptor.specialAnimationLayers != null) InitSessions(descriptor.specialAnimationLayers);
|
||||
|
||||
_asc = context.PluginBuildContext.Extension<AnimatorServicesContext>();
|
||||
|
||||
var toMerge = avatarGameObject.transform.GetComponentsInChildren<ModularAvatarMergeAnimator>(true);
|
||||
Dictionary<VRCAvatarDescriptor.AnimLayerType, List<ModularAvatarMergeAnimator>> byLayerType
|
||||
= new Dictionary<VRCAvatarDescriptor.AnimLayerType, List<ModularAvatarMergeAnimator>>();
|
||||
@ -89,10 +60,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
ProcessLayerType(context, entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
descriptor.baseAnimationLayers = FinishSessions(descriptor.baseAnimationLayers);
|
||||
descriptor.specialAnimationLayers = FinishSessions(descriptor.specialAnimationLayers);
|
||||
descriptor.customizeAnimationLayers = true;
|
||||
}
|
||||
|
||||
private void ProcessLayerType(
|
||||
@ -109,34 +76,34 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
var afterOriginal = sorted.Where(x => x.layerPriority >= 0)
|
||||
.ToList();
|
||||
|
||||
var session = new AnimatorCombiner(context.PluginBuildContext, layerType.ToString() + " (merged)");
|
||||
mergeSessions[layerType] = session;
|
||||
mergeSessions[layerType].BlendableLayer = BlendableLayerFor(layerType);
|
||||
var controller = _asc.ControllerContext[layerType];
|
||||
|
||||
var wdStateCounter = controller.Layers.SelectMany(l => l.StateMachine.AllStates())
|
||||
.Select(s => s.WriteDefaultValues)
|
||||
.GroupBy(b => b)
|
||||
.ToDictionary(g => g.Key, g => g.Count());
|
||||
|
||||
foreach (var component in beforeOriginal)
|
||||
bool? writeDefaults = null;
|
||||
if (wdStateCounter.Count == 1) writeDefaults = wdStateCounter.First().Key;
|
||||
|
||||
foreach (var component in sorted)
|
||||
{
|
||||
MergeSingle(context, session, component);
|
||||
}
|
||||
|
||||
if (defaultControllers_.TryGetValue(layerType, out var defaultController) &&
|
||||
defaultController.layers.Length > 0)
|
||||
{
|
||||
session.AddController("", defaultController, null, forceFirstLayerWeight: true);
|
||||
}
|
||||
|
||||
foreach (var component in afterOriginal)
|
||||
{
|
||||
MergeSingle(context, session, component);
|
||||
MergeSingle(context, controller, component, writeDefaults);
|
||||
}
|
||||
}
|
||||
|
||||
private void MergeSingle(BuildContext context, AnimatorCombiner session, ModularAvatarMergeAnimator merge)
|
||||
private void MergeSingle(BuildContext context, VirtualAnimatorController controller, ModularAvatarMergeAnimator merge, bool? initialWriteDefaults)
|
||||
{
|
||||
if (merge.animator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stash = context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||
|
||||
var clonedController = stash.Controllers.GetValueOrDefault(merge)
|
||||
?? _asc.ControllerContext.CloneContext.Clone(merge.animator);
|
||||
|
||||
string basePath;
|
||||
if (merge.pathMode == MergeAnimatorPathMode.Relative)
|
||||
{
|
||||
@ -145,200 +112,60 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
var relativePath = RuntimeUtil.RelativePath(context.AvatarRootObject, targetObject);
|
||||
basePath = relativePath != "" ? relativePath + "/" : "";
|
||||
|
||||
var animationIndex = new AnimationIndex(new[] { clonedController });
|
||||
animationIndex.RewritePaths(p => p == "" ? relativePath : basePath + p);
|
||||
}
|
||||
else
|
||||
{
|
||||
basePath = "";
|
||||
}
|
||||
|
||||
var writeDefaults = merge.matchAvatarWriteDefaults
|
||||
? writeDefaults_.GetValueOrDefault(merge.layerType)
|
||||
: null;
|
||||
var controller = _context.ConvertAnimatorController(merge.animator);
|
||||
session.AddController(basePath, controller, writeDefaults);
|
||||
foreach (var l in clonedController.Layers)
|
||||
{
|
||||
if (initialWriteDefaults != null)
|
||||
{
|
||||
foreach (var s in l.StateMachine.AllStates())
|
||||
{
|
||||
s.WriteDefaultValues = initialWriteDefaults.Value;
|
||||
}
|
||||
}
|
||||
controller.AddLayer(new LayerPriority(merge.layerPriority), l);
|
||||
}
|
||||
|
||||
foreach (var (name, parameter) in clonedController.Parameters)
|
||||
{
|
||||
if (controller.Parameters.TryGetValue(name, out var existingParam))
|
||||
{
|
||||
if (existingParam.type != parameter.type)
|
||||
{
|
||||
// Force to float
|
||||
switch (parameter.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
existingParam.defaultFloat = existingParam.defaultBool ? 1.0f : 0.0f;
|
||||
break;
|
||||
case AnimatorControllerParameterType.Int:
|
||||
existingParam.defaultFloat = existingParam.defaultInt;
|
||||
break;
|
||||
}
|
||||
|
||||
existingParam.type = AnimatorControllerParameterType.Float;
|
||||
|
||||
controller.Parameters = controller.Parameters.SetItem(name, existingParam);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
controller.Parameters = controller.Parameters.Add(name, parameter);
|
||||
}
|
||||
|
||||
if (merge.deleteAttachedAnimator)
|
||||
{
|
||||
var animator = merge.GetComponent<Animator>();
|
||||
if (animator != null) Object.DestroyImmediate(animator);
|
||||
}
|
||||
}
|
||||
|
||||
private VRCAvatarDescriptor.CustomAnimLayer[] FinishSessions(
|
||||
VRCAvatarDescriptor.CustomAnimLayer[] layers
|
||||
)
|
||||
{
|
||||
layers = (VRCAvatarDescriptor.CustomAnimLayer[])layers.Clone();
|
||||
|
||||
// Ensure types are consistent across layers
|
||||
Dictionary<string, AnimatorControllerParameterType> types =
|
||||
new Dictionary<string, AnimatorControllerParameterType>();
|
||||
// Learn types...
|
||||
foreach (var session in mergeSessions.Values)
|
||||
{
|
||||
session.MergeTypes(types);
|
||||
}
|
||||
// And propagate them
|
||||
foreach (var session in mergeSessions.Values)
|
||||
{
|
||||
session.MergeTypes(types);
|
||||
}
|
||||
|
||||
for (int i = 0; i < layers.Length; i++)
|
||||
{
|
||||
if (mergeSessions.TryGetValue(layers[i].type, out var session))
|
||||
{
|
||||
if (layers[i].type == VRCAvatarDescriptor.AnimLayerType.Gesture && layers[i].isDefault)
|
||||
{
|
||||
// We need to set the mask field for the gesture layer on initial configuration
|
||||
layers[i].mask = AssetDatabase.LoadAssetAtPath<AvatarMask>(
|
||||
AssetDatabase.GUIDToAssetPath(GUID_GESTURE_HANDSONLY_MASK)
|
||||
);
|
||||
}
|
||||
|
||||
layers[i].isDefault = false;
|
||||
layers[i].animatorController = session.Finish();
|
||||
}
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
private void InitSessions(VRCAvatarDescriptor.CustomAnimLayer[] layers)
|
||||
{
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var controller = ResolveLayerController(layer);
|
||||
if (controller == null) controller = new AnimatorController();
|
||||
|
||||
defaultControllers_[layer.type] = controller;
|
||||
writeDefaults_[layer.type] = ProbeWriteDefaults(controller);
|
||||
if (!layer.isDefault)
|
||||
{
|
||||
// For non-default layers, ensure we always clone the controller for the benefit of subsequent
|
||||
// processing phases
|
||||
mergeSessions[layer.type] =
|
||||
new AnimatorCombiner(_context.PluginBuildContext, layer.type.ToString());
|
||||
mergeSessions[layer.type].BlendableLayer = BlendableLayerFor(layer.type);
|
||||
mergeSessions[layer.type].AddController("", controller, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VRC_AnimatorLayerControl.BlendableLayer? BlendableLayerFor(VRCAvatarDescriptor.AnimLayerType layerType)
|
||||
{
|
||||
if (Enum.TryParse(layerType.ToString(), out VRC_AnimatorLayerControl.BlendableLayer result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool? ProbeWriteDefaults(AnimatorController controller)
|
||||
{
|
||||
if (controller == null) return null;
|
||||
|
||||
bool hasWDOn = false;
|
||||
bool hasWDOff = false;
|
||||
|
||||
var stateMachineQueue = new Queue<AnimatorStateMachine>();
|
||||
foreach (var layer in controller.layers)
|
||||
{
|
||||
// Special case: A layer with a single state, which contains a blend tree, is ignored for WD analysis.
|
||||
// This is because WD ON blend trees have different behavior from most WD ON states, and can be safely
|
||||
// used in a WD OFF animator.
|
||||
|
||||
if (layer.stateMachine.states.Length == 1
|
||||
&& layer.stateMachine.states[0].state.motion is BlendTree
|
||||
&& layer.stateMachine.stateMachines.Length == 0
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
stateMachineQueue.Enqueue(layer.stateMachine);
|
||||
}
|
||||
|
||||
while (stateMachineQueue.Count > 0)
|
||||
{
|
||||
var stateMachine = stateMachineQueue.Dequeue();
|
||||
foreach (var state in stateMachine.states)
|
||||
{
|
||||
if (state.state.writeDefaultValues) hasWDOn = true;
|
||||
else hasWDOff = true;
|
||||
}
|
||||
|
||||
foreach (var child in stateMachine.stateMachines)
|
||||
{
|
||||
stateMachineQueue.Enqueue(child.stateMachine);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasWDOn == hasWDOff) return null;
|
||||
return hasWDOn;
|
||||
}
|
||||
|
||||
|
||||
private static AnimatorController ResolveLayerController(VRCAvatarDescriptor.CustomAnimLayer layer)
|
||||
{
|
||||
AnimatorController controller = null;
|
||||
|
||||
if (!layer.isDefault && layer.animatorController != null &&
|
||||
layer.animatorController is AnimatorController c)
|
||||
{
|
||||
controller = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
string name;
|
||||
switch (layer.type)
|
||||
{
|
||||
case VRCAvatarDescriptor.AnimLayerType.Action:
|
||||
name = "Action";
|
||||
break;
|
||||
case VRCAvatarDescriptor.AnimLayerType.Additive:
|
||||
name = "Idle";
|
||||
break;
|
||||
case VRCAvatarDescriptor.AnimLayerType.Base:
|
||||
name = "Locomotion";
|
||||
break;
|
||||
case VRCAvatarDescriptor.AnimLayerType.Gesture:
|
||||
name = "Hands";
|
||||
break;
|
||||
case VRCAvatarDescriptor.AnimLayerType.Sitting:
|
||||
name = "Sitting";
|
||||
break;
|
||||
case VRCAvatarDescriptor.AnimLayerType.FX:
|
||||
name = "Face";
|
||||
break;
|
||||
case VRCAvatarDescriptor.AnimLayerType.TPose:
|
||||
name = "UtilityTPose";
|
||||
break;
|
||||
case VRCAvatarDescriptor.AnimLayerType.IKPose:
|
||||
name = "UtilityIKPose";
|
||||
break;
|
||||
default:
|
||||
name = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (name != null)
|
||||
{
|
||||
name = "/vrc_AvatarV3" + name + "Layer.controller";
|
||||
|
||||
controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(SAMPLE_PATH_PACKAGE + name);
|
||||
if (controller == null)
|
||||
{
|
||||
controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(SAMPLE_PATH_LEGACY + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,8 +31,8 @@ using VRC.SDK3.Dynamics.PhysBone.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations;
|
||||
@ -54,12 +54,13 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
#endif
|
||||
private BoneDatabase BoneDatabase = new BoneDatabase();
|
||||
|
||||
private PathMappings PathMappings => frameworkContext.Extension<AnimationServicesContext>()
|
||||
.PathMappings;
|
||||
private AnimatorServicesContext AnimatorServices => frameworkContext.Extension<AnimatorServicesContext>();
|
||||
|
||||
private HashSet<Transform> humanoidBones = new HashSet<Transform>();
|
||||
private HashSet<Transform> mergedObjects = new HashSet<Transform>();
|
||||
private HashSet<Transform> thisPassAdded = new HashSet<Transform>();
|
||||
|
||||
private HashSet<Transform> transformLookthrough = new HashSet<Transform>();
|
||||
|
||||
internal void OnPreprocessAvatar(ndmf.BuildContext context, GameObject avatarGameObject)
|
||||
{
|
||||
@ -117,7 +118,68 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
RetainBoneReferences(c as Component);
|
||||
}
|
||||
|
||||
new RetargetMeshes().OnPreprocessAvatar(avatarGameObject, BoneDatabase, PathMappings);
|
||||
new RetargetMeshes().OnPreprocessAvatar(avatarGameObject, BoneDatabase, AnimatorServices);
|
||||
|
||||
ProcessTransformLookthrough();
|
||||
}
|
||||
|
||||
private void ProcessTransformLookthrough()
|
||||
{
|
||||
var asc = frameworkContext.Extension<AnimatorServicesContext>();
|
||||
|
||||
transformLookthrough.RemoveWhere(t => !t);
|
||||
|
||||
var clipsToEdit = transformLookthrough.SelectMany(
|
||||
xform =>
|
||||
{
|
||||
var path = asc.ObjectPathRemapper.GetVirtualPathForObject(xform);
|
||||
return asc.AnimationIndex.GetClipsForObjectPath(path);
|
||||
});
|
||||
|
||||
Dictionary<string, string> parentCache = new();
|
||||
|
||||
foreach (var clip in clipsToEdit)
|
||||
{
|
||||
foreach (var binding in clip.GetFloatCurveBindings())
|
||||
{
|
||||
if (binding.type == typeof(Transform))
|
||||
{
|
||||
var newPath = GetReplacementPath(binding.path);
|
||||
|
||||
var newBinding = EditorCurveBinding.FloatCurve(newPath, binding.type, binding.propertyName);
|
||||
clip.SetFloatCurve(newBinding, clip.GetFloatCurve(binding));
|
||||
clip.SetFloatCurve(binding, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string GetReplacementPath(string bindingPath)
|
||||
{
|
||||
if (parentCache.TryGetValue(bindingPath, out var cached))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
var obj = asc.ObjectPathRemapper.GetObjectForPath(bindingPath)!.transform;
|
||||
while (obj != null && transformLookthrough.Contains(obj))
|
||||
{
|
||||
obj = obj.parent;
|
||||
}
|
||||
|
||||
string path;
|
||||
if (obj == null)
|
||||
{
|
||||
path = bindingPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = asc.ObjectPathRemapper.GetVirtualPathForObject(obj);
|
||||
}
|
||||
|
||||
parentCache[bindingPath] = path;
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
private void TopoProcessMergeArmatures(ModularAvatarMergeArmature[] mergeArmatures)
|
||||
@ -276,6 +338,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
_activeRetargeter.FixupAnimations();
|
||||
|
||||
thisPassAdded.UnionWith(_activeRetargeter.AddedGameObjects.Select(x => x.transform));
|
||||
transformLookthrough.UnionWith(_activeRetargeter.AddedGameObjects.Select(x => x.transform));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -339,7 +402,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
BoneDatabase.AddMergedBone(mergedSrcBone.transform);
|
||||
BoneDatabase.RetainMergedBone(mergedSrcBone.transform);
|
||||
PathMappings.MarkTransformLookthrough(mergedSrcBone);
|
||||
transformLookthrough.Add(mergedSrcBone.transform);
|
||||
thisPassAdded.Add(mergedSrcBone.transform);
|
||||
}
|
||||
|
||||
@ -354,7 +417,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
if (zipMerge)
|
||||
{
|
||||
PathMappings.MarkTransformLookthrough(src);
|
||||
transformLookthrough.Add(src.transform);
|
||||
BoneDatabase.AddMergedBone(src.transform);
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,13 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using nadena.dev.ndmf.util;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEditor.Search;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
|
||||
@ -20,56 +23,49 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
internal const string ALWAYS_ONE = "__ModularAvatarInternal/One";
|
||||
internal const string BlendTreeLayerName = "ModularAvatar: Merge Blend Tree";
|
||||
|
||||
private AnimatorController _controller;
|
||||
private BlendTree _rootBlendTree;
|
||||
private GameObject _mergeHost;
|
||||
private AnimatorServicesContext _asc;
|
||||
private VirtualBlendTree _rootBlendTree;
|
||||
private HashSet<string> _parameterNames;
|
||||
|
||||
protected override void Execute(ndmf.BuildContext context)
|
||||
{
|
||||
_asc = context.Extension<AnimatorServicesContext>();
|
||||
_rootBlendTree = null;
|
||||
_parameterNames = new HashSet<string>();
|
||||
_controller = new AnimatorController();
|
||||
|
||||
var fx = _asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX];
|
||||
|
||||
foreach (var component in
|
||||
context.AvatarRootObject.GetComponentsInChildren<ModularAvatarMergeBlendTree>(true))
|
||||
{
|
||||
ErrorReport.WithContextObject(component, () => ProcessComponent(context, component));
|
||||
}
|
||||
|
||||
List<AnimatorControllerParameter> parameters = new List<AnimatorControllerParameter>(_parameterNames.Count + 1);
|
||||
if (_mergeHost != null)
|
||||
|
||||
// always add the ALWAYS_ONE parameter
|
||||
fx.Parameters = fx.Parameters.SetItem(ALWAYS_ONE, new AnimatorControllerParameter()
|
||||
{
|
||||
_parameterNames.Remove(ALWAYS_ONE);
|
||||
name = ALWAYS_ONE,
|
||||
type = AnimatorControllerParameterType.Float,
|
||||
defaultFloat = 1
|
||||
});
|
||||
|
||||
parameters.Add(new AnimatorControllerParameter()
|
||||
{
|
||||
name = ALWAYS_ONE,
|
||||
type = AnimatorControllerParameterType.Float,
|
||||
defaultFloat = 1
|
||||
});
|
||||
foreach (var name in _parameterNames)
|
||||
{
|
||||
if (fx.Parameters.ContainsKey(name)) continue;
|
||||
|
||||
foreach (var name in _parameterNames)
|
||||
fx.Parameters = fx.Parameters.SetItem(name, new AnimatorControllerParameter()
|
||||
{
|
||||
parameters.Add(new AnimatorControllerParameter()
|
||||
{
|
||||
name = name,
|
||||
type = AnimatorControllerParameterType.Float,
|
||||
defaultFloat = 0
|
||||
});
|
||||
}
|
||||
|
||||
var paramsAnimator = new AnimatorController();
|
||||
paramsAnimator.parameters = parameters.ToArray();
|
||||
|
||||
var paramsComponent = _mergeHost.AddComponent<ModularAvatarMergeAnimator>();
|
||||
paramsComponent.animator = paramsAnimator;
|
||||
paramsComponent.layerPriority = Int32.MaxValue;
|
||||
name = name,
|
||||
type = AnimatorControllerParameterType.Float,
|
||||
defaultFloat = 1.0f
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessComponent(ndmf.BuildContext context, ModularAvatarMergeBlendTree component)
|
||||
private void ProcessComponent(BuildContext context, ModularAvatarMergeBlendTree component)
|
||||
{
|
||||
var stash = context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||
|
||||
BlendTree componentBlendTree = component.BlendTree as BlendTree;
|
||||
|
||||
if (componentBlendTree == null)
|
||||
@ -79,46 +75,60 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
|
||||
string basePath = null;
|
||||
string rootPath = null;
|
||||
if (component.PathMode == MergeAnimatorPathMode.Relative)
|
||||
{
|
||||
var root = component.RelativePathRoot.Get(context.AvatarRootTransform);
|
||||
if (root == null) root = component.gameObject;
|
||||
|
||||
basePath = RuntimeUtil.AvatarRootPath(root) + "/";
|
||||
rootPath = RuntimeUtil.AvatarRootPath(root);
|
||||
basePath = rootPath + "/";
|
||||
}
|
||||
|
||||
var bt = stash.BlendTrees.GetValueOrDefault(component)
|
||||
?? _asc.ControllerContext.CloneContext.Clone(componentBlendTree);
|
||||
|
||||
if (basePath != null)
|
||||
{
|
||||
var animationIndex = new AnimationIndex(new[] { bt });
|
||||
animationIndex.RewritePaths(p => p == "" ? rootPath : basePath + p);
|
||||
}
|
||||
|
||||
var bt = new DeepClone(context).DoClone(componentBlendTree, basePath);
|
||||
var rootBlend = GetRootBlendTree(context);
|
||||
var rootBlend = GetRootBlendTree();
|
||||
|
||||
rootBlend.AddChild(bt);
|
||||
var children = rootBlend.children;
|
||||
children[children.Length - 1].directBlendParameter = ALWAYS_ONE;
|
||||
rootBlend.children = children;
|
||||
|
||||
foreach (var asset in bt.ReferencedAssets(includeScene: false))
|
||||
rootBlend.Children = rootBlend.Children.Add(new()
|
||||
{
|
||||
if (asset is BlendTree bt2)
|
||||
Motion = bt,
|
||||
DirectBlendParameter = ALWAYS_ONE,
|
||||
Threshold = 1,
|
||||
CycleOffset = 1,
|
||||
TimeScale = 1,
|
||||
});
|
||||
|
||||
foreach (var asset in bt.AllReachableNodes())
|
||||
{
|
||||
if (asset is VirtualBlendTree bt2)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(bt2.blendParameter) && bt2.blendType != BlendTreeType.Direct)
|
||||
if (!string.IsNullOrEmpty(bt2.BlendParameter) && bt2.BlendType != BlendTreeType.Direct)
|
||||
{
|
||||
_parameterNames.Add(bt2.blendParameter);
|
||||
_parameterNames.Add(bt2.BlendParameter);
|
||||
}
|
||||
|
||||
if (bt2.blendType != BlendTreeType.Direct && bt2.blendType != BlendTreeType.Simple1D)
|
||||
if (bt2.BlendType != BlendTreeType.Direct && bt2.BlendType != BlendTreeType.Simple1D)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(bt2.blendParameterY))
|
||||
if (!string.IsNullOrEmpty(bt2.BlendParameterY))
|
||||
{
|
||||
_parameterNames.Add(bt2.blendParameterY);
|
||||
_parameterNames.Add(bt2.BlendParameterY);
|
||||
}
|
||||
}
|
||||
|
||||
if (bt2.blendType == BlendTreeType.Direct)
|
||||
if (bt2.BlendType == BlendTreeType.Direct)
|
||||
{
|
||||
foreach (var childMotion in bt2.children)
|
||||
foreach (var childMotion in bt2.Children)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(childMotion.directBlendParameter))
|
||||
if (!string.IsNullOrEmpty(childMotion.DirectBlendParameter))
|
||||
{
|
||||
_parameterNames.Add(childMotion.directBlendParameter);
|
||||
_parameterNames.Add(childMotion.DirectBlendParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,59 +136,22 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
|
||||
private BlendTree GetRootBlendTree(ndmf.BuildContext context)
|
||||
private VirtualBlendTree GetRootBlendTree()
|
||||
{
|
||||
if (_rootBlendTree != null) return _rootBlendTree;
|
||||
|
||||
var newController = new AnimatorController();
|
||||
var newStateMachine = new AnimatorStateMachine();
|
||||
var newState = new AnimatorState();
|
||||
|
||||
_rootBlendTree = new BlendTree();
|
||||
_controller = newController;
|
||||
|
||||
newController.layers = new[]
|
||||
{
|
||||
new AnimatorControllerLayer
|
||||
{
|
||||
blendingMode = AnimatorLayerBlendingMode.Override,
|
||||
defaultWeight = 1,
|
||||
name = BlendTreeLayerName,
|
||||
stateMachine = newStateMachine
|
||||
}
|
||||
};
|
||||
|
||||
newStateMachine.name = "ModularAvatarMergeBlendTree";
|
||||
newStateMachine.states = new[]
|
||||
{
|
||||
new ChildAnimatorState
|
||||
{
|
||||
state = newState,
|
||||
position = Vector3.zero
|
||||
}
|
||||
};
|
||||
newStateMachine.defaultState = newState;
|
||||
var fx = _asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX];
|
||||
var controller = fx.AddLayer(new LayerPriority(int.MinValue), BlendTreeLayerName);
|
||||
var stateMachine = controller.StateMachine;
|
||||
|
||||
newState.writeDefaultValues = true;
|
||||
newState.motion = _rootBlendTree;
|
||||
|
||||
_rootBlendTree.blendType = BlendTreeType.Direct;
|
||||
_rootBlendTree.blendParameter = ALWAYS_ONE;
|
||||
_rootBlendTree = VirtualBlendTree.Create("Root");
|
||||
var state = stateMachine.AddState("State", _rootBlendTree);
|
||||
stateMachine.DefaultState = state;
|
||||
state.WriteDefaultValues = true;
|
||||
|
||||
var mergeObject = new GameObject("ModularAvatarMergeBlendTree");
|
||||
var merger = mergeObject.AddComponent<ModularAvatarMergeAnimator>();
|
||||
merger.animator = newController;
|
||||
merger.pathMode = MergeAnimatorPathMode.Absolute;
|
||||
merger.matchAvatarWriteDefaults = false;
|
||||
merger.layerType = VRCAvatarDescriptor.AnimLayerType.FX;
|
||||
merger.deleteAttachedAnimator = false;
|
||||
merger.layerPriority = Int32.MinValue;
|
||||
_rootBlendTree.BlendType = BlendTreeType.Direct;
|
||||
_rootBlendTree.BlendParameter = ALWAYS_ONE;
|
||||
|
||||
mergeObject.transform.SetParent(context.AvatarRootTransform, false);
|
||||
mergeObject.transform.SetSiblingIndex(0);
|
||||
|
||||
_mergeHost = mergeObject;
|
||||
|
||||
return _rootBlendTree;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEngine;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
@ -84,13 +85,15 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
internal class RetargetMeshes
|
||||
{
|
||||
private BoneDatabase _boneDatabase;
|
||||
private PathMappings _pathTracker;
|
||||
private AnimationIndex _animationIndex;
|
||||
private ObjectPathRemapper _pathRemapper;
|
||||
|
||||
internal void OnPreprocessAvatar(GameObject avatarGameObject, BoneDatabase boneDatabase,
|
||||
PathMappings pathMappings)
|
||||
AnimatorServicesContext pathMappings)
|
||||
{
|
||||
this._boneDatabase = boneDatabase;
|
||||
this._pathTracker = pathMappings;
|
||||
this._animationIndex = pathMappings.AnimationIndex;
|
||||
this._pathRemapper = pathMappings.ObjectPathRemapper;
|
||||
|
||||
foreach (var renderer in avatarGameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true))
|
||||
{
|
||||
@ -153,7 +156,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
child.SetParent(destBone, true);
|
||||
}
|
||||
|
||||
_pathTracker.MarkRemoved(sourceBone.gameObject);
|
||||
// Remap any animation clips that reference this bone into its parent
|
||||
_pathRemapper.ReplaceObject(sourceBone.gameObject, sourceBone.transform.parent.gameObject);
|
||||
UnityEngine.Object.DestroyImmediate(sourceBone.gameObject);
|
||||
}
|
||||
}
|
||||
|
@ -48,17 +48,20 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
seq.Run(ClearEditorOnlyTags.Instance);
|
||||
seq.Run(MeshSettingsPluginPass.Instance);
|
||||
seq.Run(ScaleAdjusterPass.Instance).PreviewingWith(new ScaleAdjusterPreview());
|
||||
|
||||
// All these need to move to the new ASC
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.Run(ReactiveObjectPrepass.Instance);
|
||||
seq.Run(RenameParametersPluginPass.Instance);
|
||||
seq.Run(ParameterAssignerPass.Instance);
|
||||
seq.Run(MergeBlendTreePass.Instance);
|
||||
seq.Run(MergeAnimatorPluginPass.Instance);
|
||||
seq.Run(ApplyAnimatorDefaultValuesPass.Instance);
|
||||
#endif
|
||||
seq.WithRequiredExtension(typeof(AnimatorServicesContext), _s2 =>
|
||||
{
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.Run(RenameParametersPluginPass.Instance);
|
||||
seq.Run(ParameterAssignerPass.Instance);
|
||||
seq.Run(MergeBlendTreePass.Instance);
|
||||
seq.Run(MergeAnimatorPluginPass.Instance);
|
||||
seq.Run(ApplyAnimatorDefaultValuesPass.Instance);
|
||||
|
||||
seq.WithRequiredExtension(typeof(ReadablePropertyExtension), _s3 =>
|
||||
{
|
||||
seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute())
|
||||
@ -66,19 +69,18 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
new MaterialSetterPreview());
|
||||
});
|
||||
seq.Run(GameObjectDelayDisablePass.Instance);
|
||||
#endif
|
||||
});
|
||||
|
||||
seq.WithRequiredExtension(typeof(AnimationServicesContext), _s2 =>
|
||||
{
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
|
||||
// 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?
|
||||
seq.Run(MenuInstallPluginPass.Instance);
|
||||
#endif
|
||||
|
||||
seq.Run(MergeArmaturePluginPass.Instance);
|
||||
|
||||
});
|
||||
|
||||
seq.WithRequiredExtension(typeof(AnimationServicesContext), _s2 =>
|
||||
{
|
||||
seq.Run(BoneProxyPluginPass.Instance);
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.Run(VisibleHeadAccessoryPluginPass.Instance);
|
||||
|
@ -39,7 +39,14 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
// Having a WD OFF layer after WD ON layers can break WD. We match the behavior of the existing states,
|
||||
// and if mixed, use WD ON to maximize compatibility.
|
||||
_writeDefaults = MergeAnimatorProcessor.ProbeWriteDefaults(FindFxController().animatorController as AnimatorController) ?? true;
|
||||
var asc = context.Extension<AnimatorServicesContext>();
|
||||
_writeDefaults = asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX]?.Layers.Any(
|
||||
l => l.StateMachine.StateMachines.Any(
|
||||
sm => sm.StateMachine.AllStates().Any(
|
||||
s => s.WriteDefaultValues && s.Motion is not VirtualBlendTree
|
||||
)
|
||||
)
|
||||
) ?? true;
|
||||
|
||||
var analysis = new ReactiveObjectAnalyzer(context).Analyze(context.AvatarRootObject);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
@ -201,18 +202,20 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
if (mamiWithRC.Count > 0)
|
||||
{
|
||||
// This make sures the parameters are correctly merged into the FX layer.
|
||||
var mergeAnimator = context.AvatarRootObject.AddComponent<ModularAvatarMergeAnimator>();
|
||||
mergeAnimator.layerType = VRCAvatarDescriptor.AnimLayerType.FX;
|
||||
mergeAnimator.deleteAttachedAnimator = false;
|
||||
mergeAnimator.animator = new AnimatorController
|
||||
var asc = context.Extension<AnimatorServicesContext>();
|
||||
var fx = asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX];
|
||||
|
||||
foreach (var (name, _) in mamiWithRC)
|
||||
{
|
||||
parameters = mamiWithRC.Select(kvp => new AnimatorControllerParameter
|
||||
if (!fx.Parameters.ContainsKey(name))
|
||||
{
|
||||
name = kvp.Key,
|
||||
type = AnimatorControllerParameterType.Float,
|
||||
}).ToArray(),
|
||||
};
|
||||
fx.Parameters = fx.Parameters.SetItem(name, new()
|
||||
{
|
||||
name = name,
|
||||
type = AnimatorControllerParameterType.Float,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
@ -55,6 +56,43 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
public ImmutableDictionary<string, float> InitialValueOverrides;
|
||||
}
|
||||
|
||||
internal class RenamedMergeAnimators
|
||||
{
|
||||
public AnimatorServicesContext AnimatorServices;
|
||||
public Dictionary<ModularAvatarMergeAnimator, VirtualAnimatorController> Controllers = new();
|
||||
public Dictionary<ModularAvatarMergeBlendTree, VirtualBlendTree> BlendTrees = new();
|
||||
|
||||
public VirtualAnimatorController Clone(ModularAvatarMergeAnimator mama)
|
||||
{
|
||||
if (Controllers.TryGetValue(mama, out var controller))
|
||||
{
|
||||
return controller;
|
||||
}
|
||||
|
||||
if (mama.animator == null) return null;
|
||||
|
||||
var cloned = AnimatorServices.ControllerContext.CloneContext.Clone(mama.animator);
|
||||
Controllers[mama] = cloned;
|
||||
|
||||
return cloned;
|
||||
}
|
||||
|
||||
public VirtualBlendTree Clone(ModularAvatarMergeBlendTree mbt)
|
||||
{
|
||||
if (BlendTrees.TryGetValue(mbt, out var blendTree))
|
||||
{
|
||||
return blendTree;
|
||||
}
|
||||
|
||||
if (mbt.BlendTree is not BlendTree bt) return null;
|
||||
|
||||
var cloned = (VirtualBlendTree)AnimatorServices.ControllerContext.CloneContext.Clone(bt);
|
||||
BlendTrees[mbt] = cloned;
|
||||
|
||||
return cloned;
|
||||
}
|
||||
}
|
||||
|
||||
internal class RenameParametersHook
|
||||
{
|
||||
@ -163,6 +201,10 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
if (!context.AvatarDescriptor) return;
|
||||
|
||||
_context = context;
|
||||
|
||||
var stash = _context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||
var asc = _context.PluginBuildContext.Extension<AnimatorServicesContext>();
|
||||
stash.AnimatorServices = asc;
|
||||
|
||||
var syncParams = WalkTree(avatar);
|
||||
|
||||
@ -389,11 +431,13 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
if (merger.animator != null)
|
||||
{
|
||||
Profiler.BeginSample("DeepCloneAnimator");
|
||||
merger.animator = new DeepClone(_context.PluginBuildContext).DoClone(merger.animator);
|
||||
Profiler.EndSample();
|
||||
var stash = _context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||
|
||||
ProcessRuntimeAnimatorController(merger.animator, remap);
|
||||
var controller = stash.Clone(merger);
|
||||
|
||||
ProcessVirtualAnimatorController(controller, remap);
|
||||
|
||||
stash.Controllers[merger] = controller;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -404,8 +448,12 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
var bt = merger.BlendTree as BlendTree;
|
||||
if (bt != null)
|
||||
{
|
||||
merger.BlendTree = bt = new DeepClone(_context.PluginBuildContext).DoClone(bt);
|
||||
ProcessBlendtree(bt, paramInfo.GetParameterRemappingsAt(obj));
|
||||
var stash = _context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||
|
||||
var virtualbt = stash.Clone(merger);
|
||||
ProcessBlendtree(virtualbt, paramInfo.GetParameterRemappingsAt(obj));
|
||||
|
||||
stash.BlendTrees[merger] = virtualbt;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -485,28 +533,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void ProcessRuntimeAnimatorController(RuntimeAnimatorController controller,
|
||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remap)
|
||||
{
|
||||
if (controller is AnimatorController ac)
|
||||
{
|
||||
ProcessAnimator(ac, remap);
|
||||
}
|
||||
else if (controller is AnimatorOverrideController aoc)
|
||||
{
|
||||
var list = new List<KeyValuePair<AnimationClip, AnimationClip>>();
|
||||
aoc.GetOverrides(list);
|
||||
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
var kvp = list[i];
|
||||
if (kvp.Value != null) ProcessClip(kvp.Value, remap);
|
||||
}
|
||||
|
||||
ProcessRuntimeAnimatorController(aoc.runtimeAnimatorController, remap);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessMenuInstaller(ModularAvatarMenuInstaller installer,
|
||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
{
|
||||
@ -525,113 +551,57 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
});
|
||||
}
|
||||
|
||||
private void ProcessAnimator(AnimatorController controller,
|
||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
private void ProcessVirtualAnimatorController(VirtualAnimatorController controller,
|
||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remap)
|
||||
{
|
||||
if (remaps.IsEmpty) return;
|
||||
foreach (var node in controller.AllReachableNodes())
|
||||
{
|
||||
switch (node)
|
||||
{
|
||||
case VirtualState vs: ProcessState(vs, remap); break;
|
||||
case VirtualTransition vt: ProcessTransition(vt, remap); break;
|
||||
case VirtualClip vc: ProcessClip(vc, remap); break;
|
||||
case VirtualBlendTree bt: ProcessBlendtree(bt, remap); break;
|
||||
}
|
||||
}
|
||||
|
||||
var newParameters = controller.Parameters.Clear();
|
||||
|
||||
foreach (var (name, parameter) in controller.Parameters)
|
||||
{
|
||||
if (remap.TryGetValue((ParameterNamespace.Animator, name), out var newParam))
|
||||
{
|
||||
newParameters = newParameters.Add(newParam.ParameterName, parameter);
|
||||
}
|
||||
else
|
||||
{
|
||||
newParameters = newParameters.Add(name, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
var visited = new HashSet<AnimatorStateMachine>();
|
||||
var queue = new Queue<AnimatorStateMachine>();
|
||||
|
||||
|
||||
var parameters = controller.parameters;
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (remaps.TryGetValue((ParameterNamespace.Animator, parameters[i].name), out var newName))
|
||||
{
|
||||
parameters[i].name = newName.ParameterName;
|
||||
}
|
||||
}
|
||||
|
||||
controller.parameters = parameters;
|
||||
|
||||
foreach (var layer in controller.layers)
|
||||
{
|
||||
if (layer.stateMachine != null)
|
||||
{
|
||||
queue.Enqueue(layer.stateMachine);
|
||||
}
|
||||
}
|
||||
|
||||
Profiler.BeginSample("Walk animator graph");
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var sm = queue.Dequeue();
|
||||
if (visited.Contains(sm)) continue;
|
||||
visited.Add(sm);
|
||||
|
||||
foreach (var behavior in sm.behaviours)
|
||||
{
|
||||
if (behavior is VRCAvatarParameterDriver driver)
|
||||
{
|
||||
ProcessDriver(driver, remaps);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var t in sm.anyStateTransitions)
|
||||
{
|
||||
ProcessTransition(t, remaps);
|
||||
}
|
||||
|
||||
foreach (var t in sm.entryTransitions)
|
||||
{
|
||||
ProcessTransition(t, remaps);
|
||||
}
|
||||
|
||||
foreach (var sub in sm.stateMachines)
|
||||
{
|
||||
queue.Enqueue(sub.stateMachine);
|
||||
|
||||
|
||||
foreach (var t in sm.GetStateMachineTransitions(sub.stateMachine))
|
||||
{
|
||||
ProcessTransition(t, remaps);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var st in sm.states)
|
||||
{
|
||||
ProcessState(st.state, remaps);
|
||||
}
|
||||
}
|
||||
Profiler.EndSample();
|
||||
controller.Parameters = newParameters;
|
||||
}
|
||||
|
||||
private void ProcessState(AnimatorState state, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
private void ProcessState(VirtualState state, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
{
|
||||
state.mirrorParameter = remap(remaps, state.mirrorParameter);
|
||||
state.timeParameter = remap(remaps, state.timeParameter);
|
||||
state.speedParameter = remap(remaps, state.speedParameter);
|
||||
state.cycleOffsetParameter = remap(remaps, state.cycleOffsetParameter);
|
||||
state.MirrorParameter = remap(remaps, state.MirrorParameter);
|
||||
state.TimeParameter = remap(remaps, state.TimeParameter);
|
||||
state.SpeedParameter = remap(remaps, state.SpeedParameter);
|
||||
state.CycleOffsetParameter = remap(remaps, state.CycleOffsetParameter);
|
||||
|
||||
foreach (var t in state.transitions)
|
||||
{
|
||||
ProcessTransition(t, remaps);
|
||||
}
|
||||
|
||||
foreach (var behavior in state.behaviours)
|
||||
foreach (var behavior in state.Behaviours)
|
||||
{
|
||||
if (behavior is VRCAvatarParameterDriver driver)
|
||||
{
|
||||
ProcessDriver(driver, remaps);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessMotion(state.motion, remaps);
|
||||
}
|
||||
|
||||
private void ProcessMotion(Motion motion,
|
||||
private void ProcessClip(VirtualClip clip,
|
||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
{
|
||||
if (motion is BlendTree blendTree) ProcessBlendtree(blendTree, remaps);
|
||||
|
||||
if (motion is AnimationClip clip) ProcessClip(clip, remaps);
|
||||
}
|
||||
|
||||
private void ProcessClip(AnimationClip clip,
|
||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
{
|
||||
var curveBindings = AnimationUtility.GetCurveBindings(clip);
|
||||
var curveBindings = clip.GetFloatCurveBindings();
|
||||
|
||||
var bindingsToUpdate = new List<EditorCurveBinding>();
|
||||
var newCurves = new List<AnimationCurve>();
|
||||
@ -641,48 +611,30 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
if (binding.path != "" || binding.type != typeof(Animator)) continue;
|
||||
if (remaps.TryGetValue((ParameterNamespace.Animator, binding.propertyName), out var newBinding))
|
||||
{
|
||||
var curCurve = AnimationUtility.GetEditorCurve(clip, binding);
|
||||
|
||||
bindingsToUpdate.Add(binding);
|
||||
newCurves.Add(null);
|
||||
|
||||
bindingsToUpdate.Add(new EditorCurveBinding
|
||||
var curCurve = clip.GetFloatCurve(binding);
|
||||
var newECB = new EditorCurveBinding
|
||||
{
|
||||
path = "",
|
||||
type = typeof(Animator),
|
||||
propertyName = newBinding.ParameterName
|
||||
});
|
||||
newCurves.Add(curCurve);
|
||||
};
|
||||
|
||||
clip.SetFloatCurve(binding, null);
|
||||
clip.SetFloatCurve(newECB, curCurve);
|
||||
}
|
||||
}
|
||||
|
||||
if (bindingsToUpdate.Any())
|
||||
{
|
||||
AnimationUtility.SetEditorCurves(clip, bindingsToUpdate.ToArray(), newCurves.ToArray());
|
||||
|
||||
// Workaround apparent unity bug where the clip's curves are not deleted
|
||||
for (var i = 0; i < bindingsToUpdate.Count; i++)
|
||||
if (newCurves[i] == null && AnimationUtility.GetEditorCurve(clip, bindingsToUpdate[i]) != null)
|
||||
AnimationUtility.SetEditorCurve(clip, bindingsToUpdate[i], newCurves[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessBlendtree(BlendTree blendTree, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
private void ProcessBlendtree(VirtualBlendTree blendTree, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
{
|
||||
blendTree.blendParameter = remap(remaps, blendTree.blendParameter);
|
||||
blendTree.blendParameterY = remap(remaps, blendTree.blendParameterY);
|
||||
blendTree.BlendParameter = remap(remaps, blendTree.BlendParameter);
|
||||
blendTree.BlendParameterY = remap(remaps, blendTree.BlendParameterY);
|
||||
|
||||
var children = blendTree.children;
|
||||
for (int i = 0; i < children.Length; i++)
|
||||
var children = blendTree.Children;
|
||||
foreach (var child in children)
|
||||
{
|
||||
var childMotion = children[i];
|
||||
ProcessMotion(childMotion.motion, remaps);
|
||||
|
||||
childMotion.directBlendParameter = remap(remaps, childMotion.directBlendParameter);
|
||||
children[i] = childMotion;
|
||||
child.DirectBlendParameter = remap(remaps, child.DirectBlendParameter);
|
||||
}
|
||||
|
||||
blendTree.children = children;
|
||||
}
|
||||
|
||||
private void ProcessDriver(VRCAvatarParameterDriver driver, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
@ -698,19 +650,17 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessTransition(AnimatorTransitionBase t, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
private void ProcessTransition(VirtualTransitionBase t, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||
{
|
||||
bool dirty = false;
|
||||
var conditions = t.conditions;
|
||||
|
||||
for (int i = 0; i < conditions.Length; i++)
|
||||
{
|
||||
var cond = conditions[i];
|
||||
cond.parameter = remap(remaps, cond.parameter, ref dirty);
|
||||
conditions[i] = cond;
|
||||
}
|
||||
|
||||
if (dirty) t.conditions = conditions;
|
||||
var conditions = t.Conditions
|
||||
.Select(cond =>
|
||||
{
|
||||
cond.parameter = remap(remaps, cond.parameter, ref dirty);
|
||||
return cond;
|
||||
})
|
||||
.ToImmutableList();
|
||||
t.Conditions = conditions;
|
||||
}
|
||||
|
||||
private ImmutableDictionary<string, ParameterInfo> CollectParameters(ModularAvatarParameters p,
|
||||
|
@ -1,11 +1,14 @@
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
|
||||
using System.Linq;
|
||||
using modular_avatar_tests;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
using EditorCurveBinding = UnityEditor.EditorCurveBinding;
|
||||
|
||||
public class ActiveAnimationRetargeterTests : TestBase
|
||||
@ -17,8 +20,7 @@ public class ActiveAnimationRetargeterTests : TestBase
|
||||
|
||||
// initialize context
|
||||
var buildContext = new BuildContext(avatar);
|
||||
var pathMappings = buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>()
|
||||
.PathMappings;
|
||||
var asc = buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
// get game objects
|
||||
var changedChild = avatar.transform.Find("Toggled/Child");
|
||||
@ -29,18 +31,16 @@ public class ActiveAnimationRetargeterTests : TestBase
|
||||
var created = retargeter.CreateIntermediateObjects(newParent.gameObject);
|
||||
retargeter.FixupAnimations();
|
||||
|
||||
// commit
|
||||
buildContext.AnimationDatabase.Commit();
|
||||
|
||||
var clip = findFxClip(avatar, layerName: "retarget");
|
||||
var curveBindings = AnimationUtility.GetCurveBindings(clip);
|
||||
var fx = asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX]!;
|
||||
var clip = (VirtualClip) fx.Layers.First(l => l.Name == "retarget").StateMachine.DefaultState!.Motion;
|
||||
var curveBindings = clip!.GetFloatCurveBindings();
|
||||
|
||||
// Intermediate object must be created
|
||||
Assert.That(created, Is.Not.EqualTo(newParent.gameObject));
|
||||
|
||||
// The created animation must have m_IsActive of intermediate object
|
||||
Assert.That(curveBindings, Does.Contain(EditorCurveBinding.FloatCurve(
|
||||
pathMappings.GetObjectIdentifier(created), typeof(GameObject), "m_IsActive")));
|
||||
asc.ObjectPathRemapper.GetVirtualPathForObject(created), typeof(GameObject), "m_IsActive")));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.core;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using BuildContext = nadena.dev.ndmf.BuildContext;
|
||||
@ -22,9 +23,13 @@ namespace UnitTests.MergeAnimatorTests
|
||||
|
||||
var ctx = new BuildContext(av, null);
|
||||
ctx.ActivateExtensionContext<ModularAvatarContext>();
|
||||
ctx.ActivateExtensionContext<AnimationServicesContext>();
|
||||
ctx.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
var errors = ErrorReport.CaptureErrors(() => new MergeAnimatorProcessor().OnPreprocessAvatar(av, ctx));
|
||||
var errors = ErrorReport.CaptureErrors(() =>
|
||||
{
|
||||
new MergeAnimatorProcessor().OnPreprocessAvatar(av, ctx);
|
||||
ctx.DeactivateAllExtensionContexts();
|
||||
});
|
||||
|
||||
Assert.IsEmpty(errors);
|
||||
}
|
||||
@ -39,9 +44,15 @@ namespace UnitTests.MergeAnimatorTests
|
||||
|
||||
var ctx = new BuildContext(av, null);
|
||||
ctx.ActivateExtensionContext<ModularAvatarContext>();
|
||||
ctx.ActivateExtensionContext<AnimationServicesContext>();
|
||||
ctx.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
var errors = ErrorReport.CaptureErrors(() => new MergeAnimatorProcessor().OnPreprocessAvatar(av, ctx));
|
||||
var errors = ErrorReport.CaptureErrors(() =>
|
||||
{
|
||||
new MergeAnimatorProcessor().OnPreprocessAvatar(av, ctx);
|
||||
ctx.DeactivateAllExtensionContexts();
|
||||
});
|
||||
|
||||
ctx.DeactivateAllExtensionContexts();
|
||||
|
||||
Assert.IsEmpty(errors);
|
||||
|
||||
|
@ -25,6 +25,8 @@ public class PreexistingParamsTest : TestBase
|
||||
|
||||
foreach (var kvp in paramDict)
|
||||
{
|
||||
if (kvp.Key.StartsWith("__ModularAvatarInternal/")) continue;
|
||||
|
||||
if (kvp.Key == "default_override" || kvp.Key == "animator_only")
|
||||
{
|
||||
Assert.AreEqual(1, kvp.Value);
|
||||
|
@ -2,6 +2,7 @@ using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.core;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
@ -54,7 +55,7 @@ namespace modular_avatar_tests.MergeArmatureTests
|
||||
nadena.dev.ndmf.BuildContext context =
|
||||
new nadena.dev.ndmf.BuildContext(root, null);
|
||||
context.ActivateExtensionContext<ModularAvatarContext>();
|
||||
context.ActivateExtensionContext<AnimationServicesContext>();
|
||||
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
||||
|
||||
Assert.IsTrue(bone.GetComponentInChildren<TestComponentA>() != null);
|
||||
@ -82,7 +83,7 @@ namespace modular_avatar_tests.MergeArmatureTests
|
||||
nadena.dev.ndmf.BuildContext context =
|
||||
new nadena.dev.ndmf.BuildContext(root, null);
|
||||
context.ActivateExtensionContext<ModularAvatarContext>();
|
||||
context.ActivateExtensionContext<AnimationServicesContext>();
|
||||
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
||||
|
||||
Assert.IsTrue(m_bone == null); // destroyed by retargeting pass
|
||||
@ -106,7 +107,7 @@ namespace modular_avatar_tests.MergeArmatureTests
|
||||
nadena.dev.ndmf.BuildContext context =
|
||||
new nadena.dev.ndmf.BuildContext(root, null);
|
||||
context.ActivateExtensionContext<ModularAvatarContext>();
|
||||
context.ActivateExtensionContext<AnimationServicesContext>();
|
||||
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
||||
|
||||
Assert.IsTrue(m_bone == null); // destroyed by retargeting pass
|
||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||
using nadena.dev.modular_avatar.core;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
@ -79,6 +80,7 @@ namespace modular_avatar_tests.RenameParametersTests
|
||||
|
||||
var context = CreateContext(prefab);
|
||||
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
||||
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
var errors = ErrorReport.CaptureErrors(
|
||||
() =>
|
||||
@ -209,6 +211,7 @@ namespace modular_avatar_tests.RenameParametersTests
|
||||
|
||||
var context = CreateContext(av);
|
||||
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
||||
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
var errors = ErrorReport.CaptureErrors(() => new RenameParametersHook().OnPreprocessAvatar(av, maContext));
|
||||
|
||||
@ -243,6 +246,7 @@ namespace modular_avatar_tests.RenameParametersTests
|
||||
|
||||
var context = CreateContext(av);
|
||||
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
||||
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
var errors = ErrorReport.CaptureErrors(() => new RenameParametersHook().OnPreprocessAvatar(av, maContext));
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
@ -21,13 +22,12 @@ namespace modular_avatar_tests
|
||||
Debug.Assert(skinnedMeshRenderer.bones.Length == 0);
|
||||
|
||||
var build_context = new nadena.dev.ndmf.BuildContext(root, null);
|
||||
var torc = new AnimationServicesContext();
|
||||
torc.OnActivate(build_context);
|
||||
var asc = build_context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
var bonedb = new BoneDatabase();
|
||||
bonedb.AddMergedBone(b.transform);
|
||||
|
||||
new RetargetMeshes().OnPreprocessAvatar(root, bonedb, torc.PathMappings);
|
||||
new RetargetMeshes().OnPreprocessAvatar(root, bonedb, asc);
|
||||
|
||||
Assert.AreEqual(a.transform, skinnedMeshRenderer.rootBone);
|
||||
}
|
||||
@ -47,13 +47,12 @@ namespace modular_avatar_tests
|
||||
Debug.Assert(skinnedMeshRenderer.bones.Length == 0);
|
||||
|
||||
var build_context = new nadena.dev.ndmf.BuildContext(root, null);
|
||||
var torc = new AnimationServicesContext();
|
||||
torc.OnActivate(build_context);
|
||||
var asc = build_context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
var bonedb = new BoneDatabase();
|
||||
bonedb.AddMergedBone(b.transform);
|
||||
|
||||
new RetargetMeshes().OnPreprocessAvatar(root, bonedb, torc.PathMappings);
|
||||
new RetargetMeshes().OnPreprocessAvatar(root, bonedb, asc);
|
||||
|
||||
Assert.AreEqual(a.transform, skinnedMeshRenderer.rootBone);
|
||||
Assert.AreEqual(new Bounds(new Vector3(0, 0, 0), new Vector3(2, 2, 2)),
|
||||
|
@ -7,6 +7,7 @@ using nadena.dev.modular_avatar.core;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.modular_avatar.core.editor.menu;
|
||||
using nadena.dev.modular_avatar.core.menu;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
@ -644,6 +645,7 @@ namespace modular_avatar_tests.VirtualMenuTests
|
||||
};
|
||||
|
||||
var buildContext = new BuildContext(av_root.GetComponent<VRCAvatarDescriptor>());
|
||||
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
new RenameParametersHook().OnPreprocessAvatar(av_root, buildContext);
|
||||
|
||||
var virtualMenu = VirtualMenu.ForAvatar(av_root.GetComponent<VRCAvatarDescriptor>(), buildContext);
|
||||
@ -663,6 +665,7 @@ namespace modular_avatar_tests.VirtualMenuTests
|
||||
var root = CreatePrefab("InternalParameterTest.prefab");
|
||||
|
||||
BuildContext buildContext = new BuildContext(root.GetComponent<VRCAvatarDescriptor>());
|
||||
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
new RenameParametersHook().OnPreprocessAvatar(root, buildContext);
|
||||
var virtualMenu = VirtualMenu.ForAvatar(root.GetComponent<VRCAvatarDescriptor>(), buildContext);
|
||||
|
||||
@ -676,6 +679,7 @@ namespace modular_avatar_tests.VirtualMenuTests
|
||||
var root = CreatePrefab("UnusedSubParametersAreStripped.prefab");
|
||||
|
||||
BuildContext buildContext = new BuildContext(root.GetComponent<VRCAvatarDescriptor>());
|
||||
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
new RenameParametersHook().OnPreprocessAvatar(root, buildContext);
|
||||
var virtualMenu = VirtualMenu.ForAvatar(root.GetComponent<VRCAvatarDescriptor>(), buildContext);
|
||||
|
||||
|
@ -2,6 +2,7 @@ using modular_avatar_tests;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.core;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf.animator;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.Animations;
|
||||
|
||||
@ -16,7 +17,7 @@ public class WorldFixedObjectTest : TestBase
|
||||
|
||||
// initialize context
|
||||
var buildContext = new BuildContext(avatar);
|
||||
buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>();
|
||||
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
new WorldFixedObjectProcessor().Process(buildContext);
|
||||
|
||||
@ -42,7 +43,7 @@ public class WorldFixedObjectTest : TestBase
|
||||
|
||||
// initialize context
|
||||
var buildContext = new BuildContext(avatar);
|
||||
buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>();
|
||||
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
new WorldFixedObjectProcessor().Process(buildContext);
|
||||
|
||||
@ -75,7 +76,7 @@ public class WorldFixedObjectTest : TestBase
|
||||
|
||||
// initialize context
|
||||
var buildContext = new BuildContext(avatar);
|
||||
var animationServices = buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>();
|
||||
var animationServices = buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||
|
||||
new WorldFixedObjectProcessor().Process(buildContext);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user