Merge branch 'main' into enhance_EasySetupOutfit_humanoid

This commit is contained in:
Sayamame-beans 2024-10-17 19:20:24 +09:00 committed by GitHub
commit 73d4f77840
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
118 changed files with 5411 additions and 763 deletions

View File

@ -19,7 +19,7 @@
"dependencies": {}
},
"nadena.dev.ndmf": {
"version": "1.5.0-rc.4"
"version": "1.5.3"
}
}
}

View File

@ -122,7 +122,7 @@ jobs:
workingDirectory: docs-site~
- name: Purge cache
uses: nathanvaughn/actions-cloudflare-purge@aa1121a867565ea71b60f445f441544df0c7b0b9
uses: nathanvaughn/actions-cloudflare-purge@992cc4e96422fb8ddf077281678373fe41e7736c
continue-on-error: true
with:
cf_zone: ${{ secrets.CF_ZONE_ID }}

View File

@ -116,6 +116,7 @@ jobs:
with:
repos: |
https://vpm.nadena.dev/vpm-prerelease.json
https://vrchat.github.io/packages/index.json?download
- if: ${{ steps.setup.outputs.should_test == 'true' }}
name: "Debug: List project contents"

View File

@ -9,6 +9,7 @@ using nadena.dev.ndmf;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using UnityEngine.Profiling;
using BuildContext = nadena.dev.ndmf.BuildContext;
#if MA_VRCSDK3_AVATARS
using VRC.SDK3.Avatars.Components;
@ -52,7 +53,21 @@ namespace nadena.dev.modular_avatar.animation
set
{
_originalClip = value;
IsProxyAnimation = value != null && Util.IsProxyAnimation(value);
var baseClip = ObjectRegistry.GetReference(value)?.Object as AnimationClip;
IsProxyAnimation = false;
if (value != null && Util.IsProxyAnimation(value))
{
IsProxyAnimation = true;
}
else if (baseClip != null && Util.IsProxyAnimation(baseClip))
{
// RenameParametersPass replaces proxy clips outside of the purview of the animation database,
// so trace this using ObjectRegistry and correct the reference.
IsProxyAnimation = true;
_originalClip = baseClip;
}
}
}
@ -97,11 +112,13 @@ namespace nadena.dev.modular_avatar.animation
internal void Commit()
{
Profiler.BeginSample("AnimationDatabase.Commit");
foreach (var clip in _clips)
{
if (clip.IsProxyAnimation) clip.CurrentClip = clip.OriginalClip;
}
Profiler.BeginSample("UpdateClipProperties");
foreach (var clip in _clips)
{
// Changing the "high quality curve" setting can result in behavior changes (but can happen accidentally
@ -121,11 +138,16 @@ namespace nadena.dev.modular_avatar.animation
}
}
}
Profiler.EndSample();
Profiler.BeginSample("ClipCommitActions");
foreach (var action in _clipCommitActions)
{
action();
}
Profiler.EndSample();
Profiler.EndSample();
}
internal void OnActivate(BuildContext context)
@ -192,7 +214,11 @@ namespace nadena.dev.modular_avatar.animation
var clipHolder = RegisterMotion(state.motion, state, processClip, _originalToHolder);
state.motion = clipHolder.CurrentClip;
_clipCommitActions.Add(() => { state.motion = clipHolder.CurrentClip; });
_clipCommitActions.Add(() =>
{
state.motion = clipHolder.CurrentClip;
MaybeSaveClip(clipHolder.CurrentClip);
});
}
internal void ForeachClip(Action<ClipHolder> processClip)
@ -368,6 +394,8 @@ namespace nadena.dev.modular_avatar.animation
children[i].motion = curClip;
dirty = true;
}
MaybeSaveClip(curClip);
}
if (dirty)
@ -379,5 +407,23 @@ namespace nadena.dev.modular_avatar.animation
return treeHolder;
}
private void MaybeSaveClip(Motion curClip)
{
Profiler.BeginSample("MaybeSaveClip");
if (curClip != null && !EditorUtility.IsPersistent(curClip) && EditorUtility.IsPersistent(_context.AssetContainer) && _context.AssetContainer != null)
{
try
{
AssetDatabase.AddObjectToAsset(curClip, _context.AssetContainer);
}
catch (Exception e)
{
Debug.LogException(e);
throw;
}
}
Profiler.EndSample();
}
}
}

View File

@ -42,7 +42,9 @@ namespace nadena.dev.modular_avatar.animation
throw new Exception("Unknown RuntimeAnimatorContoller type " + controller.GetType());
}
return merger.Finish();
var clone = merger.Finish();
ObjectRegistry.RegisterReplacedObject(controller, clone);
return clone;
}
internal static void CloneAllControllers(BuildContext context)

View File

@ -573,6 +573,8 @@ namespace nadena.dev.modular_avatar.animation
private AnimatorStateMachine mapStateMachine(string basePath, AnimatorStateMachine layerStateMachine)
{
if (layerStateMachine == null) return null;
var cacheKey = new KeyValuePair<string, AnimatorStateMachine>(basePath, layerStateMachine);
if (_stateMachines.TryGetValue(cacheKey, out var asm))
@ -611,7 +613,7 @@ namespace nadena.dev.modular_avatar.animation
{
if (!string.IsNullOrEmpty(playAudio.SourcePath) && !string.IsNullOrEmpty(basePath) && !playAudio.SourcePath.StartsWith(basePath))
{
playAudio.SourcePath = $"{basePath}/{playAudio.SourcePath}";
playAudio.SourcePath = $"{basePath}{playAudio.SourcePath}";
}
break;
}

View File

@ -93,6 +93,11 @@ namespace nadena.dev.modular_avatar.animation
{
ObjectRegistry.RegisterReplacedObject(original, obj);
}
if (_isSaved && !EditorUtility.IsPersistent(obj))
{
AssetDatabase.AddObjectToAsset(obj, _combined);
}
return (T)obj;
}

View File

@ -8,6 +8,7 @@ using nadena.dev.ndmf.util;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using UnityEngine.Profiling;
#if MA_VRCSDK3_AVATARS_3_5_2_OR_NEWER
#endif
@ -337,8 +338,10 @@ namespace nadena.dev.modular_avatar.animation
internal void OnDeactivate(BuildContext context)
{
Profiler.BeginSample("PathMappings.OnDeactivate");
Dictionary<AnimationClip, AnimationClip> clipCache = new Dictionary<AnimationClip, AnimationClip>();
Profiler.BeginSample("ApplyMappingsToClip");
_animationDatabase.ForeachClip(holder =>
{
if (holder.CurrentClip is AnimationClip clip)
@ -346,23 +349,29 @@ namespace nadena.dev.modular_avatar.animation
holder.CurrentClip = ApplyMappingsToClip(clip, clipCache);
}
});
Profiler.EndSample();
#if MA_VRCSDK3_AVATARS_3_5_2_OR_NEWER
Profiler.BeginSample("MapPlayAudio");
_animationDatabase.ForeachPlayAudio(playAudio =>
{
if (playAudio == null) return;
playAudio.SourcePath = MapPath(playAudio.SourcePath, true);
});
Profiler.EndSample();
#endif
Profiler.BeginSample("InvokeIOnCommitObjectRenamesCallbacks");
foreach (var listener in context.AvatarRootObject.GetComponentsInChildren<IOnCommitObjectRenames>())
{
listener.OnCommitObjectRenames(context, this);
}
Profiler.EndSample();
var layers = context.AvatarDescriptor.baseAnimationLayers
.Concat(context.AvatarDescriptor.specialAnimationLayers);
Profiler.BeginSample("ApplyMappingsToAvatarMasks");
foreach (var layer in layers)
{
ApplyMappingsToAvatarMask(layer.mask);
@ -373,6 +382,9 @@ namespace nadena.dev.modular_avatar.animation
foreach (var acLayer in ac.layers)
ApplyMappingsToAvatarMask(acLayer.avatarMask);
}
Profiler.EndSample();
Profiler.EndSample();
}
public GameObject PathToObject(string path)

View File

@ -40,7 +40,7 @@ namespace nadena.dev.modular_avatar.core.editor
switch (parameters[i].type)
{
case AnimatorControllerParameterType.Bool:
parameters[i].defaultBool = defaultValue > 0.5f;
parameters[i].defaultBool = defaultValue != 0.0f;
break;
case AnimatorControllerParameterType.Int:
parameters[i].defaultInt = Mathf.RoundToInt(defaultValue);

View File

@ -11,6 +11,8 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
{
internal class PatchLoader
{
private const string HarmonyId = "nadena.dev.modular_avatar";
private static readonly Action<Harmony>[] patches = new Action<Harmony>[]
{
//HierarchyViewPatches.Patch,
@ -19,7 +21,7 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
[InitializeOnLoadMethod]
static void ApplyPatches()
{
var harmony = new Harmony("nadena.dev.modular_avatar");
var harmony = new Harmony(HarmonyId);
foreach (var patch in patches)
{
@ -33,7 +35,7 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
}
}
AssemblyReloadEvents.beforeAssemblyReload += () => { harmony.UnpatchAll(); };
AssemblyReloadEvents.beforeAssemblyReload += () => { harmony.UnpatchAll(HarmonyId); };
}
}
}

View File

@ -46,7 +46,7 @@ namespace nadena.dev.modular_avatar.core.editor
new[] {"LeftFoot", "Foot_Left", "Foot_L", "Ankle_L", "Foot.L.001", "Left ankle", "heel.L", "heel"},
new[] {"RightFoot", "Foot_Right", "Foot_R", "Ankle_R", "Foot.R.001", "Right ankle", "heel.R", "heel"},
new[] {"Spine", "spine01"},
new[] {"Chest", "Bust", "spine02"},
new[] {"Chest", "Bust", "spine02", "upper_chest"},
new[] {"Neck"},
new[] {"Head"},
new[] {"LeftShoulder", "Shoulder_Left", "Shoulder_L"},

View File

@ -95,7 +95,7 @@ namespace nadena.dev.modular_avatar.core.editor
var t = (ModularAvatarBoneProxy) targets[i];
Undo.RecordObjects(targets, "Set targets");
var xform = ((TempObjRef) objRefs[i]).target;
if (RuntimeUtil.FindAvatarTransformInParents(xform)?.gameObject != parentAvatar) continue;
if (xform != null && RuntimeUtil.FindAvatarTransformInParents(xform)?.gameObject != parentAvatar) continue;
t.target = xform;
}
}
@ -159,4 +159,4 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
}
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace nadena.dev.modular_avatar.core.editor
{
internal abstract class DragAndDropManipulator<T> : PointerManipulator where T : Component, IHaveObjReferences
{
private const string DragActiveClassName = "drop-area--drag-active";
public T TargetComponent { get; set; }
protected virtual bool AllowKnownObjects => true;
private Transform _avatarRoot;
private GameObject[] _draggingObjects = Array.Empty<GameObject>();
public DragAndDropManipulator(VisualElement targetElement, T targetComponent)
{
target = targetElement;
TargetComponent = targetComponent;
}
protected sealed override void RegisterCallbacksOnTarget()
{
target.RegisterCallback<DragEnterEvent>(OnDragEnter);
target.RegisterCallback<DragLeaveEvent>(OnDragLeave);
target.RegisterCallback<DragExitedEvent>(OnDragExited);
target.RegisterCallback<DragUpdatedEvent>(OnDragUpdated);
target.RegisterCallback<DragPerformEvent>(OnDragPerform);
}
protected sealed override void UnregisterCallbacksFromTarget()
{
target.UnregisterCallback<DragEnterEvent>(OnDragEnter);
target.UnregisterCallback<DragLeaveEvent>(OnDragLeave);
target.UnregisterCallback<DragExitedEvent>(OnDragExited);
target.UnregisterCallback<DragUpdatedEvent>(OnDragUpdated);
target.UnregisterCallback<DragPerformEvent>(OnDragPerform);
}
private void OnDragEnter(DragEnterEvent _)
{
if (TargetComponent == null) return;
_avatarRoot = RuntimeUtil.FindAvatarTransformInParents(TargetComponent.transform);
if (_avatarRoot == null) return;
var knownObjects = TargetComponent.GetObjectReferences().Select(x => x.Get(TargetComponent)).ToHashSet();
_draggingObjects = DragAndDrop.objectReferences.OfType<GameObject>()
.Where(x => AllowKnownObjects || !knownObjects.Contains(x))
.Where(x => RuntimeUtil.FindAvatarTransformInParents(x.transform) == _avatarRoot)
.Where(FilterGameObject)
.ToArray();
if (_draggingObjects.Length == 0) return;
target.AddToClassList(DragActiveClassName);
}
private void OnDragLeave(DragLeaveEvent _)
{
_draggingObjects = Array.Empty<GameObject>();
target.RemoveFromClassList(DragActiveClassName);
}
private void OnDragExited(DragExitedEvent _)
{
_draggingObjects = Array.Empty<GameObject>();
target.RemoveFromClassList(DragActiveClassName);
}
private void OnDragUpdated(DragUpdatedEvent _)
{
if (TargetComponent == null) return;
if (_avatarRoot == null) return;
if (_draggingObjects.Length == 0) return;
DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
}
private void OnDragPerform(DragPerformEvent _)
{
if (TargetComponent == null) return;
if (_avatarRoot == null) return;
if (_draggingObjects.Length == 0) return;
AddObjectReferences(_draggingObjects
.Select(x =>
{
var reference = new AvatarObjectReference();
reference.Set(x);
return reference;
})
.ToArray());
}
protected virtual bool FilterGameObject(GameObject obj)
{
return true;
}
protected abstract void AddObjectReferences(AvatarObjectReference[] references);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 528c660b56905844ea2f88bc73837e9f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -19,12 +19,6 @@ namespace nadena.dev.modular_avatar.core.editor
{
var target = (ModularAvatarVisibleHeadAccessory) this.target;
#if UNITY_ANDROID
EditorGUILayout.HelpBox(Localization.S("fpvisible.quest"), MessageType.Warning);
#else
if (_validation != null)
{
var status = _validation.Validate(target);
@ -35,6 +29,9 @@ namespace nadena.dev.modular_avatar.core.editor
case VisibleHeadAccessoryValidation.ReadyStatus.ParentMarked:
EditorGUILayout.HelpBox(Localization.S("fpvisible.normal"), MessageType.Info);
break;
case VisibleHeadAccessoryValidation.ReadyStatus.NotUnderHead:
EditorGUILayout.HelpBox(Localization.S("fpvisible.NotUnderHead"), MessageType.Warning);
break;
default:
{
var label = "fpvisible." + status;
@ -44,9 +41,7 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
#endif
Localization.ShowLanguageUI();
}
}
}
}

View File

@ -18,7 +18,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
return (EditorStyles.label?.lineHeight ?? 0) * 3;
}
catch (NullReferenceException e)
catch (NullReferenceException)
{
// This can happen in early initialization...
return 0;

View File

@ -16,10 +16,11 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
[SerializeField] private StyleSheet uss;
[SerializeField] private VisualTreeAsset uxml;
private DragAndDropManipulator _dragAndDropManipulator;
protected override void OnInnerInspectorGUI()
{
throw new NotImplementedException();
EditorGUILayout.HelpBox("Unable to show override changes", MessageType.Info);
}
protected override VisualElement CreateInnerInspectorGUI()
@ -37,7 +38,44 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
listView.showBoundCollectionSize = false;
listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight;
_dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarMaterialSetter);
return root;
}
private void OnEnable()
{
if (_dragAndDropManipulator != null)
_dragAndDropManipulator.TargetComponent = target as ModularAvatarMaterialSetter;
}
private class DragAndDropManipulator : DragAndDropManipulator<ModularAvatarMaterialSetter>
{
public DragAndDropManipulator(VisualElement targetElement, ModularAvatarMaterialSetter targetComponent)
: base(targetElement, targetComponent) { }
protected override bool FilterGameObject(GameObject obj)
{
if (obj.TryGetComponent<Renderer>(out var renderer))
{
return renderer.sharedMaterials.Length > 0;
}
return false;
}
protected override void AddObjectReferences(AvatarObjectReference[] references)
{
Undo.RecordObject(TargetComponent, "Add Material Switch Objects");
foreach (var reference in references)
{
var materialSwitchObject = new MaterialSwitchObject { Object = reference, MaterialIndex = 0 };
TargetComponent.Objects.Add(materialSwitchObject);
}
EditorUtility.SetDirty(TargetComponent);
PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent);
}
}
}
}

View File

@ -62,3 +62,13 @@
#f-material {
flex-grow: 1;
}
.drop-area--drag-active {
background-color: rgba(0, 127, 255, 0.2);
}
.drop-area--drag-active .unity-scroll-view,
.drop-area--drag-active .unity-list-view__footer,
.drop-area--drag-active .unity-list-view__reorderable-item {
background-color: rgba(0, 0, 0, 0.0);
}

View File

@ -3,7 +3,6 @@
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;
#endregion
@ -148,7 +147,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
{
return targetObject?.GetComponent<Renderer>()?.sharedMaterials;
}
catch (MissingComponentException e)
catch (MissingComponentException)
{
return null;
}

View File

@ -2,10 +2,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.Serialization;
using nadena.dev.modular_avatar.core.menu;
using nadena.dev.ndmf;
using nadena.dev.ndmf.preview;
using UnityEditor;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
@ -21,6 +23,40 @@ namespace nadena.dev.modular_avatar.core.editor
protected override string localizationPrefix => "submenu_source";
}
internal static class ParameterIntrospectionCache
{
internal static PropCache<GameObject, ImmutableList<ProvidedParameter>> ProvidedParameterCache =
new("GetParametersForObject", GetParametersForObject_miss);
internal static PropCache<GameObject, ImmutableDictionary<(ParameterNamespace, string), ParameterMapping>>
ParameterRemappingCache = new("GetParameterRemappingsAt", GetParameterRemappingsAt_miss);
private static ImmutableList<ProvidedParameter> GetParametersForObject_miss(ComputeContext ctx, GameObject obj)
{
if (obj == null) return ImmutableList<ProvidedParameter>.Empty;
return ParameterInfo.ForPreview(ctx).GetParametersForObject(obj).ToImmutableList();
}
private static ImmutableDictionary<(ParameterNamespace, string), ParameterMapping>
GetParameterRemappingsAt_miss(ComputeContext ctx, GameObject obj)
{
if (obj == null) return ImmutableDictionary<(ParameterNamespace, string), ParameterMapping>.Empty;
return ParameterInfo.ForPreview(ctx).GetParameterRemappingsAt(obj);
}
internal static ImmutableList<ProvidedParameter> GetParametersForObject(GameObject avatar)
{
return ProvidedParameterCache.Get(ComputeContext.NullContext, avatar);
}
internal static ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> GetParameterRemappingsAt(GameObject avatar)
{
return ParameterRemappingCache.Get(ComputeContext.NullContext, avatar);
}
}
internal class MenuItemCoreGUI
{
private static readonly ObjectIDGenerator IdGenerator = new ObjectIDGenerator();
@ -134,12 +170,12 @@ namespace nadena.dev.modular_avatar.core.editor
Dictionary<string, ProvidedParameter> rootParameters = new();
foreach (var param in ParameterInfo.ForUI.GetParametersForObject(parentAvatar.gameObject)
foreach (var param in ParameterIntrospectionCache.GetParametersForObject(parentAvatar.gameObject)
.Where(p => p.Namespace == ParameterNamespace.Animator)
)
rootParameters[param.EffectiveName] = param;
var remaps = ParameterInfo.ForUI.GetParameterRemappingsAt(paramRef);
var remaps = ParameterIntrospectionCache.GetParameterRemappingsAt(paramRef);
foreach (var remap in remaps)
{
if (remap.Key.Item1 != ParameterNamespace.Animator) continue;
@ -610,14 +646,16 @@ namespace nadena.dev.modular_avatar.core.editor
var myMenuItem = serializedObject.targetObject as ModularAvatarMenuItem;
if (myMenuItem == null) return null;
var avatarRoot = RuntimeUtil.FindAvatarInParents(myMenuItem.gameObject.transform);
if (avatarRoot == null) return null;
var myParameterName = myMenuItem.Control.parameter.name;
if (string.IsNullOrEmpty(myParameterName)) return new List<ModularAvatarMenuItem>();
var myMappings = ParameterInfo.ForUI.GetParameterRemappingsAt(myMenuItem.gameObject);
var myMappings = ParameterIntrospectionCache.GetParameterRemappingsAt(myMenuItem.gameObject);
if (myMappings.TryGetValue((ParameterNamespace.Animator, myParameterName), out var myReplacement))
myParameterName = myReplacement.ParameterName;
var avatarRoot = RuntimeUtil.FindAvatarInParents(myMenuItem.gameObject.transform);
var siblings = new List<ModularAvatarMenuItem>();
foreach (var otherMenuItem in avatarRoot.GetComponentsInChildren<ModularAvatarMenuItem>(true))
@ -627,7 +665,7 @@ namespace nadena.dev.modular_avatar.core.editor
var otherParameterName = otherMenuItem.Control.parameter.name;
if (string.IsNullOrEmpty(otherParameterName)) continue;
var otherMappings = ParameterInfo.ForUI.GetParameterRemappingsAt(otherMenuItem.gameObject);
var otherMappings = ParameterIntrospectionCache.GetParameterRemappingsAt(otherMenuItem.gameObject);
if (otherMappings.TryGetValue((ParameterNamespace.Animator, otherParameterName),
out var otherReplacement))
otherParameterName = otherReplacement.ParameterName;

View File

@ -30,7 +30,7 @@ namespace nadena.dev.modular_avatar.core.editor
createInstaller = false;
}
}
catch (MissingComponentException e)
catch (MissingComponentException)
{
// ignore
}

View File

@ -84,6 +84,7 @@ namespace nadena.dev.modular_avatar.core.editor
}
private bool posResetOptionFoldout = false;
private bool posReset_convertATPose = true;
private bool posReset_adjustRotation = false;
private bool posReset_adjustScale = false;
private bool posReset_heuristicRootScale = true;
@ -154,14 +155,17 @@ namespace nadena.dev.modular_avatar.core.editor
MessageType.Info
);
posReset_heuristicRootScale = EditorGUILayout.ToggleLeft(
G("merge_armature.reset_pos.heuristic_scale"),
posReset_heuristicRootScale);
posReset_convertATPose = EditorGUILayout.ToggleLeft(
G("merge_armature.reset_pos.convert_atpose"),
posReset_convertATPose);
posReset_adjustRotation = EditorGUILayout.ToggleLeft(
G("merge_armature.reset_pos.adjust_rotation"),
posReset_adjustRotation);
posReset_adjustScale = EditorGUILayout.ToggleLeft(G("merge_armature.reset_pos.adjust_scale"),
posReset_adjustScale);
posReset_heuristicRootScale = EditorGUILayout.ToggleLeft(
G("merge_armature.reset_pos.heuristic_scale"),
posReset_heuristicRootScale);
if (GUILayout.Button(G("merge_armature.reset_pos.execute")))
{
@ -208,6 +212,11 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
if (posReset_convertATPose)
{
SetupOutfit.FixAPose(RuntimeUtil.FindAvatarTransformInParents(mergeTarget.transform).gameObject, mama.transform, false);
}
if (posReset_heuristicRootScale && !suppressRootScale)
{
AdjustRootScale();

View File

@ -23,7 +23,7 @@ namespace nadena.dev.modular_avatar.core.editor
protected override void OnInnerInspectorGUI()
{
throw new NotImplementedException();
EditorGUILayout.HelpBox("Unable to show override changes", MessageType.Info);
}
protected override VisualElement CreateInnerInspectorGUI()
@ -61,7 +61,7 @@ namespace nadena.dev.modular_avatar.core.editor
var grouped = ctx.Observe(target,
t => (t.GroupedBones ?? Array.Empty<GameObject>())
.Select(obj => obj.transform)
.ToHashSet(new ObjectIdentityComparer<Transform>()),
.ToHashSet(),
(x, y) => x.SetEquals(y)
);

View File

@ -21,7 +21,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
protected override void OnInnerInspectorGUI()
{
throw new NotImplementedException();
EditorGUILayout.HelpBox("Unable to show override changes", MessageType.Info);
}
protected override VisualElement CreateInnerInspectorGUI()
@ -35,14 +35,12 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
ROSimulatorButton.BindRefObject(root, target);
var listView = root.Q<ListView>("Shapes");
_dragAndDropManipulator = new DragAndDropManipulator(listView)
{
TargetComponent = target as ModularAvatarObjectToggle
};
listView.showBoundCollectionSize = false;
listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight;
_dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarObjectToggle);
return root;
}
@ -52,91 +50,25 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
_dragAndDropManipulator.TargetComponent = target as ModularAvatarObjectToggle;
}
private class DragAndDropManipulator : PointerManipulator
private class DragAndDropManipulator : DragAndDropManipulator<ModularAvatarObjectToggle>
{
public ModularAvatarObjectToggle TargetComponent;
private GameObject[] _nowDragging = Array.Empty<GameObject>();
private Transform _avatarRoot;
public DragAndDropManipulator(VisualElement targetElement, ModularAvatarObjectToggle targetComponent)
: base(targetElement, targetComponent) { }
private readonly VisualElement _parentElem;
protected override bool AllowKnownObjects => false;
public DragAndDropManipulator(VisualElement target)
protected override void AddObjectReferences(AvatarObjectReference[] references)
{
this.target = target;
_parentElem = target.parent;
}
Undo.RecordObject(TargetComponent, "Add Toggled Objects");
protected override void RegisterCallbacksOnTarget()
{
target.RegisterCallback<DragEnterEvent>(OnDragEnter);
target.RegisterCallback<DragLeaveEvent>(OnDragLeave);
target.RegisterCallback<DragPerformEvent>(OnDragPerform);
target.RegisterCallback<DragUpdatedEvent>(OnDragUpdate);
}
protected override void UnregisterCallbacksFromTarget()
{
target.UnregisterCallback<DragEnterEvent>(OnDragEnter);
target.UnregisterCallback<DragLeaveEvent>(OnDragLeave);
target.UnregisterCallback<DragPerformEvent>(OnDragPerform);
target.RegisterCallback<DragUpdatedEvent>(OnDragUpdate);
}
private void OnDragEnter(DragEnterEvent evt)
{
if (TargetComponent == null) return;
_avatarRoot = RuntimeUtil.FindAvatarTransformInParents(TargetComponent.transform);
if (_avatarRoot == null) return;
_nowDragging = DragAndDrop.objectReferences.OfType<GameObject>()
.Where(o => RuntimeUtil.FindAvatarTransformInParents(o.transform) == _avatarRoot)
.ToArray();
if (_nowDragging.Length > 0)
foreach (var reference in references)
{
DragAndDrop.visualMode = DragAndDropVisualMode.Link;
_parentElem.AddToClassList("drop-area--drag-active");
}
}
private void OnDragUpdate(DragUpdatedEvent _)
{
if (_nowDragging.Length > 0) DragAndDrop.visualMode = DragAndDropVisualMode.Link;
}
private void OnDragLeave(DragLeaveEvent evt)
{
_nowDragging = Array.Empty<GameObject>();
_parentElem.RemoveFromClassList("drop-area--drag-active");
}
private void OnDragPerform(DragPerformEvent evt)
{
if (_nowDragging.Length > 0 && TargetComponent != null && _avatarRoot != null)
{
var knownObjs = TargetComponent.Objects.Select(o => o.Object.Get(TargetComponent)).ToHashSet();
Undo.RecordObject(TargetComponent, "Add Toggled Objects");
foreach (var obj in _nowDragging)
{
if (knownObjs.Contains(obj)) continue;
var aor = new AvatarObjectReference();
aor.Set(obj);
var toggledObject = new ToggledObject { Object = aor, Active = !obj.activeSelf };
TargetComponent.Objects.Add(toggledObject);
}
EditorUtility.SetDirty(TargetComponent);
PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent);
var toggledObject = new ToggledObject { Object = reference, Active = !reference.Get(TargetComponent).activeSelf };
TargetComponent.Objects.Add(toggledObject);
}
_nowDragging = Array.Empty<GameObject>();
_parentElem.RemoveFromClassList("drop-area--drag-active");
EditorUtility.SetDirty(TargetComponent);
PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent);
}
}
}

View File

@ -39,14 +39,24 @@
margin: 0;
}
#f-active {
justify-content: center;
}
#f-object {
flex-grow: 1;
}
.drop-area--drag-active > ListView ScrollView {
background-color: rgba(0, 255, 255, 0.1);
#f-active {
display: none;
}
#f-active-dropdown {
width: 60px;
}
.drop-area--drag-active {
background-color: rgba(0, 127, 255, 0.2);
}
.drop-area--drag-active .unity-scroll-view,
.drop-area--drag-active .unity-list-view__footer,
.drop-area--drag-active .unity-list-view__reorderable-item {
background-color: rgba(0, 0, 0, 0.0);
}

View File

@ -15,6 +15,9 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
private const string UxmlPath = Root + "ToggledObjectEditor.uxml";
private const string UssPath = Root + "ObjectSwitcherStyles.uss";
private const string V_On = "ON";
private const string V_Off = "OFF";
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var uxml = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(UxmlPath).CloneTree();
@ -24,6 +27,21 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
uxml.styleSheets.Add(uss);
uxml.BindProperty(property);
var f_active = uxml.Q<Toggle>("f-active");
var f_active_dropdown = uxml.Q<DropdownField>("f-active-dropdown");
f_active_dropdown.choices.Add(V_On);
f_active_dropdown.choices.Add(V_Off);
f_active.RegisterValueChangedCallback(evt =>
{
f_active_dropdown.SetValueWithoutNotify(evt.newValue ? V_On : V_Off);
});
f_active_dropdown.RegisterValueChangedCallback(evt =>
{
f_active.value = evt.newValue == V_On;
});
return uxml;
}
}

View File

@ -1,6 +1,7 @@
<UXML xmlns:ui="UnityEngine.UIElements" xmlns:ed="UnityEditor.UIElements">
<ui:VisualElement class="horizontal">
<ed:PropertyField name="f-active" binding-path="Active" label=""/>
<ed:PropertyField name="f-object" binding-path="Object" label=""/>
<ui:Toggle name="f-active" binding-path="Active" label=""/>
<ui:DropdownField name="f-active-dropdown"/>
</ui:VisualElement>
</UXML>

View File

@ -1,6 +1,4 @@
using System.Globalization;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
@ -15,95 +13,104 @@ namespace nadena.dev.modular_avatar.core.editor
private const string V_None = " ";
private const string V_True = "ON";
private const string V_False = "OFF";
private readonly TextField _visibleField;
private readonly FloatField _defaultValueField;
private readonly Toggle _hasExplicitDefaultValueField;
private readonly TextField _numberField;
private readonly DropdownField _boolField;
private readonly Toggle _hasExplicitDefaultSetField;
private ParameterSyncType _syncType;
public DefaultValueField()
{
// Hidden binding elements
_defaultValueField = new FloatField();
_hasExplicitDefaultSetField = new Toggle();
_boolField = new DropdownField();
_defaultValueField.style.display = DisplayStyle.None;
_defaultValueField.bindingPath = nameof(ParameterConfig.defaultValue);
_defaultValueField.RegisterValueChangedCallback(evt => UpdateVisibleField(evt.newValue, _hasExplicitDefaultValueField.value));
_hasExplicitDefaultValueField = new Toggle();
_hasExplicitDefaultValueField.style.display = DisplayStyle.None;
_hasExplicitDefaultValueField.bindingPath = nameof(ParameterConfig.hasExplicitDefaultValue);
_hasExplicitDefaultValueField.RegisterValueChangedCallback(evt => UpdateVisibleField(_defaultValueField.value, evt.newValue));
// Visible elements for input
_numberField = new TextField();
_numberField.isDelayed = true;
_numberField.RegisterValueChangedCallback(evt => OnUpdateNumberValue(evt.newValue));
_boolField = new DropdownField();
_boolField.choices.Add(V_None);
_boolField.choices.Add(V_True);
_boolField.choices.Add(V_False);
_boolField.RegisterValueChangedCallback(evt => OnUpdateBoolValue(evt.newValue));
_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);
_boolField.RegisterValueChangedCallback(evt =>
{
if (evt.newValue == V_True)
_defaultValueField.value = 1;
else
_defaultValueField.value = 0;
_hasExplicitDefaultSetField.value = evt.newValue != V_None;
});
style.flexDirection = FlexDirection.Row;
Add(_visibleField);
Add(_boolField);
Add(_defaultValueField);
Add(_hasExplicitDefaultSetField);
Add(_hasExplicitDefaultValueField);
Add(_numberField);
Add(_boolField);
}
public void ManualBindProperty(SerializedProperty property)
public void OnUpdateSyncType(ParameterSyncType syncType)
{
_defaultValueField.BindProperty(property);
_hasExplicitDefaultSetField.BindProperty(property);
_syncType = syncType;
if (syncType != ParameterSyncType.Bool)
{
_numberField.style.display = DisplayStyle.Flex;
_boolField.style.display = DisplayStyle.None;
OnUpdateNumberValue(_numberField.value);
}
else
{
_numberField.style.display = DisplayStyle.None;
_boolField.style.display = DisplayStyle.Flex;
OnUpdateBoolValue(_boolField.value);
}
}
private void OnUpdateNumberValue(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
_defaultValueField.value = 0;
_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
{
ParameterSyncType.Int => Mathf.FloorToInt(Mathf.Clamp(parsed, 0, 255)),
ParameterSyncType.Float => Mathf.Clamp(parsed, -1, 1),
ParameterSyncType.Bool => parsed != 0 ? 1 : 0,
_ => parsed,
};
_hasExplicitDefaultValueField.value = true;
}
UpdateVisibleField(_defaultValueField.value, _hasExplicitDefaultValueField.value);
}
private void OnUpdateBoolValue(string value)
{
_defaultValueField.value = value == V_True ? 1 : 0;
_hasExplicitDefaultValueField.value = value != V_None;
UpdateVisibleField(_defaultValueField.value, _hasExplicitDefaultValueField.value);
}
private void UpdateVisibleField(float value, bool hasExplicitValue)
{
if (Mathf.Abs(value) > 0.0000001)
if (hasExplicitValue || Mathf.Abs(value) > 0.0000001)
{
hasExplicitValue = true;
_numberField.SetValueWithoutNotify(value.ToString(CultureInfo.InvariantCulture));
_boolField.SetValueWithoutNotify(value != 0 ? V_True : V_False);
}
var str = hasExplicitValue ? value.ToString(CultureInfo.InvariantCulture) : "";
_visibleField.SetValueWithoutNotify(str);
string boolStr;
if (!hasExplicitValue)
boolStr = V_None;
else if (value > 0.5)
boolStr = V_True;
else
boolStr = V_False;
_boolField.SetValueWithoutNotify(boolStr);
{
_numberField.SetValueWithoutNotify(string.Empty);
_boolField.SetValueWithoutNotify(V_None);
}
}
}
}
}

View File

@ -20,14 +20,14 @@ namespace nadena.dev.modular_avatar.core.editor.Parameters
Localization.UI.Localize(root);
root.styleSheets.Add(uss);
var type_field = root.Q<DropdownField>("f-type");
var f_sync_type = root.Q<VisualElement>("f-sync-type");
var f_type = root.Q<DropdownField>("f-type");
var f_sync_type = root.Q<DropdownField>("f-sync-type");
var f_is_prefix = root.Q<VisualElement>("f-is-prefix");
SetupPairedDropdownField(
root,
type_field,
f_type,
f_sync_type,
root.Q<VisualElement>("f-is-prefix"),
f_is_prefix,
("Bool", "False", "params.syncmode.Bool"),
("Float", "False", "params.syncmode.Float"),
("Int", "False", "params.syncmode.Int"),
@ -35,15 +35,9 @@ namespace nadena.dev.modular_avatar.core.editor.Parameters
(null, "True", "params.syncmode.PhysBonesPrefix")
);
f_sync_type.Q<DropdownField>().RegisterValueChangedCallback(evt =>
{
var is_anim_only = evt.newValue == "Not Synced";
if (is_anim_only)
root.AddToClassList("st-anim-only");
else
root.RemoveFromClassList("st-anim-only");
});
var f_default = root.Q<DefaultValueField>();
f_default.OnUpdateSyncType((ParameterSyncType)f_sync_type.index);
f_sync_type.RegisterValueChangedCallback(evt => f_default.OnUpdateSyncType((ParameterSyncType)f_sync_type.index));
var f_synced = root.Q<Toggle>("f-synced");
var f_local_only = root.Q<Toggle>("f-local-only");

View File

@ -30,7 +30,7 @@
display: none;
}
.st-anim-only .st-anim-only__hide {
.st-ty-Not-Synced .st-anim-only__hide {
display: none;
}
@ -79,28 +79,12 @@
margin: 0;
}
DefaultValueField TextField {
display: flex;
DefaultValueField > * {
width: 60px;
height: 100%;
margin: 0;
}
DefaultValueField DropdownField {
display: none;
width: 60px;
height: 100%;
margin: 0;
}
.st-ty-Bool DefaultValueField TextField {
display: none;
}
.st-ty-Bool DefaultValueField DropdownField {
display: flex;
}
#f-local-only {
display: none;
}

View File

@ -64,7 +64,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
.Select(x => mesh.GetBlendShapeName(x))
.ToList();
}
catch (MissingComponentException e)
catch (MissingComponentException)
{
shapeNames = null;
}

View File

@ -19,11 +19,12 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
[SerializeField] private StyleSheet uss;
[SerializeField] private VisualTreeAsset uxml;
private DragAndDropManipulator _dragAndDropManipulator;
private BlendshapeSelectWindow _window;
protected override void OnInnerInspectorGUI()
{
throw new NotImplementedException();
EditorGUILayout.HelpBox("Unable to show override changes", MessageType.Info);
}
protected override VisualElement CreateInnerInspectorGUI()
@ -41,6 +42,8 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
listView.showBoundCollectionSize = false;
listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight;
_dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarShapeChanger);
// The Add button callback isn't exposed publicly for some reason...
var field_addButton = typeof(BaseListView).GetField("m_AddButton", NonPublic | Instance);
var addButton = (Button)field_addButton.GetValue(listView);
@ -50,6 +53,41 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger
return root;
}
private void OnEnable()
{
if (_dragAndDropManipulator != null)
_dragAndDropManipulator.TargetComponent = target as ModularAvatarShapeChanger;
}
private class DragAndDropManipulator : DragAndDropManipulator<ModularAvatarShapeChanger>
{
public DragAndDropManipulator(VisualElement targetElement, ModularAvatarShapeChanger targetComponent)
: base(targetElement, targetComponent) { }
protected override bool FilterGameObject(GameObject obj)
{
if (obj.TryGetComponent<SkinnedMeshRenderer>(out var smr))
{
return smr.sharedMesh != null && smr.sharedMesh.blendShapeCount > 0;
}
return false;
}
protected override void AddObjectReferences(AvatarObjectReference[] references)
{
Undo.RecordObject(TargetComponent, "Add Changed Shapes");
foreach (var reference in references)
{
var changedShape = new ChangedShape { Object = reference, ShapeName = string.Empty };
TargetComponent.Shapes.Add(changedShape);
}
EditorUtility.SetDirty(TargetComponent);
PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent);
}
}
private void OnDisable()
{
if (_window != null) DestroyImmediate(_window);

View File

@ -68,3 +68,13 @@
.change-type-delete #f-value-delete {
display: flex;
}
.drop-area--drag-active {
background-color: rgba(0, 127, 255, 0.2);
}
.drop-area--drag-active .unity-scroll-view,
.drop-area--drag-active .unity-list-view__footer,
.drop-area--drag-active .unity-list-view__reorderable-item {
background-color: rgba(0, 0, 0, 0.0);
}

View File

@ -86,6 +86,7 @@
"merge_armature.lockmode.bidirectional.body": "The base armature and the merged armature will always have the same position. This is useful when creating animations that are meant to target the base armature. In order to activate this, your armatures must already be in the exact same position.",
"merge_armature.reset_pos": "Reset position to base avatar",
"merge_armature.reset_pos.info": "This command will force the position of all bones in the outfit to match that of the base avatar. This can be helpful as a starting point for installing outfits not set up for your current avatar.",
"merge_armature.reset_pos.convert_atpose": "Convert A-Pose/T-Pose to match base avatar",
"merge_armature.reset_pos.adjust_rotation": "Also set rotation to base avatar",
"merge_armature.reset_pos.adjust_scale": "Also set local scale to base avatar",
"merge_armature.reset_pos.execute": "Do it!",
@ -99,7 +100,7 @@
"worldfixed.quest": "This component is not compatible with Android builds and will have no effect.",
"worldfixed.normal": "This object will be fixed to world unless you fixed to avatar with constraint.",
"fpvisible.normal": "This object will be visible in your first person view.",
"fpvisible.NotUnderHead": "This component has no effect when not placed under the head bone.",
"fpvisible.NotUnderHead": "This component has no effect when not placed under the head bone.\nIf this will be placed under the head bone through Bone Proxy etc., this warning can be ignored.",
"fpvisible.quest": "This component is not compatible with Android builds and will have no effect.",
"fpvisible.InPhysBoneChain": "This object is controlled by a Physics Bone chain and cannot be made visible in first person safely. Select the start of the chain instead.",
"blendshape.mesh": "Mesh",
@ -149,6 +150,8 @@
"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.",
"error.replace_object.replacing_replacement": "[MA-0009] The same target object cannot be specified in multiple Replace Object components",
"error.replace_object.parent_of_target": "[MA-0010] The target object cannot be a parent of this object",
"validation.blendshape_sync.no_local_renderer": "[MA-1000] No renderer found on this object",
"validation.blendshape_sync.no_local_renderer:hint": "Blendshape Sync acts on a Skinned Mesh Renderer on the same GameObject. Did you attach it to the right object?",
"validation.blendshape_sync.no_local_mesh": "[MA-1001] No mesh found on the renderer on this object",
@ -240,7 +243,7 @@
"setup_outfit.err.no_avatar_descriptor": "No avatar descriptor found in {0}'s parents. Make sure your outfit is placed inside your avatar.",
"setup_outfit.err.no_animator": "Your avatar does not have an Animator component.",
"setup_outfit.err.no_hips": "Your avatar does not have a Hips bone. Setup Outfit only works on humanoid avatars.",
"setup_outfit.err.no_outfit_hips": "Unable to identify the Hips object for the outfit. Searched for objects containing the following names:",
"setup_outfit.err.no_outfit_hips": "Unable to identify the Hips object for the outfit. Searched for objects containing the following names(case-insensitive):",
"move_independently.group-header": "Objects to move together",
"scale_adjuster.scale": "Scale adjustment",
"scale_adjuster.adjust_children": "Adjust position of child objects",
@ -281,4 +284,4 @@
"ro_sim.effect_group.rule_inverted": "This rule is inverted",
"ro_sim.effect_group.rule_inverted.tooltip": "This rule will be applied when one of its conditions is NOT met",
"ro_sim.effect_group.conditions": "Conditions"
}
}

View File

@ -9,7 +9,7 @@
"menuinstall.showcontents": "メニュー内容を表示",
"menuinstall.showcontents.notselected": "メニューが選択されていません",
"menuinstall.devoptions": "プレハブ開発者向け設定",
"menuinstall.menu_icon_too_large": "メニューに設定されているアイコンが256ピクセルより大きすぎます。",
"menuinstall.menu_icon_too_large": "メニューに設定されているアイコンが256x256より大きいです。",
"menuinstall.menu_icon_uncompressed": "メニューに設定されているアイコンが圧縮設定されていません。",
"menuinstall.srcmenu": "インストールされるメニュー",
"params.syncmode.NotSynced": "Animatorのみ",
@ -31,32 +31,32 @@
"merge_parameter.ui.name": "パラメーター名",
"merge_parameter.ui.prefix": "PhysBone 接頭辞",
"merge_parameter.ui.remapTo": "名前を変更",
"merge_parameter.ui.remapTo.tooltip": "ここに新しい名前を入れることで、このパラメーターの名前を変更できます。これで名前かぶりを回避したり、あるいはあえて複数のギミックを連動できます。",
"merge_parameter.ui.remapTo.tooltip": "ここに新しい名前を入れることで、このパラメーターの名前を変更できます。これで名前かぶりを回避したり、複数のギミックを連動させたりすることができます。",
"merge_parameter.ui.remapTo.automatic": "(自動的に設定)",
"merge_parameter.ui.defaultValue": "初期値",
"merge_parameter.ui.defaultValue.tooltip": "アバターがリセット、または最初に着た時にこの値が採用されます。",
"merge_parameter.ui.defaultValue.tooltip": "アバターをリセットした時、または最初に着た時にこの値が採用されます。",
"merge_parameter.ui.saved": "保存する",
"merge_parameter.ui.saved.tooltip": "保存されたパラメーターは、アバター変更やワールド移動保持されます",
"merge_parameter.ui.saved.tooltip": "保存されたパラメーターは、アバター変更やワールド移動をしても保持されます",
"merge_parameter.ui.internalParameter": "自動リネーム",
"merge_parameter.ui.internalParameter.tooltip": "有効にすると、名前かぶりを回避するために自動的に名前を変更します",
"merge_parameter.ui.isPrefix": "PhysBone 接頭辞",
"merge_parameter.ui.syncType": "パラメーター型",
"merge_parameter.ui.synced": "同期する",
"merge_parameter.ui.synced.tooltip": "有効にすると、ネットワーク上同期されます",
"merge_parameter.ui.synced.tooltip": "有効にすると、このパラメーターは同期されます",
"merge_parameter.ui.unregistered_foldout": "未登録パラメーター",
"merge_parameter.ui.add_button": "追加",
"merge_parameter.ui.details": "パラメーターの詳細設定",
"merge_parameter.ui.overrideAnimatorDefaults": "アニメーターの初期値を設定",
"merge_parameter.ui.overrideAnimatorDefaults": "アニメーターの初期値を設定",
"merge_armature.merge_target": "統合先",
"merge_armature.merge_target.tooltip": "このオブジェクトを統合先のアーマチュアに統合します",
"merge_armature.prefix": "接頭辞",
"merge_armature.prefix.tooltip": "マージするボーンに付いている接頭辞",
"merge_armature.prefix.tooltip": "統合されるボーンに付いている接頭辞",
"merge_armature.suffix": "接尾辞",
"merge_armature.suffix.tooltip": "マージするボーンに付いている接尾辞",
"merge_armature.suffix.tooltip": "統合されるボーンに付いている接尾辞",
"merge_armature.locked": "位置を固定",
"merge_armature.locked.tooltip": "このオブジェクトのボーンを統合先のボーンに常に相互的に位置を合わせる。アニメーション制作向け",
"merge_armature.locked.tooltip": "このオブジェクトのボーンと統合先のボーンの位置を常に合わせます。アニメーション作成時に便利です。",
"merge_armature.adjust_names": "ボーン名を統合先に合わせる",
"merge_armature.adjust_names.tooltip": "統合先のボーン名に合わせて、衣装のボーン名を合わせて変更します。統合先アバターに非対応の衣装導入向け機能です。",
"merge_armature.adjust_names.tooltip": "衣装のボーン名をアバターのボーン名に合わせて変更します。アバターに非対応の衣装を導入する時に便利です。",
"merge_armature.mangle_names": "名前かぶりを回避",
"merge_armature.mangle_names.tooltip": "ほかのアセットとの名前かぶりを裂けるため、新規ボーンの名前を自動で変更する",
"path_mode.Relative": "相対的(このオブジェクトからのパスを使用)",
@ -66,12 +66,12 @@
"merge_animator.delete_attached_animator": "付属アニメーターを削除",
"merge_animator.delete_attached_animator.tooltip": "統合後、このオブジェクトについているアニメーターを削除します",
"merge_animator.path_mode": "パスモード",
"merge_animator.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトについているアニメーターでアニメーション編集できます",
"merge_animator.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトにつけたアニメーターでアニメーションを編集することができます。",
"merge_animator.match_avatar_write_defaults": "アバターのWrite Defaults設定に合わせる",
"merge_animator.match_avatar_write_defaults.tooltip": "アバターの該当アニメーターのWrite Defaults設定に合わせます。アバター側の設定が矛盾する場合は、統合されるアニメーターのWD値がそのまま採用されます。",
"merge_animator.relative_path_root": "相対的パスのルート",
"merge_animator.relative_path_root.tooltip": "相対的パスはこのオブジェクトを基準に解釈されます。指定がない場合は、このコンポーネントがついているオブジェクトを基準とします。",
"merge_animator.layer_priority": "レイヤー優先度",
"merge_animator.layer_priority": "レイヤー統合優先度",
"merge_animator.layer_priority.tooltip": "アニメーターにレイヤーが統合される順番を制御します。低い値から高い値の順に統合されます。マイナスの場合は元々のAvatar Descriptorについているコントローラーより前に統合され、ゼロ以上の場合はそのあとに統合されます。",
"merge_armature.lockmode": "位置追従モード",
"merge_armature.lockmode.not_locked.title": "追従なし",
@ -81,28 +81,29 @@
"merge_armature.lockmode.bidirectional.title": "アバター <=====> オブジェクト (双方向)",
"merge_armature.lockmode.bidirectional.body": "アバターと統合されるアーマチュアは常に同じ位置になります。元のアバターを操作するアニメーションを作る時に便利かもしれません。有効にするためには、統合されるアーマチュアの位置を統合先と同じにしておく必要があります。",
"merge_armature.reset_pos": "位置を元アバターに合わせてリセット",
"merge_armature.reset_pos.info": "このコマンドは、衣装のボーンの位置をアバターのボーンの位置に合わせます。非対応衣装を導入するとき、アバウトに合わせるために便利です。",
"merge_armature.reset_pos.info": "衣装のボーンの位置をアバターのボーンの位置に合わせます。非対応衣装を導入する際、アバウトに位置を合わせるのに便利です。",
"merge_armature.reset_pos.convert_atpose": "Aポーズ/Tポーズを合わせる",
"merge_armature.reset_pos.adjust_rotation": "回転も合わせる",
"merge_armature.reset_pos.adjust_scale": "スケールも合わせる",
"merge_armature.reset_pos.execute": "実行",
"merge_armature.reset_pos.heuristic_scale": "衣装の全体的なスケールをアバターに合わせる",
"merge_armature.reset_pos.heuristic_scale.tooltip": "腕の長さを参考に、衣装全体のスケールをアバターに合わせます。非対応衣装を導入するときは推奨です。",
"merge_armature.reset_pos.heuristic_scale.tooltip": "腕の長さを参考に、衣装全体のスケールをアバターに合わせます。非対応衣装を導入する時にお勧めです。",
"merge_blend_tree.blend_tree": "ブレンドツリー",
"merge_blend_tree.path_mode": "パスモード",
"merge_blend_tree.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトについているアニメーターでアニメーション編集できます",
"merge_blend_tree.path_mode.tooltip": "アニメーション内のパスを解釈するモード。相対的にすると、このオブジェクトにつけたアニメーターでアニメーションを編集することができます。",
"merge_blend_tree.relative_path_root": "相対的パスのルート",
"merge_blend_tree.relative_path_root.tooltip": "相対的パスはこのオブジェクトを基準に解釈されます。指定がない場合は、このコンポーネントがついているオブジェクトを基準とします。",
"worldfixed.quest": "このコンポーネントはアンドロイドビルド非対応のため無効となっています。",
"worldfixed.quest": "このコンポーネントはアンドロイドビルドに非対応であるため、無効となっています。",
"worldfixed.normal": "このオブジェクトはConstraint等でアバターに追従させない限りワールドに固定されます。",
"fpvisible.normal": "このオブジェクトは一人視点で表示されます。",
"fpvisible.NotUnderHead": "このコンポーネントはヘッドボーン外では効果がありません。",
"fpvisible.quest": "このコンポーネントはアンドロイドビルド非対応のため無効となっています。",
"fpvisible.InPhysBoneChain": "このオブジェクトはPhysBoneに制御されているため、一人視点で表示できません。PhysBoneの始点を指定してください。",
"fpvisible.normal": "このオブジェクトは一人視点で表示されます。",
"fpvisible.NotUnderHead": "このコンポーネントは頭ボーンの配下でないと効果がありません。\nBone Proxyなどで頭ボーンの配下に配置される場合は効果あります",
"fpvisible.quest": "このコンポーネントはアンドロイドビルドに非対応であるため、無効となっています。",
"fpvisible.InPhysBoneChain": "このオブジェクトはPhysBoneで制御されているため、一人称視点でうまく表示させることができません。PhysBoneの始点を指定してください。",
"blendshape.mesh": "メッシュ",
"blendshape.source": "元メッシュのブレンドシェプ",
"blendshape.target": "このメッシュのブレンドシェプ",
"hint.not_in_avatar": "このコンポーネントが正しく動作するには、アバター内に配置する必要があります。",
"boneproxy.err.MovingTarget": "他のモジュラーアバターコンポーネントで移動されるオブジェクト指定できません。",
"blendshape.source": "元メッシュのブレンドシェプ",
"blendshape.target": "このメッシュのブレンドシェプ",
"hint.not_in_avatar": "このコンポーネントを正しく動作させるには、アバター内に配置する必要があります。",
"boneproxy.err.MovingTarget": "他のモジュラーアバターコンポーネントで移動されるオブジェクト指定できません。",
"boneproxy.err.NotInAvatar": "アバター内のオブジェクトを指定してください。",
"boneproxy.attachment": "配置モード",
"boneproxy.attachment.AsChildAtRoot": "子として・ルートに配置",
@ -111,28 +112,28 @@
"boneproxy.attachment.AsChildKeepRotation": "子として・ワールド向きを維持",
"mesh_settings.header_probe_anchor": "Anchor Override 設定",
"mesh_settings.inherit_probe_anchor": "設定モード",
"mesh_settings.probe_anchor": "アンカーオーバーライド",
"mesh_settings.probe_anchor.tooltip": "このオブジェクトとその子のレンダラーのAnchorOverrideを設定します。",
"mesh_settings.probe_anchor": "Anchor Override",
"mesh_settings.probe_anchor.tooltip": "このオブジェクトとその子のレンダラーのAnchor Overrideを設定します。",
"mesh_settings.header_bounds": "Bounds 設定",
"mesh_settings.inherit_bounds": "設定モード",
"mesh_settings.root_bone": "ルートボーン",
"mesh_settings.root_bone.tooltip": "このオブジェクトとその子のメッシュで設定されるルートボーン。メッシュのバウンズを計算するための参照点として使用されます。",
"mesh_settings.bounds": "バウンズ",
"mesh_settings.root_bone.tooltip": "このオブジェクトとその子のメッシュで設定されるルートボーン。メッシュのBoundsを計算する際の基準として使用されます。",
"mesh_settings.bounds": "Bounds",
"mesh_settings.bounds.tooltip": "このオブジェクトとその子のメッシュで設定されるバウンズ。画面外のメッシュのレンダリングを省略するかどうかを決定するために使用されます。",
"mesh_settings.inherit_mode.Inherit": "継承",
"mesh_settings.inherit_mode.Set": "設定",
"mesh_settings.inherit_mode.DontSet": "設定しない(メッシュ本体の設定のまま)",
"mesh_settings.inherit_mode.SetOrInherit": "親が指定されてる時は継承、または設定",
"mesh_settings.inherit_mode.SetOrInherit": "親で指定されている時は継承、それ以外では設定",
"pb_blocker.help": "このオブジェクトは親のPhysBoneから影響を受けなくなります。",
"hint.bad_vrcsdk": "使用中のVRCSDKのバージョンとは互換性がありません。\n\nVRCSDKを更新してみてください。それでもだめでしたら、Modular Avatarにも最新版が出てないかチェックしてください。",
"hint.bad_vrcsdk": "使用中のVRCSDKのバージョンとは互換性がありません。\n\nVRCSDKを更新してみてください。それでも駄目な場合、Modular Avatarにも最新版が出ていないか確認してみてください。",
"error.stack_trace": "スタックトレース(バグを報告する時は必ず添付してください!)",
"error.merge_armature.circular_dependency": "[MA-0001] Merge Armatureに循環参照があります",
"error.merge_armature.circular_dependency:description": "Merge Armature コンポーネントは、自分自身、または自分の子をマージターゲットとして参照しています。",
"error.merge_armature.circular_dependency:hint": "Merge Armature は通常、ターゲットフィールドにアバター本体の Armature オブジェクトを指定する必要があります。衣装自体を指定しないでください。",
"error.merge_armature.physbone_on_humanoid_bone": "[MA-0002] ヒューマイドボーンにPhysBoneコンポーネントを検出",
"error.merge_armature.physbone_on_humanoid_bone:hint": "一部のヒューマイドボーンは、PhysBonesによって制御されています。 マージ対象の対応するヒューマイドボーンとは位置が異なるため、適切にマージすることはできません。マージするにはヒューマイドボーンのPhysBonesを取り除く必要があります。",
"error.merge_armature.circular_dependency:description": "Merge Armatureコンポーネントの統合先として、自分自身、または自分の子が参照されています。",
"error.merge_armature.circular_dependency:hint": "通常、Merge Armatureは統合先としてアバター本体のArmatureオブジェクトを指定する必要があります。衣装自体を指定しないように注意してください。",
"error.merge_armature.physbone_on_humanoid_bone": "[MA-0002] HumanoidボーンにPhysBoneコンポーネントがついています。",
"error.merge_armature.physbone_on_humanoid_bone:hint": "一部のHumanoidボーンがPhysBoneによって制御されています。対応する統合先のHumanoidボーンと位置が異なるため、適切に統合することができません。統合するにはHumanoidボーンについているPhysBoneを取り除く必要があります。",
"error.merge_blend_tree.missing_tree": "[MA-0009] ブレンドツリーが指定されていません",
"error.merge_blend_tree.missing_tree:hint": "Merge Blend Treeが動作するには、どのブレンドツリーを統合するか指定する必要があります。「ブレンドツリー」欄を設定してみてください。",
"error.merge_blend_tree.missing_tree:hint": "Merge Blend Treeが動作するには、どのブレンドツリーを統合するか指定する必要があります。「ブレンドツリー」欄を設定しているかご確認ください。",
"error.internal_error": "[MA-9999] 内部エラーが発生しました:{0}\n以下のオブジェクトの処理中に発生しました",
"error.merge_animator.param_type_mismatch": "[MA-0003] パラメータの型が競合しています",
"error.merge_animator.param_type_mismatch:description": "パラメータ {0} には複数の型が指定されています: {1} != {2}",
@ -142,37 +143,39 @@
"error.rename_params.type_conflict:description": "パラメータ {0} には複数の型が指定されています: {1} != {2}",
"error.rename_params.default_value_conflict": "[MA-0007] 初期値の競合",
"error.rename_params.default_value_conflict:description": "パラメータ {0} には複数の初期値が指定されています: {1} != {2}",
"error.rename_params.default_value_conflict:hint": "予測不可能な動作を避けるために、MAパラメータコンポーネントのデフォルト値フィールドはパラメーター名毎に一個のコンポーネント以外空白のままにしてください。 複数の値が存在する場合、Modular Avatarは階層順に指定された最初のデフォルト値を選択します。",
"error.replace_object.null_target": "[MA-0008] ターゲットが指定されていません",
"error.replace_object.null_target:hint": "Replace Object には置き換えるべきターゲットオブジェクトが必要です。設定してみてください。",
"validation.blendshape_sync.no_local_renderer": "[MA-1000] このオブジェクトにはSkinnedMeshRendererがありません。",
"validation.blendshape_sync.no_local_renderer:hint": "Blendshape Syncは同じGameObject上のSkinned Mesh Rendererに作用します。正しいオブジェクトに追加しましたか",
"validation.blendshape_sync.no_local_mesh": "[MA-1001] このオブジェクトにはSkinnedMeshRendererがありますが、メッシュがありません。",
"error.rename_params.default_value_conflict:hint": "予測不可能な動作を避けるため、MA Parametersコンポーネントの初期値フィールドはパラメーター名毎に1つだけしか指定しないようにし、他のコンポーネントでは空白のままにしてください。複数の値が存在する場合、Modular Avatarは階層順で最初に指定された初期値を採用します。",
"error.replace_object.null_target": "[MA-0008] 置き換え先が指定されていません",
"error.replace_object.null_target:hint": "Replace Objectは置き換え先のオブジェクトを指定する必要があります。",
"error.replace_object.replacing_replacement": "[MA-0009] 複数のReplace Objectコンポーネントで、同じ置き換え先を指定できません",
"error.replace_object.parent_of_target": "[MA-0010] このオブジェクトの親を置き換え先に指定できません",
"validation.blendshape_sync.no_local_renderer": "[MA-1000] このオブジェクトにはSkinned Mesh Rendererがありません。",
"validation.blendshape_sync.no_local_renderer:hint": "Blendshape Syncは同じGameObject上のSkinned Mesh Rendererに作用します。コンポーネントが正しいオブジェクトに追加されているか確認してください。",
"validation.blendshape_sync.no_local_mesh": "[MA-1001] このオブジェクトにはSkinned Mesh Rendererがありますが、メッシュがありません。",
"validation.blendshape_sync.no_local_mesh:hint": "このオブジェクトの Skinned Mesh Renderer の設定が壊れている可能性があります。 元のプレハブまたはFBXからオブジェクトを再作成してみてください。",
"validation.blendshape_sync.no_bindings": "[MA-1002] このBlendshapeSyncにはバインドが設定されていません。",
"validation.blendshape_sync.no_bindings": "[MA-1002] このBlendshape Syncにはバインドが設定されていません。",
"validation.blendshape_sync.no_bindings:hint": "Blendshape Syncは、どのブレンドシェイプを同期するかを知る必要があります。追加するには、「+」ボタンをクリックしてください。",
"validation.blendshape_sync.missing_local_shape": "[MA-1003] 同期先のメッシュに該当するブレンドシェープ「{0}」がありません。",
"validation.blendshape_sync.missing_local_shape:description": "ローカルブレンドシェイプがありません: {0}",
"validation.blendshape_sync.missing_local_shape:hint": "ターゲットオブジェクトから値を受け取るように設定されたブレンドシェイプがありません。赤で示されているブレンドシェイプ名を変更してみてください。",
"validation.blendshape_sync.missing_target_shape": "[MA-1004] 同期先メッシュにブレンドシェープ「{0}」が見つかりません",
"validation.blendshape_sync.missing_target_shape:description": "ターゲットブレンドシェイプがありません: {0}",
"validation.blendshape_sync.missing_target_shape:hint": "ローカルオブジェクトに値を「送る」ように設定されたブレンドシェイプがありません。赤で示されているブレンドシェイプ名を変更してみてください。",
"validation.blendshape_sync.no_target": "[MA-1005] このBlendshapeSyncには同期元が設定されていないバインドがあります。",
"validation.blendshape_sync.no_target:hint": "どのオブジェクトからBlendshapeを同期するかを教える必要があります。メッシュを設定してみてください。",
"validation.blendshape_sync.missing_target_renderer": "[MA-1006] 同期元のオブジェクトにSkinnedMeshRendererがありません。",
"validation.blendshape_sync.missing_target_renderer:hint": "Blendshape Syncは、対象オブジェクトのSkinned Mesh Rendererからブレンド形状の値を受け取ります。正しいオブジェクトに追加しましたか",
"validation.blendshape_sync.missing_target_mesh": "[MA-1007] 同期元のオブジェクトにはSkinnedMeshRendererがありますが、メッシュがありません。",
"validation.blendshape_sync.missing_target_mesh:hint": "ターゲットオブジェクトの Skinned Mesh Renderer の設定が壊れている可能性があります。 元のプレハブまたはFBXからオブジェクトを再作成してみてください。",
"validation.bone_proxy.no_target": "[MA-1100] ターゲットオブジェクトが未設定もしくは存在しません。",
"validation.bone_proxy.no_target:hint": "ボーンプロキシがどのオブジェクトに追尾するかを知る必要があります。追尾すべきオブジェクトをターゲットフィールドに設定してみてください。",
"validation.blendshape_sync.missing_local_shape": "[MA-1003] 同期先のメッシュにブレンドシェイプ「{0}」がありません。",
"validation.blendshape_sync.missing_local_shape:description": "ブレンドシェイプ「{0}」がありません。",
"validation.blendshape_sync.missing_local_shape:hint": "値を受け取るように設定されたブレンドシェイプが同期先のメッシュに存在しません。赤で示されているブレンドシェイプ名を変更してみてください。",
"validation.blendshape_sync.missing_target_shape": "[MA-1004] 同期元のメッシュにブレンドシェイプ「{0}」がありません。",
"validation.blendshape_sync.missing_target_shape:description": "ブレンドシェイプ「{0}」がありません。",
"validation.blendshape_sync.missing_target_shape:hint": "値を「送る」ように設定されたブレンドシェイプが同期元のメッシュに存在しません。赤で示されているブレンドシェイプ名を変更してみてください。",
"validation.blendshape_sync.no_target": "[MA-1005] このBlendshape Syncには同期元が設定されていないバインドがあります。",
"validation.blendshape_sync.no_target:hint": "どのオブジェクトからBlendshapeを同期するか指定する必要があります。メッシュを設定してください。",
"validation.blendshape_sync.missing_target_renderer": "[MA-1006] 同期元のオブジェクトにSkinned Mesh Rendererがありません。",
"validation.blendshape_sync.missing_target_renderer:hint": "Blendshape Syncは、同期元のSkinned Mesh Rendererからブレンドシェイプの値を受け取ります。コンポーネントが正しいオブジェクトに追加されているか確認してください。",
"validation.blendshape_sync.missing_target_mesh": "[MA-1007] 同期元のオブジェクトにはSkinned Mesh Rendererがありますが、メッシュがありません。",
"validation.blendshape_sync.missing_target_mesh:hint": "同期元のオブジェクトの Skinned Mesh Renderer の設定が壊れている可能性があります。 元のプレハブまたはFBXからオブジェクトを再作成してみてください。",
"validation.bone_proxy.no_target": "[MA-1100] ターゲットオブジェクトが未設定であるか、存在しません。",
"validation.bone_proxy.no_target:hint": "Bone Proxyはどのオブジェクトに追従させるかを指定する必要があります。追従させたい対象のオブジェクトをターゲット欄に設定してみてください。",
"validation.menu_installer.no_menu": "[MA-1200] インストールするメニューがありません。",
"validation.menu_installer.no_menu:hint": "Menu Installer は、どのメニューをインストールするべきか設定する必要があります。 「Prefab開発者向けオプション」内の「インストールするメニュー」フィールドを設定するか、MA Menu Itemコンポーネントを追加してみてください。",
"validation.merge_animator.no_animator": "[MA-1300] Animator Controllerが設定されていません",
"validation.merge_animator.no_animator:hint": "Animatorをマージするには、どのアニメーターを統合するか設定する必要があります。「統合されるアニメーター」を設定してみてください。",
"validation.merge_armature.no_target": "[MA-1400] 統合先が未設定もしくは存在しません。",
"validation.merge_animator.no_animator": "[MA-1300] Animator Controllerが未設定であるか、存在しません。",
"validation.merge_animator.no_animator:hint": "どのアニメーターを統合するか設定する必要があります。「統合されるアニメーター」を設定してみてください。",
"validation.merge_armature.no_target": "[MA-1400] 統合先が未設定であるか、存在しません。",
"validation.merge_armature.no_target:hint": "Merge Armatureはどこに統合するか設定する必要があります。「統合先」を設定してみてください。",
"validation.merge_armature.target_is_child": "[MA-1500] 統合先をこのオブジェクトの子にすることができません",
"validation.merge_armature.target_is_child:hint": "Merge Armature は自身の子に統合できません。「統合先」を別のオブジェクトに設定してみてください。",
"validation.merge_armature.target_is_child": "[MA-1500] 統合先として自身の子を指定することはできません。",
"validation.merge_armature.target_is_child:hint": "Merge Armatureで自身の子に統合することはできません。「統合先」を別のオブジェクトに設定してみてください。",
"submenu_source.Children": "子オブジェクトから生成",
"submenu_source.MenuAsset": "Expressions Menu アセットを指定",
"menuitem.showcontents": "メニュー内容を表示",
@ -182,27 +185,27 @@
"menuitem.prop.type": "タイプ",
"menuitem.prop.type.tooltip": "この項目の種別",
"menuitem.prop.value": "パラメーター値",
"menuitem.prop.value.tooltip": "この項目が操作されたとき、パラメーターが設定される値",
"menuitem.prop.value.tooltip": "この項目が操作されたとき、パラメーターに設定される値",
"menuitem.prop.automatic_value": "自動",
"menuitem.prop.automatic_value.tooltip": "かぶらない値を自動的に割り振る",
"menuitem.prop.parameter": "パラメーター名",
"menuitem.prop.label": "表示名",
"menuitem.prop.submenu_asset": "サブメニューアセット",
"menuitem.prop.submenu_asset.tooltip": "サブメニューとして引用するアセット",
"menuitem.prop.submenu_source": "サブメニュー引用元",
"menuitem.prop.submenu_source.tooltip": "このサブメニューの内容をどこから引用するべきか指定",
"menuitem.prop.submenu_source.tooltip": "このサブメニューの内容をどこから引用するべきか指定します。",
"menuitem.prop.source_override": "引用元オブジェクト",
"menuitem.prop.source_override.tooltip": "指定した場合、指定したオブジェクトの子をメニューの内容として指定します。指定されてない場合はこのオブジェクト直下の子を使用します。",
"menuitem.prop.source_override.tooltip": "指定した場合、指定したオブジェクトの子をメニューの内容として指定します。指定されてない場合はこのオブジェクト直下の子を使用します。",
"menuitem.prop.is_default": "初期設定にする",
"menuitem.prop.is_default.tooltip": "ONの場合、アバター初期化の際にこのメニューアイテムを選択します",
"menuitem.prop.is_default.tooltip": "有効になっていると、アバター初期化の際にこのメニューアイテムを選択した状態にします",
"menuitem.prop.is_saved": "保存する",
"menuitem.prop.is_saved.tooltip": "有効になっていると、アバター変更やワールド移動するときこの設定が保持されます。",
"menuitem.prop.is_saved.tooltip": "有効になっていると、アバター変更やワールド移動をしてもこの設定が保持されます。",
"menuitem.prop.is_synced": "同期する",
"menuitem.prop.is_synced.tooltip": "有効の場合はほかのプレイヤーに同期されます。",
"menuitem.prop.automatic_value": "自動",
"menuitem.prop.automatic_value.tooltip": "かぶらない値を自動的に割り振る",
"menuitem.param.rotation": "回転パラメーター名",
"menuitem.param.rotation.tooltip": "このメニューアイテムの回転に連動するべきパラメーター",
"menuitem.prop.is_synced.tooltip": "有効になっていると、メニューがほかのプレイヤーに同期されます。",
"menuitem.param.rotation": "ラジアルメニュー用パラメーター名",
"menuitem.param.rotation.tooltip": "このラジアルメニューの操作と連動するべきパラメーター",
"menuitem.param.horizontal": "横パラメーター名",
"menuitem.param.horizontal.tooltip": "操作に連動するパラメーター名",
"menuitem.param.horizontal.tooltip": "左右操作に連動するパラメーター名",
"menuitem.param.vertical": "縦パラメーター名",
"menuitem.param.vertical.tooltip": "上下操作に連動するパラメーター名",
"menuitem.label.control_labels_and_params": "表示名・パラメーター",
@ -218,29 +221,29 @@
"control_group.foldout.actions": "アクション",
"control_group.foldout.menu_items": "関連付けされたメニューアイテム",
"control_group.is_saved": "保存する",
"control_group.is_saved.tooltip": "有効になっていると、アバター変更やワールド移動するときこの設定が保持されます。",
"control_group.is_saved.tooltip": "有効になっていると、アバター変更やワールド移動をしてもこの設定が保持されます。",
"control_group.is_synced": "同期する",
"control_group.is_synced.tooltip": "有効の場合はほかのプレイヤーに同期されます。",
"control_group.is_synced.tooltip": "有効になっていると、メニューがほかのプレイヤーに同期されます。",
"control_group.default_value": "初期値",
"control_group.default_value.unset": "(どれも選択されない)",
"animation_gen.duplicate_binding": "別々のコントロールグループから、同じパラメーターが操作されています。パラメーター:{0}",
"animation_gen.multiple_defaults": "同じコントロールグループに初期設定に指定されたメニューアイテムが複数あります。",
"animation_gen.multiple_defaults": "同じコントロールグループに初期設定にするとして指定されたメニューアイテムが複数あります。",
"menuitem.misc.add_item": "メニューアイテムを追加",
"replace_object.target_object": "上書き先",
"setup_outfit.err.header.notarget": "Setup outfit の処理が失敗しました",
"setup_outfit.err.header": "Setup outfit が「{0}」を処理中に失敗しました。",
"replace_object.target_object": "置き換え先",
"setup_outfit.err.header.notarget": "Setup Outfit の処理に失敗しました",
"setup_outfit.err.header": "Setup Outfit が「{0}」を処理中に失敗しました。",
"setup_outfit.err.unknown": "原因不明のエラーが発生しました。",
"setup_outfit.err.no_selection": "オブジェクトが選択されていません。",
"setup_outfit.err.run_on_avatar_itself": "Setup Outfitはアバター自体ではなく、衣装のほうで実行してください。\n\nキメラアバターを作る場合は、中のほうのAvatar Descriptorを消して、衣装として扱ってください。",
"setup_outfit.err.multiple_avatar_descriptors": "「{0}」とその親に、複数のavatar descriptorを発見しました。\n\nキメラアバターを作る場合は、中のほうのAvatar Descriptorを消して、衣装として扱ってください。",
"setup_outfit.err.no_avatar_descriptor": "「{0}」の親に、avatar descriptorが見つかりませんでした。衣装のオブジェクトをアバターの中に配置してください。",
"setup_outfit.err.run_on_avatar_itself": "Setup Outfitはアバター自体ではなく、衣装の方で実行してください。\n\nキメラアバターを作る場合は、中の方のAvatar Descriptorを消して、衣装として扱ってください。",
"setup_outfit.err.multiple_avatar_descriptors": "「{0}」とその親で複数のAvatar Descriptorを発見しました。\n\nキメラアバターを作る場合は、中の方のAvatar Descriptorを消して、衣装として扱ってください。",
"setup_outfit.err.no_avatar_descriptor": "「{0}」の親にAvatar Descriptorが見つかりませんでした。衣装のオブジェクトをアバターの中に配置してください。",
"setup_outfit.err.no_animator": "アバターにAnimatorコンポーネントがありません。",
"setup_outfit.err.no_hips": "アバターにHipsボーンがありません。なお、Setup Outfitはヒューマノイドアバター以外には対応していません。",
"setup_outfit.err.no_outfit_hips": "衣装のHipsボーンを発見できませんでした。以下の名前を含むボーンを探しました:",
"move_independently.group-header": "一緒に動かすオブジェクト",
"setup_outfit.err.no_hips": "アバターにHipsボーンがありません。なお、Setup OutfitはHumanoidアバター以外には対応していません。",
"setup_outfit.err.no_outfit_hips": "衣装のHipsボーンを発見できませんでした。アクセサリー等に対してSetup Outfitを試みた場合は、代わりにBone Proxyコンポーネントを使って配置してみてください。\n以下の名前を含むボーンを(大文字・小文字を区別つけずに)探しました:",
"move_independently.group-header": "同時に動かすオブジェクト",
"scale_adjuster.scale": "Scale調整値",
"scale_adjuster.adjust_children": "子オブジェクトの位置を調整",
"world_fixed_object.err.unsupported_platform": "World Fixed Objectこのプラットフォームに対応していません",
"world_fixed_object.err.unsupported_platform": "World Fixed Objectこのプラットフォームに対応していません",
"world_fixed_object.err.unsupported_platform:description": "World Fixed ObjectはAndroid向けビルドには対応していないため、動作しません。",
"ma_info.param_usage_ui.header": "Expressions Parameter 使用状況",
"ma_info.param_usage_ui.other_objects": "このアバター内の他のオブジェクト",
@ -250,8 +253,8 @@
"reactive_object.inverse": "条件を反転",
"reactive_object.material-setter.set-to": "変更先のマテリアル ",
"menuitem.misc.add_toggle": "トグルを追加",
"ro_sim.open_debugger_button": "リアクションデバッグツールを開く",
"ro_sim.window.title": "MA リアクションデバッグツール",
"ro_sim.open_debugger_button": "Reaction デバッガーを開く",
"ro_sim.window.title": "MA Reaction デバッガー",
"ro_sim.header.inspecting": "表示中のオブジェクト",
"ro_sim.header.clear_overrides": "すべてのオーバーライドを解除",
"ro_sim.header.object_state": "オブジェクトのアクティブ状態",
@ -260,16 +263,17 @@
"ro_sim.header.override_gameobject_state": "GameObject のアクティブ状態をオーバーライド",
"ro_sim.header.override_menuitem_state": "MenuItem の選択状態をオーバーライト",
"ro_sim.affected_by.title": "以下のルールに影響されています",
"ro_sim.effect_group.controls_obj_state": "オブジェクトのアクティブ状態を設定する➡",
"ro_sim.effect_group.component": "Reactive Component",
"ro_sim.effect_group.controls_obj_state": "オブジェクトのアクティブ状態を次に設定する➡",
"ro_sim.effect_group.target_component": "コンポーネント",
"ro_sim.effect_group.target_component.tooltip": "Reactive Componentに影響されるコンポーネント",
"ro_sim.effect_group.property": "プロパティ",
"ro_sim.effect_group.property.tooltip": "設定されるプロパティ",
"ro_sim.effect_group.value": "値",
"ro_sim.effect_group.value.tooltip": "上記 Reactive Component が活性状態の時に設定される値",
"ro_sim.effect_group.value.tooltip": "上記の Reactive Component がアクティブな時に設定される値",
"ro_sim.effect_group.material": "マテリアル",
"ro_sim.effect_group.material.tooltip": "上記 Reactive Component が活性状態の時に設定されるマテリアル",
"ro_sim.effect_group.rule_inverted": "このルールの条件反転されています",
"ro_sim.effect_group.material.tooltip": "上記の Reactive Component がアクティブな時に設定されるマテリアル",
"ro_sim.effect_group.rule_inverted": "このルールの条件反転されています",
"ro_sim.effect_group.rule_inverted.tooltip": "このルールは、いずれかの条件が満たされていない場合に適用されます",
"ro_sim.effect_group.conditions": "条件"
}
}

View File

@ -96,7 +96,6 @@
"worldfixed.quest": "이 컴포넌트는 안드로이드 빌드를 대응하지 않습니다.",
"worldfixed.normal": "이 오브젝트는 Constraint 을 사용하여 아바타에 고정하지 않는 이상 월드축에 고정됩니다.",
"fpvisible.normal": "이 오브젝트는 일인칭 시점에서 보일 것입니다.",
"fpvisible.NotUnderHead": "Head 본(Bone) 내의 오브젝트가 아닌 경우 작동하지 않습니다.",
"fpvisible.quest": "이 컴포넌트는 오큘러스 퀘스트 단독 버전과 호환되지 않으며 영향을 미치지 않습니다.",
"fpvisible.InPhysBoneChain": "이 객체는 Physics Bone 체인에 의해 컨트롤되므로 체인의 세부적인 선택이 불가능합니다. 대신 체인의 시작 부분을 선택하세요.",
"blendshape.mesh": "메시",
@ -144,4 +143,4 @@
"control_group.is_synced": "동기화됨",
"setup_outfit.err.unknown": "알 수 없는 오류",
"ma_info.param_usage_ui.other_objects": "이 아바타안의 다른 오브젝트들"
}
}

View File

@ -94,7 +94,6 @@
"worldfixed.quest": "此组件未生效,因为它与 Oculus Quest 不兼容。",
"worldfixed.normal": "当前对象将会固定于世界,除非您使用约束将它绑定在 Avatar 内。",
"fpvisible.normal": "当前对象将在第一人称视角中可见。",
"fpvisible.NotUnderHead": "此组件未生效,因为它需要放置在 Head 骨骼下。",
"fpvisible.quest": "此组件未生效,因为它与 Oculus Quest 不兼容。",
"fpvisible.InPhysBoneChain": "当前对象由 PhysicsBone 控制,可能无法在第一人称视角中可见;请指定 PhysicsBone 链的起点。",
"blendshape.mesh": "网格",
@ -227,7 +226,6 @@
"setup_outfit.err.no_avatar_descriptor": "在 {0} 的父级中找不到 VRC Avatar Descriptor。请确保您的服装放置在 Avatar 内。",
"setup_outfit.err.no_animator": "您的 Avatar 没有动画控制器 (Animator) 组件。",
"setup_outfit.err.no_hips": "您的 Avatar 没有 Hips 骨骼。Setup Outfit 只能用于 humanoid Avatars。",
"setup_outfit.err.no_outfit_hips": "无法识别服装的 Hips已搜索包含以下名称的对象",
"move_independently.group-header": "要一起移动的对象",
"scale_adjuster.scale": "调整比例",
"scale_adjuster.adjust_children": "调整子级的位置",
@ -237,4 +235,4 @@
"ma_info.param_usage_ui.other_objects": "此 Avatar 上的其他对象",
"ma_info.param_usage_ui.free_space": "未使用的参数空间 ({0} bits)",
"ma_info.param_usage_ui.bits_template": "{0} ({1} bits)"
}
}

View File

@ -99,7 +99,6 @@
"worldfixed.quest": "此元件未生效,因為它與 Android 環境不相容。",
"worldfixed.normal": "當前物件將會固定於世界,除非你使用約束將它綁在 Avatar 內。",
"fpvisible.normal": "當前物件將在第一人稱視角中可見。",
"fpvisible.NotUnderHead": "此元件未生效,因為它需要放置在 Head 骨骼下。",
"fpvisible.quest": "此元件未生效,因為它與 Android 環境不相容。",
"fpvisible.InPhysBoneChain": "當前物件由 Physics Bone 控制,可能無法在第一人稱視角中可見;請指定 Physics Bone 鏈的起點。",
"blendshape.mesh": "網格",
@ -239,7 +238,6 @@
"setup_outfit.err.no_avatar_descriptor": "在 {0} 的父級中找不到 VRC Avatar Descriptor。請確保你的服裝放置在 Avatar 裡。",
"setup_outfit.err.no_animator": "你的 Avatar 沒有 Animator 元件。",
"setup_outfit.err.no_hips": "你的 Avatar 沒有 Hips 骨骼。Setup Outfit 只能用於 humanoid Avatars。",
"setup_outfit.err.no_outfit_hips": "識別不到服裝的 Hips 骨骼,已搜尋含有以下名稱的骨骼物件:",
"move_independently.group-header": "要一起移動的物件",
"scale_adjuster.scale": "調整比例",
"scale_adjuster.adjust_children": "調整子級的位置",
@ -253,7 +251,7 @@
"reactive_object.inverse": "反轉條件",
"reactive_object.material-setter.set-to": "將材質設定為:",
"menuitem.misc.add_toggle": "新增開關",
"ro_sim.open_debugger_button": "開啟響應除錯工具",
"ro_sim.open_debugger_button": "開啟 Reaction 除錯工具",
"ro_sim.window.title": "MA 響應除錯工具",
"ro_sim.header.inspecting": "檢視物件",
"ro_sim.header.clear_overrides": "清除所有覆寫",
@ -275,4 +273,4 @@
"ro_sim.effect_group.rule_inverted": "規則的條件已反轉",
"ro_sim.effect_group.rule_inverted.tooltip": "這條規則將在未達成其任一條件時套用。",
"ro_sim.effect_group.conditions": "條件"
}
}

View File

@ -7,6 +7,7 @@ using System.Linq;
using JetBrains.Annotations;
using nadena.dev.modular_avatar.core.menu;
using nadena.dev.modular_avatar.editor.ErrorReporting;
using nadena.dev.ndmf;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using VRC.SDK3.Avatars.ScriptableObjects;
@ -102,7 +103,7 @@ namespace nadena.dev.modular_avatar.core.editor.menu
PushControl(control);
}
if (_menuToInstallerMap.TryGetValue(expMenu, out var installers))
if (_menuToInstallerMap.TryGetValue(ObjectRegistry.GetReference(expMenu), out var installers))
{
foreach (var installer in installers)
{
@ -311,7 +312,7 @@ namespace nadena.dev.modular_avatar.core.editor.menu
// initial validation
if (installer.menuToAppend == null && installer.GetComponent<MenuSource>() == null) return;
var target = installer.installTargetMenu ? (object) installer.installTargetMenu : RootMenuKey;
var target = installer.installTargetMenu ? (object) ObjectRegistry.GetReference(installer.installTargetMenu) : RootMenuKey;
if (!_targetMenuToInstaller.TryGetValue(target, out var targets))
{
targets = new List<ModularAvatarMenuInstaller>();
@ -366,7 +367,7 @@ namespace nadena.dev.modular_avatar.core.editor.menu
}
// Some menu installers may be bound to the root menu _asset_ directly.
if (menuToInstallerFiltered.TryGetValue(menu, out var installers))
if (menuToInstallerFiltered.TryGetValue(ObjectRegistry.GetReference(menu), out var installers))
{
foreach (var installer in installers)
{

View File

@ -236,14 +236,28 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
private bool? ProbeWriteDefaults(AnimatorController controller)
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);
}

View File

@ -45,7 +45,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
var converters = context.AvatarRootObject.GetComponentsInChildren<ModularAvatarConvertConstraints>(true)
.Select(c => c.gameObject)
.ToHashSet(new ObjectIdentityComparer<GameObject>());
.ToHashSet();
if (converters.Count == 0) return;
var constraintGameObjects = context.AvatarRootObject.GetComponentsInChildren<IConstraint>(true)

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
#if MA_VRCSDK3_AVATARS
using VRC.SDK3.Dynamics.PhysBone.Components;
#endif
@ -123,7 +123,7 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
}
catch (MissingComponentException _)
catch (MissingComponentException)
{
// No animator? weird. Move on.
}
@ -194,7 +194,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (!referencedGameObjects.Contains(go))
{
UnityEngine.Object.DestroyImmediate(go);
Object.DestroyImmediate(go);
}
}
}

View File

@ -34,20 +34,12 @@ namespace nadena.dev.modular_avatar.core.editor
hidden = true;
}
var type = AnimatorControllerParameterType.Bool;
if (type != AnimatorControllerParameterType.Float &&
(_component.Control.value > 1.01 || _component.Control.value < -0.01))
type = AnimatorControllerParameterType.Int;
if (Mathf.Abs(Mathf.Round(_component.Control.value) - _component.Control.value) > 0.01f)
type = AnimatorControllerParameterType.Float;
yield return new ProvidedParameter(
name,
ParameterNamespace.Animator,
_component, PluginDefinition.Instance, type)
_component, PluginDefinition.Instance, _component.AnimatorControllerParameterType)
{
ExpandTypeOnConflict = true,
WantSynced = _component.isSynced,
IsHidden = hidden,
DefaultValue = _component.isDefault ? _component.Control.value : null

View File

@ -109,7 +109,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
return uxml != null && EditorStyles.label != null;
}
catch (NullReferenceException _)
catch (NullReferenceException)
{
return false;
}

View File

@ -59,6 +59,13 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
{
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);
seq.Run(VisibleHeadAccessoryPluginPass.Instance);
@ -73,7 +80,6 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
seq.Run(ConstraintConverterPass.Instance);
});
#if MA_VRCSDK3_AVATARS
seq.Run(MenuInstallPluginPass.Instance);
seq.Run(PhysbonesBlockerPluginPass.Instance);
seq.Run("Fixup Expressions Menu", ctx =>
{

View File

@ -1,14 +1,14 @@
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using Object = UnityEngine.Object;
namespace nadena.dev.modular_avatar.core.editor
{
internal class AnimatedProperty
{
public TargetProp TargetProp { get; }
public string ControlParam { get; set; }
public bool alwaysDeleted;
public object currentState;
// Objects which trigger deletion of this shape key.
@ -25,5 +25,30 @@ namespace nadena.dev.modular_avatar.core.editor
TargetProp = key;
this.currentState = currentState;
}
protected bool Equals(AnimatedProperty other)
{
return Equals(currentState, other.currentState) && actionGroups.SequenceEqual(other.actionGroups) &&
TargetProp.Equals(other.TargetProp);
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((AnimatedProperty)obj);
}
public override int GetHashCode()
{
var actionGroupHash = 0;
foreach (var ag in actionGroups)
{
actionGroupHash = HashCode.Combine(actionGroupHash, ag);
}
return HashCode.Combine(currentState, actionGroupHash, TargetProp);
}
}
}

View File

@ -1,11 +1,13 @@
using UnityEngine;
using System;
using UnityEngine;
using Object = UnityEngine.Object;
namespace nadena.dev.modular_avatar.core.editor
{
internal class ControlCondition
{
public string Parameter;
public UnityEngine.Object DebugReference;
public Object DebugReference;
public string DebugName;
public bool IsConstant;
@ -14,5 +16,31 @@ namespace nadena.dev.modular_avatar.core.editor
public bool IsConstantActive => InitiallyActive && IsConstant;
public GameObject ReferenceObject;
protected bool Equals(ControlCondition other)
{
return Parameter == other.Parameter
&& Equals(DebugReference, other.DebugReference)
&& DebugName == other.DebugName
&& IsConstant == other.IsConstant
&& ParameterValueLo.Equals(other.ParameterValueLo)
&& ParameterValueHi.Equals(other.ParameterValueHi)
&& InitialValue.Equals(other.InitialValue)
&& Equals(ReferenceObject, other.ReferenceObject);
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((ControlCondition)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(Parameter, DebugReference, DebugName, IsConstant, ParameterValueLo,
ParameterValueHi, InitialValue, ReferenceObject);
}
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using nadena.dev.ndmf.preview;
using UnityEngine;
using nadena.dev.ndmf.preview.trace;
namespace nadena.dev.modular_avatar.core.editor
{
@ -13,54 +13,118 @@ namespace nadena.dev.modular_avatar.core.editor
public PropCache<Key, Value> Owner;
public Key Key;
public Value Value;
public string DebugName;
public int Generation;
}
private readonly string _debugName;
private readonly Func<ComputeContext, Key, Value> _operator;
private readonly Func<Value, Value, bool> _equalityComparer;
private readonly Dictionary<Key, CacheEntry> _cache = new();
public PropCache(Func<ComputeContext, Key, Value> operatorFunc, Func<Value, Value, bool> equalityComparer = null)
private static int _generation = 0;
public PropCache(string debugName, Func<ComputeContext, Key, Value> operatorFunc,
Func<Value, Value, bool> equalityComparer = null)
{
_debugName = debugName;
_operator = operatorFunc;
_equalityComparer = equalityComparer;
}
private static void InvalidateEntry(CacheEntry entry)
{
var newGenContext = new ComputeContext("PropCache for key " + entry.Key);
var newGenContext = new ComputeContext("PropCache/" + entry.DebugName + " key " + FormatKey(entry.Key) + " gen=" + _generation++);
var newValue = entry.Owner._operator(newGenContext, entry.Key);
if (entry.Owner._equalityComparer != null && entry.Owner._equalityComparer(entry.Value, newValue))
if (!entry.ObserverContext.IsInvalidated && entry.Owner._equalityComparer != null && entry.Owner._equalityComparer(entry.Value, newValue))
{
TraceBuffer.RecordTraceEvent(
"PropCache.InvalidateEntry",
(ev) => $"[PropCache/{ev.Arg0}] Value did not change, retaining result (new gen={ev.Arg1})",
entry.DebugName, entry.Generation
);
entry.GenerateContext = newGenContext;
entry.GenerateContext.InvokeOnInvalidate(entry, InvalidateEntry);
return;
}
var trace = TraceBuffer.RecordTraceEvent(
"PropCache.InvalidateEntry",
(ev) => $"[PropCache/{ev.Arg0}] Value changed, invalidating",
entry.DebugName
);
entry.Owner._cache.Remove(entry.Key);
entry.ObserverContext.Invalidate();
using (trace.Scope()) entry.ObserverContext.Invalidate();
}
public Value Get(ComputeContext context, Key key)
{
TraceEvent ev;
var formattedKey = FormatKey(key);
if (!_cache.TryGetValue(key, out var entry) || entry.GenerateContext.IsInvalidated)
{
var subContext = new ComputeContext("PropCache for key " + key);
var curGen = _generation++;
var subContext = new ComputeContext("PropCache/" + _debugName + " key " + formattedKey + " gen=" + curGen);
entry = new CacheEntry
{
GenerateContext = subContext,
ObserverContext = new ComputeContext("Observer for PropCache for key " + key),
ObserverContext = new ComputeContext("Observer for PropCache/" + _debugName + " for key " +
formattedKey + " gen=" + curGen),
Owner = this,
Key = key,
Value = _operator(subContext, key)
DebugName = _debugName,
Generation = curGen
};
ev = TraceBuffer.RecordTraceEvent(
"PropCache.Get",
(ev) =>
{
var entry_ = (CacheEntry)ev.Arg0;
return
$"[PropCache/{entry_.DebugName}] Cache miss for key {entry_.Key} gen={entry_.Generation} from context {ev.Arg1}";
},
entry, context
);
_cache[key] = entry;
subContext.InvokeOnInvalidate(entry, InvalidateEntry);
using (ev.Scope())
{
entry.Value = _operator(subContext, key);
entry.GenerateContext.InvokeOnInvalidate(entry, InvalidateEntry);
}
}
else
{
ev = TraceBuffer.RecordTraceEvent(
"PropCache.Get",
(ev) =>
{
var entry_ = (CacheEntry) ev.Arg0;
return $"[PropCache/{entry_.DebugName}] Cache hit for key {entry_.Key} gen={entry_.Generation} from context {ev.Arg1}";
},
entry, context
);
}
entry.ObserverContext.Invalidates(context);
return entry.Value;
}
private static string FormatKey(object obj)
{
if (obj is UnityEngine.Object unityObj)
{
return $"{unityObj.GetHashCode()}#{unityObj}";
}
else
{
return "" + obj;
}
}
}
}

View File

@ -1,7 +1,8 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.modular_avatar.animation;
using UnityEngine;
using Object = UnityEngine.Object;
namespace nadena.dev.modular_avatar.core.editor
{
@ -9,8 +10,8 @@ namespace nadena.dev.modular_avatar.core.editor
{
public ReactionRule(TargetProp key, float value)
: this(key, (object)value) { }
public ReactionRule(TargetProp key, UnityEngine.Object value)
public ReactionRule(TargetProp key, Object value)
: this(key, (object)value) { }
private ReactionRule(TargetProp key, object value)
@ -31,13 +32,15 @@ namespace nadena.dev.modular_avatar.core.editor
public bool InitiallyActive =>
((ControllingConditions.Count == 0) || ControllingConditions.All(c => c.InitiallyActive)) ^ Inverted;
public bool IsDelete;
public bool Inverted;
public bool IsConstant => ControllingConditions.Count == 0 || ControllingConditions.All(c => c.IsConstant);
public bool IsConstantOn => IsConstant && InitiallyActive;
public bool IsConstant => ControllingConditions.Count == 0
|| ControllingConditions.All(c => c.IsConstant)
|| ControllingConditions.Any(c => c.IsConstant && !c.InitiallyActive);
public bool IsConstantActive => IsConstant && InitiallyActive ^ Inverted;
public override string ToString()
{
return $"AGK: {TargetProp}={Value}";
@ -55,9 +58,36 @@ namespace nadena.dev.modular_avatar.core.editor
}
else return false;
if (!ControllingConditions.SequenceEqual(other.ControllingConditions)) return false;
if (IsDelete || other.IsDelete) return false;
return true;
}
protected bool Equals(ReactionRule other)
{
return TargetProp.Equals(other.TargetProp)
&& Equals(Value, other.Value)
&& Equals(ControllingObject, other.ControllingObject)
&& ControllingConditions.SequenceEqual(other.ControllingConditions)
&& Inverted == other.Inverted;
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((ReactionRule)obj);
}
public override int GetHashCode()
{
var ccHash = 0;
foreach (var cc in ControllingConditions)
{
ccHash = HashCode.Combine(ccHash, cc);
}
return HashCode.Combine(TargetProp, Value, ControllingObject, ccHash, Inverted);
}
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using nadena.dev.ndmf.preview;
using UnityEngine;
@ -38,6 +39,77 @@ namespace nadena.dev.modular_avatar.core.editor
return param;
}
}
private readonly Dictionary<(SkinnedMeshRenderer, string), HashSet<(SkinnedMeshRenderer, string)>>
_blendshapeSyncMappings = new();
private void LocateBlendshapeSyncs(GameObject root)
{
var components = _computeContext.GetComponentsInChildren<ModularAvatarBlendshapeSync>(root, true);
foreach (var bss in components)
{
var localMesh = _computeContext.GetComponent<SkinnedMeshRenderer>(bss.gameObject);
if (localMesh == null) continue;
foreach (var entry in _computeContext.Observe(bss, bss_ => bss_.Bindings.ToImmutableList(),
Enumerable.SequenceEqual))
{
var src = entry.ReferenceMesh.Get(bss);
if (src == null) continue;
var srcMesh = _computeContext.GetComponent<SkinnedMeshRenderer>(src);
var localBlendshape = entry.LocalBlendshape;
if (string.IsNullOrWhiteSpace(localBlendshape))
{
localBlendshape = entry.Blendshape;
}
var srcBinding = (srcMesh, entry.Blendshape);
var dstBinding = (localMesh, localBlendshape);
if (!_blendshapeSyncMappings.TryGetValue(srcBinding, out var dstSet))
{
dstSet = new HashSet<(SkinnedMeshRenderer, string)>();
_blendshapeSyncMappings[srcBinding] = dstSet;
}
dstSet.Add(dstBinding);
}
}
// For recursive blendshape syncs, we need to precompute the full set of affected blendshapes.
foreach (var (src, dsts) in _blendshapeSyncMappings)
{
var visited = new HashSet<(SkinnedMeshRenderer, string)>();
foreach (var item in Visit(src, visited).ToList())
{
dsts.Add(item);
}
}
IEnumerable<(SkinnedMeshRenderer, string)> Visit(
(SkinnedMeshRenderer, string) key,
HashSet<(SkinnedMeshRenderer, string)> visited
)
{
if (!visited.Add(key)) yield break;
if (_blendshapeSyncMappings.TryGetValue(key, out var children))
{
foreach (var child in children)
{
foreach (var item in Visit(child, visited))
{
yield return item;
}
}
}
yield return key;
}
}
private void BuildConditions(Component controllingComponent, ReactionRule rule)
{
@ -82,7 +154,7 @@ namespace nadena.dev.modular_avatar.core.editor
Parameter = GetActiveSelfProxy(cursor.gameObject),
DebugName = cursor.gameObject.name,
IsConstant = false,
InitialValue = cursor.gameObject.activeSelf ? 1.0f : 0.0f,
InitialValue = _computeContext.Observe(cursor.gameObject, go => go.activeSelf) ? 1.0f : 0.0f,
ParameterValueLo = 0.5f,
ParameterValueHi = float.PositiveInfinity,
ReferenceObject = cursor.gameObject,
@ -124,50 +196,80 @@ namespace nadena.dev.modular_avatar.core.editor
var key = new TargetProp
{
TargetObject = renderer,
PropertyName = "blendShape." + shape.ShapeName,
PropertyName = BlendshapePrefix + shape.ShapeName
};
var currentValue = renderer.GetBlendShapeWeight(shapeId);
var value = shape.ChangeType == ShapeChangeType.Delete ? 100 : shape.Value;
if (!shapeKeys.TryGetValue(key, out var info))
RegisterAction(key, currentValue, value, changer);
if (_blendshapeSyncMappings.TryGetValue((renderer, shape.ShapeName), out var bindings))
{
info = new AnimatedProperty(key, renderer.GetBlendShapeWeight(shapeId));
shapeKeys[key] = info;
// Propagate the new value through any Blendshape Syncs we might have.
// Note that we don't propagate deletes; it's common to e.g. want to delete breasts from the
// base model while retaining outerwear that matches the breast size.
foreach (var binding in bindings)
{
var bindingKey = new TargetProp
{
TargetObject = binding.Item1,
PropertyName = BlendshapePrefix + binding.Item2
};
var bindingRenderer = binding.Item1;
// Add initial state
var agk = new ReactionRule(key, value);
agk.Value = renderer.GetBlendShapeWeight(shapeId);
info.actionGroups.Add(agk);
var bindingMesh = bindingRenderer.sharedMesh;
if (bindingMesh == null) continue;
var bindingShapeIndex = bindingMesh.GetBlendShapeIndex(binding.Item2);
if (bindingShapeIndex < 0) continue;
var bindingInitialState = bindingRenderer.GetBlendShapeWeight(bindingShapeIndex);
RegisterAction(bindingKey, bindingInitialState, value, changer);
}
}
var action = ObjectRule(key, changer, value);
action.Inverted = _computeContext.Observe(changer, c => c.Inverted);
var isCurrentlyActive = changer.gameObject.activeInHierarchy;
if (shape.ChangeType == ShapeChangeType.Delete)
key = new TargetProp
{
action.IsDelete = true;
if (isCurrentlyActive) info.currentState = 100;
TargetObject = renderer,
PropertyName = DeletedShapePrefix + shape.ShapeName
};
info.actionGroups.Add(action); // Never merge
continue;
}
if (changer.gameObject.activeInHierarchy) info.currentState = action.Value;
if (info.actionGroups.Count == 0)
{
info.actionGroups.Add(action);
}
else if (!info.actionGroups[^1].TryMerge(action))
{
info.actionGroups.Add(action);
}
value = shape.ChangeType == ShapeChangeType.Delete ? 1 : 0;
RegisterAction(key, 0, value, changer);
}
}
return shapeKeys;
void RegisterAction(TargetProp key, float currentValue, float value, ModularAvatarShapeChanger changer)
{
if (!shapeKeys.TryGetValue(key, out var info))
{
info = new AnimatedProperty(key, currentValue);
shapeKeys[key] = info;
// Add initial state
var agk = new ReactionRule(key, value);
agk.Value = currentValue;
info.actionGroups.Add(agk);
}
var action = ObjectRule(key, changer, value);
action.Inverted = _computeContext.Observe(changer, c => c.Inverted);
if (changer.gameObject.activeInHierarchy) info.currentState = action.Value;
if (info.actionGroups.Count == 0)
{
info.actionGroups.Add(action);
}
else if (!info.actionGroups[^1].TryMerge(action))
{
info.actionGroups.Add(action);
}
}
}
private void FindMaterialSetters(Dictionary<TargetProp, AnimatedProperty> objectGroups, GameObject root)
@ -228,7 +330,8 @@ namespace nadena.dev.modular_avatar.core.editor
if (!objectGroups.TryGetValue(key, out var group))
{
group = new AnimatedProperty(key, target.activeSelf ? 1 : 0);
var active = _computeContext.Observe(target, t => t.activeSelf);
group = new AnimatedProperty(key, active ? 1 : 0);
objectGroups[key] = group;
}

View File

@ -18,6 +18,11 @@ namespace nadena.dev.modular_avatar.core.editor
private readonly ndmf.BuildContext _context;
private readonly AnimationServicesContext _asc;
private Dictionary<string, float> _simulationInitialStates;
public const string BlendshapePrefix = "blendShape.";
public const string DeletedShapePrefix = "deletedShape.";
public bool OptimizeShapes = true;
public ImmutableDictionary<string, float> ForcePropertyOverrides { get; set; } = ImmutableDictionary<string, float>.Empty;
@ -58,7 +63,6 @@ namespace nadena.dev.modular_avatar.core.editor
{
public Dictionary<TargetProp, AnimatedProperty> Shapes;
public Dictionary<TargetProp, object> InitialStates;
public HashSet<TargetProp> DeletedShapes;
}
private static PropCache<GameObject, AnalysisResult> _analysisCache;
@ -67,7 +71,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (_analysisCache == null)
{
_analysisCache = new PropCache<GameObject, AnalysisResult>((ctx, root) =>
_analysisCache = new PropCache<GameObject, AnalysisResult>("ROAnalyzer", (ctx, root) =>
{
var analysis = new ReactiveObjectAnalyzer(ctx);
analysis.ForcePropertyOverrides = ctx.Observe(ROSimulator.PropertyOverrides, a=>a, (a,b) => false)
@ -86,7 +90,6 @@ namespace nadena.dev.modular_avatar.core.editor
/// </summary>
/// <param name="root">The avatar root</param>
/// <param name="initialStates">A dictionary of target property to initial state (float or UnityEngine.Object)</param>
/// <param name="deletedShapes">A hashset of blendshape properties which are always deleted</param>
/// <returns></returns>
public AnalysisResult Analyze(
GameObject root
@ -98,9 +101,10 @@ namespace nadena.dev.modular_avatar.core.editor
{
result.Shapes = new();
result.InitialStates = new();
result.DeletedShapes = new();
return result;
}
LocateBlendshapeSyncs(root);
Dictionary<TargetProp, AnimatedProperty> shapes = FindShapes(root);
FindObjectToggles(shapes, root);
@ -109,7 +113,7 @@ namespace nadena.dev.modular_avatar.core.editor
ApplyInitialStateOverrides(shapes);
AnalyzeConstants(shapes);
ResolveToggleInitialStates(shapes);
PreprocessShapes(shapes, out result.InitialStates, out result.DeletedShapes);
PreprocessShapes(shapes, out result.InitialStates);
result.Shapes = shapes;
return result;
@ -144,7 +148,7 @@ namespace nadena.dev.modular_avatar.core.editor
HashSet<GameObject> toggledObjects = new();
if (asc == null) return;
foreach (var targetProp in shapes.Keys)
if (targetProp is { TargetObject: GameObject go, PropertyName: "m_IsActive" })
toggledObjects.Add(go);
@ -157,9 +161,7 @@ namespace nadena.dev.modular_avatar.core.editor
if (condition.ReferenceObject != null && !toggledObjects.Contains(condition.ReferenceObject))
condition.IsConstant = asc.AnimationDatabase.ClipsForPath(asc.PathMappings.GetObjectIdentifier(condition.ReferenceObject)).IsEmpty;
var i = 0;
// Remove redundant active conditions.
int retain = 0;
actionGroup.ControllingConditions.RemoveAll(c => c.IsConstant && c.InitiallyActive);
}
@ -167,7 +169,7 @@ namespace nadena.dev.modular_avatar.core.editor
group.actionGroups.RemoveAll(agk => agk.IsConstant && !agk.InitiallyActive);
// Remove all action groups up until the last one where we're always on
var lastAlwaysOnGroup = group.actionGroups.FindLastIndex(ag => ag.IsConstantOn);
var lastAlwaysOnGroup = group.actionGroups.FindLastIndex(ag => ag.IsConstantActive);
if (lastAlwaysOnGroup > 0)
group.actionGroups.RemoveRange(0, lastAlwaysOnGroup - 1);
}
@ -266,39 +268,26 @@ namespace nadena.dev.modular_avatar.core.editor
}
/// <summary>
/// Determine initial state and deleted shapes for all properties
/// Determine initial state for all properties
/// </summary>
/// <param name="shapes"></param>
/// <param name="initialStates"></param>
/// <param name="deletedShapes"></param>
private void PreprocessShapes(Dictionary<TargetProp, AnimatedProperty> shapes, out Dictionary<TargetProp, object> initialStates, out HashSet<TargetProp> deletedShapes)
private void PreprocessShapes(Dictionary<TargetProp, AnimatedProperty> shapes,
out Dictionary<TargetProp, object> initialStates)
{
// For each shapekey, determine 1) if we can just set an initial state and skip and 2) if we can delete the
// corresponding mesh. If we can't, delete ops are merged into the main list of operations.
initialStates = new Dictionary<TargetProp, object>();
deletedShapes = new HashSet<TargetProp>();
foreach (var (key, info) in shapes.ToList())
{
if (info.actionGroups.Count == 0)
{
// never active control; ignore it entirely
shapes.Remove(key);
if (OptimizeShapes) shapes.Remove(key);
continue;
}
var deletions = info.actionGroups.Where(agk => agk.IsDelete).ToList();
if (deletions.Any(d => d.ControllingConditions.All(c => c.IsConstantActive)))
{
// always deleted
shapes.Remove(key);
deletedShapes.Add(key);
continue;
}
// Move deleted shapes to the end of the list, so they override all Set actions
info.actionGroups = info.actionGroups.Where(agk => !agk.IsDelete).Concat(deletions).ToList();
var initialState = info.actionGroups.Where(agk => agk.InitiallyActive)
.Select(agk => agk.Value)
@ -310,7 +299,7 @@ namespace nadena.dev.modular_avatar.core.editor
// If we're now constant-on, we can skip animation generation
if (info.actionGroups[^1].IsConstant)
{
shapes.Remove(key);
if (OptimizeShapes) shapes.Remove(key);
}
}
}

View File

@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using nadena.dev.modular_avatar.animation;
using UnityEditor;
@ -25,6 +24,7 @@ namespace nadena.dev.modular_avatar.core.editor
private HashSet<string> activeProps = new();
private AnimationClip _initialStateClip;
private bool _writeDefaults;
public ReactiveObjectPass(ndmf.BuildContext context)
{
@ -33,14 +33,19 @@ namespace nadena.dev.modular_avatar.core.editor
internal void Execute()
{
// Having a WD OFF layer after WD ON layers can break WD. We match the behavior of the existing states,
// and if mixed, use WD ON to maximize compatibility.
_writeDefaults = MergeAnimatorProcessor.ProbeWriteDefaults(FindFxController().animatorController as AnimatorController) ?? true;
var analysis = new ReactiveObjectAnalyzer(context).Analyze(context.AvatarRootObject);
var shapes = analysis.Shapes;
var initialStates = analysis.InitialStates;
var deletedShapes = analysis.DeletedShapes;
GenerateActiveSelfProxies(shapes);
ProcessMeshDeletion(initialStates, shapes);
ProcessInitialStates(initialStates, shapes);
ProcessInitialAnimatorVariables(shapes);
@ -48,8 +53,6 @@ namespace nadena.dev.modular_avatar.core.editor
{
ProcessShapeKey(groups);
}
ProcessMeshDeletion(deletedShapes);
}
private void GenerateActiveSelfProxies(Dictionary<TargetProp, AnimatedProperty> shapes)
@ -80,7 +83,7 @@ namespace nadena.dev.modular_avatar.core.editor
initialValues[condition.Parameter] = condition.InitialValue;
}
}
}
}
private void ProcessInitialStates(Dictionary<TargetProp, object> initialStates,
Dictionary<TargetProp, AnimatedProperty> shapes)
@ -220,30 +223,65 @@ namespace nadena.dev.modular_avatar.core.editor
#region Mesh processing
private void ProcessMeshDeletion(HashSet<TargetProp> deletedKeys)
private void ProcessMeshDeletion(Dictionary<TargetProp, object> initialStates,
Dictionary<TargetProp, AnimatedProperty> shapes)
{
ImmutableDictionary<SkinnedMeshRenderer, List<TargetProp>> renderers = deletedKeys
.GroupBy(
v => (SkinnedMeshRenderer) v.TargetObject
).ToImmutableDictionary(
g => (SkinnedMeshRenderer) g.Key,
g => g.ToList()
);
var renderers = initialStates
.Where(kvp => kvp.Key.PropertyName.StartsWith(ReactiveObjectAnalyzer.DeletedShapePrefix))
.Where(kvp => kvp.Key.TargetObject is SkinnedMeshRenderer)
.Where(kvp => kvp.Value is float f && f > 0.5f)
// Filter any non-constant keys
.Where(kvp =>
{
if (!shapes.ContainsKey(kvp.Key))
{
// Constant value
return true;
}
foreach (var (renderer, infos) in renderers)
var lastGroup = shapes[kvp.Key].actionGroups.LastOrDefault();
return lastGroup?.IsConstantActive == true && lastGroup.Value is float f && f > 0.5f;
})
.GroupBy(kvp => kvp.Key.TargetObject as SkinnedMeshRenderer)
.Select(grouping => (grouping.Key, grouping.Select(
kvp => kvp.Key.PropertyName.Substring(ReactiveObjectAnalyzer.DeletedShapePrefix.Length)
).ToList()))
.ToList();
foreach (var (renderer, shapeNamesToDelete) in renderers)
{
if (renderer == null) continue;
var mesh = renderer.sharedMesh;
if (mesh == null) continue;
renderer.sharedMesh = RemoveBlendShapeFromMesh.RemoveBlendshapes(
mesh,
infos
.Select(i => mesh.GetBlendShapeIndex(i.PropertyName.Substring("blendShape.".Length)))
.Where(k => k >= 0)
.ToList()
);
var shapesToDelete = shapeNamesToDelete
.Select(shape => mesh.GetBlendShapeIndex(shape))
.Where(k => k >= 0)
.ToList();
renderer.sharedMesh = RemoveBlendShapeFromMesh.RemoveBlendshapes(mesh, shapesToDelete);
foreach (var name in shapeNamesToDelete)
{
// Don't need to animate this anymore...!
shapes.Remove(new TargetProp
{
TargetObject = renderer,
PropertyName = ReactiveObjectAnalyzer.BlendshapePrefix + name
});
shapes.Remove(new TargetProp
{
TargetObject = renderer,
PropertyName = ReactiveObjectAnalyzer.DeletedShapePrefix + name
});
initialStates.Remove(new TargetProp
{
TargetObject = renderer,
PropertyName = ReactiveObjectAnalyzer.BlendshapePrefix + name
});
}
}
}
@ -252,10 +290,6 @@ namespace nadena.dev.modular_avatar.core.editor
private void ProcessShapeKey(AnimatedProperty info)
{
// TODO: prune non-animated keys
// Check if this is non-animated and skip most processing if so
if (info.alwaysDeleted || info.actionGroups[^1].IsConstant) return;
var asm = GenerateStateMachine(info);
ApplyController(asm, "MA Responsive: " + info.TargetProp.TargetObject.name);
}
@ -277,7 +311,7 @@ namespace nadena.dev.modular_avatar.core.editor
var initial = new AnimationClip();
var initialState = new AnimatorState();
initialState.motion = initial;
initialState.writeDefaultValues = false;
initialState.writeDefaultValues = _writeDefaults;
initialState.name = "<default>";
asm.defaultState = initialState;
@ -351,7 +385,7 @@ namespace nadena.dev.modular_avatar.core.editor
state.name = group.ControllingConditions[0].DebugName.Replace(".", "_");
state.motion = clip;
state.writeDefaultValues = false;
state.writeDefaultValues = _writeDefaults;
states.Add(new ChildAnimatorState
{
position = new Vector3(x, y),
@ -527,13 +561,13 @@ namespace nadena.dev.modular_avatar.core.editor
private void ApplyController(AnimatorStateMachine asm, string layerName)
{
var fx = context.AvatarDescriptor.baseAnimationLayers
.FirstOrDefault(l => l.type == VRCAvatarDescriptor.AnimLayerType.FX);
var fx = FindFxController();
if (fx.animatorController == null)
{
throw new InvalidOperationException("No FX layer found");
}
if (!context.IsTemporaryAsset(fx.animatorController))
{
throw new InvalidOperationException("FX layer is not a temporary asset");
@ -569,5 +603,13 @@ namespace nadena.dev.modular_avatar.core.editor
}
).ToArray();
}
private VRCAvatarDescriptor.CustomAnimLayer FindFxController()
{
var fx = context.AvatarDescriptor.baseAnimationLayers
.FirstOrDefault(l => l.type == VRCAvatarDescriptor.AnimLayerType.FX);
return fx;
}
}
}

View File

@ -14,11 +14,11 @@ namespace nadena.dev.modular_avatar.core.editor
protected override void Execute(ndmf.BuildContext context)
{
var hasShapeChanger = context.AvatarRootObject.GetComponentInChildren<ModularAvatarShapeChanger>() != null;
var hasShapeChanger = context.AvatarRootObject.GetComponentInChildren<ModularAvatarShapeChanger>(true) != null;
var hasObjectSwitcher =
context.AvatarRootObject.GetComponentInChildren<ModularAvatarObjectToggle>() != null;
context.AvatarRootObject.GetComponentInChildren<ModularAvatarObjectToggle>(true) != null;
var hasMaterialSetter =
context.AvatarRootObject.GetComponentInChildren<ModularAvatarMaterialSetter>() != null;
context.AvatarRootObject.GetComponentInChildren<ModularAvatarMaterialSetter>(true) != null;
if (hasShapeChanger || hasObjectSwitcher || hasMaterialSetter)
{
var clip = new AnimationClip();

View File

@ -28,7 +28,7 @@ namespace nadena.dev.modular_avatar.core.editor
private const string PREFIX = "m_Materials.Array.data[";
private PropCache<Renderer, ImmutableList<(int, Material)>> _cache = new(
GetMaterialOverridesForRenderer, Enumerable.SequenceEqual
"GetMaterialOverridesForRenderer", GetMaterialOverridesForRenderer, Enumerable.SequenceEqual
);
private static ImmutableList<(int, Material)> GetMaterialOverridesForRenderer(ComputeContext ctx, Renderer r)

View File

@ -87,23 +87,24 @@ namespace nadena.dev.modular_avatar.core.editor
foreach (var (paramName, list) in _mamiByParam)
{
// Assign automatic values first
float defaultValue;
int? defaultValue = null;
if (declaredParams.TryGetValue(paramName, out var p))
{
defaultValue = p.defaultValue;
defaultValue = (int) p.defaultValue;
}
else
{
defaultValue = list.FirstOrDefault(m => m.isDefault && !m.automaticValue)?.Control?.value ?? 0;
var floatDefault = list.FirstOrDefault(m => m.isDefault && !m.automaticValue)?.Control?.value;
if (floatDefault.HasValue) defaultValue = (int) floatDefault.Value;
if (list.Count == 1)
// If we have only a single entry, it's probably an on-off toggle, so we'll implicitly let 0
// be the 'unselected' default value (if this is not default)
defaultValue = 0;
if (list.Count == 1 && list[0].isDefault && list[0].automaticValue)
// If we have only a single entry, it's probably an on-off toggle, so we'll implicitly let 1
// be the 'selected' default value (if this is default and automatic value)
defaultValue = 1;
}
HashSet<int> usedValues = new();
usedValues.Add((int)defaultValue);
if (defaultValue.HasValue) usedValues.Add(defaultValue.Value);
foreach (var item in list)
{
@ -113,24 +114,32 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
if (!defaultValue.HasValue)
{
for (int i = 0; i < 256; i++)
{
if (!usedValues.Contains(i))
{
defaultValue = i;
usedValues.Add(i);
break;
}
}
}
var nextValue = 1;
var canBeBool = true;
var canBeInt = true;
var isSaved = true;
var isSynced = true;
var valueType = VRCExpressionParameters.ValueType.Bool;
var isSaved = false;
var isSynced = false;
foreach (var mami in list)
{
if (mami.automaticValue)
{
if (list.Count == 1)
if (mami.isDefault)
{
mami.Control.value = 1;
}
else if (mami.isDefault)
{
mami.Control.value = defaultValue;
mami.Control.value = defaultValue.GetValueOrDefault();
}
else
{
@ -141,28 +150,24 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
if (Mathf.Abs(mami.Control.value - Mathf.Round(mami.Control.value)) > 0.01f)
canBeInt = false;
else
canBeBool &= mami.Control.value is >= 0 and <= 1;
var newValueType = mami.ExpressionParametersValueType;
if (valueType == VRCExpressionParameters.ValueType.Bool || newValueType == VRCExpressionParameters.ValueType.Float)
{
valueType = newValueType;
}
isSaved &= mami.isSaved;
isSynced &= mami.isSynced;
isSaved |= mami.isSaved;
isSynced |= mami.isSynced;
}
if (!declaredParams.ContainsKey(paramName))
{
VRCExpressionParameters.ValueType newType;
if (canBeBool) newType = VRCExpressionParameters.ValueType.Bool;
else if (canBeInt) newType = VRCExpressionParameters.ValueType.Int;
else newType = VRCExpressionParameters.ValueType.Float;
var newParam = new VRCExpressionParameters.Parameter
{
name = paramName,
valueType = newType,
valueType = valueType,
saved = isSaved,
defaultValue = defaultValue,
defaultValue = defaultValue.GetValueOrDefault(),
networkSynced = isSynced
};
newParameters[paramName] = newParam;
@ -225,8 +230,8 @@ namespace nadena.dev.modular_avatar.core.editor
// we basically force-disable any conditions for nonselected menu items and force-enable any for default
// menu items.
InitialValue = mami.isDefault ? mami.Control.value : -999,
ParameterValueLo = mami.Control.value - 0.5f,
ParameterValueHi = mami.Control.value + 0.5f,
ParameterValueLo = mami.Control.value - 0.005f,
ParameterValueHi = mami.Control.value + 0.005f,
DebugReference = mami,
};
}

View File

@ -128,7 +128,7 @@ namespace nadena.dev.modular_avatar.core.editor
n_nrm[i] = o_nrm[newToOrigVertIndex[i]];
n_tan[i] = o_tan[newToOrigVertIndex[i]];
}
catch (IndexOutOfRangeException e)
catch (IndexOutOfRangeException)
{
throw;
}
@ -250,7 +250,6 @@ namespace nadena.dev.modular_avatar.core.editor
{
List<int> n2o = new List<int>(toRetainVertices.Length);
List<int> o2n = new List<int>(toRetainVertices.Length);
int i = 0;
for (int j = 0; j < toRetainVertices.Length; j++)
{

View File

@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using nadena.dev.modular_avatar.core.editor.Simulator;
using nadena.dev.ndmf.preview;
using UnityEngine;
using Object = UnityEngine.Object;
@ -60,8 +59,8 @@ namespace nadena.dev.modular_avatar.core.editor
}
}
private PropCache<GameObject, ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>>>
_blendshapeCache = new(ShapesForAvatar);
private readonly PropCache<GameObject, ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>>>
_blendshapeCache = new("ShapesForAvatar", ShapesForAvatar);
private static ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>> ShapesForAvatar(ComputeContext context, GameObject avatarRoot)
{
@ -73,9 +72,9 @@ namespace nadena.dev.modular_avatar.core.editor
var analysis = ReactiveObjectAnalyzer.CachedAnalyze(context, avatarRoot);
var shapes = analysis.Shapes;
ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>>.Builder rendererStates =
ImmutableDictionary.CreateBuilder<SkinnedMeshRenderer, ImmutableList<(int, float)>>(
new ObjectIdentityComparer<SkinnedMeshRenderer>()
var rendererStates =
ImmutableDictionary.CreateBuilder<SkinnedMeshRenderer, ImmutableDictionary<int, float>>(
);
var avatarRootTransform = avatarRoot.transform;
@ -84,16 +83,29 @@ namespace nadena.dev.modular_avatar.core.editor
var target = prop.TargetProp;
if (target.TargetObject == null || target.TargetObject is not SkinnedMeshRenderer r) continue;
if (!r.transform.IsChildOf(avatarRootTransform)) continue;
if (!target.PropertyName.StartsWith("blendShape.")) continue;
var isDelete = false;
string shapeName = null;
if (target.PropertyName.StartsWith(ReactiveObjectAnalyzer.DeletedShapePrefix))
{
isDelete = true;
shapeName = target.PropertyName.Substring(ReactiveObjectAnalyzer.DeletedShapePrefix.Length);
}
else if (target.PropertyName.StartsWith(ReactiveObjectAnalyzer.BlendshapePrefix))
{
shapeName = target.PropertyName.Substring(ReactiveObjectAnalyzer.BlendshapePrefix.Length);
}
else
{
continue;
}
var mesh = r.sharedMesh;
if (mesh == null) continue;
var shapeName = target.PropertyName.Substring("blendShape.".Length);
if (!rendererStates.TryGetValue(r, out var states))
{
states = ImmutableList<(int, float)>.Empty;
states = ImmutableDictionary<int, float>.Empty;
rendererStates[r] = states;
}
@ -102,16 +114,32 @@ namespace nadena.dev.modular_avatar.core.editor
var activeRule = prop.actionGroups.LastOrDefault(rule => rule.InitiallyActive);
if (activeRule == null || activeRule.Value is not float value) continue;
if (activeRule.ControllingObject == null) continue; // default value is being inherited
value = Math.Clamp(value, 0, 100);
if (activeRule.IsDelete) value = -1;
states = states.Add((index, value));
if (isDelete)
{
if (value < 0.5f) continue;
value = -1;
}
else
{
if (states.ContainsKey(index))
{
// Delete takes precedence over set in preview
continue;
}
value = Math.Clamp(value, 0, 100);
}
states = states.SetItem(index, value);
rendererStates[r] = states;
}
return rendererStates.ToImmutableDictionary();
return rendererStates.ToImmutableDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Select(shapePair => (shapePair.Key, shapePair.Value)
).ToImmutableList());
}
private IEnumerable<RenderGroup> ShapesToGroups(GameObject avatarRoot, ImmutableDictionary<SkinnedMeshRenderer, ImmutableList<(int, float)>> shapes)
@ -133,12 +161,18 @@ namespace nadena.dev.modular_avatar.core.editor
.ToImmutableList();
}
public Task<IRenderFilterNode> Instantiate(RenderGroup group, IEnumerable<(Renderer, Renderer)> proxyPairs, ComputeContext context)
public async Task<IRenderFilterNode> Instantiate(RenderGroup group, IEnumerable<(Renderer, Renderer)> proxyPairs, ComputeContext context)
{
var shapeValues = group.GetData<StaticContext>();
var node = new Node(shapeValues, proxyPairs.First().Item2 as SkinnedMeshRenderer, _blendshapeCache);
return node.Refresh(proxyPairs, context, 0);
var rv = await node.Refresh(proxyPairs, context, 0);
if (rv == null)
{
context.Invalidate();
}
return node;
}
private class Node : IRenderFilterNode

View File

@ -5,6 +5,7 @@ using System.Linq;
using nadena.dev.modular_avatar.ui;
using nadena.dev.ndmf.localization;
using nadena.dev.ndmf.preview;
using nadena.dev.ndmf.preview.trace;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
@ -14,8 +15,8 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
{
internal class ROSimulator : EditorWindow, IHasCustomMenu
{
public static PublishedValue<ImmutableDictionary<string, float>> PropertyOverrides = new(null);
public static PublishedValue<ImmutableDictionary<string, ModularAvatarMenuItem>> MenuItemOverrides = new(null);
public static PublishedValue<ImmutableDictionary<string, float>> PropertyOverrides = new(null, debugName: "ROSimulator.PropertyOverrides");
public static PublishedValue<ImmutableDictionary<string, ModularAvatarMenuItem>> MenuItemOverrides = new(null, debugName: "ROSimulator.MenuItemOverrides");
internal static string ROOT_PATH = "Packages/nadena.dev.modular-avatar/Editor/ReactiveObjects/Simulator/";
private static string USS = ROOT_PATH + "ROSimulator.uss";
@ -64,27 +65,41 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
private void OnEnable()
{
PropertyOverrides.Value = ImmutableDictionary<string, float>.Empty;
MenuItemOverrides.Value = ImmutableDictionary<string, ModularAvatarMenuItem>.Empty;
EditorApplication.delayCall += LoadUI;
Selection.selectionChanged += SelectionChanged;
is_enabled = true;
EditorApplication.delayCall += () =>
{
PropertyOverrides.Value = ImmutableDictionary<string, float>.Empty;
MenuItemOverrides.Value = ImmutableDictionary<string, ModularAvatarMenuItem>.Empty;
EditorApplication.delayCall += LoadUI;
EditorApplication.update += PeriodicRefresh;
Selection.selectionChanged += SelectionChanged;
is_enabled = true;
};
}
private void OnDisable()
{
Selection.selectionChanged -= SelectionChanged;
is_enabled = false;
// Delay this to ensure that we don't try to change this value from within assembly reload callbacks
// (which generates a noisy exception)
EditorApplication.delayCall += () =>
{
Selection.selectionChanged -= SelectionChanged;
EditorApplication.update -= PeriodicRefresh;
PropertyOverrides.Value = null;
MenuItemOverrides.Value = null;
};
}
private void PeriodicRefresh()
{
if (_refreshPending)
{
RefreshUI();
}
}
private ComputeContext _lastComputeContext;
private GameObject currentSelection;
private GUIStyle lockButtonStyle;
@ -101,11 +116,21 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
_refreshPending = true;
EditorApplication.delayCall += RefreshUI;
// For some reason, this seems to get dropped occasionally, resulting in us being wedged with _refreshPending = true.
// Instead, we'll trigger this from EditorApplication.update...
// EditorApplication.delayCall += RefreshUI;
}
private void UpdatePropertyOverride(string prop, bool? enable, float f_val)
{
var trace = TraceBuffer.RecordTraceEvent(
"ROSimulator.UpdatePropertyOverride",
(ev) => $"Property {ev.Arg0} set to {ev.Arg1}",
arg0: prop,
arg1: enable == null ? "null" : enable.Value ? f_val : 0f
);
using (trace.Scope())
if (enable == null)
{
PropertyOverrides.Value = PropertyOverrides.Value.Remove(prop);
@ -123,6 +148,15 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
private void UpdateMenuItemOverride(string prop, ModularAvatarMenuItem item, bool? value)
{
var trace = TraceBuffer.RecordTraceEvent(
"ROSimulator.UpdateMenuItemOverride",
(ev) => $"MenuItem {ev.Arg0} for prop {ev.Arg1} set to {ev.Arg2}",
arg0: item.gameObject.name,
arg1: prop,
arg2: value == null ? "null" : value.Value ? "true" : "false"
);
using (trace.Scope())
if (value == null)
{
MenuItemOverrides.Value = MenuItemOverrides.Value.Remove(prop);
@ -222,7 +256,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
return;
}
_btn_clear.SetEnabled(!PropertyOverrides.Value.IsEmpty || !MenuItemOverrides.Value.IsEmpty);
_btn_clear.SetEnabled(PropertyOverrides.Value?.IsEmpty == false || MenuItemOverrides.Value?.IsEmpty == false);
e_debugInfo.style.display = DisplayStyle.Flex;
@ -230,6 +264,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
_lastComputeContext.InvokeOnInvalidate(this, MaybeRefreshUI);
var analysis = new ReactiveObjectAnalyzer(_lastComputeContext);
analysis.OptimizeShapes = false;
analysis.ForcePropertyOverrides = PropertyOverrides.Value;
analysis.ForceMenuItems = MenuItemOverrides.Value;
var result = analysis.Analyze(avatar.gameObject);
@ -286,7 +321,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
// these properties in a closure
_menuItemOverrideProperty = prop;
_menuItemOverrideTarget = mami;
soc.OnStateOverrideChanged += MenuItemOverrideChanged;
soc.OnStateOverrideChanged = MenuItemOverrideChanged;
}
private void MenuItemOverrideChanged(bool? obj)
@ -319,7 +354,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
_propertyOverrideProperty = property;
_propertyOverrideTargetValue = targetValue;
soc.OnStateOverrideChanged += OnParameterOverrideChanged;
soc.OnStateOverrideChanged = OnParameterOverrideChanged;
}
private void OnParameterOverrideChanged(bool? state)
@ -437,7 +472,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
var f_set_inactive = effectGroup.Q<VisualElement>("effect__set-inactive");
var f_value = effectGroup.Q<FloatField>("effect__value");
var f_material = effectGroup.Q<ObjectField>("effect__material");
var f_delete = effectGroup.Q("effect__deleted");
var f_delete = effectGroup.Q<TextField>("effect__deleted");
f_target_component.style.display = DisplayStyle.None;
f_target_component.SetEnabled(false);
@ -470,9 +505,10 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
f_property.value = targetProp.PropertyName;
f_property.style.display = DisplayStyle.Flex;
if (reactionRule.IsDelete)
if (reactionRule.TargetProp.PropertyName.StartsWith(ReactiveObjectAnalyzer.DeletedShapePrefix))
{
f_delete.style.display = DisplayStyle.Flex;
f_delete.value = reactionRule.Value is > 0.5f ? "DELETE" : "RETAIN";
} else if (reactionRule.Value is float f)
{
f_value.SetValueWithoutNotify(f);
@ -539,11 +575,11 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
soc.SetWithoutNotify(menuOverride);
}
soc.OnStateOverrideChanged += value => { UpdateMenuItemOverride(prop, mami, value); };
soc.OnStateOverrideChanged = value => { UpdateMenuItemOverride(prop, mami, value); };
}
else
{
soc.OnStateOverrideChanged += value => UpdatePropertyOverride(prop, value, targetValue);
soc.OnStateOverrideChanged = value => UpdatePropertyOverride(prop, value, targetValue);
}
var active = condition.InitiallyActive;

View File

@ -16,7 +16,7 @@ namespace nadena.dev.modular_avatar.core.editor
private static StyleSheet uss;
private Button btn_disable, btn_default, btn_enable;
public event System.Action<bool?> OnStateOverrideChanged;
public System.Action<bool?> OnStateOverrideChanged;
public StateOverrideController()
{

View File

@ -116,6 +116,7 @@ namespace nadena.dev.modular_avatar.core.editor
}
ResolvedParameter.saved |= info.ResolvedParameter.saved;
ResolvedParameter.localOnly &= info.ResolvedParameter.localOnly;
}
public void MergeChild(ParameterInfo info)
@ -305,7 +306,7 @@ namespace nadena.dev.modular_avatar.core.editor
newParameter.defaultValue = info.ResolvedParameter.HasDefaultValue ? info.ResolvedParameter.defaultValue : parameter.defaultValue;
newParameter.name = parameter.name;
newParameter.valueType = parameter.valueType;
newParameter.networkSynced = parameter.networkSynced;
newParameter.networkSynced = parameter.networkSynced || !info.ResolvedParameter.localOnly;
newParameter.saved = parameter.saved || info.ResolvedParameter.saved;
return newParameter;

View File

@ -31,7 +31,7 @@ namespace nadena.dev.modular_avatar.core.editor.ScaleAdjuster
public BoneState parentHint;
}
private Dictionary<Component, BoneState> _bones = new(new ObjectIdentityComparer<Component>());
private readonly Dictionary<Component, BoneState> _bones = new();
//private List<BoneState> _states = new List<BoneState>();
public void Clear()

View File

@ -59,29 +59,29 @@ namespace nadena.dev.modular_avatar.core.editor
var scaleAdjusters = ctx.GetComponentsByType<ModularAvatarScaleAdjuster>();
var avatarToRenderer =
new Dictionary<GameObject, HashSet<Renderer>>(new ObjectIdentityComparer<GameObject>());
new Dictionary<GameObject, HashSet<Renderer>>();
foreach (var adjuster in scaleAdjusters)
foreach (var root in ctx.GetAvatarRoots())
{
if (adjuster == null) continue;
// Find parent object
// TODO: Reactive helper
var root = FindAvatarRootObserving(ctx, adjuster.gameObject);
if (root == null) continue;
if (!avatarToRenderer.TryGetValue(root, out var renderers))
if (ctx.GetComponentsInChildren<ModularAvatarScaleAdjuster>(root, true).Length == 0)
{
renderers = new HashSet<Renderer>(new ObjectIdentityComparer<Renderer>());
avatarToRenderer.Add(root, renderers);
continue;
}
foreach (var renderer in root.GetComponentsInChildren<Renderer>())
{
// For now, the preview system only supports MeshRenderer and SkinnedMeshRenderer
if (renderer is not MeshRenderer and not SkinnedMeshRenderer) continue;
if (ctx.GetAvatarRoot(root?.transform?.parent?.gameObject) != null)
{
continue; // nested avatar descriptor
}
renderers.Add(renderer);
}
var renderers = new HashSet<Renderer>();
avatarToRenderer.Add(root, renderers);
foreach (var renderer in root.GetComponentsInChildren<Renderer>())
{
// For now, the preview system only supports MeshRenderer and SkinnedMeshRenderer
if (renderer is not MeshRenderer and not SkinnedMeshRenderer) continue;
renderers.Add(renderer);
}
}
@ -97,6 +97,8 @@ namespace nadena.dev.modular_avatar.core.editor
internal class ScaleAdjusterPreviewNode : IRenderFilterNode
{
private readonly HashSet<Transform> _knownProxies = new();
private readonly GameObject SourceAvatarRoot;
private readonly GameObject VirtualAvatarRoot;
@ -110,12 +112,12 @@ namespace nadena.dev.modular_avatar.core.editor
private readonly Dictionary<Transform, Transform> _shadowBoneMap;
// Map from bones found in initial proxy state to shadow bones (with scale adjuster bones substituted)
private readonly Dictionary<Transform, Transform> _finalBonesMap = new(new ObjectIdentityComparer<Transform>());
private readonly Dictionary<Transform, Transform> _finalBonesMap = new();
private readonly Dictionary<ModularAvatarScaleAdjuster, Transform> _scaleAdjusters =
new(new ObjectIdentityComparer<ModularAvatarScaleAdjuster>());
new();
private Dictionary<Renderer, Transform[]> _rendererBoneStates = new(new ObjectIdentityComparer<Renderer>());
private Dictionary<Renderer, Transform[]> _rendererBoneStates = new();
public ScaleAdjusterPreviewNode(ComputeContext context, RenderGroup group,
IEnumerable<(Renderer, Renderer)> proxyPairs)
@ -129,28 +131,37 @@ namespace nadena.dev.modular_avatar.core.editor
var priorScene = SceneManager.GetActiveScene();
var bonesSet = GetSourceBonesSet(context, proxyPairList);
var bones = bonesSet.ToArray();
var bones = bonesSet.OrderBy(k => k.gameObject.name).ToArray();
Transform[] sourceBones;
Transform[] destinationBones;
try
{
SceneManager.SetActiveScene(scene);
VirtualAvatarRoot = new GameObject(avatarRoot.name + " [ScaleAdjuster]");
_srcBones = new TransformAccessArray(bones.ToArray());
destinationBones = CreateShadowBones(bones);
_shadowBoneMap = CreateShadowBones(bones);
sourceBones = new Transform[_shadowBoneMap.Count];
destinationBones = new Transform[_shadowBoneMap.Count];
var i = 0;
foreach (var (src, dst) in _shadowBoneMap)
{
sourceBones[i] = src;
destinationBones[i] = dst;
i++;
}
}
finally
{
SceneManager.SetActiveScene(priorScene);
}
_shadowBoneMap = new Dictionary<Transform, Transform>(new ObjectIdentityComparer<Transform>());
for (var i = 0; i < bones.Length; i++) _shadowBoneMap[bones[i]] = destinationBones[i];
_srcBones = new TransformAccessArray(sourceBones);
_dstBones = new TransformAccessArray(destinationBones);
_boneIsValid = new NativeArray<bool>(bones.Length, Allocator.Persistent);
_boneStates = new NativeArray<TransformState>(bones.Length, Allocator.Persistent);
_boneIsValid = new NativeArray<bool>(sourceBones.Length, Allocator.Persistent);
_boneStates = new NativeArray<TransformState>(sourceBones.Length, Allocator.Persistent);
FindScaleAdjusters(context);
TransferBoneStates();
@ -158,7 +169,7 @@ namespace nadena.dev.modular_avatar.core.editor
private HashSet<Transform> GetSourceBonesSet(ComputeContext context, List<(Renderer, Renderer)> proxyPairs)
{
var bonesSet = new HashSet<Transform>(new ObjectIdentityComparer<Transform>());
var bonesSet = new HashSet<Transform>();
foreach (var (_, r) in proxyPairs)
{
if (r == null) continue;
@ -170,8 +181,12 @@ namespace nadena.dev.modular_avatar.core.editor
if (smr == null) continue;
foreach (var b in context.Observe(smr, smr_ => smr_.bones, Enumerable.SequenceEqual))
{
if (b != null)
{
bonesSet.Add(b);
}
}
}
return bonesSet;
@ -182,10 +197,14 @@ namespace nadena.dev.modular_avatar.core.editor
_finalBonesMap.Clear();
foreach (var (sa, proxy) in _scaleAdjusters.ToList())
{
// Note: We leak the proxy here, as destroying it can cause visual artifacts. They'll eventually get
// cleaned up whenever the pipeline is fully reset, or when the scene is reloaded.
if (sa == null)
{
_scaleAdjusters.Remove(sa);
}
}
_scaleAdjusters.Clear();
@ -213,6 +232,9 @@ namespace nadena.dev.modular_avatar.core.editor
{
if (SourceAvatarRoot == null) return Task.FromResult<IRenderFilterNode>(null);
// Clean any destroyed objects out of _knownProxies to avoid growing this set indefinitely
_knownProxies.RemoveWhere(p => p == null);
var proxyPairList = proxyPairs.ToList();
if (!GetSourceBonesSet(context, proxyPairList).SetEquals(_shadowBoneMap.Keys))
@ -224,14 +246,13 @@ namespace nadena.dev.modular_avatar.core.editor
return Task.FromResult<IRenderFilterNode>(this);
}
private Transform[] CreateShadowBones(Transform[] srcBones)
private Dictionary<Transform, Transform> CreateShadowBones(Transform[] srcBones)
{
var srcToDst = new Dictionary<Transform, Transform>(new ObjectIdentityComparer<Transform>());
var srcToDst = new Dictionary<Transform, Transform>();
var dstBones = new Transform[srcBones.Length];
for (var i = 0; i < srcBones.Length; i++) dstBones[i] = GetShadowBone(srcBones[i]);
for (var i = 0; i < srcBones.Length; i++) GetShadowBone(srcBones[i]);
return dstBones;
return srcToDst;
Transform GetShadowBone(Transform srcBone)
{
@ -278,12 +299,14 @@ namespace nadena.dev.modular_avatar.core.editor
BoneIsValid[index] = transform.isValid;
if (transform.isValid)
{
BoneStates[index] = new TransformState
{
localPosition = transform.position,
localRotation = transform.rotation,
localScale = transform.localScale
};
}
}
}
@ -321,7 +344,15 @@ namespace nadena.dev.modular_avatar.core.editor
if (proxy == null) return;
var curParent = proxy.transform.parent ?? original.transform.parent;
if (_finalBonesMap.TryGetValue(curParent, out var newRoot)) proxy.transform.SetParent(newRoot, false);
if (curParent != null && _finalBonesMap.TryGetValue(curParent, out var newRoot))
{
// We need to remember this proxy so we can avoid destroying it when we destroy VirtualAvatarRoot
// in Dispose
_knownProxies.Add(proxy.transform);
proxy.transform.SetParent(newRoot, false);
}
var smr = proxy as SkinnedMeshRenderer;
if (smr == null) return;
@ -333,6 +364,14 @@ namespace nadena.dev.modular_avatar.core.editor
public void Dispose()
{
foreach (var proxy in _knownProxies)
{
if (proxy != null && proxy.IsChildOf(VirtualAvatarRoot.transform))
{
proxy.transform.SetParent(null, false);
}
}
Object.DestroyImmediate(VirtualAvatarRoot);
_srcBones.Dispose();

View File

@ -263,7 +263,7 @@ namespace nadena.dev.modular_avatar.core.editor
return outfitHumanoidBones;
}
private static void FixAPose(GameObject avatarRoot, Transform outfitArmature)
internal static void FixAPose(GameObject avatarRoot, Transform outfitArmature, bool strictMode = true)
{
var mergeArmature = outfitArmature.GetComponent<ModularAvatarMergeArmature>();
if (mergeArmature == null) return;
@ -283,7 +283,7 @@ namespace nadena.dev.modular_avatar.core.editor
{
var lowerArm = (HumanBodyBones)((int)arm + 2);
// check if the rotation of the arm differs, but distances and origin point are the same
// check if the rotation of the arm differs(, but distances and origin point are the same when strictMode)
var avatarArm = rootAnimator.GetBoneTransform(arm);
var outfitArm = avatarToOutfit(avatarArm);
@ -293,22 +293,27 @@ namespace nadena.dev.modular_avatar.core.editor
if (outfitArm == null) return;
if (outfitLowerArm == null) return;
if ((avatarArm.position - outfitArm.position).magnitude > 0.001f) return;
if (strictMode)
{
if ((avatarArm.position - outfitArm.position).magnitude > 0.001f) return;
// check relative distance to lower arm as well
var avatarArmLength = (avatarLowerArm.position - avatarArm.position).magnitude;
var outfitArmLength = (outfitLowerArm.position - outfitArm.position).magnitude;
// check relative distance to lower arm as well
var avatarArmLength = (avatarLowerArm.position - avatarArm.position).magnitude;
var outfitArmLength = (outfitLowerArm.position - outfitArm.position).magnitude;
if (Mathf.Abs(avatarArmLength - outfitArmLength) > 0.001f) return;
if (Mathf.Abs(avatarArmLength - outfitArmLength) > 0.001f) return;
} else {
if (Vector3.Dot((outfitLowerArm.position - outfitArm.position).normalized, (avatarLowerArm.position - avatarArm.position).normalized) > 0.999f) return;
}
// Rotate the outfit arm to ensure these two points match.
// Rotate the outfit arm to ensure these two bone orientations match.
Undo.RecordObject(outfitArm, "Convert A/T Pose");
var relRot = Quaternion.FromToRotation(
outfitLowerArm.position - outfitArm.position,
avatarLowerArm.position - avatarArm.position
);
outfitArm.rotation = relRot * outfitArm.rotation;
PrefabUtility.RecordPrefabInstancePropertyModifications(outfitArm);
EditorUtility.SetDirty(outfitArm);
}
Transform avatarToOutfit(Transform avBone)
@ -419,7 +424,13 @@ namespace nadena.dev.modular_avatar.core.editor
private static bool ValidateSetupOutfit(GameObject gameObj)
{
Object obj;
if (gameObj == null)
{
errorHeader = S("setup_outfit.err.header.notarget");
errorMessageGroups = new string[] { S("setup_outfit.err.no_selection") };
return false;
}
errorHeader = S_f("setup_outfit.err.header", gameObj.name);
var xform = gameObj.transform;

View File

@ -33,4 +33,4 @@ For more information, check out the [documentation](https://m-a.nadena.dev).
* 部分的なアニメーターを親に統合することで、様々のギミックの実装を簡単にします。
* 他にもいろいろ!
詳しくは[ドキュメンテーションページにご参照ください](https://modular-avatar.nadena.dev/ja/).
詳しくは[ドキュメンテーションページをご覧ください](https://modular-avatar.nadena.dev/ja/).

View File

@ -72,7 +72,9 @@ namespace nadena.dev.modular_avatar.core
private void Rebind()
{
#if UNITY_EDITOR
if (this == null) return;
if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this)) return;
_editorBindings = new List<EditorBlendshapeBinding>();
@ -110,6 +112,7 @@ namespace nadena.dev.modular_avatar.core
}
Update();
#endif
}
private void Update()

View File

@ -205,6 +205,13 @@ namespace nadena.dev.modular_avatar.core
Transform iter = newTarget;
if (newTarget == null)
{
boneReference = HumanBodyBones.LastBone;
subPath = null;
return;
}
if (newTarget == avatarTransform)
{
boneReference = HumanBodyBones.LastBone;
@ -230,4 +237,4 @@ namespace nadena.dev.modular_avatar.core
_targetCache = newTarget;
}
}
}
}

View File

@ -149,6 +149,38 @@ namespace nadena.dev.modular_avatar.core
if (control.subParameters.Length > maxSubParams)
control.subParameters = control.subParameters.Take(maxSubParams).ToArray();
}
internal VRCExpressionParameters.ValueType ExpressionParametersValueType
{
get
{
// 0, 1
var type = VRCExpressionParameters.ValueType.Bool;
// 2, 3, ..., (255)
if (Control.value > 1)
{
type = VRCExpressionParameters.ValueType.Int;
}
// (-1.0), ..., -0.1, 0.1, ..., 0.9
if (Control.value < 0 || Mathf.Abs(Control.value - Mathf.Round(Control.value)) > 0.01f)
{
type = VRCExpressionParameters.ValueType.Float;
}
return type;
}
}
internal AnimatorControllerParameterType AnimatorControllerParameterType
=> ExpressionParametersValueType switch
{
VRCExpressionParameters.ValueType.Bool => AnimatorControllerParameterType.Bool,
VRCExpressionParameters.ValueType.Int => AnimatorControllerParameterType.Int,
VRCExpressionParameters.ValueType.Float => AnimatorControllerParameterType.Float,
_ => 0,
};
}
}

View File

@ -38,7 +38,7 @@ namespace nadena.dev.modular_avatar.core
}
[AddComponentMenu("Modular Avatar/MA Material Setter")]
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/material-setter?lang=auto")]
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/reaction/material-setter?lang=auto")]
public class ModularAvatarMaterialSetter : ReactiveComponent, IHaveObjReferences
{
[SerializeField] private List<MaterialSwitchObject> m_objects = new();

View File

@ -21,7 +21,7 @@ namespace nadena.dev.modular_avatar.core
}
[AddComponentMenu("Modular Avatar/MA Object Toggle")]
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/object-toggle?lang=auto")]
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/reaction/object-toggle?lang=auto")]
public class ModularAvatarObjectToggle : ReactiveComponent, IHaveObjReferences
{
[SerializeField] private List<ToggledObject> m_objects = new();

View File

@ -57,14 +57,9 @@ namespace nadena.dev.modular_avatar.core
}
[AddComponentMenu("Modular Avatar/MA Shape Changer")]
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/shape-changer?lang=auto")]
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/reaction/shape-changer?lang=auto")]
public class ModularAvatarShapeChanger : ReactiveComponent, IHaveObjReferences
{
// Migration field to help with 1.10-beta series avatar data. Since this was never in a released version of MA,
// this migration support will be removed in 1.10.0.
[SerializeField] [FormerlySerializedAs("targetRenderer")] [HideInInspector]
private AvatarObjectReference m_targetRenderer = new();
[SerializeField] [FormerlySerializedAs("Shapes")]
private List<ChangedShape> m_shapes = new();
@ -82,40 +77,6 @@ namespace nadena.dev.modular_avatar.core
}
}
private void OnEnable()
{
MigrateTargetRenderer();
}
protected override void OnValidate()
{
base.OnValidate();
MigrateTargetRenderer();
}
// Migrate early versions of MASC (from Modular Avatar 1.10.0-beta.4 or earlier) to the new format, where the
// target renderer is stored separately for each shape.
// This logic will be removed in 1.10.0.
private void MigrateTargetRenderer()
{
// Note: This method runs in the context of OnValidate, and therefore cannot touch any other unity objects.
if (!string.IsNullOrEmpty(m_targetRenderer.referencePath) || m_targetRenderer.targetObject != null)
{
foreach (var shape in m_shapes)
{
if (shape.Object == null) shape.Object = new AvatarObjectReference();
if (string.IsNullOrEmpty(shape.Object.referencePath) && shape.Object.targetObject == null)
{
shape.Object.referencePath = m_targetRenderer.referencePath;
shape.Object.targetObject = m_targetRenderer.targetObject;
}
}
m_targetRenderer.referencePath = null;
m_targetRenderer.targetObject = null;
}
}
public IEnumerable<AvatarObjectReference> GetObjectReferences()
{
foreach (var shape in m_shapes)

View File

@ -1,23 +0,0 @@
#region
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#endregion
namespace nadena.dev.modular_avatar
{
internal class ObjectIdentityComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
return (object)x == (object)y;
}
public int GetHashCode(T obj)
{
if (obj == null) return 0;
return RuntimeHelpers.GetHashCode(obj);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: e674cbd75db24fb2b238674cd7010edb
timeCreated: 1709448428

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.ndmf;
using NUnit.Framework;
@ -28,6 +29,11 @@ namespace modular_avatar_tests
return transformMaskElements.SequenceEqual(other.transformMaskElements);
}
public override int GetHashCode()
{
return HashCode.Combine(humanoidMaskElements, transformMaskElements);
}
public static ExtractedMask FromAvatarMask(AvatarMask mask)
{
var so = new SerializedObject(mask);

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 130af01284a51c24f99eeb52361a81fb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,72 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1102 &-9046052599989551153
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: proxy_tpose
m_Speed: 1
m_CycleOffset: 0
m_Transitions: []
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: 7400000, guid: 645a7092829eff9478fb3a29f959a6fa, type: 2}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!91 &9100000
AnimatorController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: New Animator Controller
serializedVersion: 5
m_AnimatorParameters: []
m_AnimatorLayers:
- serializedVersion: 5
m_Name: proxy
m_StateMachine: {fileID: 1953483892909110087}
m_Mask: {fileID: 0}
m_Motions: []
m_Behaviours: []
m_BlendingMode: 0
m_SyncedLayerIndex: -1
m_DefaultWeight: 0
m_IKPass: 0
m_SyncedLayerAffectsTiming: 0
m_Controller: {fileID: 9100000}
--- !u!1107 &1953483892909110087
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: proxy
m_ChildStates:
- serializedVersion: 1
m_State: {fileID: -9046052599989551153}
m_Position: {x: 360, y: 110, z: 0}
m_ChildStateMachines: []
m_AnyStateTransitions: []
m_EntryTransitions: []
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: -9046052599989551153}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 60603c8e68ac87447b02be4e3af6a7bd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 9100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
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.MergeAnimatorTests.ProxyAnim
{
public class ProxyAnimTest : TestBase
{
[Test]
public void whenProxyAnimIsMerged_itIsNotReplaced()
{
var root = CreatePrefab("ProxyAnimTest.prefab");
var originalAnimator = (AnimatorController) root.GetComponentInChildren<ModularAvatarMergeAnimator>().animator;
var originalClip = originalAnimator.layers[0].stateMachine.states[0].state.motion as AnimationClip;
AvatarProcessor.ProcessAvatar(root);
var resultLayer = findFxLayer(root, "proxy");
var resultClip = resultLayer.stateMachine.states[0].state.motion as AnimationClip;
Assert.AreEqual(originalClip, resultClip);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2cfba229fa1e4c718f5cb5dd579d3319
timeCreated: 1728166108

View File

@ -0,0 +1,378 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &603567390109878184
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2557688482630581002}
- component: {fileID: 2868037606074871127}
- component: {fileID: 1028276594299388724}
- component: {fileID: 223023489903813839}
m_Layer: 0
m_Name: ProxyAnimTest
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2557688482630581002
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 603567390109878184}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 1.1618705, y: 1.0265146, z: 1.8807894}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 2189711873526373063}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!95 &2868037606074871127
Animator:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 603567390109878184}
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!114 &1028276594299388724
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 603567390109878184}
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: 0
expressionsMenu: {fileID: 0}
expressionParameters: {fileID: 0}
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 &223023489903813839
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 603567390109878184}
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!1 &1425973809379277617
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2189711873526373063}
- component: {fileID: 3380859768730913427}
m_Layer: 0
m_Name: GameObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2189711873526373063
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1425973809379277617}
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: 1
m_Children: []
m_Father: {fileID: 2557688482630581002}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &3380859768730913427
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1425973809379277617}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1bb122659f724ebf85fe095ac02dc339, type: 3}
m_Name:
m_EditorClassIdentifier:
animator: {fileID: 9100000, guid: 60603c8e68ac87447b02be4e3af6a7bd, type: 2}
layerType: 5
deleteAttachedAnimator: 1
pathMode: 0
matchAvatarWriteDefaults: 0
relativePathRoot:
referencePath:
targetObject: {fileID: 0}
layerPriority: 0

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a77dd3314cc88714bb6e9f1ad014cfc8
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 77aba4362cec4cea91ee64c6e640b6b2
timeCreated: 1726436552

View File

@ -0,0 +1,71 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using nadena.dev.modular_avatar.core.editor;
using nadena.dev.ndmf.preview;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace UnitTests.PropCacheTest
{
public class PropCacheTest
{
[UnityTest]
public IEnumerator TestCacheInvalidation()
{
int seq = 0;
Dictionary<int, List<WeakReference<ComputeContext>>> invalidators = new();
PropCache<int,int> cache = new PropCache<int, int>("test", (ctx, k) =>
{
Debug.Log("Generating value for " + k);
if (!invalidators.TryGetValue(k, out var list))
{
list = new List<WeakReference<ComputeContext>>();
invalidators[k] = list;
}
list.Add(new WeakReference<ComputeContext>(ctx));
return (k * 10) + seq++;
});
ComputeContext ctx = new ComputeContext("c1");
int val = cache.Get(ctx, 1);
Assert.AreEqual(10, val);
ComputeContext ctx2 = new ComputeContext("c2");
val = cache.Get(ctx2, 1);
Assert.AreEqual(10, val);
invalidators[1][0].TryGetTarget(out var target);
target?.Invalidate();
Debug.Log("Pre-flush");
ComputeContext.FlushInvalidates();
Debug.Log("Mid-flush");
ComputeContext.FlushInvalidates();
Debug.Log("Post-flush");
// Task processing can happen asynchronously.
int limit = 10;
while (limit-- > 0 && (!ctx.IsInvalidated || !ctx2.IsInvalidated))
{
Debug.Log("Waiting for invalidation: " + limit);
Thread.Sleep(100);
}
Assert.IsTrue(ctx.IsInvalidated);
Assert.IsTrue(ctx2.IsInvalidated);
val = cache.Get(ctx, 1);
Assert.AreEqual(12, val);
yield return null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6891e8d136c942878bca9b50b5c58ec9
timeCreated: 1726436558

View File

@ -0,0 +1,61 @@
using System.Linq;
using modular_avatar_tests;
using nadena.dev.modular_avatar.core.editor;
using NUnit.Framework;
using UnityEngine;
namespace UnitTests.ReactiveComponent
{
public class BlendshapeSyncTest : TestBase
{
[Test]
public void blendshapeSync_propagatesThroughMeshes()
{
var root = CreatePrefab("BlendshapeSyncTest.prefab");
var analysis = new ReactiveObjectAnalyzer().Analyze(root);
var m1 = analysis.Shapes[new TargetProp()
{
TargetObject = root.transform.Find("m1").GetComponent<SkinnedMeshRenderer>(),
PropertyName = "blendShape.bottom"
}];
var m2 = analysis.Shapes[new TargetProp()
{
TargetObject = root.transform.Find("m2").GetComponent<SkinnedMeshRenderer>(),
PropertyName = "blendShape.bottom"
}];
var m3 = analysis.Shapes[new TargetProp()
{
TargetObject = root.transform.Find("m3").GetComponent<SkinnedMeshRenderer>(),
PropertyName = "blendShape.top"
}];
Assert.IsTrue(analysis.Shapes.ContainsKey(new TargetProp()
{
TargetObject = root.transform.Find("m1").GetComponent<SkinnedMeshRenderer>(),
PropertyName = "deletedShape.bottom"
}));
Assert.AreEqual(4, analysis.Shapes.Count);
foreach (var ag in m1.actionGroups)
{
ag.TargetProp = new TargetProp();
}
foreach (var ag in m2.actionGroups)
{
ag.TargetProp = new TargetProp();
}
foreach (var ag in m3.actionGroups)
{
ag.TargetProp = new TargetProp();
}
Assert.AreEqual(m2.actionGroups, m1.actionGroups);
Assert.AreEqual(m3.actionGroups, m1.actionGroups);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f74d4e816d0247159d977e21ebc57458
timeCreated: 1728168066

View File

@ -0,0 +1,799 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4750389987621451750
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3724328547737935111}
- component: {fileID: 6141552197888553193}
- component: {fileID: 866105862211182099}
- component: {fileID: 5955186656294850035}
m_Layer: 0
m_Name: New Toggle
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3724328547737935111
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4750389987621451750}
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: 1
m_Children: []
m_Father: {fileID: 295226914695240947}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6141552197888553193
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4750389987621451750}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3b29d45007c5493d926d2cd45a489529, type: 3}
m_Name:
m_EditorClassIdentifier:
Control:
name: New Toggle
icon: {fileID: 0}
type: 102
parameter:
name:
value: 1
style: 0
subMenu: {fileID: 0}
subParameters: []
labels: []
MenuSource: 1
menuSource_otherObjectChildren: {fileID: 0}
isSynced: 1
isSaved: 1
isDefault: 1
automaticValue: 1
--- !u!114 &866105862211182099
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4750389987621451750}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7ef83cb0c23d4d7c9d41021e544a1978, type: 3}
m_Name:
m_EditorClassIdentifier:
menuToAppend: {fileID: 0}
installTargetMenu: {fileID: 0}
--- !u!114 &5955186656294850035
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4750389987621451750}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 0
m_shapes:
- Object:
referencePath: m1
targetObject: {fileID: 7767603723203631002}
ShapeName: bottom
ChangeType: 1
Value: 100
--- !u!1 &7303978391080220300
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 295226914695240947}
- component: {fileID: 1177795503533185300}
- component: {fileID: 7792711537747161192}
- component: {fileID: 8538029171187693289}
m_Layer: 0
m_Name: BlendshapeSyncTest
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &295226914695240947
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7303978391080220300}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 1.1618705, y: 1.0265146, z: 1.8807894}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 6955742288500591904}
- {fileID: 2035853062413530075}
- {fileID: 2118711245520540949}
- {fileID: 3724328547737935111}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!95 &1177795503533185300
Animator:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7303978391080220300}
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!114 &7792711537747161192
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7303978391080220300}
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: 0
expressionsMenu: {fileID: 0}
expressionParameters: {fileID: 0}
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 &8538029171187693289
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7303978391080220300}
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!1001 &1940371780088492798
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 295226914695240947}
m_Modifications:
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.x
value: 5.33
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071067
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.x
value: -0.7071068
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_ConstrainProportionsScale
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_DirtyAABB
value: 0
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.z
value: 2
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_BlendShapeWeights.Array.data[0]
value: 0
objectReference: {fileID: 0}
- target: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_Name
value: m3
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
insertIndex: -1
addedObject: {fileID: 4462016549056391230}
m_SourcePrefab: {fileID: 100100000, guid: fe5b76dae94c07345b74d51e9a9a8440, type: 3}
--- !u!1 &1597929094539956143 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
m_PrefabInstance: {fileID: 1940371780088492798}
m_PrefabAsset: {fileID: 0}
--- !u!114 &4462016549056391230
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1597929094539956143}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6fd7cab7d93b403280f2f9da978d8a4f, type: 3}
m_Name:
m_EditorClassIdentifier:
Bindings:
- ReferenceMesh:
referencePath: m2
targetObject: {fileID: 1660453041666320737}
Blendshape: bottom
LocalBlendshape: top
--- !u!4 &2118711245520540949 stripped
Transform:
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
m_PrefabInstance: {fileID: 1940371780088492798}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &2002541556693849136
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 295226914695240947}
m_Modifications:
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.x
value: 2.75
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071067
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.x
value: -0.7071068
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_ConstrainProportionsScale
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_DirtyAABB
value: 0
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.z
value: 2
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_BlendShapeWeights.Array.data[0]
value: 0
objectReference: {fileID: 0}
- target: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_Name
value: m2
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
insertIndex: -1
addedObject: {fileID: 1532028097981150578}
m_SourcePrefab: {fileID: 100100000, guid: fe5b76dae94c07345b74d51e9a9a8440, type: 3}
--- !u!1 &1660453041666320737 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
m_PrefabInstance: {fileID: 2002541556693849136}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1532028097981150578
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1660453041666320737}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6fd7cab7d93b403280f2f9da978d8a4f, type: 3}
m_Name:
m_EditorClassIdentifier:
Bindings:
- ReferenceMesh:
referencePath: m1
targetObject: {fileID: 7767603723203631002}
Blendshape: bottom
LocalBlendshape:
--- !u!4 &2035853062413530075 stripped
Transform:
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
m_PrefabInstance: {fileID: 2002541556693849136}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &7425727422508624587
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 295226914695240947}
m_Modifications:
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071067
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.x
value: -0.7071068
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_DirtyAABB
value: 0
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.z
value: 2
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_Materials.Array.data[0]
value:
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_BlendShapeWeights.Array.data[0]
value: 0
objectReference: {fileID: 0}
- target: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_Name
value: m1
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: fe5b76dae94c07345b74d51e9a9a8440, type: 3}
--- !u!4 &6955742288500591904 stripped
Transform:
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
m_PrefabInstance: {fileID: 7425727422508624587}
m_PrefabAsset: {fileID: 0}
--- !u!1 &7767603723203631002 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
m_PrefabInstance: {fileID: 7425727422508624587}
m_PrefabAsset: {fileID: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 05653f5cab04e764b80709fe866c1b35
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,666 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2464504760772767737
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3251791125987375227}
- component: {fileID: 6611954401356246169}
- component: {fileID: 4257580493320060063}
- component: {fileID: 7095484051158404692}
m_Layer: 0
m_Name: DeletionTest
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3251791125987375227
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2464504760772767737}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0.06867766, y: 0.7869835, z: -0.57959247}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 8671858138418525756}
- {fileID: 3787319563290092876}
- {fileID: 2780879708549973278}
- {fileID: 6867583134219554799}
- {fileID: 3617623734196600728}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!95 &6611954401356246169
Animator:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2464504760772767737}
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!114 &4257580493320060063
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2464504760772767737}
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: 0
expressionsMenu: {fileID: 0}
expressionParameters: {fileID: 0}
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 &7095484051158404692
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2464504760772767737}
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!1 &3134446681435896768
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2780879708549973278}
- component: {fileID: 2470606632396626262}
m_Layer: 0
m_Name: Delete
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2780879708549973278
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3134446681435896768}
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: 1
m_Children: []
m_Father: {fileID: 3251791125987375227}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2470606632396626262
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3134446681435896768}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 0
m_shapes:
- Object:
referencePath: shape deletion test mesh
targetObject: {fileID: 0}
ShapeName: bottom
ChangeType: 0
Value: 50
--- !u!1 &7874409458034691206
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3787319563290092876}
- component: {fileID: 8462455628590652122}
m_Layer: 0
m_Name: PriorSet
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3787319563290092876
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7874409458034691206}
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: 1
m_Children: []
m_Father: {fileID: 3251791125987375227}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8462455628590652122
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7874409458034691206}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 0
m_shapes:
- Object:
referencePath: shape deletion test mesh
targetObject: {fileID: 0}
ShapeName: bottom
ChangeType: 1
Value: 50
--- !u!1 &7956182162252432618
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3617623734196600728}
- component: {fileID: 4167915178638071617}
- component: {fileID: 3280847981733507148}
m_Layer: 0
m_Name: MenuSet
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &3617623734196600728
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7956182162252432618}
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: 1
m_Children: []
m_Father: {fileID: 3251791125987375227}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4167915178638071617
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7956182162252432618}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 0
m_shapes:
- Object:
referencePath: shape deletion test mesh
targetObject: {fileID: 0}
ShapeName: bottom
ChangeType: 1
Value: 0
--- !u!114 &3280847981733507148
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7956182162252432618}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3b29d45007c5493d926d2cd45a489529, type: 3}
m_Name:
m_EditorClassIdentifier:
Control:
name:
icon: {fileID: 0}
type: 102
parameter:
name:
value: 1
style: 0
subMenu: {fileID: 0}
subParameters: []
labels: []
MenuSource: 1
menuSource_otherObjectChildren: {fileID: 0}
isSynced: 1
isSaved: 1
isDefault: 0
automaticValue: 1
--- !u!1 &8389945206789797712
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6867583134219554799}
- component: {fileID: 8099891503683627458}
m_Layer: 0
m_Name: NullSet
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &6867583134219554799
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8389945206789797712}
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: 1
m_Children: []
m_Father: {fileID: 3251791125987375227}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8099891503683627458
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8389945206789797712}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 0
m_shapes:
- Object:
referencePath: shape deletion test mesh
targetObject: {fileID: 0}
ShapeName: bottom
ChangeType: 1
Value: 0
--- !u!1001 &9210451080691405271
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 3251791125987375227}
m_Modifications:
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071067
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.x
value: -0.7071068
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_DirtyAABB
value: 0
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_AABB.m_Extent.z
value: 2
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_BlendShapeWeights.Array.data[0]
value: 0
objectReference: {fileID: 0}
- target: {fileID: 919132149155446097, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
propertyPath: m_Name
value: shape deletion test mesh
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: fe5b76dae94c07345b74d51e9a9a8440, type: 3}
--- !u!4 &8671858138418525756 stripped
Transform:
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: fe5b76dae94c07345b74d51e9a9a8440,
type: 3}
m_PrefabInstance: {fileID: 9210451080691405271}
m_PrefabAsset: {fileID: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a82669288fc87d94db320a2494fd76c5
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -16,10 +16,14 @@ namespace UnitTests.ReactiveComponent.ParameterAssignment
[Test]
public void ManuallyAssignedParametersAreNotReplaced()
{
TestAssignments(
new[] { (false, 1.0f), (false, 4.0f) },
new[] { 1.0f, 4.0f }
);
TestAssignments(new[] { (false, 1.0f) }, new[] { 1.0f });
TestAssignments(new[] { (false, 1.0f) }, new[] { 1.0f }, 0);
TestAssignments(new[] { (false, 4.0f) }, new[] { 4.0f });
TestAssignments(new[] { (false, 4.0f) }, new[] { 4.0f }, 0);
TestAssignments(new[] { (false, 1.0f), (false, 4.0f) }, new[] { 1.0f, 4.0f });
TestAssignments(new[] { (false, 1.0f), (false, 4.0f) }, new[] { 1.0f, 4.0f }, 0);
}
[Test]
@ -44,13 +48,18 @@ namespace UnitTests.ReactiveComponent.ParameterAssignment
{
TestAssignments(new[] { (false, 2.0f), (true, 0.0f), (true, 0.0f) }, new[] { 2.0f, 1.0f, 3.0f }, null);
TestAssignments(new[] { (false, 2.7f), (true, 0.0f), (true, 0.0f) }, new[] { 2.7f, 1.0f, 3.0f }, null);
TestAssignments(new[] { (true, 1.0f), (false, 0.0f) }, new[] { 2.0f, 0.0f }, null, overrideExpectedDefaultValue: 1.0f);
TestAssignments(new[] { (true, 1.0f), (false, 0.0f) }, new[] { 1.0f, 0.0f }, 0);
TestAssignments(new[] { (true, 1.0f), (false, 0.0f) }, new[] { 1.0f, 0.0f }, 1);
}
void TestAssignments(
(bool, float)[] assignments,
float[] expectedAssignments,
int? defaultIndex = null,
Action<List<ModularAvatarMenuItem>> customize = null
Action<List<ModularAvatarMenuItem>> customize = null,
float? overrideExpectedDefaultValue = null
)
{
var root = CreateRoot("root");
@ -98,6 +107,9 @@ namespace UnitTests.ReactiveComponent.ParameterAssignment
{
Assert.AreEqual(expected, mami.Control.value);
}
var expectedDefaultValue = overrideExpectedDefaultValue ?? (defaultIndex.HasValue ? expectedAssignments[defaultIndex.Value] : 0);
Assert.AreEqual(expectedDefaultValue, avDesc.expressionParameters.parameters.Single().defaultValue);
}
}
}

View File

@ -0,0 +1,85 @@
using System;
using System.Linq;
using modular_avatar_tests;
using nadena.dev.modular_avatar.core;
using nadena.dev.modular_avatar.core.editor;
using NUnit.Framework;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using VRC.SDK3.Avatars.ScriptableObjects;
namespace UnitTests.ReactiveComponent.ParameterAssignment
{
public class ParameterTypeTests : TestBase
{
[Test]
public void BoolTest()
{
Test(new[] { 0.0f }, VRCExpressionParameters.ValueType.Bool);
Test(new[] { 1.0f }, VRCExpressionParameters.ValueType.Bool);
Test(new[] { 0.0f, 1.0f }, VRCExpressionParameters.ValueType.Bool);
Test(new[] { 1.0f, 0.0f }, VRCExpressionParameters.ValueType.Bool);
}
[Test]
public void IntTest()
{
Test(new[] { 2.0f }, VRCExpressionParameters.ValueType.Int);
Test(new[] { 3.0f }, VRCExpressionParameters.ValueType.Int);
Test(new[] { 0.0f, 1.0f, 2.0f }, VRCExpressionParameters.ValueType.Int);
Test(new[] { 2.0f, 1.0f, 0.0f }, VRCExpressionParameters.ValueType.Int);
Test(new[] { 253.0f, 254.0f, 255.0f }, VRCExpressionParameters.ValueType.Int);
Test(new[] { 255.0f, 254.0f, 253.0f }, VRCExpressionParameters.ValueType.Int);
}
[Test]
public void FloatTest()
{
Test(new[] { -1.0f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { -0.1f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { 0.1f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { 0.9f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { -1.0f, 0.0f, 1.0f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { 1.0f, 0.0f, -1.0f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { -0.1f, 0.0f, 0.1f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { 0.1f, 0.0f, -0.1f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { 0.0f, 0.1f, 0.2f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { 0.2f, 0.1f, 0.0f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { 0.7f, 0.8f, 0.9f }, VRCExpressionParameters.ValueType.Float);
Test(new[] { 0.9f, 0.8f, 0.7f }, VRCExpressionParameters.ValueType.Float);
}
private void Test(float[] values, VRCExpressionParameters.ValueType expected)
{
var root = CreateRoot("Root");
var descriptor = root.GetComponent<VRCAvatarDescriptor>();
descriptor.expressionParameters = ScriptableObject.CreateInstance<VRCExpressionParameters>();
descriptor.expressionParameters.parameters = Array.Empty<VRCExpressionParameters.Parameter>();
for (var i = 0; i < values.Length; i++)
{
var obj = CreateChild(root, i.ToString());
var mami = obj.AddComponent<ModularAvatarMenuItem>();
mami.Control = new VRCExpressionsMenu.Control()
{
name = obj.name,
type = VRCExpressionsMenu.Control.ControlType.Toggle,
value = values[i],
parameter = new() { name = "Test" },
};
}
var context = new nadena.dev.ndmf.BuildContext(root, null);
new ParameterAssignerPass().TestExecute(context);
Assert.AreEqual(expected, descriptor.expressionParameters.parameters.Single().valueType);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c7051ac5a4bbe084c9d34c01c523eda3
timeCreated: 1726276608

View File

@ -0,0 +1,98 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using modular_avatar_tests;
using nadena.dev.modular_avatar.core;
using nadena.dev.modular_avatar.core.editor;
using NUnit.Framework;
using UnityEngine;
public class ShapeDeletionAnalysis : TestBase
{
[Test]
public void BasicShapeDeletionAnalysis()
{
var root = CreatePrefab("DeletionTest.prefab");
var mesh = AssertPreviewDeletion(root);
AssertBuildDeletion(mesh, root);
}
[Test]
public void WhenShapeDeletionIsConditionedOnSubsequentChanger_DoesNotDelete()
{
var root = CreatePrefab("DeletionTest.prefab");
root.transform.Find("MenuSet").gameObject.SetActive(true);
AssertPreviewDeletion(root);
AssertNoMeshDeletion(root);
var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
Assert.AreEqual(100, mesh.GetBlendShapeWeight(mesh.sharedMesh.GetBlendShapeIndex("bottom")));
}
[Test]
public void WhenShapeDeletionIsConditionedOnItself_DoesNotDelete()
{
var root = CreatePrefab("DeletionTest.prefab");
root.transform.Find("Delete").gameObject.AddComponent<ModularAvatarMenuItem>().InitSettings();
AssertNoPreviewDeletion(root);
AssertNoMeshDeletion(root);
var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
// deletion action is initially off, so we use the shape changer above it, which is set to 50.
Assert.AreEqual(50f, mesh.GetBlendShapeWeight(mesh.sharedMesh.GetBlendShapeIndex("bottom")));
}
private static void AssertBuildDeletion(SkinnedMeshRenderer mesh, GameObject root)
{
var originalSharedMesh = mesh.sharedMesh;
AvatarProcessor.ProcessAvatar(root);
Assert.AreNotEqual(originalSharedMesh, mesh.sharedMesh);
Assert.IsTrue(mesh.sharedMesh.vertices.All(v => v.z >= 0));
}
private static SkinnedMeshRenderer AssertPreviewDeletion(GameObject root)
{
var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
var analysis = new ReactiveObjectAnalyzer().Analyze(root);
var deletedShape = analysis.Shapes.GetValueOrDefault(new TargetProp()
{
TargetObject = mesh,
PropertyName = "deletedShape.bottom"
});
Assert.IsNotNull(deletedShape);
var activeGroup = deletedShape.actionGroups.LastOrDefault(ag => ag.InitiallyActive);
Assert.AreEqual(1.0f, activeGroup?.Value);
return mesh;
}
private static void AssertNoPreviewDeletion(GameObject root)
{
var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
var analysis = new ReactiveObjectAnalyzer().Analyze(root);
var deletedShape = analysis.Shapes.GetValueOrDefault(new TargetProp()
{
TargetObject = mesh,
PropertyName = "deletedShape.bottom"
});
if (deletedShape != null)
{
var activeGroup = deletedShape.actionGroups.LastOrDefault(ag => ag.InitiallyActive);
Assert.IsFalse(activeGroup?.Value is float f && f > 0);
}
}
private static void AssertNoMeshDeletion(GameObject root)
{
var mesh = root.GetComponentInChildren<SkinnedMeshRenderer>();
var originalSharedMesh = mesh.sharedMesh;
AvatarProcessor.ProcessAvatar(root);
Assert.AreEqual(originalSharedMesh, mesh.sharedMesh);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18eb55e1b66a00243a91142456dfd5f5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,109 @@
fileFormatVersion: 2
guid: fe5b76dae94c07345b74d51e9a9a8440
ModelImporter:
serializedVersion: 22200
internalIDToNameTable: []
externalObjects: {}
materials:
materialImportMode: 2
materialName: 0
materialSearch: 1
materialLocation: 1
animations:
legacyGenerateAnimations: 4
bakeSimulation: 0
resampleCurves: 1
optimizeGameObjects: 0
removeConstantScaleCurves: 0
motionNodeName:
rigImportErrors:
rigImportWarnings:
animationImportErrors:
animationImportWarnings:
animationRetargetingWarnings:
animationDoRetargetingWarnings: 0
importAnimatedCustomProperties: 0
importConstraints: 0
animationCompression: 1
animationRotationError: 0.5
animationPositionError: 0.5
animationScaleError: 0.5
animationWrapMode: 0
extraExposedTransformPaths: []
extraUserProperties: []
clipAnimations: []
isReadable: 0
meshes:
lODScreenPercentages: []
globalScale: 1
meshCompression: 0
addColliders: 0
useSRGBMaterialColor: 1
sortHierarchyByName: 1
importPhysicalCameras: 1
importVisibility: 1
importBlendShapes: 1
importCameras: 1
importLights: 1
nodeNameCollisionStrategy: 1
fileIdsGeneration: 2
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
keepQuads: 0
weldVertices: 1
bakeAxisConversion: 0
preserveHierarchy: 0
skinWeightsMode: 0
maxBonesPerVertex: 4
minBoneWeight: 0.001
optimizeBones: 1
meshOptimizationFlags: -1
indexFormat: 0
secondaryUVAngleDistortion: 8
secondaryUVAreaDistortion: 15.000001
secondaryUVHardAngle: 88
secondaryUVMarginMethod: 1
secondaryUVMinLightmapResolution: 40
secondaryUVMinObjectScale: 1
secondaryUVPackMargin: 4
useFileScale: 1
strictVertexDataChecks: 0
tangentSpace:
normalSmoothAngle: 60
normalImportMode: 0
tangentImportMode: 3
normalCalculationMode: 4
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
blendShapeNormalImportMode: 1
normalSmoothingSource: 0
referencedClips: []
importAnimation: 1
humanDescription:
serializedVersion: 3
human: []
skeleton: []
armTwist: 0.5
foreArmTwist: 0.5
upperLegTwist: 0.5
legTwist: 0.5
armStretch: 0.05
legStretch: 0.05
feetSpacing: 0
globalScale: 1
rootMotionBoneName:
hasTranslationDoF: 0
hasExtraRoot: 0
skeletonHasParents: 1
lastHumanDescriptionAvatarSource: {instanceID: 0}
autoGenerateAvatarMappingIfUnspecified: 1
animationType: 2
humanoidOversampling: 1
avatarSetup: 0
addHumanoidExtraRootOnlyWhenUsingAvatar: 1
importBlendShapeDeformPercent: 1
remapMaterialsIfMaterialImportModeIsNone: 0
additionalBone: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -14,7 +14,67 @@ namespace ShapeChangerTests
[Test]
public void SetsCorrectInitialStatesAndAnimations()
{
var root = CreatePrefab("SCDefaultAnimation.prefab");
SetsCorrectInitialStatesAndAnimations("SCDefaultAnimation.prefab");
}
[Test]
public void SetsCorrectInitialStatesAndAnimationsForInactiveSC()
{
var root = CreatePrefab("SCDefaultAnimationInactive.prefab");
AvatarProcessor.ProcessAvatar(root);
var fx = (AnimatorController) FindFxController(root).animatorController;
var baseLayer = fx.layers[0];
var bt = baseLayer.stateMachine.states[0].state.motion as BlendTree;
Assert.NotNull(bt);
var subBt = bt.children[0].motion as BlendTree;
Assert.NotNull(subBt);
var clip = subBt.children[0].motion as AnimationClip;
Assert.NotNull(clip);
var smr = root.transform.Find("test mesh").GetComponent<SkinnedMeshRenderer>();
var sharedMesh = smr.sharedMesh;
var bindings = AnimationUtility.GetCurveBindings(clip);
var curve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(
"test mesh",
typeof(SkinnedMeshRenderer),
"blendShape.key1"
));
Assert.IsNull(curve); // always off MenuItem (due to object disable), no curve should be generated
curve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(
"test mesh",
typeof(SkinnedMeshRenderer),
"blendShape.key2"
));
// Always-on delete, no curve should be generated
Assert.IsNull(curve);
curve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(
"test mesh",
typeof(SkinnedMeshRenderer),
"blendShape.key3"
));
// Always-on set, no curve should be generated
Assert.IsNull(curve);
// Check actual blendshape states
Assert.AreEqual(10.0f, smr.GetBlendShapeWeight(sharedMesh.GetBlendShapeIndex("key1")), 0.1f);
Assert.AreEqual(5.0f, smr.GetBlendShapeWeight(sharedMesh.GetBlendShapeIndex("key2")), 0.1f);
Assert.AreEqual(100.0f, smr.GetBlendShapeWeight(sharedMesh.GetBlendShapeIndex("key3")), 0.1f);
}
[Test]
public void SetsCorrectInitialStatesAndAnimationsForInvertedSC()
{
SetsCorrectInitialStatesAndAnimations("SCDefaultAnimationInverted.prefab");
}
private void SetsCorrectInitialStatesAndAnimations(string prefabPath)
{
var root = CreatePrefab(prefabPath);
AvatarProcessor.ProcessAvatar(root);
var fx = (AnimatorController) FindFxController(root).animatorController;

View File

@ -46,10 +46,13 @@ MonoBehaviour:
m_EditorClassIdentifier:
m_inverted: 0
m_targetRenderer:
referencePath: test mesh
referencePath:
targetObject: {fileID: 0}
m_shapes:
- ShapeName: key2
- Object:
referencePath: test mesh
targetObject: {fileID: 0}
ShapeName: key2
ChangeType: 0
Value: 100
--- !u!1 &2598725701317979415
@ -98,10 +101,13 @@ MonoBehaviour:
m_EditorClassIdentifier:
m_inverted: 0
m_targetRenderer:
referencePath: test mesh
referencePath:
targetObject: {fileID: 0}
m_shapes:
- ShapeName: key1
- Object:
referencePath: test mesh
targetObject: {fileID: 0}
ShapeName: key1
ChangeType: 1
Value: 10
--- !u!1 &2845086157653980983
@ -150,10 +156,13 @@ MonoBehaviour:
m_EditorClassIdentifier:
m_inverted: 0
m_targetRenderer:
referencePath: test mesh
referencePath:
targetObject: {fileID: 0}
m_shapes:
- ShapeName: key3
- Object:
referencePath: test mesh
targetObject: {fileID: 0}
ShapeName: key3
ChangeType: 1
Value: 100
--- !u!1 &6385483934583485188
@ -174,7 +183,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!4 &569625391371299408
Transform:
m_ObjectHideFlags: 0
@ -204,10 +213,13 @@ MonoBehaviour:
m_EditorClassIdentifier:
m_inverted: 0
m_targetRenderer:
referencePath: test mesh
referencePath:
targetObject: {fileID: 0}
m_shapes:
- ShapeName: key1
- Object:
referencePath: test mesh
targetObject: {fileID: 0}
ShapeName: key1
ChangeType: 1
Value: 20
--- !u!114 &2918390808850211981
@ -238,6 +250,7 @@ MonoBehaviour:
isSynced: 1
isSaved: 1
isDefault: 0
automaticValue: 0
--- !u!114 &664065153831629983
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@ -0,0 +1,668 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1307328145036867423
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7471115643889882934}
- component: {fileID: 2015798673852064281}
m_Layer: 0
m_Name: AlwaysOffDelete
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &7471115643889882934
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1307328145036867423}
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: 8936933457054072598}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2015798673852064281
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1307328145036867423}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 1
m_shapes:
- Object:
referencePath: test mesh
targetObject: {fileID: 0}
ShapeName: key2
ChangeType: 0
Value: 100
--- !u!1 &2598725701317979415
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1861262250770563182}
- component: {fileID: 8866671501173891171}
m_Layer: 0
m_Name: Toggled
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &1861262250770563182
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2598725701317979415}
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: 8936933457054072598}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8866671501173891171
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2598725701317979415}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 1
m_shapes:
- Object:
referencePath: test mesh
targetObject: {fileID: 0}
ShapeName: key1
ChangeType: 1
Value: 10
--- !u!1 &2845086157653980983
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 110069860838053623}
- component: {fileID: 8218581995269956798}
m_Layer: 0
m_Name: AlwaysOffSet
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &110069860838053623
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2845086157653980983}
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: 8936933457054072598}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8218581995269956798
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2845086157653980983}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 1
m_shapes:
- Object:
referencePath: test mesh
targetObject: {fileID: 0}
ShapeName: key3
ChangeType: 1
Value: 100
--- !u!1 &6385483934583485188
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 569625391371299408}
- component: {fileID: 3841502665919975468}
- component: {fileID: 2918390808850211981}
- component: {fileID: 664065153831629983}
m_Layer: 0
m_Name: InitialOffToggled
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &569625391371299408
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6385483934583485188}
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: 8936933457054072598}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &3841502665919975468
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6385483934583485188}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2db441f589c3407bb6fb5f02ff8ab541, type: 3}
m_Name:
m_EditorClassIdentifier:
m_inverted: 0
m_shapes:
- Object:
referencePath: test mesh
targetObject: {fileID: 0}
ShapeName: key1
ChangeType: 1
Value: 20
--- !u!114 &2918390808850211981
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6385483934583485188}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3b29d45007c5493d926d2cd45a489529, type: 3}
m_Name:
m_EditorClassIdentifier:
Control:
name:
icon: {fileID: 0}
type: 102
parameter:
name:
value: 1
style: 0
subMenu: {fileID: 0}
subParameters: []
labels: []
MenuSource: 1
menuSource_otherObjectChildren: {fileID: 0}
isSynced: 1
isSaved: 1
isDefault: 0
automaticValue: 0
--- !u!114 &664065153831629983
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6385483934583485188}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7ef83cb0c23d4d7c9d41021e544a1978, type: 3}
m_Name:
m_EditorClassIdentifier:
menuToAppend: {fileID: 0}
installTargetMenu: {fileID: 0}
--- !u!1 &6855505756433160176
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8936933457054072598}
- component: {fileID: 6580323041906195452}
- component: {fileID: 146169679456758165}
- component: {fileID: 5146811121193962360}
m_Layer: 0
m_Name: SCDefaultAnimationInactive
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8936933457054072598
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6855505756433160176}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 1.0024816, y: 0.25853348, z: -0.63345385}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1861262250770563182}
- {fileID: 7471115643889882934}
- {fileID: 110069860838053623}
- {fileID: 569625391371299408}
- {fileID: 1326682634762807916}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!95 &6580323041906195452
Animator:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6855505756433160176}
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!114 &146169679456758165
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6855505756433160176}
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: 0
expressionsMenu: {fileID: 0}
expressionParameters: {fileID: 0}
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 &5146811121193962360
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6855505756433160176}
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!1001 &1577363430154308999
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 8936933457054072598}
m_Modifications:
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalPosition.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071067
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalRotation.x
value: -0.7071068
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_DirtyAABB
value: 0
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_AABB.m_Extent.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_AABB.m_Extent.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_BlendShapeWeights.Array.data[0]
value: 5
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_BlendShapeWeights.Array.data[1]
value: 6
objectReference: {fileID: 0}
- target: {fileID: -3887185075125053422, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_BlendShapeWeights.Array.data[2]
value: 7
objectReference: {fileID: 0}
- target: {fileID: 919132149155446097, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
propertyPath: m_Name
value: test mesh
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
--- !u!4 &1326682634762807916 stripped
Transform:
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: cd28f61dacdc2424d951194ff69ba154, type: 3}
m_PrefabInstance: {fileID: 1577363430154308999}
m_PrefabAsset: {fileID: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4744d7b0db7db0d459f3aa7e6a0cf7db
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More