mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-04-04 19:49:02 +08:00
Merge branch 'main' into enhance_EasySetupOutfit_humanoid
This commit is contained in:
commit
05b97e74e5
4
.github/ProjectRoot/vpm-manifest-2022.json
vendored
4
.github/ProjectRoot/vpm-manifest-2022.json
vendored
@ -4,7 +4,7 @@
|
||||
"version": "3.7.0"
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.4.0"
|
||||
"version": "1.5.6"
|
||||
}
|
||||
},
|
||||
"locked": {
|
||||
@ -19,7 +19,7 @@
|
||||
"dependencies": {}
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.5.3"
|
||||
"version": "1.5.6"
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,9 @@ using nadena.dev.ndmf;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@ -89,12 +91,14 @@ namespace nadena.dev.modular_avatar.animation
|
||||
// HACK: This is a temporary crutch until we rework the entire animator services system
|
||||
public void AddPropertyDefinition(AnimatorControllerParameter paramDef)
|
||||
{
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
var fx = (AnimatorController)
|
||||
_context.AvatarDescriptor.baseAnimationLayers
|
||||
.First(l => l.type == VRCAvatarDescriptor.AnimLayerType.FX)
|
||||
.animatorController;
|
||||
|
||||
fx.parameters = fx.parameters.Concat(new[] { paramDef }).ToArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
public string GetActiveSelfProxy(GameObject obj)
|
||||
|
@ -1,9 +1,12 @@
|
||||
using System.Linq;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
using BuildContext = nadena.dev.ndmf.BuildContext;
|
||||
|
||||
namespace nadena.dev.modular_avatar.animation
|
||||
{
|
||||
@ -23,11 +26,16 @@ namespace nadena.dev.modular_avatar.animation
|
||||
|
||||
if (fx == null) return;
|
||||
|
||||
var nullMotion = new AnimationClip();
|
||||
nullMotion.name = "NullMotion";
|
||||
|
||||
var blendTree = new BlendTree();
|
||||
blendTree.blendType = BlendTreeType.Direct;
|
||||
blendTree.useAutomaticThresholds = false;
|
||||
|
||||
blendTree.children = asc.BoundReadableProperties.Select(GenerateDelayChild).ToArray();
|
||||
blendTree.children = asc.BoundReadableProperties
|
||||
.Select(prop => GenerateDelayChild(nullMotion, prop))
|
||||
.ToArray();
|
||||
|
||||
var asm = new AnimatorStateMachine();
|
||||
var state = new AnimatorState();
|
||||
@ -52,9 +60,24 @@ namespace nadena.dev.modular_avatar.animation
|
||||
defaultWeight = 1,
|
||||
blendingMode = AnimatorLayerBlendingMode.Override
|
||||
}).ToArray();
|
||||
|
||||
// Ensure the initial state of readable props matches the actual state of the gameobject
|
||||
var parameters = fx.parameters;
|
||||
var paramToIndex = parameters.Select((p, i) => (p, i)).ToDictionary(x => x.p.name, x => x.i);
|
||||
foreach (var (binding, prop) in asc.BoundReadableProperties)
|
||||
{
|
||||
var obj = asc.PathMappings.PathToObject(binding.path);
|
||||
|
||||
if (obj != null && paramToIndex.TryGetValue(prop, out var index))
|
||||
{
|
||||
parameters[index].defaultFloat = obj.activeSelf ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
fx.parameters = parameters;
|
||||
}
|
||||
|
||||
private ChildMotion GenerateDelayChild((EditorCurveBinding, string) binding)
|
||||
private ChildMotion GenerateDelayChild(Motion nullMotion, (EditorCurveBinding, string) binding)
|
||||
{
|
||||
var ecb = binding.Item1;
|
||||
var prop = binding.Item2;
|
||||
@ -64,12 +87,43 @@ namespace nadena.dev.modular_avatar.animation
|
||||
curve.AddKey(0, 1);
|
||||
AnimationUtility.SetEditorCurve(motion, ecb, curve);
|
||||
|
||||
// Occasionally, we'll have a very small value pop up, probably due to FP errors.
|
||||
// To correct for this, instead of directly using the property in the direct blend tree,
|
||||
// we'll use a 1D blend tree to give ourselves a buffer.
|
||||
|
||||
var bufferBlendTree = new BlendTree();
|
||||
bufferBlendTree.blendType = BlendTreeType.Simple1D;
|
||||
bufferBlendTree.useAutomaticThresholds = false;
|
||||
bufferBlendTree.blendParameter = prop;
|
||||
bufferBlendTree.children = new[]
|
||||
{
|
||||
new ChildMotion
|
||||
{
|
||||
motion = nullMotion,
|
||||
timeScale = 1,
|
||||
threshold = 0
|
||||
},
|
||||
new ChildMotion
|
||||
{
|
||||
motion = nullMotion,
|
||||
timeScale = 1,
|
||||
threshold = 0.01f
|
||||
},
|
||||
new ChildMotion
|
||||
{
|
||||
motion = motion,
|
||||
timeScale = 1,
|
||||
threshold = 1
|
||||
}
|
||||
};
|
||||
|
||||
return new ChildMotion
|
||||
{
|
||||
motion = motion,
|
||||
directBlendParameter = prop,
|
||||
motion = bufferBlendTree,
|
||||
directBlendParameter = MergeBlendTreePass.ALWAYS_ONE,
|
||||
timeScale = 1
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -368,6 +368,7 @@ namespace nadena.dev.modular_avatar.animation
|
||||
}
|
||||
Profiler.EndSample();
|
||||
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
var layers = context.AvatarDescriptor.baseAnimationLayers
|
||||
.Concat(context.AvatarDescriptor.specialAnimationLayers);
|
||||
|
||||
@ -383,6 +384,7 @@ namespace nadena.dev.modular_avatar.animation
|
||||
ApplyMappingsToAvatarMask(acLayer.avatarMask);
|
||||
}
|
||||
Profiler.EndSample();
|
||||
#endif
|
||||
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#region
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
@ -57,4 +58,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
using UnityEditor;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using UnityEditor;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
@ -45,3 +46,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -368,7 +368,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
Localization.ShowLanguageUI();
|
||||
ShowLanguageUI();
|
||||
}
|
||||
|
||||
private string ObjectHierarchyOrder(Component arg)
|
||||
@ -415,6 +415,9 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
var group = installer.gameObject.AddComponent<ModularAvatarMenuGroup>();
|
||||
var menuRoot = new GameObject();
|
||||
menuRoot.name = "Menu";
|
||||
|
||||
group.targetObject = menuRoot;
|
||||
|
||||
Undo.RegisterCreatedObjectUndo(menuRoot, "Extract menu");
|
||||
menuRoot.transform.SetParent(group.transform, false);
|
||||
foreach (var control in menu.controls)
|
||||
|
@ -301,10 +301,9 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
if (_type.hasMultipleDifferentValues) return;
|
||||
VRCExpressionsMenu.Control.ControlType type =
|
||||
(VRCExpressionsMenu.Control.ControlType) Enum
|
||||
.GetValues(typeof(VRCExpressionsMenu.Control.ControlType))
|
||||
.GetValue(_type.enumValueIndex);
|
||||
var controlTypeArray = Enum.GetValues(typeof(VRCExpressionsMenu.Control.ControlType));
|
||||
var index = Math.Clamp(_type.enumValueIndex, 0, controlTypeArray.Length - 1);
|
||||
var type = (VRCExpressionsMenu.Control.ControlType)controlTypeArray.GetValue(index);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using nadena.dev.modular_avatar.ui;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using nadena.dev.modular_avatar.ui;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.ScriptableObjects;
|
||||
@ -62,4 +63,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
Undo.RegisterCreatedObjectUndo(toggle, "Create Toggle");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -20,7 +20,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
private readonly DropdownField _boolField;
|
||||
|
||||
private ParameterSyncType _syncType;
|
||||
|
||||
private bool _hasInitialBinding;
|
||||
|
||||
public DefaultValueField()
|
||||
{
|
||||
// Hidden binding elements
|
||||
@ -57,28 +58,39 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
_numberField.style.display = DisplayStyle.Flex;
|
||||
_boolField.style.display = DisplayStyle.None;
|
||||
OnUpdateNumberValue(_numberField.value);
|
||||
OnUpdateNumberValue(_numberField.value, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_numberField.style.display = DisplayStyle.None;
|
||||
_boolField.style.display = DisplayStyle.Flex;
|
||||
OnUpdateBoolValue(_boolField.value);
|
||||
OnUpdateBoolValue(_boolField.value, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUpdateNumberValue(string value)
|
||||
private void OnUpdateNumberValue(string value, bool implicitUpdate = false)
|
||||
{
|
||||
// Upon initial creation, sometimes the OnUpdateSyncType fires before we receive the initial value event.
|
||||
// In this case, suppress the update to avoid losing data.
|
||||
if (implicitUpdate && !_hasInitialBinding) return;
|
||||
|
||||
var theValue = _defaultValueField.value;
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
_defaultValueField.value = 0;
|
||||
if (!implicitUpdate)
|
||||
{
|
||||
_defaultValueField.value = 0;
|
||||
}
|
||||
|
||||
theValue = _defaultValueField.value;
|
||||
|
||||
_hasExplicitDefaultValueField.value = false;
|
||||
}
|
||||
else if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed)
|
||||
&& !float.IsNaN(parsed)
|
||||
&& !float.IsInfinity(parsed))
|
||||
{
|
||||
_defaultValueField.value = _syncType switch
|
||||
theValue = _defaultValueField.value = _syncType switch
|
||||
{
|
||||
ParameterSyncType.Int => Mathf.FloorToInt(Mathf.Clamp(parsed, 0, 255)),
|
||||
ParameterSyncType.Float => Mathf.Clamp(parsed, -1, 1),
|
||||
@ -88,11 +100,15 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
_hasExplicitDefaultValueField.value = true;
|
||||
}
|
||||
|
||||
UpdateVisibleField(_defaultValueField.value, _hasExplicitDefaultValueField.value);
|
||||
UpdateVisibleField(theValue, _hasExplicitDefaultValueField.value);
|
||||
}
|
||||
|
||||
private void OnUpdateBoolValue(string value)
|
||||
private void OnUpdateBoolValue(string value, bool implicitUpdate = false)
|
||||
{
|
||||
// Upon initial creation, sometimes the OnUpdateSyncType fires before we receive the initial value event.
|
||||
// In this case, suppress the update to avoid losing data.
|
||||
if (implicitUpdate && !_hasInitialBinding) return;
|
||||
|
||||
_defaultValueField.value = value == V_True ? 1 : 0;
|
||||
_hasExplicitDefaultValueField.value = value != V_None;
|
||||
|
||||
@ -101,6 +117,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
private void UpdateVisibleField(float value, bool hasExplicitValue)
|
||||
{
|
||||
_hasInitialBinding = true;
|
||||
|
||||
if (hasExplicitValue || Mathf.Abs(value) > 0.0000001)
|
||||
{
|
||||
_numberField.SetValueWithoutNotify(value.ToString(CultureInfo.InvariantCulture));
|
||||
|
@ -119,9 +119,11 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
internal static VRCExpressionsMenu.Control CloneControl(VRCExpressionsMenu.Control c)
|
||||
{
|
||||
var type = c.type != 0 ? c.type : VRCExpressionsMenu.Control.ControlType.Button;
|
||||
|
||||
return new VRCExpressionsMenu.Control()
|
||||
{
|
||||
type = c.type,
|
||||
type = type,
|
||||
name = c.name,
|
||||
icon = c.icon,
|
||||
parameter = new VRCExpressionsMenu.Control.Parameter() { name = c.parameter?.name },
|
||||
|
@ -57,26 +57,29 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
#endif
|
||||
seq.WithRequiredExtension(typeof(AnimationServicesContext), _s2 =>
|
||||
{
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute())
|
||||
.PreviewingWith(new ShapeChangerPreview(), new ObjectSwitcherPreview(), new MaterialSetterPreview());
|
||||
#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.Run(BoneProxyPluginPass.Instance);
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.Run(VisibleHeadAccessoryPluginPass.Instance);
|
||||
#endif
|
||||
seq.Run("World Fixed Object",
|
||||
ctx => new WorldFixedObjectProcessor().Process(ctx)
|
||||
);
|
||||
seq.Run(ReplaceObjectPluginPass.Instance);
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
seq.Run(BlendshapeSyncAnimationPluginPass.Instance);
|
||||
#endif
|
||||
seq.Run(GameObjectDelayDisablePass.Instance);
|
||||
#endif
|
||||
seq.Run(ConstraintConverterPass.Instance);
|
||||
});
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
@ -213,6 +216,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
}
|
||||
}
|
||||
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
class VisibleHeadAccessoryPluginPass : MAPass<VisibleHeadAccessoryPluginPass>
|
||||
{
|
||||
protected override void Execute(ndmf.BuildContext context)
|
||||
@ -220,6 +224,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
new VisibleHeadAccessoryProcessor(MAContext(context)).Process();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
class ReplaceObjectPluginPass : MAPass<ReplaceObjectPluginPass>
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using nadena.dev.ndmf.preview;
|
||||
@ -346,4 +347,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
@ -128,7 +129,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
foreach (var cond in rule.ControllingConditions)
|
||||
{
|
||||
var paramName = cond.Parameter;
|
||||
if (ForcePropertyOverrides.TryGetValue(paramName, out var value))
|
||||
if (ForcePropertyOverrides?.TryGetValue(paramName, out var value) == true)
|
||||
{
|
||||
cond.InitialValue = value;
|
||||
}
|
||||
@ -304,4 +305,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
#region
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -613,3 +614,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
using nadena.dev.ndmf;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
|
||||
@ -53,4 +54,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -144,4 +145,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.preview;
|
||||
@ -70,4 +71,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
return _context.Observe(mami, _ => mami.isDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -104,4 +105,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEngine;
|
||||
@ -236,4 +237,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
#region
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -294,4 +295,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
@ -637,4 +638,5 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
|
||||
ve_inactive.style.display = activeState ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,5 +1,7 @@
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using nadena.dev.modular_avatar.core.editor.Simulator;
|
||||
#endif
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
@ -42,11 +44,13 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
private void OpenDebugger()
|
||||
{
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
GameObject target = Selection.activeGameObject;
|
||||
if (ReferenceObject is Component c) target = c.gameObject;
|
||||
else if (ReferenceObject is GameObject go) target = go;
|
||||
|
||||
ROSimulator.OpenDebugger(target);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using nadena.dev.modular_avatar.core.editor.Simulator;
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
using System;
|
||||
using nadena.dev.modular_avatar.core.editor.Simulator;
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
@ -74,4 +76,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -146,28 +146,57 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
out var avatarRoot, out var avatarHips, out var outfitHips)
|
||||
) return;
|
||||
|
||||
Undo.SetCurrentGroupName("Setup Outfit");
|
||||
|
||||
var avatarArmature = avatarHips.transform.parent;
|
||||
var outfitArmature = outfitHips.transform.parent;
|
||||
|
||||
if (outfitArmature.GetComponent<ModularAvatarMergeArmature>() == null)
|
||||
var merge = outfitArmature.GetComponent<ModularAvatarMergeArmature>();
|
||||
if (merge == null)
|
||||
{
|
||||
merge = Undo.AddComponent<ModularAvatarMergeArmature>(outfitArmature.gameObject);
|
||||
} else {
|
||||
Undo.RecordObject(merge, "");
|
||||
}
|
||||
|
||||
if (merge.mergeTarget == null || merge.mergeTargetObject == null)
|
||||
{
|
||||
var merge = Undo.AddComponent<ModularAvatarMergeArmature>(outfitArmature.gameObject);
|
||||
merge.mergeTarget = new AvatarObjectReference();
|
||||
merge.mergeTarget.referencePath = RuntimeUtil.RelativePath(avatarRoot, avatarArmature.gameObject);
|
||||
merge.LockMode = ArmatureLockMode.BaseToMerge;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(merge.prefix) && string.IsNullOrEmpty(merge.suffix))
|
||||
{
|
||||
merge.InferPrefixSuffix();
|
||||
}
|
||||
|
||||
var outfitAnimator = outfitRoot.GetComponent<Animator>();
|
||||
var outfitHumanoidBones = GetOutfitHumanoidBones(outfitRoot.transform, outfitAnimator);
|
||||
var avatarAnimator = avatarRoot.GetComponent<Animator>();
|
||||
List<Transform> subRoots = new List<Transform>();
|
||||
HeuristicBoneMapper.RenameBonesByHeuristic(merge, skipped: subRoots, outfitHumanoidBones: outfitHumanoidBones, avatarAnimator: avatarAnimator);
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(merge);
|
||||
|
||||
// If the outfit has an UpperChest bone but the avatar doesn't, add an additional MergeArmature to
|
||||
// help with this
|
||||
foreach (var subRoot in subRoots)
|
||||
var outfitAnimator = outfitRoot.GetComponent<Animator>();
|
||||
var outfitHumanoidBones = GetOutfitHumanoidBones(outfitRoot.transform, outfitAnimator);
|
||||
var avatarAnimator = avatarRoot.GetComponent<Animator>();
|
||||
List<Transform> subRoots = new List<Transform>();
|
||||
HeuristicBoneMapper.RenameBonesByHeuristic(merge, skipped: subRoots, outfitHumanoidBones: outfitHumanoidBones, avatarAnimator: avatarAnimator);
|
||||
|
||||
// If the outfit has an UpperChest bone but the avatar doesn't, add an additional MergeArmature to
|
||||
// help with this
|
||||
foreach (var subRoot in subRoots)
|
||||
{
|
||||
var subConfig = subRoot.GetComponent<ModularAvatarMergeArmature>();
|
||||
var subConfigMangleNames = false;
|
||||
if (subConfig == null)
|
||||
{
|
||||
subConfig = Undo.AddComponent<ModularAvatarMergeArmature>(subRoot.gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Undo.RecordObject(subConfig, "");
|
||||
subConfigMangleNames = subConfig.mangleNames;
|
||||
}
|
||||
|
||||
if (subConfig.mergeTarget == null || subConfig.mergeTargetObject == null)
|
||||
{
|
||||
var subConfig = Undo.AddComponent<ModularAvatarMergeArmature>(subRoot.gameObject);
|
||||
var parentTransform = subConfig.transform.parent;
|
||||
var parentConfig = parentTransform.GetComponentInParent<ModularAvatarMergeArmature>();
|
||||
var parentMapping = parentConfig.MapBone(parentTransform);
|
||||
@ -178,33 +207,47 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
subConfig.LockMode = ArmatureLockMode.BaseToMerge;
|
||||
subConfig.prefix = merge.prefix;
|
||||
subConfig.suffix = merge.suffix;
|
||||
subConfig.mangleNames = false;
|
||||
subConfig.mangleNames = subConfigMangleNames;
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(subConfig);
|
||||
}
|
||||
}
|
||||
|
||||
var avatarRootMatchingArmature = avatarRoot.transform.Find(outfitArmature.gameObject.name);
|
||||
if (merge.prefix == "" && merge.suffix == "" && avatarRootMatchingArmature != null)
|
||||
{
|
||||
// We have an armature whose names exactly match the root armature - this can cause some serious
|
||||
// confusion in Unity's humanoid armature matching system. Fortunately, we can avoid this by
|
||||
// renaming a bone close to the root; this will ensure the number of matching bones is small, and
|
||||
// Unity's heuristics (apparently) will choose the base avatar's armature as the "true" armature.
|
||||
outfitArmature.name += ".1";
|
||||
var avatarRootMatchingArmature = avatarRoot.transform.Find(outfitArmature.gameObject.name);
|
||||
if (merge.prefix == "" && merge.suffix == "" && avatarRootMatchingArmature != null)
|
||||
{
|
||||
// We have an armature whose names exactly match the root armature - this can cause some serious
|
||||
// confusion in Unity's humanoid armature matching system. Fortunately, we can avoid this by
|
||||
// renaming a bone close to the root; this will ensure the number of matching bones is small, and
|
||||
// Unity's heuristics (apparently) will choose the base avatar's armature as the "true" armature.
|
||||
outfitArmature.name += ".1";
|
||||
|
||||
// Also make sure to refresh the avatar's animator humanoid bone cache.
|
||||
var humanDescription = avatarAnimator.avatar;
|
||||
avatarAnimator.avatar = null;
|
||||
// ReSharper disable once Unity.InefficientPropertyAccess
|
||||
avatarAnimator.avatar = humanDescription;
|
||||
}
|
||||
// Also make sure to refresh the avatar's animator humanoid bone cache.
|
||||
var humanDescription = avatarAnimator.avatar;
|
||||
avatarAnimator.avatar = null;
|
||||
// ReSharper disable once Unity.InefficientPropertyAccess
|
||||
avatarAnimator.avatar = humanDescription;
|
||||
}
|
||||
|
||||
FixAPose(avatarRoot, outfitArmature);
|
||||
|
||||
var meshSettings = outfitRoot.GetComponent<ModularAvatarMeshSettings>();
|
||||
var mSInheritProbeAnchor = ModularAvatarMeshSettings.InheritMode.SetOrInherit;
|
||||
var mSInheritBounds = ModularAvatarMeshSettings.InheritMode.SetOrInherit;
|
||||
if (outfitRoot != null
|
||||
&& outfitRoot.GetComponent<ModularAvatarMeshSettings>() == null
|
||||
&& meshSettings == null
|
||||
&& outfitRoot.GetComponentInParent<ModularAvatarMeshSettings>() == null)
|
||||
{
|
||||
var meshSettings = Undo.AddComponent<ModularAvatarMeshSettings>(outfitRoot.gameObject);
|
||||
meshSettings = Undo.AddComponent<ModularAvatarMeshSettings>(outfitRoot.gameObject);
|
||||
} else if (outfitRoot != null && meshSettings != null) {
|
||||
Undo.RecordObject(meshSettings, "");
|
||||
mSInheritProbeAnchor = meshSettings.InheritProbeAnchor;
|
||||
mSInheritBounds = meshSettings.InheritBounds;
|
||||
}
|
||||
|
||||
if (meshSettings != null
|
||||
&& (meshSettings.ProbeAnchor == null || meshSettings.ProbeAnchor.Get(meshSettings) == null
|
||||
|| meshSettings.RootBone == null || meshSettings.RootBone.Get(meshSettings) == null))
|
||||
{
|
||||
Transform rootBone = null, probeAnchor = null;
|
||||
Bounds bounds = ModularAvatarMeshSettings.DEFAULT_BOUNDS;
|
||||
|
||||
@ -220,8 +263,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
rootBone = avatarRoot.transform;
|
||||
}
|
||||
|
||||
meshSettings.InheritProbeAnchor = ModularAvatarMeshSettings.InheritMode.SetOrInherit;
|
||||
meshSettings.InheritBounds = ModularAvatarMeshSettings.InheritMode.SetOrInherit;
|
||||
meshSettings.InheritProbeAnchor = mSInheritProbeAnchor;
|
||||
meshSettings.InheritBounds = mSInheritBounds;
|
||||
|
||||
meshSettings.ProbeAnchor = new AvatarObjectReference();
|
||||
meshSettings.ProbeAnchor.referencePath = RuntimeUtil.RelativePath(avatarRoot, probeAnchor.gameObject);
|
||||
@ -229,6 +272,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
meshSettings.RootBone = new AvatarObjectReference();
|
||||
meshSettings.RootBone.referencePath = RuntimeUtil.RelativePath(avatarRoot, rootBone.gameObject);
|
||||
meshSettings.Bounds = bounds;
|
||||
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(meshSettings);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#region
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -251,3 +252,5 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -19,7 +19,7 @@ namespace nadena.dev.modular_avatar.core
|
||||
/// initially inactive in the scene (which can have high overhead if the user has a lot of inactive avatars in the
|
||||
/// scene).
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
[AddComponentMenu("/")]
|
||||
[ExecuteInEditMode]
|
||||
[DefaultExecutionOrder(-9998)]
|
||||
public class Activator : MonoBehaviour, IEditorOnly
|
||||
@ -30,7 +30,7 @@ namespace nadena.dev.modular_avatar.core
|
||||
}
|
||||
}
|
||||
|
||||
[AddComponentMenu("")]
|
||||
[AddComponentMenu("/")]
|
||||
[ExecuteInEditMode]
|
||||
[DefaultExecutionOrder(-9997)]
|
||||
public class AvatarActivator : MonoBehaviour, IEditorOnly
|
||||
|
@ -6,7 +6,7 @@ namespace nadena.dev.modular_avatar.core
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
[AddComponentMenu("Modular Avatar/MA Convert Constraints")]
|
||||
#else
|
||||
[AddComponentMenu("")]
|
||||
[AddComponentMenu("/")]
|
||||
#endif
|
||||
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/convert-constraints?lang=auto")]
|
||||
public class ModularAvatarConvertConstraints : AvatarTagComponent
|
||||
|
50
UnitTests~/ReactiveComponent/ObjectToggleTests.cs
Normal file
50
UnitTests~/ReactiveComponent/ObjectToggleTests.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System.Linq;
|
||||
using modular_avatar_tests;
|
||||
using nadena.dev.modular_avatar.core;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnitTests.ReactiveComponent
|
||||
{
|
||||
internal class ObjectToggleTests : TestBase
|
||||
{
|
||||
[Test]
|
||||
public void WhenObjectIsAlwaysOn_CorrectProxyParameterIsGenerated()
|
||||
{
|
||||
var root = CreateRoot("root");
|
||||
var obj = CreateChild(root, "obj");
|
||||
var toggle = CreateChild(root, "toggle");
|
||||
|
||||
// Prevent obj from being removed by the GC game objects pass
|
||||
obj.AddComponent<MeshRenderer>();
|
||||
|
||||
var toggleComponent = toggle.AddComponent<ModularAvatarObjectToggle>();
|
||||
var aor = new AvatarObjectReference();
|
||||
aor.Set(obj);
|
||||
|
||||
toggleComponent.Objects = new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Active = false,
|
||||
Object = aor
|
||||
}
|
||||
};
|
||||
|
||||
AvatarProcessor.ProcessAvatar(root);
|
||||
|
||||
// TODO: Ideally we should start using play mode testing for these things...
|
||||
var fx = (AnimatorController)FindFxController(root).animatorController;
|
||||
var readableProp = fx.parameters.FirstOrDefault(
|
||||
p => p.name.StartsWith("__MA/ReadableProp/obj/UnityEngine.GameObject/m_IsActive")
|
||||
);
|
||||
|
||||
Assert.IsNotNull(readableProp);
|
||||
Assert.AreEqual(readableProp.defaultFloat, 0);
|
||||
|
||||
Assert.IsFalse(obj.activeSelf);
|
||||
}
|
||||
}
|
||||
}
|
3
UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta
Normal file
3
UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c68d69f7b4a46c5b2ce3d8f26b0fa76
|
||||
timeCreated: 1729376563
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "nadena.dev.modular-avatar",
|
||||
"displayName": "Modular Avatar",
|
||||
"version": "1.10.4",
|
||||
"version": "1.10.5",
|
||||
"unity": "2022.3",
|
||||
"description": "A suite of tools for assembling your avatar out of reusable components",
|
||||
"author": {
|
||||
@ -16,6 +16,6 @@
|
||||
},
|
||||
"vpmDependencies": {
|
||||
"com.vrchat.avatars": ">=3.7.0",
|
||||
"nadena.dev.ndmf": ">=1.5.4 <2.0.0-a"
|
||||
"nadena.dev.ndmf": ">=1.5.6 <2.0.0-a"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user