mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-02-12 16:52:50 +08:00
wip
This commit is contained in:
parent
81ef5419f7
commit
322a13ca44
@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.modular_avatar.animation;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using EditorCurveBinding = UnityEditor.EditorCurveBinding;
|
using EditorCurveBinding = UnityEditor.EditorCurveBinding;
|
||||||
@ -16,7 +17,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
{
|
{
|
||||||
private readonly BuildContext _context;
|
private readonly BuildContext _context;
|
||||||
private readonly BoneDatabase _boneDatabase;
|
private readonly BoneDatabase _boneDatabase;
|
||||||
private readonly PathMappings _pathMappings;
|
private readonly AnimatorServicesContext _asc;
|
||||||
private readonly List<IntermediateObj> _intermediateObjs = new List<IntermediateObj>();
|
private readonly List<IntermediateObj> _intermediateObjs = new List<IntermediateObj>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -55,15 +56,15 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_boneDatabase = boneDatabase;
|
_boneDatabase = boneDatabase;
|
||||||
_pathMappings = context.PluginBuildContext.Extension<AnimationServicesContext>().PathMappings;
|
_asc = context.PluginBuildContext.Extension<AnimatorServicesContext>();
|
||||||
|
|
||||||
while (root != null && !RuntimeUtil.IsAvatarRoot(root))
|
while (root != null && !RuntimeUtil.IsAvatarRoot(root))
|
||||||
{
|
{
|
||||||
var originalPath = RuntimeUtil.AvatarRootPath(root.gameObject);
|
var originalPath = RuntimeUtil.AvatarRootPath(root.gameObject);
|
||||||
System.Diagnostics.Debug.Assert(originalPath != null);
|
System.Diagnostics.Debug.Assert(originalPath != null);
|
||||||
|
|
||||||
if (context.AnimationDatabase.ClipsForPath(originalPath).Any(clip =>
|
if (_asc.AnimationIndex.GetClipsForObjectPath(originalPath).Any(clip =>
|
||||||
GetActiveBinding(clip.CurrentClip as AnimationClip, originalPath) != null
|
GetActiveBinding(clip, originalPath) != null
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
_intermediateObjs.Add(new IntermediateObj
|
_intermediateObjs.Add(new IntermediateObj
|
||||||
@ -118,7 +119,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
// Ensure mesh retargeting looks through this
|
// Ensure mesh retargeting looks through this
|
||||||
_boneDatabase.AddMergedBone(sourceBone.transform);
|
_boneDatabase.AddMergedBone(sourceBone.transform);
|
||||||
_boneDatabase.RetainMergedBone(sourceBone.transform);
|
_boneDatabase.RetainMergedBone(sourceBone.transform);
|
||||||
_pathMappings.MarkTransformLookthrough(sourceBone);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sourceBone;
|
return sourceBone;
|
||||||
@ -130,22 +130,14 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
{
|
{
|
||||||
var path = intermediate.OriginalPath;
|
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);
|
var curve = GetActiveBinding(clip, path);
|
||||||
if (curve != null)
|
if (curve != null)
|
||||||
{
|
{
|
||||||
foreach (var mapping in intermediate.Created)
|
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);
|
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,
|
return clip.GetFloatCurve(EditorCurveBinding.FloatCurve(path, typeof(GameObject), "m_IsActive"));
|
||||||
EditorCurveBinding.FloatCurve(path, typeof(GameObject), "m_IsActive"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ namespace nadena.dev.modular_avatar.animation
|
|||||||
}
|
}
|
||||||
|
|
||||||
private AnimatorServicesContext? _asc;
|
private AnimatorServicesContext? _asc;
|
||||||
private Retained _retained;
|
private Retained _retained = null!;
|
||||||
|
|
||||||
private AnimatorServicesContext asc =>
|
private AnimatorServicesContext asc =>
|
||||||
_asc ?? throw new InvalidOperationException("ActiveSelfProxyExtension is not active");
|
_asc ?? throw new InvalidOperationException("ActiveSelfProxyExtension is not active");
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using nadena.dev.ndmf;
|
using nadena.dev.ndmf;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using UnityEditor.Animations;
|
using UnityEditor.Animations;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -21,40 +22,34 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
var values = context.GetState<DefaultValues>()?.InitialValueOverrides
|
var values = context.GetState<DefaultValues>()?.InitialValueOverrides
|
||||||
?? ImmutableDictionary<string, float>.Empty;
|
?? ImmutableDictionary<string, float>.Empty;
|
||||||
|
|
||||||
foreach (var layer in context.AvatarDescriptor.baseAnimationLayers
|
var asc = context.Extension<AnimatorServicesContext>();
|
||||||
.Concat(context.AvatarDescriptor.specialAnimationLayers))
|
|
||||||
|
foreach (var controller in asc.ControllerContext.GetAllControllers())
|
||||||
{
|
{
|
||||||
if (layer.isDefault || layer.animatorController == null) continue;
|
var parameters = controller.Parameters;
|
||||||
|
foreach (var (name, parameter) in parameters)
|
||||||
// We should have converted anything that's not an AnimationController by now
|
|
||||||
var controller = layer.animatorController as AnimatorController;
|
|
||||||
if (controller == null || !context.IsTemporaryAsset(controller))
|
|
||||||
{
|
{
|
||||||
throw new Exception("Leaked unexpected controller: " + layer.animatorController + " (type " + layer.animatorController?.GetType() + ")");
|
if (!values.TryGetValue(name, out var defaultValue)) continue;
|
||||||
}
|
|
||||||
|
|
||||||
var parameters = controller.parameters;
|
switch (parameter.type)
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
|
||||||
{
|
|
||||||
if (!values.TryGetValue(parameters[i].name, out var defaultValue)) continue;
|
|
||||||
|
|
||||||
switch (parameters[i].type)
|
|
||||||
{
|
{
|
||||||
case AnimatorControllerParameterType.Bool:
|
case AnimatorControllerParameterType.Bool:
|
||||||
parameters[i].defaultBool = defaultValue != 0.0f;
|
parameter.defaultBool = defaultValue != 0.0f;
|
||||||
break;
|
break;
|
||||||
case AnimatorControllerParameterType.Int:
|
case AnimatorControllerParameterType.Int:
|
||||||
parameters[i].defaultInt = Mathf.RoundToInt(defaultValue);
|
parameter.defaultInt = Mathf.RoundToInt(defaultValue);
|
||||||
break;
|
break;
|
||||||
case AnimatorControllerParameterType.Float:
|
case AnimatorControllerParameterType.Float:
|
||||||
parameters[i].defaultFloat = defaultValue;
|
parameter.defaultFloat = defaultValue;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
continue; // unhandled type, e.g. trigger
|
continue; // unhandled type, e.g. trigger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameters = parameters.SetItem(name, parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.parameters = parameters;
|
controller.Parameters = parameters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,51 +24,22 @@
|
|||||||
|
|
||||||
#if MA_VRCSDK3_AVATARS
|
#if MA_VRCSDK3_AVATARS
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.ndmf.animator;
|
||||||
using UnityEditor;
|
|
||||||
using UnityEditor.Animations;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using VRC.SDK3.Avatars.Components;
|
using VRC.SDK3.Avatars.Components;
|
||||||
using VRC.SDKBase;
|
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace nadena.dev.modular_avatar.core.editor
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
{
|
{
|
||||||
internal class MergeAnimatorProcessor
|
internal class MergeAnimatorProcessor
|
||||||
{
|
{
|
||||||
private const string SAMPLE_PATH_PACKAGE =
|
private AnimatorServicesContext _asc;
|
||||||
"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>();
|
|
||||||
|
|
||||||
internal void OnPreprocessAvatar(GameObject avatarGameObject, BuildContext context)
|
internal void OnPreprocessAvatar(GameObject avatarGameObject, BuildContext context)
|
||||||
{
|
{
|
||||||
_context = context;
|
_asc = context.PluginBuildContext.Extension<AnimatorServicesContext>();
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
var toMerge = avatarGameObject.transform.GetComponentsInChildren<ModularAvatarMergeAnimator>(true);
|
var toMerge = avatarGameObject.transform.GetComponentsInChildren<ModularAvatarMergeAnimator>(true);
|
||||||
Dictionary<VRCAvatarDescriptor.AnimLayerType, List<ModularAvatarMergeAnimator>> byLayerType
|
Dictionary<VRCAvatarDescriptor.AnimLayerType, List<ModularAvatarMergeAnimator>> byLayerType
|
||||||
@ -89,10 +60,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
{
|
{
|
||||||
ProcessLayerType(context, entry.Key, entry.Value);
|
ProcessLayerType(context, entry.Key, entry.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptor.baseAnimationLayers = FinishSessions(descriptor.baseAnimationLayers);
|
|
||||||
descriptor.specialAnimationLayers = FinishSessions(descriptor.specialAnimationLayers);
|
|
||||||
descriptor.customizeAnimationLayers = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessLayerType(
|
private void ProcessLayerType(
|
||||||
@ -109,34 +76,34 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
var afterOriginal = sorted.Where(x => x.layerPriority >= 0)
|
var afterOriginal = sorted.Where(x => x.layerPriority >= 0)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var session = new AnimatorCombiner(context.PluginBuildContext, layerType.ToString() + " (merged)");
|
var controller = _asc.ControllerContext[layerType];
|
||||||
mergeSessions[layerType] = session;
|
|
||||||
mergeSessions[layerType].BlendableLayer = BlendableLayerFor(layerType);
|
|
||||||
|
|
||||||
foreach (var component in beforeOriginal)
|
var wdStateCounter = controller.Layers.SelectMany(l => l.StateMachine.AllStates())
|
||||||
{
|
.Select(s => s.WriteDefaultValues)
|
||||||
MergeSingle(context, session, component);
|
.GroupBy(b => b)
|
||||||
}
|
.ToDictionary(g => g.Key, g => g.Count());
|
||||||
|
|
||||||
if (defaultControllers_.TryGetValue(layerType, out var defaultController) &&
|
bool? writeDefaults = null;
|
||||||
defaultController.layers.Length > 0)
|
if (wdStateCounter.Count == 1) writeDefaults = wdStateCounter.First().Key;
|
||||||
{
|
|
||||||
session.AddController("", defaultController, null, forceFirstLayerWeight: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var component in afterOriginal)
|
foreach (var component in sorted)
|
||||||
{
|
{
|
||||||
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)
|
if (merge.animator == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var stash = context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||||
|
|
||||||
|
var clonedController = stash.Controllers.GetValueOrDefault(merge)
|
||||||
|
?? _asc.ControllerContext.CloneContext.Clone(merge.animator);
|
||||||
|
|
||||||
string basePath;
|
string basePath;
|
||||||
if (merge.pathMode == MergeAnimatorPathMode.Relative)
|
if (merge.pathMode == MergeAnimatorPathMode.Relative)
|
||||||
{
|
{
|
||||||
@ -145,17 +112,53 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
var relativePath = RuntimeUtil.RelativePath(context.AvatarRootObject, targetObject);
|
var relativePath = RuntimeUtil.RelativePath(context.AvatarRootObject, targetObject);
|
||||||
basePath = relativePath != "" ? relativePath + "/" : "";
|
basePath = relativePath != "" ? relativePath + "/" : "";
|
||||||
|
|
||||||
|
var animationIndex = new AnimationIndex(new[] { clonedController });
|
||||||
|
animationIndex.RewritePaths(p => p == "" ? relativePath : basePath + p);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
basePath = "";
|
basePath = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
var writeDefaults = merge.matchAvatarWriteDefaults
|
foreach (var l in clonedController.Layers)
|
||||||
? writeDefaults_.GetValueOrDefault(merge.layerType)
|
{
|
||||||
: null;
|
if (initialWriteDefaults != null)
|
||||||
var controller = _context.ConvertAnimatorController(merge.animator);
|
{
|
||||||
session.AddController(basePath, controller, writeDefaults);
|
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)
|
if (merge.deleteAttachedAnimator)
|
||||||
{
|
{
|
||||||
@ -163,182 +166,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
if (animator != null) Object.DestroyImmediate(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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using nadena.dev.modular_avatar.animation;
|
|
||||||
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 UnityEngine.Animations;
|
using UnityEngine.Animations;
|
||||||
@ -54,13 +54,14 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
#endif
|
#endif
|
||||||
private BoneDatabase BoneDatabase = new BoneDatabase();
|
private BoneDatabase BoneDatabase = new BoneDatabase();
|
||||||
|
|
||||||
private PathMappings PathMappings => frameworkContext.Extension<AnimationServicesContext>()
|
private AnimatorServicesContext AnimatorServices => frameworkContext.Extension<AnimatorServicesContext>();
|
||||||
.PathMappings;
|
|
||||||
|
|
||||||
private HashSet<Transform> humanoidBones = new HashSet<Transform>();
|
private HashSet<Transform> humanoidBones = new HashSet<Transform>();
|
||||||
private HashSet<Transform> mergedObjects = new HashSet<Transform>();
|
private HashSet<Transform> mergedObjects = new HashSet<Transform>();
|
||||||
private HashSet<Transform> thisPassAdded = 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)
|
internal void OnPreprocessAvatar(ndmf.BuildContext context, GameObject avatarGameObject)
|
||||||
{
|
{
|
||||||
this.frameworkContext = context;
|
this.frameworkContext = context;
|
||||||
@ -135,7 +136,68 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
private void TopoProcessMergeArmatures(ModularAvatarMergeArmature[] mergeArmatures)
|
||||||
@ -294,6 +356,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
_activeRetargeter.FixupAnimations();
|
_activeRetargeter.FixupAnimations();
|
||||||
|
|
||||||
thisPassAdded.UnionWith(_activeRetargeter.AddedGameObjects.Select(x => x.transform));
|
thisPassAdded.UnionWith(_activeRetargeter.AddedGameObjects.Select(x => x.transform));
|
||||||
|
transformLookthrough.UnionWith(_activeRetargeter.AddedGameObjects.Select(x => x.transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -357,7 +420,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
BoneDatabase.AddMergedBone(mergedSrcBone.transform);
|
BoneDatabase.AddMergedBone(mergedSrcBone.transform);
|
||||||
BoneDatabase.RetainMergedBone(mergedSrcBone.transform);
|
BoneDatabase.RetainMergedBone(mergedSrcBone.transform);
|
||||||
PathMappings.MarkTransformLookthrough(mergedSrcBone);
|
transformLookthrough.Add(mergedSrcBone.transform);
|
||||||
thisPassAdded.Add(mergedSrcBone.transform);
|
thisPassAdded.Add(mergedSrcBone.transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,7 +435,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
if (zipMerge)
|
if (zipMerge)
|
||||||
{
|
{
|
||||||
PathMappings.MarkTransformLookthrough(src);
|
transformLookthrough.Add(src.transform);
|
||||||
BoneDatabase.AddMergedBone(src.transform);
|
BoneDatabase.AddMergedBone(src.transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,13 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.modular_avatar.animation;
|
||||||
using nadena.dev.ndmf;
|
using nadena.dev.ndmf;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using nadena.dev.ndmf.util;
|
using nadena.dev.ndmf.util;
|
||||||
using UnityEditor.Animations;
|
using UnityEditor.Animations;
|
||||||
|
using UnityEditor.Search;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using VRC.SDK3.Avatars.Components;
|
using VRC.SDK3.Avatars.Components;
|
||||||
|
|
||||||
@ -20,16 +23,17 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
internal const string ALWAYS_ONE = "__ModularAvatarInternal/One";
|
internal const string ALWAYS_ONE = "__ModularAvatarInternal/One";
|
||||||
internal const string BlendTreeLayerName = "ModularAvatar: Merge Blend Tree";
|
internal const string BlendTreeLayerName = "ModularAvatar: Merge Blend Tree";
|
||||||
|
|
||||||
private AnimatorController _controller;
|
private AnimatorServicesContext _asc;
|
||||||
private BlendTree _rootBlendTree;
|
private VirtualBlendTree _rootBlendTree;
|
||||||
private GameObject _mergeHost;
|
|
||||||
private HashSet<string> _parameterNames;
|
private HashSet<string> _parameterNames;
|
||||||
|
|
||||||
protected override void Execute(ndmf.BuildContext context)
|
protected override void Execute(ndmf.BuildContext context)
|
||||||
{
|
{
|
||||||
|
_asc = context.Extension<AnimatorServicesContext>();
|
||||||
_rootBlendTree = null;
|
_rootBlendTree = null;
|
||||||
_parameterNames = new HashSet<string>();
|
_parameterNames = new HashSet<string>();
|
||||||
_controller = new AnimatorController();
|
|
||||||
|
var fx = _asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX];
|
||||||
|
|
||||||
foreach (var component in
|
foreach (var component in
|
||||||
context.AvatarRootObject.GetComponentsInChildren<ModularAvatarMergeBlendTree>(true))
|
context.AvatarRootObject.GetComponentsInChildren<ModularAvatarMergeBlendTree>(true))
|
||||||
@ -37,39 +41,31 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
ErrorReport.WithContextObject(component, () => ProcessComponent(context, component));
|
ErrorReport.WithContextObject(component, () => ProcessComponent(context, component));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AnimatorControllerParameter> parameters = new List<AnimatorControllerParameter>(_parameterNames.Count + 1);
|
// always add the ALWAYS_ONE parameter
|
||||||
if (_mergeHost != null)
|
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()
|
foreach (var name in _parameterNames)
|
||||||
|
{
|
||||||
|
if (fx.Parameters.ContainsKey(name)) continue;
|
||||||
|
|
||||||
|
fx.Parameters = fx.Parameters.SetItem(name, new AnimatorControllerParameter()
|
||||||
{
|
{
|
||||||
name = ALWAYS_ONE,
|
name = name,
|
||||||
type = AnimatorControllerParameterType.Float,
|
type = AnimatorControllerParameterType.Float,
|
||||||
defaultFloat = 1
|
defaultFloat = 1.0f
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var name in _parameterNames)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
BlendTree componentBlendTree = component.BlendTree as BlendTree;
|
||||||
|
|
||||||
if (componentBlendTree == null)
|
if (componentBlendTree == null)
|
||||||
@ -79,46 +75,60 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
string basePath = null;
|
string basePath = null;
|
||||||
|
string rootPath = null;
|
||||||
if (component.PathMode == MergeAnimatorPathMode.Relative)
|
if (component.PathMode == MergeAnimatorPathMode.Relative)
|
||||||
{
|
{
|
||||||
var root = component.RelativePathRoot.Get(context.AvatarRootTransform);
|
var root = component.RelativePathRoot.Get(context.AvatarRootTransform);
|
||||||
if (root == null) root = component.gameObject;
|
if (root == null) root = component.gameObject;
|
||||||
|
|
||||||
basePath = RuntimeUtil.AvatarRootPath(root) + "/";
|
rootPath = RuntimeUtil.AvatarRootPath(root);
|
||||||
|
basePath = rootPath + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
var bt = new DeepClone(context).DoClone(componentBlendTree, basePath);
|
var bt = stash.BlendTrees.GetValueOrDefault(component)
|
||||||
var rootBlend = GetRootBlendTree(context);
|
?? _asc.ControllerContext.CloneContext.Clone(componentBlendTree);
|
||||||
|
|
||||||
rootBlend.AddChild(bt);
|
if (basePath != null)
|
||||||
var children = rootBlend.children;
|
|
||||||
children[children.Length - 1].directBlendParameter = ALWAYS_ONE;
|
|
||||||
rootBlend.children = children;
|
|
||||||
|
|
||||||
foreach (var asset in bt.ReferencedAssets(includeScene: false))
|
|
||||||
{
|
{
|
||||||
if (asset is BlendTree bt2)
|
var animationIndex = new AnimationIndex(new[] { bt });
|
||||||
|
animationIndex.RewritePaths(p => p == "" ? rootPath : basePath + p);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootBlend = GetRootBlendTree();
|
||||||
|
|
||||||
|
rootBlend.Children = rootBlend.Children.Add(new()
|
||||||
|
{
|
||||||
|
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,58 +136,21 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlendTree GetRootBlendTree(ndmf.BuildContext context)
|
private VirtualBlendTree GetRootBlendTree()
|
||||||
{
|
{
|
||||||
if (_rootBlendTree != null) return _rootBlendTree;
|
if (_rootBlendTree != null) return _rootBlendTree;
|
||||||
|
|
||||||
var newController = new AnimatorController();
|
var fx = _asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX];
|
||||||
var newStateMachine = new AnimatorStateMachine();
|
var controller = fx.AddLayer(new LayerPriority(int.MinValue), BlendTreeLayerName);
|
||||||
var newState = new AnimatorState();
|
var stateMachine = controller.StateMachine;
|
||||||
|
|
||||||
_rootBlendTree = new BlendTree();
|
_rootBlendTree = VirtualBlendTree.Create("Root");
|
||||||
_controller = newController;
|
var state = stateMachine.AddState("State", _rootBlendTree);
|
||||||
|
stateMachine.DefaultState = state;
|
||||||
|
state.WriteDefaultValues = true;
|
||||||
|
|
||||||
newController.layers = new[]
|
_rootBlendTree.BlendType = BlendTreeType.Direct;
|
||||||
{
|
_rootBlendTree.BlendParameter = ALWAYS_ONE;
|
||||||
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;
|
|
||||||
|
|
||||||
newState.writeDefaultValues = true;
|
|
||||||
newState.motion = _rootBlendTree;
|
|
||||||
|
|
||||||
_rootBlendTree.blendType = BlendTreeType.Direct;
|
|
||||||
_rootBlendTree.blendParameter = ALWAYS_ONE;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
mergeObject.transform.SetParent(context.AvatarRootTransform, false);
|
|
||||||
mergeObject.transform.SetSiblingIndex(0);
|
|
||||||
|
|
||||||
_mergeHost = mergeObject;
|
|
||||||
|
|
||||||
return _rootBlendTree;
|
return _rootBlendTree;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ using System.Linq;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.modular_avatar.animation;
|
||||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace nadena.dev.modular_avatar.core.editor
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
@ -84,13 +85,15 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
internal class RetargetMeshes
|
internal class RetargetMeshes
|
||||||
{
|
{
|
||||||
private BoneDatabase _boneDatabase;
|
private BoneDatabase _boneDatabase;
|
||||||
private PathMappings _pathTracker;
|
private AnimationIndex _animationIndex;
|
||||||
|
private ObjectPathRemapper _pathRemapper;
|
||||||
|
|
||||||
internal void OnPreprocessAvatar(GameObject avatarGameObject, BoneDatabase boneDatabase,
|
internal void OnPreprocessAvatar(GameObject avatarGameObject, BoneDatabase boneDatabase,
|
||||||
PathMappings pathMappings)
|
AnimatorServicesContext pathMappings)
|
||||||
{
|
{
|
||||||
this._boneDatabase = boneDatabase;
|
this._boneDatabase = boneDatabase;
|
||||||
this._pathTracker = pathMappings;
|
this._animationIndex = pathMappings.AnimationIndex;
|
||||||
|
this._pathRemapper = pathMappings.ObjectPathRemapper;
|
||||||
|
|
||||||
foreach (var renderer in avatarGameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true))
|
foreach (var renderer in avatarGameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true))
|
||||||
{
|
{
|
||||||
@ -153,7 +156,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
child.SetParent(destBone, true);
|
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);
|
UnityEngine.Object.DestroyImmediate(sourceBone.gameObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,17 +47,20 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
|||||||
seq.Run(ClearEditorOnlyTags.Instance);
|
seq.Run(ClearEditorOnlyTags.Instance);
|
||||||
seq.Run(MeshSettingsPluginPass.Instance);
|
seq.Run(MeshSettingsPluginPass.Instance);
|
||||||
seq.Run(ScaleAdjusterPass.Instance).PreviewingWith(new ScaleAdjusterPreview());
|
seq.Run(ScaleAdjusterPass.Instance).PreviewingWith(new ScaleAdjusterPreview());
|
||||||
|
|
||||||
|
// All these need to move to the new ASC
|
||||||
#if MA_VRCSDK3_AVATARS
|
#if MA_VRCSDK3_AVATARS
|
||||||
seq.Run(ReactiveObjectPrepass.Instance);
|
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
|
#endif
|
||||||
seq.WithRequiredExtension(typeof(AnimatorServicesContext), _s2 =>
|
seq.WithRequiredExtension(typeof(AnimatorServicesContext), _s2 =>
|
||||||
{
|
{
|
||||||
#if MA_VRCSDK3_AVATARS
|
#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.WithRequiredExtension(typeof(ReadablePropertyExtension), _s3 =>
|
||||||
{
|
{
|
||||||
seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute())
|
seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute())
|
||||||
@ -65,19 +68,18 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
|||||||
new MaterialSetterPreview());
|
new MaterialSetterPreview());
|
||||||
});
|
});
|
||||||
seq.Run(GameObjectDelayDisablePass.Instance);
|
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
|
// 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
|
// game objects which contain Menu Installers. It'd probably be better however to teach Merge Armature
|
||||||
// to retain those objects? maybe?
|
// to retain those objects? maybe?
|
||||||
seq.Run(MenuInstallPluginPass.Instance);
|
seq.Run(MenuInstallPluginPass.Instance);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
seq.Run(MergeArmaturePluginPass.Instance);
|
seq.Run(MergeArmaturePluginPass.Instance);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
seq.WithRequiredExtension(typeof(AnimationServicesContext), _s2 =>
|
||||||
|
{
|
||||||
seq.Run(BoneProxyPluginPass.Instance);
|
seq.Run(BoneProxyPluginPass.Instance);
|
||||||
#if MA_VRCSDK3_AVATARS
|
#if MA_VRCSDK3_AVATARS
|
||||||
seq.Run(VisibleHeadAccessoryPluginPass.Instance);
|
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,
|
// 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.
|
// 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);
|
var analysis = new ReactiveObjectAnalyzer(context).Analyze(context.AvatarRootObject);
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using nadena.dev.ndmf;
|
using nadena.dev.ndmf;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using UnityEditor.Animations;
|
using UnityEditor.Animations;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using VRC.SDK3.Avatars.Components;
|
using VRC.SDK3.Avatars.Components;
|
||||||
@ -201,18 +202,20 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
if (mamiWithRC.Count > 0)
|
if (mamiWithRC.Count > 0)
|
||||||
{
|
{
|
||||||
// This make sures the parameters are correctly merged into the FX layer.
|
var asc = context.Extension<AnimatorServicesContext>();
|
||||||
var mergeAnimator = context.AvatarRootObject.AddComponent<ModularAvatarMergeAnimator>();
|
var fx = asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX];
|
||||||
mergeAnimator.layerType = VRCAvatarDescriptor.AnimLayerType.FX;
|
|
||||||
mergeAnimator.deleteAttachedAnimator = false;
|
foreach (var (name, _) in mamiWithRC)
|
||||||
mergeAnimator.animator = new AnimatorController
|
|
||||||
{
|
{
|
||||||
parameters = mamiWithRC.Select(kvp => new AnimatorControllerParameter
|
if (!fx.Parameters.ContainsKey(name))
|
||||||
{
|
{
|
||||||
name = kvp.Key,
|
fx.Parameters = fx.Parameters.SetItem(name, new()
|
||||||
type = AnimatorControllerParameterType.Float,
|
{
|
||||||
}).ToArray(),
|
name = name,
|
||||||
};
|
type = AnimatorControllerParameterType.Float,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ using System.Linq;
|
|||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.modular_avatar.animation;
|
||||||
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 UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Animations;
|
using UnityEditor.Animations;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -56,6 +57,43 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
public ImmutableDictionary<string, float> InitialValueOverrides;
|
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
|
internal class RenameParametersHook
|
||||||
{
|
{
|
||||||
private const string DEFAULT_EXP_PARAMS_ASSET_GUID = "03a6d797deb62f0429471c4e17ea99a7";
|
private const string DEFAULT_EXP_PARAMS_ASSET_GUID = "03a6d797deb62f0429471c4e17ea99a7";
|
||||||
@ -164,6 +202,10 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
|
var stash = _context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||||
|
var asc = _context.PluginBuildContext.Extension<AnimatorServicesContext>();
|
||||||
|
stash.AnimatorServices = asc;
|
||||||
|
|
||||||
var syncParams = WalkTree(avatar);
|
var syncParams = WalkTree(avatar);
|
||||||
|
|
||||||
SetExpressionParameters(avatar, syncParams);
|
SetExpressionParameters(avatar, syncParams);
|
||||||
@ -389,11 +431,13 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
if (merger.animator != null)
|
if (merger.animator != null)
|
||||||
{
|
{
|
||||||
Profiler.BeginSample("DeepCloneAnimator");
|
var stash = _context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||||
merger.animator = new DeepClone(_context.PluginBuildContext).DoClone(merger.animator);
|
|
||||||
Profiler.EndSample();
|
|
||||||
|
|
||||||
ProcessRuntimeAnimatorController(merger.animator, remap);
|
var controller = stash.Clone(merger);
|
||||||
|
|
||||||
|
ProcessVirtualAnimatorController(controller, remap);
|
||||||
|
|
||||||
|
stash.Controllers[merger] = controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -404,8 +448,12 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
var bt = merger.BlendTree as BlendTree;
|
var bt = merger.BlendTree as BlendTree;
|
||||||
if (bt != null)
|
if (bt != null)
|
||||||
{
|
{
|
||||||
merger.BlendTree = bt = new DeepClone(_context.PluginBuildContext).DoClone(bt);
|
var stash = _context.PluginBuildContext.GetState<RenamedMergeAnimators>();
|
||||||
ProcessBlendtree(bt, paramInfo.GetParameterRemappingsAt(obj));
|
|
||||||
|
var virtualbt = stash.Clone(merger);
|
||||||
|
ProcessBlendtree(virtualbt, paramInfo.GetParameterRemappingsAt(obj));
|
||||||
|
|
||||||
|
stash.BlendTrees[merger] = virtualbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -497,28 +545,6 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
return rv;
|
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,
|
private void ProcessMenuInstaller(ModularAvatarMenuInstaller installer,
|
||||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||||
{
|
{
|
||||||
@ -537,113 +563,57 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessAnimator(AnimatorController controller,
|
private void ProcessVirtualAnimatorController(VirtualAnimatorController controller,
|
||||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remap)
|
||||||
{
|
{
|
||||||
if (remaps.IsEmpty) return;
|
foreach (var node in controller.AllReachableNodes())
|
||||||
|
|
||||||
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))
|
switch (node)
|
||||||
{
|
{
|
||||||
parameters[i].name = newName.ParameterName;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.parameters = parameters;
|
var newParameters = controller.Parameters.Clear();
|
||||||
|
|
||||||
foreach (var layer in controller.layers)
|
foreach (var (name, parameter) in controller.Parameters)
|
||||||
{
|
{
|
||||||
if (layer.stateMachine != null)
|
if (remap.TryGetValue((ParameterNamespace.Animator, name), out var newParam))
|
||||||
{
|
{
|
||||||
queue.Enqueue(layer.stateMachine);
|
newParameters = newParameters.Add(newParam.ParameterName, parameter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newParameters = newParameters.Add(name, parameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Profiler.BeginSample("Walk animator graph");
|
controller.Parameters = newParameters;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.MirrorParameter = remap(remaps, state.MirrorParameter);
|
||||||
state.timeParameter = remap(remaps, state.timeParameter);
|
state.TimeParameter = remap(remaps, state.TimeParameter);
|
||||||
state.speedParameter = remap(remaps, state.speedParameter);
|
state.SpeedParameter = remap(remaps, state.SpeedParameter);
|
||||||
state.cycleOffsetParameter = remap(remaps, state.cycleOffsetParameter);
|
state.CycleOffsetParameter = remap(remaps, state.CycleOffsetParameter);
|
||||||
|
|
||||||
foreach (var t in state.transitions)
|
foreach (var behavior in state.Behaviours)
|
||||||
{
|
|
||||||
ProcessTransition(t, remaps);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var behavior in state.behaviours)
|
|
||||||
{
|
{
|
||||||
if (behavior is VRCAvatarParameterDriver driver)
|
if (behavior is VRCAvatarParameterDriver driver)
|
||||||
{
|
{
|
||||||
ProcessDriver(driver, remaps);
|
ProcessDriver(driver, remaps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessMotion(state.motion, remaps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessMotion(Motion motion,
|
private void ProcessClip(VirtualClip clip,
|
||||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||||
{
|
{
|
||||||
if (motion is BlendTree blendTree) ProcessBlendtree(blendTree, remaps);
|
var curveBindings = clip.GetFloatCurveBindings();
|
||||||
|
|
||||||
if (motion is AnimationClip clip) ProcessClip(clip, remaps);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessClip(AnimationClip clip,
|
|
||||||
ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
|
||||||
{
|
|
||||||
var curveBindings = AnimationUtility.GetCurveBindings(clip);
|
|
||||||
|
|
||||||
var bindingsToUpdate = new List<EditorCurveBinding>();
|
var bindingsToUpdate = new List<EditorCurveBinding>();
|
||||||
var newCurves = new List<AnimationCurve>();
|
var newCurves = new List<AnimationCurve>();
|
||||||
@ -653,48 +623,30 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
if (binding.path != "" || binding.type != typeof(Animator)) continue;
|
if (binding.path != "" || binding.type != typeof(Animator)) continue;
|
||||||
if (remaps.TryGetValue((ParameterNamespace.Animator, binding.propertyName), out var newBinding))
|
if (remaps.TryGetValue((ParameterNamespace.Animator, binding.propertyName), out var newBinding))
|
||||||
{
|
{
|
||||||
var curCurve = AnimationUtility.GetEditorCurve(clip, binding);
|
var curCurve = clip.GetFloatCurve(binding);
|
||||||
|
var newECB = new EditorCurveBinding
|
||||||
bindingsToUpdate.Add(binding);
|
|
||||||
newCurves.Add(null);
|
|
||||||
|
|
||||||
bindingsToUpdate.Add(new EditorCurveBinding
|
|
||||||
{
|
{
|
||||||
path = "",
|
path = "",
|
||||||
type = typeof(Animator),
|
type = typeof(Animator),
|
||||||
propertyName = newBinding.ParameterName
|
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.BlendParameter = remap(remaps, blendTree.BlendParameter);
|
||||||
blendTree.blendParameterY = remap(remaps, blendTree.blendParameterY);
|
blendTree.BlendParameterY = remap(remaps, blendTree.BlendParameterY);
|
||||||
|
|
||||||
var children = blendTree.children;
|
var children = blendTree.Children;
|
||||||
for (int i = 0; i < children.Length; i++)
|
foreach (var child in children)
|
||||||
{
|
{
|
||||||
var childMotion = children[i];
|
child.DirectBlendParameter = remap(remaps, child.DirectBlendParameter);
|
||||||
ProcessMotion(childMotion.motion, remaps);
|
|
||||||
|
|
||||||
childMotion.directBlendParameter = remap(remaps, childMotion.directBlendParameter);
|
|
||||||
children[i] = childMotion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blendTree.children = children;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessDriver(VRCAvatarParameterDriver driver, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
private void ProcessDriver(VRCAvatarParameterDriver driver, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> remaps)
|
||||||
@ -710,19 +662,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;
|
bool dirty = false;
|
||||||
var conditions = t.conditions;
|
var conditions = t.Conditions
|
||||||
|
.Select(cond =>
|
||||||
for (int i = 0; i < conditions.Length; i++)
|
{
|
||||||
{
|
cond.parameter = remap(remaps, cond.parameter, ref dirty);
|
||||||
var cond = conditions[i];
|
return cond;
|
||||||
cond.parameter = remap(remaps, cond.parameter, ref dirty);
|
})
|
||||||
conditions[i] = cond;
|
.ToImmutableList();
|
||||||
}
|
t.Conditions = conditions;
|
||||||
|
|
||||||
if (dirty) t.conditions = conditions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableDictionary<string, ParameterInfo> CollectParameters(ModularAvatarParameters p,
|
private ImmutableDictionary<string, ParameterInfo> CollectParameters(ModularAvatarParameters p,
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
#if MA_VRCSDK3_AVATARS
|
#if MA_VRCSDK3_AVATARS
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using modular_avatar_tests;
|
using modular_avatar_tests;
|
||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.modular_avatar.animation;
|
||||||
using nadena.dev.modular_avatar.core.editor;
|
using nadena.dev.modular_avatar.core.editor;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using VRC.SDK3.Avatars.Components;
|
||||||
using EditorCurveBinding = UnityEditor.EditorCurveBinding;
|
using EditorCurveBinding = UnityEditor.EditorCurveBinding;
|
||||||
|
|
||||||
public class ActiveAnimationRetargeterTests : TestBase
|
public class ActiveAnimationRetargeterTests : TestBase
|
||||||
@ -17,8 +20,7 @@ public class ActiveAnimationRetargeterTests : TestBase
|
|||||||
|
|
||||||
// initialize context
|
// initialize context
|
||||||
var buildContext = new BuildContext(avatar);
|
var buildContext = new BuildContext(avatar);
|
||||||
var pathMappings = buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>()
|
var asc = buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
.PathMappings;
|
|
||||||
|
|
||||||
// get game objects
|
// get game objects
|
||||||
var changedChild = avatar.transform.Find("Toggled/Child");
|
var changedChild = avatar.transform.Find("Toggled/Child");
|
||||||
@ -29,18 +31,16 @@ public class ActiveAnimationRetargeterTests : TestBase
|
|||||||
var created = retargeter.CreateIntermediateObjects(newParent.gameObject);
|
var created = retargeter.CreateIntermediateObjects(newParent.gameObject);
|
||||||
retargeter.FixupAnimations();
|
retargeter.FixupAnimations();
|
||||||
|
|
||||||
// commit
|
var fx = asc.ControllerContext[VRCAvatarDescriptor.AnimLayerType.FX]!;
|
||||||
buildContext.AnimationDatabase.Commit();
|
var clip = (VirtualClip) fx.Layers.First(l => l.Name == "retarget").StateMachine.DefaultState!.Motion;
|
||||||
|
var curveBindings = clip!.GetFloatCurveBindings();
|
||||||
var clip = findFxClip(avatar, layerName: "retarget");
|
|
||||||
var curveBindings = AnimationUtility.GetCurveBindings(clip);
|
|
||||||
|
|
||||||
// Intermediate object must be created
|
// Intermediate object must be created
|
||||||
Assert.That(created, Is.Not.EqualTo(newParent.gameObject));
|
Assert.That(created, Is.Not.EqualTo(newParent.gameObject));
|
||||||
|
|
||||||
// The created animation must have m_IsActive of intermediate object
|
// The created animation must have m_IsActive of intermediate object
|
||||||
Assert.That(curveBindings, Does.Contain(EditorCurveBinding.FloatCurve(
|
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;
|
||||||
using nadena.dev.modular_avatar.core.editor;
|
using nadena.dev.modular_avatar.core.editor;
|
||||||
using nadena.dev.ndmf;
|
using nadena.dev.ndmf;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using BuildContext = nadena.dev.ndmf.BuildContext;
|
using BuildContext = nadena.dev.ndmf.BuildContext;
|
||||||
@ -22,9 +23,13 @@ namespace UnitTests.MergeAnimatorTests
|
|||||||
|
|
||||||
var ctx = new BuildContext(av, null);
|
var ctx = new BuildContext(av, null);
|
||||||
ctx.ActivateExtensionContext<ModularAvatarContext>();
|
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);
|
Assert.IsEmpty(errors);
|
||||||
}
|
}
|
||||||
@ -39,9 +44,15 @@ namespace UnitTests.MergeAnimatorTests
|
|||||||
|
|
||||||
var ctx = new BuildContext(av, null);
|
var ctx = new BuildContext(av, null);
|
||||||
ctx.ActivateExtensionContext<ModularAvatarContext>();
|
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);
|
Assert.IsEmpty(errors);
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ public class PreexistingParamsTest : TestBase
|
|||||||
|
|
||||||
foreach (var kvp in paramDict)
|
foreach (var kvp in paramDict)
|
||||||
{
|
{
|
||||||
|
if (kvp.Key.StartsWith("__ModularAvatarInternal/")) continue;
|
||||||
|
|
||||||
if (kvp.Key == "default_override" || kvp.Key == "animator_only")
|
if (kvp.Key == "default_override" || kvp.Key == "animator_only")
|
||||||
{
|
{
|
||||||
Assert.AreEqual(1, kvp.Value);
|
Assert.AreEqual(1, kvp.Value);
|
||||||
|
@ -2,6 +2,7 @@ using System.Linq;
|
|||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.modular_avatar.animation;
|
||||||
using nadena.dev.modular_avatar.core;
|
using nadena.dev.modular_avatar.core;
|
||||||
using nadena.dev.modular_avatar.core.editor;
|
using nadena.dev.modular_avatar.core.editor;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ namespace modular_avatar_tests.MergeArmatureTests
|
|||||||
nadena.dev.ndmf.BuildContext context =
|
nadena.dev.ndmf.BuildContext context =
|
||||||
new nadena.dev.ndmf.BuildContext(root, null);
|
new nadena.dev.ndmf.BuildContext(root, null);
|
||||||
context.ActivateExtensionContext<ModularAvatarContext>();
|
context.ActivateExtensionContext<ModularAvatarContext>();
|
||||||
context.ActivateExtensionContext<AnimationServicesContext>();
|
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
||||||
|
|
||||||
Assert.IsTrue(bone.GetComponentInChildren<TestComponentA>() != null);
|
Assert.IsTrue(bone.GetComponentInChildren<TestComponentA>() != null);
|
||||||
@ -82,7 +83,7 @@ namespace modular_avatar_tests.MergeArmatureTests
|
|||||||
nadena.dev.ndmf.BuildContext context =
|
nadena.dev.ndmf.BuildContext context =
|
||||||
new nadena.dev.ndmf.BuildContext(root, null);
|
new nadena.dev.ndmf.BuildContext(root, null);
|
||||||
context.ActivateExtensionContext<ModularAvatarContext>();
|
context.ActivateExtensionContext<ModularAvatarContext>();
|
||||||
context.ActivateExtensionContext<AnimationServicesContext>();
|
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
||||||
|
|
||||||
Assert.IsTrue(m_bone == null); // destroyed by retargeting pass
|
Assert.IsTrue(m_bone == null); // destroyed by retargeting pass
|
||||||
@ -106,7 +107,7 @@ namespace modular_avatar_tests.MergeArmatureTests
|
|||||||
nadena.dev.ndmf.BuildContext context =
|
nadena.dev.ndmf.BuildContext context =
|
||||||
new nadena.dev.ndmf.BuildContext(root, null);
|
new nadena.dev.ndmf.BuildContext(root, null);
|
||||||
context.ActivateExtensionContext<ModularAvatarContext>();
|
context.ActivateExtensionContext<ModularAvatarContext>();
|
||||||
context.ActivateExtensionContext<AnimationServicesContext>();
|
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
new MergeArmatureHook().OnPreprocessAvatar(context, root);
|
||||||
|
|
||||||
Assert.IsTrue(m_bone == null); // destroyed by retargeting pass
|
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;
|
||||||
using nadena.dev.modular_avatar.core.editor;
|
using nadena.dev.modular_avatar.core.editor;
|
||||||
using nadena.dev.ndmf;
|
using nadena.dev.ndmf;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEditor.Animations;
|
using UnityEditor.Animations;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -79,6 +80,7 @@ namespace modular_avatar_tests.RenameParametersTests
|
|||||||
|
|
||||||
var context = CreateContext(prefab);
|
var context = CreateContext(prefab);
|
||||||
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
||||||
|
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
|
|
||||||
var errors = ErrorReport.CaptureErrors(
|
var errors = ErrorReport.CaptureErrors(
|
||||||
() =>
|
() =>
|
||||||
@ -209,6 +211,7 @@ namespace modular_avatar_tests.RenameParametersTests
|
|||||||
|
|
||||||
var context = CreateContext(av);
|
var context = CreateContext(av);
|
||||||
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
||||||
|
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
|
|
||||||
var errors = ErrorReport.CaptureErrors(() => new RenameParametersHook().OnPreprocessAvatar(av, maContext));
|
var errors = ErrorReport.CaptureErrors(() => new RenameParametersHook().OnPreprocessAvatar(av, maContext));
|
||||||
|
|
||||||
@ -243,6 +246,7 @@ namespace modular_avatar_tests.RenameParametersTests
|
|||||||
|
|
||||||
var context = CreateContext(av);
|
var context = CreateContext(av);
|
||||||
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
||||||
|
context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
|
|
||||||
var errors = ErrorReport.CaptureErrors(() => new RenameParametersHook().OnPreprocessAvatar(av, maContext));
|
var errors = ErrorReport.CaptureErrors(() => new RenameParametersHook().OnPreprocessAvatar(av, maContext));
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using nadena.dev.modular_avatar.animation;
|
using nadena.dev.modular_avatar.animation;
|
||||||
using nadena.dev.modular_avatar.core.editor;
|
using nadena.dev.modular_avatar.core.editor;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -21,13 +22,12 @@ namespace modular_avatar_tests
|
|||||||
Debug.Assert(skinnedMeshRenderer.bones.Length == 0);
|
Debug.Assert(skinnedMeshRenderer.bones.Length == 0);
|
||||||
|
|
||||||
var build_context = new nadena.dev.ndmf.BuildContext(root, null);
|
var build_context = new nadena.dev.ndmf.BuildContext(root, null);
|
||||||
var torc = new AnimationServicesContext();
|
var asc = build_context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
torc.OnActivate(build_context);
|
|
||||||
|
|
||||||
var bonedb = new BoneDatabase();
|
var bonedb = new BoneDatabase();
|
||||||
bonedb.AddMergedBone(b.transform);
|
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(a.transform, skinnedMeshRenderer.rootBone);
|
||||||
}
|
}
|
||||||
@ -47,13 +47,12 @@ namespace modular_avatar_tests
|
|||||||
Debug.Assert(skinnedMeshRenderer.bones.Length == 0);
|
Debug.Assert(skinnedMeshRenderer.bones.Length == 0);
|
||||||
|
|
||||||
var build_context = new nadena.dev.ndmf.BuildContext(root, null);
|
var build_context = new nadena.dev.ndmf.BuildContext(root, null);
|
||||||
var torc = new AnimationServicesContext();
|
var asc = build_context.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
torc.OnActivate(build_context);
|
|
||||||
|
|
||||||
var bonedb = new BoneDatabase();
|
var bonedb = new BoneDatabase();
|
||||||
bonedb.AddMergedBone(b.transform);
|
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(a.transform, skinnedMeshRenderer.rootBone);
|
||||||
Assert.AreEqual(new Bounds(new Vector3(0, 0, 0), new Vector3(2, 2, 2)),
|
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;
|
||||||
using nadena.dev.modular_avatar.core.editor.menu;
|
using nadena.dev.modular_avatar.core.editor.menu;
|
||||||
using nadena.dev.modular_avatar.core.menu;
|
using nadena.dev.modular_avatar.core.menu;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -644,6 +645,7 @@ namespace modular_avatar_tests.VirtualMenuTests
|
|||||||
};
|
};
|
||||||
|
|
||||||
var buildContext = new BuildContext(av_root.GetComponent<VRCAvatarDescriptor>());
|
var buildContext = new BuildContext(av_root.GetComponent<VRCAvatarDescriptor>());
|
||||||
|
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
new RenameParametersHook().OnPreprocessAvatar(av_root, buildContext);
|
new RenameParametersHook().OnPreprocessAvatar(av_root, buildContext);
|
||||||
|
|
||||||
var virtualMenu = VirtualMenu.ForAvatar(av_root.GetComponent<VRCAvatarDescriptor>(), buildContext);
|
var virtualMenu = VirtualMenu.ForAvatar(av_root.GetComponent<VRCAvatarDescriptor>(), buildContext);
|
||||||
@ -663,6 +665,7 @@ namespace modular_avatar_tests.VirtualMenuTests
|
|||||||
var root = CreatePrefab("InternalParameterTest.prefab");
|
var root = CreatePrefab("InternalParameterTest.prefab");
|
||||||
|
|
||||||
BuildContext buildContext = new BuildContext(root.GetComponent<VRCAvatarDescriptor>());
|
BuildContext buildContext = new BuildContext(root.GetComponent<VRCAvatarDescriptor>());
|
||||||
|
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
new RenameParametersHook().OnPreprocessAvatar(root, buildContext);
|
new RenameParametersHook().OnPreprocessAvatar(root, buildContext);
|
||||||
var virtualMenu = VirtualMenu.ForAvatar(root.GetComponent<VRCAvatarDescriptor>(), buildContext);
|
var virtualMenu = VirtualMenu.ForAvatar(root.GetComponent<VRCAvatarDescriptor>(), buildContext);
|
||||||
|
|
||||||
@ -676,6 +679,7 @@ namespace modular_avatar_tests.VirtualMenuTests
|
|||||||
var root = CreatePrefab("UnusedSubParametersAreStripped.prefab");
|
var root = CreatePrefab("UnusedSubParametersAreStripped.prefab");
|
||||||
|
|
||||||
BuildContext buildContext = new BuildContext(root.GetComponent<VRCAvatarDescriptor>());
|
BuildContext buildContext = new BuildContext(root.GetComponent<VRCAvatarDescriptor>());
|
||||||
|
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
new RenameParametersHook().OnPreprocessAvatar(root, buildContext);
|
new RenameParametersHook().OnPreprocessAvatar(root, buildContext);
|
||||||
var virtualMenu = VirtualMenu.ForAvatar(root.GetComponent<VRCAvatarDescriptor>(), 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.animation;
|
||||||
using nadena.dev.modular_avatar.core;
|
using nadena.dev.modular_avatar.core;
|
||||||
using nadena.dev.modular_avatar.core.editor;
|
using nadena.dev.modular_avatar.core.editor;
|
||||||
|
using nadena.dev.ndmf.animator;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine.Animations;
|
using UnityEngine.Animations;
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ public class WorldFixedObjectTest : TestBase
|
|||||||
|
|
||||||
// initialize context
|
// initialize context
|
||||||
var buildContext = new BuildContext(avatar);
|
var buildContext = new BuildContext(avatar);
|
||||||
buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>();
|
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
|
|
||||||
new WorldFixedObjectProcessor().Process(buildContext);
|
new WorldFixedObjectProcessor().Process(buildContext);
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ public class WorldFixedObjectTest : TestBase
|
|||||||
|
|
||||||
// initialize context
|
// initialize context
|
||||||
var buildContext = new BuildContext(avatar);
|
var buildContext = new BuildContext(avatar);
|
||||||
buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>();
|
buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
|
|
||||||
new WorldFixedObjectProcessor().Process(buildContext);
|
new WorldFixedObjectProcessor().Process(buildContext);
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ public class WorldFixedObjectTest : TestBase
|
|||||||
|
|
||||||
// initialize context
|
// initialize context
|
||||||
var buildContext = new BuildContext(avatar);
|
var buildContext = new BuildContext(avatar);
|
||||||
var animationServices = buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>();
|
var animationServices = buildContext.PluginBuildContext.ActivateExtensionContextRecursive<AnimatorServicesContext>();
|
||||||
|
|
||||||
new WorldFixedObjectProcessor().Process(buildContext);
|
new WorldFixedObjectProcessor().Process(buildContext);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user