diff --git a/.github/ProjectRoot/vpm-manifest-2022.json b/.github/ProjectRoot/vpm-manifest-2022.json index eaba4f86..0c39da67 100644 --- a/.github/ProjectRoot/vpm-manifest-2022.json +++ b/.github/ProjectRoot/vpm-manifest-2022.json @@ -1,25 +1,25 @@ { "dependencies": { "com.vrchat.avatars": { - "version": "3.7.0" + "version": "3.7.4" }, "nadena.dev.ndmf": { - "version": "1.4.0" + "version": "1.6.0" } }, "locked": { "com.vrchat.avatars": { - "version": "3.7.0", + "version": "3.7.4", "dependencies": { - "com.vrchat.base": "3.7.0" + "com.vrchat.base": "3.7.4" } }, "com.vrchat.base": { - "version": "3.7.0", + "version": "3.7.4", "dependencies": {} }, "nadena.dev.ndmf": { - "version": "1.5.0" + "version": "1.6.0" } } } \ No newline at end of file diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 03f8b287..8ba40f61 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -79,7 +79,7 @@ jobs: path: ${{ env.zipFile }} - name: Make Release - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 + uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 if: startsWith(github.ref, 'refs/tags/') with: draft: true diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index 2ee06ea0..ec465e5c 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -122,7 +122,7 @@ jobs: workingDirectory: docs-site~ - name: Purge cache - uses: nathanvaughn/actions-cloudflare-purge@cd4afdf666c2e6a6720048f27ac9cbdd664a673a + uses: nathanvaughn/actions-cloudflare-purge@992cc4e96422fb8ddf077281678373fe41e7736c continue-on-error: true with: cf_zone: ${{ secrets.CF_ZONE_ID }} diff --git a/.github/workflows/gameci.yml b/.github/workflows/gameci.yml index 2d30547d..adf05e8e 100644 --- a/.github/workflows/gameci.yml +++ b/.github/workflows/gameci.yml @@ -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" diff --git a/Editor/Animation/AnimationDatabase.cs b/Editor/Animation/AnimationDatabase.cs index bf788923..d34ddff8 100644 --- a/Editor/Animation/AnimationDatabase.cs +++ b/Editor/Animation/AnimationDatabase.cs @@ -53,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; + } } } @@ -144,6 +158,7 @@ namespace nadena.dev.modular_avatar.animation #if MA_VRCSDK3_AVATARS var avatarDescriptor = context.AvatarDescriptor; + if (!avatarDescriptor) return; foreach (var layer in avatarDescriptor.baseAnimationLayers) { @@ -401,7 +416,7 @@ namespace nadena.dev.modular_avatar.animation { try { - AssetDatabase.AddObjectToAsset(curClip, _context.AssetContainer); + _context.AssetSaver.SaveAsset(curClip); } catch (Exception e) { diff --git a/Editor/Animation/AnimationServicesContext.cs b/Editor/Animation/AnimationServicesContext.cs index 95d68ec3..57ad5dd0 100644 --- a/Editor/Animation/AnimationServicesContext.cs +++ b/Editor/Animation/AnimationServicesContext.cs @@ -7,7 +7,9 @@ using nadena.dev.ndmf; using UnityEditor; using UnityEditor.Animations; using UnityEngine; +#if MA_VRCSDK3_AVATARS using VRC.SDK3.Avatars.Components; +#endif #endregion @@ -89,12 +91,16 @@ namespace nadena.dev.modular_avatar.animation // HACK: This is a temporary crutch until we rework the entire animator services system public void AddPropertyDefinition(AnimatorControllerParameter paramDef) { +#if MA_VRCSDK3_AVATARS + if (!_context.AvatarDescriptor) return; + var fx = (AnimatorController) _context.AvatarDescriptor.baseAnimationLayers .First(l => l.type == VRCAvatarDescriptor.AnimLayerType.FX) .animatorController; fx.parameters = fx.parameters.Concat(new[] { paramDef }).ToArray(); +#endif } public string GetActiveSelfProxy(GameObject obj) diff --git a/Editor/Animation/AnimationUtil.cs b/Editor/Animation/AnimationUtil.cs index aa026548..1016cba9 100644 --- a/Editor/Animation/AnimationUtil.cs +++ b/Editor/Animation/AnimationUtil.cs @@ -53,6 +53,8 @@ namespace nadena.dev.modular_avatar.animation // This helps reduce the risk that we'll accidentally modify the original assets. #if MA_VRCSDK3_AVATARS + if (!context.AvatarDescriptor) return; + context.AvatarDescriptor.baseAnimationLayers = CloneLayers(context, context.AvatarDescriptor.baseAnimationLayers); context.AvatarDescriptor.specialAnimationLayers = diff --git a/Editor/Animation/AnimatorCombiner.cs b/Editor/Animation/AnimatorCombiner.cs index 674140dc..ea886afa 100644 --- a/Editor/Animation/AnimatorCombiner.cs +++ b/Editor/Animation/AnimatorCombiner.cs @@ -78,7 +78,7 @@ namespace nadena.dev.modular_avatar.animation _combined = new AnimatorController(); if (context.AssetContainer != null && EditorUtility.IsPersistent(context.AssetContainer)) { - AssetDatabase.AddObjectToAsset(_combined, context.AssetContainer); + context.AssetSaver.SaveAsset(_combined); } _combined.name = assetName; @@ -191,7 +191,7 @@ namespace nadena.dev.modular_avatar.animation EditorUtility.CopySerialized(t, newTransition); if (_context.AssetContainer != null) { - AssetDatabase.AddObjectToAsset(newTransition, _context.AssetContainer); + _context.AssetSaver.SaveAsset(newTransition); } t = newTransition; } @@ -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(basePath, layerStateMachine); if (_stateMachines.TryGetValue(cacheKey, out var asm)) diff --git a/Editor/Animation/DeepClone.cs b/Editor/Animation/DeepClone.cs index e84bac6f..50caadca 100644 --- a/Editor/Animation/DeepClone.cs +++ b/Editor/Animation/DeepClone.cs @@ -14,6 +14,7 @@ namespace nadena.dev.modular_avatar.animation internal class DeepClone { + private BuildContext _context; private bool _isSaved; private UnityObject _combined; @@ -21,6 +22,7 @@ namespace nadena.dev.modular_avatar.animation public DeepClone(BuildContext context) { + _context = context; _isSaved = context.AssetContainer != null && EditorUtility.IsPersistent(context.AssetContainer); _combined = context.AssetContainer; } @@ -33,6 +35,8 @@ namespace nadena.dev.modular_avatar.animation if (original == null) return null; if (cloneMap == null) cloneMap = new Dictionary(); + using var scope = _context.OpenSerializationScope(); + Func visitor = null; if (basePath != null) { @@ -96,14 +100,12 @@ namespace nadena.dev.modular_avatar.animation if (_isSaved && !EditorUtility.IsPersistent(obj)) { - AssetDatabase.AddObjectToAsset(obj, _combined); + scope.SaveAsset(obj); } return (T)obj; } - - var ctor = original.GetType().GetConstructor(Type.EmptyTypes); if (ctor == null || original is ScriptableObject) { @@ -120,7 +122,7 @@ namespace nadena.dev.modular_avatar.animation if (_isSaved) { - AssetDatabase.AddObjectToAsset(obj, _combined); + scope.SaveAsset(obj); } SerializedObject so = new SerializedObject(obj); @@ -233,7 +235,7 @@ namespace nadena.dev.modular_avatar.animation newClip.name = "rebased " + clip.name; if (_isSaved) { - AssetDatabase.AddObjectToAsset(newClip, _combined); + _context.AssetSaver.SaveAsset(newClip); } foreach (var binding in AnimationUtility.GetCurveBindings(clip)) diff --git a/Editor/Animation/GameObjectDisableDelayPass.cs b/Editor/Animation/GameObjectDisableDelayPass.cs index 8e8bca4c..87692657 100644 --- a/Editor/Animation/GameObjectDisableDelayPass.cs +++ b/Editor/Animation/GameObjectDisableDelayPass.cs @@ -1,9 +1,12 @@ -using System.Linq; +#if MA_VRCSDK3_AVATARS +using System.Linq; +using nadena.dev.modular_avatar.core.editor; using nadena.dev.ndmf; using UnityEditor; using UnityEditor.Animations; using UnityEngine; using VRC.SDK3.Avatars.Components; +using BuildContext = nadena.dev.ndmf.BuildContext; namespace nadena.dev.modular_avatar.animation { @@ -23,11 +26,16 @@ namespace nadena.dev.modular_avatar.animation if (fx == null) return; + var nullMotion = new AnimationClip(); + nullMotion.name = "NullMotion"; + var blendTree = new BlendTree(); blendTree.blendType = BlendTreeType.Direct; blendTree.useAutomaticThresholds = false; - blendTree.children = asc.BoundReadableProperties.Select(GenerateDelayChild).ToArray(); + blendTree.children = asc.BoundReadableProperties + .Select(prop => GenerateDelayChild(nullMotion, prop)) + .ToArray(); var asm = new AnimatorStateMachine(); var state = new AnimatorState(); @@ -52,9 +60,24 @@ namespace nadena.dev.modular_avatar.animation defaultWeight = 1, blendingMode = AnimatorLayerBlendingMode.Override }).ToArray(); + + // Ensure the initial state of readable props matches the actual state of the gameobject + var parameters = fx.parameters; + var paramToIndex = parameters.Select((p, i) => (p, i)).ToDictionary(x => x.p.name, x => x.i); + foreach (var (binding, prop) in asc.BoundReadableProperties) + { + var obj = asc.PathMappings.PathToObject(binding.path); + + if (obj != null && paramToIndex.TryGetValue(prop, out var index)) + { + parameters[index].defaultFloat = obj.activeSelf ? 1 : 0; + } + } + + fx.parameters = parameters; } - private ChildMotion GenerateDelayChild((EditorCurveBinding, string) binding) + private ChildMotion GenerateDelayChild(Motion nullMotion, (EditorCurveBinding, string) binding) { var ecb = binding.Item1; var prop = binding.Item2; @@ -64,12 +87,43 @@ namespace nadena.dev.modular_avatar.animation curve.AddKey(0, 1); AnimationUtility.SetEditorCurve(motion, ecb, curve); + // Occasionally, we'll have a very small value pop up, probably due to FP errors. + // To correct for this, instead of directly using the property in the direct blend tree, + // we'll use a 1D blend tree to give ourselves a buffer. + + var bufferBlendTree = new BlendTree(); + bufferBlendTree.blendType = BlendTreeType.Simple1D; + bufferBlendTree.useAutomaticThresholds = false; + bufferBlendTree.blendParameter = prop; + bufferBlendTree.children = new[] + { + new ChildMotion + { + motion = nullMotion, + timeScale = 1, + threshold = 0 + }, + new ChildMotion + { + motion = nullMotion, + timeScale = 1, + threshold = 0.01f + }, + new ChildMotion + { + motion = motion, + timeScale = 1, + threshold = 1 + } + }; + return new ChildMotion { - motion = motion, - directBlendParameter = prop, + motion = bufferBlendTree, + directBlendParameter = MergeBlendTreePass.ALWAYS_ONE, timeScale = 1 }; } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/Animation/PathMappings.cs b/Editor/Animation/PathMappings.cs index 9546a063..4d53f6a2 100644 --- a/Editor/Animation/PathMappings.cs +++ b/Editor/Animation/PathMappings.cs @@ -368,21 +368,26 @@ namespace nadena.dev.modular_avatar.animation } Profiler.EndSample(); - var layers = context.AvatarDescriptor.baseAnimationLayers - .Concat(context.AvatarDescriptor.specialAnimationLayers); - - Profiler.BeginSample("ApplyMappingsToAvatarMasks"); - foreach (var layer in layers) +#if MA_VRCSDK3_AVATARS + if (context.AvatarDescriptor) { - ApplyMappingsToAvatarMask(layer.mask); + var layers = context.AvatarDescriptor.baseAnimationLayers + .Concat(context.AvatarDescriptor.specialAnimationLayers); - if (layer.animatorController is AnimatorController ac) - // By this point, all AnimationOverrideControllers have been collapsed into an ephemeral - // AnimatorController so we can safely modify the controller in-place. - foreach (var acLayer in ac.layers) - ApplyMappingsToAvatarMask(acLayer.avatarMask); + Profiler.BeginSample("ApplyMappingsToAvatarMasks"); + foreach (var layer in layers) + { + ApplyMappingsToAvatarMask(layer.mask); + + if (layer.animatorController is AnimatorController ac) + // By this point, all AnimationOverrideControllers have been collapsed into an ephemeral + // AnimatorController so we can safely modify the controller in-place. + foreach (var acLayer in ac.layers) + ApplyMappingsToAvatarMask(acLayer.avatarMask); + } + Profiler.EndSample(); } - Profiler.EndSample(); +#endif Profiler.EndSample(); } diff --git a/Editor/ApplyAnimatorDefaultValuesPass.cs b/Editor/ApplyAnimatorDefaultValuesPass.cs index 7b3c91ed..0f9cb15e 100644 --- a/Editor/ApplyAnimatorDefaultValuesPass.cs +++ b/Editor/ApplyAnimatorDefaultValuesPass.cs @@ -1,4 +1,5 @@ -#region +#if MA_VRCSDK3_AVATARS +#region using System; using System.Collections.Immutable; @@ -57,4 +58,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/BuildContext.cs b/Editor/BuildContext.cs index acd3f5cf..b1dfa3fc 100644 --- a/Editor/BuildContext.cs +++ b/Editor/BuildContext.cs @@ -71,7 +71,7 @@ namespace nadena.dev.modular_avatar.core.editor { if (!SaveImmediate || AssetDatabase.IsMainAsset(obj) || AssetDatabase.IsSubAsset(obj)) return; - AssetDatabase.AddObjectToAsset(obj, AssetContainer); + PluginBuildContext.AssetSaver.SaveAsset(obj); } public AnimatorController CreateAnimator(AnimatorController toClone = null) diff --git a/Editor/FixupPasses/FixupExpressionsMenuPass.cs b/Editor/FixupPasses/FixupExpressionsMenuPass.cs index f4b549ee..4f36ec2b 100644 --- a/Editor/FixupPasses/FixupExpressionsMenuPass.cs +++ b/Editor/FixupPasses/FixupExpressionsMenuPass.cs @@ -19,6 +19,8 @@ namespace nadena.dev.modular_avatar.core.editor internal static void FixupExpressionsMenu(BuildContext context) { + if (!context.AvatarDescriptor) return; + context.AvatarDescriptor.customExpressions = true; var expressionsMenu = context.AvatarDescriptor.expressionsMenu; diff --git a/Editor/HarmonyPatches/PatchLoader.cs b/Editor/HarmonyPatches/PatchLoader.cs index 0ce7ffa4..b368fca7 100644 --- a/Editor/HarmonyPatches/PatchLoader.cs +++ b/Editor/HarmonyPatches/PatchLoader.cs @@ -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[] patches = new Action[] { //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); }; } } } \ No newline at end of file diff --git a/Editor/HeuristicBoneMapper.cs b/Editor/HeuristicBoneMapper.cs index b1ba169b..755e652d 100644 --- a/Editor/HeuristicBoneMapper.cs +++ b/Editor/HeuristicBoneMapper.cs @@ -231,6 +231,9 @@ namespace nadena.dev.modular_avatar.core.editor }; internal static readonly Regex Regex_VRM_Bone = new Regex(@"^([LRC])_(.*)$"); + + internal static ImmutableHashSet AllBoneNames = + boneNamePatterns.SelectMany(x => x).Select(NormalizeName).ToImmutableHashSet(); internal static string NormalizeName(string name) { @@ -243,6 +246,14 @@ namespace nadena.dev.modular_avatar.core.editor internal static readonly ImmutableDictionary> NameToBoneMap; internal static readonly ImmutableDictionary> BoneToNameMap; + [InitializeOnLoadMethod] + private static void InsertboneNamePatternsToRuntime() + { + ModularAvatarMergeArmature.boneNamePatterns = boneNamePatterns; + ModularAvatarMergeArmature.AllBoneNames = AllBoneNames; + ModularAvatarMergeArmature.NormalizeBoneName = NormalizeName; + } + static HeuristicBoneMapper() { var pat_end_side = new Regex(@"[_\.]([LR])$"); @@ -306,7 +317,9 @@ namespace nadena.dev.modular_avatar.core.editor GameObject src, GameObject newParent, List skipped = null, - HashSet unassigned = null + HashSet unassigned = null, + Animator avatarAnimator = null, + Dictionary outfitHumanoidBones = null ) { Dictionary mappings = new Dictionary(); @@ -355,21 +368,65 @@ namespace nadena.dev.modular_avatar.core.editor var childName = child.gameObject.name; var targetObjectName = childName.Substring(config.prefix.Length, childName.Length - config.prefix.Length - config.suffix.Length); - - if (!NameToBoneMap.TryGetValue( - NormalizeName(targetObjectName), out var bodyBones)) + List bodyBones = null; + var isMapped = false; + + if (outfitHumanoidBones != null && outfitHumanoidBones.TryGetValue(child, out var outfitHumanoidBone)) + { + if (avatarAnimator != null) + { + var avatarBone = avatarAnimator.GetBoneTransform(outfitHumanoidBone); + if (avatarBone != null && unassigned.Contains(avatarBone)) + { + mappings[child] = avatarBone; + unassigned.Remove(avatarBone); + lcNameToXform.Remove(NormalizeName(avatarBone.gameObject.name)); + isMapped = true; + } else { + bodyBones = new List { outfitHumanoidBone }; + } + } else { + bodyBones = new List() { outfitHumanoidBone }; + } + } + + if (!isMapped && bodyBones == null && !NameToBoneMap.TryGetValue( + NormalizeName(targetObjectName), out bodyBones)) { continue; } - foreach (var otherName in bodyBones.SelectMany(bone => BoneToNameMap[bone])) + if (!isMapped) { - if (lcNameToXform.TryGetValue(otherName, out var targetObject)) + foreach (var bodyBone in bodyBones) { - mappings[child] = targetObject; - unassigned.Remove(targetObject); - lcNameToXform.Remove(otherName.ToLowerInvariant()); - break; + if (avatarAnimator != null) + { + var avatarBone = avatarAnimator.GetBoneTransform(bodyBone); + if (avatarBone != null && unassigned.Contains(avatarBone)) + { + mappings[child] = avatarBone; + unassigned.Remove(avatarBone); + lcNameToXform.Remove(NormalizeName(avatarBone.gameObject.name)); + isMapped = true; + break; + } + } + } + } + + if (!isMapped) + { + foreach (var otherName in bodyBones.SelectMany(bone => BoneToNameMap[bone])) + { + if (lcNameToXform.TryGetValue(otherName, out var targetObject)) + { + mappings[child] = targetObject; + unassigned.Remove(targetObject); + lcNameToXform.Remove(otherName.ToLowerInvariant()); + isMapped = true; + break; + } } } @@ -388,7 +445,7 @@ namespace nadena.dev.modular_avatar.core.editor return mappings; } - internal static void RenameBonesByHeuristic(ModularAvatarMergeArmature config, List skipped = null) + internal static void RenameBonesByHeuristic(ModularAvatarMergeArmature config, List skipped = null, Dictionary outfitHumanoidBones = null, Animator avatarAnimator = null) { var target = config.mergeTarget.Get(RuntimeUtil.FindAvatarTransformInParents(config.transform)); if (target == null) return; @@ -399,7 +456,7 @@ namespace nadena.dev.modular_avatar.core.editor void Traverse(Transform src, Transform dst) { - var mappings = AssignBoneMappings(config, src.gameObject, dst.gameObject, skipped: skipped); + var mappings = AssignBoneMappings(config, src.gameObject, dst.gameObject, skipped: skipped, outfitHumanoidBones: outfitHumanoidBones, avatarAnimator: avatarAnimator); foreach (var pair in mappings) { diff --git a/Editor/Inspector/DragAndDropManipulator.cs b/Editor/Inspector/DragAndDropManipulator.cs new file mode 100644 index 00000000..0d3de862 --- /dev/null +++ b/Editor/Inspector/DragAndDropManipulator.cs @@ -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 : 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(); + + public DragAndDropManipulator(VisualElement targetElement, T targetComponent) + { + target = targetElement; + TargetComponent = targetComponent; + } + + protected sealed override void RegisterCallbacksOnTarget() + { + target.RegisterCallback(OnDragEnter); + target.RegisterCallback(OnDragLeave); + target.RegisterCallback(OnDragExited); + target.RegisterCallback(OnDragUpdated); + target.RegisterCallback(OnDragPerform); + } + + protected sealed override void UnregisterCallbacksFromTarget() + { + target.UnregisterCallback(OnDragEnter); + target.UnregisterCallback(OnDragLeave); + target.UnregisterCallback(OnDragExited); + target.UnregisterCallback(OnDragUpdated); + target.UnregisterCallback(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() + .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(); + target.RemoveFromClassList(DragActiveClassName); + } + + private void OnDragExited(DragExitedEvent _) + { + _draggingObjects = Array.Empty(); + 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); + } +} diff --git a/Editor/Inspector/DragAndDropManipulator.cs.meta b/Editor/Inspector/DragAndDropManipulator.cs.meta new file mode 100644 index 00000000..b78bdf93 --- /dev/null +++ b/Editor/Inspector/DragAndDropManipulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 528c660b56905844ea2f88bc73837e9f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Inspector/FirstPersonVisibleEditor.cs b/Editor/Inspector/FirstPersonVisibleEditor.cs index b9646ddb..8f7cedb9 100644 --- a/Editor/Inspector/FirstPersonVisibleEditor.cs +++ b/Editor/Inspector/FirstPersonVisibleEditor.cs @@ -1,4 +1,5 @@ -using UnityEditor; +#if MA_VRCSDK3_AVATARS +using UnityEditor; namespace nadena.dev.modular_avatar.core.editor { @@ -45,3 +46,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } + +#endif \ No newline at end of file diff --git a/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs b/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs index c7410eb8..a2b055b3 100644 --- a/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs +++ b/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs @@ -16,6 +16,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger [SerializeField] private StyleSheet uss; [SerializeField] private VisualTreeAsset uxml; + private DragAndDropManipulator _dragAndDropManipulator; protected override void OnInnerInspectorGUI() { @@ -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 + { + public DragAndDropManipulator(VisualElement targetElement, ModularAvatarMaterialSetter targetComponent) + : base(targetElement, targetComponent) { } + + protected override bool FilterGameObject(GameObject obj) + { + if (obj.TryGetComponent(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); + } + } } } \ No newline at end of file diff --git a/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss b/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss index 84204231..8e422d2d 100644 --- a/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss +++ b/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss @@ -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); +} diff --git a/Editor/Inspector/Menu/MenuInstallerEditor.cs b/Editor/Inspector/Menu/MenuInstallerEditor.cs index 5110aa36..79dbb02f 100644 --- a/Editor/Inspector/Menu/MenuInstallerEditor.cs +++ b/Editor/Inspector/Menu/MenuInstallerEditor.cs @@ -33,6 +33,29 @@ namespace nadena.dev.modular_avatar.core.editor private Dictionary> _menuInstallersMap; + private static Editor _cachedEditor; + + [InitializeOnLoadMethod] + private static void Init() + { + ModularAvatarMenuInstaller._openSelectMenu = OpenSelectInstallTargetMenu; + } + + private static void OpenSelectInstallTargetMenu(ModularAvatarMenuInstaller installer) + { + CreateCachedEditor(installer, typeof(MenuInstallerEditor), ref _cachedEditor); + + var editor = (MenuInstallerEditor)_cachedEditor; + editor.OnEnable(); + + var serializedObject = editor.serializedObject; + var installTo = serializedObject.FindProperty(nameof(ModularAvatarMenuInstaller.installTargetMenu)); + + var root = editor.FindCommonAvatar(); + + editor.OpenSelectMenu(root, installTo); + } + private void OnEnable() { _installer = (ModularAvatarMenuInstaller) target; @@ -215,74 +238,7 @@ namespace nadena.dev.modular_avatar.core.editor var avatar = commonAvatar; if (avatar != null && InstallTargets.Count == 1 && GUILayout.Button(G("menuinstall.selectmenu"))) { - AvMenuTreeViewWindow.Show(avatar, _installer, menu => - { - if (InstallTargets.Count != 1 || menu == InstallTargets[0]) return; - - if (InstallTargets[0] is ModularAvatarMenuInstallTarget oldTarget && oldTarget != null) - { - DestroyInstallTargets(); - } - - if (menu is ValueTuple vt) // TODO: This should be a named type... - { - // Menu, ContextCallback - menu = vt.Item1; - } - - if (menu is ModularAvatarMenuItem item) - { - if (item.MenuSource == SubmenuSource.MenuAsset) - { - menu = item.Control.subMenu; - } - else - { - var menuParent = item.menuSource_otherObjectChildren != null - ? item.menuSource_otherObjectChildren - : item.gameObject; - - menu = new MenuNodesUnder(menuParent); - } - } - else if (menu is ModularAvatarMenuGroup group) - { - if (group.targetObject != null) menu = new MenuNodesUnder(group.targetObject); - else menu = new MenuNodesUnder(group.gameObject); - } - - if (menu is VRCExpressionsMenu expMenu) - { - if (expMenu == avatar.expressionsMenu) installTo.objectReferenceValue = null; - else installTo.objectReferenceValue = expMenu; - } - else if (menu is RootMenu) - { - installTo.objectReferenceValue = null; - } - else if (menu is MenuNodesUnder nodesUnder) - { - installTo.objectReferenceValue = null; - - foreach (var target in targets.Cast().OrderBy(ObjectHierarchyOrder)) - { - var installer = (ModularAvatarMenuInstaller) target; - var child = new GameObject(); - Undo.RegisterCreatedObjectUndo(child, "Set install target"); - child.transform.SetParent(nodesUnder.root.transform, false); - child.name = installer.gameObject.name; - - var targetComponent = child.AddComponent(); - targetComponent.installer = installer; - - EditorGUIUtility.PingObject(child); - } - } - - serializedObject.ApplyModifiedProperties(); - VirtualMenu.InvalidateCaches(); - Repaint(); - }); + OpenSelectMenu(avatar, installTo); } } @@ -368,7 +324,79 @@ namespace nadena.dev.modular_avatar.core.editor serializedObject.ApplyModifiedProperties(); - Localization.ShowLanguageUI(); + ShowLanguageUI(); + } + + private void OpenSelectMenu(VRCAvatarDescriptor avatar, SerializedProperty installTo) + { + AvMenuTreeViewWindow.Show(avatar, _installer, menu => + { + if (InstallTargets.Count != 1 || menu == InstallTargets[0]) return; + + if (InstallTargets[0] is ModularAvatarMenuInstallTarget oldTarget && oldTarget != null) + { + DestroyInstallTargets(); + } + + if (menu is ValueTuple vt) // TODO: This should be a named type... + { + // Menu, ContextCallback + menu = vt.Item1; + } + + if (menu is ModularAvatarMenuItem item) + { + if (item.MenuSource == SubmenuSource.MenuAsset) + { + menu = item.Control.subMenu; + } + else + { + var menuParent = item.menuSource_otherObjectChildren != null + ? item.menuSource_otherObjectChildren + : item.gameObject; + + menu = new MenuNodesUnder(menuParent); + } + } + else if (menu is ModularAvatarMenuGroup group) + { + if (group.targetObject != null) menu = new MenuNodesUnder(group.targetObject); + else menu = new MenuNodesUnder(group.gameObject); + } + + if (menu is VRCExpressionsMenu expMenu) + { + if (expMenu == avatar.expressionsMenu) installTo.objectReferenceValue = null; + else installTo.objectReferenceValue = expMenu; + } + else if (menu is RootMenu) + { + installTo.objectReferenceValue = null; + } + else if (menu is MenuNodesUnder nodesUnder) + { + installTo.objectReferenceValue = null; + + foreach (var target in targets.Cast().OrderBy(ObjectHierarchyOrder)) + { + var installer = (ModularAvatarMenuInstaller)target; + var child = new GameObject(); + Undo.RegisterCreatedObjectUndo(child, "Set install target"); + child.transform.SetParent(nodesUnder.root.transform, false); + child.name = installer.gameObject.name; + + var targetComponent = child.AddComponent(); + targetComponent.installer = installer; + + EditorGUIUtility.PingObject(child); + } + } + + serializedObject.ApplyModifiedProperties(); + VirtualMenu.InvalidateCaches(); + Repaint(); + }); } private string ObjectHierarchyOrder(Component arg) @@ -415,6 +443,9 @@ namespace nadena.dev.modular_avatar.core.editor var group = installer.gameObject.AddComponent(); var menuRoot = new GameObject(); menuRoot.name = "Menu"; + + group.targetObject = menuRoot; + Undo.RegisterCreatedObjectUndo(menuRoot, "Extract menu"); menuRoot.transform.SetParent(group.transform, false); foreach (var control in menu.controls) diff --git a/Editor/Inspector/Menu/MenuItemGUI.cs b/Editor/Inspector/Menu/MenuItemGUI.cs index fca5fcd9..bf3cfccd 100644 --- a/Editor/Inspector/Menu/MenuItemGUI.cs +++ b/Editor/Inspector/Menu/MenuItemGUI.cs @@ -176,11 +176,16 @@ namespace nadena.dev.modular_avatar.core.editor } Dictionary rootParameters = new(); - + foreach (var param in ParameterIntrospectionCache.GetParametersForObject(parentAvatar.gameObject) .Where(p => p.Namespace == ParameterNamespace.Animator) ) - rootParameters[param.EffectiveName] = param; + { + if (!string.IsNullOrWhiteSpace(param.EffectiveName)) + { + rootParameters[param.EffectiveName] = param; + } + } var remaps = ParameterIntrospectionCache.GetParameterRemappingsAt(paramRef); foreach (var remap in remaps) @@ -366,10 +371,9 @@ namespace nadena.dev.modular_avatar.core.editor EditorGUILayout.BeginVertical(); if (_type.hasMultipleDifferentValues) return; - VRCExpressionsMenu.Control.ControlType type = - (VRCExpressionsMenu.Control.ControlType) Enum - .GetValues(typeof(VRCExpressionsMenu.Control.ControlType)) - .GetValue(_type.enumValueIndex); + var controlTypeArray = Enum.GetValues(typeof(VRCExpressionsMenu.Control.ControlType)); + var index = Math.Clamp(_type.enumValueIndex, 0, controlTypeArray.Length - 1); + var type = (VRCExpressionsMenu.Control.ControlType)controlTypeArray.GetValue(index); switch (type) { @@ -582,7 +586,12 @@ namespace nadena.dev.modular_avatar.core.editor // But, we do want to see if _any_ are default. var anyIsDefault = _prop_isDefault.hasMultipleDifferentValues || _prop_isDefault.boolValue; var mixedIsDefault = multipleSelections && anyIsDefault; - using (new EditorGUI.DisabledScope(multipleSelections || isDefaultByKnownParam != null)) + + var allAreAutoParams = !_parameterName.hasMultipleDifferentValues && + string.IsNullOrWhiteSpace(_parameterName.stringValue); + + using (new EditorGUI.DisabledScope((!allAreAutoParams && multipleSelections) || + isDefaultByKnownParam != null)) { EditorGUI.BeginChangeCheck(); DrawHorizontalToggleProp(_prop_isDefault, G("menuitem.prop.is_default"), mixedIsDefault, @@ -711,6 +720,9 @@ 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(); @@ -718,7 +730,6 @@ namespace nadena.dev.modular_avatar.core.editor if (myMappings.TryGetValue((ParameterNamespace.Animator, myParameterName), out var myReplacement)) myParameterName = myReplacement.ParameterName; - var avatarRoot = RuntimeUtil.FindAvatarInParents(myMenuItem.gameObject.transform); var siblings = new List(); foreach (var otherMenuItem in avatarRoot.GetComponentsInChildren(true)) diff --git a/Editor/Inspector/Menu/ToggleCreatorShortcut.cs b/Editor/Inspector/Menu/ToggleCreatorShortcut.cs index e549d319..d1027b95 100644 --- a/Editor/Inspector/Menu/ToggleCreatorShortcut.cs +++ b/Editor/Inspector/Menu/ToggleCreatorShortcut.cs @@ -1,4 +1,5 @@ -using nadena.dev.modular_avatar.ui; +#if MA_VRCSDK3_AVATARS +using nadena.dev.modular_avatar.ui; using UnityEditor; using UnityEngine; using VRC.SDK3.Avatars.ScriptableObjects; @@ -62,4 +63,5 @@ namespace nadena.dev.modular_avatar.core.editor Undo.RegisterCreatedObjectUndo(toggle, "Create Toggle"); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/Inspector/MergeArmatureEditor.cs b/Editor/Inspector/MergeArmatureEditor.cs index 3785a608..0dbf58f0 100644 --- a/Editor/Inspector/MergeArmatureEditor.cs +++ b/Editor/Inspector/MergeArmatureEditor.cs @@ -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; @@ -99,7 +100,7 @@ namespace nadena.dev.modular_avatar.core.editor { serializedObject.ApplyModifiedProperties(); - if (target.mergeTargetObject != null && priorMergeTarget == null + if (target.mergeTargetObject != null && priorMergeTarget != target.mergeTargetObject && string.IsNullOrEmpty(target.prefix) && string.IsNullOrEmpty(target.suffix)) { @@ -114,7 +115,27 @@ namespace nadena.dev.modular_avatar.core.editor { if (GUILayout.Button(G("merge_armature.adjust_names"))) { - HeuristicBoneMapper.RenameBonesByHeuristic(target); + var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(target.mergeTarget.Get(target).transform); + var avatarAnimator = avatarRoot != null ? avatarRoot.GetComponent() : null; + + // Search Outfit Root Animator + var outfitRoot = ((ModularAvatarMergeArmature)serializedObject.targetObject).transform; + Animator outfitAnimator = null; + while (outfitRoot != null) + { + if (outfitRoot == avatarRoot) + { + outfitAnimator = null; + break; + } + outfitAnimator = outfitRoot.GetComponent(); + if (outfitAnimator != null && outfitAnimator.isHuman) break; + outfitAnimator = null; + outfitRoot = outfitRoot.parent; + } + + var outfitHumanoidBones = SetupOutfit.GetOutfitHumanoidBones(outfitRoot, outfitAnimator); + HeuristicBoneMapper.RenameBonesByHeuristic(target, outfitHumanoidBones: outfitHumanoidBones, avatarAnimator: avatarAnimator); } } @@ -134,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"))) { @@ -188,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(); @@ -279,4 +308,4 @@ namespace nadena.dev.modular_avatar.core.editor } } } -} \ No newline at end of file +} diff --git a/Editor/Inspector/MoveIndependently/MoveIndependentlyEditor.cs b/Editor/Inspector/MoveIndependently/MoveIndependentlyEditor.cs index 4b741d90..3bcc0b3f 100644 --- a/Editor/Inspector/MoveIndependently/MoveIndependentlyEditor.cs +++ b/Editor/Inspector/MoveIndependently/MoveIndependentlyEditor.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using nadena.dev.modular_avatar.core.ArmatureAwase; using nadena.dev.ndmf.preview; using UnityEditor; using UnityEngine; diff --git a/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs b/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs index 627121b7..999f596c 100644 --- a/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs +++ b/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs @@ -35,14 +35,12 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger ROSimulatorButton.BindRefObject(root, target); var listView = root.Q("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 { - public ModularAvatarObjectToggle TargetComponent; - private GameObject[] _nowDragging = Array.Empty(); - 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(OnDragEnter); - target.RegisterCallback(OnDragLeave); - target.RegisterCallback(OnDragPerform); - target.RegisterCallback(OnDragUpdate); - } - - protected override void UnregisterCallbacksFromTarget() - { - target.UnregisterCallback(OnDragEnter); - target.UnregisterCallback(OnDragLeave); - target.UnregisterCallback(OnDragPerform); - target.RegisterCallback(OnDragUpdate); - } - - - private void OnDragEnter(DragEnterEvent evt) - { - if (TargetComponent == null) return; - - _avatarRoot = RuntimeUtil.FindAvatarTransformInParents(TargetComponent.transform); - if (_avatarRoot == null) return; - - _nowDragging = DragAndDrop.objectReferences.OfType() - .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(); - _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(); - _parentElem.RemoveFromClassList("drop-area--drag-active"); + EditorUtility.SetDirty(TargetComponent); + PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent); } } } diff --git a/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss b/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss index d4bdedc5..12402b5d 100644 --- a/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss +++ b/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss @@ -51,6 +51,12 @@ width: 60px; } -.drop-area--drag-active > ListView ScrollView { - background-color: rgba(0, 255, 255, 0.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); } diff --git a/Editor/Inspector/Parameters/AvatarParametersEditor.cs b/Editor/Inspector/Parameters/AvatarParametersEditor.cs index 40d7c5bb..f8af2fa2 100644 --- a/Editor/Inspector/Parameters/AvatarParametersEditor.cs +++ b/Editor/Inspector/Parameters/AvatarParametersEditor.cs @@ -5,8 +5,12 @@ using System.Linq; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; +using UnityEngine.UI; using UnityEngine.UIElements; +using VRC.SDK3.Avatars.ScriptableObjects; using static nadena.dev.modular_avatar.core.editor.Localization; +using Button = UnityEngine.UIElements.Button; +using Image = UnityEngine.UIElements.Image; namespace nadena.dev.modular_avatar.core.editor { @@ -35,6 +39,37 @@ namespace nadena.dev.modular_avatar.core.editor listView.showBoundCollectionSize = false; listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight; + listView.selectionType = SelectionType.Multiple; + listView.RegisterCallback(evt => + { + if (evt.keyCode == KeyCode.Delete) + { + serializedObject.Update(); + + var prop = serializedObject.FindProperty("parameters"); + + var indices = listView.selectedIndices.ToList(); + + foreach (var index in indices.OrderByDescending(i => i)) + { + prop.DeleteArrayElementAtIndex(index); + } + + serializedObject.ApplyModifiedProperties(); + + if (indices.Count == 0) + { + EditorApplication.delayCall += () => + { + // Works around an issue where the inner text boxes are auto-selected, preventing you from + // just hitting delete over and over + listView.SetSelectionWithoutNotify(indices); + }; + } + } + + evt.StopPropagation(); + }, TrickleDown.NoTrickleDown); unregisteredListView = root.Q("UnregisteredParameters"); @@ -128,10 +163,73 @@ namespace nadena.dev.modular_avatar.core.editor EditorApplication.delayCall += DetectParameters; } }; + + var importProp = root.Q("p_import"); + importProp.RegisterValueChangedCallback(evt => + { + ImportValues(importProp); + importProp.SetValueWithoutNotify(null); + }); + importProp.objectType = typeof(VRCExpressionParameters); + importProp.allowSceneObjects = false; return root; } + private void ImportValues(ObjectField importProp) + { + var known = new HashSet(); + + var target = (ModularAvatarParameters)this.target; + foreach (var parameter in target.parameters) + { + if (!parameter.isPrefix) + { + known.Add(parameter.nameOrPrefix); + } + } + + Undo.RecordObject(target, "Import parameters"); + + var source = (VRCExpressionParameters)importProp.value; + if (source == null) + { + return; + } + + foreach (var parameter in source.parameters) + { + if (!known.Contains(parameter.name)) + { + ParameterSyncType pst; + + switch (parameter.valueType) + { + case VRCExpressionParameters.ValueType.Bool: pst = ParameterSyncType.Bool; break; + case VRCExpressionParameters.ValueType.Float: pst = ParameterSyncType.Float; break; + case VRCExpressionParameters.ValueType.Int: pst = ParameterSyncType.Int; break; + default: pst = ParameterSyncType.Float; break; + } + + if (!parameter.networkSynced) + { + pst = ParameterSyncType.NotSynced; + } + + target.parameters.Add(new ParameterConfig() + { + internalParameter = false, + nameOrPrefix = parameter.name, + isPrefix = false, + remapTo = "", + syncType = pst, + defaultValue = parameter.defaultValue, + saved = parameter.saved, + }); + } + } + } + private void DetectParameters() { var known = new HashSet(); diff --git a/Editor/Inspector/Parameters/DefaultValueField.cs b/Editor/Inspector/Parameters/DefaultValueField.cs index 6ba6cd22..f29f217b 100644 --- a/Editor/Inspector/Parameters/DefaultValueField.cs +++ b/Editor/Inspector/Parameters/DefaultValueField.cs @@ -20,7 +20,8 @@ namespace nadena.dev.modular_avatar.core.editor private readonly DropdownField _boolField; private ParameterSyncType _syncType; - + private bool _hasInitialBinding; + public DefaultValueField() { // Hidden binding elements @@ -57,28 +58,39 @@ namespace nadena.dev.modular_avatar.core.editor { _numberField.style.display = DisplayStyle.Flex; _boolField.style.display = DisplayStyle.None; - OnUpdateNumberValue(_numberField.value); + OnUpdateNumberValue(_numberField.value, true); } else { _numberField.style.display = DisplayStyle.None; _boolField.style.display = DisplayStyle.Flex; - OnUpdateBoolValue(_boolField.value); + OnUpdateBoolValue(_boolField.value, true); } } - private void OnUpdateNumberValue(string value) + private void OnUpdateNumberValue(string value, bool implicitUpdate = false) { + // Upon initial creation, sometimes the OnUpdateSyncType fires before we receive the initial value event. + // In this case, suppress the update to avoid losing data. + if (implicitUpdate && !_hasInitialBinding) return; + + var theValue = _defaultValueField.value; if (string.IsNullOrWhiteSpace(value)) { - _defaultValueField.value = 0; + if (!implicitUpdate) + { + _defaultValueField.value = 0; + } + + theValue = _defaultValueField.value; + _hasExplicitDefaultValueField.value = false; } else if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed) && !float.IsNaN(parsed) && !float.IsInfinity(parsed)) { - _defaultValueField.value = _syncType switch + theValue = _defaultValueField.value = _syncType switch { ParameterSyncType.Int => Mathf.FloorToInt(Mathf.Clamp(parsed, 0, 255)), ParameterSyncType.Float => Mathf.Clamp(parsed, -1, 1), @@ -88,11 +100,15 @@ namespace nadena.dev.modular_avatar.core.editor _hasExplicitDefaultValueField.value = true; } - UpdateVisibleField(_defaultValueField.value, _hasExplicitDefaultValueField.value); + UpdateVisibleField(theValue, _hasExplicitDefaultValueField.value); } - private void OnUpdateBoolValue(string value) + private void OnUpdateBoolValue(string value, bool implicitUpdate = false) { + // Upon initial creation, sometimes the OnUpdateSyncType fires before we receive the initial value event. + // In this case, suppress the update to avoid losing data. + if (implicitUpdate && !_hasInitialBinding) return; + _defaultValueField.value = value == V_True ? 1 : 0; _hasExplicitDefaultValueField.value = value != V_None; @@ -101,6 +117,8 @@ namespace nadena.dev.modular_avatar.core.editor private void UpdateVisibleField(float value, bool hasExplicitValue) { + _hasInitialBinding = true; + if (hasExplicitValue || Mathf.Abs(value) > 0.0000001) { _numberField.SetValueWithoutNotify(value.ToString(CultureInfo.InvariantCulture)); diff --git a/Editor/Inspector/Parameters/ParameterConfigDrawer.cs b/Editor/Inspector/Parameters/ParameterConfigDrawer.cs index 09bff39d..878a1fda 100644 --- a/Editor/Inspector/Parameters/ParameterConfigDrawer.cs +++ b/Editor/Inspector/Parameters/ParameterConfigDrawer.cs @@ -81,6 +81,12 @@ namespace nadena.dev.modular_avatar.core.editor.Parameters updateRemapToPlaceholder(); + foreach (var elem in root.Query().Build()) + { + // Prevent keypresses from bubbling up + elem.RegisterCallback(evt => evt.StopPropagation(), TrickleDown.NoTrickleDown); + } + return root; } diff --git a/Editor/Inspector/Parameters/Parameters.uss b/Editor/Inspector/Parameters/Parameters.uss index 4d243bb1..80b51b48 100644 --- a/Editor/Inspector/Parameters/Parameters.uss +++ b/Editor/Inspector/Parameters/Parameters.uss @@ -1,5 +1,6 @@ #ListViewContainer { margin-top: 4px; + max-height: 500px; } .horizontal { diff --git a/Editor/Inspector/Parameters/ParametersMainUI.uxml b/Editor/Inspector/Parameters/ParametersMainUI.uxml index 4bb53140..e91178d4 100644 --- a/Editor/Inspector/Parameters/ParametersMainUI.uxml +++ b/Editor/Inspector/Parameters/ParametersMainUI.uxml @@ -12,7 +12,6 @@ show-border="true" show-foldout-header="false" name="Parameters" - item-height="100" binding-path="parameters" style="flex-grow: 1;" /> @@ -33,5 +32,7 @@ /> + + \ No newline at end of file diff --git a/Editor/Inspector/RemoveVertexColorEditor.cs b/Editor/Inspector/RemoveVertexColorEditor.cs new file mode 100644 index 00000000..8e086567 --- /dev/null +++ b/Editor/Inspector/RemoveVertexColorEditor.cs @@ -0,0 +1,39 @@ +using System.Diagnostics.CodeAnalysis; +using UnityEditor; +using static nadena.dev.modular_avatar.core.editor.Localization; + +namespace nadena.dev.modular_avatar.core.editor +{ + [CustomPropertyDrawer(typeof(ModularAvatarRemoveVertexColor.RemoveMode))] + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal class RVCModeDrawer : EnumDrawer + { + protected override string localizationPrefix => "remove-vertex-color.mode"; + } + + [CustomEditor(typeof(ModularAvatarRemoveVertexColor))] + internal class RemoveVertexColorEditor : MAEditorBase + { + private SerializedProperty _p_mode; + + protected void OnEnable() + { + _p_mode = serializedObject.FindProperty(nameof(ModularAvatarRemoveVertexColor.Mode)); + } + + protected override void OnInnerInspectorGUI() + { + serializedObject.Update(); + + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(_p_mode, G("remove-vertex-color.mode")); + + if (EditorGUI.EndChangeCheck()) + { + serializedObject.ApplyModifiedProperties(); + } + + ShowLanguageUI(); + } + } +} \ No newline at end of file diff --git a/Editor/Inspector/RemoveVertexColorEditor.cs.meta b/Editor/Inspector/RemoveVertexColorEditor.cs.meta new file mode 100644 index 00000000..a4ba2d29 --- /dev/null +++ b/Editor/Inspector/RemoveVertexColorEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bfcaf601e9f94ba2900e66d66f469037 +timeCreated: 1733085477 \ No newline at end of file diff --git a/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs b/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs index 9a278278..9bec32a3 100644 --- a/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs +++ b/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs @@ -19,6 +19,7 @@ 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() @@ -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 + { + public DragAndDropManipulator(VisualElement targetElement, ModularAvatarShapeChanger targetComponent) + : base(targetElement, targetComponent) { } + + protected override bool FilterGameObject(GameObject obj) + { + if (obj.TryGetComponent(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); diff --git a/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss b/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss index adff445e..e74734ff 100644 --- a/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss +++ b/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss @@ -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); +} diff --git a/Editor/Inspector/SyncParameterSequenceEditor.cs b/Editor/Inspector/SyncParameterSequenceEditor.cs new file mode 100644 index 00000000..284e5fa1 --- /dev/null +++ b/Editor/Inspector/SyncParameterSequenceEditor.cs @@ -0,0 +1,101 @@ +using System; +using UnityEditor; +using UnityEngine; +using VRC.SDK3.Avatars.ScriptableObjects; +using static nadena.dev.modular_avatar.core.editor.Localization; + +namespace nadena.dev.modular_avatar.core.editor +{ + [CustomEditor(typeof(ModularAvatarSyncParameterSequence))] + [CanEditMultipleObjects] + public class SyncParameterSequenceEditor : MAEditorBase + { + private SerializedProperty _p_platform; + private SerializedProperty _p_parameters; + + private void OnEnable() + { + _p_platform = serializedObject.FindProperty(nameof(ModularAvatarSyncParameterSequence.PrimaryPlatform)); + _p_parameters = serializedObject.FindProperty(nameof(ModularAvatarSyncParameterSequence.Parameters)); + } + + protected override void OnInnerInspectorGUI() + { + serializedObject.Update(); + + EditorGUI.BeginChangeCheck(); + +#if MA_VRCSDK3_AVATARS + var disable = false; +#else + bool disable = true; +#endif + + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (disable) + // ReSharper disable HeuristicUnreachableCode + { + EditorGUILayout.HelpBox(S("general.vrcsdk-required"), MessageType.Warning); + } + // ReSharper restore HeuristicUnreachableCode + + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + using (new EditorGUI.DisabledGroupScope(disable)) + { + EditorGUILayout.PropertyField(_p_platform, G("sync-param-sequence.platform")); + GUILayout.BeginHorizontal(); + + var label = G("sync-param-sequence.parameters"); + var sizeCalc = EditorStyles.objectField.CalcSize(label); + EditorGUILayout.PropertyField(_p_parameters, label); + + if (GUILayout.Button(G("sync-param-sequence.create-asset"), + GUILayout.ExpandWidth(false), + GUILayout.Height(sizeCalc.y) + )) + { + CreateParameterAsset(); + } + + GUILayout.EndHorizontal(); + } + + if (EditorGUI.EndChangeCheck()) + { + serializedObject.ApplyModifiedProperties(); + } + + ShowLanguageUI(); + } + + private void CreateParameterAsset() + { +#if MA_VRCSDK3_AVATARS + Transform avatarRoot = null; + if (targets.Length == 1) + { + avatarRoot = + RuntimeUtil.FindAvatarTransformInParents(((ModularAvatarSyncParameterSequence)target).transform); + } + + var assetName = "Avatar"; + if (avatarRoot != null) assetName = avatarRoot.gameObject.name; + + assetName += " SyncedParams"; + + var file = EditorUtility.SaveFilePanelInProject("Create new parameter asset", assetName, "asset", + "Create a new parameter asset"); + + var obj = CreateInstance(); + obj.parameters = Array.Empty(); + obj.isEmpty = true; + + AssetDatabase.CreateAsset(obj, file); + Undo.RegisterCreatedObjectUndo(obj, "Create parameter asset"); + + _p_parameters.objectReferenceValue = obj; + serializedObject.ApplyModifiedProperties(); +#endif + } + } +} \ No newline at end of file diff --git a/Editor/Inspector/SyncParameterSequenceEditor.cs.meta b/Editor/Inspector/SyncParameterSequenceEditor.cs.meta new file mode 100644 index 00000000..e0d762ac --- /dev/null +++ b/Editor/Inspector/SyncParameterSequenceEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bf6030b7fa704997885767897d1acba0 +timeCreated: 1733090792 \ No newline at end of file diff --git a/Editor/Localization/en-US.json b/Editor/Localization/en-US.json index 08c1dcf3..e01648df 100644 --- a/Editor/Localization/en-US.json +++ b/Editor/Localization/en-US.json @@ -51,6 +51,7 @@ "merge_parameter.ui.add_button": "Add", "merge_parameter.ui.details": "Parameter Configuration", "merge_parameter.ui.overrideAnimatorDefaults": "Override Animator Defaults", + "merge_parameter.ui.importFromAsset": "Import from asset", "merge_armature.merge_target": "Merge Target", "merge_armature.merge_target.tooltip": "The armature (or subtree) to merge this object into", "merge_armature.prefix": "Prefix", @@ -86,6 +87,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!", @@ -149,6 +151,9 @@ "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", + "error.singleton": "[MA-0011] Only one instance of {0} is allowed in an avatar", "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", @@ -281,7 +286,19 @@ "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", - + "menuitem.label.long_name.tooltip": "Use a long name which may contain rich text and line breaks.", - "menuitem.label.gameobject_name.tooltip": "Use the GameObject name." + "menuitem.label.gameobject_name.tooltip": "Use the GameObject name.", + + "remove-vertex-color.mode": "Mode", + "remove-vertex-color.mode.Remove": "Remove Vertex Colors", + "remove-vertex-color.mode.DontRemove": "Keep Vertex Colors", + "general.vrcsdk-required": "This component requires the VRCSDK to function.", + "sync-param-sequence.platform": "Primary Platform", + "sync-param-sequence.platform.tooltip": "When building for this platform, Modular Avatar will record all expression parameters for use on other platform builds", + "sync-param-sequence.parameters": "Common parameters asset", + "sync-param-sequence.parameters.tooltip": "The asset to store common parameters in. Do not use the same Expression Parameters that you have set in your avatar descriptor.", + "sync-param-sequence.create-asset": "New", + "sync-param-sequence.create-asset.tooltip": "Creates a new expression parameters asset" + } diff --git a/Editor/Localization/ja-JP.json b/Editor/Localization/ja-JP.json index 94c6022a..269545ff 100644 --- a/Editor/Localization/ja-JP.json +++ b/Editor/Localization/ja-JP.json @@ -47,6 +47,7 @@ "merge_parameter.ui.add_button": "追加", "merge_parameter.ui.details": "パラメーターの詳細設定", "merge_parameter.ui.overrideAnimatorDefaults": "アニメーターでの初期値を設定", + "merge_parameter.ui.importFromAsset": "アセットからインポートする", "merge_armature.merge_target": "統合先", "merge_armature.merge_target.tooltip": "このオブジェクトを統合先のアーマチュアに統合します", "merge_armature.prefix": "接頭辞", @@ -82,6 +83,7 @@ "merge_armature.lockmode.bidirectional.body": "アバターと統合されるアーマチュアは常に同じ位置になります。元のアバターを操作するアニメーションを作る時に便利かもしれません。有効にするためには、統合されるアーマチュアの位置を統合先と同じにしておく必要があります。", "merge_armature.reset_pos": "位置を元アバターに合わせてリセット", "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": "実行", @@ -145,6 +147,9 @@ "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] このオブジェクトの親を置き換え先に指定できません", + "error.singleton": "[MA-0011] {0} はアバターに一個しか存在できません", "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がありますが、メッシュがありません。", @@ -272,5 +277,15 @@ "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": "条件" + "ro_sim.effect_group.conditions": "条件", + "remove-vertex-color.mode": "モード", + "remove-vertex-color.mode.Remove": "頂点カラーを削除する", + "remove-vertex-color.mode.DontRemove": "頂点カラーを削除しない", + "general.vrcsdk-required": "このコンポーネントにはVRCSDKが必要です。", + "sync-param-sequence.platform": "主要プラットホーム", + "sync-param-sequence.platform.tooltip": "このプラットホームでビルドすると、他のプラットホームを合わせるためにパラメーターを記録します。", + "sync-param-sequence.parameters": "共用パラメーターアセット", + "sync-param-sequence.parameters.tooltip": "共用パラメーターがこのアセットに保持されます。アバターデスクリプターに使われるアセットを流用しないでください。", + "sync-param-sequence.create-asset": "新規作成", + "sync-param-sequence.create-asset.tooltip": "新しい共用パラメーターアセットを作成します" } diff --git a/Editor/Localization/zh-Hant.json b/Editor/Localization/zh-Hant.json index 21cd5a61..bdc8b921 100644 --- a/Editor/Localization/zh-Hant.json +++ b/Editor/Localization/zh-Hant.json @@ -251,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": "清除所有覆寫", diff --git a/Editor/Menu/MenuExtractor.cs b/Editor/Menu/MenuExtractor.cs index 89ee232c..9f670382 100644 --- a/Editor/Menu/MenuExtractor.cs +++ b/Editor/Menu/MenuExtractor.cs @@ -119,9 +119,11 @@ namespace nadena.dev.modular_avatar.core.editor internal static VRCExpressionsMenu.Control CloneControl(VRCExpressionsMenu.Control c) { + var type = c.type != 0 ? c.type : VRCExpressionsMenu.Control.ControlType.Button; + return new VRCExpressionsMenu.Control() { - type = c.type, + type = type, name = c.name, icon = c.icon, parameter = new VRCExpressionsMenu.Control.Parameter() { name = c.parameter?.name }, diff --git a/Editor/MergeAnimatorProcessor.cs b/Editor/MergeAnimatorProcessor.cs index 50efd939..aa4dbcbc 100644 --- a/Editor/MergeAnimatorProcessor.cs +++ b/Editor/MergeAnimatorProcessor.cs @@ -65,6 +65,7 @@ namespace nadena.dev.modular_avatar.core.editor mergeSessions.Clear(); var descriptor = avatarGameObject.GetComponent(); + if (!descriptor) return; if (descriptor.baseAnimationLayers != null) InitSessions(descriptor.baseAnimationLayers); if (descriptor.specialAnimationLayers != null) InitSessions(descriptor.specialAnimationLayers); @@ -246,6 +247,18 @@ namespace nadena.dev.modular_avatar.core.editor var stateMachineQueue = new Queue(); 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); } diff --git a/Editor/MergeArmatureHook.cs b/Editor/MergeArmatureHook.cs index cd0d05cc..e9896fdb 100644 --- a/Editor/MergeArmatureHook.cs +++ b/Editor/MergeArmatureHook.cs @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2022 bd_ @@ -116,6 +116,24 @@ namespace nadena.dev.modular_avatar.core.editor { RetainBoneReferences(c as Component); } + + foreach (var smr in avatarGameObject.transform.GetComponentsInChildren(true)) + { + // If the root bone has been offset, or has a different sign for its scale, we need to retain it. + // see https://github.com/bdunderscore/modular-avatar/pull/1355 + // (we avoid retaining otherwise to avoid excess bone transforms) + + if (smr.rootBone == null || smr.rootBone.parent == null) continue; + + var root = smr.rootBone; + var parent = root.parent; + + if ((parent.position - root.position).sqrMagnitude > 0.000001f + || Vector3.Dot(parent.localScale.normalized, root.localScale.normalized) < 0.9999f) + { + BoneDatabase.RetainMergedBone(smr.rootBone); + } + } new RetargetMeshes().OnPreprocessAvatar(avatarGameObject, BoneDatabase, PathMappings); } diff --git a/Editor/MeshSettingsPass.cs b/Editor/MeshSettingsPass.cs index 4382fc07..05db1d32 100644 --- a/Editor/MeshSettingsPass.cs +++ b/Editor/MeshSettingsPass.cs @@ -1,5 +1,9 @@ -using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; using UnityEngine; +using Object = UnityEngine.Object; namespace nadena.dev.modular_avatar.core.editor { @@ -50,12 +54,12 @@ namespace nadena.dev.modular_avatar.core.editor or ModularAvatarMeshSettings.InheritMode.Inherit or ModularAvatarMeshSettings.InheritMode.DontSet or ModularAvatarMeshSettings.InheritMode.SetOrInherit), _): - throw new System.InvalidOperationException($"Logic failure: invalid InheritMode: {currentMode}"); + throw new InvalidOperationException($"Logic failure: invalid InheritMode: {currentMode}"); case (_, not (ModularAvatarMeshSettings.InheritMode.Set or ModularAvatarMeshSettings.InheritMode.Inherit or ModularAvatarMeshSettings.InheritMode.DontSet or ModularAvatarMeshSettings.InheritMode.SetOrInherit)): - throw new System.ArgumentOutOfRangeException(nameof(srcMode), $"Invalid InheritMode: {srcMode}"); + throw new ArgumentOutOfRangeException(nameof(srcMode), $"Invalid InheritMode: {srcMode}"); // If current value is came from Set or DontSet, it should not be changed case (ModularAvatarMeshSettings.InheritMode.Set, _): @@ -144,9 +148,57 @@ namespace nadena.dev.modular_avatar.core.editor if (newMesh) context.SaveAsset(newMesh); } - smr.rootBone = settings.RootBone; - smr.localBounds = settings.Bounds; + + var settingsRootBone = settings.RootBone; + settingsRootBone = settingsRootBone == null ? smr.transform : settingsRootBone; + var smrRootBone = smr.rootBone; + smrRootBone = smrRootBone == null ? smr.transform : smrRootBone; + + if (IsInverted(smrRootBone) != IsInverted(settingsRootBone)) + { + smr.rootBone = GetInvertedRootBone(settingsRootBone); + + var bounds = settings.Bounds; + var center = bounds.center; + center.x *= -1; + bounds.center = center; + smr.localBounds = bounds; + } + else + { + smr.rootBone = settings.RootBone; + smr.localBounds = settings.Bounds; + } } } + + private bool IsInverted(Transform bone) + { + var inverseCount = 0; + + var scale = bone.lossyScale; + if (scale.x < 0) inverseCount += 1; + if (scale.y < 0) inverseCount += 1; + if (scale.z < 0) inverseCount += 1; + + return (inverseCount % 2) != 0; + } + private Dictionary invertedRootBoneCache = new(); + private Transform GetInvertedRootBone(Transform rootBone) + { + if (invertedRootBoneCache.TryGetValue(rootBone, out var cache)) { return cache; } + + var invertedRootBone = new GameObject($"{rootBone.gameObject.name}-InvertedRootBone"); + EditorUtility.CopySerialized(rootBone, invertedRootBone.transform); + invertedRootBone.transform.parent = rootBone; + + var transform = invertedRootBone.transform; + var scale = transform.localScale; + scale.x *= -1; + transform.localScale = scale; + + invertedRootBoneCache[rootBone] = transform; + return transform; + } } -} \ No newline at end of file +} diff --git a/Editor/MiscPreview.meta b/Editor/MiscPreview.meta new file mode 100644 index 00000000..3e9c5ff4 --- /dev/null +++ b/Editor/MiscPreview.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ea61a438a5d54a289c6abbb1e05c56da +timeCreated: 1733085642 \ No newline at end of file diff --git a/Editor/MiscPreview/RemoveVertexColorPreview.cs b/Editor/MiscPreview/RemoveVertexColorPreview.cs new file mode 100644 index 00000000..e1e8a4e0 --- /dev/null +++ b/Editor/MiscPreview/RemoveVertexColorPreview.cs @@ -0,0 +1,121 @@ +#nullable enable + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using nadena.dev.ndmf.preview; +using UnityEngine; + +namespace nadena.dev.modular_avatar.core.editor +{ + internal class RemoveVertexColorPreview : IRenderFilter + { + private static string ToPathString(ComputeContext ctx, Transform t) + { + return string.Join("/", ctx.ObservePath(t).Select(t2 => t2.gameObject.name).Reverse()); + } + + public ImmutableList GetTargetGroups(ComputeContext context) + { + var roots = context.GetAvatarRoots(); + var removers = roots + .SelectMany(r => context.GetComponentsInChildren(r, true)) + .Select(rvc => (ToPathString(context, rvc.transform), + context.Observe(rvc, r => r.Mode) == ModularAvatarRemoveVertexColor.RemoveMode.Remove)) + .OrderBy(pair => pair.Item1) + .ToList(); + var targets = roots.SelectMany( + r => context.GetComponentsInChildren(r, true) + .Concat( + context.GetComponentsInChildren(r, true) + .SelectMany(mf => context.GetComponents(mf.gameObject)) + ) + ); + + targets = targets.Where(target => + { + var stringPath = ToPathString(context, target.transform); + var index = removers.BinarySearch((stringPath, true)); + + if (index >= 0) + { + // There is a component on this mesh + return true; + } + + var priorIndex = ~index - 1; + if (priorIndex < 0) return false; // no match + + var (maybeParent, mode) = removers[priorIndex]; + if (!stringPath.StartsWith(maybeParent)) return false; // no parent matched + return mode; + }); + + return targets.Select(RenderGroup.For).ToImmutableList(); + } + + public Task Instantiate(RenderGroup group, IEnumerable<(Renderer, Renderer)> proxyPairs, + ComputeContext context) + { + Dictionary conversionMap = new(); + + foreach (var (_, proxy) in proxyPairs) + { + Component c = proxy; + if (!(c is SkinnedMeshRenderer)) + { + c = context.GetComponent(proxy.gameObject); + } + + if (c == null) continue; + + RemoveVertexColorPass.ForceRemove(_ => false, c, conversionMap); + } + + return Task.FromResult(new Node(conversionMap.Values.FirstOrDefault())); + } + + private class Node : IRenderFilterNode + { + private readonly Mesh? _theMesh; + + public Node(Mesh? theMesh) + { + _theMesh = theMesh; + } + + public Task Refresh(IEnumerable<(Renderer, Renderer)> proxyPairs, ComputeContext context, + RenderAspects updatedAspects) + { + if (updatedAspects.HasFlag(RenderAspects.Mesh)) return Task.FromResult(null); + if (_theMesh == null) return Task.FromResult(null); + + return Task.FromResult(this); + } + + public RenderAspects WhatChanged => RenderAspects.Mesh; + + public void Dispose() + { + if (_theMesh != null) Object.DestroyImmediate(_theMesh); + } + + public void OnFrame(Renderer original, Renderer proxy) + { + if (_theMesh == null) return; + + switch (proxy) + { + case SkinnedMeshRenderer smr: smr.sharedMesh = _theMesh; break; + default: + { + var mf = proxy.GetComponent(); + if (mf != null) mf.sharedMesh = _theMesh; + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Editor/MiscPreview/RemoveVertexColorPreview.cs.meta b/Editor/MiscPreview/RemoveVertexColorPreview.cs.meta new file mode 100644 index 00000000..28b9cb3d --- /dev/null +++ b/Editor/MiscPreview/RemoveVertexColorPreview.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b05d5c04f86b4924bf8acdd135448463 +timeCreated: 1733085648 \ No newline at end of file diff --git a/Editor/ObjectReferenceFixer.cs b/Editor/ObjectReferenceFixer.cs index b6fe69e6..c0ddcf80 100644 --- a/Editor/ObjectReferenceFixer.cs +++ b/Editor/ObjectReferenceFixer.cs @@ -11,30 +11,62 @@ namespace nadena.dev.modular_avatar.core { private static ComputeContext _context; - private static PrefabStage _lastStage; + private static int? _lastStage; + private static int? GetCurrentContentsRootId(out GameObject contentsRoot) + { + contentsRoot = null; + + var stage = PrefabStageUtility.GetCurrentPrefabStage(); + if (stage == null || stage.prefabContentsRoot == null) return null; + + contentsRoot = stage.prefabContentsRoot; + + return stage.prefabContentsRoot.GetInstanceID(); + } + [InitializeOnLoadMethod] private static void Init() { EditorApplication.delayCall += ProcessObjectReferences; EditorApplication.update += () => { - if (PrefabStageUtility.GetCurrentPrefabStage() != _lastStage) _context?.Invalidate?.Invoke(); + var curStage = GetCurrentContentsRootId(out _); + + if (curStage != _lastStage) + { + _context?.Invalidate?.Invoke(); + } + }; + EditorApplication.playModeStateChanged += state => + { + if (state == PlayModeStateChange.EnteredEditMode) + { + EditorApplication.delayCall += ProcessObjectReferences; + } }; } private static void ProcessObjectReferences() { - _lastStage = PrefabStageUtility.GetCurrentPrefabStage(); + if (EditorApplication.isPlayingOrWillChangePlaymode) + { + _context = null; + return; + } + + _lastStage = GetCurrentContentsRootId(out var contentsRoot); + + AvatarObjectReference.InvalidateAll(); _context = new ComputeContext("ObjectReferenceFixer"); _context.InvokeOnInvalidate(typeof(ObjectReferenceFixer), _ => ProcessObjectReferences()); IEnumerable withReferences = _context.GetComponentsByType(); - if (_lastStage != null) + if (contentsRoot != null) withReferences = withReferences.Concat( - _context.GetComponentsInChildren(_lastStage.prefabContentsRoot, true) + _context.GetComponentsInChildren(contentsRoot, true) ); foreach (var obj in withReferences) @@ -56,10 +88,26 @@ namespace nadena.dev.modular_avatar.core foreach (var (targetObject, referencePath, objRef) in references) { - if (targetObject == null) continue; - _context.ObservePath(targetObject.transform); + var resolvedTarget = objRef.Get(component); + if (objRef.Get(component) == null) continue; + if (targetObject == null) + { + Undo.RecordObject(component, ""); + objRef.targetObject = resolvedTarget; + dirty = true; + } + else + { + // Direct object reference always wins in the event of a conflict. + resolvedTarget = targetObject; + } - if (!targetObject.transform.IsChildOf(avatar.transform)) continue; + foreach (var t in _context.ObservePath(resolvedTarget.transform)) + { + _context.Observe(t.gameObject, g => g.name); + } + + if (!resolvedTarget.transform.IsChildOf(avatar.transform)) continue; if (objRef.IsConsistent(avatar)) continue; @@ -80,4 +128,4 @@ namespace nadena.dev.modular_avatar.core } } } -} \ No newline at end of file +} diff --git a/Editor/OptimizationPasses/PruneParametersPass.cs b/Editor/OptimizationPasses/PruneParametersPass.cs index a38b3655..801fb3e9 100644 --- a/Editor/OptimizationPasses/PruneParametersPass.cs +++ b/Editor/OptimizationPasses/PruneParametersPass.cs @@ -9,6 +9,8 @@ namespace nadena.dev.modular_avatar.core.editor { protected override void Execute(ndmf.BuildContext context) { + if (!context.AvatarDescriptor) return; + var expParams = context.AvatarDescriptor.expressionParameters; if (expParams != null && context.IsTemporaryAsset(expParams)) { diff --git a/Editor/ParamsUsage/MAParametersIntrospection.cs b/Editor/ParamsUsage/MAParametersIntrospection.cs index 1924c2d0..1d9bf2e3 100644 --- a/Editor/ParamsUsage/MAParametersIntrospection.cs +++ b/Editor/ParamsUsage/MAParametersIntrospection.cs @@ -59,7 +59,8 @@ namespace nadena.dev.modular_avatar.core.editor public IEnumerable GetSuppliedParameters(ndmf.BuildContext context = null) { - return _component.parameters.Select(p => + return _component.parameters + .Select(p => { AnimatorControllerParameterType paramType; bool animatorOnly = false; @@ -87,7 +88,7 @@ namespace nadena.dev.modular_avatar.core.editor _component, PluginDefinition.Instance, paramType) { IsAnimatorOnly = animatorOnly, - WantSynced = !p.localOnly, + WantSynced = !p.localOnly && !animatorOnly, IsHidden = p.internalParameter, DefaultValue = p.defaultValue }; diff --git a/Editor/PluginDefinition/PluginDefinition.cs b/Editor/PluginDefinition/PluginDefinition.cs index 69f179d7..ffc77033 100644 --- a/Editor/PluginDefinition/PluginDefinition.cs +++ b/Editor/PluginDefinition/PluginDefinition.cs @@ -2,7 +2,6 @@ using System; using nadena.dev.modular_avatar.animation; -using nadena.dev.modular_avatar.core.ArmatureAwase; using nadena.dev.modular_avatar.core.editor.plugin; using nadena.dev.modular_avatar.editor.ErrorReporting; using nadena.dev.ndmf; @@ -57,30 +56,41 @@ namespace nadena.dev.modular_avatar.core.editor.plugin #endif seq.WithRequiredExtension(typeof(AnimationServicesContext), _s2 => { +#if MA_VRCSDK3_AVATARS seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute()) .PreviewingWith(new ShapeChangerPreview(), new ObjectSwitcherPreview(), new MaterialSetterPreview()); + + // TODO: We currently run this above MergeArmaturePlugin, because Merge Armature might destroy + // game objects which contain Menu Installers. It'd probably be better however to teach Merge Armature + // to retain those objects? maybe? + seq.Run(MenuInstallPluginPass.Instance); +#endif + seq.Run(MergeArmaturePluginPass.Instance); seq.Run(BoneProxyPluginPass.Instance); +#if MA_VRCSDK3_AVATARS seq.Run(VisibleHeadAccessoryPluginPass.Instance); +#endif seq.Run("World Fixed Object", ctx => new WorldFixedObjectProcessor().Process(ctx) ); seq.Run(ReplaceObjectPluginPass.Instance); #if MA_VRCSDK3_AVATARS seq.Run(BlendshapeSyncAnimationPluginPass.Instance); -#endif seq.Run(GameObjectDelayDisablePass.Instance); +#endif seq.Run(ConstraintConverterPass.Instance); }); #if MA_VRCSDK3_AVATARS - seq.Run(MenuInstallPluginPass.Instance); seq.Run(PhysbonesBlockerPluginPass.Instance); seq.Run("Fixup Expressions Menu", ctx => { var maContext = ctx.Extension().BuildContext; FixupExpressionsMenuPass.FixupExpressionsMenu(maContext); }); + seq.Run(SyncParameterSequencePass.Instance); #endif + seq.Run(RemoveVertexColorPass.Instance).PreviewingWith(new RemoveVertexColorPreview()); seq.Run(RebindHumanoidAvatarPass.Instance); seq.Run("Purge ModularAvatar components", ctx => { @@ -207,6 +217,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin } } +#if MA_VRCSDK3_AVATARS class VisibleHeadAccessoryPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) @@ -214,6 +225,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin new VisibleHeadAccessoryProcessor(MAContext(context)).Process(); } } +#endif class ReplaceObjectPluginPass : MAPass { diff --git a/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs b/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs index c3bded7d..6a488a5a 100644 --- a/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs +++ b/Editor/ReactiveObjects/AnimationGeneration/AnimatedProperty.cs @@ -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); + } } } \ No newline at end of file diff --git a/Editor/ReactiveObjects/AnimationGeneration/ControlCondition.cs b/Editor/ReactiveObjects/AnimationGeneration/ControlCondition.cs index 33368ea5..f61b33e0 100644 --- a/Editor/ReactiveObjects/AnimationGeneration/ControlCondition.cs +++ b/Editor/ReactiveObjects/AnimationGeneration/ControlCondition.cs @@ -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); + } } } \ No newline at end of file diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs index 68ae3154..497046ab 100644 --- a/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs +++ b/Editor/ReactiveObjects/AnimationGeneration/ReactionRule.cs @@ -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); + } } } \ No newline at end of file diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs index f84d0b30..7cc2e403 100644 --- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs +++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.LocateReactions.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +#if MA_VRCSDK3_AVATARS +using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using nadena.dev.ndmf.preview; using UnityEngine; @@ -38,6 +40,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(root, true); + + foreach (var bss in components) + { + var localMesh = _computeContext.GetComponent(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(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) { @@ -124,50 +197,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 objectGroups, GameObject root) @@ -244,4 +347,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs index b7482b27..228f215c 100644 --- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs +++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectAnalyzer.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +#if MA_VRCSDK3_AVATARS +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using nadena.dev.modular_avatar.animation; @@ -18,6 +19,11 @@ namespace nadena.dev.modular_avatar.core.editor private readonly ndmf.BuildContext _context; private readonly AnimationServicesContext _asc; private Dictionary _simulationInitialStates; + + public const string BlendshapePrefix = "blendShape."; + public const string DeletedShapePrefix = "deletedShape."; + + public bool OptimizeShapes = true; public ImmutableDictionary ForcePropertyOverrides { get; set; } = ImmutableDictionary.Empty; @@ -58,7 +64,6 @@ namespace nadena.dev.modular_avatar.core.editor { public Dictionary Shapes; public Dictionary InitialStates; - public HashSet DeletedShapes; } private static PropCache _analysisCache; @@ -86,7 +91,6 @@ namespace nadena.dev.modular_avatar.core.editor /// /// The avatar root /// A dictionary of target property to initial state (float or UnityEngine.Object) - /// A hashset of blendshape properties which are always deleted /// public AnalysisResult Analyze( GameObject root @@ -98,9 +102,10 @@ namespace nadena.dev.modular_avatar.core.editor { result.Shapes = new(); result.InitialStates = new(); - result.DeletedShapes = new(); return result; } + + LocateBlendshapeSyncs(root); Dictionary shapes = FindShapes(root); FindObjectToggles(shapes, root); @@ -109,7 +114,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; @@ -124,7 +129,7 @@ namespace nadena.dev.modular_avatar.core.editor foreach (var cond in rule.ControllingConditions) { var paramName = cond.Parameter; - if (ForcePropertyOverrides.TryGetValue(paramName, out var value)) + if (ForcePropertyOverrides?.TryGetValue(paramName, out var value) == true) { cond.InitialValue = value; } @@ -144,7 +149,7 @@ namespace nadena.dev.modular_avatar.core.editor HashSet toggledObjects = new(); if (asc == null) return; - + foreach (var targetProp in shapes.Keys) if (targetProp is { TargetObject: GameObject go, PropertyName: "m_IsActive" }) toggledObjects.Add(go); @@ -165,7 +170,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); } @@ -264,39 +269,26 @@ namespace nadena.dev.modular_avatar.core.editor } /// - /// Determine initial state and deleted shapes for all properties + /// Determine initial state for all properties /// /// /// - /// - private void PreprocessShapes(Dictionary shapes, out Dictionary initialStates, out HashSet deletedShapes) + private void PreprocessShapes(Dictionary shapes, + out Dictionary 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(); - deletedShapes = new HashSet(); - + 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) @@ -308,9 +300,10 @@ 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); } } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs index 793fba66..2d73a816 100644 --- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs +++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPass.cs @@ -1,8 +1,8 @@ -#region +#if MA_VRCSDK3_AVATARS +#region using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using nadena.dev.modular_avatar.animation; using UnityEditor; @@ -34,6 +34,8 @@ namespace nadena.dev.modular_avatar.core.editor internal void Execute() { + if (!context.AvatarDescriptor) return; + // 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; @@ -42,10 +44,11 @@ namespace nadena.dev.modular_avatar.core.editor var shapes = analysis.Shapes; var initialStates = analysis.InitialStates; - var deletedShapes = analysis.DeletedShapes; GenerateActiveSelfProxies(shapes); + ProcessMeshDeletion(initialStates, shapes); + ProcessInitialStates(initialStates, shapes); ProcessInitialAnimatorVariables(shapes); @@ -53,8 +56,6 @@ namespace nadena.dev.modular_avatar.core.editor { ProcessShapeKey(groups); } - - ProcessMeshDeletion(deletedShapes); } private void GenerateActiveSelfProxies(Dictionary shapes) @@ -85,7 +86,7 @@ namespace nadena.dev.modular_avatar.core.editor initialValues[condition.Parameter] = condition.InitialValue; } } - } + } private void ProcessInitialStates(Dictionary initialStates, Dictionary shapes) @@ -225,30 +226,65 @@ namespace nadena.dev.modular_avatar.core.editor #region Mesh processing - private void ProcessMeshDeletion(HashSet deletedKeys) + private void ProcessMeshDeletion(Dictionary initialStates, + Dictionary shapes) { - ImmutableDictionary> 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 + }); + } } } @@ -257,10 +293,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); } @@ -300,8 +332,7 @@ namespace nadena.dev.modular_avatar.core.editor var transitionBuffer = new List<(AnimatorState, List)>(); var entryTransitions = new List(); - var initialStateTransitionList = new List(); - transitionBuffer.Add((initialState, initialStateTransitionList)); + transitionBuffer.Add((initialState, new List())); foreach (var group in info.actionGroups.Skip(lastConstant)) { @@ -321,30 +352,33 @@ namespace nadena.dev.modular_avatar.core.editor var conditions = GetTransitionConditions(asc, group); - if (!group.Inverted) + foreach (var (st, transitions) in transitionBuffer) { - var transition = new AnimatorStateTransition + if (!group.Inverted) { - isExit = true, - hasExitTime = false, - duration = 0, - hasFixedDuration = true, - conditions = (AnimatorCondition[])conditions.Clone() - }; - initialStateTransitionList.Add(transition); - } - else - { - foreach (var cond in conditions) - { - initialStateTransitionList.Add(new AnimatorStateTransition + var transition = new AnimatorStateTransition { isExit = true, hasExitTime = false, duration = 0, hasFixedDuration = true, - conditions = new[] { InvertCondition(cond) } - }); + conditions = (AnimatorCondition[])conditions.Clone() + }; + transitions.Add(transition); + } + else + { + foreach (var cond in conditions) + { + transitions.Add(new AnimatorStateTransition + { + isExit = true, + hasExitTime = false, + duration = 0, + hasFixedDuration = true, + conditions = new[] { InvertCondition(cond) } + }); + } } } @@ -582,3 +616,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } + +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPrepass.cs b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPrepass.cs index 37e0de57..cec2bf17 100644 --- a/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPrepass.cs +++ b/Editor/ReactiveObjects/AnimationGeneration/ReactiveObjectPrepass.cs @@ -1,4 +1,5 @@ -using nadena.dev.ndmf; +#if MA_VRCSDK3_AVATARS +using nadena.dev.ndmf; using UnityEditor.Animations; using UnityEngine; @@ -14,11 +15,11 @@ namespace nadena.dev.modular_avatar.core.editor protected override void Execute(ndmf.BuildContext context) { - var hasShapeChanger = context.AvatarRootObject.GetComponentInChildren() != null; + var hasShapeChanger = context.AvatarRootObject.GetComponentInChildren(true) != null; var hasObjectSwitcher = - context.AvatarRootObject.GetComponentInChildren() != null; + context.AvatarRootObject.GetComponentInChildren(true) != null; var hasMaterialSetter = - context.AvatarRootObject.GetComponentInChildren() != null; + context.AvatarRootObject.GetComponentInChildren(true) != null; if (hasShapeChanger || hasObjectSwitcher || hasMaterialSetter) { var clip = new AnimationClip(); @@ -53,4 +54,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/MaterialSetterPreview.cs b/Editor/ReactiveObjects/MaterialSetterPreview.cs index a181429d..9b1a8e59 100644 --- a/Editor/ReactiveObjects/MaterialSetterPreview.cs +++ b/Editor/ReactiveObjects/MaterialSetterPreview.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +#if MA_VRCSDK3_AVATARS +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; @@ -144,4 +145,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/MenuItemPreviewCondition.cs b/Editor/ReactiveObjects/MenuItemPreviewCondition.cs index 1d8f8629..04ce486a 100644 --- a/Editor/ReactiveObjects/MenuItemPreviewCondition.cs +++ b/Editor/ReactiveObjects/MenuItemPreviewCondition.cs @@ -1,4 +1,5 @@ -using System; +#if MA_VRCSDK3_AVATARS +using System; using System.Collections.Generic; using nadena.dev.ndmf; using nadena.dev.ndmf.preview; @@ -70,4 +71,5 @@ namespace nadena.dev.modular_avatar.core.editor return _context.Observe(mami, _ => mami.isDefault); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/ObjectTogglePreview.cs b/Editor/ReactiveObjects/ObjectTogglePreview.cs index 45b4e9fa..3d138ed0 100644 --- a/Editor/ReactiveObjects/ObjectTogglePreview.cs +++ b/Editor/ReactiveObjects/ObjectTogglePreview.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +#if MA_VRCSDK3_AVATARS +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; @@ -104,4 +105,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/ParameterAssignerPass.cs b/Editor/ReactiveObjects/ParameterAssignerPass.cs index 9b891f84..f7512050 100644 --- a/Editor/ReactiveObjects/ParameterAssignerPass.cs +++ b/Editor/ReactiveObjects/ParameterAssignerPass.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +#if MA_VRCSDK3_AVATARS +using System.Collections.Generic; using System.Linq; using nadena.dev.ndmf; +using UnityEditor.Animations; using UnityEngine; +using VRC.SDK3.Avatars.Components; using VRC.SDK3.Avatars.ScriptableObjects; namespace nadena.dev.modular_avatar.core.editor @@ -141,6 +144,12 @@ namespace nadena.dev.modular_avatar.core.editor { mami.Control.value = defaultValue.GetValueOrDefault(); } + else if (p != null && p.valueType != VRCExpressionParameters.ValueType.Int) + { + // For a float or bool value, we don't really have a lot of good choices, so just set it to + // 1 + mami.Control.value = 1; + } else { while (usedValues.Contains(nextValue)) nextValue++; @@ -185,6 +194,26 @@ namespace nadena.dev.modular_avatar.core.editor expParams.parameters = expParams.parameters.Concat(newParameters.Values).ToArray(); } + + var mamiWithRC = _mamiByParam.Where(kvp => kvp.Value.Any( + component => component.TryGetComponent(out _) + )).ToList(); + + if (mamiWithRC.Count > 0) + { + // This make sures the parameters are correctly merged into the FX layer. + var mergeAnimator = context.AvatarRootObject.AddComponent(); + mergeAnimator.layerType = VRCAvatarDescriptor.AnimLayerType.FX; + mergeAnimator.deleteAttachedAnimator = false; + mergeAnimator.animator = new AnimatorController + { + parameters = mamiWithRC.Select(kvp => new AnimatorControllerParameter + { + name = kvp.Key, + type = AnimatorControllerParameterType.Float, + }).ToArray(), + }; + } } internal static ControlCondition AssignMenuItemParameter( @@ -205,7 +234,8 @@ namespace nadena.dev.modular_avatar.core.editor if (simulationInitialStates != null) { var isDefault = mami.isDefault; - if (isDefaultOverrides?.TryGetValue(paramName, out var target) == true) + ModularAvatarMenuItem target = null; + if (isDefaultOverrides?.TryGetValue(paramName, out target) == true) isDefault = ReferenceEquals(mami, target); if (isDefault) @@ -236,4 +266,5 @@ namespace nadena.dev.modular_avatar.core.editor }; } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/ShapeChangerPreview.cs b/Editor/ReactiveObjects/ShapeChangerPreview.cs index d17c7a37..d3cde839 100644 --- a/Editor/ReactiveObjects/ShapeChangerPreview.cs +++ b/Editor/ReactiveObjects/ShapeChangerPreview.cs @@ -1,4 +1,5 @@ -#region +#if MA_VRCSDK3_AVATARS +#region using System; using System.Collections.Generic; @@ -72,8 +73,8 @@ namespace nadena.dev.modular_avatar.core.editor var analysis = ReactiveObjectAnalyzer.CachedAnalyze(context, avatarRoot); var shapes = analysis.Shapes; - ImmutableDictionary>.Builder rendererStates = - ImmutableDictionary.CreateBuilder>( + var rendererStates = + ImmutableDictionary.CreateBuilder>( ); var avatarRootTransform = avatarRoot.transform; @@ -83,16 +84,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.Empty; rendererStates[r] = states; } @@ -101,16 +115,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 ShapesToGroups(GameObject avatarRoot, ImmutableDictionary> shapes) @@ -265,4 +295,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/Simulator/ROSimulator.cs b/Editor/ReactiveObjects/Simulator/ROSimulator.cs index 3aaf3237..4d192515 100644 --- a/Editor/ReactiveObjects/Simulator/ROSimulator.cs +++ b/Editor/ReactiveObjects/Simulator/ROSimulator.cs @@ -1,4 +1,5 @@ -using System; +#if MA_VRCSDK3_AVATARS +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -256,7 +257,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; @@ -264,6 +265,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); @@ -471,7 +473,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator var f_set_inactive = effectGroup.Q("effect__set-inactive"); var f_value = effectGroup.Q("effect__value"); var f_material = effectGroup.Q("effect__material"); - var f_delete = effectGroup.Q("effect__deleted"); + var f_delete = effectGroup.Q("effect__deleted"); f_target_component.style.display = DisplayStyle.None; f_target_component.SetEnabled(false); @@ -504,9 +506,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); @@ -635,4 +638,5 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator ve_inactive.style.display = activeState ? DisplayStyle.None : DisplayStyle.Flex; } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/ReactiveObjects/Simulator/ROSimulatorButton.cs b/Editor/ReactiveObjects/Simulator/ROSimulatorButton.cs index 285f4c6b..0d1eced6 100644 --- a/Editor/ReactiveObjects/Simulator/ROSimulatorButton.cs +++ b/Editor/ReactiveObjects/Simulator/ROSimulatorButton.cs @@ -1,5 +1,7 @@ using nadena.dev.modular_avatar.core.editor; +#if MA_VRCSDK3_AVATARS using nadena.dev.modular_avatar.core.editor.Simulator; +#endif using UnityEditor; using UnityEngine; using UnityEngine.UIElements; @@ -42,11 +44,13 @@ namespace nadena.dev.modular_avatar.core.editor private void OpenDebugger() { +#if MA_VRCSDK3_AVATARS GameObject target = Selection.activeGameObject; if (ReferenceObject is Component c) target = c.gameObject; else if (ReferenceObject is GameObject go) target = go; ROSimulator.OpenDebugger(target); +#endif } } } \ No newline at end of file diff --git a/Editor/ReactiveObjects/Simulator/StateOverrideController.cs b/Editor/ReactiveObjects/Simulator/StateOverrideController.cs index 74d9c45e..4392f953 100644 --- a/Editor/ReactiveObjects/Simulator/StateOverrideController.cs +++ b/Editor/ReactiveObjects/Simulator/StateOverrideController.cs @@ -1,4 +1,6 @@ -using nadena.dev.modular_avatar.core.editor.Simulator; +#if MA_VRCSDK3_AVATARS +using System; +using nadena.dev.modular_avatar.core.editor.Simulator; using UnityEditor; using UnityEngine.UIElements; @@ -74,4 +76,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Editor/RemoveVertexColorPass.cs b/Editor/RemoveVertexColorPass.cs new file mode 100644 index 00000000..3886b32c --- /dev/null +++ b/Editor/RemoveVertexColorPass.cs @@ -0,0 +1,93 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using nadena.dev.ndmf; +using UnityEditor; +using UnityEngine; +using UnityEngine.Rendering; +using Object = UnityEngine.Object; + +namespace nadena.dev.modular_avatar.core.editor +{ + internal class RemoveVertexColorPass : Pass + { + protected override void Execute(ndmf.BuildContext context) + { + var removers = context.AvatarRootTransform.GetComponentsInChildren(true)!; + + Dictionary conversionMap = new(); + + foreach (var remover in removers) + { + foreach (var smr in remover!.GetComponentsInChildren(true)) + { + TryRemove(context.IsTemporaryAsset, smr, conversionMap); + } + + foreach (var mf in remover.GetComponentsInChildren(true)) + { + TryRemove(context.IsTemporaryAsset, mf, conversionMap); + } + } + } + + private const string PropPath = "m_Mesh"; + + private static void TryRemove( + Func isTempAsset, + Component c, + Dictionary conversionMap + ) + { + var nearestRemover = c.GetComponentInParent()!; + if (nearestRemover.Mode != ModularAvatarRemoveVertexColor.RemoveMode.Remove) return; + + ForceRemove(isTempAsset, c, conversionMap); + } + + internal static void ForceRemove(Func isTempAsset, Component c, + Dictionary conversionMap) + { + var obj = new SerializedObject(c); + var prop = obj.FindProperty("m_Mesh"); + if (prop == null) + { + throw new Exception("Property not found: " + PropPath); + } + + var mesh = prop.objectReferenceValue as Mesh; + if (mesh == null) + { + return; + } + + var originalMesh = mesh; + + if (conversionMap.TryGetValue(mesh, out var converted)) + { + prop.objectReferenceValue = converted; + obj.ApplyModifiedPropertiesWithoutUndo(); + return; + } + + if (mesh.GetVertexAttributes().All(va => va.attribute != VertexAttribute.Color)) + { + // no-op + return; + } + + if (!isTempAsset(mesh)) + { + mesh = Object.Instantiate(mesh); + prop.objectReferenceValue = mesh; + obj.ApplyModifiedPropertiesWithoutUndo(); + } + + mesh.colors = null; + + conversionMap[originalMesh] = mesh; + } + } +} \ No newline at end of file diff --git a/Editor/RemoveVertexColorPass.cs.meta b/Editor/RemoveVertexColorPass.cs.meta new file mode 100644 index 00000000..4c61929e --- /dev/null +++ b/Editor/RemoveVertexColorPass.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a227da6f9f1548c3867b1ed113f28e9d +timeCreated: 1733008734 \ No newline at end of file diff --git a/Editor/RenameParametersHook.cs b/Editor/RenameParametersHook.cs index 5bd401c3..afbf83ba 100644 --- a/Editor/RenameParametersHook.cs +++ b/Editor/RenameParametersHook.cs @@ -136,6 +136,7 @@ namespace nadena.dev.modular_avatar.core.editor if (ResolvedParameter.syncType == ParameterSyncType.NotSynced) { ResolvedParameter.syncType = info.ResolvedParameter.syncType; + ResolvedParameter.localOnly = info.ResolvedParameter.localOnly; } else if (ResolvedParameter.syncType != info.ResolvedParameter.syncType && info.ResolvedParameter.syncType != ParameterSyncType.NotSynced) { TypeConflict = true; @@ -159,6 +160,8 @@ namespace nadena.dev.modular_avatar.core.editor public void OnPreprocessAvatar(GameObject avatar, BuildContext context) { + if (!context.AvatarDescriptor) return; + _context = context; var syncParams = WalkTree(avatar); @@ -732,6 +735,7 @@ namespace nadena.dev.modular_avatar.core.editor ParameterConfig parameterConfig = param; parameterConfig.nameOrPrefix = remapTo; parameterConfig.remapTo = remapTo; + parameterConfig.localOnly = parameterConfig.localOnly || param.syncType == ParameterSyncType.NotSynced; var info = new ParameterInfo() { ResolvedParameter = parameterConfig, diff --git a/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs b/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs index 542cd5ff..3b34cec1 100644 --- a/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs +++ b/Editor/ScaleAdjuster/ScaleAdjusterPreview.cs @@ -344,7 +344,7 @@ 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)) + 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 diff --git a/Editor/SetupOutfit.cs b/Editor/SetupOutfit.cs index 922fbc74..4259209c 100644 --- a/Editor/SetupOutfit.cs +++ b/Editor/SetupOutfit.cs @@ -8,6 +8,7 @@ using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; using static nadena.dev.modular_avatar.core.editor.Localization; +using System; #endregion @@ -145,25 +146,57 @@ namespace nadena.dev.modular_avatar.core.editor out var avatarRoot, out var avatarHips, out var outfitHips) ) return; + Undo.SetCurrentGroupName("Setup Outfit"); + var avatarArmature = avatarHips.transform.parent; var outfitArmature = outfitHips.transform.parent; - if (outfitArmature.GetComponent() == null) + var merge = outfitArmature.GetComponent(); + if (merge == null) + { + merge = Undo.AddComponent(outfitArmature.gameObject); + } else { + Undo.RecordObject(merge, ""); + } + + if (merge.mergeTarget == null || merge.mergeTargetObject == null) { - var merge = Undo.AddComponent(outfitArmature.gameObject); merge.mergeTarget = new AvatarObjectReference(); merge.mergeTarget.referencePath = RuntimeUtil.RelativePath(avatarRoot, avatarArmature.gameObject); merge.LockMode = ArmatureLockMode.BaseToMerge; + } + + if (string.IsNullOrEmpty(merge.prefix) && string.IsNullOrEmpty(merge.suffix)) + { merge.InferPrefixSuffix(); + } - List subRoots = new List(); - HeuristicBoneMapper.RenameBonesByHeuristic(merge, skipped: subRoots); + PrefabUtility.RecordPrefabInstancePropertyModifications(merge); - // If the outfit has an UpperChest bone but the avatar doesn't, add an additional MergeArmature to - // help with this - foreach (var subRoot in subRoots) + var outfitAnimator = outfitRoot.GetComponent(); + var outfitHumanoidBones = GetOutfitHumanoidBones(outfitRoot.transform, outfitAnimator); + var avatarAnimator = avatarRoot.GetComponent(); + List subRoots = new List(); + HeuristicBoneMapper.RenameBonesByHeuristic(merge, skipped: subRoots, outfitHumanoidBones: outfitHumanoidBones, avatarAnimator: avatarAnimator); + + // If the outfit has an UpperChest bone but the avatar doesn't, add an additional MergeArmature to + // help with this + foreach (var subRoot in subRoots) + { + var subConfig = subRoot.GetComponent(); + var subConfigMangleNames = false; + if (subConfig == null) + { + subConfig = Undo.AddComponent(subRoot.gameObject); + } + else + { + Undo.RecordObject(subConfig, ""); + subConfigMangleNames = subConfig.mangleNames; + } + + if (subConfig.mergeTarget == null || subConfig.mergeTargetObject == null) { - var subConfig = Undo.AddComponent(subRoot.gameObject); var parentTransform = subConfig.transform.parent; var parentConfig = parentTransform.GetComponentInParent(); var parentMapping = parentConfig.MapBone(parentTransform); @@ -174,34 +207,51 @@ namespace nadena.dev.modular_avatar.core.editor subConfig.LockMode = ArmatureLockMode.BaseToMerge; subConfig.prefix = merge.prefix; subConfig.suffix = merge.suffix; - subConfig.mangleNames = false; + subConfig.mangleNames = subConfigMangleNames; + PrefabUtility.RecordPrefabInstancePropertyModifications(subConfig); } + } - var avatarRootMatchingArmature = avatarRoot.transform.Find(outfitArmature.gameObject.name); - if (merge.prefix == "" && merge.suffix == "" && avatarRootMatchingArmature != null) - { - // We have an armature whose names exactly match the root armature - this can cause some serious - // confusion in Unity's humanoid armature matching system. Fortunately, we can avoid this by - // renaming a bone close to the root; this will ensure the number of matching bones is small, and - // Unity's heuristics (apparently) will choose the base avatar's armature as the "true" armature. - outfitArmature.name += ".1"; + var avatarRootMatchingArmature = avatarRoot.transform.Find(outfitArmature.gameObject.name); + if (merge.prefix == "" && merge.suffix == "" && avatarRootMatchingArmature != null) + { + // We have an armature whose names exactly match the root armature - this can cause some serious + // confusion in Unity's humanoid armature matching system. Fortunately, we can avoid this by + // renaming a bone close to the root; this will ensure the number of matching bones is small, and + // Unity's heuristics (apparently) will choose the base avatar's armature as the "true" armature. + outfitArmature.name += ".1"; - // Also make sure to refresh the avatar's animator humanoid bone cache. - var avatarAnimator = avatarRoot.GetComponent(); - var humanDescription = avatarAnimator.avatar; - avatarAnimator.avatar = null; - // ReSharper disable once Unity.InefficientPropertyAccess - avatarAnimator.avatar = humanDescription; - } + // Also make sure to refresh the avatar's animator humanoid bone cache. + var humanDescription = avatarAnimator.avatar; + avatarAnimator.avatar = null; + // ReSharper disable once Unity.InefficientPropertyAccess + avatarAnimator.avatar = humanDescription; } FixAPose(avatarRoot, outfitArmature); - if (outfitRoot != null - && outfitRoot.GetComponent() == null - && outfitRoot.GetComponentInParent() == null) + var meshSettings = outfitRoot.GetComponent(); + var mSInheritProbeAnchor = ModularAvatarMeshSettings.InheritMode.SetOrInherit; + var mSInheritBounds = ModularAvatarMeshSettings.InheritMode.SetOrInherit; + + if (outfitRoot != null) + { + if (meshSettings == null) + { + meshSettings = Undo.AddComponent(outfitRoot.gameObject); + } + else + { + Undo.RecordObject(meshSettings, ""); + mSInheritProbeAnchor = meshSettings.InheritProbeAnchor; + mSInheritBounds = meshSettings.InheritBounds; + } + } + + if (meshSettings != null + && (meshSettings.ProbeAnchor == null || meshSettings.ProbeAnchor.Get(meshSettings) == null + || meshSettings.RootBone == null || meshSettings.RootBone.Get(meshSettings) == null)) { - var meshSettings = Undo.AddComponent(outfitRoot.gameObject); Transform rootBone = null, probeAnchor = null; Bounds bounds = ModularAvatarMeshSettings.DEFAULT_BOUNDS; @@ -217,8 +267,8 @@ namespace nadena.dev.modular_avatar.core.editor rootBone = avatarRoot.transform; } - meshSettings.InheritProbeAnchor = ModularAvatarMeshSettings.InheritMode.SetOrInherit; - meshSettings.InheritBounds = ModularAvatarMeshSettings.InheritMode.SetOrInherit; + meshSettings.InheritProbeAnchor = mSInheritProbeAnchor; + meshSettings.InheritBounds = mSInheritBounds; meshSettings.ProbeAnchor = new AvatarObjectReference(); meshSettings.ProbeAnchor.referencePath = RuntimeUtil.RelativePath(avatarRoot, probeAnchor.gameObject); @@ -226,10 +276,43 @@ namespace nadena.dev.modular_avatar.core.editor meshSettings.RootBone = new AvatarObjectReference(); meshSettings.RootBone.referencePath = RuntimeUtil.RelativePath(avatarRoot, rootBone.gameObject); meshSettings.Bounds = bounds; + + PrefabUtility.RecordPrefabInstancePropertyModifications(meshSettings); } } - private static void FixAPose(GameObject avatarRoot, Transform outfitArmature) + internal static Dictionary GetOutfitHumanoidBones(Transform outfitRoot, Animator outfitAnimator) + { + if (outfitAnimator != null) + { + var hipsCheck = outfitAnimator.isHuman ? outfitAnimator.GetBoneTransform(HumanBodyBones.Hips) : null; + if (hipsCheck != null && hipsCheck.parent == outfitRoot) + { + // Sometimes broken rigs can have the hips as a direct child of the root, instead of having + // an intermediate Armature object. We do not currently support this kind of rig, and so we'll + // assume the outfit's humanoid rig is broken and move on to heuristic matching. + outfitAnimator = null; + } else if (hipsCheck == null) { + outfitAnimator = null; + } + } + + Dictionary outfitHumanoidBones = null; + if (outfitAnimator != null) + { + outfitHumanoidBones = new Dictionary(); + foreach (HumanBodyBones boneIndex in Enum.GetValues(typeof(HumanBodyBones))) + { + var bone = boneIndex != HumanBodyBones.LastBone ? outfitAnimator.GetBoneTransform(boneIndex) : null; + if (bone == null) continue; + outfitHumanoidBones[bone] = boneIndex; + } + } + + return outfitHumanoidBones; + } + + internal static void FixAPose(GameObject avatarRoot, Transform outfitArmature, bool strictMode = true) { var mergeArmature = outfitArmature.GetComponent(); if (mergeArmature == null) return; @@ -249,7 +332,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); @@ -259,22 +342,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) @@ -490,6 +578,7 @@ namespace nadena.dev.modular_avatar.core.editor } var hipsCandidates = new List(); + var hipsExtraCandidateRoots = new List(); if (outfitHips == null) { @@ -498,6 +587,23 @@ namespace nadena.dev.modular_avatar.core.editor foreach (Transform child in outfitRoot.transform) { foreach (Transform tempHip in child) + { + if (tempHip.name.Contains(avatarHips.name)) + { + outfitHips = tempHip.gameObject; + // Prefer the first hips we find + break; + } + hipsExtraCandidateRoots.Add(tempHip); + } + + if (outfitHips != null) return true; // found an exact match, bail outgit + } + + // Sometimes, Hips is in deeper place(like root -> Armature -> Armature 1 -> Hips). + foreach (Transform extraCandidateRoot in hipsExtraCandidateRoots) + { + foreach (Transform tempHip in extraCandidateRoot) { if (tempHip.name.Contains(avatarHips.name)) { @@ -511,6 +617,7 @@ namespace nadena.dev.modular_avatar.core.editor } hipsCandidates.Add(avatarHips.name); + hipsExtraCandidateRoots = new List(); // If that doesn't work out, we'll check for heuristic bone mapper mappings. foreach (var hbm in HeuristicBoneMapper.BoneToNameMap[HumanBodyBones.Hips]) @@ -531,6 +638,25 @@ namespace nadena.dev.modular_avatar.core.editor { outfitHips = tempHip.gameObject; } + hipsExtraCandidateRoots.Add(tempHip); + } + } + } + + if (outfitHips == null) + { + // Sometimes, Hips is in deeper place(like root -> Armature -> Armature 1 -> Hips). + foreach (Transform extraCandidateRoot in hipsExtraCandidateRoots) + { + foreach (Transform tempHip in extraCandidateRoot) + { + foreach (var candidate in hipsCandidates) + { + if (HeuristicBoneMapper.NormalizeName(tempHip.name).Contains(candidate)) + { + outfitHips = tempHip.gameObject; + } + } } } } diff --git a/Editor/SyncParameterSequencePass.cs b/Editor/SyncParameterSequencePass.cs new file mode 100644 index 00000000..e6e83d37 --- /dev/null +++ b/Editor/SyncParameterSequencePass.cs @@ -0,0 +1,118 @@ +#nullable enable + +using System; +using System.Collections.Specialized; +using System.Linq; +using nadena.dev.modular_avatar.editor.ErrorReporting; +using nadena.dev.ndmf; +using UnityEditor; +using VRC.SDK3.Avatars.ScriptableObjects; +using static nadena.dev.modular_avatar.core.ModularAvatarSyncParameterSequence; +using Object = UnityEngine.Object; + +namespace nadena.dev.modular_avatar.core.editor +{ + public class SyncParameterSequencePass : Pass + { + private static Platform? CurrentPlatform + { + get + { + switch (EditorUserBuildSettings.activeBuildTarget) + { + case BuildTarget.Android: return Platform.Android; + case BuildTarget.iOS: return Platform.iOS; + case BuildTarget.StandaloneWindows64: return Platform.PC; + case BuildTarget.StandaloneLinux64: return Platform.PC; // for CI + default: return null; + } + } + } + + protected override void Execute(ndmf.BuildContext context) + { + ExecuteStatic(context); + } + + internal static void ExecuteStatic(ndmf.BuildContext context) + { + var avDesc = context.AvatarDescriptor; + + var components = context.AvatarRootObject.GetComponentsInChildren(true); + if (components.Length == 0) return; + if (components.Length > 1) + { + BuildReport.LogFatal("error.singleton", "Sync Parameter Sequence", components.Cast().ToArray()); + return; + } + + var syncComponent = components[0]; + if (syncComponent.Parameters == null) return; + + if (avDesc.expressionParameters == null) return; + var avatarParams = avDesc.expressionParameters; + + if (!context.IsTemporaryAsset(avatarParams)) + { + avatarParams = Object.Instantiate(avatarParams); + avDesc.expressionParameters = avatarParams; + } + + if (syncComponent.Parameters.parameters == null) + { + syncComponent.Parameters.parameters = Array.Empty(); + EditorUtility.SetDirty(syncComponent.Parameters); + } + + // If we're on the primary platform, add in any unknown parameters, and prune if we exceed the limit. + if (CurrentPlatform != null && CurrentPlatform == syncComponent.PrimaryPlatform) + { + var registered = new OrderedDictionary(); + + foreach (var param in syncComponent.Parameters.parameters) + { + if (param == null) continue; + if (!param.networkSynced) continue; + registered[param.name] = param; + } + + foreach (var param in avatarParams.parameters) + { + if (param == null) continue; + if (!param.networkSynced) continue; + registered[param.name] = param; + } + + syncComponent.Parameters.parameters = registered.Values.Cast().ToArray(); + if (!syncComponent.Parameters.IsWithinBudget()) + { + var knownParams = avatarParams.parameters.Where(p => p != null).Select(p => p.name).ToHashSet(); + syncComponent.Parameters.parameters = syncComponent.Parameters.parameters.Where( + p => p != null && knownParams.Contains(p.name) + ).ToArray(); + } + + EditorUtility.SetDirty(syncComponent.Parameters); + } + + // Now copy back... + OrderedDictionary finalParams = new(); + foreach (var param in syncComponent.Parameters.parameters) + { + if (param == null) continue; + if (!param.networkSynced) continue; + finalParams[param.name] = param; + } + + foreach (var param in avatarParams.parameters) + { + if (param == null) continue; + finalParams[param.name] = param; + } + + avatarParams.parameters = finalParams.Values.Cast().ToArray(); + + EditorUtility.SetDirty(avatarParams); + } + } +} \ No newline at end of file diff --git a/Editor/SyncParameterSequencePass.cs.meta b/Editor/SyncParameterSequencePass.cs.meta new file mode 100644 index 00000000..cd61ea01 --- /dev/null +++ b/Editor/SyncParameterSequencePass.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 756425df8aeb4926afceda71bedffa40 +timeCreated: 1733011801 \ No newline at end of file diff --git a/Editor/VisibleHeadAccessoryProcessor.cs b/Editor/VisibleHeadAccessoryProcessor.cs index b5014d2d..54bbcb1a 100644 --- a/Editor/VisibleHeadAccessoryProcessor.cs +++ b/Editor/VisibleHeadAccessoryProcessor.cs @@ -1,4 +1,5 @@ -#region +#if MA_VRCSDK3_AVATARS +#region using System; using System.Collections.Generic; @@ -251,3 +252,5 @@ namespace nadena.dev.modular_avatar.core.editor } } } + +#endif \ No newline at end of file diff --git a/README.md b/README.md index 033d49d3..c4b74d03 100644 --- a/README.md +++ b/README.md @@ -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/). diff --git a/Runtime/Activator.cs b/Runtime/Activator.cs index 9d45d930..6825177e 100644 --- a/Runtime/Activator.cs +++ b/Runtime/Activator.cs @@ -19,7 +19,7 @@ namespace nadena.dev.modular_avatar.core /// initially inactive in the scene (which can have high overhead if the user has a lot of inactive avatars in the /// scene). /// - [AddComponentMenu("")] + [AddComponentMenu("/")] [ExecuteInEditMode] [DefaultExecutionOrder(-9998)] public class Activator : MonoBehaviour, IEditorOnly @@ -30,7 +30,7 @@ namespace nadena.dev.modular_avatar.core } } - [AddComponentMenu("")] + [AddComponentMenu("/")] [ExecuteInEditMode] [DefaultExecutionOrder(-9997)] public class AvatarActivator : MonoBehaviour, IEditorOnly diff --git a/Runtime/ArmatureAwase/MoveIndep.meta b/Runtime/ArmatureAwase/MoveIndep.meta new file mode 100644 index 00000000..da1c9674 --- /dev/null +++ b/Runtime/ArmatureAwase/MoveIndep.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: abec4f397dc74b2f9bba6f71b5e702f3 +timeCreated: 1732395066 \ No newline at end of file diff --git a/Runtime/ArmatureAwase/MoveIndep/MAMoveIndependentlyManager.cs b/Runtime/ArmatureAwase/MoveIndep/MAMoveIndependentlyManager.cs new file mode 100644 index 00000000..6332ce38 --- /dev/null +++ b/Runtime/ArmatureAwase/MoveIndep/MAMoveIndependentlyManager.cs @@ -0,0 +1,647 @@ +using System; +using System.Collections.Generic; +using Unity.Burst; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using UnityEngine; +using UnityEngine.Jobs; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace nadena.dev.modular_avatar.core.armature_lock +{ + internal class MaMoveIndependentlyManager + { + internal static MaMoveIndependentlyManager Instance { get; } = new(); + + private MaMoveIndependentlyManager() + { + _nativeMemoryManager = new NativeMemoryManager(); + + _vpState = _nativeMemoryManager.CreateArray(); + _tpState = _nativeMemoryManager.CreateArray(); + _targetState = _nativeMemoryManager.CreateArray(); + _mappingStates = _nativeMemoryManager.CreateArray(); + + _errorFlags = _nativeMemoryManager.CreateArray(); + _enabled = _nativeMemoryManager.CreateArray(); + _sceneRootParent = _nativeMemoryManager.CreateArray(); + _falseArray = _nativeMemoryManager.CreateArray(); + + _anyError = new NativeArray(1, Allocator.Persistent); + _anyDirty = new NativeArray(1, Allocator.Persistent); + + _nativeMemoryManager.OnSegmentMove += MoveTransforms; +#if UNITY_EDITOR + AssemblyReloadEvents.beforeAssemblyReload += OnDomainUnload; +#endif + } + + private void OnDomainUnload() + { + Dispose(); + } + + private void Dispose() + { + _lastJob.Complete(); + +#if UNITY_EDITOR + AssemblyReloadEvents.beforeAssemblyReload -= OnDomainUnload; +#endif + + if (_virtualParents.isCreated) DeferDestroy.DeferDestroyObj(_virtualParents); + if (_trueParents.isCreated) DeferDestroy.DeferDestroyObj(_trueParents); + if (_targets.isCreated) DeferDestroy.DeferDestroyObj(_targets); + + _nativeMemoryManager.Dispose(); + + if (_anyError.IsCreated) _anyError.Dispose(); + if (_anyDirty.IsCreated) _anyDirty.Dispose(); + } + + private const float PosEpsilon = 0.0000001f; + private const float RotEpsilon = 0.0000001f; + private const float ScaleEpsilon = 0.0000001f; + + // Our basic strategy is to identify all children of MoveIndependently objects, and to find the first parent + // that is not a member of the same MoveIndependently group. We then compute the local transform of the child + // relative to that parent, and keep it constant (unless the true local transform of the child changes). + // + // If an active MAMoveIndep is a child of another MAMoveIndep, we consider it to be ungrouped (even if it's + // named in the parent). + + private readonly NativeMemoryManager _nativeMemoryManager; + + private Transform[] _virtualParentsT; + private Transform[] _trueParentsT; + private Transform[] _targetsT; + + private TransformAccessArray _virtualParents; + private TransformAccessArray _trueParents; + private TransformAccessArray _targets; + + private bool _transformAccessDirty; + + private readonly NativeArrayRef _vpState; + private readonly NativeArrayRef _tpState; + private readonly NativeArrayRef _targetState; + private readonly NativeArrayRef _mappingStates; + private readonly NativeArrayRef _errorFlags; + private NativeArray _anyError, _anyDirty; + private readonly NativeArrayRef _enabled; + private readonly NativeArrayRef _sceneRootParent, _falseArray; + private readonly Dictionary _slotToState = new(); + + private struct MappingState + { + // Our last observed local transform, relative to our actual parent transform + public TransformState TrueLocal; + + // Our last observed local transform, relative to our virtual parent transform + public Matrix4x4 VirtualLocal; + + // The position of our parent relative to our virtual parent + public Matrix4x4 TrueLocalToVirtualLocal; + public bool RequestWriteback, CacheValid; + } + + private class State + { + public MAMoveIndependently MoveIndep; + public ISegment Segment; + } + + private readonly Dictionary _moveIndeps = new(); + private JobHandle _lastJob; + + private bool _isRegistered; + private int _maxComputeDepth; + + private bool UpdateRegistered + { + get => _isRegistered; + set + { + if (value == _isRegistered) return; + if (value) + { + UpdateLoopController.OnMoveIndependentlyUpdate += Update; + } + else + { + UpdateLoopController.OnMoveIndependentlyUpdate -= Update; + } + + _isRegistered = value; + } + } + + private void EnsureTransformCapacity(int targetLength) + { + if (_virtualParentsT == null) + { + _virtualParentsT = new Transform[targetLength]; + _trueParentsT = new Transform[targetLength]; + _targetsT = new Transform[targetLength]; + return; + } + + if (targetLength <= _virtualParentsT.Length) return; + + var newCapacity = Mathf.Max(_virtualParentsT.Length * 2, targetLength); + Array.Resize(ref _virtualParentsT, newCapacity); + Array.Resize(ref _trueParentsT, newCapacity); + Array.Resize(ref _targetsT, newCapacity); + } + + private void MoveTransforms(int oldoffset, int newoffset, int length) + { + Array.Copy(_virtualParentsT, oldoffset, _virtualParentsT, newoffset, length); + Array.Copy(_trueParentsT, oldoffset, _trueParentsT, newoffset, length); + Array.Copy(_targetsT, oldoffset, _targetsT, newoffset, length); + + _transformAccessDirty = true; + } + + private void UpdateTransformAccess() + { + if (!_transformAccessDirty) return; + + UpdateTransformAccess(ref _virtualParents, _virtualParentsT); + UpdateTransformAccess(ref _trueParents, _trueParentsT); + UpdateTransformAccess(ref _targets, _targetsT); + + _transformAccessDirty = false; + } + + private void UpdateTransformAccess(ref TransformAccessArray arr, Transform[] t) + { + if (!arr.isCreated || arr.length != t.Length) + { + if (arr.isCreated) arr.Dispose(); + arr = new TransformAccessArray(t); + } + else + { + arr.SetTransforms(t); + } + } + + private void Update() + { + _lastJob.Complete(); + + UpdateTransformAccess(); + + _anyError[0] = false; + _anyDirty[0] = false; + + var clearErrors = new JClearErrorFlags + { + ErrorFlags = _errorFlags + }; + var clearErrorsHandle = clearErrors.Schedule(_errorFlags.Length, 16); + + var readVp = new JReadTransforms + { + States = _vpState, + Enabled = _enabled, + ErrorFlags = _errorFlags, + SceneRootParent = _sceneRootParent + }; + var readTp = new JReadTransforms + { + States = _tpState, + Enabled = _enabled, + ErrorFlags = _errorFlags, + SceneRootParent = _falseArray + }; + var readTarget = new JReadTransforms + { + States = _targetState, + Enabled = _enabled, + ErrorFlags = _errorFlags, + SceneRootParent = _falseArray + }; + + var readVpHandle = readVp.Schedule(_virtualParents, clearErrorsHandle); + var clearVpHandle = new JClearRootTransforms + { + States = _vpState, + SceneRootParent = _sceneRootParent + }.Schedule(_vpState.Length, 16, readVpHandle); + var readTpHandle = readTp.Schedule(_trueParents, clearErrorsHandle); + var readTargetHandle = readTarget.Schedule(_targets, clearErrorsHandle); + var readHandle = JobHandle.CombineDependencies(clearVpHandle, readTpHandle, readTargetHandle); + + var compute = new JCompute + { + VpState = _vpState, + TpState = _tpState, + TargetState = _targetState, + States = _mappingStates, + AnyDirty = _anyDirty, + AnyError = _anyError, + ErrorFlags = _errorFlags, + Enabled = _enabled + }; + + var computeHandle = compute.Schedule(_mappingStates.Length, 16, readHandle); + _lastJob = computeHandle; + + computeHandle.Complete(); + + List prefabRecord = null; + if (_anyDirty[0]) + { + prefabRecord = new List(); + for (var i = 0; i < _mappingStates.Length; i++) + { + if (_mappingStates[i].RequestWriteback) + { +#if UNITY_EDITOR + Undo.RecordObject(_targets[i], "Move Independently"); +#endif + prefabRecord.Add(_targets[i]); + } + } + } + + var writeback = new JWriteback + { + States = _mappingStates, + Errors = _errorFlags, + Enabled = _enabled, + AnyError = _anyError + }; + + var writebackHandle = writeback.Schedule(_targets, computeHandle); + + _lastJob = writebackHandle; + + writebackHandle.Complete(); + + if (prefabRecord != null) + { + foreach (var transform in prefabRecord) + { +#if UNITY_EDITOR + PrefabUtility.RecordPrefabInstancePropertyModifications(transform); +#endif + } + } + + if (_anyError[0]) + { + List reactivate = new(); + for (var i = 0; i < _mappingStates.Length; i++) + { + if (_errorFlags[i] && _slotToState.TryGetValue(i, out var state)) + { + Deactivate(state); + reactivate.Add(state.MoveIndep); + } + } + + foreach (var moveIndep in reactivate) + { + if (moveIndep != null) Activate(moveIndep); + } + } + } + + internal void Activate(MAMoveIndependently moveIndep) + { + if (!_anyDirty.IsCreated) return; // domain reload timing issues + + if (_moveIndeps.TryGetValue(moveIndep, out var state)) Deactivate(state); + + HashSet groupedTransforms = new(); + groupedTransforms.Add(moveIndep.transform); + + RegisterGroupedTransforms(moveIndep, groupedTransforms); + + List toReregister = new(); + + foreach (var t in groupedTransforms) + { + // If we have a direct child MAMI, we need it to change its virtual parent, so trigger a reregister + // on it. + if (t.TryGetComponent(out var mami) && mami != moveIndep) + { + toReregister.Add(mami); + } + } + + var ptr = moveIndep.transform.parent; + while (ptr != null) + { + var parentMoveIndep = ptr.GetComponentInParent(); + if (parentMoveIndep == null) break; + + RegisterGroupedTransforms(parentMoveIndep, groupedTransforms); + + ptr = parentMoveIndep.transform.parent; + } + + // Compute leaf transforms + List leafTransforms = new(); + Walk(moveIndep.transform); + + var segment = _nativeMemoryManager.Allocate(leafTransforms.Count); + EnsureTransformCapacity(segment.Offset + segment.Length); + _transformAccessDirty = true; + + var virtualParent = moveIndep.transform.parent; + while (virtualParent != null && groupedTransforms.Contains(virtualParent)) + virtualParent = virtualParent.parent; + + for (var i = 0; i < leafTransforms.Count; i++) + { + var j = i + segment.Offset; + _mappingStates[j] = new MappingState + { + + CacheValid = false + }; + + _virtualParentsT[j] = virtualParent; + _trueParentsT[j] = leafTransforms[i].parent; + _targetsT[j] = leafTransforms[i]; + _enabled[j] = true; + _sceneRootParent[j] = virtualParent == null; + _slotToState[j] = state; + } + + _moveIndeps[moveIndep] = new State + { + MoveIndep = moveIndep, + Segment = segment + }; + + UpdateRegistered = true; + + foreach (var mami in toReregister) + { + if (mami != null) Activate(mami); + } + + void Walk(Transform t) + { + foreach (Transform child in t) + { + if (groupedTransforms.Contains(child)) + { + Walk(child); + continue; + } + + leafTransforms.Add(child); + } + } + } + + private void RegisterGroupedTransforms(MAMoveIndependently moveIndep, HashSet groupedTransforms) + { + var candidates = new HashSet(moveIndep.GroupedBones); + candidates.Add(moveIndep.gameObject); + + Walk(moveIndep.transform); + + void Walk(Transform t) + { + if (!candidates.Contains(t.gameObject)) return; + + groupedTransforms.Add(t); + + foreach (Transform child in t) + { + if (child.TryGetComponent(out _)) continue; + + Walk(child); + } + } + } + + internal void Deactivate(MAMoveIndependently moveIndep) + { + if (_moveIndeps.TryGetValue(moveIndep, out var state)) Deactivate(state); + } + + private void Deactivate(State state) + { + if (!_anyDirty.IsCreated) return; // domain reload timing issues + + for (var i = 0; i < state.Segment.Length; i++) + { + var j = i + state.Segment.Offset; + _enabled[j] = false; + _virtualParents[j] = null; + _trueParents[j] = null; + _targets[j] = null; + _slotToState.Remove(j); + } + + _nativeMemoryManager.Free(state.Segment); + _moveIndeps.Remove(state.MoveIndep); + + if (_moveIndeps.Count == 0) UpdateRegistered = false; + } + + [BurstCompile] + private static bool MatDiffers(Matrix4x4 a, Matrix4x4 b) + { + var aPos = a.GetColumn(3); + var bPos = b.GetColumn(3); + + if ((aPos - bPos).sqrMagnitude > PosEpsilon) return true; + + var aRot = a.rotation; + var bRot = b.rotation; + + if (Quaternion.Angle(aRot, bRot) > RotEpsilon) return true; + + var aScale = a.lossyScale; + var bScale = b.lossyScale; + + return (aScale - bScale).sqrMagnitude > ScaleEpsilon; + } + + private struct JClearErrorFlags : IJobParallelFor + { + [WriteOnly] public NativeArray ErrorFlags; + + public void Execute(int index) + { + ErrorFlags[index] = false; + } + } + + // For some reason checking SceneRootParent in JReadTransforms was ignored...? + // Maybe IJobParallelForTransform doesn't execute on null transforms. + private struct JClearRootTransforms : IJobParallelFor + { + [WriteOnly] public NativeArray States; + [ReadOnly] public NativeArray SceneRootParent; + + public void Execute(int index) + { + if (SceneRootParent[index]) + { + States[index] = new TransformState + { + localToWorldMatrix = Matrix4x4.identity, + localRotation = Quaternion.identity, + localScale = Vector3.one, + localPosition = Vector3.zero + }; + } + } + } + + private struct JReadTransforms : IJobParallelForTransform + { + [WriteOnly] public NativeArray States; + + [ReadOnly] public NativeArray Enabled; + [ReadOnly] public NativeArray SceneRootParent; + + [NativeDisableContainerSafetyRestriction] [WriteOnly] + public NativeArray ErrorFlags; + + [BurstCompile] + public void Execute(int index, TransformAccess transform) + { + if (!Enabled[index]) return; + + if (SceneRootParent[index]) return; + + if (!transform.isValid) + { + ErrorFlags[index] = true; + return; + } + + States[index] = new TransformState + { + localToWorldMatrix = transform.localToWorldMatrix, + localRotation = transform.localRotation, + localScale = transform.localScale, + localPosition = transform.localPosition + }; + } + } + + private struct JCompute : IJobParallelFor + { + [ReadOnly] public NativeArray VpState, TpState, TargetState; + + [WriteOnly] [NativeDisableContainerSafetyRestriction] + public NativeArray AnyDirty; + + [WriteOnly] [NativeDisableContainerSafetyRestriction] + public NativeArray AnyError; + + public NativeArray States; + + public NativeArray ErrorFlags; + + [ReadOnly] public NativeArray Enabled; + + [BurstCompile] + public void Execute(int index) + { + if (!Enabled[index]) return; + + var state = States[index]; + var vp = VpState[index]; + var tp = TpState[index]; + var target = TargetState[index]; + + if (ErrorFlags[index]) + { + AnyError[0] = true; + return; + } + + // First, compute the virtual parent transform - we'll need it in any case. + var trueLocalToVirtualLocal = vp.worldToLocalMatrix * tp.localToWorldMatrix; + + state.RequestWriteback = false; + + if (TransformState.Differs(target, state.TrueLocal) || !state.CacheValid) + { + // Our local position changed, so don't try to make any corrections; just remember the new values. + state.CacheValid = true; + state.TrueLocal = target; + state.TrueLocalToVirtualLocal = trueLocalToVirtualLocal; + state.VirtualLocal = trueLocalToVirtualLocal * Matrix4x4.TRS( + state.TrueLocal.localPosition, + state.TrueLocal.localRotation, + state.TrueLocal.localScale + ); + } + else if (MatDiffers(trueLocalToVirtualLocal, state.TrueLocalToVirtualLocal)) + { + // Our local position didn't change, but our virtual parent did, so we need to correct. + // To do this, we take our _old_ virtual local transform, and use it to transform our old true local + // position into virtual local space; we then go from _current_ virtual local space to true local. + var virtualLocalToTrueLocal = trueLocalToVirtualLocal.inverse; + var trueLocal = virtualLocalToTrueLocal * state.VirtualLocal; + + state.TrueLocal = new TransformState + { + localPosition = trueLocal.GetColumn(3), + localRotation = trueLocal.rotation, + localScale = trueLocal.lossyScale + }; + + state.TrueLocalToVirtualLocal = trueLocalToVirtualLocal; + + state.RequestWriteback = true; + AnyDirty[0] = true; + } + + States[index] = state; + } + } + + private struct JWriteback : IJobParallelForTransform + { + [ReadOnly] public NativeArray States; + + [ReadOnly] public NativeArray Errors; + + [ReadOnly] public NativeArray Enabled; + + [NativeDisableContainerSafetyRestriction] [WriteOnly] + public NativeArray AnyError; + + [BurstCompile] + public void Execute(int index, TransformAccess transform) + { + var state = States[index]; + + if (!Enabled[index] || Errors[index] || !state.RequestWriteback) return; + + if (!transform.isValid) + { + Errors[index] = true; + AnyError[0] = true; + return; + } + + var pos = state.TrueLocal.localPosition; + var rot = state.TrueLocal.localRotation; + var scale = state.TrueLocal.localScale; + + transform.localPosition = pos; + transform.localRotation = rot; + transform.localScale = scale; + } + } + } +} \ No newline at end of file diff --git a/Runtime/ArmatureAwase/MoveIndep/MAMoveIndependentlyManager.cs.meta b/Runtime/ArmatureAwase/MoveIndep/MAMoveIndependentlyManager.cs.meta new file mode 100644 index 00000000..01c95468 --- /dev/null +++ b/Runtime/ArmatureAwase/MoveIndep/MAMoveIndependentlyManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 154891b009044835b43580e745f50a9e +timeCreated: 1732394861 \ No newline at end of file diff --git a/Runtime/ArmatureAwase/NativeMemoryManager.cs b/Runtime/ArmatureAwase/NativeMemoryManager.cs index 0f98b920..4f801aa9 100644 --- a/Runtime/ArmatureAwase/NativeMemoryManager.cs +++ b/Runtime/ArmatureAwase/NativeMemoryManager.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using UnityEngine; #endregion @@ -15,12 +14,19 @@ namespace nadena.dev.modular_avatar.core.armature_lock internal NativeArray Array; public static implicit operator NativeArray(NativeArrayRef arrayRef) => arrayRef.Array; + public int Length => Array.Length; public void Dispose() { Array.Dispose(); } + public T this[int key] + { + get => Array[key]; + set => Array[key] = value; + } + public void Resize(int n) { if (Array.Length == n) return; @@ -143,6 +149,11 @@ namespace nadena.dev.modular_avatar.core.armature_lock // We perform trial creations of segments (and then immediately free them if they exceed the bounds of the // array). As such, we clamp the length, rather than throwing an exception. length = Math.Min(length, InUseMask.Array.Length - offset); + + if (length < 0) + { + throw new ArgumentException("negative length"); + } unsafe { diff --git a/Runtime/AvatarObjectReference.cs b/Runtime/AvatarObjectReference.cs index bc6407a0..db3c88df 100644 --- a/Runtime/AvatarObjectReference.cs +++ b/Runtime/AvatarObjectReference.cs @@ -29,6 +29,11 @@ namespace nadena.dev.modular_avatar.core EditorApplication.hierarchyChanged += () => HIERARCHY_CHANGED_SEQ += 1; } #endif + + internal static void InvalidateAll() + { + HIERARCHY_CHANGED_SEQ++; + } public AvatarObjectReference Clone() { diff --git a/Runtime/MAMoveIndependently.cs b/Runtime/MAMoveIndependently.cs index b193e4a2..0ef36c6b 100644 --- a/Runtime/MAMoveIndependently.cs +++ b/Runtime/MAMoveIndependently.cs @@ -1,13 +1,14 @@ using System; -using System.Collections.Generic; using nadena.dev.modular_avatar.core.armature_lock; -using UnityEditor; using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif #if MA_VRCSDK3_AVATARS using VRC.SDKBase; #endif -namespace nadena.dev.modular_avatar.core.ArmatureAwase +namespace nadena.dev.modular_avatar.core { [ExecuteInEditMode] [AddComponentMenu("Modular Avatar/MA Move Independently")] @@ -22,227 +23,35 @@ namespace nadena.dev.modular_avatar.core.ArmatureAwase public GameObject[] GroupedBones { - get => m_groupedBones.Clone() as GameObject[]; + get => m_groupedBones?.Clone() as GameObject[] ?? Array.Empty(); set { m_groupedBones = value.Clone() as GameObject[]; - OnValidate(); + MaMoveIndependentlyManager.Instance.Activate(this); } } - struct ChildState - { - internal Vector3 childLocalPos; - internal Quaternion childLocalRot; - internal Vector3 childLocalScale; - - // The child world position, recorded when we first initialized (or after unexpected child movement) - internal Matrix4x4 childToRoot; - } - - private Dictionary _children = new Dictionary(); - private HashSet _excluded = new HashSet(); - - void Awake() - { - hideFlags = HideFlags.DontSave; - } - - // We need to reparent the TRS values of the children from our prior frame state to the current frame state. - // This is done by computing the world affine matrix for the child in the prior frame, then converting to - // a local affine matrix in the current frame. - private void OnValidate() { - hideFlags = HideFlags.DontSave; - _excluded = new HashSet(); - if (m_groupedBones == null) - { - m_groupedBones = Array.Empty(); - } - - foreach (var grouped in m_groupedBones) - { - if (grouped != null) - { - _excluded.Add(grouped.transform); - } - } - - _priorFramePos = transform.localPosition; - _priorFrameRot = transform.localRotation; - _priorFrameScale = transform.localScale; - - _children.Clear(); - CheckChildren(); - } - - HashSet _observed = new HashSet(); - - private void CheckChildren() - { - _observed.Clear(); - - CheckChildren(transform); - foreach (var obj in m_groupedBones) - { - CheckChildren(obj.transform); - } - - // Remove any children that are no longer children - var toRemove = new List(); - foreach (var child in _children) - { - if (child.Key == null || !_observed.Contains(child.Key)) - { - toRemove.Add(child.Key); - } - } - - foreach (var child in toRemove) - { - _children.Remove(child); - } - } - - private Matrix4x4 ParentTransformMatrix(Transform parent) - { - Matrix4x4 transform = Matrix4x4.TRS( - parent.localPosition, - parent.localRotation, - parent.localScale - ); - - if (_excluded.Contains(parent)) - { - transform = ParentTransformMatrix(parent.parent) * transform; - } - - return transform; - } - - private void CheckChildren(Transform parent) - { - Matrix4x4 parentToRoot = ParentTransformMatrix(parent); - Matrix4x4 rootToParent = parentToRoot.inverse; - - foreach (Transform child in parent) - { - if (_excluded.Contains(child)) continue; - - _observed.Add(child); - - var localPosition = child.localPosition; - var localRotation = child.localRotation; - var localScale = child.localScale; - - if (!ArmatureLockController.MovedThisFrame && _children.TryGetValue(child, out var state)) - { - var deltaPos = localPosition - state.childLocalPos; - var deltaRot = Quaternion.Angle(localRotation, state.childLocalRot); - var deltaScale = (localScale - state.childLocalScale).sqrMagnitude; - - if (deltaPos.magnitude > EPSILON || deltaRot > EPSILON || deltaScale > EPSILON) - { - // The child object was moved in between parent updates; reconstruct its childToRoot to correct - // for this. - var oldChildTRS = Matrix4x4.TRS( - state.childLocalPos, - state.childLocalRot, - state.childLocalScale - ); - - var newChildTRS = Matrix4x4.TRS( - localPosition, - localRotation, - localScale - ); - - state.childToRoot = state.childToRoot * oldChildTRS.inverse * newChildTRS; - } - - Matrix4x4 childNewLocal = rootToParent * state.childToRoot; - - var newPosition = childNewLocal.MultiplyPoint(Vector3.zero); - var newRotation = childNewLocal.rotation; - var newScale = childNewLocal.lossyScale; #if UNITY_EDITOR - Undo.RecordObject(child, Undo.GetCurrentGroupName()); -#endif - - child.localPosition = newPosition; - child.localRotation = newRotation; - child.localScale = newScale; - - state.childLocalPos = child.localPosition; - state.childLocalRot = child.localRotation; - state.childLocalScale = child.localScale; - - _children[child] = state; - - continue; - } - - Matrix4x4 childTRS = Matrix4x4.TRS(localPosition, localRotation, localScale); - - state = new ChildState() + if (!PrefabUtility.IsPartOfPrefabAsset(this)) + { + EditorApplication.delayCall += () => { - childLocalPos = localPosition, - childLocalRot = localRotation, - childLocalScale = localScale, - childToRoot = parentToRoot * childTRS, + if (this != null) MaMoveIndependentlyManager.Instance.Activate(this); }; - - _children[child] = state; } +#endif } private void OnEnable() { - UpdateLoopController.OnMoveIndependentlyUpdate += OnUpdate; + MaMoveIndependentlyManager.Instance.Activate(this); } private void OnDisable() { - UpdateLoopController.OnMoveIndependentlyUpdate -= OnUpdate; - } - - private Vector3 _priorFramePos, _priorFrameScale; - private Quaternion _priorFrameRot; - - void OnUpdate() - { - if (this == null) - { - UpdateLoopController.OnMoveIndependentlyUpdate -= OnUpdate; - return; - } - - var pos = transform.localPosition; - var rot = transform.localRotation; - var scale = transform.localScale; - - var deltaPos = transform.parent.localToWorldMatrix.MultiplyVector(pos - _priorFramePos); - var deltaRot = Quaternion.Angle(rot, _priorFrameRot); - - var deltaScaleX = Mathf.Abs((scale - _priorFrameScale).x) / _priorFrameScale.x; - var deltaScaleY = Mathf.Abs((scale - _priorFrameScale).y) / _priorFrameScale.y; - var deltaScaleZ = Mathf.Abs((scale - _priorFrameScale).z) / _priorFrameScale.z; - - if (float.IsNaN(deltaScaleX) || float.IsInfinity(deltaScaleX)) deltaScaleX = 1; - if (float.IsNaN(deltaScaleY) || float.IsInfinity(deltaScaleY)) deltaScaleY = 1; - if (float.IsNaN(deltaScaleZ) || float.IsInfinity(deltaScaleZ)) deltaScaleZ = 1; - - float maxDeltaScale = Mathf.Max(deltaScaleX, Mathf.Max(deltaScaleY, deltaScaleZ)); - - if (deltaPos.magnitude > EPSILON || deltaRot > EPSILON || maxDeltaScale > 0.001) - { - CheckChildren(); - - _priorFramePos = pos; - _priorFrameRot = rot; - _priorFrameScale = scale; - } + MaMoveIndependentlyManager.Instance.Deactivate(this); } } } \ No newline at end of file diff --git a/Runtime/Menu/ModularAvatarMenuInstaller.cs b/Runtime/Menu/ModularAvatarMenuInstaller.cs index 96a78cc5..75ea8f02 100644 --- a/Runtime/Menu/ModularAvatarMenuInstaller.cs +++ b/Runtime/Menu/ModularAvatarMenuInstaller.cs @@ -1,5 +1,7 @@ #if MA_VRCSDK3_AVATARS +using System; +using JetBrains.Annotations; using UnityEngine; using VRC.SDK3.Avatars.ScriptableObjects; @@ -12,6 +14,16 @@ namespace nadena.dev.modular_avatar.core public VRCExpressionsMenu menuToAppend; public VRCExpressionsMenu installTargetMenu; + internal static Action _openSelectMenu = _ => { }; + + /// + /// Opens the "Select Menu" window, as if the user had clicked this button in the inspector. + /// + [PublicAPI] + public void OpenSelectMenu() + { + _openSelectMenu(this); + } // ReSharper disable once Unity.RedundantEventFunction void Start() diff --git a/Runtime/ModularAvatarConvertConstraints.cs b/Runtime/ModularAvatarConvertConstraints.cs index 1440fa25..f4f5ebdd 100644 --- a/Runtime/ModularAvatarConvertConstraints.cs +++ b/Runtime/ModularAvatarConvertConstraints.cs @@ -6,7 +6,7 @@ namespace nadena.dev.modular_avatar.core #if MA_VRCSDK3_AVATARS [AddComponentMenu("Modular Avatar/MA Convert Constraints")] #else - [AddComponentMenu("")] + [AddComponentMenu("/")] #endif [HelpURL("https://modular-avatar.nadena.dev/docs/reference/convert-constraints?lang=auto")] public class ModularAvatarConvertConstraints : AvatarTagComponent diff --git a/Runtime/ModularAvatarMergeArmature.cs b/Runtime/ModularAvatarMergeArmature.cs index 36e5e36e..98a5a91a 100644 --- a/Runtime/ModularAvatarMergeArmature.cs +++ b/Runtime/ModularAvatarMergeArmature.cs @@ -26,6 +26,8 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; using nadena.dev.modular_avatar.core.armature_lock; using UnityEngine; using UnityEngine.Serialization; @@ -49,6 +51,10 @@ namespace nadena.dev.modular_avatar.core [HelpURL("https://modular-avatar.nadena.dev/docs/reference/merge-armature?lang=auto")] public class ModularAvatarMergeArmature : AvatarTagComponent, IHaveObjReferences { + // Injected by HeuristicBoneMapper + internal static Func NormalizeBoneName; + internal static ImmutableHashSet AllBoneNames; + public AvatarObjectReference mergeTarget = new AvatarObjectReference(); public GameObject mergeTargetObject => mergeTarget.Get(this); @@ -61,6 +67,9 @@ namespace nadena.dev.modular_avatar.core public bool mangleNames = true; + // Inserted from HeuristicBoneMapper(Editor Assembly) with InitializeOnLoadMethod + // We use raw `boneNamePatterns` instead of `BoneToNameMap` because BoneToNameMap requires matching with normalized bone name, but normalizing makes raw prefix/suffix unavailable. + internal static string[][] boneNamePatterns; private ArmatureLockController _lockController; internal Transform MapBone(Transform bone) @@ -200,6 +209,66 @@ namespace nadena.dev.modular_avatar.core } } + class PSCandidate + { + public string prefix, suffix; + public int matches; + + public PSCandidate CountMatches(ModularAvatarMergeArmature merger) + { + var target = merger.mergeTarget.Get(merger).transform; + var source = merger.transform; + + var oldPrefix = merger.prefix; + var oldSuffix = merger.suffix; + + try + { + merger.prefix = prefix; + merger.suffix = suffix; + + matches = merger.GetBonesForLock().Count; + return this; + } + finally + { + merger.prefix = oldPrefix; + merger.suffix = oldSuffix; + } + } + + /// + /// Counts the number of children which take the form prefix // heuristic bone name // suffix + /// + /// + public PSCandidate CountHeuristicMatches(Transform root) + { + int count = 1; + + Walk(root); + + matches = count; + return this; + + void Walk(Transform t) + { + foreach (Transform child in t) + { + if (child.name.StartsWith(prefix) && child.name.EndsWith(suffix)) + { + var boneName = child.name.Substring(prefix.Length, child.name.Length - prefix.Length - suffix.Length); + boneName = NormalizeBoneName(boneName); + if (AllBoneNames.Contains(boneName)) + { + count++; + Walk(child); + } + } + } + } + } + } + public void InferPrefixSuffix() { // We only infer if targeting the armature (below the Hips bone) @@ -212,18 +281,65 @@ namespace nadena.dev.modular_avatar.core // We also require that the attached object has exactly one child (presumably the hips) if (transform.childCount != 1) return; + List candidates = new(); + + // always consider the current configuration + candidates.Add(new PSCandidate() {prefix = prefix, suffix = suffix}.CountMatches(this)); + // Infer the prefix and suffix by comparing the names of the mergeTargetObject's hips with the child of the // GameObject we're attached to. var baseName = hips.name; - var mergeName = transform.GetChild(0).name; + var mergeHips = transform.GetChild(0); + var mergeName = mergeHips.name; - var prefixLength = mergeName.IndexOf(baseName, StringComparison.InvariantCulture); - if (prefixLength < 0) return; + // Classic substring match + { + var prefixLength = mergeName.IndexOf(baseName, StringComparison.InvariantCulture); + if (prefixLength >= 0) + { + var suffixLength = mergeName.Length - prefixLength - baseName.Length; - var suffixLength = mergeName.Length - prefixLength - baseName.Length; + candidates.Add(new PSCandidate() + { + prefix = mergeName.Substring(0, prefixLength), + suffix = mergeName.Substring(mergeName.Length - suffixLength) + }.CountMatches(this)); + } + } - prefix = mergeName.Substring(0, prefixLength); - suffix = mergeName.Substring(mergeName.Length - suffixLength); + // Heuristic match - try to see if we get a better prefix/suffix pattern if we allow for fuzzy-matching of + // bone names. Since our goal is to minimize unnecessary renaming (and potentially failing matches), we do + // this only if the number of heuristic matches is more than twice the number of matches from the static + // pattern above, as using this will force most bones to be renamed. + foreach (var hipNameCandidate in + boneNamePatterns[(int)HumanBodyBones.Hips].OrderByDescending(p => p.Length)) + { + var prefixLength = mergeName.IndexOf(hipNameCandidate, StringComparison.InvariantCultureIgnoreCase); + if (prefixLength < 0) continue; + + var suffixLength = mergeName.Length - prefixLength - hipNameCandidate.Length; + + var prefix = mergeName.Substring(0, prefixLength); + var suffix = mergeName.Substring(mergeName.Length - suffixLength); + + var candidate = new PSCandidate + { + prefix = prefix, + suffix = suffix + }.CountHeuristicMatches(mergeHips); + candidate.matches = (candidate.matches + 1) / 2; + + candidates.Add(candidate); + break; + } + + // Select which candidate to use + var selected = candidates.OrderByDescending(c => c.matches).FirstOrDefault(); + if (selected != null && selected.matches > 0) + { + prefix = selected.prefix; + suffix = selected.suffix; + } if (prefix == "J_Bip_C_") { @@ -242,4 +358,4 @@ namespace nadena.dev.modular_avatar.core if (mergeTarget != null) yield return mergeTarget; } } -} \ No newline at end of file +} diff --git a/Runtime/ModularAvatarSyncParameterSequence.cs b/Runtime/ModularAvatarSyncParameterSequence.cs new file mode 100644 index 00000000..e721a9ef --- /dev/null +++ b/Runtime/ModularAvatarSyncParameterSequence.cs @@ -0,0 +1,31 @@ +using System; +using JetBrains.Annotations; +using UnityEngine; +using VRC.SDK3.Avatars.ScriptableObjects; + +namespace nadena.dev.modular_avatar.core +{ + [AddComponentMenu("Modular Avatar/MA Sync Parameter Sequence")] + [DisallowMultipleComponent] + [HelpURL("https://modular-avatar.nadena.dev/docs/reference/sync-parameter-sequence?lang=auto")] + [PublicAPI] + public class ModularAvatarSyncParameterSequence : AvatarTagComponent + { + [Serializable] + [PublicAPI] + public enum Platform + { + PC, + Android, + iOS + } + + public Platform PrimaryPlatform = Platform.Android; + #if MA_VRCSDK3_AVATARS + public VRCExpressionParameters Parameters; + #else + // preserve settings on non-VRC platforms at least + public UnityEngine.Object Parameters; + #endif + } +} \ No newline at end of file diff --git a/Runtime/ModularAvatarSyncParameterSequence.cs.meta b/Runtime/ModularAvatarSyncParameterSequence.cs.meta new file mode 100644 index 00000000..051758ba --- /dev/null +++ b/Runtime/ModularAvatarSyncParameterSequence.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 934543afe4744213b5621aa13a67e3b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs b/Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs index 0a919dd0..dfe2cf7d 100644 --- a/Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs +++ b/Runtime/ReactiveObjects/ModularAvatarMaterialSetter.cs @@ -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 m_objects = new(); diff --git a/Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs b/Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs index 93b1421a..74cc69a8 100644 --- a/Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs +++ b/Runtime/ReactiveObjects/ModularAvatarObjectToggle.cs @@ -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 m_objects = new(); diff --git a/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs b/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs index d8e1dea5..4fff480b 100644 --- a/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs +++ b/Runtime/ReactiveObjects/ModularAvatarShapeChanger.cs @@ -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 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 GetObjectReferences() { foreach (var shape in m_shapes) diff --git a/Runtime/RemoveVertexColor.cs b/Runtime/RemoveVertexColor.cs new file mode 100644 index 00000000..d8201ad4 --- /dev/null +++ b/Runtime/RemoveVertexColor.cs @@ -0,0 +1,23 @@ +using System; +using JetBrains.Annotations; +using UnityEngine; + +namespace nadena.dev.modular_avatar.core +{ + [AddComponentMenu("Modular Avatar/MA Remove Vertex Color")] + [DisallowMultipleComponent] + [HelpURL("https://modular-avatar.nadena.dev/docs/reference/remove-vertex-color?lang=auto")] + [PublicAPI] + public class ModularAvatarRemoveVertexColor : AvatarTagComponent + { + [Serializable] + [PublicAPI] + public enum RemoveMode + { + Remove, + DontRemove + } + + public RemoveMode Mode = RemoveMode.Remove; + } +} \ No newline at end of file diff --git a/Runtime/RemoveVertexColor.cs.meta b/Runtime/RemoveVertexColor.cs.meta new file mode 100644 index 00000000..d2582877 --- /dev/null +++ b/Runtime/RemoveVertexColor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc5f8bfae24244aeaedcd6c2bb7264f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/Animation/AnimParameterPathRewrite/AnimParameterPathRewritingTest.cs b/UnitTests~/Animation/AnimParameterPathRewrite/AnimParameterPathRewritingTest.cs index cf51e6b2..426b9ad3 100644 --- a/UnitTests~/Animation/AnimParameterPathRewrite/AnimParameterPathRewritingTest.cs +++ b/UnitTests~/Animation/AnimParameterPathRewrite/AnimParameterPathRewritingTest.cs @@ -1,4 +1,6 @@ -using nadena.dev.modular_avatar.core.editor; +#if MA_VRCSDK3_AVATARS + +using nadena.dev.modular_avatar.core.editor; using NUnit.Framework; using UnityEditor; using UnityEngine; @@ -36,4 +38,6 @@ namespace modular_avatar_tests Assert.AreEqual("x", curves[0].propertyName); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/UnitTests~/Animation/AvatarMask/AvatarMaskTest.cs b/UnitTests~/Animation/AvatarMask/AvatarMaskTest.cs index 897e323f..09bf1825 100644 --- a/UnitTests~/Animation/AvatarMask/AvatarMaskTest.cs +++ b/UnitTests~/Animation/AvatarMask/AvatarMaskTest.cs @@ -1,4 +1,6 @@ -using System; +#if MA_VRCSDK3_AVATARS + +using System; using System.Collections.Generic; using System.Linq; using nadena.dev.ndmf; @@ -158,4 +160,6 @@ namespace modular_avatar_tests Assert.IsFalse(state.transformMaskElements.Any(e => e.Item1 == "Armature/Hips/UpperLeg.R")); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/UnitTests~/Animation/BaseLayerReferenceCorrection/BaseLayerReferenceCorrectionTest.cs b/UnitTests~/Animation/BaseLayerReferenceCorrection/BaseLayerReferenceCorrectionTest.cs index e2bed491..2f9bb828 100644 --- a/UnitTests~/Animation/BaseLayerReferenceCorrection/BaseLayerReferenceCorrectionTest.cs +++ b/UnitTests~/Animation/BaseLayerReferenceCorrection/BaseLayerReferenceCorrectionTest.cs @@ -1,4 +1,6 @@ -using System.Linq; +#if MA_VRCSDK3_AVATARS + +using System.Linq; using nadena.dev.ndmf; using NUnit.Framework; using UnityEditor.Animations; @@ -26,4 +28,6 @@ namespace modular_avatar_tests Assert.AreEqual(desiredIndex, alc.layer); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/UnitTests~/Animation/PlayAudio/PlayAudioRemapping.cs b/UnitTests~/Animation/PlayAudio/PlayAudioRemapping.cs index 9ccdc7e3..8bdf1489 100644 --- a/UnitTests~/Animation/PlayAudio/PlayAudioRemapping.cs +++ b/UnitTests~/Animation/PlayAudio/PlayAudioRemapping.cs @@ -1,9 +1,10 @@ -using nadena.dev.modular_avatar.core.editor; +#if MA_VRCSDK3_AVATARS_3_5_2_OR_NEWER + +using nadena.dev.modular_avatar.core.editor; using NUnit.Framework; using UnityEditor.Animations; using VRC.SDK3.Avatars.Components; -#if MA_VRCSDK3_AVATARS_3_5_2_OR_NEWER namespace modular_avatar_tests { public class PlayAudioRemapping : TestBase @@ -28,4 +29,5 @@ namespace modular_avatar_tests } } } + #endif \ No newline at end of file diff --git a/UnitTests~/EasySetupOutfit/PreferFirstHipsMatch.cs b/UnitTests~/EasySetupOutfit/HipsMatchTest.cs similarity index 54% rename from UnitTests~/EasySetupOutfit/PreferFirstHipsMatch.cs rename to UnitTests~/EasySetupOutfit/HipsMatchTest.cs index 717e5332..46195fcf 100644 --- a/UnitTests~/EasySetupOutfit/PreferFirstHipsMatch.cs +++ b/UnitTests~/EasySetupOutfit/HipsMatchTest.cs @@ -4,7 +4,7 @@ using nadena.dev.modular_avatar.core.editor; using NUnit.Framework; using UnityEngine; -public class PreferFirstHipsMatch : TestBase +public class HipsMatchTest : TestBase { [Test] public void SetupHeuristicPrefersFirstHipsMatch() @@ -21,7 +21,28 @@ public class PreferFirstHipsMatch : TestBase var outfit = CreateChild(root, "Outfit"); var outfit_armature = CreateChild(outfit, "Armature"); var outfit_hips = CreateChild(outfit_armature, "Hips"); - + + Assert.IsTrue(SetupOutfit.FindBones(outfit, out var det_av_root, out var det_av_hips, out var det_outfit_hips)); + Assert.AreSame(root, det_av_root); + Assert.AreSame(root_hips, det_av_hips); + Assert.AreSame(outfit_hips, det_outfit_hips); + } + + [Test] + public void TestOutfitDeepHipsMatch() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var root_hips = root.GetComponent().GetBoneTransform(HumanBodyBones.Hips).gameObject; + root_hips.name = "hip"; + + var outfit = CreateChild(root, "Outfit"); + var outfit_armature = CreateChild(outfit, "armature"); + var outfit_armature2 = CreateChild(outfit_armature, "armature2"); + var outfit_hips = CreateChild(outfit_armature2, "hips"); + Assert.IsTrue(SetupOutfit.FindBones(outfit, out var det_av_root, out var det_av_hips, out var det_outfit_hips)); Assert.AreSame(root, det_av_root); Assert.AreSame(root_hips, det_av_hips); diff --git a/UnitTests~/EasySetupOutfit/PreferFirstHipsMatch.cs.meta b/UnitTests~/EasySetupOutfit/HipsMatchTest.cs.meta similarity index 100% rename from UnitTests~/EasySetupOutfit/PreferFirstHipsMatch.cs.meta rename to UnitTests~/EasySetupOutfit/HipsMatchTest.cs.meta diff --git a/UnitTests~/EasySetupOutfit/InferPrefixSuffixTest.cs b/UnitTests~/EasySetupOutfit/InferPrefixSuffixTest.cs new file mode 100644 index 00000000..50634578 --- /dev/null +++ b/UnitTests~/EasySetupOutfit/InferPrefixSuffixTest.cs @@ -0,0 +1,152 @@ +using modular_avatar_tests; +using nadena.dev.modular_avatar.core; +using NUnit.Framework; +using UnityEngine; + +public class InferPrefixSuffixTest : TestBase +{ + [Test] + public void TestNoPrefixSuffix() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var root_hips = root.GetComponent().GetBoneTransform(HumanBodyBones.Hips); + root_hips.name = "hip"; + + var outfit = CreateChild(root, "Outfit"); + var outfit_armature = CreateChild(outfit, "armature"); + var outfit_hips = CreateChild(outfit_armature, "hip"); + + var outfit_mama = outfit_armature.AddComponent(); + outfit_mama.mergeTarget = new AvatarObjectReference(); + outfit_mama.mergeTarget.referencePath = RuntimeUtil.RelativePath(root, root_hips.parent.gameObject); + outfit_mama.LockMode = ArmatureLockMode.BaseToMerge; + + outfit_mama.InferPrefixSuffix(); + + Assert.AreEqual("", outfit_mama.prefix); + Assert.AreEqual("", outfit_mama.suffix); + } + + [Test] + public void TestDifferentHipsName() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var root_hips = root.GetComponent().GetBoneTransform(HumanBodyBones.Hips); + root_hips.name = "hip"; + + var outfit = CreateChild(root, "Outfit"); + var outfit_armature = CreateChild(outfit, "armature"); + var outfit_hips = CreateChild(outfit_armature, "pre_hips.suf"); + + + var outfit_mama = outfit_armature.AddComponent(); + outfit_mama.mergeTarget = new AvatarObjectReference(); + outfit_mama.mergeTarget.referencePath = RuntimeUtil.RelativePath(root, root_hips.parent.gameObject); + outfit_mama.LockMode = ArmatureLockMode.BaseToMerge; + + outfit_mama.InferPrefixSuffix(); + + // Initially, we determine "hip" to be the match + Assert.AreEqual("pre_", outfit_mama.prefix); + Assert.AreEqual("s.suf", outfit_mama.suffix); + + // Now, add the legs. + var outfit_left_leg = CreateChild(outfit_hips, "pre_upleg.l.suf"); + var outfit_right_leg = CreateChild(outfit_hips, "pre_upleg.r.suf"); + + // Now, we match 3 with ".suf" vs 1 with "s.suf", so the inference should change. + outfit_mama.InferPrefixSuffix(); + + Assert.AreEqual("pre_", outfit_mama.prefix); + Assert.AreEqual(".suf", outfit_mama.suffix); + } + + [Test] + public void TestSameHipsName_Multiple() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var root_hips = root.GetComponent().GetBoneTransform(HumanBodyBones.Hips); + root_hips.name = "TEST_HI"; + + var outfit = CreateChild(root, "Outfit"); + var outfit_armature = CreateChild(outfit, "armature"); + var outfit_hips = CreateChild(outfit_armature, "pre_TEST_HI2.suf"); // Make it a little bit different name to confirm it matches the current implementation + var outfit_spine = CreateChild(outfit_hips, "pre_Spine2.suf"); + var outfit_chest = CreateChild(outfit_spine, "pre_Bust2.suf"); + + var outfit_mama = outfit_armature.AddComponent(); + outfit_mama.mergeTarget = new AvatarObjectReference(); + outfit_mama.mergeTarget.referencePath = RuntimeUtil.RelativePath(root, root_hips.parent.gameObject); + outfit_mama.LockMode = ArmatureLockMode.BaseToMerge; + + outfit_mama.InferPrefixSuffix(); + + Assert.AreEqual("pre_", outfit_mama.prefix); + Assert.AreEqual("2.suf", outfit_mama.suffix); + } + + [Test] + public void TestSameHipsName_Single() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var root_hips = root.GetComponent().GetBoneTransform(HumanBodyBones.Hips); + root_hips.name = "TE_HIPS_ST"; + + var outfit = CreateChild(root, "Outfit"); + var outfit_armature = CreateChild(outfit, "armature"); + var outfit_hips = CreateChild(outfit_armature, "pre_TE_HIPS_ST.suf"); + + var outfit_mama = outfit_armature.AddComponent(); + outfit_mama.mergeTarget = new AvatarObjectReference(); + outfit_mama.mergeTarget.referencePath = RuntimeUtil.RelativePath(root, root_hips.parent.gameObject); + outfit_mama.LockMode = ArmatureLockMode.BaseToMerge; + + outfit_mama.InferPrefixSuffix(); + + Assert.AreEqual("pre_", outfit_mama.prefix); + Assert.AreEqual(".suf", outfit_mama.suffix); + } + + [Test] + public void TestSpuriousMatch() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var animator = root.GetComponent(); + var root_hips = animator.GetBoneTransform(HumanBodyBones.Hips); + var root_armature = root_hips.parent; + + root_hips.gameObject.name = "bone_pelvis"; + root_armature.gameObject.name = "bone_root"; + animator.GetBoneTransform(HumanBodyBones.Spine).gameObject.name = "bone_Spine"; + + var outfit = CreateChild(root, "Outfit"); + var outfit_armature = CreateChild(outfit, "bone_root"); + var outfit_hips = CreateChild(outfit_armature, "bone_pelvis"); + var outfit_spine = CreateChild(outfit_hips, "bone_Spine"); + + var outfit_mama = outfit_armature.AddComponent(); + outfit_mama.mergeTarget = new AvatarObjectReference(); + outfit_mama.mergeTarget.referencePath = RuntimeUtil.RelativePath(root, root_armature.gameObject); + outfit_mama.LockMode = ArmatureLockMode.BaseToMerge; + + outfit_mama.InferPrefixSuffix(); + + Assert.AreEqual("", outfit_mama.prefix); + Assert.AreEqual("", outfit_mama.suffix); + } +} diff --git a/UnitTests~/EasySetupOutfit/InferPrefixSuffixTest.cs.meta b/UnitTests~/EasySetupOutfit/InferPrefixSuffixTest.cs.meta new file mode 100644 index 00000000..8fd3b1c4 --- /dev/null +++ b/UnitTests~/EasySetupOutfit/InferPrefixSuffixTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 426df05704d87424baeb85496181868d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/EasySetupOutfit/SetupOutfitRenameTest.cs b/UnitTests~/EasySetupOutfit/SetupOutfitRenameTest.cs new file mode 100644 index 00000000..36d85f34 --- /dev/null +++ b/UnitTests~/EasySetupOutfit/SetupOutfitRenameTest.cs @@ -0,0 +1,71 @@ +using modular_avatar_tests; +using nadena.dev.modular_avatar.core; +using nadena.dev.modular_avatar.core.editor; +using NUnit.Framework; +using UnityEngine; + +public class SetupOutfitRenameTest : TestBase +{ + [Test] + public void TestSetupHumanoidOutfit() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var root_chest = root.GetComponent().GetBoneTransform(HumanBodyBones.Chest); + + var outfit = CreateCommonPrefab("shapell.fbx"); + outfit.transform.SetParent(root.transform); + var outfit_chest = outfit.GetComponent().GetBoneTransform(HumanBodyBones.Chest); + outfit_chest.name = "c"; + + SetupOutfit.SetupOutfitUI(outfit); + + Assert.AreEqual(root_chest.name, outfit_chest.name); + } + + [Test] + public void TestSetupUpperChestOutfit() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var root_armature = root.GetComponent().GetBoneTransform(HumanBodyBones.Hips).parent.gameObject; + var root_chest = root.GetComponent().GetBoneTransform(HumanBodyBones.Chest).gameObject; + + var outfit = CreateChild(root, "Outfit"); + var outfit_armature = CreateChild(outfit, "armature"); + var outfit_hips = CreateChild(outfit_armature, "hips"); + var outfit_spine = CreateChild(outfit_hips, "spine"); + var outfit_chest = CreateChild(outfit_spine, "chest"); + var outfit_upperchest = CreateChild(outfit_chest, "upperchest"); + + SetupOutfit.SetupOutfitUI(outfit); + + Assert.AreSame(root_armature, outfit_armature.GetComponent().mergeTargetObject); + Assert.AreSame(root_chest, outfit_upperchest.GetComponent().mergeTargetObject); + } + + [Test] + public void TestSetupSetupedOutfit() + { + var root = CreateCommonPrefab("shapell.fbx"); +#if MA_VRCSDK3_AVATARS + root.AddComponent(); +#endif + var root_hips = root.GetComponent().GetBoneTransform(HumanBodyBones.Hips); + var root_armature = root.GetComponent().GetBoneTransform(HumanBodyBones.Hips).parent.gameObject; + + var outfit = CreateChild(root, "Outfit"); + var outfit_armature = CreateChild(outfit, "armature"); + var outfit_hips = CreateChild(outfit_armature, "HIP"); + outfit_armature.AddComponent(); + + SetupOutfit.SetupOutfitUI(outfit); + + Assert.AreEqual(root_armature, outfit_armature.GetComponent().mergeTargetObject); + Assert.AreEqual(root_hips.name, outfit_hips.name); + } +} diff --git a/UnitTests~/EasySetupOutfit/SetupOutfitRenameTest.cs.meta b/UnitTests~/EasySetupOutfit/SetupOutfitRenameTest.cs.meta new file mode 100644 index 00000000..38a7ce2f --- /dev/null +++ b/UnitTests~/EasySetupOutfit/SetupOutfitRenameTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 89b8a54f81c4e7244a858b30825de67c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/MergeAnimatorTests/MergeSingleTests.cs b/UnitTests~/MergeAnimatorTests/MergeSingleTests.cs index 1f546212..2da5afbf 100644 --- a/UnitTests~/MergeAnimatorTests/MergeSingleTests.cs +++ b/UnitTests~/MergeAnimatorTests/MergeSingleTests.cs @@ -1,4 +1,6 @@ -using modular_avatar_tests; +#if MA_VRCSDK3_AVATARS + +using modular_avatar_tests; using nadena.dev.modular_avatar.animation; using nadena.dev.modular_avatar.core; using nadena.dev.modular_avatar.core.editor; @@ -47,4 +49,6 @@ namespace UnitTests.MergeAnimatorTests Assert.IsTrue(state.motion.name.StartsWith("Anim2")); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/UnitTests~/MergeAnimatorTests/PreexistingAnimatorParams/PreexistingParamsTest.cs b/UnitTests~/MergeAnimatorTests/PreexistingAnimatorParams/PreexistingParamsTest.cs index 8af5146e..3f1739d7 100644 --- a/UnitTests~/MergeAnimatorTests/PreexistingAnimatorParams/PreexistingParamsTest.cs +++ b/UnitTests~/MergeAnimatorTests/PreexistingAnimatorParams/PreexistingParamsTest.cs @@ -1,3 +1,5 @@ +#if MA_VRCSDK3_AVATARS + using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -35,3 +37,4 @@ public class PreexistingParamsTest : TestBase } } +#endif \ No newline at end of file diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim.meta b/UnitTests~/MergeAnimatorTests/ProxyAnim.meta new file mode 100644 index 00000000..659236e2 --- /dev/null +++ b/UnitTests~/MergeAnimatorTests/ProxyAnim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 130af01284a51c24f99eeb52361a81fb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller b/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller new file mode 100644 index 00000000..9a6333ca --- /dev/null +++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller @@ -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} diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller.meta b/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller.meta new file mode 100644 index 00000000..b2d87165 --- /dev/null +++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/New Animator Controller.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60603c8e68ac87447b02be4e3af6a7bd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs new file mode 100644 index 00000000..974865d7 --- /dev/null +++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs @@ -0,0 +1,32 @@ +#if MA_VRCSDK3_AVATARS + +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().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); + } + } +} + +#endif \ No newline at end of file diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs.meta b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs.meta new file mode 100644 index 00000000..bc3638bd --- /dev/null +++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2cfba229fa1e4c718f5cb5dd579d3319 +timeCreated: 1728166108 \ No newline at end of file diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab new file mode 100644 index 00000000..dff56d51 --- /dev/null +++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab @@ -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 diff --git a/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab.meta b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab.meta new file mode 100644 index 00000000..6ceea0ce --- /dev/null +++ b/UnitTests~/MergeAnimatorTests/ProxyAnim/ProxyAnimTest.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a77dd3314cc88714bb6e9f1ad014cfc8 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/MergeAnimatorTests/SyncedLayerOverrideInSubStatemachine/SyncedLayerOverrideInSubStateMachine.cs b/UnitTests~/MergeAnimatorTests/SyncedLayerOverrideInSubStatemachine/SyncedLayerOverrideInSubStateMachine.cs index 765e1333..ecf3c88a 100644 --- a/UnitTests~/MergeAnimatorTests/SyncedLayerOverrideInSubStatemachine/SyncedLayerOverrideInSubStateMachine.cs +++ b/UnitTests~/MergeAnimatorTests/SyncedLayerOverrideInSubStatemachine/SyncedLayerOverrideInSubStateMachine.cs @@ -1,4 +1,6 @@ -using modular_avatar_tests; +#if MA_VRCSDK3_AVATARS + +using modular_avatar_tests; using nadena.dev.ndmf; using NUnit.Framework; using UnityEditor.Animations; @@ -38,4 +40,6 @@ namespace UnitTests.MergeAnimatorTests.SyncedLayerOverrideInSubStatemachine Assert.NotNull(motion); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/UnitTests~/MergeAnimatorTests/TypeAdjustment/ConvertTransitionTypes.cs b/UnitTests~/MergeAnimatorTests/TypeAdjustment/ConvertTransitionTypes.cs index 0161235d..e0a024de 100644 --- a/UnitTests~/MergeAnimatorTests/TypeAdjustment/ConvertTransitionTypes.cs +++ b/UnitTests~/MergeAnimatorTests/TypeAdjustment/ConvertTransitionTypes.cs @@ -1,3 +1,5 @@ +#if MA_VRCSDK3_AVATARS + using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -268,3 +270,4 @@ public class ConvertTransitionTypes : TestBase } } +#endif \ No newline at end of file diff --git a/UnitTests~/MergeDBT/MergeDirectBlendTreeTests.cs b/UnitTests~/MergeDBT/MergeDirectBlendTreeTests.cs index 188ef484..61c00154 100644 --- a/UnitTests~/MergeDBT/MergeDirectBlendTreeTests.cs +++ b/UnitTests~/MergeDBT/MergeDirectBlendTreeTests.cs @@ -1,3 +1,5 @@ +#if MA_VRCSDK3_AVATARS + using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; @@ -24,3 +26,5 @@ public class MergeDirectBlendTreeTests : TestBase Assert.AreEqual(0, parameters["DEF"]); } } + +#endif \ No newline at end of file diff --git a/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs new file mode 100644 index 00000000..eb8bba90 --- /dev/null +++ b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs @@ -0,0 +1,65 @@ +#if MA_VRCSDK3_AVATARS + +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(), + PropertyName = "blendShape.bottom" + }]; + var m2 = analysis.Shapes[new TargetProp() + { + TargetObject = root.transform.Find("m2").GetComponent(), + PropertyName = "blendShape.bottom" + }]; + var m3 = analysis.Shapes[new TargetProp() + { + TargetObject = root.transform.Find("m3").GetComponent(), + PropertyName = "blendShape.top" + }]; + + Assert.IsTrue(analysis.Shapes.ContainsKey(new TargetProp() + { + TargetObject = root.transform.Find("m1").GetComponent(), + 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); + } + } +} + +#endif \ No newline at end of file diff --git a/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs.meta b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs.meta new file mode 100644 index 00000000..c614946b --- /dev/null +++ b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f74d4e816d0247159d977e21ebc57458 +timeCreated: 1728168066 \ No newline at end of file diff --git a/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab new file mode 100644 index 00000000..c0b98180 --- /dev/null +++ b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab @@ -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} diff --git a/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab.meta b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab.meta new file mode 100644 index 00000000..d6863646 --- /dev/null +++ b/UnitTests~/ReactiveComponent/BlendshapeSyncTest.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 05653f5cab04e764b80709fe866c1b35 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/ReactiveComponent/DeletionTest.prefab b/UnitTests~/ReactiveComponent/DeletionTest.prefab new file mode 100644 index 00000000..6cefc636 --- /dev/null +++ b/UnitTests~/ReactiveComponent/DeletionTest.prefab @@ -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} diff --git a/UnitTests~/ReactiveComponent/DeletionTest.prefab.meta b/UnitTests~/ReactiveComponent/DeletionTest.prefab.meta new file mode 100644 index 00000000..0197de59 --- /dev/null +++ b/UnitTests~/ReactiveComponent/DeletionTest.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a82669288fc87d94db320a2494fd76c5 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/ReactiveComponent/ObjectToggleTests.cs b/UnitTests~/ReactiveComponent/ObjectToggleTests.cs new file mode 100644 index 00000000..30bbfe65 --- /dev/null +++ b/UnitTests~/ReactiveComponent/ObjectToggleTests.cs @@ -0,0 +1,54 @@ +#if MA_VRCSDK3_AVATARS + +using System.Linq; +using modular_avatar_tests; +using nadena.dev.modular_avatar.core; +using nadena.dev.modular_avatar.core.editor; +using NUnit.Framework; +using UnityEditor.Animations; +using UnityEngine; + +namespace UnitTests.ReactiveComponent +{ + internal class ObjectToggleTests : TestBase + { + [Test] + public void WhenObjectIsAlwaysOn_CorrectProxyParameterIsGenerated() + { + var root = CreateRoot("root"); + var obj = CreateChild(root, "obj"); + var toggle = CreateChild(root, "toggle"); + + // Prevent obj from being removed by the GC game objects pass + obj.AddComponent(); + + var toggleComponent = toggle.AddComponent(); + var aor = new AvatarObjectReference(); + aor.Set(obj); + + toggleComponent.Objects = new() + { + new() + { + Active = false, + Object = aor + } + }; + + AvatarProcessor.ProcessAvatar(root); + + // TODO: Ideally we should start using play mode testing for these things... + var fx = (AnimatorController)FindFxController(root).animatorController; + var readableProp = fx.parameters.FirstOrDefault( + p => p.name.StartsWith("__MA/ReadableProp/obj/UnityEngine.GameObject/m_IsActive") + ); + + Assert.IsNotNull(readableProp); + Assert.AreEqual(readableProp.defaultFloat, 0); + + Assert.IsFalse(obj.activeSelf); + } + } +} + +#endif \ No newline at end of file diff --git a/UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta b/UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta new file mode 100644 index 00000000..9e86034a --- /dev/null +++ b/UnitTests~/ReactiveComponent/ObjectToggleTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7c68d69f7b4a46c5b2ce3d8f26b0fa76 +timeCreated: 1729376563 \ No newline at end of file diff --git a/UnitTests~/ReactiveComponent/ParameterAssignment/AutoValueAssignmentTests.cs b/UnitTests~/ReactiveComponent/ParameterAssignment/AutoValueAssignmentTests.cs index d7eb71f5..75492c37 100644 --- a/UnitTests~/ReactiveComponent/ParameterAssignment/AutoValueAssignmentTests.cs +++ b/UnitTests~/ReactiveComponent/ParameterAssignment/AutoValueAssignmentTests.cs @@ -1,4 +1,6 @@ -using System; +#if MA_VRCSDK3_AVATARS + +using System; using System.Collections.Generic; using System.Linq; using modular_avatar_tests; @@ -13,6 +15,43 @@ namespace UnitTests.ReactiveComponent.ParameterAssignment { public class AutoValueAssignmentTests : TestBase { + [Test] + public void AutoValueWithMAParameters() + { + var root = CreateRoot("root"); + var child = CreateChild(root, "child"); + + var parameters = child.AddComponent(); + parameters.parameters = new() + { + new ParameterConfig() + { + defaultValue = 1.0f, + hasExplicitDefaultValue = true, + nameOrPrefix = "foo", + syncType = ParameterSyncType.Bool + } + }; + + var mami = child.AddComponent(); + mami.Control = new() + { + parameter = new() + { + name = "foo" + }, + name = "x" + }; + mami.automaticValue = true; + + child.AddComponent(); + + AvatarProcessor.ProcessAvatar(root); + var menu = root.GetComponent().expressionsMenu + .controls.First(c => c.name == "child"); + Assert.AreEqual(1, menu.value); + } + [Test] public void ManuallyAssignedParametersAreNotReplaced() { @@ -112,4 +151,6 @@ namespace UnitTests.ReactiveComponent.ParameterAssignment Assert.AreEqual(expectedDefaultValue, avDesc.expressionParameters.parameters.Single().defaultValue); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/UnitTests~/ReactiveComponent/ParameterAssignment/ParameterTypeTests.cs b/UnitTests~/ReactiveComponent/ParameterAssignment/ParameterTypeTests.cs index 6a4a45b5..f16844c3 100644 --- a/UnitTests~/ReactiveComponent/ParameterAssignment/ParameterTypeTests.cs +++ b/UnitTests~/ReactiveComponent/ParameterAssignment/ParameterTypeTests.cs @@ -1,4 +1,6 @@ -using System; +#if MA_VRCSDK3_AVATARS + +using System; using System.Linq; using modular_avatar_tests; using nadena.dev.modular_avatar.core; @@ -82,4 +84,6 @@ namespace UnitTests.ReactiveComponent.ParameterAssignment Assert.AreEqual(expected, descriptor.expressionParameters.parameters.Single().valueType); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs b/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs new file mode 100644 index 00000000..debfe8c9 --- /dev/null +++ b/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs @@ -0,0 +1,102 @@ +#if MA_VRCSDK3_AVATARS + +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(); + 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().InitSettings(); + + AssertNoPreviewDeletion(root); + AssertNoMeshDeletion(root); + + var mesh = root.GetComponentInChildren(); + // 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(); + 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(); + 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(); + var originalSharedMesh = mesh.sharedMesh; + AvatarProcessor.ProcessAvatar(root); + Assert.AreEqual(originalSharedMesh, mesh.sharedMesh); + } +} + +#endif \ No newline at end of file diff --git a/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs.meta b/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs.meta new file mode 100644 index 00000000..024b7f11 --- /dev/null +++ b/UnitTests~/ReactiveComponent/ShapeDeletionAnalysis.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18eb55e1b66a00243a91142456dfd5f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx b/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx new file mode 100644 index 00000000..52fa8bb9 Binary files /dev/null and b/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx differ diff --git a/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx.meta b/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx.meta new file mode 100644 index 00000000..0f60ec10 --- /dev/null +++ b/UnitTests~/ReactiveComponent/shape deletion test mesh.fbx.meta @@ -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: diff --git a/UnitTests~/RenameParametersTests/RenameParametersTests.cs b/UnitTests~/RenameParametersTests/RenameParametersTests.cs index 983fd328..8942b5e4 100644 --- a/UnitTests~/RenameParametersTests/RenameParametersTests.cs +++ b/UnitTests~/RenameParametersTests/RenameParametersTests.cs @@ -306,6 +306,45 @@ namespace modular_avatar_tests.RenameParametersTests Assert.AreNotEqual(fuga.name, "Fuga"); // should be auto-renamed } + + [Test] + public void AnimatorOnlyAndLocalParameterShouldNotSync() + { + var root = CreateRoot("x"); + + var c1 = CreateChild(root, "c1"); + var c2 = CreateChild(root, "c2"); + + var p1 = c1.AddComponent(); + var p2 = c2.AddComponent(); + + p1.parameters = new() + { + new() + { + nameOrPrefix = "a", + syncType = ParameterSyncType.NotSynced, + localOnly = false + } + }; + p2.parameters = new() + { + new() + { + nameOrPrefix = "a", + syncType = ParameterSyncType.Int, + localOnly = true + } + }; + + AvatarProcessor.ProcessAvatar(root); + + var expParams = root.GetComponent().expressionParameters.parameters + .Select(p => new KeyValuePair(p.name, p)) + .ToImmutableDictionary(); + + Assert.IsFalse(expParams["a"].networkSynced); + } } } diff --git a/UnitTests~/SerializationTests/SerializationTests.cs b/UnitTests~/SerializationTests/SerializationTests.cs index b7272b90..c68f308b 100644 --- a/UnitTests~/SerializationTests/SerializationTests.cs +++ b/UnitTests~/SerializationTests/SerializationTests.cs @@ -63,7 +63,7 @@ namespace _ModularAvatar.EditModeTests.SerializationTests Assert.False(string.IsNullOrEmpty(path)); var mainAsset = AssetDatabase.LoadMainAssetAtPath(path); - Assert.IsInstanceOf(mainAsset); + Assert.IsTrue(mainAsset is GeneratedAssets or SubAssetContainer); } } } diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs index 13be9ca7..2651f178 100644 --- a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs +++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.cs @@ -1,4 +1,6 @@ -using modular_avatar_tests; +#if MA_VRCSDK3_AVATARS + +using modular_avatar_tests; using nadena.dev.modular_avatar.animation; using nadena.dev.modular_avatar.core.editor; using NUnit.Framework; @@ -14,7 +16,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(); + 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; @@ -61,4 +123,6 @@ namespace ShapeChangerTests Assert.AreEqual(100.0f, smr.GetBlendShapeWeight(sharedMesh.GetBlendShapeIndex("key3")), 0.1f); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab index f6431564..6dbfb2b7 100644 --- a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab +++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimation.prefab @@ -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 diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab new file mode 100644 index 00000000..e7640290 --- /dev/null +++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab @@ -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} diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab.meta b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab.meta new file mode 100644 index 00000000..d37cf9c1 --- /dev/null +++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInactive.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4744d7b0db7db0d459f3aa7e6a0cf7db +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab new file mode 100644 index 00000000..daa2600d --- /dev/null +++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab @@ -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: InitialOnToggled + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !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: 1 + 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: 1 + 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: SCDefaultAnimationInverted + 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} diff --git a/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab.meta b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab.meta new file mode 100644 index 00000000..487346d8 --- /dev/null +++ b/UnitTests~/ShapeChanger/InitialStates/SCDefaultAnimationInverted.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c86e54d4b828d364aa677a5b3ce7be12 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnitTests~/SyncParameterSequence.meta b/UnitTests~/SyncParameterSequence.meta new file mode 100644 index 00000000..ef65c7de --- /dev/null +++ b/UnitTests~/SyncParameterSequence.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7b09a147690448ac94d495e90c761c0d +timeCreated: 1733093978 \ No newline at end of file diff --git a/UnitTests~/SyncParameterSequence/SyncParameterSequenceTest.cs b/UnitTests~/SyncParameterSequence/SyncParameterSequenceTest.cs new file mode 100644 index 00000000..73c5eb33 --- /dev/null +++ b/UnitTests~/SyncParameterSequence/SyncParameterSequenceTest.cs @@ -0,0 +1,251 @@ +#if MA_VRCSDK3_AVATARS + +using modular_avatar_tests; +using nadena.dev.modular_avatar.core; +using nadena.dev.modular_avatar.core.editor; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; +using VRC.SDK3.Avatars.Components; +using VRC.SDK3.Avatars.ScriptableObjects; + +namespace UnitTests.SyncParameterSequence +{ + public class SyncParameterSequenceTest : TestBase + { + [Test] + public void NonPrimaryPlatform() + { + ModularAvatarSyncParameterSequence.Platform platform; + switch (EditorUserBuildSettings.activeBuildTarget) + { + case BuildTarget.Android: + platform = ModularAvatarSyncParameterSequence.Platform.PC; + break; + default: + platform = ModularAvatarSyncParameterSequence.Platform.Android; + break; + } + + var root = CreateRoot("root"); + var avdesc = root.GetComponent(); + + var expParams = ScriptableObject.CreateInstance(); + + expParams.parameters = new[] + { + new VRCExpressionParameters.Parameter() + { + name = "p1", + valueType = VRCExpressionParameters.ValueType.Bool, + networkSynced = true, + defaultValue = 0.5f, + }, + new VRCExpressionParameters.Parameter() + { + name = "p2", + valueType = VRCExpressionParameters.ValueType.Int, + networkSynced = true, + defaultValue = 0.5f, + } + }; + + var refParams = ScriptableObject.CreateInstance(); + refParams.parameters = new[] + { + new VRCExpressionParameters.Parameter() + { + name = "p0", + valueType = VRCExpressionParameters.ValueType.Bool, + networkSynced = true + }, + new VRCExpressionParameters.Parameter() + { + name = "p2", + valueType = VRCExpressionParameters.ValueType.Int, + networkSynced = true + } + }; + + var c = avdesc.gameObject.AddComponent(); + c.PrimaryPlatform = platform; + c.Parameters = refParams; + + avdesc.expressionParameters = expParams; + + var context = CreateContext(root); + SyncParameterSequencePass.ExecuteStatic(context); + + expParams = avdesc.expressionParameters; + + Assert.AreEqual("p0", expParams.parameters[0].name); + Assert.AreEqual("p2", expParams.parameters[1].name); + Assert.AreEqual("p1", expParams.parameters[2].name); + + Assert.IsTrue(Mathf.Approximately(0f, expParams.parameters[0].defaultValue)); + Assert.IsTrue(Mathf.Approximately(0.5f, expParams.parameters[1].defaultValue)); + Assert.IsTrue(Mathf.Approximately(0.5f, expParams.parameters[2].defaultValue)); + + Assert.AreEqual(2, refParams.parameters.Length); + } + + [Test] + public void PrimaryPlatform() + { + ModularAvatarSyncParameterSequence.Platform platform; + switch (EditorUserBuildSettings.activeBuildTarget) + { + case BuildTarget.Android: + platform = ModularAvatarSyncParameterSequence.Platform.Android; + break; + default: + platform = ModularAvatarSyncParameterSequence.Platform.PC; + break; + } + + var root = CreateRoot("root"); + var avdesc = root.GetComponent(); + + var expParams = ScriptableObject.CreateInstance(); + + expParams.parameters = new[] + { + new VRCExpressionParameters.Parameter() + { + name = "p1", + valueType = VRCExpressionParameters.ValueType.Bool, + networkSynced = true, + defaultValue = 0.5f, + }, + new VRCExpressionParameters.Parameter() + { + name = "p2", + valueType = VRCExpressionParameters.ValueType.Int, + networkSynced = true, + defaultValue = 0.5f, + }, + new VRCExpressionParameters.Parameter() { + name = "notsynced", + valueType = VRCExpressionParameters.ValueType.Int, + networkSynced = false, + } + }; + + var refParams = ScriptableObject.CreateInstance(); + refParams.parameters = new[] + { + new VRCExpressionParameters.Parameter() + { + name = "p0", + valueType = VRCExpressionParameters.ValueType.Bool, + networkSynced = true + }, + new VRCExpressionParameters.Parameter() + { + name = "p2", + valueType = VRCExpressionParameters.ValueType.Int, + networkSynced = true + } + }; + + var c = avdesc.gameObject.AddComponent(); + c.PrimaryPlatform = platform; + c.Parameters = refParams; + + avdesc.expressionParameters = expParams; + + var context = CreateContext(root); + SyncParameterSequencePass.ExecuteStatic(context); + + expParams = avdesc.expressionParameters; + + Assert.AreEqual("p0", expParams.parameters[0].name); + Assert.AreEqual("p2", expParams.parameters[1].name); + Assert.AreEqual("p1", expParams.parameters[2].name); + Assert.AreEqual("notsynced", expParams.parameters[3].name); + + Assert.IsTrue(Mathf.Approximately(0f, expParams.parameters[0].defaultValue)); + Assert.IsTrue(Mathf.Approximately(0.5f, expParams.parameters[1].defaultValue)); + Assert.IsTrue(Mathf.Approximately(0.5f, expParams.parameters[2].defaultValue)); + + Assert.AreEqual(3, refParams.parameters.Length); + Assert.AreEqual("p0", refParams.parameters[0].name); + Assert.AreEqual("p2", refParams.parameters[1].name); + Assert.AreEqual("p1", refParams.parameters[2].name); + } + + + [Test] + public void PrimaryPlatformOverflow() + { + ModularAvatarSyncParameterSequence.Platform platform; + switch (EditorUserBuildSettings.activeBuildTarget) + { + case BuildTarget.Android: + platform = ModularAvatarSyncParameterSequence.Platform.Android; + break; + default: + platform = ModularAvatarSyncParameterSequence.Platform.PC; + break; + } + + var root = CreateRoot("root"); + var avdesc = root.GetComponent(); + + var expParams = ScriptableObject.CreateInstance(); + + expParams.parameters = new[] + { + new VRCExpressionParameters.Parameter() + { + name = "p1", + valueType = VRCExpressionParameters.ValueType.Bool, + networkSynced = true, + defaultValue = 0.5f, + }, + new VRCExpressionParameters.Parameter() + { + name = "p2", + valueType = VRCExpressionParameters.ValueType.Int, + networkSynced = true, + defaultValue = 0.5f, + } + }; + + var refParams = ScriptableObject.CreateInstance(); + var paramList = new System.Collections.Generic.List(); + for (int i = 0; i < VRCExpressionParameters.MAX_PARAMETER_COST; i++) + { + paramList.Add(new() + { + name = "b" + i, + valueType = VRCExpressionParameters.ValueType.Bool, + networkSynced = true + }); + } + + refParams.parameters = paramList.ToArray(); + + var c = avdesc.gameObject.AddComponent(); + c.PrimaryPlatform = platform; + c.Parameters = refParams; + + avdesc.expressionParameters = expParams; + + var context = CreateContext(root); + SyncParameterSequencePass.ExecuteStatic(context); + + expParams = avdesc.expressionParameters; + + Assert.AreEqual(2, expParams.parameters.Length); + Assert.AreEqual("p1", expParams.parameters[0].name); + Assert.AreEqual("p2", expParams.parameters[1].name); + + Assert.AreEqual(2, refParams.parameters.Length); + Assert.AreEqual("p1", refParams.parameters[0].name); + Assert.AreEqual("p2", refParams.parameters[1].name); + } + } +} + +#endif \ No newline at end of file diff --git a/UnitTests~/SyncParameterSequence/SyncParameterSequenceTest.cs.meta b/UnitTests~/SyncParameterSequence/SyncParameterSequenceTest.cs.meta new file mode 100644 index 00000000..9611d253 --- /dev/null +++ b/UnitTests~/SyncParameterSequence/SyncParameterSequenceTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 37fcaa6b7094408eac945c2887a1101e +timeCreated: 1733093994 \ No newline at end of file diff --git a/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs b/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs index 81a9ffa3..5450dba0 100644 --- a/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs +++ b/UnitTests~/VirtualMenuTests/VirtualMenuTests.cs @@ -698,6 +698,32 @@ namespace modular_avatar_tests.VirtualMenuTests Assert.AreEqual(4, virtualMenu.RootMenuNode.Controls[0].SubmenuNode.Controls[5].subParameters.Length); } + [Test] + public void MergeArmatureAndMenuInstallerOnSameObjectWorks() + { + var root = CreateRoot("root"); + var armature = CreateChild(root, "Armature"); + var installer = armature.AddComponent(); + + var merge = installer.gameObject.AddComponent(); + merge.mergeTarget.Set(root); + + var menu = Create(); + menu.controls.Add(new VRCExpressionsMenu.Control() + { + name = "control", + type = VRCExpressionsMenu.Control.ControlType.Toggle + }); + + installer.menuToAppend = menu; + + AvatarProcessor.ProcessAvatar(root); + + var realizedMenu = root.GetComponent().expressionsMenu; + Assert.AreEqual(1, realizedMenu.controls.Count); + Assert.AreEqual("control", realizedMenu.controls[0].name); + } + ModularAvatarMenuInstaller CreateInstaller(string name) { GameObject obj = new GameObject(); diff --git a/UnitTests~/VisibleHeadAccessoryTest/VisibleHeadAccessoryTest.cs b/UnitTests~/VisibleHeadAccessoryTest/VisibleHeadAccessoryTest.cs index a85f2954..7685066b 100644 --- a/UnitTests~/VisibleHeadAccessoryTest/VisibleHeadAccessoryTest.cs +++ b/UnitTests~/VisibleHeadAccessoryTest/VisibleHeadAccessoryTest.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +#if MA_VRCSDK3_AVATARS + +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -57,4 +59,6 @@ namespace UnitTests.VisibleHeadAccessoryTest Assert.AreEqual(headchop.globalScaleFactor, 1); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/docs-site~/yarn.lock b/docs-site~/yarn.lock index 8a4071ab..445b3555 100644 --- a/docs-site~/yarn.lock +++ b/docs-site~/yarn.lock @@ -1,1931 +1,613 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 8 - cacheKey: 10c0 - -"@cloudflare/kv-asset-handler@npm:^0.2.0": - version: 0.2.0 - resolution: "@cloudflare/kv-asset-handler@npm:0.2.0" - dependencies: - mime: "npm:^3.0.0" - checksum: 10/56affbe5afdcfcf0860e7b9c826b3156210f1286791e702320b0ee378e540ed3e2d7ecdd55928e404475d4469433a47ca255889374b88992b481499a6d30b84b - languageName: node - linkType: hard - -"@cloudflare/kv-asset-handler@npm:^0.3.0": - version: 0.3.0 - resolution: "@cloudflare/kv-asset-handler@npm:0.3.0" - dependencies: - mime: "npm:^3.0.0" - checksum: 10/92a28e39fc1c2efc81b32cecd19ed748e91180cc3854e749b9d1f6c25bb4a10c7ff3a260d3b953de334d9101adad8633274ed84bdd55ebfff1d31114dbbfd803 - languageName: node - linkType: hard - -"@cloudflare/workerd-darwin-64@npm:1.20231218.0": - version: 1.20231218.0 - resolution: "@cloudflare/workerd-darwin-64@npm:1.20231218.0" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@cloudflare/workerd-darwin-arm64@npm:1.20231218.0": - version: 1.20231218.0 - resolution: "@cloudflare/workerd-darwin-arm64@npm:1.20231218.0" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@cloudflare/workerd-linux-64@npm:1.20231218.0": - version: 1.20231218.0 - resolution: "@cloudflare/workerd-linux-64@npm:1.20231218.0" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - -"@cloudflare/workerd-linux-arm64@npm:1.20231218.0": - version: 1.20231218.0 - resolution: "@cloudflare/workerd-linux-arm64@npm:1.20231218.0" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - -"@cloudflare/workerd-windows-64@npm:1.20231218.0": - version: 1.20231218.0 - resolution: "@cloudflare/workerd-windows-64@npm:1.20231218.0" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@cspotcode/source-map-support@npm:0.8.1": - version: 0.8.1 - resolution: "@cspotcode/source-map-support@npm:0.8.1" - dependencies: - "@jridgewell/trace-mapping": "npm:0.3.9" - checksum: 10/b6e38a1712fab242c86a241c229cf562195aad985d0564bd352ac404be583029e89e93028ffd2c251d2c407ecac5fb0cbdca94a2d5c10f29ac806ede0508b3ff - languageName: node - linkType: hard - -"@esbuild-plugins/node-globals-polyfill@npm:^0.2.3": - version: 0.2.3 - resolution: "@esbuild-plugins/node-globals-polyfill@npm:0.2.3" - peerDependencies: - esbuild: "*" - checksum: 10/6452637b55da3d577b03bb6e9e9c5b88ec153a2c260a71d4f237fac1b46577e3536059030524b7088c9af7bc8da2afd926a5ebb72653876ce83621cc63d57efc - languageName: node - linkType: hard - -"@esbuild-plugins/node-modules-polyfill@npm:^0.2.2": - version: 0.2.2 - resolution: "@esbuild-plugins/node-modules-polyfill@npm:0.2.2" - dependencies: - escape-string-regexp: "npm:^4.0.0" - rollup-plugin-node-polyfills: "npm:^0.2.1" - peerDependencies: - esbuild: "*" - checksum: 10/0f5601f0ce46b33079c16881142966afff2a528799f85667db7cab38e53607157ef53d8e48cdb1d082b410688a536e14d87b7cd2971784b3afc15befb9b86520 - languageName: node - linkType: hard - -"@esbuild/android-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/android-arm64@npm:0.17.19" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/android-arm@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/android-arm@npm:0.17.19" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@esbuild/android-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/android-x64@npm:0.17.19" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/darwin-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/darwin-arm64@npm:0.17.19" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/darwin-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/darwin-x64@npm:0.17.19" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/freebsd-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/freebsd-arm64@npm:0.17.19" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/freebsd-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/freebsd-x64@npm:0.17.19" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/linux-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-arm64@npm:0.17.19" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/linux-arm@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-arm@npm:0.17.19" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@esbuild/linux-ia32@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-ia32@npm:0.17.19" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - -"@esbuild/linux-loong64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-loong64@npm:0.17.19" - conditions: os=linux & cpu=loong64 - languageName: node - linkType: hard - -"@esbuild/linux-mips64el@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-mips64el@npm:0.17.19" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - -"@esbuild/linux-ppc64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-ppc64@npm:0.17.19" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - -"@esbuild/linux-riscv64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-riscv64@npm:0.17.19" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - -"@esbuild/linux-s390x@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-s390x@npm:0.17.19" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - -"@esbuild/linux-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/linux-x64@npm:0.17.19" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/netbsd-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/netbsd-x64@npm:0.17.19" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/openbsd-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/openbsd-x64@npm:0.17.19" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/sunos-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/sunos-x64@npm:0.17.19" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/win32-arm64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/win32-arm64@npm:0.17.19" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/win32-ia32@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/win32-ia32@npm:0.17.19" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@esbuild/win32-x64@npm:0.17.19": - version: 0.17.19 - resolution: "@esbuild/win32-x64@npm:0.17.19" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@fastify/busboy@npm:^2.0.0": - version: 2.0.0 - resolution: "@fastify/busboy@npm:2.0.0" - checksum: 10/6a2366d06b82aac1069b8323792f76f7a8fca02533cb3745fcd218d8f0f953dc4dbef057287237414658cd43f8dede0846ef33398999e3dbe54ddaeefec71c0a - languageName: node - linkType: hard - -"@isaacs/cliui@npm:^8.0.2": - version: 8.0.2 - resolution: "@isaacs/cliui@npm:8.0.2" - dependencies: - string-width: "npm:^5.1.2" - string-width-cjs: "npm:string-width@^4.2.0" - strip-ansi: "npm:^7.0.1" - strip-ansi-cjs: "npm:strip-ansi@^6.0.1" - wrap-ansi: "npm:^8.1.0" - wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" - checksum: 10/e9ed5fd27c3aec1095e3a16e0c0cf148d1fee55a38665c35f7b3f86a9b5d00d042ddaabc98e8a1cb7463b9378c15f22a94eb35e99469c201453eb8375191f243 - languageName: node - linkType: hard - -"@jridgewell/resolve-uri@npm:^3.0.3": - version: 3.1.1 - resolution: "@jridgewell/resolve-uri@npm:3.1.1" - checksum: 10/64d59df8ae1a4e74315eb1b61e012f1c7bc8aac47a3a1e683f6fe7008eab07bc512a742b7aa7c0405685d1421206de58c9c2e6adbfe23832f8bd69408ffc183e - languageName: node - linkType: hard - -"@jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.15 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" - checksum: 10/89960ac087781b961ad918978975bcdf2051cd1741880469783c42de64239703eab9db5230d776d8e6a09d73bb5e4cb964e07d93ee6e2e7aea5a7d726e865c09 - languageName: node - linkType: hard - -"@jridgewell/trace-mapping@npm:0.3.9": - version: 0.3.9 - resolution: "@jridgewell/trace-mapping@npm:0.3.9" - dependencies: - "@jridgewell/resolve-uri": "npm:^3.0.3" - "@jridgewell/sourcemap-codec": "npm:^1.4.10" - checksum: 10/83deafb8e7a5ca98993c2c6eeaa93c270f6f647a4c0dc00deb38c9cf9b2d3b7bf15e8839540155247ef034a052c0ec4466f980bf0c9e2ab63b97d16c0cedd3ff - languageName: node - linkType: hard - -"@npmcli/fs@npm:^3.1.0": - version: 3.1.0 - resolution: "@npmcli/fs@npm:3.1.0" - dependencies: - semver: "npm:^7.3.5" - checksum: 10/f3a7ab3a31de65e42aeb6ed03ed035ef123d2de7af4deb9d4a003d27acc8618b57d9fb9d259fe6c28ca538032a028f37337264388ba27d26d37fff7dde22476e - languageName: node - linkType: hard - -"@pkgjs/parseargs@npm:^0.11.0": - version: 0.11.0 - resolution: "@pkgjs/parseargs@npm:0.11.0" - checksum: 10/115e8ceeec6bc69dff2048b35c0ab4f8bbee12d8bb6c1f4af758604586d802b6e669dcb02dda61d078de42c2b4ddce41b3d9e726d7daa6b4b850f4adbf7333ff - languageName: node - linkType: hard - -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: 10/ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 - languageName: node - linkType: hard - -"abbrev@npm:^1.0.0": - version: 1.1.1 - resolution: "abbrev@npm:1.1.1" - checksum: 10/2d882941183c66aa665118bafdab82b7a177e9add5eb2776c33e960a4f3c89cff88a1b38aba13a456de01d0dd9d66a8bea7c903268b21ea91dd1097e1e2e8243 - languageName: node - linkType: hard - -"acorn-walk@npm:^8.2.0": - version: 8.2.0 - resolution: "acorn-walk@npm:8.2.0" - checksum: 10/e69f7234f2adfeb16db3671429a7c80894105bd7534cb2032acf01bb26e6a847952d11a062d071420b43f8d82e33d2e57f26fe87d9cce0853e8143d8910ff1de - languageName: node - linkType: hard - -"acorn@npm:^8.8.0": - version: 8.10.0 - resolution: "acorn@npm:8.10.0" - bin: - acorn: bin/acorn - checksum: 10/522310c20fdc3c271caed3caf0f06c51d61cb42267279566edd1d58e83dbc12eebdafaab666a0f0be1b7ad04af9c6bc2a6f478690a9e6391c3c8b165ada917dd - languageName: node - linkType: hard - -"agent-base@npm:6, agent-base@npm:^6.0.2": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: "npm:4" - checksum: 10/21fb903e0917e5cb16591b4d0ef6a028a54b83ac30cd1fca58dece3d4e0990512a8723f9f83130d88a41e2af8b1f7be1386fda3ea2d181bb1a62155e75e95e23 - languageName: node - linkType: hard - -"agentkeepalive@npm:^4.2.1": - version: 4.5.0 - resolution: "agentkeepalive@npm:4.5.0" - dependencies: - humanize-ms: "npm:^1.2.1" - checksum: 10/dd210ba2a2e2482028f027b1156789744aadbfd773a6c9dd8e4e8001930d5af82382abe19a69240307b1d8003222ce6b0542935038313434b900e351914fc15f - languageName: node - linkType: hard - -"aggregate-error@npm:^3.0.0": - version: 3.1.0 - resolution: "aggregate-error@npm:3.1.0" - dependencies: - clean-stack: "npm:^2.0.0" - indent-string: "npm:^4.0.0" - checksum: 10/1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 - languageName: node - linkType: hard - -"ansi-regex@npm:^5.0.1": - version: 5.0.1 - resolution: "ansi-regex@npm:5.0.1" - checksum: 10/2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b - languageName: node - linkType: hard - -"ansi-regex@npm:^6.0.1": - version: 6.0.1 - resolution: "ansi-regex@npm:6.0.1" - checksum: 10/1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 - languageName: node - linkType: hard - -"ansi-styles@npm:^4.0.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: "npm:^2.0.1" - checksum: 10/b4494dfbfc7e4591b4711a396bd27e540f8153914123dccb4cdbbcb514015ada63a3809f362b9d8d4f6b17a706f1d7bea3c6f974b15fa5ae76b5b502070889ff - languageName: node - linkType: hard - -"ansi-styles@npm:^6.1.0": - version: 6.2.1 - resolution: "ansi-styles@npm:6.2.1" - checksum: 10/70fdf883b704d17a5dfc9cde206e698c16bcd74e7f196ab821511651aee4f9f76c9514bdfa6ca3a27b5e49138b89cb222a28caf3afe4567570139577f991df32 - languageName: node - linkType: hard - -"anymatch@npm:~3.1.2": - version: 3.1.3 - resolution: "anymatch@npm:3.1.3" - dependencies: - normalize-path: "npm:^3.0.0" - picomatch: "npm:^2.0.4" - checksum: 10/3e044fd6d1d26545f235a9fe4d7a534e2029d8e59fa7fd9f2a6eb21230f6b5380ea1eaf55136e60cbf8e613544b3b766e7a6fa2102e2a3a117505466e3025dc2 - languageName: node - linkType: hard - -"aproba@npm:^1.0.3 || ^2.0.0": - version: 2.0.0 - resolution: "aproba@npm:2.0.0" - checksum: 10/c2b9a631298e8d6f3797547e866db642f68493808f5b37cd61da778d5f6ada890d16f668285f7d60bd4fc3b03889bd590ffe62cf81b700e9bb353431238a0a7b - languageName: node - linkType: hard - -"are-we-there-yet@npm:^3.0.0": - version: 3.0.1 - resolution: "are-we-there-yet@npm:3.0.1" - dependencies: - delegates: "npm:^1.0.0" - readable-stream: "npm:^3.6.0" - checksum: 10/390731720e1bf9ed5d0efc635ea7df8cbc4c90308b0645a932f06e8495a0bf1ecc7987d3b97e805f62a17d6c4b634074b25200aa4d149be2a7b17250b9744bc4 - languageName: node - linkType: hard - -"as-table@npm:^1.0.36": - version: 1.0.55 - resolution: "as-table@npm:1.0.55" - dependencies: - printable-characters: "npm:^1.0.42" - checksum: 10/8bbfbd7b6f240efb22f6553f756e89d1cae074e9f7a24580282e9d247c1bd9cf1fd9fb49056202a78a5e69907209d8bf032d8b6c3eaaab5fb6ad92da64a7894a - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 10/9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 - languageName: node - linkType: hard - -"binary-extensions@npm:^2.0.0": - version: 2.2.0 - resolution: "binary-extensions@npm:2.2.0" - checksum: 10/ccd267956c58d2315f5d3ea6757cf09863c5fc703e50fbeb13a7dc849b812ef76e3cf9ca8f35a0c48498776a7478d7b4a0418e1e2b8cb9cb9731f2922aaad7f8 - languageName: node - linkType: hard - -"blake3-wasm@npm:^2.1.5": - version: 2.1.5 - resolution: "blake3-wasm@npm:2.1.5" - checksum: 10/7138aa209ac8411755ba07df7d035974886aac1fb4bb8cf710d354732037069bacc9984c19b3bc68bf5e17cc203f454cc9cfcb7115393aaf21ce865630dbf920 - languageName: node - linkType: hard - -"brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" - dependencies: - balanced-match: "npm:^1.0.0" - concat-map: "npm:0.0.1" - checksum: 10/faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 - languageName: node - linkType: hard - -"brace-expansion@npm:^2.0.1": - version: 2.0.1 - resolution: "brace-expansion@npm:2.0.1" - dependencies: - balanced-match: "npm:^1.0.0" - checksum: 10/a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 - languageName: node - linkType: hard - -"braces@npm:~3.0.2": - version: 3.0.3 - resolution: "braces@npm:3.0.3" - dependencies: - fill-range: "npm:^7.1.1" - checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 - languageName: node - linkType: hard - -"cacache@npm:^17.0.0": - version: 17.1.4 - resolution: "cacache@npm:17.1.4" - dependencies: - "@npmcli/fs": "npm:^3.1.0" - fs-minipass: "npm:^3.0.0" - glob: "npm:^10.2.2" - lru-cache: "npm:^7.7.1" - minipass: "npm:^7.0.3" - minipass-collect: "npm:^1.0.2" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - p-map: "npm:^4.0.0" - ssri: "npm:^10.0.0" - tar: "npm:^6.1.11" - unique-filename: "npm:^3.0.0" - checksum: 10/6e26c788bc6a18ff42f4d4f97db30d5c60a5dfac8e7c10a03b0307a92cf1b647570547cf3cd96463976c051eb9c7258629863f156e224c82018862c1a8ad0e70 - languageName: node - linkType: hard - -"capnp-ts@npm:^0.7.0": - version: 0.7.0 - resolution: "capnp-ts@npm:0.7.0" - dependencies: - debug: "npm:^4.3.1" - tslib: "npm:^2.2.0" - checksum: 10/186a76662e31ab16fe46fe0785ed2a511969d0c5198e2d7baec6b44f71c9b3bf8c05e7627036dc86c2d3ddc229c846559350c13f904fdd8da3590d7054715ba8 - languageName: node - linkType: hard - -"chokidar@npm:^3.5.3": - version: 3.5.3 - resolution: "chokidar@npm:3.5.3" - dependencies: - anymatch: "npm:~3.1.2" - braces: "npm:~3.0.2" - fsevents: "npm:~2.3.2" - glob-parent: "npm:~5.1.2" - is-binary-path: "npm:~2.1.0" - is-glob: "npm:~4.0.1" - normalize-path: "npm:~3.0.0" - readdirp: "npm:~3.6.0" - dependenciesMeta: - fsevents: - optional: true - checksum: 10/863e3ff78ee7a4a24513d2a416856e84c8e4f5e60efbe03e8ab791af1a183f569b62fc6f6b8044e2804966cb81277ddbbc1dc374fba3265bd609ea8efd62f5b3 - languageName: node - linkType: hard - -"chownr@npm:^2.0.0": - version: 2.0.0 - resolution: "chownr@npm:2.0.0" - checksum: 10/c57cf9dd0791e2f18a5ee9c1a299ae6e801ff58fee96dc8bfd0dcb4738a6ce58dd252a3605b1c93c6418fe4f9d5093b28ffbf4d66648cb2a9c67eaef9679be2f - languageName: node - linkType: hard - -"clean-stack@npm:^2.0.0": - version: 2.2.0 - resolution: "clean-stack@npm:2.2.0" - checksum: 10/2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: "npm:~1.1.4" - checksum: 10/fa00c91b4332b294de06b443923246bccebe9fab1b253f7fe1772d37b06a2269b4039a85e309abe1fe11b267b11c08d1d0473fda3badd6167f57313af2887a64 - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: 10/b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 - languageName: node - linkType: hard - -"color-support@npm:^1.1.3": - version: 1.1.3 - resolution: "color-support@npm:1.1.3" - bin: - color-support: bin.js - checksum: 10/4bcfe30eea1498fe1cabc852bbda6c9770f230ea0e4faf4611c5858b1b9e4dde3730ac485e65f54ca182f4c50b626c1bea7c8441ceda47367a54a818c248aa7a - languageName: node - linkType: hard - -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 10/9680699c8e2b3af0ae22592cb764acaf973f292a7b71b8a06720233011853a58e256c89216a10cbe889727532fd77f8bcd49a760cedfde271b8e006c20e079f2 - languageName: node - linkType: hard - -"console-control-strings@npm:^1.1.0": - version: 1.1.0 - resolution: "console-control-strings@npm:1.1.0" - checksum: 10/27b5fa302bc8e9ae9e98c03c66d76ca289ad0c61ce2fe20ab288d288bee875d217512d2edb2363fc83165e88f1c405180cf3f5413a46e51b4fe1a004840c6cdb - languageName: node - linkType: hard - -"cookie@npm:^0.5.0": - version: 0.5.0 - resolution: "cookie@npm:0.5.0" - checksum: 10/aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380 - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.0": - version: 7.0.3 - resolution: "cross-spawn@npm:7.0.3" - dependencies: - path-key: "npm:^3.1.0" - shebang-command: "npm:^2.0.0" - which: "npm:^2.0.1" - checksum: 10/e1a13869d2f57d974de0d9ef7acbf69dc6937db20b918525a01dacb5032129bd552d290d886d981e99f1b624cb03657084cc87bd40f115c07ecf376821c729ce - languageName: node - linkType: hard - -"data-uri-to-buffer@npm:^2.0.0": - version: 2.0.2 - resolution: "data-uri-to-buffer@npm:2.0.2" - checksum: 10/152bec5e77513ee253a7c686700a1723246f582dad8b614e8eaaaba7fa45a15c8671ae4b8f4843f4f3a002dae1d3e7a20f852f7d7bdc8b4c15cfe7adfdfb07f8 - languageName: node - linkType: hard - -"debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.3": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: "npm:2.1.2" - peerDependenciesMeta: - supports-color: - optional: true - checksum: 10/0073c3bcbd9cb7d71dd5f6b55be8701af42df3e56e911186dfa46fac3a5b9eb7ce7f377dd1d3be6db8977221f8eb333d945216f645cf56f6b688cd484837d255 - languageName: node - linkType: hard - -"delegates@npm:^1.0.0": - version: 1.0.0 - resolution: "delegates@npm:1.0.0" - checksum: 10/a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd - languageName: node - linkType: hard - -"eastasianwidth@npm:^0.2.0": - version: 0.2.0 - resolution: "eastasianwidth@npm:0.2.0" - checksum: 10/9b1d3e1baefeaf7d70799db8774149cef33b97183a6addceeba0cf6b85ba23ee2686f302f14482006df32df75d32b17c509c143a3689627929e4a8efaf483952 - languageName: node - linkType: hard - -"emoji-regex@npm:^8.0.0": - version: 8.0.0 - resolution: "emoji-regex@npm:8.0.0" - checksum: 10/c72d67a6821be15ec11997877c437491c313d924306b8da5d87d2a2bcc2cec9903cb5b04ee1a088460501d8e5b44f10df82fdc93c444101a7610b80c8b6938e1 - languageName: node - linkType: hard - -"emoji-regex@npm:^9.2.2": - version: 9.2.2 - resolution: "emoji-regex@npm:9.2.2" - checksum: 10/915acf859cea7131dac1b2b5c9c8e35c4849e325a1d114c30adb8cd615970f6dca0e27f64f3a4949d7d6ed86ecd79a1c5c63f02e697513cddd7b5835c90948b8 - languageName: node - linkType: hard - -"encoding@npm:^0.1.13": - version: 0.1.13 - resolution: "encoding@npm:0.1.13" - dependencies: - iconv-lite: "npm:^0.6.2" - checksum: 10/bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f - languageName: node - linkType: hard - -"env-paths@npm:^2.2.0": - version: 2.2.1 - resolution: "env-paths@npm:2.2.1" - checksum: 10/65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e - languageName: node - linkType: hard - -"err-code@npm:^2.0.2": - version: 2.0.3 - resolution: "err-code@npm:2.0.3" - checksum: 10/1d20d825cdcce8d811bfbe86340f4755c02655a7feb2f13f8c880566d9d72a3f6c92c192a6867632e490d6da67b678271f46e01044996a6443e870331100dfdd - languageName: node - linkType: hard - -"esbuild@npm:0.17.19": - version: 0.17.19 - resolution: "esbuild@npm:0.17.19" - dependencies: - "@esbuild/android-arm": "npm:0.17.19" - "@esbuild/android-arm64": "npm:0.17.19" - "@esbuild/android-x64": "npm:0.17.19" - "@esbuild/darwin-arm64": "npm:0.17.19" - "@esbuild/darwin-x64": "npm:0.17.19" - "@esbuild/freebsd-arm64": "npm:0.17.19" - "@esbuild/freebsd-x64": "npm:0.17.19" - "@esbuild/linux-arm": "npm:0.17.19" - "@esbuild/linux-arm64": "npm:0.17.19" - "@esbuild/linux-ia32": "npm:0.17.19" - "@esbuild/linux-loong64": "npm:0.17.19" - "@esbuild/linux-mips64el": "npm:0.17.19" - "@esbuild/linux-ppc64": "npm:0.17.19" - "@esbuild/linux-riscv64": "npm:0.17.19" - "@esbuild/linux-s390x": "npm:0.17.19" - "@esbuild/linux-x64": "npm:0.17.19" - "@esbuild/netbsd-x64": "npm:0.17.19" - "@esbuild/openbsd-x64": "npm:0.17.19" - "@esbuild/sunos-x64": "npm:0.17.19" - "@esbuild/win32-arm64": "npm:0.17.19" - "@esbuild/win32-ia32": "npm:0.17.19" - "@esbuild/win32-x64": "npm:0.17.19" - dependenciesMeta: - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 10/86ada7cad6d37a3445858fee31ca39fc6c0436c7c00b2e07b9ce308235be67f36aefe0dda25da9ab08653fde496d1e759d6ad891ce9479f9e1fb4964c8f2a0fa - languageName: node - linkType: hard - -"escape-string-regexp@npm:^4.0.0": - version: 4.0.0 - resolution: "escape-string-regexp@npm:4.0.0" - checksum: 10/98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 - languageName: node - linkType: hard - -"estree-walker@npm:^0.6.1": - version: 0.6.1 - resolution: "estree-walker@npm:0.6.1" - checksum: 10/b8da7815030c4e0b735f5f8af370af09525e052ee14e539cecabc24ad6da1782448778361417e7c438091a59e7ca9f4a0c11642f7da4f2ebf1ba7a150a590bcc - languageName: node - linkType: hard - -"exit-hook@npm:^2.2.1": - version: 2.2.1 - resolution: "exit-hook@npm:2.2.1" - checksum: 10/75835919e0aca624daa8d114c0014ae84506c4b79ac5806748cc7a86d1610a864ee974be58eec823c7757e5e6b07a5e332647e20ef84f6cc3dc3385c953c78c9 - languageName: node - linkType: hard - -"exponential-backoff@npm:^3.1.1": - version: 3.1.1 - resolution: "exponential-backoff@npm:3.1.1" - checksum: 10/2d9bbb6473de7051f96790d5f9a678f32e60ed0aa70741dc7fdc96fec8d631124ec3374ac144387604f05afff9500f31a1d45bd9eee4cdc2e4f9ad2d9b9d5dbd - languageName: node - linkType: hard - -"fill-range@npm:^7.1.1": - version: 7.1.1 - resolution: "fill-range@npm:7.1.1" - dependencies: - to-regex-range: "npm:^5.0.1" - checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 - languageName: node - linkType: hard - -"foreground-child@npm:^3.1.0": - version: 3.1.1 - resolution: "foreground-child@npm:3.1.1" - dependencies: - cross-spawn: "npm:^7.0.0" - signal-exit: "npm:^4.0.1" - checksum: 10/087edd44857d258c4f73ad84cb8df980826569656f2550c341b27adf5335354393eec24ea2fabd43a253233fb27cee177ebe46bd0b7ea129c77e87cb1e9936fb - languageName: node - linkType: hard - -"fs-minipass@npm:^2.0.0": - version: 2.1.0 - resolution: "fs-minipass@npm:2.1.0" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10/03191781e94bc9a54bd376d3146f90fe8e082627c502185dbf7b9b3032f66b0b142c1115f3b2cc5936575fc1b44845ce903dd4c21bec2a8d69f3bd56f9cee9ec - languageName: node - linkType: hard - -"fs-minipass@npm:^3.0.0": - version: 3.0.3 - resolution: "fs-minipass@npm:3.0.3" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10/af143246cf6884fe26fa281621d45cfe111d34b30535a475bfa38dafe343dadb466c047a924ffc7d6b7b18265df4110224ce3803806dbb07173bf2087b648d7f - languageName: node - linkType: hard - -"fs.realpath@npm:^1.0.0": - version: 1.0.0 - resolution: "fs.realpath@npm:1.0.0" - checksum: 10/e703107c28e362d8d7b910bbcbfd371e640a3bb45ae157a362b5952c0030c0b6d4981140ec319b347bce7adc025dd7813da1ff908a945ac214d64f5402a51b96 - languageName: node - linkType: hard - -"fsevents@npm:~2.3.2": - version: 2.3.3 - resolution: "fsevents@npm:2.3.3" - dependencies: - node-gyp: "npm:latest" - checksum: 10/4c1ade961ded57cdbfbb5cac5106ec17bc8bccd62e16343c569a0ceeca83b9dfef87550b4dc5cbb89642da412b20c5071f304c8c464b80415446e8e155a038c0 - conditions: os=darwin - languageName: node - linkType: hard - -"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": - version: 2.3.3 - resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" - dependencies: - node-gyp: "npm:latest" - conditions: os=darwin - languageName: node - linkType: hard - -"gauge@npm:^4.0.3": - version: 4.0.4 - resolution: "gauge@npm:4.0.4" - dependencies: - aproba: "npm:^1.0.3 || ^2.0.0" - color-support: "npm:^1.1.3" - console-control-strings: "npm:^1.1.0" - has-unicode: "npm:^2.0.1" - signal-exit: "npm:^3.0.7" - string-width: "npm:^4.2.3" - strip-ansi: "npm:^6.0.1" - wide-align: "npm:^1.1.5" - checksum: 10/09535dd53b5ced6a34482b1fa9f3929efdeac02f9858569cde73cef3ed95050e0f3d095706c1689614059898924b7a74aa14042f51381a1ccc4ee5c29d2389c4 - languageName: node - linkType: hard - -"get-source@npm:^2.0.12": - version: 2.0.12 - resolution: "get-source@npm:2.0.12" - dependencies: - data-uri-to-buffer: "npm:^2.0.0" - source-map: "npm:^0.6.1" - checksum: 10/6ba35ae0755046199b57d7fe254d50c6d7550d3b150e065a3607e3da8c55c617302f4c7cc3712252c7810954a04e2e56467ad02a0798c0841a5e980064bd3048 - languageName: node - linkType: hard - -"glob-parent@npm:~5.1.2": - version: 5.1.2 - resolution: "glob-parent@npm:5.1.2" - dependencies: - is-glob: "npm:^4.0.1" - checksum: 10/32cd106ce8c0d83731966d31517adb766d02c3812de49c30cfe0675c7c0ae6630c11214c54a5ae67aca882cf738d27fd7768f21aa19118b9245950554be07247 - languageName: node - linkType: hard - -"glob-to-regexp@npm:^0.4.1": - version: 0.4.1 - resolution: "glob-to-regexp@npm:0.4.1" - checksum: 10/9009529195a955c40d7b9690794aeff5ba665cc38f1519e111c58bb54366fd0c106bde80acf97ba4e533208eb53422c83b136611a54c5fefb1edd8dc267cb62e - languageName: node - linkType: hard - -"glob@npm:^10.2.2": - version: 10.3.10 - resolution: "glob@npm:10.3.10" - dependencies: - foreground-child: "npm:^3.1.0" - jackspeak: "npm:^2.3.5" - minimatch: "npm:^9.0.1" - minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry: "npm:^1.10.1" - bin: - glob: dist/esm/bin.mjs - checksum: 10/38bdb2c9ce75eb5ed168f309d4ed05b0798f640b637034800a6bf306f39d35409bf278b0eaaffaec07591085d3acb7184a201eae791468f0f617771c2486a6a8 - languageName: node - linkType: hard - -"glob@npm:^7.1.3, glob@npm:^7.1.4": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: "npm:^1.0.0" - inflight: "npm:^1.0.4" - inherits: "npm:2" - minimatch: "npm:^3.1.1" - once: "npm:^1.3.0" - path-is-absolute: "npm:^1.0.0" - checksum: 10/59452a9202c81d4508a43b8af7082ca5c76452b9fcc4a9ab17655822e6ce9b21d4f8fbadabe4fe3faef448294cec249af305e2cd824b7e9aaf689240e5e96a7b - languageName: node - linkType: hard - -"graceful-fs@npm:^4.2.6": - version: 4.2.11 - resolution: "graceful-fs@npm:4.2.11" - checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 - languageName: node - linkType: hard - -"has-unicode@npm:^2.0.1": - version: 2.0.1 - resolution: "has-unicode@npm:2.0.1" - checksum: 10/041b4293ad6bf391e21c5d85ed03f412506d6623786b801c4ab39e4e6ca54993f13201bceb544d92963f9e0024e6e7fbf0cb1d84c9d6b31cb9c79c8c990d13d8 - languageName: node - linkType: hard - -"http-cache-semantics@npm:^4.1.1": - version: 4.1.1 - resolution: "http-cache-semantics@npm:4.1.1" - checksum: 10/362d5ed66b12ceb9c0a328fb31200b590ab1b02f4a254a697dc796850cc4385603e75f53ec59f768b2dad3bfa1464bd229f7de278d2899a0e3beffc634b6683f - languageName: node - linkType: hard - -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" - dependencies: - "@tootallnate/once": "npm:2" - agent-base: "npm:6" - debug: "npm:4" - checksum: 10/5ee19423bc3e0fd5f23ce991b0755699ad2a46a440ce9cec99e8126bb98448ad3479d2c0ea54be5519db5b19a4ffaa69616bac01540db18506dd4dac3dc418f0 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: "npm:6" - debug: "npm:4" - checksum: 10/f0dce7bdcac5e8eaa0be3c7368bb8836ed010fb5b6349ffb412b172a203efe8f807d9a6681319105ea1b6901e1972c7b5ea899672a7b9aad58309f766dcbe0df - languageName: node - linkType: hard - -"humanize-ms@npm:^1.2.1": - version: 1.2.1 - resolution: "humanize-ms@npm:1.2.1" - dependencies: - ms: "npm:^2.0.0" - checksum: 10/9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 - languageName: node - linkType: hard - -"iconv-lite@npm:^0.6.2": - version: 0.6.3 - resolution: "iconv-lite@npm:0.6.3" - dependencies: - safer-buffer: "npm:>= 2.1.2 < 3.0.0" - checksum: 10/24e3292dd3dadaa81d065c6f8c41b274a47098150d444b96e5f53b4638a9a71482921ea6a91a1f59bb71d9796de25e04afd05919fa64c360347ba65d3766f10f - languageName: node - linkType: hard - -"imurmurhash@npm:^0.1.4": - version: 0.1.4 - resolution: "imurmurhash@npm:0.1.4" - checksum: 10/2d30b157a91fe1c1d7c6f653cbf263f039be6c5bfa959245a16d4ee191fc0f2af86c08545b6e6beeb041c56b574d2d5b9f95343d378ab49c0f37394d541e7fc8 - languageName: node - linkType: hard - -"indent-string@npm:^4.0.0": - version: 4.0.0 - resolution: "indent-string@npm:4.0.0" - checksum: 10/cd3f5cbc9ca2d624c6a1f53f12e6b341659aba0e2d3254ae2b4464aaea8b4294cdb09616abbc59458f980531f2429784ed6a420d48d245bcad0811980c9efae9 - languageName: node - linkType: hard - -"inflight@npm:^1.0.4": - version: 1.0.6 - resolution: "inflight@npm:1.0.6" - dependencies: - once: "npm:^1.3.0" - wrappy: "npm:1" - checksum: 10/d2ebd65441a38c8336c223d1b80b921b9fa737e37ea466fd7e253cb000c64ae1f17fa59e68130ef5bda92cfd8d36b83d37dab0eb0a4558bcfec8e8cdfd2dcb67 - languageName: node - linkType: hard - -"inherits@npm:2, inherits@npm:^2.0.3": - version: 2.0.4 - resolution: "inherits@npm:2.0.4" - checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521 - languageName: node - linkType: hard - -"intl-parse-accept-language@npm:^1.0.0": - version: 1.0.0 - resolution: "intl-parse-accept-language@npm:1.0.0" - checksum: 10/9d86e6b1d5e6547eeea6ad2e57e7cea8fb3c40a9c97af579660135fb912fbf4ea60dcf9ecd2f4ca83f139851df824b9fe6f78f760d58cfe0d9392d48f8c9b7f0 - languageName: node - linkType: hard - -"ip@npm:^2.0.0": - version: 2.0.1 - resolution: "ip@npm:2.0.1" - checksum: 8/d765c9fd212b8a99023a4cde6a558a054c298d640fec1020567494d257afd78ca77e37126b1a3ef0e053646ced79a816bf50621d38d5e768cdde0431fa3b0d35 - languageName: node - linkType: hard - -"is-binary-path@npm:~2.1.0": - version: 2.1.0 - resolution: "is-binary-path@npm:2.1.0" - dependencies: - binary-extensions: "npm:^2.0.0" - checksum: 10/078e51b4f956c2c5fd2b26bb2672c3ccf7e1faff38e0ebdba45612265f4e3d9fc3127a1fa8370bbf09eab61339203c3d3b7af5662cbf8be4030f8fac37745b0e - languageName: node - linkType: hard - -"is-extglob@npm:^2.1.1": - version: 2.1.1 - resolution: "is-extglob@npm:2.1.1" - checksum: 10/df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 - languageName: node - linkType: hard - -"is-fullwidth-code-point@npm:^3.0.0": - version: 3.0.0 - resolution: "is-fullwidth-code-point@npm:3.0.0" - checksum: 10/44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 - languageName: node - linkType: hard - -"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1": - version: 4.0.3 - resolution: "is-glob@npm:4.0.3" - dependencies: - is-extglob: "npm:^2.1.1" - checksum: 10/3ed74f2b0cdf4f401f38edb0442ddfde3092d79d7d35c9919c86641efdbcbb32e45aa3c0f70ce5eecc946896cd5a0f26e4188b9f2b881876f7cb6c505b82da11 - languageName: node - linkType: hard - -"is-lambda@npm:^1.0.1": - version: 1.0.1 - resolution: "is-lambda@npm:1.0.1" - checksum: 10/93a32f01940220532e5948538699ad610d5924ac86093fcee83022252b363eb0cc99ba53ab084a04e4fb62bf7b5731f55496257a4c38adf87af9c4d352c71c35 - languageName: node - linkType: hard - -"is-number@npm:^7.0.0": - version: 7.0.0 - resolution: "is-number@npm:7.0.0" - checksum: 10/6a6c3383f68afa1e05b286af866017c78f1226d43ac8cb064e115ff9ed85eb33f5c4f7216c96a71e4dfea289ef52c5da3aef5bbfade8ffe47a0465d70c0c8e86 - languageName: node - linkType: hard - -"isexe@npm:^2.0.0": - version: 2.0.0 - resolution: "isexe@npm:2.0.0" - checksum: 10/7c9f715c03aff08f35e98b1fadae1b9267b38f0615d501824f9743f3aab99ef10e303ce7db3f186763a0b70a19de5791ebfc854ff884d5a8c4d92211f642ec92 - languageName: node - linkType: hard - -"jackspeak@npm:^2.3.5": - version: 2.3.6 - resolution: "jackspeak@npm:2.3.6" - dependencies: - "@isaacs/cliui": "npm:^8.0.2" - "@pkgjs/parseargs": "npm:^0.11.0" - dependenciesMeta: - "@pkgjs/parseargs": - optional: true - checksum: 10/6e6490d676af8c94a7b5b29b8fd5629f21346911ebe2e32931c2a54210134408171c24cee1a109df2ec19894ad04a429402a8438cbf5cc2794585d35428ace76 - languageName: node - linkType: hard - -"lru-cache@npm:^6.0.0": - version: 6.0.0 - resolution: "lru-cache@npm:6.0.0" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10/fc1fe2ee205f7c8855fa0f34c1ab0bcf14b6229e35579ec1fd1079f31d6fc8ef8eb6fd17f2f4d99788d7e339f50e047555551ebd5e434dda503696e7c6591825 - languageName: node - linkType: hard - -"lru-cache@npm:^7.7.1": - version: 7.18.3 - resolution: "lru-cache@npm:7.18.3" - checksum: 10/6029ca5aba3aacb554e919d7ef804fffd4adfc4c83db00fac8248c7c78811fb6d4b6f70f7fd9d55032b3823446546a007edaa66ad1f2377ae833bd983fac5d98 - languageName: node - linkType: hard - -"lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.0.1 - resolution: "lru-cache@npm:10.0.1" - checksum: 10/5bb91a97a342a41fd049c3494b44d9e21a7d4843f9284d0a0b26f00bb0e436f1f627d0641c78f88be16b86b4231546c5ee4f284733fb530c7960f0bcd7579026 - languageName: node - linkType: hard - -"magic-string@npm:^0.25.3": - version: 0.25.9 - resolution: "magic-string@npm:0.25.9" - dependencies: - sourcemap-codec: "npm:^1.4.8" - checksum: 10/87a14b944bd169821cbd54b169a7ab6b0348fd44b5497266dc555dd70280744e9e88047da9dcb95675bdc23b1ce33f13398b0f70b3be7b858225ccb1d185ff51 - languageName: node - linkType: hard - -"make-fetch-happen@npm:^11.0.3": - version: 11.1.1 - resolution: "make-fetch-happen@npm:11.1.1" - dependencies: - agentkeepalive: "npm:^4.2.1" - cacache: "npm:^17.0.0" - http-cache-semantics: "npm:^4.1.1" - http-proxy-agent: "npm:^5.0.0" - https-proxy-agent: "npm:^5.0.0" - is-lambda: "npm:^1.0.1" - lru-cache: "npm:^7.7.1" - minipass: "npm:^5.0.0" - minipass-fetch: "npm:^3.0.0" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - negotiator: "npm:^0.6.3" - promise-retry: "npm:^2.0.1" - socks-proxy-agent: "npm:^7.0.0" - ssri: "npm:^10.0.0" - checksum: 10/b4b442cfaaec81db159f752a5f2e3ee3d7aa682782868fa399200824ec6298502e01bdc456e443dc219bcd5546c8e4471644d54109c8599841dc961d17a805fa - languageName: node - linkType: hard - -"mime@npm:^3.0.0": - version: 3.0.0 - resolution: "mime@npm:3.0.0" - bin: - mime: cli.js - checksum: 10/b2d31580deb58be89adaa1877cbbf152b7604b980fd7ef8f08b9e96bfedf7d605d9c23a8ba62aa12c8580b910cd7c1d27b7331d0f40f7a14e17d5a0bbec3b49f - languageName: node - linkType: hard - -"miniflare@npm:3.20231218.1": - version: 3.20231218.1 - resolution: "miniflare@npm:3.20231218.1" - dependencies: - "@cspotcode/source-map-support": "npm:0.8.1" - acorn: "npm:^8.8.0" - acorn-walk: "npm:^8.2.0" - capnp-ts: "npm:^0.7.0" - exit-hook: "npm:^2.2.1" - glob-to-regexp: "npm:^0.4.1" - stoppable: "npm:^1.1.0" - undici: "npm:^5.22.1" - workerd: "npm:1.20231218.0" - ws: "npm:^8.11.0" - youch: "npm:^3.2.2" - zod: "npm:^3.20.6" - bin: - miniflare: bootstrap.js - checksum: 10/08acfcb819233ab4f7ba4a631a3c5a78a8b7592febf8d426a28393d905a435e562744fbc69528184d95bd9582982316a12a5cd99210deabc1b7fb7bfecb00cc4 - languageName: node - linkType: hard - -"minimatch@npm:^3.1.1": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10/e0b25b04cd4ec6732830344e5739b13f8690f8a012d73445a4a19fbc623f5dd481ef7a5827fde25954cd6026fede7574cc54dc4643c99d6c6b653d6203f94634 - languageName: node - linkType: hard - -"minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10/c81b47d28153e77521877649f4bab48348d10938df9e8147a58111fe00ef89559a2938de9f6632910c4f7bf7bb5cd81191a546167e58d357f0cfb1e18cecc1c5 - languageName: node - linkType: hard - -"minipass-collect@npm:^1.0.2": - version: 1.0.2 - resolution: "minipass-collect@npm:1.0.2" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10/14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 - languageName: node - linkType: hard - -"minipass-fetch@npm:^3.0.0": - version: 3.0.4 - resolution: "minipass-fetch@npm:3.0.4" - dependencies: - encoding: "npm:^0.1.13" - minipass: "npm:^7.0.3" - minipass-sized: "npm:^1.0.3" - minizlib: "npm:^2.1.2" - dependenciesMeta: - encoding: - optional: true - checksum: 10/3edf72b900e30598567eafe96c30374432a8709e61bb06b87198fa3192d466777e2ec21c52985a0999044fa6567bd6f04651585983a1cbb27e2c1770a07ed2a2 - languageName: node - linkType: hard - -"minipass-flush@npm:^1.0.5": - version: 1.0.5 - resolution: "minipass-flush@npm:1.0.5" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10/56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf - languageName: node - linkType: hard - -"minipass-pipeline@npm:^1.2.4": - version: 1.2.4 - resolution: "minipass-pipeline@npm:1.2.4" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10/b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b - languageName: node - linkType: hard - -"minipass-sized@npm:^1.0.3": - version: 1.0.3 - resolution: "minipass-sized@npm:1.0.3" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10/40982d8d836a52b0f37049a0a7e5d0f089637298e6d9b45df9c115d4f0520682a78258905e5c8b180fb41b593b0a82cc1361d2c74b45f7ada66334f84d1ecfdd - languageName: node - linkType: hard - -"minipass@npm:^3.0.0": - version: 3.3.6 - resolution: "minipass@npm:3.3.6" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10/a5c6ef069f70d9a524d3428af39f2b117ff8cd84172e19b754e7264a33df460873e6eb3d6e55758531580970de50ae950c496256bb4ad3691a2974cddff189f0 - languageName: node - linkType: hard - -"minipass@npm:^5.0.0": - version: 5.0.0 - resolution: "minipass@npm:5.0.0" - checksum: 10/61682162d29f45d3152b78b08bab7fb32ca10899bc5991ffe98afc18c9e9543bd1e3be94f8b8373ba6262497db63607079dc242ea62e43e7b2270837b7347c93 - languageName: node - linkType: hard - -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.3": - version: 7.0.4 - resolution: "minipass@npm:7.0.4" - checksum: 10/e864bd02ceb5e0707696d58f7ce3a0b89233f0d686ef0d447a66db705c0846a8dc6f34865cd85256c1472ff623665f616b90b8ff58058b2ad996c5de747d2d18 - languageName: node - linkType: hard - -"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": - version: 2.1.2 - resolution: "minizlib@npm:2.1.2" - dependencies: - minipass: "npm:^3.0.0" - yallist: "npm:^4.0.0" - checksum: 10/ae0f45436fb51344dcb87938446a32fbebb540d0e191d63b35e1c773d47512e17307bf54aa88326cc6d176594d00e4423563a091f7266c2f9a6872cdc1e234d1 - languageName: node - linkType: hard - -"mkdirp@npm:^1.0.3": - version: 1.0.4 - resolution: "mkdirp@npm:1.0.4" - bin: - mkdirp: bin/cmd.js - checksum: 10/d71b8dcd4b5af2fe13ecf3bd24070263489404fe216488c5ba7e38ece1f54daf219e72a833a3a2dc404331e870e9f44963a33399589490956bff003a3404d3b2 - languageName: node - linkType: hard - -"ms@npm:2.1.2": - version: 2.1.2 - resolution: "ms@npm:2.1.2" - checksum: 10/673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f - languageName: node - linkType: hard - -"ms@npm:^2.0.0": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d - languageName: node - linkType: hard - -"mustache@npm:^4.2.0": - version: 4.2.0 - resolution: "mustache@npm:4.2.0" - bin: - mustache: bin/mustache - checksum: 10/6e668bd5803255ab0779c3983b9412b5c4f4f90e822230e0e8f414f5449ed7a137eed29430e835aa689886f663385cfe05f808eb34b16e1f3a95525889b05cd3 - languageName: node - linkType: hard - -"nanoid@npm:^3.3.3": - version: 3.3.6 - resolution: "nanoid@npm:3.3.6" - bin: - nanoid: bin/nanoid.cjs - checksum: 10/67235c39d1bc05851383dadde5cf77ae1c90c2a1d189e845c7f20f646f0488d875ad5f5226bbba072a88cebbb085a3f784a6673117daf785bdf614a852550362 - languageName: node - linkType: hard - -"negotiator@npm:^0.6.3": - version: 0.6.3 - resolution: "negotiator@npm:0.6.3" - checksum: 10/2723fb822a17ad55c93a588a4bc44d53b22855bf4be5499916ca0cab1e7165409d0b288ba2577d7b029f10ce18cf2ed8e703e5af31c984e1e2304277ef979837 - languageName: node - linkType: hard - -"node-forge@npm:^1": - version: 1.3.1 - resolution: "node-forge@npm:1.3.1" - checksum: 10/05bab6868633bf9ad4c3b1dd50ec501c22ffd69f556cdf169a00998ca1d03e8107a6032ba013852f202035372021b845603aeccd7dfcb58cdb7430013b3daa8d - languageName: node - linkType: hard - -"node-gyp@npm:latest": - version: 9.4.0 - resolution: "node-gyp@npm:9.4.0" - dependencies: - env-paths: "npm:^2.2.0" - exponential-backoff: "npm:^3.1.1" - glob: "npm:^7.1.4" - graceful-fs: "npm:^4.2.6" - make-fetch-happen: "npm:^11.0.3" - nopt: "npm:^6.0.0" - npmlog: "npm:^6.0.0" - rimraf: "npm:^3.0.2" - semver: "npm:^7.3.5" - tar: "npm:^6.1.2" - which: "npm:^2.0.2" - bin: - node-gyp: bin/node-gyp.js - checksum: 10/458317127c63877365f227b18ef2362b013b7f8440b35ae722935e61b31e6b84ec0e3625ab07f90679e2f41a1d5a7df6c4049fdf8e7b3c81fcf22775147b47ac - languageName: node - linkType: hard - -"nopt@npm:^6.0.0": - version: 6.0.0 - resolution: "nopt@npm:6.0.0" - dependencies: - abbrev: "npm:^1.0.0" - bin: - nopt: bin/nopt.js - checksum: 10/3c1128e07cd0241ae66d6e6a472170baa9f3e84dd4203950ba8df5bafac4efa2166ce917a57ef02b01ba7c40d18b2cc64b29b225fd3640791fe07b24f0b33a32 - languageName: node - linkType: hard - -"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": - version: 3.0.0 - resolution: "normalize-path@npm:3.0.0" - checksum: 10/88eeb4da891e10b1318c4b2476b6e2ecbeb5ff97d946815ffea7794c31a89017c70d7f34b3c2ebf23ef4e9fc9fb99f7dffe36da22011b5b5c6ffa34f4873ec20 - languageName: node - linkType: hard - -"npmlog@npm:^6.0.0": - version: 6.0.2 - resolution: "npmlog@npm:6.0.2" - dependencies: - are-we-there-yet: "npm:^3.0.0" - console-control-strings: "npm:^1.1.0" - gauge: "npm:^4.0.3" - set-blocking: "npm:^2.0.0" - checksum: 10/82b123677e62deb9e7472e27b92386c09e6e254ee6c8bcd720b3011013e4168bc7088e984f4fbd53cb6e12f8b4690e23e4fa6132689313e0d0dc4feea45489bb - languageName: node - linkType: hard - -"once@npm:^1.3.0": - version: 1.4.0 - resolution: "once@npm:1.4.0" - dependencies: - wrappy: "npm:1" - checksum: 10/cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 - languageName: node - linkType: hard - -"p-map@npm:^4.0.0": - version: 4.0.0 - resolution: "p-map@npm:4.0.0" - dependencies: - aggregate-error: "npm:^3.0.0" - checksum: 10/7ba4a2b1e24c05e1fc14bbaea0fc6d85cf005ae7e9c9425d4575550f37e2e584b1af97bcde78eacd7559208f20995988d52881334db16cf77bc1bcf68e48ed7c - languageName: node - linkType: hard - -"path-is-absolute@npm:^1.0.0": - version: 1.0.1 - resolution: "path-is-absolute@npm:1.0.1" - checksum: 10/060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 - languageName: node - linkType: hard - -"path-key@npm:^3.1.0": - version: 3.1.1 - resolution: "path-key@npm:3.1.1" - checksum: 10/55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 - languageName: node - linkType: hard - -"path-scurry@npm:^1.10.1": - version: 1.10.1 - resolution: "path-scurry@npm:1.10.1" - dependencies: - lru-cache: "npm:^9.1.1 || ^10.0.0" - minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" - checksum: 10/eebfb8304fef1d4f7e1486df987e4fd77413de4fce16508dea69fcf8eb318c09a6b15a7a2f4c22877cec1cb7ecbd3071d18ca9de79eeece0df874a00f1f0bdc8 - languageName: node - linkType: hard - -"path-to-regexp@npm:^6.2.0": - version: 6.3.0 - resolution: "path-to-regexp@npm:6.3.0" - checksum: 10c0/73b67f4638b41cde56254e6354e46ae3a2ebc08279583f6af3d96fe4664fc75788f74ed0d18ca44fa4a98491b69434f9eee73b97bb5314bd1b5adb700f5c18d6 - languageName: node - linkType: hard - -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1": - version: 2.3.1 - resolution: "picomatch@npm:2.3.1" - checksum: 10/60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc - languageName: node - linkType: hard - -"printable-characters@npm:^1.0.42": - version: 1.0.42 - resolution: "printable-characters@npm:1.0.42" - checksum: 10/5fd9f44f2b24c9d875a97642a72be27f53aaac7f0f8f2792f969f3082e4516878db21cfa999f827606b002a890e6afeac0e0cc8dcb0d2d7965252975e634c6b2 - languageName: node - linkType: hard - -"promise-retry@npm:^2.0.1": - version: 2.0.1 - resolution: "promise-retry@npm:2.0.1" - dependencies: - err-code: "npm:^2.0.2" - retry: "npm:^0.12.0" - checksum: 10/96e1a82453c6c96eef53a37a1d6134c9f2482f94068f98a59145d0986ca4e497bf110a410adf73857e588165eab3899f0ebcf7b3890c1b3ce802abc0d65967d4 - languageName: node - linkType: hard - -"readable-stream@npm:^3.6.0": - version: 3.6.2 - resolution: "readable-stream@npm:3.6.2" - dependencies: - inherits: "npm:^2.0.3" - string_decoder: "npm:^1.1.1" - util-deprecate: "npm:^1.0.1" - checksum: 10/d9e3e53193adcdb79d8f10f2a1f6989bd4389f5936c6f8b870e77570853561c362bee69feca2bbb7b32368ce96a85504aa4cedf7cf80f36e6a9de30d64244048 - languageName: node - linkType: hard - -"readdirp@npm:~3.6.0": - version: 3.6.0 - resolution: "readdirp@npm:3.6.0" - dependencies: - picomatch: "npm:^2.2.1" - checksum: 10/196b30ef6ccf9b6e18c4e1724b7334f72a093d011a99f3b5920470f0b3406a51770867b3e1ae9711f227ef7a7065982f6ee2ce316746b2cb42c88efe44297fe7 - languageName: node - linkType: hard - -"resolve.exports@npm:^2.0.2": - version: 2.0.2 - resolution: "resolve.exports@npm:2.0.2" - checksum: 10/f1cc0b6680f9a7e0345d783e0547f2a5110d8336b3c2a4227231dd007271ffd331fd722df934f017af90bae0373920ca0d4005da6f76cb3176c8ae426370f893 - languageName: node - linkType: hard - -"retry@npm:^0.12.0": - version: 0.12.0 - resolution: "retry@npm:0.12.0" - checksum: 10/1f914879f97e7ee931ad05fe3afa629bd55270fc6cf1c1e589b6a99fab96d15daad0fa1a52a00c729ec0078045fe3e399bd4fd0c93bcc906957bdc17f89cb8e6 - languageName: node - linkType: hard - -"rimraf@npm:^3.0.2": - version: 3.0.2 - resolution: "rimraf@npm:3.0.2" - dependencies: - glob: "npm:^7.1.3" - bin: - rimraf: bin.js - checksum: 10/063ffaccaaaca2cfd0ef3beafb12d6a03dd7ff1260d752d62a6077b5dfff6ae81bea571f655bb6b589d366930ec1bdd285d40d560c0dae9b12f125e54eb743d5 - languageName: node - linkType: hard - -"rollup-plugin-inject@npm:^3.0.0": - version: 3.0.2 - resolution: "rollup-plugin-inject@npm:3.0.2" - dependencies: - estree-walker: "npm:^0.6.1" - magic-string: "npm:^0.25.3" - rollup-pluginutils: "npm:^2.8.1" - checksum: 10/34081611c4b00b582339fc76880844d9729d9a26ede987c9939440cb0affe5965d4c9b1ebb62a021bb67e118426420de77114731404fa57126e35186267548e7 - languageName: node - linkType: hard - -"rollup-plugin-node-polyfills@npm:^0.2.1": - version: 0.2.1 - resolution: "rollup-plugin-node-polyfills@npm:0.2.1" - dependencies: - rollup-plugin-inject: "npm:^3.0.0" - checksum: 10/283c108108f93684975c83fd2b274d028162a9df0db2225737bfd0f8cab9215f0228d3703928ef667a8ba2f4749649ba06c58b89f48a211d7116e7f98fc988dd - languageName: node - linkType: hard - -"rollup-pluginutils@npm:^2.8.1": - version: 2.8.2 - resolution: "rollup-pluginutils@npm:2.8.2" - dependencies: - estree-walker: "npm:^0.6.1" - checksum: 10/f3dc20a8731523aff43e07fa50ed84857e9dd3ab81e2cfb0351d517c46820e585bfbd1530a5dddec3ac14d61d41eb9bf50b38ded987e558292790331cc5b0628 - languageName: node - linkType: hard - -"safe-buffer@npm:~5.2.0": - version: 5.2.1 - resolution: "safe-buffer@npm:5.2.1" - checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451 - languageName: node - linkType: hard - -"safer-buffer@npm:>= 2.1.2 < 3.0.0": - version: 2.1.2 - resolution: "safer-buffer@npm:2.1.2" - checksum: 10/7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83 - languageName: node - linkType: hard - -"selfsigned@npm:^2.0.1": - version: 2.1.1 - resolution: "selfsigned@npm:2.1.1" - dependencies: - node-forge: "npm:^1" - checksum: 10/6005206e0d005448274aceceaded5195b944f67a42b72d212a6169d2e5f4bdc87c15a3fe45732c544db8c7175702091aaf95403ad6632585294a6ec8cca63638 - languageName: node - linkType: hard - -"semver@npm:^7.3.5": - version: 7.5.4 - resolution: "semver@npm:7.5.4" - dependencies: - lru-cache: "npm:^6.0.0" - bin: - semver: bin/semver.js - checksum: 10/985dec0d372370229a262c737063860fabd4a1c730662c1ea3200a2f649117761a42184c96df62a0e885e76fbd5dace41087d6c1ac0351b13c0df5d6bcb1b5ac - languageName: node - linkType: hard - -"set-blocking@npm:^2.0.0": - version: 2.0.0 - resolution: "set-blocking@npm:2.0.0" - checksum: 10/8980ebf7ae9eb945bb036b6e283c547ee783a1ad557a82babf758a065e2fb6ea337fd82cac30dd565c1e606e423f30024a19fff7afbf4977d784720c4026a8ef - languageName: node - linkType: hard - -"shebang-command@npm:^2.0.0": - version: 2.0.0 - resolution: "shebang-command@npm:2.0.0" - dependencies: - shebang-regex: "npm:^3.0.0" - checksum: 10/6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa - languageName: node - linkType: hard - -"shebang-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "shebang-regex@npm:3.0.0" - checksum: 10/1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 - languageName: node - linkType: hard - -"signal-exit@npm:^3.0.7": - version: 3.0.7 - resolution: "signal-exit@npm:3.0.7" - checksum: 10/a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 - languageName: node - linkType: hard - -"signal-exit@npm:^4.0.1": - version: 4.1.0 - resolution: "signal-exit@npm:4.1.0" - checksum: 10/c9fa63bbbd7431066174a48ba2dd9986dfd930c3a8b59de9c29d7b6854ec1c12a80d15310869ea5166d413b99f041bfa3dd80a7947bcd44ea8e6eb3ffeabfa1f - languageName: node - linkType: hard - -"smart-buffer@npm:^4.2.0": - version: 4.2.0 - resolution: "smart-buffer@npm:4.2.0" - checksum: 10/927484aa0b1640fd9473cee3e0a0bcad6fce93fd7bbc18bac9ad0c33686f5d2e2c422fba24b5899c184524af01e11dd2bd051c2bf2b07e47aff8ca72cbfc60d2 - languageName: node - linkType: hard - -"socks-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "socks-proxy-agent@npm:7.0.0" - dependencies: - agent-base: "npm:^6.0.2" - debug: "npm:^4.3.3" - socks: "npm:^2.6.2" - checksum: 10/26c75d9c62a9ed3fd494df60e65e88da442f78e0d4bc19bfd85ac37bd2c67470d6d4bba5202e804561cda6674db52864c9e2a2266775f879bc8d89c1445a5f4c - languageName: node - linkType: hard - -"socks@npm:^2.6.2": - version: 2.7.1 - resolution: "socks@npm:2.7.1" - dependencies: - ip: "npm:^2.0.0" - smart-buffer: "npm:^4.2.0" - checksum: 10/5074f7d6a13b3155fa655191df1c7e7a48ce3234b8ccf99afa2ccb56591c195e75e8bb78486f8e9ea8168e95a29573cbaad55b2b5e195160ae4d2ea6811ba833 - languageName: node - linkType: hard - -"source-map@npm:0.6.1, source-map@npm:^0.6.1": - version: 0.6.1 - resolution: "source-map@npm:0.6.1" - checksum: 10/59ef7462f1c29d502b3057e822cdbdae0b0e565302c4dd1a95e11e793d8d9d62006cdc10e0fd99163ca33ff2071360cf50ee13f90440806e7ed57d81cba2f7ff - languageName: node - linkType: hard - -"sourcemap-codec@npm:^1.4.8": - version: 1.4.8 - resolution: "sourcemap-codec@npm:1.4.8" - checksum: 10/6fc57a151e982b5c9468362690c6d062f3a0d4d8520beb68a82f319c79e7a4d7027eeb1e396de0ecc2cd19491e1d602b2d06fd444feac9b63dd43fea4c55a857 - languageName: node - linkType: hard - -"ssri@npm:^10.0.0": - version: 10.0.5 - resolution: "ssri@npm:10.0.5" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10/453f9a1c241c13f5dfceca2ab7b4687bcff354c3ccbc932f35452687b9ef0ccf8983fd13b8a3baa5844c1a4882d6e3ddff48b0e7fd21d743809ef33b80616d79 - languageName: node - linkType: hard - -"stacktracey@npm:^2.1.8": - version: 2.1.8 - resolution: "stacktracey@npm:2.1.8" - dependencies: - as-table: "npm:^1.0.36" - get-source: "npm:^2.0.12" - checksum: 10/c87f708b639636788c4b46ecc6e503c27b6124bec724bcdc3180d7cdddfab0dee370225009e3b407adaedf847362cfc77af64f01c805516e39a28d16c6d40df8 - languageName: node - linkType: hard - -"stoppable@npm:^1.1.0": - version: 1.1.0 - resolution: "stoppable@npm:1.1.0" - checksum: 10/63104fcbdece130bc4906fd982061e763d2ef48065ed1ab29895e5ad00552c625f8a4c50c9cd2e3bfa805c8a2c3bfdda0f07c5ae39694bd2d5cb0bee1618d1e9 - languageName: node - linkType: hard - -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.3": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: "npm:^8.0.0" - is-fullwidth-code-point: "npm:^3.0.0" - strip-ansi: "npm:^6.0.1" - checksum: 10/e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb - languageName: node - linkType: hard - -"string-width@npm:^5.0.1, string-width@npm:^5.1.2": - version: 5.1.2 - resolution: "string-width@npm:5.1.2" - dependencies: - eastasianwidth: "npm:^0.2.0" - emoji-regex: "npm:^9.2.2" - strip-ansi: "npm:^7.0.1" - checksum: 10/7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 - languageName: node - linkType: hard - -"string_decoder@npm:^1.1.1": - version: 1.3.0 - resolution: "string_decoder@npm:1.3.0" - dependencies: - safe-buffer: "npm:~5.2.0" - checksum: 10/54d23f4a6acae0e93f999a585e673be9e561b65cd4cca37714af1e893ab8cd8dfa52a9e4f58f48f87b4a44918d3a9254326cb80ed194bf2e4c226e2b21767e56 - languageName: node - linkType: hard - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" - dependencies: - ansi-regex: "npm:^5.0.1" - checksum: 10/ae3b5436d34fadeb6096367626ce987057713c566e1e7768818797e00ac5d62023d0f198c4e681eae9e20701721980b26a64a8f5b91238869592a9c6800719a2 - languageName: node - linkType: hard - -"strip-ansi@npm:^7.0.1": - version: 7.1.0 - resolution: "strip-ansi@npm:7.1.0" - dependencies: - ansi-regex: "npm:^6.0.1" - checksum: 10/475f53e9c44375d6e72807284024ac5d668ee1d06010740dec0b9744f2ddf47de8d7151f80e5f6190fc8f384e802fdf9504b76a7e9020c9faee7103623338be2 - languageName: node - linkType: hard - -"tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.2.1 - resolution: "tar@npm:6.2.1" - dependencies: - chownr: "npm:^2.0.0" - fs-minipass: "npm:^2.0.0" - minipass: "npm:^5.0.0" - minizlib: "npm:^2.1.1" - mkdirp: "npm:^1.0.3" - yallist: "npm:^4.0.0" - checksum: 8/f1322768c9741a25356c11373bce918483f40fa9a25c69c59410c8a1247632487edef5fe76c5f12ac51a6356d2f1829e96d2bc34098668a2fc34d76050ac2b6c - languageName: node - linkType: hard - -"to-regex-range@npm:^5.0.1": - version: 5.0.1 - resolution: "to-regex-range@npm:5.0.1" - dependencies: - is-number: "npm:^7.0.0" - checksum: 10/10dda13571e1f5ad37546827e9b6d4252d2e0bc176c24a101252153ef435d83696e2557fe128c4678e4e78f5f01e83711c703eef9814eb12dab028580d45980a - languageName: node - linkType: hard - -"tslib@npm:^2.2.0": - version: 2.6.2 - resolution: "tslib@npm:2.6.2" - checksum: 10/bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca - languageName: node - linkType: hard - -"undici@npm:^5.22.1": - version: 5.28.4 - resolution: "undici@npm:5.28.4" - dependencies: - "@fastify/busboy": "npm:^2.0.0" - checksum: 8/a8193132d84540e4dc1895ecc8dbaa176e8a49d26084d6fbe48a292e28397cd19ec5d13bc13e604484e76f94f6e334b2bdc740d5f06a6e50c44072818d0c19f9 - languageName: node - linkType: hard - -"unique-filename@npm:^3.0.0": - version: 3.0.0 - resolution: "unique-filename@npm:3.0.0" - dependencies: - unique-slug: "npm:^4.0.0" - checksum: 10/8e2f59b356cb2e54aab14ff98a51ac6c45781d15ceaab6d4f1c2228b780193dc70fae4463ce9e1df4479cb9d3304d7c2043a3fb905bdeca71cc7e8ce27e063df - languageName: node - linkType: hard - -"unique-slug@npm:^4.0.0": - version: 4.0.0 - resolution: "unique-slug@npm:4.0.0" - dependencies: - imurmurhash: "npm:^0.1.4" - checksum: 10/40912a8963fc02fb8b600cf50197df4a275c602c60de4cac4f75879d3c48558cfac48de08a25cc10df8112161f7180b3bbb4d662aadb711568602f9eddee54f0 - languageName: node - linkType: hard - -"util-deprecate@npm:^1.0.1": - version: 1.0.2 - resolution: "util-deprecate@npm:1.0.2" - checksum: 10/474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 - languageName: node - linkType: hard - -"which@npm:^2.0.1, which@npm:^2.0.2": - version: 2.0.2 - resolution: "which@npm:2.0.2" - dependencies: - isexe: "npm:^2.0.0" - bin: - node-which: ./bin/node-which - checksum: 10/4782f8a1d6b8fc12c65e968fea49f59752bf6302dc43036c3bf87da718a80710f61a062516e9764c70008b487929a73546125570acea95c5b5dcc8ac3052c70f - languageName: node - linkType: hard - -"wide-align@npm:^1.1.5": - version: 1.1.5 - resolution: "wide-align@npm:1.1.5" - dependencies: - string-width: "npm:^1.0.2 || 2 || 3 || 4" - checksum: 10/d5f8027b9a8255a493a94e4ec1b74a27bff6679d5ffe29316a3215e4712945c84ef73ca4045c7e20ae7d0c72f5f57f296e04a4928e773d4276a2f1222e4c2e99 - languageName: node - linkType: hard - -"worker-sites-template@workspace:.": - version: 0.0.0-use.local - resolution: "worker-sites-template@workspace:." - dependencies: - "@cloudflare/kv-asset-handler": "npm:^0.3.0" - intl-parse-accept-language: "npm:^1.0.0" - wrangler: "npm:^3.22.3" - languageName: unknown - linkType: soft - -"workerd@npm:1.20231218.0": - version: 1.20231218.0 - resolution: "workerd@npm:1.20231218.0" - dependencies: - "@cloudflare/workerd-darwin-64": "npm:1.20231218.0" - "@cloudflare/workerd-darwin-arm64": "npm:1.20231218.0" - "@cloudflare/workerd-linux-64": "npm:1.20231218.0" - "@cloudflare/workerd-linux-arm64": "npm:1.20231218.0" - "@cloudflare/workerd-windows-64": "npm:1.20231218.0" - dependenciesMeta: - "@cloudflare/workerd-darwin-64": - optional: true - "@cloudflare/workerd-darwin-arm64": - optional: true - "@cloudflare/workerd-linux-64": - optional: true - "@cloudflare/workerd-linux-arm64": - optional: true - "@cloudflare/workerd-windows-64": - optional: true - bin: - workerd: bin/workerd - checksum: 10/055f2d624a6a7bd951c30b56e1b48b122ecacd6755658a342a8bb386efa520b64e63c8ca8448fd09d4e56a9761f7fd3061205b27dc4b1437be197c8cd2187ff7 - languageName: node - linkType: hard - -"wrangler@npm:^3.22.3": - version: 3.22.3 - resolution: "wrangler@npm:3.22.3" - dependencies: - "@cloudflare/kv-asset-handler": "npm:^0.2.0" - "@cspotcode/source-map-support": "npm:0.8.1" - "@esbuild-plugins/node-globals-polyfill": "npm:^0.2.3" - "@esbuild-plugins/node-modules-polyfill": "npm:^0.2.2" - blake3-wasm: "npm:^2.1.5" - chokidar: "npm:^3.5.3" - esbuild: "npm:0.17.19" - fsevents: "npm:~2.3.2" - miniflare: "npm:3.20231218.1" - nanoid: "npm:^3.3.3" - path-to-regexp: "npm:^6.2.0" - resolve.exports: "npm:^2.0.2" - selfsigned: "npm:^2.0.1" - source-map: "npm:0.6.1" - xxhash-wasm: "npm:^1.0.1" - dependenciesMeta: - fsevents: - optional: true - bin: - wrangler: bin/wrangler.js - wrangler2: bin/wrangler.js - checksum: 10/e5c97afd80cfc169c6e493e0418adb0b6e04589c937f69297f174b4fb9db5b47adf850dd60e0bf71d55c5ce18782b6fe09961a7ca0b0db9044d27dabfde89844 - languageName: node - linkType: hard - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" - dependencies: - ansi-styles: "npm:^4.0.0" - string-width: "npm:^4.1.0" - strip-ansi: "npm:^6.0.0" - checksum: 10/cebdaeca3a6880da410f75209e68cd05428580de5ad24535f22696d7d9cab134d1f8498599f344c3cf0fb37c1715807a183778d8c648d6cc0cb5ff2bb4236540 - languageName: node - linkType: hard - -"wrap-ansi@npm:^8.1.0": - version: 8.1.0 - resolution: "wrap-ansi@npm:8.1.0" - dependencies: - ansi-styles: "npm:^6.1.0" - string-width: "npm:^5.0.1" - strip-ansi: "npm:^7.0.1" - checksum: 10/7b1e4b35e9bb2312d2ee9ee7dc95b8cb5f8b4b5a89f7dde5543fe66c1e3715663094defa50d75454ac900bd210f702d575f15f3f17fa9ec0291806d2578d1ddf - languageName: node - linkType: hard - -"wrappy@npm:1": - version: 1.0.2 - resolution: "wrappy@npm:1.0.2" - checksum: 10/159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 - languageName: node - linkType: hard - -"ws@npm:^8.11.0": - version: 8.17.1 - resolution: "ws@npm:8.17.1" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 10c0/f4a49064afae4500be772abdc2211c8518f39e1c959640457dcee15d4488628620625c783902a52af2dd02f68558da2868fd06e6fd0e67ebcd09e6881b1b5bfe - languageName: node - linkType: hard - -"xxhash-wasm@npm:^1.0.1": - version: 1.0.2 - resolution: "xxhash-wasm@npm:1.0.2" - checksum: 10/fb66e00f57c87353688ff31a8456ca71e16b1c13610d94d09f83cbd859a1985de07ccfc6aa912a045c991da0078d4122d78d409123e36557afab7ce5d3b04a98 - languageName: node - linkType: hard - -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 10/4cb02b42b8a93b5cf50caf5d8e9beb409400a8a4d85e83bb0685c1457e9ac0b7a00819e9f5991ac25ffabb56a78e2f017c1acc010b3a1babfe6de690ba531abd - languageName: node - linkType: hard - -"youch@npm:^3.2.2": - version: 3.3.2 - resolution: "youch@npm:3.3.2" - dependencies: - cookie: "npm:^0.5.0" - mustache: "npm:^4.2.0" - stacktracey: "npm:^2.1.8" - checksum: 10/36cc2204b898f3051e2d4f4a41ade6220d51e85f905b5884965458e242df998832313902c65080791fe7a2091718b99f7b290ead3b44b6a68a116ebc143ed372 - languageName: node - linkType: hard - -"zod@npm:^3.20.6": - version: 3.22.4 - resolution: "zod@npm:3.22.4" - checksum: 10/73622ca36a916f785cf528fe612a884b3e0f183dbe6b33365a7d0fc92abdbedf7804c5e2bd8df0a278e1472106d46674281397a3dd800fa9031dc3429758c6ac - languageName: node - linkType: hard +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cloudflare/kv-asset-handler@0.3.4", "@cloudflare/kv-asset-handler@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz#5cc152847c8ae4d280ec5d7f4f6ba8c976b585c3" + integrity sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q== + dependencies: + mime "^3.0.0" + +"@cloudflare/workerd-darwin-64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241106.1.tgz#4f470f98ca12dbc3262ec8a432466e1c1525aad9" + integrity sha512-zxvaToi1m0qzAScrxFt7UvFVqU8DxrCO2CinM1yQkv5no7pA1HolpIrwZ0xOhR3ny64Is2s/J6BrRjpO5dM9Zw== + +"@cloudflare/workerd-darwin-arm64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20241106.1.tgz#c34d6306afc50ae2eee3e538329af7192ae17dd0" + integrity sha512-j3dg/42D/bPgfNP3cRUBxF+4waCKO/5YKwXNj+lnVOwHxDu+ne5pFw9TIkKYcWTcwn0ZUkbNZNM5rhJqRn4xbg== + +"@cloudflare/workerd-linux-64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20241106.1.tgz#42c425137c151348146a70d3f271e5f3293d3b75" + integrity sha512-Ih+Ye8E1DMBXcKrJktGfGztFqHKaX1CeByqshmTbODnWKHt6O65ax3oTecUwyC0+abuyraOpAtdhHNpFMhUkmw== + +"@cloudflare/workerd-linux-arm64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20241106.1.tgz#f3bc7ab9424dafbf1816d8bc2e8aae24646ecad0" + integrity sha512-mdQFPk4+14Yywn7n1xIzI+6olWM8Ybz10R7H3h+rk0XulMumCWUCy1CzIDauOx6GyIcSgKIibYMssVHZR30ObA== + +"@cloudflare/workerd-windows-64@1.20241106.1": + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20241106.1.tgz#9e2f1ec0d993c8b12c4cd7f9c5e6b953a0672707" + integrity sha512-4rtcss31E/Rb/PeFocZfr+B9i1MdrkhsTBWizh8siNR4KMmkslU2xs2wPaH1z8+ErxkOsHrKRa5EPLh5rIiFeg== + +"@cloudflare/workers-shared@0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workers-shared/-/workers-shared-0.7.1.tgz#cf32caaf58808d9e36f9ebc8baa84a9699b388f2" + integrity sha512-46cP5FCrl3TrvHeoHLb5SRuiDMKH5kc9Yvo36SAfzt8dqJI/qJRoY1GP3ioHn/gP7v2QIoUOTAzIl7Ml7MnfrA== + dependencies: + mime "^3.0.0" + zod "^3.22.3" + +"@cspotcode/source-map-support@0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@esbuild-plugins/node-globals-polyfill@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz#0e4497a2b53c9e9485e149bc92ddb228438d6bcf" + integrity sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw== + +"@esbuild-plugins/node-modules-polyfill@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz#cefa3dc0bd1c16277a8338b52833420c94987327" + integrity sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA== + dependencies: + escape-string-regexp "^4.0.0" + rollup-plugin-node-polyfills "^0.2.1" + +"@esbuild/android-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" + integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== + +"@esbuild/android-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" + integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== + +"@esbuild/android-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" + integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== + +"@esbuild/darwin-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" + integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + +"@esbuild/darwin-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" + integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== + +"@esbuild/freebsd-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" + integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== + +"@esbuild/freebsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" + integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== + +"@esbuild/linux-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" + integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== + +"@esbuild/linux-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" + integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== + +"@esbuild/linux-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" + integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== + +"@esbuild/linux-loong64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" + integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== + +"@esbuild/linux-mips64el@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" + integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== + +"@esbuild/linux-ppc64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" + integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== + +"@esbuild/linux-riscv64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" + integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== + +"@esbuild/linux-s390x@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" + integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== + +"@esbuild/linux-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== + +"@esbuild/netbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" + integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== + +"@esbuild/openbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" + integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== + +"@esbuild/sunos-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" + integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== + +"@esbuild/win32-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" + integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== + +"@esbuild/win32-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" + integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== + +"@esbuild/win32-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" + integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== + +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@types/node-forge@^1.3.0": + version "1.3.11" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "22.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" + integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== + dependencies: + undici-types "~6.19.8" + +acorn-walk@^8.2.0: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.8.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +as-table@^1.0.36: + version "1.0.55" + resolved "https://registry.yarnpkg.com/as-table/-/as-table-1.0.55.tgz#dc984da3937745de902cea1d45843c01bdbbec4f" + integrity sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ== + dependencies: + printable-characters "^1.0.42" + +blake3-wasm@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/blake3-wasm/-/blake3-wasm-2.1.5.tgz#b22dbb84bc9419ed0159caa76af4b1b132e6ba52" + integrity sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g== + +capnp-ts@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/capnp-ts/-/capnp-ts-0.7.0.tgz#16fd8e76b667d002af8fcf4bf92bf15d1a7b54a9" + integrity sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g== + dependencies: + debug "^4.3.1" + tslib "^2.2.0" + +chokidar@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" + integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA== + dependencies: + readdirp "^4.0.1" + +cookie@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +data-uri-to-buffer@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz#d296973d5a4897a5dbe31716d118211921f04770" + integrity sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA== + +date-fns@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" + integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== + +debug@^4.3.1: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + +defu@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== + +esbuild@0.17.19: + version "0.17.19" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955" + integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== + optionalDependencies: + "@esbuild/android-arm" "0.17.19" + "@esbuild/android-arm64" "0.17.19" + "@esbuild/android-x64" "0.17.19" + "@esbuild/darwin-arm64" "0.17.19" + "@esbuild/darwin-x64" "0.17.19" + "@esbuild/freebsd-arm64" "0.17.19" + "@esbuild/freebsd-x64" "0.17.19" + "@esbuild/linux-arm" "0.17.19" + "@esbuild/linux-arm64" "0.17.19" + "@esbuild/linux-ia32" "0.17.19" + "@esbuild/linux-loong64" "0.17.19" + "@esbuild/linux-mips64el" "0.17.19" + "@esbuild/linux-ppc64" "0.17.19" + "@esbuild/linux-riscv64" "0.17.19" + "@esbuild/linux-s390x" "0.17.19" + "@esbuild/linux-x64" "0.17.19" + "@esbuild/netbsd-x64" "0.17.19" + "@esbuild/openbsd-x64" "0.17.19" + "@esbuild/sunos-x64" "0.17.19" + "@esbuild/win32-arm64" "0.17.19" + "@esbuild/win32-ia32" "0.17.19" + "@esbuild/win32-x64" "0.17.19" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +exit-hook@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593" + integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-source@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/get-source/-/get-source-2.0.12.tgz#0b47d57ea1e53ce0d3a69f4f3d277eb8047da944" + integrity sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w== + dependencies: + data-uri-to-buffer "^2.0.0" + source-map "^0.6.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +intl-parse-accept-language@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/intl-parse-accept-language/-/intl-parse-accept-language-1.0.0.tgz#204d1bc0b13c5baea7775f5eeb95bef2b795675c" + integrity sha512-YFMSV91JNBOSjw1cOfw2tup6hDP7mkz+2AUV7W1L1AM6ntgI75qC1ZeFpjPGMrWp+upmBRTX2fJWQ8c7jsUWpA== + +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +itty-time@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/itty-time/-/itty-time-1.0.6.tgz#a6eeda619f19d2f4c480ceddd013b93acb05714d" + integrity sha512-+P8IZaLLBtFv8hCkIjcymZOp4UJ+xW6bSlQsXGqrkmJh7vSiMFSlNne0mCYagEE0N7HDNR5jJBRxwN0oYv61Rw== + +magic-string@^0.25.3: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + +miniflare@3.20241106.0: + version "3.20241106.0" + resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-3.20241106.0.tgz#d69854e7267862468dfc057c23ca57bf7801e89d" + integrity sha512-PjOoJKjUUofCueQskfhXlGvvHxZj36UAJAp1DnquMK88MFF50zCULblh0KXMSNM+bXeQYA94Gj06a7kfmBGxPw== + dependencies: + "@cspotcode/source-map-support" "0.8.1" + acorn "^8.8.0" + acorn-walk "^8.2.0" + capnp-ts "^0.7.0" + exit-hook "^2.2.1" + glob-to-regexp "^0.4.1" + stoppable "^1.1.0" + undici "^5.28.4" + workerd "1.20241106.1" + ws "^8.18.0" + youch "^3.2.2" + zod "^3.22.3" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + +nanoid@^3.3.3: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +ohash@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/ohash/-/ohash-1.1.4.tgz#ae8d83014ab81157d2c285abf7792e2995fadd72" + integrity sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" + integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== + +pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +printable-characters@^1.0.42: + version "1.0.42" + resolved "https://registry.yarnpkg.com/printable-characters/-/printable-characters-1.0.42.tgz#3f18e977a9bd8eb37fcc4ff5659d7be90868b3d8" + integrity sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ== + +readdirp@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" + integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== + +resolve.exports@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + +resolve@^1.22.8: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rollup-plugin-inject@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz#e4233855bfba6c0c12a312fd6649dff9a13ee9f4" + integrity sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w== + dependencies: + estree-walker "^0.6.1" + magic-string "^0.25.3" + rollup-pluginutils "^2.8.1" + +rollup-plugin-node-polyfills@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz#53092a2744837164d5b8a28812ba5f3ff61109fd" + integrity sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA== + dependencies: + rollup-plugin-inject "^3.0.0" + +rollup-pluginutils@^2.8.1: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + +selfsigned@^2.0.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +stacktracey@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/stacktracey/-/stacktracey-2.1.8.tgz#bf9916020738ce3700d1323b32bd2c91ea71199d" + integrity sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw== + dependencies: + as-table "^1.0.36" + get-source "^2.0.12" + +stoppable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" + integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tslib@^2.2.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +ufo@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754" + integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ== + +undici-types@~6.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +undici@^5.28.4: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + +"unenv@npm:unenv-nightly@2.0.0-20241024-111401-d4156ac": + version "2.0.0-20241024-111401-d4156ac" + resolved "https://registry.yarnpkg.com/unenv-nightly/-/unenv-nightly-2.0.0-20241024-111401-d4156ac.tgz#000835e7383ace38ad31351dc13e623d20b82855" + integrity sha512-xJO1hfY+Te+/XnfCYrCbFbRcgu6XEODND1s5wnVbaBCkuQX7JXF7fHEXPrukFE2j8EOH848P8QN19VO47XN8hw== + dependencies: + defu "^6.1.4" + ohash "^1.1.4" + pathe "^1.1.2" + ufo "^1.5.4" + +workerd@1.20241106.1: + version "1.20241106.1" + resolved "https://registry.yarnpkg.com/workerd/-/workerd-1.20241106.1.tgz#08f3d63f70cd750a1f2c0652cd76c4844fe06409" + integrity sha512-1GdKl0kDw8rrirr/ThcK66Kbl4/jd4h8uHx5g7YHBrnenY5SX1UPuop2cnCzYUxlg55kPjzIqqYslz1muRFgFw== + optionalDependencies: + "@cloudflare/workerd-darwin-64" "1.20241106.1" + "@cloudflare/workerd-darwin-arm64" "1.20241106.1" + "@cloudflare/workerd-linux-64" "1.20241106.1" + "@cloudflare/workerd-linux-arm64" "1.20241106.1" + "@cloudflare/workerd-windows-64" "1.20241106.1" + +wrangler@^3.22.3: + version "3.87.0" + resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-3.87.0.tgz#3dbd47583f6ba74adf6b622fa61f24a345558ee9" + integrity sha512-BExktnSLeGgG+uxgnr4h9eZ5nefdpTVcTHR+gEIWRvqk07XL04nJwpPYAOIPKPpB7E2tMdDJgNLGQN/CY6e1xQ== + dependencies: + "@cloudflare/kv-asset-handler" "0.3.4" + "@cloudflare/workers-shared" "0.7.1" + "@esbuild-plugins/node-globals-polyfill" "^0.2.3" + "@esbuild-plugins/node-modules-polyfill" "^0.2.2" + blake3-wasm "^2.1.5" + chokidar "^4.0.1" + date-fns "^4.1.0" + esbuild "0.17.19" + itty-time "^1.0.6" + miniflare "3.20241106.0" + nanoid "^3.3.3" + path-to-regexp "^6.3.0" + resolve "^1.22.8" + resolve.exports "^2.0.2" + selfsigned "^2.0.1" + source-map "^0.6.1" + unenv "npm:unenv-nightly@2.0.0-20241024-111401-d4156ac" + workerd "1.20241106.1" + xxhash-wasm "^1.0.1" + optionalDependencies: + fsevents "~2.3.2" + +ws@^8.18.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + +xxhash-wasm@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz#ecc0f813219b727af4d5f3958ca6becee2f2f1ff" + integrity sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A== + +youch@^3.2.2: + version "3.3.4" + resolved "https://registry.yarnpkg.com/youch/-/youch-3.3.4.tgz#f13ee0966846c6200e7fb9ece89306d95df5e489" + integrity sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg== + dependencies: + cookie "^0.7.1" + mustache "^4.2.0" + stacktracey "^2.1.8" + +zod@^3.22.3: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== diff --git a/docs~/docs/reference/parameters.md b/docs~/docs/reference/parameters.md index 4bf00acc..8739dbc7 100644 --- a/docs~/docs/reference/parameters.md +++ b/docs~/docs/reference/parameters.md @@ -14,36 +14,50 @@ than VRChat builtins. ## How do I use it? -### Setting defaults +Each entry in the MA Parameters list configures a single parameter, or a prefix used for a VRChat PhysBone. +You set the name (or prefix) in the top row, with the type of parameter next to it. -The parameters UI shows each parameter initially in a collapsed view. This view makes it easy to set the default values -for each parameter, and whether they are saved when you change avatars, or change worlds. Setting a non-blank default -value will override the value in the main Expressions Parameters asset, if any. +### Parameter types -You can set a blank value here as well; this can be useful if multiple MA Parameters components define the same -parameter, where the "default value" field will ignore any MA Parameters component which does not set a default. -Setting default values for the same parameter in multiple Parameters components will result in a warning, as it's -unclear which should be used. +The parameter type field in the upper right can be set to any of the following: -For the "Saved" option, generally speaking the parameter will be saved if any MA Parameters component sets it to be -saved. However, there is an exception; see the section on "Nesting" for more information. Note also that the "Saved" -setting will be enabled if either MA Parameters or the original Expression Parameters asset enables saving for the -parameter. +* Bool +* Int +* Float +* Animator Only +* PB Prefix -Normally, the default values you set will only affect the Expressions Parameters settings for your avatar. However, -you can override the default values for the _animator_ itself by either setting the "Parameter Type" to "Animator Only", -or by enabling the "Override Animator Defaults" checkbox (note that this is ignored for animator only parameters). +If you select Animator Only, the parameter will not be added to the Expressions Parameters list. However, it will still +be able to rename the parameter in question, as described below. + +The PB Prefix setting is used when this parameter prefix is set in a PhysBones component. As with Animator Only, this will +not be added to the Expressions Parameters list. ### Renaming parameters -By setting the "Change name to" field you can _rename_ a parameter. That is, if you have a parameter "foo", which has -a "Change name to" setting of "bar", then on the object with the MA Parameters component and its children you can -refer to this parameter as "foo" (eg in [MA Menu Item](menu-item.md), [MA Merge Animator](merge-animator.md), or in -VRChat Contact Receivers); however, outside of this object, the parameter will be referred to as "bar". +If you enter a name in the "Change name to" field, the parameter will be renamed to that name for anything _outside_ +of the MA Parameters object and its children. This can be useful for avoiding conflicts between different gimmicks, +or conversely, deliberately connecting two different gimmicks by making them use the same parameter. -By renaming parameters on multiple components to use the same name, you can wire up one gimmick to trigger another. -You can also select "Auto Rename" to have Modular Avatar automatically select an unused name; this will help avoid -accidental name collisions between different gimmicks. +You can also click the "Auto rename" box to have Modular Avatar automatically select an unused name for you. + +### Default values + +You can set a default value for each parameter. This value will be used when you avatar is reset. If you leave the +default box blank, then the value (if any) in the main Expressions Parameters asset will be used, or otherwise zero (or +false) will be used. + +If you click the "Override Animator Defaults" box, then any default values specified in the _animator controller_ of +your asset will be changed to this default. This is occasionally useful with particularly complex gimmicks. If you +selected "Animator Only" and specified a default value, then this box will be ignored, and the animator controller +default will always be replaced. + +### Saved/Synced + +The Saved box controls whether the parameter will be saved across avatar changes and restarting VRChat. + +The Synced box controls whether the parameter will be synced across the network. If you clear this box, this parameter +won't use your limited parameter space. ### Creating new parameters @@ -58,18 +72,6 @@ Either way, after creating the parameter, click the chevron next to the new para There, you can set the parameter type (which controls whether the parameter is synced), and other attributes of the parameter. -### PhysBones and Contacts - -MA Parameters can rename parameters used by VRChat PhysBone components and Contact Receivers. For PhysBones, enter the -prefix (the value in the "Parameter" field of the VRC Phys Bone component) as the parameter name, and click the -"Is PhysBone Prefix" option. This will be automatically set if you add it via the "Unregistered Parameters" section. - -### Expression Parameters - -Set the "Parameter type" field to register the parameter in the VRC Expressions Parameters list. This will sync the -parameter over the network, and allow for it to be used in the expressions menu and in OSC. If you don't need the -parameter to be synced, click "Local Only". - ### Nesting MA Parameters components can be nested. This lets you build up a complex system out of multiple subcomponents. Each diff --git a/docs~/docs/reference/parameters.png b/docs~/docs/reference/parameters.png index 10158b46..f4fac55a 100644 Binary files a/docs~/docs/reference/parameters.png and b/docs~/docs/reference/parameters.png differ diff --git a/docs~/docs/reference/reaction/debugger/index.md b/docs~/docs/reference/reaction/debugger/index.md index 6cbfbf47..2eae1191 100644 --- a/docs~/docs/reference/reaction/debugger/index.md +++ b/docs~/docs/reference/reaction/debugger/index.md @@ -1,5 +1,4 @@ - ---- +--- sidebar_position: 900 --- diff --git a/docs~/docs/reference/remove-vertex-color-after.png b/docs~/docs/reference/remove-vertex-color-after.png new file mode 100644 index 00000000..957e1b65 Binary files /dev/null and b/docs~/docs/reference/remove-vertex-color-after.png differ diff --git a/docs~/docs/reference/remove-vertex-color-before.png b/docs~/docs/reference/remove-vertex-color-before.png new file mode 100644 index 00000000..f0e0c1c8 Binary files /dev/null and b/docs~/docs/reference/remove-vertex-color-before.png differ diff --git a/docs~/docs/reference/remove-vertex-color.md b/docs~/docs/reference/remove-vertex-color.md new file mode 100644 index 00000000..5724c9e2 --- /dev/null +++ b/docs~/docs/reference/remove-vertex-color.md @@ -0,0 +1,34 @@ +# Remove Vertex Color + +![Remove Vertex Color](remove-vertex-color.png) + +The Remove Vertex Color component removes vertex colors from the object it is attached to and its children. + +## When should I use it? + +Sometimes, models come with vertex colors that aren't intended for display. When changing to a shader that +makes use of vertex colors, such as the VRChat mobile shaders, this can result in undesired discoloration. You can use +this component to remove these vertex colors nondestructively. + +
+
+
+ ![With unwanted vertex colors](remove-vertex-color-before.png) +
+ *Without Remove Vertex Color, some unwanted vertex colors discolor this avatar's hair.* +
+
+
+ ![After removing vertex colors](remove-vertex-color-after.png) +
+ *After adding Remove Vertex Color, the avatar's hair is the correct color.* +
+
+ +## Detailed usage + +Simply attach the Remove Vertex Color component to an object in your avatar - often, you can just add it to the root +object. All objects below that object in the hierarchy will have their vertex colors removed. + +If you want to exclude some objects, add a Remove Vertex Color component to the object you want to exclude and set +the mode to "Keep Vertex Colors". Any objects below this object will not have their vertex colors removed. \ No newline at end of file diff --git a/docs~/docs/reference/remove-vertex-color.png b/docs~/docs/reference/remove-vertex-color.png new file mode 100644 index 00000000..0b503932 Binary files /dev/null and b/docs~/docs/reference/remove-vertex-color.png differ diff --git a/docs~/docs/reference/sync-parameter-sequence.md b/docs~/docs/reference/sync-parameter-sequence.md new file mode 100644 index 00000000..1505195f --- /dev/null +++ b/docs~/docs/reference/sync-parameter-sequence.md @@ -0,0 +1,38 @@ +# Sync Parameter Sequence + +![Sync Parameter Sequence](sync-parameter-sequence.png) + +On VRChat, it's necessary for parameters that are shared between different-platforms of an avatar (e.g. PC and Android) +to appear at the start of the expressions parameters list, and in the same order. This component adjusts the order of +your expressions parameters, and adds additional parameters where necessary, to ensure that your avatar syncs properly +between PC and Android. + +## When should I use it? + +You should use this component if you are uploading different versions of the same avatar to PC and Android, and both +versions make use of synced expressions parameters. + +## When shouldn't I use it? + +This component may have compatibility issues with certain VRCFury components, such as Parameter Compressor. + +## How should I use it? + +First, attach the Sync Parameter Sequence component to any object on your avatar. Then, click the New button to create +an asset to save the parameter sequence. On other platform variants of your avatar, attach the component, and select the +asset you just created. Upload on Android (or whichever platform you want to be the primary platform), then upload for +other platforms as well. + +Whenever you upload your avatar on the platform listed as "Primary Platform", Modular Avatar will record its expression +parameters in this asset. Then, later, when you upload on some other platform, Modular Avatar will adjust the order of +the parameters to match the primary platform. + +## Parameter limits + +The Sync Parameter Sequence component will add additional parameters to your avatar if necessary to ensure that the +order of parameters matches between platforms. This may cause your avatar to exceed the maximum number of parameters, +in which case the build will fail. + +To address this, you can clear the contents of the parameters asset to clear out obsolete parameters; otherwise, make +sure you don't have a lot of both android-only and PC-only parameters, because you'll end up using the combination of +both. \ No newline at end of file diff --git a/docs~/docs/reference/sync-parameter-sequence.png b/docs~/docs/reference/sync-parameter-sequence.png new file mode 100644 index 00000000..0bc7c83f Binary files /dev/null and b/docs~/docs/reference/sync-parameter-sequence.png differ diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/mesh-settings.md b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/mesh-settings.md index 12459d0f..8f8677ec 100644 --- a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/mesh-settings.md +++ b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/mesh-settings.md @@ -28,10 +28,10 @@ Mesh Settingsコンポーネントを使用すると、特定のゲームオブ - 継承:このコンポーネントはこの設定に対して何もしません。親のMesh Settingsで設定された値を継承します。 - 設定:このコンポーネントは、そのゲームオブジェクトとその子にあるメッシュの対応する設定を設定します。 - 設定しない:このコンポーネントは、親のMesh Settingsの影響を受けないようにします。メッシュはデフォルトの設定のままです。 -- 親が継承された時は継承、または設定:親のMesh Settingsが設定モードにある場合、それが使用されます。親のMesh +- 親で指定されている時は継承、それ以外では設定:親のMesh Settingsが設定モードにある場合、それが使用されます。親のMesh Settingsが適用されない場合、 このコンポーネントの設定が使用されます。衣装プレハブなどに、アバター全体の設定が優先されるようにするために便利です。 バウンズを設定する場合、バウンディングボックスは「Root Bone」として指定したトランスフォームに対して相対的に決定されます。 また、バウンズはSkinned Mesh Rendererのみに影響しますが、Anchor OverrideはMesh RendererやLine Rendererなどの他のタイプの -Rendererにも設定されます。 \ No newline at end of file +Rendererにも設定されます。 diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/parameters.md b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/parameters.md index 9ac72165..5380ae62 100644 --- a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/parameters.md +++ b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/parameters.md @@ -12,32 +12,45 @@ VRChatが提供するパラメーター以外の、自分で定義するパラ ## 使い方 -### 初期値を設定する +MA Parametersリストの各エントリーは、一つのパラメーター、またはVRChatのPhysBoneの前置詞を指定します。上段に名前(または前置詞)を、 +その横にパラメーターの型を設定します。 -ParametersのUIは最初はたたんだ状態でパラメーター情報を表示します。この表示では初期値と、アバター変更やワールド移動でで保存されるかを -設定できます。空以外の設定だと、Expressions Parametersに設定された初期値も上書きします。 +### パラメーター型 -複数のMA Parametersで同じパラメーターが設定された場合は、初期値を空欄にするのが便利です。設定されたところだけ初期値に関与します。 -なお、複数のMA Parametersコンポーネントで同じパラメーターの初期値を設定してしまうと初期値が不定になるため注意がでます。 +右上のパラメーター型欄には、以下のいずれかを設定できます。 -「保存する」設定に関しては、どれか一つのMA Parametersで有効になっている場合は保存状態となります。しかし、例外もあります。詳しくは -「入れ子にする」に参照。なお、Expressions ParametersとMA Parameters両方に定義される場合は、どちらか(あるいは両方)に「保存する」が -有効になっていると、保存される扱いとなります。 +* Bool +* Int +* Float +* Animatorのみ +* PB前置詞 -普段は設定した初期値はアバターのExpressions Parameters設定のみに適用されます。ただし、「パラメーター型」を「Animatorのみ」に設定するか、 -「アニメーターの初期値を設定」を有効にすることで、アニメーター自体の初期値を上書きすることができます。なお、「アニメーターの初期値を設定」は -「アニメーターのみ」の場合無視されます。 +「Animatorのみ」を選択すると、そのパラメーターはExpressions Parametersリストに追加されません。ただし、下記の名前変更はできます。 -### 名前を変更 +「PB前置詞」設定は、このパラメーター前置詞がPhysBonesコンポー年tおに設定されている場合に使います。Animatorのみと同様、Expressions Parameters +リストには追加されません。 -「名前を変更」欄に新しい名前を入れると、パラメーターのリネームができます。たとえば、「hoge」という名のパラメーターに、「piyo」という -「名前を変更」設定を入れると、MA Parametersコンポーネントのあるオブジェクトとその子では「hoge」として参照できます。その中の -[MA Menu Item](menu-item.md)、[MA Merge Animator](merge-animator.md)、VRChat Contact Receiverなどは「hoge」で扱うわけです。 -しかし、そのオブジェクトの外では、「piyo」として扱われます。 +### パラメーターのリネーム -複数のMA Parametersで名前がかぶるように設定すると、一つのギミックで別のギミックを制御するように連動させることができたりします。 -また、「自動リネーム」を設定すれば、Modular Avatarに自動的に衝突しない名前を選択してもらえます。これで意図してない名前の衝突を避ける -ことができます。 +「名前を変更」欄に新しい名前を入れると、そのパラメーターはMA Parametersオブジェクトとその子の外ではその名前として扱われます。これは、 +異なるギミック間の衝突を避けるために使ったり、逆に同じパラメーターを使って二つのギミックを連動させるために使うことができます。 + +「自動リネーム」をクリックすると、使われていない名前が自動的に選択されます。 + +### 初期値 + +各パラメーターには初期値を設定できます。この値はアバターをリセットしたときに使われます。初期値を空欄にすると、アバターのExpressions Parameters +アセットに設定された値が使われます。それもない場合は、0(またはfalse)が使われます。 + +「アニメーターでの初期値を設定」を有効にすると、アニメーターコントローラーの初期値を上書きします。一部複雑なギミックを作る際に便利です。 +「Animatorのみ」を選択して初期値を設定した場合は、この設定は無視され、常にアニメーターコントローラーの初期値が上書きされます。 + +### 保存・同期 + +「保存する」設定は、パラメーターがアバター変更やVRChat再起動時に保存されるかを設定します。 + +「同期する」設定は、パラメーターがネットワーク経由で同期されるかを設定します。この設定をクリアすると、パラメーターはネットワーク上での +パラメーター数を消費しません。 ### パラメーターの新規作成 @@ -49,17 +62,6 @@ ParametersのUIは最初はたたんだ状態でパラメーター情報を表 どのみち、パラメーターを作ったら▶を押せば詳細画面を開けます。そこで同期状態をつかさどるパラメーター型を設定したり、その他詳細設定ができます。 -### PhysBoneやContact - -MA ParametersはVRC Phys BoneやContact Receiverコンポーネントにも対応しています。PhysBonesの場合は、VRC Phys Boneコンポーネント -の「Parameter」欄にも登録されている、前置詞をパラメーター名にしてから、「PhysBone前置詞名」を有効にしてください。なお、 -「未登録パラメーター」のリストから追加する場合は自動的に設定されます。 - -### Expression Parameters - -VRC Expression Parametersに登録する場合は、「パラメーター型」を設定してください。ネットワーク経由で同期されるようになり、 -Expressions Menu(アクションメニュー)やOSCで使えるようになります。ネットワーク同期が不要の場合は「Local Only」を設定してくささい。 - ### 入れ子にする MA Parametersコンポーネントは入れ子状態にできます。これで複雑なシステムを、複数のモジュールから組み上げることができます。 diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/parameters.png b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/parameters.png index 4aac53d8..e10e7886 100644 Binary files a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/parameters.png and b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/parameters.png differ diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color-after.png b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color-after.png new file mode 100644 index 00000000..957e1b65 Binary files /dev/null and b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color-after.png differ diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color-before.png b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color-before.png new file mode 100644 index 00000000..f0e0c1c8 Binary files /dev/null and b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color-before.png differ diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color.md b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color.md new file mode 100644 index 00000000..3320746b --- /dev/null +++ b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color.md @@ -0,0 +1,33 @@ +# Remove Vertex Color + +![Remove Vertex Color](remove-vertex-color.png) + +Remove Vertex Color コンポーネントは、アタッチされたオブジェクトとその子オブジェクトから頂点カラーを削除します。 + +## いつ使うものか? + +時々、アバターや衣装には意図されていない頂点カラーが付いていることがあります。VRChat Mobile系統など頂点カラーを使用するシェーダーに変更すると、 +変色してしまうことがあります。このコンポーネントを使えば、非破壊的に問題の頂点カラーを削除できます。 + +
+
+
+ ![不要な頂点カラーがある場合](remove-vertex-color-before.png) +
+ *Remove Vertex Color を使わないと、このアバターの髪の毛に不要な頂点カラーで変色してしまいます。* +
+
+
+ ![頂点カラーを削除した後](remove-vertex-color-after.png) +
+ *Remove Vertex Color を追加した後、アバターの髪の色が正しくなります。* +
+
+ +## 詳細な使い方 + +Remove Vertex Color コンポーネントをアバターのオブジェクトに追加してください。通常、ルートオブジェクトに追加するだけで十分です。 +このオブジェクト以下のすべてのオブジェクトの頂点カラーが削除されます。 + +特定のオブジェクトを除外したい場合は、除外したいオブジェクトに Remove Vertex Color コンポーネントを追加し、モードを「頂点カラーを削除しない」 +に設定してください。このオブジェクト以下のオブジェクトの頂点カラーは削除されません。 diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color.png b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color.png new file mode 100644 index 00000000..486671f6 Binary files /dev/null and b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/remove-vertex-color.png differ diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/sync-parameter-sequence.md b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/sync-parameter-sequence.md new file mode 100644 index 00000000..11e7ffee --- /dev/null +++ b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/sync-parameter-sequence.md @@ -0,0 +1,32 @@ +# Sync Parameter Sequence + +![Sync Parameter Sequence](sync-parameter-sequence.png) + +VRChatでは、異なるプラットフォーム間で共有されるパラメータ(例:PCとAndroid)が、パラメータリストの先頭に、同じ順序で登録してある必要が +あります。このコンポーネントは、アバターがPCとAndroid間で正しく同期するために、表情パラメータの順序を調整し、必要に応じてパラメータを追加します。 + +## いつ使うべきか? + +同じアバターの異なるバージョンをPCとAndroidにアップロードし、両方のバージョンが同期パラメータを使用する場合、このコンポーネントを使用すると +便利です。 + +## 使わない方がいい場合 + +このコンポーネントは、Parameter Compressorなど一部のVRCFuryコンポーネントと互換性問題がある可能性があります。 + +## 使い方 + +まず、Sync Parameter Sequenceコンポーネントをアバターの任意のオブジェクトに追加します。次に、「新規作成」ボタンをクリックして、パラメータ +順序を保存するアセットを作成します。アバターの他のプラットフォーム用のバージョンに同じくコンポーネントを追加し、作成したアセットをセットします。 +Android(あるいは主要プラットホームで選択したプラットフォーム)でアップロードし、そのあとに他のプラットフォームにもアップロードします。 + +「主要プラットフォーム」として設定されたプラットフォームでアバターをアップロードするたびに、Modular Avatarはパラメータリストをアセットに +記録します。その後、他のプラットフォームにアップロードする際に、Modular Avatarはパラメータの順序を主要プラットフォームに合わせて調整します。 + +## パラメータ制限について + +Sync Parameter Sequenceコンポーネントは、パラメータの順序が一致するように必要に応じてアバターにパラメータを追加します。これにより、 +アバターがパラメータの最大数を超える可能性があり、ビルドが失敗することがあります。 + +解決するために、パラメータアセットの内容をクリアして、不要なパラメータを削除するか、Android専用のパラメータとPC専用のパラメータの +両方を多く持っていないことを確認してください。そうしないと、両方の組み合わせを登録して限界を超えることになります。 \ No newline at end of file diff --git a/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/sync-parameter-sequence.png b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/sync-parameter-sequence.png new file mode 100644 index 00000000..5b770059 Binary files /dev/null and b/docs~/i18n/ja/docusaurus-plugin-content-docs/current/reference/sync-parameter-sequence.png differ diff --git a/docs~/package.json b/docs~/package.json index 693afbf2..2bb1795e 100644 --- a/docs~/package.json +++ b/docs~/package.json @@ -17,22 +17,22 @@ "dependencies": { "@docusaurus/core": "latest", "@docusaurus/preset-classic": "latest", - "@mdx-js/react": "^3.0.1", - "clsx": "^2.1.0", - "prism-react-renderer": "^2.3.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "@mdx-js/react": "^3.1.0", + "clsx": "^2.1.1", + "prism-react-renderer": "^2.4.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-loadable": "^5.5.0", - "react-medium-image-zoom": "^5.1.10", + "react-medium-image-zoom": "^5.2.11", "react-modal": "^3.16.1", - "react-player": "^2.14.1", - "search-insights": "^2.13.0" + "react-player": "^2.16.0", + "search-insights": "^2.17.2" }, "devDependencies": { "@docusaurus/module-type-aliases": "latest", - "@tsconfig/docusaurus": "^2.0.2", - "@types/react": "^18.2.59", - "typescript": "^5.3.3" + "@tsconfig/docusaurus": "^2.0.3", + "@types/react": "^18.3.12", + "typescript": "^5.6.3" }, "resolutions": { "@algolia/client-search": "4.20.0", diff --git a/docs~/src/pages/index.module.css b/docs~/src/pages/index.module.css index a8166690..9ef0a6aa 100644 --- a/docs~/src/pages/index.module.css +++ b/docs~/src/pages/index.module.css @@ -23,6 +23,7 @@ justify-content: center; column-gap: 2rem; row-gap: 1rem; + margin-bottom: 1em; } @media screen and (max-width: 996px) { @@ -36,4 +37,4 @@ div.logo { text-align: center; display: flex; justify-content: center; -} \ No newline at end of file +} diff --git a/docs~/src/pages/index.tsx b/docs~/src/pages/index.tsx index 88796298..889f8ced 100644 --- a/docs~/src/pages/index.tsx +++ b/docs~/src/pages/index.tsx @@ -29,7 +29,7 @@ function HomepageHeader() {

Drag-and-Drop Avatar Assembly

-
+
Tutorials
-
+
Discord=16" + react: ">=16" + checksum: c5a9c495f43f498ece24a768762a1743abe2be33d050d7eab731beb754e631700547f039198c6262c998d9a443906bd78811c3fa38bc2fb37659848161dac331 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -2724,7 +2736,7 @@ __metadata: languageName: node linkType: hard -"@tsconfig/docusaurus@npm:^2.0.2": +"@tsconfig/docusaurus@npm:^2.0.3": version: 2.0.3 resolution: "@tsconfig/docusaurus@npm:2.0.3" checksum: d8245a64bf131daa0c287649cb9c37ad8fecb2aedea18e15047e0df6463916e5012af3755bd73ecf90999ace1f14a3748a8b3e041d5158e2a85c22a539c649c9 @@ -3034,7 +3046,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:^18.2.59": +"@types/react@npm:*": version: 18.3.3 resolution: "@types/react@npm:18.3.3" dependencies: @@ -3044,6 +3056,16 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^18.3.12": + version: 18.3.12 + resolution: "@types/react@npm:18.3.12" + dependencies: + "@types/prop-types": "*" + csstype: ^3.0.2 + checksum: 4ab1577a8c2105a5e316536f724117c90eee5f4bd5c137fc82a2253d8c1fd299dedaa07e8dfc95d6e2f04a4be3cb8b0e1b06098c6233ebd55c508d88099395b7 + languageName: node + linkType: hard + "@types/retry@npm:0.12.0": version: 0.12.0 resolution: "@types/retry@npm:0.12.0" @@ -4134,7 +4156,7 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^2.0.0, clsx@npm:^2.1.0": +"clsx@npm:^2.0.0, clsx@npm:^2.1.1": version: 2.1.1 resolution: "clsx@npm:2.1.1" checksum: acd3e1ab9d8a433ecb3cc2f6a05ab95fe50b4a3cfc5ba47abb6cbf3754585fcb87b84e90c822a1f256c4198e3b41c7f6c391577ffc8678ad587fc0976b24fd57 @@ -4446,13 +4468,13 @@ __metadata: linkType: hard "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3": - version: 7.0.3 - resolution: "cross-spawn@npm:7.0.3" + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" dependencies: path-key: ^3.1.0 shebang-command: ^2.0.0 which: ^2.0.1 - checksum: 671cc7c7288c3a8406f3c69a3ae2fc85555c04169e9d611def9a675635472614f1c0ed0ef80955d5b6d4e724f6ced67f0ad1bb006c2ea643488fcfef994d7f52 + checksum: 8d306efacaf6f3f60e0224c287664093fa9185680b2d195852ba9a863f85d02dcc737094c6e512175f8ee0161f9b87c73c6826034c2422e39de7d6569cf4503b languageName: node linkType: hard @@ -6378,8 +6400,8 @@ __metadata: linkType: hard "http-proxy-middleware@npm:^2.0.3": - version: 2.0.6 - resolution: "http-proxy-middleware@npm:2.0.6" + version: 2.0.7 + resolution: "http-proxy-middleware@npm:2.0.7" dependencies: "@types/http-proxy": ^1.17.8 http-proxy: ^1.18.1 @@ -6391,7 +6413,7 @@ __metadata: peerDependenciesMeta: "@types/express": optional: true - checksum: 2ee85bc878afa6cbf34491e972ece0f5be0a3e5c98a60850cf40d2a9a5356e1fc57aab6cff33c1fc37691b0121c3a42602d2b1956c52577e87a5b77b62ae1c3a + checksum: 18caa21145917aa1054740353916e8f03f5a3a93bede9106f1f44d84f7b174df17af1c72bf5fade5cc440c2058ee813f47cbb2bdd6ae6874af1cf33e0ac575f3 languageName: node linkType: hard @@ -8335,19 +8357,19 @@ __metadata: "@docusaurus/core": latest "@docusaurus/module-type-aliases": latest "@docusaurus/preset-classic": latest - "@mdx-js/react": ^3.0.1 - "@tsconfig/docusaurus": ^2.0.2 - "@types/react": ^18.2.59 - clsx: ^2.1.0 - prism-react-renderer: ^2.3.1 - react: ^18.2.0 - react-dom: ^18.2.0 + "@mdx-js/react": ^3.1.0 + "@tsconfig/docusaurus": ^2.0.3 + "@types/react": ^18.3.12 + clsx: ^2.1.1 + prism-react-renderer: ^2.4.0 + react: ^18.3.1 + react-dom: ^18.3.1 react-loadable: ^5.5.0 - react-medium-image-zoom: ^5.1.10 + react-medium-image-zoom: ^5.2.11 react-modal: ^3.16.1 - react-player: ^2.14.1 - search-insights: ^2.13.0 - typescript: ^5.3.3 + react-player: ^2.16.0 + search-insights: ^2.17.2 + typescript: ^5.6.3 languageName: unknown linkType: soft @@ -9410,7 +9432,7 @@ __metadata: languageName: node linkType: hard -"prism-react-renderer@npm:^2.3.0, prism-react-renderer@npm:^2.3.1": +"prism-react-renderer@npm:^2.3.0": version: 2.3.1 resolution: "prism-react-renderer@npm:2.3.1" dependencies: @@ -9422,6 +9444,18 @@ __metadata: languageName: node linkType: hard +"prism-react-renderer@npm:^2.4.0": + version: 2.4.0 + resolution: "prism-react-renderer@npm:2.4.0" + dependencies: + "@types/prismjs": ^1.26.0 + clsx: ^2.0.0 + peerDependencies: + react: ">=16.0.0" + checksum: d15d944a8cbf05f7b04deecd2cf4ffb08229a6027918641b6f046cd8ab24b65ca4ebe4ac8e95a53a7d7cefb1bba8df3ce394fe4f1d75418e34fa92553dc63ef7 + languageName: node + linkType: hard + "prismjs@npm:^1.29.0": version: 1.29.0 resolution: "prismjs@npm:1.29.0" @@ -9641,7 +9675,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.2.0": +"react-dom@npm:^18.3.1": version: 18.3.1 resolution: "react-dom@npm:18.3.1" dependencies: @@ -9753,13 +9787,13 @@ __metadata: languageName: node linkType: hard -"react-medium-image-zoom@npm:^5.1.10": - version: 5.2.5 - resolution: "react-medium-image-zoom@npm:5.2.5" +"react-medium-image-zoom@npm:^5.2.11": + version: 5.2.11 + resolution: "react-medium-image-zoom@npm:5.2.11" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 878c943f41851ea436fb7a7e99afbcbb144c3f70749e0627c53b0e704e00675e7bd2707942274c5cd1e76b5018b5f0ae390bd7eeda9bd4424e5eff1ac7ec749a + checksum: 6e4326f15fd233c5e8c4e73fd9e910abacc78b8e00f0b9d6c51ba646c857c14e93cfd150440964bfdfb165675d8d3ee26ca78c9d907cd7fe8f2d9762f961f774 languageName: node linkType: hard @@ -9778,7 +9812,7 @@ __metadata: languageName: node linkType: hard -"react-player@npm:^2.14.1": +"react-player@npm:^2.16.0": version: 2.16.0 resolution: "react-player@npm:2.16.0" dependencies: @@ -9841,7 +9875,7 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.2.0": +"react@npm:^18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" dependencies: @@ -10323,10 +10357,10 @@ __metadata: languageName: node linkType: hard -"search-insights@npm:^2.13.0": - version: 2.14.0 - resolution: "search-insights@npm:2.14.0" - checksum: 26f3524633ee1b31cd5dea9f8e7e67c102a9e90f24efc5ae84020b6051cfc4815b38d6afdee88d278ed3ee3f01acb87dd5c80b8e8b2f4bad15d7a68e48b36681 +"search-insights@npm:^2.17.2": + version: 2.17.2 + resolution: "search-insights@npm:2.17.2" + checksum: cd4751fe7735a823f2742dda29bbbc40839ff394764887009fdbc7661c1b0eb74ff84d01c5d753654e560fb0271cd2e7828bc07d3fee568e853368b133deff5a languageName: node linkType: hard @@ -11161,23 +11195,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.3.3": - version: 5.5.2 - resolution: "typescript@npm:5.5.2" +"typescript@npm:^5.6.3": + version: 5.6.3 + resolution: "typescript@npm:5.6.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 9c5a7982dadcb2d38d129c575dd38645ae11588ae0d4a12852fb04482bbc5a1660b2371e48fd5b33b6b605cc57cefe777670054546856945a05e77bd22c8c2cd + checksum: ba302f8822777ebefb28b554105f3e074466b671e7444ec6b75dadc008a62f46f373d9e57ceced1c433756d06c8b7dc569a7eefdf3a9573122a49205ff99021a languageName: node linkType: hard -"typescript@patch:typescript@^5.3.3#~builtin": - version: 5.5.2 - resolution: "typescript@patch:typescript@npm%3A5.5.2#~builtin::version=5.5.2&hash=1f5320" +"typescript@patch:typescript@^5.6.3#~builtin": + version: 5.6.3 + resolution: "typescript@patch:typescript@npm%3A5.6.3#~builtin::version=5.6.3&hash=1f5320" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 9d89bac0de650e15d6846485f238d1e65f1013f2c260d9e53e86a1da6ecf8109d9fad9402575c5c36a6592dc5d4370db090e12971c8630ae84453654baabb6b4 + checksum: ade87bce2363ee963eed0e4ca8a312ea02c81873ebd53609bc3f6dc0a57f6e61ad7e3fb8cbb7f7ab8b5081cbee801b023f7c4823ee70b1c447eae050e6c7622b languageName: node linkType: hard diff --git a/package.json b/package.json index c84e0cd9..1b9354e3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nadena.dev.modular-avatar", "displayName": "Modular Avatar", - "version": "1.10.0", + "version": "1.10.11", "unity": "2022.3", "description": "A suite of tools for assembling your avatar out of reusable components", "author": { @@ -15,7 +15,7 @@ "com.unity.nuget.newtonsoft-json": "2.0.0" }, "vpmDependencies": { - "com.vrchat.avatars": ">=3.7.0", - "nadena.dev.ndmf": ">=1.5.0 <2.0.0-a" + "com.vrchat.avatars": ">=3.7.4", + "nadena.dev.ndmf": ">=1.6.0 <2.0.0-a" } }