mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-01-30 18:22:52 +08:00
feat(params): Allow for MA Parameters to _not_ specify a default value (#636)
* feat(params): track whether a default value was explicitly set * feat(params): improve default value resolution and error handling
This commit is contained in:
parent
86fc302fa5
commit
1fc9c2d4ac
4
.github/ProjectRoot/vpm-manifest-2019.json
vendored
4
.github/ProjectRoot/vpm-manifest-2019.json
vendored
@ -4,7 +4,7 @@
|
||||
"version": "3.4.2"
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.3.0-rc.2"
|
||||
"version": "1.3.0-rc.6"
|
||||
}
|
||||
},
|
||||
"locked": {
|
||||
@ -19,7 +19,7 @@
|
||||
"dependencies": {}
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.3.0-rc.2"
|
||||
"version": "1.3.0-rc.6"
|
||||
}
|
||||
}
|
||||
}
|
4
.github/ProjectRoot/vpm-manifest-2022.json
vendored
4
.github/ProjectRoot/vpm-manifest-2022.json
vendored
@ -4,7 +4,7 @@
|
||||
"version": "3.5.0"
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.3.0-rc.2"
|
||||
"version": "1.3.0-rc.6"
|
||||
}
|
||||
},
|
||||
"locked": {
|
||||
@ -19,7 +19,7 @@
|
||||
"dependencies": {}
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.3.0-rc.2"
|
||||
"version": "1.3.0-rc.6"
|
||||
}
|
||||
}
|
||||
}
|
78
Editor/Inspector/Parameters/DefaultValueField.cs
Normal file
78
Editor/Inspector/Parameters/DefaultValueField.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using System.Globalization;
|
||||
using nadena.dev.modular_avatar.core;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class DefaultValueField : VisualElement
|
||||
{
|
||||
public new class UxmlFactory : UxmlFactory<DefaultValueField, UxmlTraits>
|
||||
{
|
||||
}
|
||||
|
||||
private readonly TextField _visibleField;
|
||||
private readonly FloatField _defaultValueField;
|
||||
private readonly Toggle _hasExplicitDefaultSetField;
|
||||
|
||||
public DefaultValueField()
|
||||
{
|
||||
// Hidden binding elements
|
||||
_defaultValueField = new FloatField();
|
||||
_hasExplicitDefaultSetField = new Toggle();
|
||||
|
||||
_defaultValueField.RegisterValueChangedCallback(
|
||||
evt => UpdateVisibleField(evt.newValue, _hasExplicitDefaultSetField.value));
|
||||
_defaultValueField.bindingPath = nameof(ParameterConfig.defaultValue);
|
||||
|
||||
_hasExplicitDefaultSetField.RegisterValueChangedCallback(
|
||||
evt => UpdateVisibleField(_defaultValueField.value, evt.newValue));
|
||||
_hasExplicitDefaultSetField.bindingPath = nameof(ParameterConfig.hasExplicitDefaultValue);
|
||||
|
||||
_visibleField = new TextField();
|
||||
_visibleField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(evt.newValue))
|
||||
{
|
||||
_hasExplicitDefaultSetField.value = false;
|
||||
_defaultValueField.value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_hasExplicitDefaultSetField.value = true;
|
||||
_defaultValueField.value = float.Parse(evt.newValue, CultureInfo.InvariantCulture);
|
||||
}
|
||||
});
|
||||
|
||||
_defaultValueField.style.width = 0;
|
||||
_defaultValueField.SetEnabled(false);
|
||||
_hasExplicitDefaultSetField.style.width = 0;
|
||||
_hasExplicitDefaultSetField.SetEnabled(false);
|
||||
|
||||
style.flexDirection = FlexDirection.Row;
|
||||
|
||||
Add(_visibleField);
|
||||
Add(_defaultValueField);
|
||||
Add(_hasExplicitDefaultSetField);
|
||||
}
|
||||
|
||||
public void ManualBindProperty(SerializedProperty property)
|
||||
{
|
||||
_defaultValueField.BindProperty(property);
|
||||
_hasExplicitDefaultSetField.BindProperty(property);
|
||||
}
|
||||
|
||||
private void UpdateVisibleField(float value, bool hasExplicitValue)
|
||||
{
|
||||
if (Mathf.Abs(value) > 0.0000001)
|
||||
{
|
||||
hasExplicitValue = true;
|
||||
}
|
||||
|
||||
var str = hasExplicitValue ? value.ToString(CultureInfo.InvariantCulture) : "";
|
||||
_visibleField.SetValueWithoutNotify(str);
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/Inspector/Parameters/DefaultValueField.cs.meta
Normal file
3
Editor/Inspector/Parameters/DefaultValueField.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea5245cc9b4342d180d30724e8838c9b
|
||||
timeCreated: 1706340919
|
@ -70,6 +70,25 @@ namespace nadena.dev.modular_avatar.core.editor.Parameters
|
||||
remapTo.style.display = evt.changedProperty.boolValue ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
remapToPlaceholder.style.display = evt.changedProperty.boolValue ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
});
|
||||
|
||||
|
||||
// This is a bit of a hack, but I'm not sure of another way to properly align property labels with a custom
|
||||
// field, when we only want to manipulate a subset of fields on an object...
|
||||
var defaultValueField = root.Q<VisualElement>("innerDefaultValueField"); // create ahead of time so it's bound...
|
||||
|
||||
// Then move it into the property field once the property field has created its inner controls
|
||||
var defaultValueProp = root.Q<PropertyField>("defaultValueProp");
|
||||
defaultValueProp.RegisterCallback<GeometryChangedEvent>(evt =>
|
||||
{
|
||||
var floatField = defaultValueProp.Q<FloatField>();
|
||||
var innerField = floatField?.Q<DefaultValueField>();
|
||||
|
||||
if (floatField != null && innerField == null)
|
||||
{
|
||||
defaultValueField.RemoveFromHierarchy();
|
||||
floatField.contentContainer.Add(defaultValueField);
|
||||
}
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
@ -1,7 +1,12 @@
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:engine="UnityEditor.UIElements" editor="UnityEditor.UIElements" ma="nadena.dev.modular_avatar.core.editor" editor-extension-mode="False">
|
||||
<ui:UXML
|
||||
xmlns:ui="UnityEngine.UIElements"
|
||||
xmlns:engine="UnityEditor.UIElements"
|
||||
xmlns:ma="nadena.dev.modular_avatar.core.editor"
|
||||
editor-extension-mode="False"
|
||||
>
|
||||
<ui:VisualElement name="MiniDisplay">
|
||||
<ui:Label text="merge_parameter.ui.defaultValue" class="ndmf-tr"/>
|
||||
<ui:FloatField binding-path="defaultValue"/>
|
||||
<ma:DefaultValueField/>
|
||||
<ui:Label text="merge_parameter.ui.saved" class="ndmf-tr"/>
|
||||
<ui:Toggle binding-path="saved"/>
|
||||
</ui:VisualElement>
|
||||
@ -12,7 +17,15 @@
|
||||
<engine:PropertyField binding-path="remapTo" label="merge_parameter.ui.remapTo" name="remapTo" class="ndmf-tr" />
|
||||
<ui:TextField label="##merge_parameter.ui.remapTo" text="merge_parameter.ui.remapTo.automatic" name="remapToPlaceholder" enabled="false" class="ndmf-tr unity-base-field__aligned" />
|
||||
|
||||
<engine:PropertyField binding-path="defaultValue" name="defaultValue" label="merge_parameter.ui.defaultValue" class="ndmf-tr ParameterConfig__isPrefix_falseOnly"/>
|
||||
<!-- this field is not visible until it's moved into the PropertyField below -->
|
||||
<ma:DefaultValueField
|
||||
name="innerDefaultValueField"
|
||||
class="unity-base-field__input unity-property-field__input"
|
||||
/>
|
||||
|
||||
<engine:PropertyField binding-path="defaultValue" name="defaultValueProp" label="merge_parameter.ui.defaultValue" class="ndmf-tr ParameterConfig__isPrefix_falseOnly">
|
||||
|
||||
</engine:PropertyField>
|
||||
|
||||
<engine:PropertyField binding-path="saved" label="merge_parameter.ui.saved" class="ndmf-tr ParameterConfig__isPrefix_falseOnly" />
|
||||
|
||||
|
@ -28,6 +28,23 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#defaultValueProp > FloatField > FloatInput {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#ParameterConfigRoot > DefaultValueField {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#innerDefaultValueField {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
DefaultValueField > TextField {
|
||||
flex-grow: 1;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#MiniDisplay {
|
||||
flex-direction: row;
|
||||
align-self: flex-end;
|
||||
@ -37,14 +54,14 @@
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
#MiniDisplay > FloatField {
|
||||
#MiniDisplay > DefaultValueField {
|
||||
max-width: 60px;
|
||||
flex-grow: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
#MiniDisplay > FloatField TextElement {
|
||||
#MiniDisplay > DefaultValueField TextElement {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
|
@ -141,15 +141,22 @@
|
||||
"error.merge_armature.physbone_on_humanoid_bone": "[MA-0002] PhysBone component found on humanoid bone",
|
||||
"error.merge_armature.physbone_on_humanoid_bone:hint": "Some Humanoid bones in the armature to merge are controlled by PhysBones, and can't be merged properly because its position is different from the corresponding Humanoid bone in the merge target. You should remove the PhysBones on those Humanoid bones in the armature to merge.",
|
||||
|
||||
"error.internal_error": "An internal error has occurred: {0}\nwhen processing:",
|
||||
"error.internal_error": "[MA-9999] An internal error has occurred: {0}\nwhen processing:",
|
||||
|
||||
"error.merge_animator.param_type_mismatch": "[MA-0003] Parameter type mismatch",
|
||||
"error.merge_animator.param_type_mismatch:description": "Parameter {0} has multiple types: {1} != {2}",
|
||||
|
||||
"error.rename_params.too_many_synced_params": "[MA-0004] Too many synced parameters",
|
||||
"error.rename_params.too_many_synced_params:description": "You have too many synced parameters in your avatar. You have assigned {0} bits worth of parameters, but the limit is {1}",
|
||||
|
||||
"error.rename_params.type_conflict": "[MA-0006] Parameter type conflict",
|
||||
"error.rename_params.type_conflict:description": "Parameter {0} has multiple types specified: {1} != {2}",
|
||||
|
||||
"error.replace_object.null_target": "[MA-0005] No target specified",
|
||||
"error.rename_params.default_value_conflict": "[MA-0007] Default value conflict",
|
||||
"error.rename_params.default_value_conflict:description": "Parameter {0} has multiple default values specified: {1} != {2}",
|
||||
"error.rename_params.default_value_conflict:hint": "To avoid unpredictable behavior, leave the default value field blank in all but on MA Parameters component. If multiple values are present, Modular Avatar will select the first default value specified in the hierarchy order.",
|
||||
|
||||
"error.replace_object.null_target": "[MA-0008] No target specified",
|
||||
"error.replace_object.null_target:hint": "Replace object needs a target object to replace. Try setting one.",
|
||||
|
||||
"validation.blendshape_sync.no_local_renderer": "[MA-1000] No renderer found on this object",
|
||||
|
@ -6,6 +6,7 @@ using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
@ -15,6 +16,8 @@ using VRC.SDK3.Dynamics.Contact.Components;
|
||||
using VRC.SDK3.Dynamics.PhysBone.Components;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class RenameParametersHook
|
||||
@ -25,21 +28,95 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
private int internalParamIndex = 0;
|
||||
|
||||
private Dictionary<string, VRCExpressionParameters.Parameter> _syncedParams =
|
||||
new Dictionary<string, VRCExpressionParameters.Parameter>();
|
||||
class ParameterInfo
|
||||
{
|
||||
private static long encounterOrderCounter;
|
||||
|
||||
public ParameterConfig ResolvedParameter;
|
||||
public List<UnityObject> TypeSources = new List<UnityObject>();
|
||||
public List<UnityObject> DefaultSources = new List<UnityObject>();
|
||||
public ImmutableHashSet<float> ConflictingValues = ImmutableHashSet<float>.Empty;
|
||||
public ImmutableHashSet<ParameterSyncType> ConflictingSyncTypes = ImmutableHashSet<ParameterSyncType>.Empty;
|
||||
|
||||
public bool TypeConflict, DefaultValueConflict;
|
||||
public long encounterOrder = encounterOrderCounter++;
|
||||
|
||||
public VRCExpressionParameters.ValueType? ValueType
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (ResolvedParameter.syncType)
|
||||
{
|
||||
case ParameterSyncType.Bool: return VRCExpressionParameters.ValueType.Bool;
|
||||
case ParameterSyncType.Float: return VRCExpressionParameters.ValueType.Float;
|
||||
case ParameterSyncType.Int: return VRCExpressionParameters.ValueType.Int;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MergeSibling(ParameterInfo info)
|
||||
{
|
||||
MergeCommon(info);
|
||||
|
||||
if (ResolvedParameter.HasDefaultValue && info.ResolvedParameter.HasDefaultValue)
|
||||
{
|
||||
if (Math.Abs(ResolvedParameter.defaultValue - info.ResolvedParameter.defaultValue) > ParameterConfig.VALUE_EPSILON)
|
||||
{
|
||||
DefaultValueConflict = true;
|
||||
ConflictingValues = ConflictingValues.Add(ResolvedParameter.defaultValue);
|
||||
ConflictingValues = ConflictingValues.Add(info.ResolvedParameter.defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MergeChild(ParameterInfo info)
|
||||
{
|
||||
MergeCommon(info);
|
||||
|
||||
if (!ResolvedParameter.HasDefaultValue && info.ResolvedParameter.HasDefaultValue)
|
||||
{
|
||||
ResolvedParameter.defaultValue = info.ResolvedParameter.defaultValue;
|
||||
ResolvedParameter.hasExplicitDefaultValue = info.ResolvedParameter.hasExplicitDefaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
void MergeCommon(ParameterInfo info)
|
||||
{
|
||||
if (ResolvedParameter.syncType == ParameterSyncType.NotSynced)
|
||||
{
|
||||
ResolvedParameter.syncType = info.ResolvedParameter.syncType;
|
||||
} else if (ResolvedParameter.syncType != info.ResolvedParameter.syncType && info.ResolvedParameter.syncType != ParameterSyncType.NotSynced)
|
||||
{
|
||||
TypeConflict = true;
|
||||
ConflictingSyncTypes = ConflictingSyncTypes
|
||||
.Add(ResolvedParameter.syncType)
|
||||
.Add(info.ResolvedParameter.syncType);
|
||||
}
|
||||
|
||||
TypeSources.AddRange(info.TypeSources);
|
||||
DefaultSources.AddRange(info.DefaultSources);
|
||||
|
||||
TypeConflict = TypeConflict || info.TypeConflict;
|
||||
DefaultValueConflict = DefaultValueConflict || info.DefaultValueConflict;
|
||||
|
||||
ConflictingValues = ConflictingValues.Union(info.ConflictingValues);
|
||||
ConflictingSyncTypes = ConflictingSyncTypes.Union(info.ConflictingSyncTypes);
|
||||
|
||||
encounterOrder = Math.Min(encounterOrder, info.encounterOrder);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPreprocessAvatar(GameObject avatar, BuildContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_syncedParams.Clear();
|
||||
var syncParams = WalkTree(avatar, ImmutableDictionary<string, string>.Empty, ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
WalkTree(avatar, ImmutableDictionary<string, string>.Empty, ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
SetExpressionParameters(avatar);
|
||||
SetExpressionParameters(avatar, syncParams);
|
||||
}
|
||||
|
||||
private void SetExpressionParameters(GameObject avatarRoot)
|
||||
private void SetExpressionParameters(GameObject avatarRoot, ImmutableDictionary<string, ParameterInfo> syncParams)
|
||||
{
|
||||
var avatar = avatarRoot.GetComponent<VRCAvatarDescriptor>();
|
||||
var expParams = avatar.expressionParameters;
|
||||
@ -60,15 +137,61 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
_context.SaveAsset(expParams);
|
||||
|
||||
var knownParams = expParams.parameters.Select(p => p.name).ToImmutableHashSet();
|
||||
var parameters = expParams.parameters.ToList();
|
||||
var parameters = expParams.parameters
|
||||
.Select(p => ResolveParameter(p, syncParams))
|
||||
.ToList();
|
||||
|
||||
foreach (var kvp in _syncedParams)
|
||||
foreach (var kvp in syncParams)
|
||||
{
|
||||
var name = kvp.Key;
|
||||
var param = kvp.Value;
|
||||
if (!knownParams.Contains(name))
|
||||
|
||||
if (param.TypeConflict)
|
||||
{
|
||||
parameters.Add(param);
|
||||
var t1 = param.ConflictingSyncTypes.First();
|
||||
var t2 = param.ConflictingSyncTypes.Skip(1).First();
|
||||
|
||||
List<object> paramList = new List<object> { name, t1, t2 };
|
||||
paramList.AddRange(param.TypeSources.Cast<object>());
|
||||
|
||||
BuildReport.Log(ErrorSeverity.Error, "error.rename_params.type_conflict", paramList.ToArray());
|
||||
}
|
||||
|
||||
if (param.DefaultValueConflict)
|
||||
{
|
||||
var v1 = param.ConflictingValues.First();
|
||||
var v2 = param.ConflictingValues.Skip(1).First();
|
||||
|
||||
List<object> paramList = new List<object> { name, v1, v2 };
|
||||
paramList.AddRange(param.DefaultSources.Cast<object>());
|
||||
|
||||
BuildReport.Log(ErrorSeverity.NonFatal, "error.rename_params.default_value_conflict", paramList.ToArray());
|
||||
}
|
||||
|
||||
if (!knownParams.Contains(name) && param.ResolvedParameter.syncType != ParameterSyncType.NotSynced)
|
||||
{
|
||||
var converted = new VRCExpressionParameters.Parameter();
|
||||
converted.name = name;
|
||||
switch (param.ResolvedParameter.syncType)
|
||||
{
|
||||
case ParameterSyncType.Bool:
|
||||
converted.valueType = VRCExpressionParameters.ValueType.Bool;
|
||||
break;
|
||||
case ParameterSyncType.Float:
|
||||
converted.valueType = VRCExpressionParameters.ValueType.Float;
|
||||
break;
|
||||
case ParameterSyncType.Int:
|
||||
converted.valueType = VRCExpressionParameters.ValueType.Int;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unknown parameter sync type " +
|
||||
param.ResolvedParameter.syncType);
|
||||
}
|
||||
converted.networkSynced = !param.ResolvedParameter.localOnly;
|
||||
converted.saved = param.ResolvedParameter.saved;
|
||||
converted.defaultValue = param.ResolvedParameter.defaultValue;
|
||||
|
||||
parameters.Add(converted);
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,16 +209,55 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
avatar.expressionParameters = expParams;
|
||||
}
|
||||
|
||||
private void WalkTree(
|
||||
private VRCExpressionParameters.Parameter ResolveParameter(
|
||||
VRCExpressionParameters.Parameter parameter,
|
||||
ImmutableDictionary<string, ParameterInfo> syncParams
|
||||
)
|
||||
{
|
||||
if (!syncParams.TryGetValue(parameter.name, out var info))
|
||||
{
|
||||
return parameter;
|
||||
}
|
||||
|
||||
if (parameter.valueType != info.ValueType && info.ValueType != null)
|
||||
{
|
||||
var list = new List<object>
|
||||
{
|
||||
parameter.name,
|
||||
parameter.valueType,
|
||||
info.ValueType,
|
||||
_context.AvatarDescriptor.expressionParameters,
|
||||
};
|
||||
list.AddRange(info.TypeSources);
|
||||
|
||||
BuildReport.Log(ErrorSeverity.Error, "error.rename_params.type_conflict",
|
||||
parameter.name,
|
||||
list
|
||||
);
|
||||
}
|
||||
|
||||
var newParameter = new VRCExpressionParameters.Parameter();
|
||||
newParameter.defaultValue = info.ResolvedParameter.HasDefaultValue ? info.ResolvedParameter.defaultValue : parameter.defaultValue;
|
||||
newParameter.name = parameter.name;
|
||||
newParameter.valueType = parameter.valueType;
|
||||
newParameter.networkSynced = parameter.networkSynced;
|
||||
newParameter.saved = parameter.saved;
|
||||
|
||||
return newParameter;
|
||||
}
|
||||
|
||||
private ImmutableDictionary<string, ParameterInfo> WalkTree(
|
||||
GameObject obj,
|
||||
ImmutableDictionary<string, string> remaps,
|
||||
ImmutableDictionary<string, string> prefixRemaps
|
||||
)
|
||||
{
|
||||
ImmutableDictionary<string, ParameterInfo> rv = ImmutableDictionary<string, ParameterInfo>.Empty;
|
||||
|
||||
var p = obj.GetComponent<ModularAvatarParameters>();
|
||||
if (p != null)
|
||||
{
|
||||
BuildReport.ReportingObject(p, () => ApplyRemappings(p, ref remaps, ref prefixRemaps));
|
||||
rv = BuildReport.ReportingObject(p, () => ApplyRemappings(p, ref remaps, ref prefixRemaps));
|
||||
}
|
||||
|
||||
var willPurgeAnimators = false;
|
||||
@ -217,10 +379,45 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
});
|
||||
}
|
||||
|
||||
var mergedChildParams = ImmutableDictionary<string, ParameterInfo>.Empty;
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
WalkTree(child.gameObject, remaps, prefixRemaps);
|
||||
var childParams = WalkTree(child.gameObject, remaps, prefixRemaps);
|
||||
|
||||
foreach (var kvp in childParams)
|
||||
{
|
||||
var name = kvp.Key;
|
||||
var info = kvp.Value;
|
||||
if (mergedChildParams.TryGetValue(name, out var priorInfo))
|
||||
{
|
||||
priorInfo.MergeSibling(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
mergedChildParams = mergedChildParams.SetItem(name, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kvp in mergedChildParams)
|
||||
{
|
||||
var name = kvp.Key;
|
||||
var info = kvp.Value;
|
||||
|
||||
var remappedName = remap(remaps, name);
|
||||
info.ResolvedParameter.nameOrPrefix = remappedName;
|
||||
|
||||
if (rv.TryGetValue(remappedName, out var priorInfo))
|
||||
{
|
||||
priorInfo.MergeChild(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = rv.SetItem(remappedName, info);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void ProcessMenuInstaller(ModularAvatarMenuInstaller installer,
|
||||
@ -401,11 +598,13 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
t.conditions = conditions;
|
||||
}
|
||||
|
||||
private void ApplyRemappings(ModularAvatarParameters p,
|
||||
private ImmutableDictionary<string, ParameterInfo> ApplyRemappings(ModularAvatarParameters p,
|
||||
ref ImmutableDictionary<string, string> remaps,
|
||||
ref ImmutableDictionary<string, string> prefixRemaps
|
||||
)
|
||||
{
|
||||
ImmutableDictionary<string, ParameterInfo> rv = ImmutableDictionary<string, ParameterInfo>.Empty;
|
||||
|
||||
foreach (var param in p.parameters)
|
||||
{
|
||||
bool doRemap = true;
|
||||
@ -451,41 +650,34 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
if (!param.isPrefix && param.syncType != ParameterSyncType.NotSynced)
|
||||
{
|
||||
AddSyncParam(param, remapTo);
|
||||
if (rv.ContainsKey(remapTo))
|
||||
{
|
||||
BuildReport.Log(ErrorSeverity.NonFatal, "error.rename_params.duplicate_parameter", param.nameOrPrefix);
|
||||
}
|
||||
|
||||
ParameterConfig parameterConfig = param;
|
||||
parameterConfig.nameOrPrefix = remapTo;
|
||||
parameterConfig.remapTo = null;
|
||||
var info = new ParameterInfo()
|
||||
{
|
||||
ResolvedParameter = parameterConfig,
|
||||
};
|
||||
|
||||
if (parameterConfig.syncType != ParameterSyncType.NotSynced)
|
||||
{
|
||||
info.TypeSources.Add(p);
|
||||
}
|
||||
|
||||
if (parameterConfig.HasDefaultValue)
|
||||
{
|
||||
info.DefaultSources.Add(p);
|
||||
}
|
||||
|
||||
rv = rv.SetItem(remapTo, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSyncParam(
|
||||
ParameterConfig parameterConfig,
|
||||
string remapTo
|
||||
)
|
||||
{
|
||||
if (_syncedParams.ContainsKey(remapTo)) return;
|
||||
|
||||
VRCExpressionParameters.ValueType type;
|
||||
switch (parameterConfig.syncType)
|
||||
{
|
||||
case ParameterSyncType.Bool:
|
||||
type = VRCExpressionParameters.ValueType.Bool;
|
||||
break;
|
||||
case ParameterSyncType.Float:
|
||||
type = VRCExpressionParameters.ValueType.Float;
|
||||
break;
|
||||
case ParameterSyncType.Int:
|
||||
type = VRCExpressionParameters.ValueType.Int;
|
||||
break;
|
||||
default: throw new Exception("Unknown sync type " + parameterConfig.syncType);
|
||||
}
|
||||
|
||||
_syncedParams[remapTo] = new VRCExpressionParameters.Parameter
|
||||
{
|
||||
name = remapTo,
|
||||
valueType = type,
|
||||
defaultValue = parameterConfig.defaultValue,
|
||||
saved = parameterConfig.saved,
|
||||
networkSynced = !parameterConfig.localOnly
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
|
||||
// This is generic to simplify remapping parameter driver fields, some of which are 'object's.
|
||||
|
@ -1,19 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core
|
||||
{
|
||||
public struct ParameterInitialValue
|
||||
{
|
||||
public bool HasExplicitDefaultSet;
|
||||
public float Value;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct ParameterConfig
|
||||
{
|
||||
internal const float VALUE_EPSILON = 0.000001f;
|
||||
|
||||
public string nameOrPrefix;
|
||||
public string remapTo;
|
||||
public bool internalParameter, isPrefix;
|
||||
public ParameterSyncType syncType;
|
||||
public bool localOnly;
|
||||
|
||||
public float defaultValue;
|
||||
public bool saved;
|
||||
|
||||
public bool hasExplicitDefaultValue;
|
||||
|
||||
public bool HasDefaultValue => hasExplicitDefaultValue || Mathf.Abs(defaultValue) > VALUE_EPSILON;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,18 +26,19 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3825275463613500755}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0.023681391, y: 1.0559628, z: -0.6872994}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 3646968714803193661}
|
||||
- {fileID: 3646968713996568948}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!95 &3825275463613500750
|
||||
Animator:
|
||||
serializedVersion: 3
|
||||
serializedVersion: 5
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
@ -50,10 +51,12 @@ Animator:
|
||||
m_UpdateMode: 0
|
||||
m_ApplyRootMotion: 0
|
||||
m_LinearVelocityBlending: 0
|
||||
m_StabilizeFeet: 0
|
||||
m_WarningMessage:
|
||||
m_HasTransformHierarchy: 1
|
||||
m_AllowConstantClipSamplingOptimization: 1
|
||||
m_KeepAnimatorControllerStateOnDisable: 0
|
||||
m_KeepAnimatorStateOnDisable: 0
|
||||
m_WriteDefaultValuesOnDisable: 0
|
||||
--- !u!114 &3825275463613500753
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -322,40 +325,12 @@ MonoBehaviour:
|
||||
contentType: 0
|
||||
assetBundleUnityVersion:
|
||||
fallbackStatus: 0
|
||||
--- !u!114 &3825275463971368602
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4167925416990528462}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 6fd7cab7d93b403280f2f9da978d8a4f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Bindings:
|
||||
- ReferenceMesh:
|
||||
referencePath: BaseMesh
|
||||
Blendshape: shape_0
|
||||
LocalBlendshape: shape_0_local
|
||||
- ReferenceMesh:
|
||||
referencePath: BaseMesh
|
||||
Blendshape: shape_1
|
||||
LocalBlendshape: shape_1
|
||||
- ReferenceMesh:
|
||||
referencePath: MissingMesh
|
||||
Blendshape: missing_mesh_shape
|
||||
LocalBlendshape: missing_mesh_shape
|
||||
- ReferenceMesh:
|
||||
referencePath:
|
||||
Blendshape: missing_mesh_shape_2
|
||||
LocalBlendshape: missing_mesh_shape_2
|
||||
--- !u!1001 &3825275463173128406
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 3825275463613500751}
|
||||
m_Modifications:
|
||||
- target: {fileID: -8679921383154817045, guid: 14ac2ad30c5d3444ca37f76cea5a7047,
|
||||
@ -454,6 +429,9 @@ PrefabInstance:
|
||||
value: BaseMesh
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 14ac2ad30c5d3444ca37f76cea5a7047, type: 3}
|
||||
--- !u!4 &3646968714803193661 stripped
|
||||
Transform:
|
||||
@ -466,6 +444,7 @@ PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 3825275463613500751}
|
||||
m_Modifications:
|
||||
- target: {fileID: -8679921383154817045, guid: 14ac2ad30c5d3444ca37f76cea5a7047,
|
||||
@ -569,16 +548,52 @@ PrefabInstance:
|
||||
value: SyncedMesh
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 14ac2ad30c5d3444ca37f76cea5a7047,
|
||||
type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 3825275463971368602}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 14ac2ad30c5d3444ca37f76cea5a7047, type: 3}
|
||||
--- !u!1 &4167925416990528462 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: 14ac2ad30c5d3444ca37f76cea5a7047,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 3825275463971368607}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!4 &3646968713996568948 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: 14ac2ad30c5d3444ca37f76cea5a7047,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 3825275463971368607}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &4167925416990528462 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: 14ac2ad30c5d3444ca37f76cea5a7047,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 3825275463971368607}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &3825275463971368602
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4167925416990528462}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 6fd7cab7d93b403280f2f9da978d8a4f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Bindings:
|
||||
- ReferenceMesh:
|
||||
referencePath: BaseMesh
|
||||
Blendshape: shape_0
|
||||
LocalBlendshape: shape_0_local
|
||||
- ReferenceMesh:
|
||||
referencePath: BaseMesh
|
||||
Blendshape: shape_1
|
||||
LocalBlendshape: shape_1
|
||||
- ReferenceMesh:
|
||||
referencePath: MissingMesh
|
||||
Blendshape: missing_mesh_shape
|
||||
LocalBlendshape: missing_mesh_shape
|
||||
- ReferenceMesh:
|
||||
referencePath:
|
||||
Blendshape: missing_mesh_shape_2
|
||||
LocalBlendshape: missing_mesh_shape_2
|
||||
|
15
UnitTests~/RenameParametersTests/EmptyMenu.asset
Normal file
15
UnitTests~/RenameParametersTests/EmptyMenu.asset
Normal file
@ -0,0 +1,15 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: -340790334, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
|
||||
m_Name: EmptyMenu
|
||||
m_EditorClassIdentifier:
|
||||
controls: []
|
8
UnitTests~/RenameParametersTests/EmptyMenu.asset.meta
Normal file
8
UnitTests~/RenameParametersTests/EmptyMenu.asset.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a595189001d956b429e278804db4f4b0
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,20 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: -1506855854, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
|
||||
m_Name: ParameterConflictTestParams
|
||||
m_EditorClassIdentifier:
|
||||
parameters:
|
||||
- name: a
|
||||
valueType: 1
|
||||
saved: 0
|
||||
defaultValue: 0.5
|
||||
networkSynced: 0
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f54979b614e8b049b6421109a0274c1
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1221
UnitTests~/RenameParametersTests/ParameterConflicts.prefab
Normal file
1221
UnitTests~/RenameParametersTests/ParameterConflicts.prefab
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9fc7cdd605ee9b44fb105a03cdb353c8
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
549
UnitTests~/RenameParametersTests/ParameterResolution.prefab
Normal file
549
UnitTests~/RenameParametersTests/ParameterResolution.prefab
Normal file
@ -0,0 +1,549 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &637676532601936319
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1045454656529665000}
|
||||
- component: {fileID: 6087609344157592032}
|
||||
m_Layer: 0
|
||||
m_Name: ParentWins
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1045454656529665000
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 637676532601936319}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 4417116879698787718}
|
||||
- {fileID: 3783466460454899559}
|
||||
m_Father: {fileID: 9027350264122327824}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &6087609344157592032
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 637676532601936319}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 71a96d4ea0c344f39e277d82035bf9bd, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
migrationCompleted: 0
|
||||
parameters:
|
||||
- nameOrPrefix: a
|
||||
remapTo:
|
||||
internalParameter: 0
|
||||
isPrefix: 0
|
||||
syncType: 2
|
||||
localOnly: 0
|
||||
defaultValue: 0
|
||||
saved: 0
|
||||
hasExplicitDefaultValue: 0
|
||||
--- !u!1 &825679903131863735
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 9027350264122327824}
|
||||
- component: {fileID: 6657619261121604472}
|
||||
- component: {fileID: 381744585999711191}
|
||||
- component: {fileID: 6018907921863585214}
|
||||
m_Layer: 0
|
||||
m_Name: ParameterResolution
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &9027350264122327824
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 825679903131863735}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -0.19918329, y: 0.8600853, z: -0.6144587}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1045454656529665000}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &6657619261121604472
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 825679903131863735}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Name:
|
||||
ViewPosition: {x: 0, y: 1.6, z: 0.2}
|
||||
Animations: 0
|
||||
ScaleIPD: 1
|
||||
lipSync: 0
|
||||
lipSyncJawBone: {fileID: 0}
|
||||
lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1}
|
||||
lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1}
|
||||
VisemeSkinnedMesh: {fileID: 0}
|
||||
MouthOpenBlendShapeName: Facial_Blends.Jaw_Down
|
||||
VisemeBlendShapes: []
|
||||
unityVersion:
|
||||
portraitCameraPositionOffset: {x: 0, y: 0, z: 0}
|
||||
portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139}
|
||||
networkIDs: []
|
||||
customExpressions: 1
|
||||
expressionsMenu: {fileID: 11400000, guid: a595189001d956b429e278804db4f4b0, type: 2}
|
||||
expressionParameters: {fileID: 11400000, guid: 2f54979b614e8b049b6421109a0274c1,
|
||||
type: 2}
|
||||
enableEyeLook: 0
|
||||
customEyeLookSettings:
|
||||
eyeMovement:
|
||||
confidence: 0.5
|
||||
excitement: 0.5
|
||||
leftEye: {fileID: 0}
|
||||
rightEye: {fileID: 0}
|
||||
eyesLookingStraight:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyesLookingUp:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyesLookingDown:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyesLookingLeft:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyesLookingRight:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyelidType: 0
|
||||
upperLeftEyelid: {fileID: 0}
|
||||
upperRightEyelid: {fileID: 0}
|
||||
lowerLeftEyelid: {fileID: 0}
|
||||
lowerRightEyelid: {fileID: 0}
|
||||
eyelidsDefault:
|
||||
upper:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
lower:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyelidsClosed:
|
||||
upper:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
lower:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyelidsLookingUp:
|
||||
upper:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
lower:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyelidsLookingDown:
|
||||
upper:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
lower:
|
||||
linked: 1
|
||||
left: {x: 0, y: 0, z: 0, w: 0}
|
||||
right: {x: 0, y: 0, z: 0, w: 0}
|
||||
eyelidsSkinnedMesh: {fileID: 0}
|
||||
eyelidsBlendshapes:
|
||||
customizeAnimationLayers: 0
|
||||
baseAnimationLayers:
|
||||
- isEnabled: 0
|
||||
type: 0
|
||||
animatorController: {fileID: 0}
|
||||
mask: {fileID: 0}
|
||||
isDefault: 1
|
||||
- isEnabled: 0
|
||||
type: 4
|
||||
animatorController: {fileID: 0}
|
||||
mask: {fileID: 0}
|
||||
isDefault: 1
|
||||
- isEnabled: 0
|
||||
type: 5
|
||||
animatorController: {fileID: 0}
|
||||
mask: {fileID: 0}
|
||||
isDefault: 1
|
||||
specialAnimationLayers:
|
||||
- isEnabled: 0
|
||||
type: 6
|
||||
animatorController: {fileID: 0}
|
||||
mask: {fileID: 0}
|
||||
isDefault: 1
|
||||
- isEnabled: 0
|
||||
type: 7
|
||||
animatorController: {fileID: 0}
|
||||
mask: {fileID: 0}
|
||||
isDefault: 1
|
||||
- isEnabled: 0
|
||||
type: 8
|
||||
animatorController: {fileID: 0}
|
||||
mask: {fileID: 0}
|
||||
isDefault: 1
|
||||
AnimationPreset: {fileID: 0}
|
||||
animationHashSet: []
|
||||
autoFootsteps: 1
|
||||
autoLocomotion: 1
|
||||
collider_head:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_torso:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_footR:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_footL:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_handR:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_handL:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_fingerIndexL:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_fingerMiddleL:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_fingerRingL:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_fingerLittleL:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_fingerIndexR:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_fingerMiddleR:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_fingerRingR:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
collider_fingerLittleR:
|
||||
isMirrored: 1
|
||||
state: 0
|
||||
transform: {fileID: 0}
|
||||
radius: 0
|
||||
height: 0
|
||||
position: {x: 0, y: 0, z: 0}
|
||||
rotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
--- !u!114 &381744585999711191
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 825679903131863735}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
launchedFromSDKPipeline: 0
|
||||
completedSDKPipeline: 0
|
||||
blueprintId:
|
||||
contentType: 0
|
||||
assetBundleUnityVersion:
|
||||
fallbackStatus: 0
|
||||
--- !u!95 &6018907921863585214
|
||||
Animator:
|
||||
serializedVersion: 5
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 825679903131863735}
|
||||
m_Enabled: 1
|
||||
m_Avatar: {fileID: 0}
|
||||
m_Controller: {fileID: 0}
|
||||
m_CullingMode: 0
|
||||
m_UpdateMode: 0
|
||||
m_ApplyRootMotion: 0
|
||||
m_LinearVelocityBlending: 0
|
||||
m_StabilizeFeet: 0
|
||||
m_WarningMessage:
|
||||
m_HasTransformHierarchy: 1
|
||||
m_AllowConstantClipSamplingOptimization: 1
|
||||
m_KeepAnimatorStateOnDisable: 0
|
||||
m_WriteDefaultValuesOnDisable: 0
|
||||
--- !u!1 &2058320306015137456
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6619961460191419643}
|
||||
- component: {fileID: 2706654488088963333}
|
||||
m_Layer: 0
|
||||
m_Name: P (1)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &6619961460191419643
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2058320306015137456}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4417116879698787718}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &2706654488088963333
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2058320306015137456}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 71a96d4ea0c344f39e277d82035bf9bd, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
migrationCompleted: 0
|
||||
parameters:
|
||||
- nameOrPrefix: a
|
||||
remapTo:
|
||||
internalParameter: 0
|
||||
isPrefix: 0
|
||||
syncType: 2
|
||||
localOnly: 0
|
||||
defaultValue: 1
|
||||
saved: 0
|
||||
hasExplicitDefaultValue: 1
|
||||
--- !u!1 &3544070647160969251
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3783466460454899559}
|
||||
- component: {fileID: 8244320322899509705}
|
||||
m_Layer: 0
|
||||
m_Name: P (2)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &3783466460454899559
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3544070647160969251}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1045454656529665000}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &8244320322899509705
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3544070647160969251}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 71a96d4ea0c344f39e277d82035bf9bd, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
migrationCompleted: 0
|
||||
parameters:
|
||||
- nameOrPrefix: a
|
||||
remapTo:
|
||||
internalParameter: 0
|
||||
isPrefix: 0
|
||||
syncType: 2
|
||||
localOnly: 0
|
||||
defaultValue: 0
|
||||
saved: 0
|
||||
hasExplicitDefaultValue: 0
|
||||
--- !u!1 &8528232542967234232
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4417116879698787718}
|
||||
- component: {fileID: 1576133641690687503}
|
||||
m_Layer: 0
|
||||
m_Name: P
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4417116879698787718
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8528232542967234232}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 6619961460191419643}
|
||||
m_Father: {fileID: 1045454656529665000}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1576133641690687503
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8528232542967234232}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 71a96d4ea0c344f39e277d82035bf9bd, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
migrationCompleted: 0
|
||||
parameters:
|
||||
- nameOrPrefix: a
|
||||
remapTo:
|
||||
internalParameter: 0
|
||||
isPrefix: 0
|
||||
syncType: 2
|
||||
localOnly: 0
|
||||
defaultValue: 0.1
|
||||
saved: 0
|
||||
hasExplicitDefaultValue: 1
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df145d36703065a40927386e65d5f1bf
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,10 +1,14 @@
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.core;
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using nadena.dev.ndmf;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
using VRC.SDK3.Avatars.ScriptableObjects;
|
||||
using AvatarProcessor = nadena.dev.modular_avatar.core.editor.AvatarProcessor;
|
||||
|
||||
namespace modular_avatar_tests.RenameParametersTests
|
||||
{
|
||||
@ -62,6 +66,60 @@ namespace modular_avatar_tests.RenameParametersTests
|
||||
|
||||
AvatarProcessor.ProcessAvatar(avatar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestParameterConflicts()
|
||||
{
|
||||
var prefab = CreatePrefab("ParameterConflicts.prefab");
|
||||
|
||||
var context = CreateContext(prefab);
|
||||
var maContext = context.ActivateExtensionContext<ModularAvatarContext>().BuildContext;
|
||||
|
||||
var errors = ErrorReport.CaptureErrors(
|
||||
() =>
|
||||
{
|
||||
using (new ObjectRegistryScope(new ObjectRegistry(prefab.transform)))
|
||||
{
|
||||
new RenameParametersHook().OnPreprocessAvatar(prefab, maContext);
|
||||
}
|
||||
});
|
||||
|
||||
var valueConflict = errors
|
||||
.Select(e => e.TheError)
|
||||
.Cast<SimpleError>()
|
||||
.First(e => e.TitleKey == "error.rename_params.default_value_conflict");
|
||||
Assert.AreEqual("a$$Internal_1", valueConflict.DetailsSubst[0]);
|
||||
Assert.AreEqual("0", valueConflict.DetailsSubst[1]);
|
||||
Assert.AreEqual("1", valueConflict.DetailsSubst[2]);
|
||||
|
||||
Assert.AreEqual("Conflict/P", valueConflict.References[0].Path);
|
||||
Assert.AreEqual("Conflict/P (1)", valueConflict.References[1].Path);
|
||||
|
||||
var typeConflict = errors
|
||||
.Select(e => e.TheError)
|
||||
.Cast<SimpleError>()
|
||||
.First(e => e.TitleKey == "error.rename_params.type_conflict");
|
||||
|
||||
Assert.AreEqual("a$$Internal_2", typeConflict.DetailsSubst[0]);
|
||||
Assert.AreEqual("Int", typeConflict.DetailsSubst[1]);
|
||||
Assert.AreEqual("Float", typeConflict.DetailsSubst[2]);
|
||||
|
||||
Assert.AreEqual("TypeConflict/P", typeConflict.References[0].Path);
|
||||
Assert.AreEqual("TypeConflict/P (2)", typeConflict.References[1].Path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestParameterResults()
|
||||
{
|
||||
var prefab = CreatePrefab("ParameterResolution.prefab");
|
||||
|
||||
AvatarProcessor.ProcessAvatar(prefab);
|
||||
|
||||
var expParams = prefab.GetComponent<VRCAvatarDescriptor>().expressionParameters;
|
||||
|
||||
Assert.AreEqual(expParams.parameters[0].name, "a");
|
||||
Assert.IsTrue(Mathf.Abs(expParams.parameters[0].defaultValue - 0.1f) < 0.0001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,6 @@
|
||||
},
|
||||
"vpmDependencies": {
|
||||
"com.vrchat.avatars": ">=3.2.0",
|
||||
"nadena.dev.ndmf": ">=1.3.0-rc.2 <2.0.0-a"
|
||||
"nadena.dev.ndmf": ">=1.3.0-rc.6 <2.0.0-a"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user