From 99386fc756e5f6eecb0bf92f9cacb28982795c75 Mon Sep 17 00:00:00 2001 From: bd_ Date: Sat, 5 Aug 2023 15:47:03 +0900 Subject: [PATCH] feat: NDMF integration --- .gitmodules | 3 + .../EditModeTests/BoneProxyTest.cs | 6 +- .../EditModeTests/ComponentSettingsTest.cs | 6 +- .../EditModeTests/DuplicateObjectNameTest.cs | 2 + .../MergeArmatureTests/MultiLevelMergeTest.cs | 18 +- .../EditModeTests/PathMappingTest.cs | 69 ---- .../EditModeTests/PathMappingTest.cs.meta | 11 - .../ReplaceObject/ReplaceObjectTests.cs | 10 +- .../EditModeTests/RetargetMeshesTest.cs | 33 +- .../EditModeTests/SerializationSweepTest.cs | 56 --- .../SerializationSweepTest.cs.meta | 3 - .../SerializationTests/SerializationTests.cs | 3 +- .../_ModularAvatar/EditModeTests/Tests.asmdef | 4 +- .../Editor/ApplyOnPlay.cs | 81 ----- .../Editor/ApplyOnPlay.cs.meta | 3 - .../Editor/AvatarProcessor.cs | 338 +----------------- .../Editor/BuildContext.cs | 111 +----- .../Editor/Menu/ClonedMenuMappings.cs | 51 --- .../Editor/Menu/ClonedMenuMappings.cs.meta | 3 - .../Editor/MergeArmatureHook.cs | 21 +- .../Editor/MeshRetargeter.cs | 62 ++-- .../Editor/PathMappings.cs | 203 ----------- .../Editor/PathMappings.cs.meta | 3 - .../Editor/PluginDefinition.meta | 3 + .../PluginDefinition/ModularAvatarContext.cs | 28 ++ .../ModularAvatarContext.cs.meta | 3 + .../PluginDefinition/PluginDefinition.cs | 206 +++++++++++ .../PluginDefinition/PluginDefinition.cs.meta | 3 + .../Editor/RemapAnimationPass.cs | 77 ---- .../Editor/RemapAnimationPass.cs.meta | 3 - .../Editor/ReplaceObjectPass.cs | 8 +- .../nadena.dev.modular-avatar/Editor/Util.cs | 12 +- ...dena.dev.modular-avatar.core.editor.asmdef | 11 +- .../Runtime/Activator.cs | 105 +----- .../Runtime/AvatarObjectReference.cs | 9 +- .../Runtime/AvatarTagComponent.cs | 21 +- .../Runtime/Menu/ModularAvatarMenuGroup.cs | 5 + .../Menu/ModularAvatarMenuInstaller.cs | 5 + .../Runtime/ModularAvatarBlendshapeSync.cs | 5 + .../Runtime/ModularAvatarBoneProxy.cs | 27 +- .../Runtime/ModularAvatarMenuInstallTarget.cs | 5 + .../Runtime/ModularAvatarMenuItem.cs | 5 + .../Runtime/ModularAvatarMergeAnimator.cs | 13 +- .../Runtime/ModularAvatarMergeArmature.cs | 13 +- .../Runtime/ModularAvatarMeshSettings.cs | 6 + .../Runtime/ModularAvatarPBBlocker.cs | 12 +- .../Runtime/ModularAvatarParameters.cs | 5 + .../Runtime/ModularAvatarReplaceObject.cs | 5 + .../ModularAvatarVisibleHeadAccessory.cs | 4 + .../nadena.dev.modular-avatar/package.json | 3 +- Packages/nadena.dev.ndmf | 1 + Packages/packages-lock.json | 6 + 52 files changed, 512 insertions(+), 1197 deletions(-) create mode 100644 .gitmodules delete mode 100644 Assets/_ModularAvatar/EditModeTests/PathMappingTest.cs delete mode 100644 Assets/_ModularAvatar/EditModeTests/PathMappingTest.cs.meta delete mode 100644 Assets/_ModularAvatar/EditModeTests/SerializationSweepTest.cs delete mode 100644 Assets/_ModularAvatar/EditModeTests/SerializationSweepTest.cs.meta delete mode 100644 Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs delete mode 100644 Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs.meta delete mode 100644 Packages/nadena.dev.modular-avatar/Editor/Menu/ClonedMenuMappings.cs delete mode 100644 Packages/nadena.dev.modular-avatar/Editor/Menu/ClonedMenuMappings.cs.meta delete mode 100644 Packages/nadena.dev.modular-avatar/Editor/PathMappings.cs delete mode 100644 Packages/nadena.dev.modular-avatar/Editor/PathMappings.cs.meta create mode 100644 Packages/nadena.dev.modular-avatar/Editor/PluginDefinition.meta create mode 100644 Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/ModularAvatarContext.cs create mode 100644 Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/ModularAvatarContext.cs.meta create mode 100644 Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs create mode 100644 Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs.meta delete mode 100644 Packages/nadena.dev.modular-avatar/Editor/RemapAnimationPass.cs delete mode 100644 Packages/nadena.dev.modular-avatar/Editor/RemapAnimationPass.cs.meta create mode 160000 Packages/nadena.dev.ndmf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..b4023be2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Packages/nadena.dev.ndmf"] + path = Packages/nadena.dev.ndmf + url = https://github.com/bdunderscore/ndmf.git diff --git a/Assets/_ModularAvatar/EditModeTests/BoneProxyTest.cs b/Assets/_ModularAvatar/EditModeTests/BoneProxyTest.cs index e5948737..db6f1947 100644 --- a/Assets/_ModularAvatar/EditModeTests/BoneProxyTest.cs +++ b/Assets/_ModularAvatar/EditModeTests/BoneProxyTest.cs @@ -29,15 +29,15 @@ namespace modular_avatar_tests var boneProxy = reference.AddComponent(); boneProxy.target = root.transform; - boneProxy.ClearCache(); + boneProxy.ClearCache(true); Assert.AreEqual(root.transform, boneProxy.target); boneProxy.target = target.transform; - boneProxy.ClearCache(); + boneProxy.ClearCache(true); Assert.AreEqual(target.transform, boneProxy.target); target.name = "target2"; - boneProxy.ClearCache(); + boneProxy.ClearCache(true); Assert.IsNull(boneProxy.target); } diff --git a/Assets/_ModularAvatar/EditModeTests/ComponentSettingsTest.cs b/Assets/_ModularAvatar/EditModeTests/ComponentSettingsTest.cs index 85d400ee..a8f37abb 100644 --- a/Assets/_ModularAvatar/EditModeTests/ComponentSettingsTest.cs +++ b/Assets/_ModularAvatar/EditModeTests/ComponentSettingsTest.cs @@ -16,7 +16,7 @@ namespace modular_avatar_tests Texture2D _iconTexture; [SetUp] - public virtual void Setup() + public override void Setup() { base.Setup(); _gameObject = new GameObject(); @@ -40,10 +40,10 @@ namespace modular_avatar_tests if (type == typeof(TestComponent)) return; // get icon - var component = (MonoBehaviour)_gameObject.AddComponent(type); + var component = (MonoBehaviour) _gameObject.AddComponent(type); var monoScript = MonoScript.FromMonoBehaviour(component); var scriptPath = AssetDatabase.GetAssetPath(monoScript); - var monoImporter = (MonoImporter)AssetImporter.GetAtPath(scriptPath); + var monoImporter = (MonoImporter) AssetImporter.GetAtPath(scriptPath); // in Unity 2021.2, we can use monoImporter.GetIcon() // but it's not available in unity 2019 so use SerializedObject var serializedImporter = new SerializedObject(monoImporter); diff --git a/Assets/_ModularAvatar/EditModeTests/DuplicateObjectNameTest.cs b/Assets/_ModularAvatar/EditModeTests/DuplicateObjectNameTest.cs index e02d63d9..0d82adab 100644 --- a/Assets/_ModularAvatar/EditModeTests/DuplicateObjectNameTest.cs +++ b/Assets/_ModularAvatar/EditModeTests/DuplicateObjectNameTest.cs @@ -6,6 +6,7 @@ namespace _ModularAvatar.EditModeTests { public class DuplicateObjectNameTest : TestBase { + /* TODO - move to build framework [Test] public void test_duplicate_object_names() { @@ -17,5 +18,6 @@ namespace _ModularAvatar.EditModeTests c2.gameObject.name = "child2"; Assert.AreEqual(PathMappings.MapPath("child"), "child"); } + */ } } \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/MergeArmatureTests/MultiLevelMergeTest.cs b/Assets/_ModularAvatar/EditModeTests/MergeArmatureTests/MultiLevelMergeTest.cs index 3e7d6224..ff064040 100644 --- a/Assets/_ModularAvatar/EditModeTests/MergeArmatureTests/MultiLevelMergeTest.cs +++ b/Assets/_ModularAvatar/EditModeTests/MergeArmatureTests/MultiLevelMergeTest.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Linq; +using nadena.dev.ndmf.animation; using nadena.dev.modular_avatar.core; using nadena.dev.modular_avatar.core.editor; using NUnit.Framework; @@ -51,7 +52,10 @@ namespace modular_avatar_tests.MergeArmatureTests m1_leaf2.AddComponent(); m2_leaf3.AddComponent(); - BuildContext context = new BuildContext(root.GetComponent()); + nadena.dev.ndmf.BuildContext context = + new nadena.dev.ndmf.BuildContext(root.GetComponent(), null); + context.ActivateExtensionContext(); + context.ActivateExtensionContext(typeof(TrackObjectRenamesContext)); new MergeArmatureHook().OnPreprocessAvatar(context, root); Assert.IsTrue(bone.GetComponentInChildren() != null); @@ -76,7 +80,10 @@ namespace modular_avatar_tests.MergeArmatureTests ma.mergeTarget.referencePath = RuntimeUtil.AvatarRootPath(armature); ma.mangleNames = false; - BuildContext context = new BuildContext(root.GetComponent()); + nadena.dev.ndmf.BuildContext context = + new nadena.dev.ndmf.BuildContext(root.GetComponent(), null); + context.ActivateExtensionContext(); + context.ActivateExtensionContext(typeof(TrackObjectRenamesContext)); new MergeArmatureHook().OnPreprocessAvatar(context, root); Assert.IsTrue(m_bone == null); // destroyed by retargeting pass @@ -97,7 +104,10 @@ namespace modular_avatar_tests.MergeArmatureTests var ma = merge.AddComponent(); ma.mergeTarget.referencePath = RuntimeUtil.AvatarRootPath(armature); - BuildContext context = new BuildContext(root.GetComponent()); + nadena.dev.ndmf.BuildContext context = + new nadena.dev.ndmf.BuildContext(root.GetComponent(), null); + context.ActivateExtensionContext(); + context.ActivateExtensionContext(typeof(TrackObjectRenamesContext)); new MergeArmatureHook().OnPreprocessAvatar(context, root); Assert.IsTrue(m_bone == null); // destroyed by retargeting pass diff --git a/Assets/_ModularAvatar/EditModeTests/PathMappingTest.cs b/Assets/_ModularAvatar/EditModeTests/PathMappingTest.cs deleted file mode 100644 index 8791cf87..00000000 --- a/Assets/_ModularAvatar/EditModeTests/PathMappingTest.cs +++ /dev/null @@ -1,69 +0,0 @@ -using nadena.dev.modular_avatar.core.editor; -using NUnit.Framework; -using UnityEngine; - -namespace modular_avatar_tests -{ - public class PathMappingTest : TestBase - { - [Test] - public void TracksSimpleRenames() - { - var root = CreateRoot("root"); - var a = CreateChild(root, "a"); - - PathMappings.Init(root); - Assert.AreEqual("a", PathMappings.MapPath("a")); - a.name = "b"; - PathMappings.ClearCache(); - Assert.AreEqual("b", PathMappings.MapPath("a")); - } - - [Test] - public void TracksObjectMoves() - { - var root = CreateRoot("root"); - var a = CreateChild(root, "a"); - var b = CreateChild(root, "b"); - - PathMappings.Init(root); - Assert.AreEqual("a", PathMappings.MapPath("a")); - a.transform.parent = b.transform; - PathMappings.ClearCache(); - Assert.AreEqual("b/a", PathMappings.MapPath("a")); - } - - [Test] - public void TracksCollapses() - { - var root = CreateRoot("root"); - var a = CreateChild(root, "a"); - var b = CreateChild(a, "b"); - var c = CreateChild(b, "c"); - - PathMappings.Init(root); - PathMappings.MarkRemoved(b); - c.transform.parent = a.transform; - Object.DestroyImmediate(b); - - Assert.AreEqual("a/c", PathMappings.MapPath("a/b/c")); - } - - [Test] - public void TransformLookthrough() - { - var root = CreateRoot("root"); - var a = CreateChild(root, "a"); - var b = CreateChild(a, "b"); - var c = CreateChild(b, "c"); - var d = CreateChild(c, "d"); - - PathMappings.Init(root); - PathMappings.MarkTransformLookthrough(b); - PathMappings.MarkTransformLookthrough(c); - Assert.AreEqual("a/b/c", PathMappings.MapPath("a/b/c")); - Assert.AreEqual("a", PathMappings.MapPath("a/b/c", true)); - Assert.AreEqual("a/b/c/d", PathMappings.MapPath("a/b/c/d", true)); - } - } -} \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/PathMappingTest.cs.meta b/Assets/_ModularAvatar/EditModeTests/PathMappingTest.cs.meta deleted file mode 100644 index d776cb53..00000000 --- a/Assets/_ModularAvatar/EditModeTests/PathMappingTest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: fc087947fd98b2b43a853f93161cfe13 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_ModularAvatar/EditModeTests/ReplaceObject/ReplaceObjectTests.cs b/Assets/_ModularAvatar/EditModeTests/ReplaceObject/ReplaceObjectTests.cs index c094cb75..52a75087 100644 --- a/Assets/_ModularAvatar/EditModeTests/ReplaceObject/ReplaceObjectTests.cs +++ b/Assets/_ModularAvatar/EditModeTests/ReplaceObject/ReplaceObjectTests.cs @@ -1,4 +1,5 @@ using System; +using nadena.dev.ndmf.animation; using nadena.dev.modular_avatar.core; using nadena.dev.modular_avatar.core.editor; using nadena.dev.modular_avatar.editor.ErrorReporting; @@ -10,10 +11,14 @@ namespace modular_avatar_tests.ReplaceObject { public class ReplaceObjectTests : TestBase { + private TrackObjectRenamesContext pathMappings; + void Process(GameObject root) { var avDesc = root.GetComponent(); - new ReplaceObjectPass(new BuildContext(avDesc)).Process(); + var buildContext = new nadena.dev.ndmf.BuildContext(avDesc, null); + pathMappings = buildContext.ActivateExtensionContext(); + new ReplaceObjectPass(buildContext).Process(); } [Test] @@ -159,10 +164,9 @@ namespace modular_avatar_tests.ReplaceObject var replaceObject = replacement.AddComponent(); replaceObject.targetObject.Set(replacee); - PathMappings.Init(root); Process(root); - Assert.AreEqual("replacement", PathMappings.MapPath("replacee")); + Assert.AreEqual("replacement", pathMappings.MapPath("replacee")); } } } \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/RetargetMeshesTest.cs b/Assets/_ModularAvatar/EditModeTests/RetargetMeshesTest.cs index d128df2f..d5759fa4 100644 --- a/Assets/_ModularAvatar/EditModeTests/RetargetMeshesTest.cs +++ b/Assets/_ModularAvatar/EditModeTests/RetargetMeshesTest.cs @@ -1,4 +1,5 @@ -using nadena.dev.modular_avatar.core.editor; +using nadena.dev.ndmf.animation; +using nadena.dev.modular_avatar.core.editor; using NUnit.Framework; using UnityEngine; using VRC.SDK3.Avatars.Components; @@ -20,13 +21,19 @@ namespace modular_avatar_tests skinnedMeshRenderer.rootBone = b.transform; Debug.Assert(skinnedMeshRenderer.bones.Length == 0); - BoneDatabase.AddMergedBone(b.transform); - var context = new BuildContext(root.GetComponent()); - new RetargetMeshes().OnPreprocessAvatar(root, context); + var build_context = + new nadena.dev.ndmf.BuildContext(root.GetComponent(), null); + var torc = new TrackObjectRenamesContext(); + torc.OnActivate(build_context); + + var bonedb = new BoneDatabase(); + bonedb.AddMergedBone(b.transform); + + new RetargetMeshes().OnPreprocessAvatar(root, bonedb, torc); Assert.AreEqual(a.transform, skinnedMeshRenderer.rootBone); } - + [Test] public void NoMeshRootBoneOnly() { @@ -41,13 +48,19 @@ namespace modular_avatar_tests skinnedMeshRenderer.rootBone = b.transform; Debug.Assert(skinnedMeshRenderer.bones.Length == 0); - BoneDatabase.AddMergedBone(b.transform); - var context = new BuildContext(root.GetComponent()); - new RetargetMeshes().OnPreprocessAvatar(root, context); + var build_context = + new nadena.dev.ndmf.BuildContext(root.GetComponent(), null); + var torc = new TrackObjectRenamesContext(); + torc.OnActivate(build_context); + + var bonedb = new BoneDatabase(); + bonedb.AddMergedBone(b.transform); + + new RetargetMeshes().OnPreprocessAvatar(root, bonedb, torc); Assert.AreEqual(a.transform, skinnedMeshRenderer.rootBone); - Assert.AreEqual(new Bounds(new Vector3(0, 0, 0), new Vector3(2, 2, 2)), + Assert.AreEqual(new Bounds(new Vector3(0, 0, 0), new Vector3(2, 2, 2)), skinnedMeshRenderer.localBounds); } } -} +} \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/SerializationSweepTest.cs b/Assets/_ModularAvatar/EditModeTests/SerializationSweepTest.cs deleted file mode 100644 index 89de1b87..00000000 --- a/Assets/_ModularAvatar/EditModeTests/SerializationSweepTest.cs +++ /dev/null @@ -1,56 +0,0 @@ -using nadena.dev.modular_avatar.core.editor; -using NUnit.Framework; -using UnityEditor; -using UnityEngine; -using VRC.SDK3.Avatars.Components; - -namespace modular_avatar_tests.serialization -{ - class TestComponent : MonoBehaviour - { - public UnityEngine.Object ref1, ref2; - } - - class TestScriptable : ScriptableObject - { - public UnityEngine.Object ref1, ref2; - } - - public class SerializationSweepTest : TestBase - { - [Test] - public void testSerialization() - { - var root = CreateRoot("root"); - var child = CreateChild(root, "child"); - var testComponent = child.AddComponent(); - var testScriptable1 = ScriptableObject.CreateInstance(); - var testScriptable2 = ScriptableObject.CreateInstance(); - var testScriptable3 = ScriptableObject.CreateInstance(); - var testScriptable4 = ScriptableObject.CreateInstance(); - - testComponent.ref1 = testScriptable1; - testComponent.ref2 = root; - - testScriptable1.ref1 = testScriptable2; - testScriptable2.ref1 = testScriptable3; - - testScriptable1.ref2 = testScriptable4; - testScriptable2.ref2 = testScriptable4; - testScriptable3.ref2 = testScriptable4; - - BuildContext bc = new BuildContext(root.GetComponent()); - bc.CommitReferencedAssets(); - - var path = AssetDatabase.GetAssetPath(testScriptable1); - Assert.IsFalse(string.IsNullOrEmpty(path)); - Assert.AreEqual(path, AssetDatabase.GetAssetPath(testScriptable2)); - Assert.AreEqual(path, AssetDatabase.GetAssetPath(testScriptable3)); - Assert.AreEqual(path, AssetDatabase.GetAssetPath(testScriptable4)); - - Assert.IsTrue(string.IsNullOrEmpty(AssetDatabase.GetAssetPath(testComponent))); - Assert.IsTrue(string.IsNullOrEmpty(AssetDatabase.GetAssetPath(root))); - Assert.IsTrue(string.IsNullOrEmpty(AssetDatabase.GetAssetPath(child))); - } - } -} \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/SerializationSweepTest.cs.meta b/Assets/_ModularAvatar/EditModeTests/SerializationSweepTest.cs.meta deleted file mode 100644 index d31d3f97..00000000 --- a/Assets/_ModularAvatar/EditModeTests/SerializationSweepTest.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: a8035784f3364865a84cc938682be7a0 -timeCreated: 1690804771 \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/SerializationTests/SerializationTests.cs b/Assets/_ModularAvatar/EditModeTests/SerializationTests/SerializationTests.cs index 1948d449..6861d7b8 100644 --- a/Assets/_ModularAvatar/EditModeTests/SerializationTests/SerializationTests.cs +++ b/Assets/_ModularAvatar/EditModeTests/SerializationTests/SerializationTests.cs @@ -1,5 +1,6 @@ using System.Linq; using modular_avatar_tests; +using nadena.dev.ndmf.runtime; using nadena.dev.modular_avatar.core; using nadena.dev.modular_avatar.core.editor; using NUnit.Framework; @@ -60,7 +61,7 @@ namespace _ModularAvatar.EditModeTests.SerializationTests Assert.False(string.IsNullOrEmpty(path)); var mainAsset = AssetDatabase.LoadMainAssetAtPath(path); - Assert.IsInstanceOf(mainAsset); + Assert.IsInstanceOf(mainAsset); } } } \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/Tests.asmdef b/Assets/_ModularAvatar/EditModeTests/Tests.asmdef index e7f708c7..e5cd2b6e 100644 --- a/Assets/_ModularAvatar/EditModeTests/Tests.asmdef +++ b/Assets/_ModularAvatar/EditModeTests/Tests.asmdef @@ -8,7 +8,9 @@ "GUID:5718fb738711cd34ea54e9553040911d", "GUID:b906909fcc54f634db50f2cad0f988d9", "GUID:3456780c4fb2d324ab9c633d6f1b0ddb", - "GUID:e9745f6a32442194c8dc5a43e9ab86f9" + "GUID:e9745f6a32442194c8dc5a43e9ab86f9", + "GUID:62ced99b048af7f4d8dfe4bed8373d76", + "GUID:fe747755f7b44e048820525b07f9b956" ], "includePlatforms": [ "Editor" diff --git a/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs b/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs deleted file mode 100644 index 9dc34aa9..00000000 --- a/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2022 bd_ - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -using UnityEditor; -using UnityEngine; - -namespace nadena.dev.modular_avatar.core.editor -{ - [InitializeOnLoad] - internal static class ApplyOnPlay - { - private const string MENU_NAME = "Tools/Modular Avatar/Apply on Play"; - - /** - * We need to process avatars before lyuma's av3 emulator wakes up and processes avatars; it does this in Awake, - * so we have to do our processing in Awake as well. This seems to work fine when first entering play mode, but - * if you subsequently enable an initially-disabled avatar, processing from within Awake causes an editor crash. - * - * To workaround this, we initially process in awake; then, after OnPlayModeStateChanged is invoked (ie, after - * all initially-enabled components have Awake called), we switch to processing from Start instead. - */ - private static RuntimeUtil.OnDemandSource armedSource = RuntimeUtil.OnDemandSource.Awake; - - static ApplyOnPlay() - { - EditorApplication.playModeStateChanged += OnPlayModeStateChanged; - RuntimeUtil.OnDemandProcessAvatar = MaybeProcessAvatar; - EditorApplication.delayCall += () => Menu.SetChecked(MENU_NAME, ModularAvatarSettings.applyOnPlay); - } - - private static void MaybeProcessAvatar(RuntimeUtil.OnDemandSource source, MonoBehaviour component) - { - if (ModularAvatarSettings.applyOnPlay && source == armedSource && component != null) - { - var avatar = RuntimeUtil.FindAvatarInParents(component.transform); - if (avatar == null) return; - AvatarProcessor.ProcessAvatar(avatar.gameObject); - } - } - - [MenuItem(MENU_NAME)] - private static void ToggleApplyOnPlay() - { - ModularAvatarSettings.applyOnPlay = !ModularAvatarSettings.applyOnPlay; - Menu.SetChecked(MENU_NAME, ModularAvatarSettings.applyOnPlay); - } - - private static void OnPlayModeStateChanged(PlayModeStateChange obj) - { - if (obj == PlayModeStateChange.EnteredPlayMode) - { - armedSource = RuntimeUtil.OnDemandSource.Start; - } - else if (obj == PlayModeStateChange.EnteredEditMode) - { - armedSource = RuntimeUtil.OnDemandSource.Awake; - } - } - } -} diff --git a/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs.meta deleted file mode 100644 index b2f1a06b..00000000 --- a/Packages/nadena.dev.modular-avatar/Editor/ApplyOnPlay.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 246c8f6cb27c4758972ceac5e8700add -timeCreated: 1661822272 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/AvatarProcessor.cs b/Packages/nadena.dev.modular-avatar/Editor/AvatarProcessor.cs index b19805cb..efba2b74 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/AvatarProcessor.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/AvatarProcessor.cs @@ -22,51 +22,16 @@ * SOFTWARE. */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; -using nadena.dev.modular_avatar.editor.ErrorReporting; using UnityEditor; using UnityEngine; -using VRC.SDK3.Avatars.Components; -using VRC.SDKBase.Editor.BuildPipeline; -using BuildReport = nadena.dev.modular_avatar.editor.ErrorReporting.BuildReport; -using Debug = UnityEngine.Debug; -using Object = UnityEngine.Object; [assembly: InternalsVisibleTo("Tests")] namespace nadena.dev.modular_avatar.core.editor { - [InitializeOnLoad] - public class AvatarProcessor : IVRCSDKPreprocessAvatarCallback, IVRCSDKPostprocessAvatarCallback + public class AvatarProcessor { - // Place after EditorOnly processing (which runs at -1024) but hopefully before most other user callbacks - public int callbackOrder => -25; - - /// - /// Avoid recursive activation of avatar processing by suppressing starting processing while processing is - /// already in progress. - /// - private static bool nowProcessing = false; - - internal delegate void AvatarProcessorCallback(GameObject obj, BuildContext context); - - /// - /// This API is NOT stable. Do not use it yet. - /// - internal static event AvatarProcessorCallback AfterProcessing; - - static AvatarProcessor() - { - EditorApplication.playModeStateChanged += OnPlayModeStateChanged; - } - [MenuItem("GameObject/ModularAvatar/Manual bake avatar", true, 100)] static bool ValidateApplyToCurrentAvatarGameobject() { @@ -82,311 +47,18 @@ namespace nadena.dev.modular_avatar.core.editor [MenuItem("Tools/Modular Avatar/Manual bake avatar", true)] private static bool ValidateApplyToCurrentAvatar() { - var avatar = Selection.activeGameObject; - return (avatar != null && avatar.GetComponent() != null); + return ndmf.AvatarProcessor.CanProcessObject(Selection.activeGameObject); } [MenuItem("Tools/Modular Avatar/Manual bake avatar", false)] private static void ApplyToCurrentAvatar() { - var avatar = Selection.activeGameObject; - if (avatar == null || avatar.GetComponent() == null) return; - var basePath = "Assets/ModularAvatarOutput/" + avatar.name; - var savePath = basePath; - - int extension = 0; - - while (File.Exists(savePath) || Directory.Exists(savePath)) - { - savePath = basePath + " " + (++extension); - } - - string originalBasePath = RuntimeUtil.RelativePath(null, avatar); - avatar = Object.Instantiate(avatar); - - string clonedBasePath = RuntimeUtil.RelativePath(null, avatar); - try - { - Util.OverridePath = savePath; - - var original = avatar; - avatar.transform.position += Vector3.forward * 2; - - BuildReport.Clear(); - - ProcessAvatar(avatar); - Selection.objects = new Object[] {avatar}; - } - finally - { - Util.OverridePath = null; - BuildReport.RemapPaths(originalBasePath, clonedBasePath); - } - } - - private static void OnPlayModeStateChanged(PlayModeStateChange obj) - { - if (obj == PlayModeStateChange.EnteredEditMode) - { - Util.DeleteTemporaryAssets(); - } - } - - public void OnPostprocessAvatar() - { - Util.DeleteTemporaryAssets(); - } - - public bool OnPreprocessAvatar(GameObject avatarGameObject) - { - try - { - BuildReport.Clear(); - ProcessAvatar(avatarGameObject); - FixupAnimatorDebugData(avatarGameObject); - return true; - } - catch (Exception e) - { - Debug.LogError(e); - return false; - } + ndmf.AvatarProcessor.ProcessAvatarUI(Selection.activeGameObject); } public static void ProcessAvatar(GameObject avatarGameObject) { - if (nowProcessing) return; - - var vrcAvatarDescriptor = avatarGameObject.GetComponent(); - - Stopwatch sw = new Stopwatch(); - sw.Start(); - - using (BuildReport.CurrentReport.ReportingOnAvatar(vrcAvatarDescriptor)) - { - try - { - try - { - AssetDatabase.StartAssetEditing(); - nowProcessing = true; - - RemoveMissingScriptComponents(avatarGameObject); - - ClearEditorOnlyTagComponents(avatarGameObject.transform); - - BoneDatabase.ResetBones(); - PathMappings.Init(vrcAvatarDescriptor.gameObject); - ClonedMenuMappings.Clear(); - - // Sometimes people like to nest one avatar in another, when transplanting clothing. To avoid issues - // with inconsistently determining the avatar root, we'll go ahead and remove the extra sub-avatars - // here. - foreach (Transform directChild in avatarGameObject.transform) - { - foreach (var component in directChild.GetComponentsInChildren(true)) - { - Object.DestroyImmediate(component); - } - - // Disable deprecation warning for reference to PipelineSaver -#pragma warning disable CS0618 - foreach (var component in directChild.GetComponentsInChildren(true)) -#pragma warning restore CS0618 - { - Object.DestroyImmediate(component); - } - } - - var context = new BuildContext(vrcAvatarDescriptor); - - new MeshSettingsPass(context).OnPreprocessAvatar(); - new RenameParametersHook().OnPreprocessAvatar(avatarGameObject, context); - new MergeAnimatorProcessor().OnPreprocessAvatar(avatarGameObject, context); - context.AnimationDatabase.Bootstrap(vrcAvatarDescriptor); - - new MenuInstallHook().OnPreprocessAvatar(avatarGameObject, context); - new MergeArmatureHook().OnPreprocessAvatar(context, avatarGameObject); - new BoneProxyProcessor().OnPreprocessAvatar(avatarGameObject); - new VisibleHeadAccessoryProcessor(vrcAvatarDescriptor).Process(context); - new ReplaceObjectPass(context).Process(); - new RemapAnimationPass(vrcAvatarDescriptor).Process(context.AnimationDatabase); - new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(avatarGameObject, context); - PhysboneBlockerPass.Process(avatarGameObject); - - context.CommitReferencedAssets(); - - AfterProcessing?.Invoke(avatarGameObject, context); - - context.AnimationDatabase.Commit(); - - new GCGameObjectsPass(context, avatarGameObject).OnPreprocessAvatar(); - - context.CommitReferencedAssets(); - } - finally - { - AssetDatabase.StopAssetEditing(); - - nowProcessing = false; - - // Ensure that we clean up AvatarTagComponents after failed processing. This ensures we don't re-enter - // processing from the Awake method on the unprocessed AvatarTagComponents - var toDestroy = avatarGameObject.GetComponentsInChildren(true).ToList(); - var retryDestroy = new List(); - - // Sometimes AvatarTagComponents have interdependencies and need to be deleted in the right order; - // retry until we purge them all. - bool madeProgress = true; - while (toDestroy.Count > 0) - { - if (!madeProgress) - { - throw new Exception("One or more components failed to destroy." + - RuntimeUtil.AvatarRootPath(toDestroy[0].gameObject)); - } - - foreach (var component in toDestroy) - { - try - { - if (component != null) - { - UnityEngine.Object.DestroyImmediate(component); - madeProgress = true; - } - } - catch (Exception) - { - retryDestroy.Add(component); - } - } - - toDestroy = retryDestroy; - retryDestroy = new List(); - } - - var activator = avatarGameObject.GetComponent(); - if (activator != null) - { - UnityEngine.Object.DestroyImmediate(activator); - } - - ClonedMenuMappings.Clear(); - - AssetDatabase.SaveAssets(); - - Resources.UnloadUnusedAssets(); - } - } - catch (Exception e) - { - BuildReport.LogException(e); - throw; - } - finally - { - ErrorReportUI.MaybeOpenErrorReportUI(); - } - - if (!BuildReport.CurrentReport.CurrentAvatar.successful) - { - throw new Exception("Fatal error reported during avatar processing."); - } - } - - Debug.Log($"Processed avatar " + avatarGameObject.name + " in " + sw.ElapsedMilliseconds + "ms"); - } - - private static void RemoveMissingScriptComponents(GameObject avatarGameObject) - { - foreach (var child in avatarGameObject.GetComponentsInChildren(true)) - GameObjectUtility.RemoveMonoBehavioursWithMissingScript(child.gameObject); - } - - private static void ClearEditorOnlyTagComponents(Transform obj) - { - // EditorOnly objects can be used for multiple purposes - users might want a camera rig to be available in - // play mode, for example. For now, we'll prune MA components from EditorOnly objects, but otherwise leave - // them in place when in play mode. - - if (obj.CompareTag("EditorOnly")) - { - foreach (var component in obj.GetComponentsInChildren(true)) - { - UnityEngine.Object.DestroyImmediate(component); - } - } - else - { - foreach (Transform transform in obj) - { - ClearEditorOnlyTagComponents(transform); - } - } - } - - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - private static void FixupAnimatorDebugData(GameObject avatarGameObject) - { - Object tempControlPanel = null; - try - { - // The VRCSDK captures some debug information about animators as part of the build process, prior to invoking - // hooks. For some reason this happens in the ValidateFeatures call on the SDK builder. Reinvoke it to - // refresh this debug info. - // - // All of these methods are public, but for compatibility with unitypackage-based SDKs, we need to use - // reflection to invoke everything here, as the asmdef structure is different between the two SDK variants. - // Bleh. - // - // Canny filed requesting that this processing move after build hooks: - // https://feedback.vrchat.com/sdk-bug-reports/p/animator-debug-information-needs-to-be-captured-after-invoking-preprocess-avatar - var ty_VRCSdkControlPanelAvatarBuilder3A = Util.FindType( - "VRC.SDK3.Editor.VRCSdkControlPanelAvatarBuilder3A" - ); - var ty_AvatarPerformanceStats = Util.FindType( - "VRC.SDKBase.Validation.Performance.Stats.AvatarPerformanceStats" - ); - var ty_VRCSdkControlPanel = Util.FindType("VRCSdkControlPanel"); - - if (ty_VRCSdkControlPanelAvatarBuilder3A == null || ty_AvatarPerformanceStats == null || - ty_VRCSdkControlPanel == null) - { - return; - } - - var avatar = avatarGameObject.GetComponent(); - var animator = avatarGameObject.GetComponent(); - var builder = ty_VRCSdkControlPanelAvatarBuilder3A.GetConstructor(Type.EmptyTypes) - ?.Invoke(Array.Empty()); - var perfStats = ty_AvatarPerformanceStats.GetConstructor(new[] {typeof(bool)}) - ?.Invoke(new object[] {false}); - - if (builder == null || perfStats == null) - { - return; - } - - tempControlPanel = ScriptableObject.CreateInstance(ty_VRCSdkControlPanel) as Object; - - ty_VRCSdkControlPanelAvatarBuilder3A - .GetMethod("RegisterBuilder", BindingFlags.Public | BindingFlags.Instance) - .Invoke(builder, new object[] {tempControlPanel}); - ty_VRCSdkControlPanelAvatarBuilder3A.GetMethod("ValidateFeatures").Invoke( - builder, new object[] {avatar, animator, perfStats} - ); - } - catch (Exception e) - { - Debug.LogWarning( - "[ModularAvatar] Incompatible VRCSDK version; failed to regenerate animator debug data"); - Debug.LogException(e); - } - finally - { - if (tempControlPanel != null) Object.DestroyImmediate(tempControlPanel); - } + ndmf.AvatarProcessor.ProcessAvatar(avatarGameObject); } } -} +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/BuildContext.cs b/Packages/nadena.dev.modular-avatar/Editor/BuildContext.cs index 47bc3db6..72a38b41 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/BuildContext.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/BuildContext.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using UnityEditor; using UnityEditor.Animations; using UnityEngine; @@ -12,9 +11,11 @@ namespace nadena.dev.modular_avatar.core.editor { internal class BuildContext { - internal readonly VRCAvatarDescriptor AvatarDescriptor; + internal readonly nadena.dev.ndmf.BuildContext PluginBuildContext; + + internal VRCAvatarDescriptor AvatarDescriptor => PluginBuildContext.AvatarDescriptor; internal readonly AnimationDatabase AnimationDatabase = new AnimationDatabase(); - internal readonly UnityEngine.Object AssetContainer; + internal UnityEngine.Object AssetContainer => PluginBuildContext.AssetContainer; private bool SaveImmediate = false; @@ -29,16 +30,14 @@ namespace nadena.dev.modular_avatar.core.editor internal readonly Dictionary> PostProcessControls = new Dictionary>(); - public BuildContext(VRCAvatarDescriptor avatarDescriptor) + public BuildContext(nadena.dev.ndmf.BuildContext PluginBuildContext) { - AvatarDescriptor = avatarDescriptor; + this.PluginBuildContext = PluginBuildContext; + } - // AssetDatabase.CreateAsset is super slow - so only do it once, and add everything else as sub-assets. - // This scriptable object exists for the sole purpose of providing a placeholder to dump everything we - // generate into. Note that we use a custom component here to force binary serialization; this saves both - // time as well as disk space (if you're using manual bake). - AssetContainer = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(AssetContainer, Util.GenerateAssetPath()); + public BuildContext(VRCAvatarDescriptor avatarDescriptor) + : this(new ndmf.BuildContext(avatarDescriptor, null)) + { } public void SaveAsset(Object obj) @@ -124,95 +123,5 @@ namespace nadena.dev.modular_avatar.core.editor return newMenu; } - - public void CommitReferencedAssets() - { - HashSet referencedAssets = new HashSet(); - HashSet sceneAssets = new HashSet(); - - Walk(AvatarDescriptor.gameObject); - - referencedAssets.RemoveWhere(sceneAssets.Contains); - referencedAssets.RemoveWhere(a => a is GameObject || a is Component); - referencedAssets.RemoveWhere(o => !string.IsNullOrEmpty(AssetDatabase.GetAssetPath(o))); - - int index = 0; - - foreach (var asset in referencedAssets) - { - if (asset.name == "") - { - asset.name = "Asset " + index++; - } - - AssetDatabase.AddObjectToAsset(asset, AssetContainer); - } - - SaveImmediate = true; - - void Walk(GameObject root) - { - var components = AvatarDescriptor.gameObject.GetComponentsInChildren(true); - Queue visitQueue = new Queue( - components.Where(t => (!(t is Transform))) - ); - - while (visitQueue.Count > 0) - { - var current = visitQueue.Dequeue(); - if (referencedAssets.Contains(current)) continue; - referencedAssets.Add(current); - - // These assets have large internal arrays we don't want to walk through... - if (current is Mesh || current is AnimationClip || current is Texture) continue; - - var so = new SerializedObject(current); - var sp = so.GetIterator(); - bool enterChildren = true; - - while (sp.Next(enterChildren)) - { - enterChildren = true; - if (sp.name == "m_GameObject") continue; - if (sp.propertyType == SerializedPropertyType.String) - { - enterChildren = false; - continue; - } - - if (sp.isArray && IsPrimitiveArray(sp)) - { - enterChildren = false; - } - - if (sp.propertyType != SerializedPropertyType.ObjectReference) - { - continue; - } - - var obj = sp.objectReferenceValue; - if (obj != null && !referencedAssets.Contains(obj) && !(obj is Transform) && - !(obj is GameObject)) - { - visitQueue.Enqueue(sp.objectReferenceValue); - } - } - } - } - } - - private bool IsPrimitiveArray(SerializedProperty prop) - { - if (prop.arraySize == 0) return false; - var propertyType = prop.GetArrayElementAtIndex(0).propertyType; - switch (propertyType) - { - case SerializedPropertyType.Generic: - case SerializedPropertyType.ObjectReference: - return false; - default: - return true; - } - } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/Menu/ClonedMenuMappings.cs b/Packages/nadena.dev.modular-avatar/Editor/Menu/ClonedMenuMappings.cs deleted file mode 100644 index f24c2cb4..00000000 --- a/Packages/nadena.dev.modular-avatar/Editor/Menu/ClonedMenuMappings.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using VRC.SDK3.Avatars.ScriptableObjects; - -namespace nadena.dev.modular_avatar.core.editor -{ - internal static class ClonedMenuMappings - { - /// - /// Map to link the cloned menu from the clone source. - /// If one menu is specified for multiple installers, they are replicated separately, so there is a one-to-many relationship. - /// - private static readonly Dictionary> ClonedMappings = - new Dictionary>(); - - /// - /// Map to link the clone source from the cloned menu. - /// Map is the opposite of ClonedMappings. - /// - private static readonly Dictionary OriginalMapping = - new Dictionary(); - - public static void Clear() - { - ClonedMappings.Clear(); - OriginalMapping.Clear(); - } - - public static void Add(VRCExpressionsMenu original, VRCExpressionsMenu clonedMenu) - { - if (!ClonedMappings.TryGetValue(original, out ImmutableList clonedMenus)) - { - clonedMenus = ImmutableList.Empty; - } - - ClonedMappings[original] = clonedMenus.Add(clonedMenu); - OriginalMapping[clonedMenu] = original; - } - - public static bool TryGetClonedMenus(VRCExpressionsMenu original, - out ImmutableList clonedMenus) - { - return ClonedMappings.TryGetValue(original, out clonedMenus); - } - - public static VRCExpressionsMenu GetOriginal(VRCExpressionsMenu cloned) - { - return OriginalMapping.TryGetValue(cloned, out VRCExpressionsMenu original) ? original : null; - } - } -} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/Menu/ClonedMenuMappings.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/Menu/ClonedMenuMappings.cs.meta deleted file mode 100644 index 656acaab..00000000 --- a/Packages/nadena.dev.modular-avatar/Editor/Menu/ClonedMenuMappings.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: aeaeff9c3af44683bb2f8f5fe6c5791d -timeCreated: 1671016064 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs b/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs index 419ab9c6..1e627692 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs @@ -1,18 +1,18 @@ /* * MIT License - * + * * Copyright (c) 2022 bd_ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; using System.Linq; +using nadena.dev.ndmf.animation; using nadena.dev.modular_avatar.editor.ErrorReporting; using UnityEditor; using UnityEngine; @@ -38,13 +39,19 @@ namespace nadena.dev.modular_avatar.core.editor { internal class MergeArmatureHook { + private ndmf.BuildContext frameworkContext; private BuildContext context; + private BoneDatabase BoneDatabase = new BoneDatabase(); + + private TrackObjectRenamesContext PathMappings => frameworkContext.Extension(); + private HashSet mergedObjects = new HashSet(); private HashSet thisPassAdded = new HashSet(); - internal void OnPreprocessAvatar(BuildContext context, GameObject avatarGameObject) + internal void OnPreprocessAvatar(ndmf.BuildContext context, GameObject avatarGameObject) { - this.context = context; + this.frameworkContext = context; + this.context = context.Extension().BuildContext; var mergeArmatures = avatarGameObject.transform.GetComponentsInChildren(true); @@ -74,7 +81,7 @@ namespace nadena.dev.modular_avatar.core.editor RetainBoneReferences(c as Component); } - new RetargetMeshes().OnPreprocessAvatar(avatarGameObject, context); + new RetargetMeshes().OnPreprocessAvatar(avatarGameObject, BoneDatabase, PathMappings); } private void TopoProcessMergeArmatures(ModularAvatarMergeArmature[] mergeArmatures) diff --git a/Packages/nadena.dev.modular-avatar/Editor/MeshRetargeter.cs b/Packages/nadena.dev.modular-avatar/Editor/MeshRetargeter.cs index e3116003..181df1f9 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/MeshRetargeter.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/MeshRetargeter.cs @@ -1,18 +1,18 @@ /* * MIT License - * + * * Copyright (c) 2022 bd_ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,37 +25,38 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using nadena.dev.ndmf.animation; using nadena.dev.modular_avatar.editor.ErrorReporting; using UnityEngine; namespace nadena.dev.modular_avatar.core.editor { - internal static class BoneDatabase + internal class BoneDatabase { - private static Dictionary m_IsRetargetable = new Dictionary(); + private Dictionary m_IsRetargetable = new Dictionary(); - internal static void ResetBones() + internal void ResetBones() { m_IsRetargetable.Clear(); } - internal static bool IsRetargetable(Transform t) + internal bool IsRetargetable(Transform t) { return m_IsRetargetable.TryGetValue(t, out var result) && result; } - internal static void AddMergedBone(Transform bone) + internal void AddMergedBone(Transform bone) { m_IsRetargetable[bone] = true; } - internal static void RetainMergedBone(Transform bone) + internal void RetainMergedBone(Transform bone) { if (bone == null) return; if (m_IsRetargetable.ContainsKey(bone)) m_IsRetargetable[bone] = false; } - internal static Transform GetRetargetedBone(Transform bone) + internal Transform GetRetargetedBone(Transform bone) { if (bone == null || !m_IsRetargetable.ContainsKey(bone)) return null; @@ -65,14 +66,14 @@ namespace nadena.dev.modular_avatar.core.editor return bone; } - internal static IEnumerable> GetRetargetedBones() + internal IEnumerable> GetRetargetedBones() { return m_IsRetargetable.Where((kvp) => kvp.Value) .Select(kvp => new KeyValuePair(kvp.Key, GetRetargetedBone(kvp.Key))) .Where(kvp => kvp.Value != null); } - public static Transform GetRetargetedBone(Transform bone, bool fallbackToOriginal) + public Transform GetRetargetedBone(Transform bone, bool fallbackToOriginal) { Transform retargeted = GetRetargetedBone(bone); @@ -82,11 +83,14 @@ namespace nadena.dev.modular_avatar.core.editor internal class RetargetMeshes { - private BuildContext _context; + private BoneDatabase _boneDatabase; + private TrackObjectRenamesContext _pathTracker; - internal void OnPreprocessAvatar(GameObject avatarGameObject, BuildContext context) + internal void OnPreprocessAvatar(GameObject avatarGameObject, BoneDatabase boneDatabase, + TrackObjectRenamesContext pathMappings) { - _context = context; + this._boneDatabase = boneDatabase; + this._pathTracker = pathMappings; foreach (var renderer in avatarGameObject.GetComponentsInChildren(true)) { @@ -95,19 +99,18 @@ namespace nadena.dev.modular_avatar.core.editor bool isRetargetable = false; foreach (var bone in renderer.bones) { - if (BoneDatabase.GetRetargetedBone(bone) != null) + if (_boneDatabase.GetRetargetedBone(bone) != null) { isRetargetable = true; break; } } - isRetargetable |= BoneDatabase.GetRetargetedBone(renderer.rootBone); + isRetargetable |= _boneDatabase.GetRetargetedBone(renderer.rootBone); if (isRetargetable) { - var newMesh = new MeshRetargeter(renderer).Retarget(); - if (newMesh) _context.SaveAsset(newMesh); + new MeshRetargeter(renderer, _boneDatabase).Retarget(); } }); } @@ -115,9 +118,9 @@ namespace nadena.dev.modular_avatar.core.editor // Now remove retargeted bones if (true) { - foreach (var bonePair in BoneDatabase.GetRetargetedBones()) + foreach (var bonePair in _boneDatabase.GetRetargetedBones()) { - if (BoneDatabase.GetRetargetedBone(bonePair.Key) == null) continue; + if (_boneDatabase.GetRetargetedBone(bonePair.Key) == null) continue; var sourceBone = bonePair.Key; var destBone = bonePair.Value; @@ -150,7 +153,7 @@ namespace nadena.dev.modular_avatar.core.editor child.SetParent(destBone, true); } - PathMappings.MarkRemoved(sourceBone.gameObject); + _pathTracker.MarkRemoved(sourceBone.gameObject); UnityEngine.Object.DestroyImmediate(sourceBone.gameObject); } } @@ -164,11 +167,14 @@ namespace nadena.dev.modular_avatar.core.editor internal class MeshRetargeter { private readonly SkinnedMeshRenderer renderer; + private readonly BoneDatabase _boneDatabase; + [CanBeNull] private Mesh src, dst; - public MeshRetargeter(SkinnedMeshRenderer renderer) + public MeshRetargeter(SkinnedMeshRenderer renderer, BoneDatabase boneDatabase) { this.renderer = renderer; + this._boneDatabase = boneDatabase; } [CanBeNull] @@ -218,7 +224,7 @@ namespace nadena.dev.modular_avatar.core.editor for (int i = 0; i < originalBones.Length; i++) { - Transform newBindTarget = BoneDatabase.GetRetargetedBone(originalBones[i]); + Transform newBindTarget = _boneDatabase.GetRetargetedBone(originalBones[i]); if (newBindTarget == null) continue; newBones[i] = newBindTarget; @@ -247,8 +253,8 @@ namespace nadena.dev.modular_avatar.core.editor renderer.sharedMesh = dst; } - var newRootBone = BoneDatabase.GetRetargetedBone(rootBone, true); - var newScaleBone = BoneDatabase.GetRetargetedBone(scaleBone, true); + var newRootBone = _boneDatabase.GetRetargetedBone(rootBone, true); + var newScaleBone = _boneDatabase.GetRetargetedBone(scaleBone, true); var oldLossyScale = scaleBone.transform.lossyScale; var newLossyScale = newScaleBone.transform.lossyScale; @@ -267,7 +273,7 @@ namespace nadena.dev.modular_avatar.core.editor renderer.localBounds = bounds; renderer.rootBone = newRootBone; - renderer.probeAnchor = BoneDatabase.GetRetargetedBone(renderer.probeAnchor, true); + renderer.probeAnchor = _boneDatabase.GetRetargetedBone(renderer.probeAnchor, true); } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/PathMappings.cs b/Packages/nadena.dev.modular-avatar/Editor/PathMappings.cs deleted file mode 100644 index f791e185..00000000 --- a/Packages/nadena.dev.modular-avatar/Editor/PathMappings.cs +++ /dev/null @@ -1,203 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2022 bd_ - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -using System.Collections.Generic; -using System.Collections.Immutable; -using UnityEditor; -using UnityEngine; - -namespace nadena.dev.modular_avatar.core.editor -{ - internal static class PathMappings - { - private static Dictionary> _objectToOriginalPaths = - new Dictionary>(); - - private static ImmutableDictionary _originalPathToMappedPath = null; - private static ImmutableDictionary _transformOriginalPathToMappedPath = null; - - private static HashSet _transformLookthroughObjects = new HashSet(); - - internal static void Init(GameObject root) - { - _objectToOriginalPaths.Clear(); - _originalPathToMappedPath = null; - _transformLookthroughObjects.Clear(); - - foreach (var xform in root.GetComponentsInChildren(true)) - { - var path = RuntimeUtil.RelativePath(root, xform.gameObject); - _objectToOriginalPaths.Add(xform.gameObject, new List {path}); - } - - ClearCache(); - } - - internal static void ClearCache() - { - _originalPathToMappedPath = _transformOriginalPathToMappedPath = null; - } - - /// - /// Returns a path identifying a given object. This can include objects not originally present; in this case, - /// they will be assigned a randomly-generated internal ID which will be replaced during path remapping with - /// the true path. - /// - /// Object to map - /// - internal static string GetObjectIdentifier(GameObject obj) - { - if (_objectToOriginalPaths.TryGetValue(obj, out var paths)) - { - return paths[0]; - } - else - { - var internalPath = "_ModularAvatarInternal/" + GUID.Generate(); - _objectToOriginalPaths.Add(obj, new List {internalPath}); - return internalPath; - } - } - - /// - /// When animating a transform component on a merged bone, we want to make sure we manipulate the original - /// avatar's bone, not a stub bone attached underneath. By making an object as transform lookthrough, any - /// queries for mapped paths on the transform component will walk up the tree to the next parent. - /// - /// The object to mark transform lookthrough - internal static void MarkTransformLookthrough(GameObject obj) - { - ClearCache(); - _transformLookthroughObjects.Add(obj); - } - - /// - /// Marks an object as having been removed. Its paths will be remapped to its parent. - /// - /// - internal static void MarkRemoved(GameObject obj) - { - ClearCache(); - if (_objectToOriginalPaths.TryGetValue(obj, out var paths)) - { - var parent = obj.transform.parent.gameObject; - if (_objectToOriginalPaths.TryGetValue(parent, out var parentPaths)) - { - parentPaths.AddRange(paths); - } - - _objectToOriginalPaths.Remove(obj); - _transformLookthroughObjects.Remove(obj); - } - } - - /// - /// Marks an object as having been replaced by another object. All references to the old object will be replaced - /// by the new object. References originally to the new object will continue to point to the new object. - /// - /// - /// - public static void ReplaceObject(GameObject old, GameObject newObject) - { - ClearCache(); - - if (_objectToOriginalPaths.TryGetValue(old, out var paths)) - { - if (!_objectToOriginalPaths.TryGetValue(newObject, out var newObjectPaths)) - { - newObjectPaths = new List(); - _objectToOriginalPaths.Add(newObject, newObjectPaths); - } - - newObjectPaths.AddRange(paths); - - _objectToOriginalPaths.Remove(old); - } - - - if (_transformLookthroughObjects.Contains(old)) - { - _transformLookthroughObjects.Remove(old); - _transformLookthroughObjects.Add(newObject); - } - } - - private static ImmutableDictionary BuildMapping(ref ImmutableDictionary cache, - bool transformLookup) - { - if (cache != null) return cache; - - ImmutableDictionary dict = ImmutableDictionary.Empty; - - foreach (var kvp in _objectToOriginalPaths) - { - var obj = kvp.Key; - var paths = kvp.Value; - - if (transformLookup) - { - while (_transformLookthroughObjects.Contains(obj)) - { - obj = obj.transform.parent.gameObject; - } - } - - var newPath = RuntimeUtil.AvatarRootPath(obj); - foreach (var origPath in paths) - { - if (!dict.ContainsKey(origPath)) - { - dict = dict.Add(origPath, newPath); - } - } - } - - cache = dict; - return cache; - } - - internal static string MapPath(string path, bool isTransformMapping = false) - { - ImmutableDictionary mappings; - - if (isTransformMapping) - { - mappings = BuildMapping(ref _originalPathToMappedPath, true); - } - else - { - mappings = BuildMapping(ref _transformOriginalPathToMappedPath, false); - } - - if (mappings.TryGetValue(path, out var mappedPath)) - { - return mappedPath; - } - else - { - return path; - } - } - } -} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/PathMappings.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/PathMappings.cs.meta deleted file mode 100644 index b14f1e00..00000000 --- a/Packages/nadena.dev.modular-avatar/Editor/PathMappings.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 6a5a2ea7723848d1bfe793debcf298cc -timeCreated: 1661649007 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition.meta b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition.meta new file mode 100644 index 00000000..2f47b229 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 06c2c2893c2741c0895e3b90ee8e33c0 +timeCreated: 1691213499 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/ModularAvatarContext.cs b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/ModularAvatarContext.cs new file mode 100644 index 00000000..70fadd0d --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/ModularAvatarContext.cs @@ -0,0 +1,28 @@ +using System; +using nadena.dev.ndmf; +using nadena.dev.modular_avatar.editor.ErrorReporting; + +namespace nadena.dev.modular_avatar.core.editor +{ + internal class ModularAvatarContext : IExtensionContext + { + private IDisposable toDispose; + internal BuildContext BuildContext { get; private set; } + + public void OnActivate(ndmf.BuildContext context) + { + if (BuildContext == null) + { + BuildContext = new BuildContext(context); + } + + toDispose = BuildReport.CurrentReport.ReportingOnAvatar(context.AvatarDescriptor); + } + + public void OnDeactivate(ndmf.BuildContext context) + { + toDispose?.Dispose(); + toDispose = null; + } + } +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/ModularAvatarContext.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/ModularAvatarContext.cs.meta new file mode 100644 index 00000000..fbe30f5b --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/ModularAvatarContext.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2be0ae3b99ac44c0a35522d7fd0c6f10 +timeCreated: 1692614275 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs new file mode 100644 index 00000000..272732e6 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using nadena.dev.ndmf; +using nadena.dev.ndmf.animation; +using UnityEngine; + +[assembly: ExportsPlugin( + typeof(nadena.dev.modular_avatar.core.editor.plugin.PluginDefinition) +)] + +namespace nadena.dev.modular_avatar.core.editor.plugin +{ + class PluginDefinition : Plugin + { + public override string QualifiedName => "nadena.dev.modular-avatar"; + + public override ImmutableList Passes => (new List() + { + new ResolveObjectReferences(), + new ClearEditorOnlyTags(), + new MeshSettingsPluginPass(), + new RenameParametersPluginPass(), + new MergeAnimatorPluginPass(), + new MenuInstallPluginPass(), + new MergeArmaturePluginPass(), + new BoneProxyPluginPass(), + new VisibleHeadAccessoryPluginPass(), + new ReplaceObjectPluginPass(), + new BlendshapeSyncAnimationPluginPass(), + new PhysbonesBlockerPluginPass(), + new GCGameObjectsPluginPass(), + }).ToImmutableList(); + } + + /// + /// This plugin runs very early in order to resolve all AvatarObjectReferences to their + /// referent before any other plugins perform heirarchy manipulations. + /// + internal class ResolveObjectReferences : PluginPass + { + public override BuiltInPhase ExecutionPhase => BuiltInPhase.Resolving; + + public override void Process(ndmf.BuildContext context) + { + foreach (var obj in context.AvatarRootObject.GetComponentsInChildren()) + { + obj.ResolveReferences(); + } + } + } + + abstract class MAPass : PluginPass + { + public override IImmutableSet RequiredContexts => + ImmutableHashSet.Empty.Add(typeof(ModularAvatarContext)); + + public override IImmutableSet CompatibleContexts => + ImmutableHashSet.Empty.Add(typeof(TrackObjectRenamesContext)); + + protected BuildContext MAContext(ndmf.BuildContext context) + { + return context.Extension().BuildContext; + } + } + + class ClearEditorOnlyTags : MAPass + { + public override void Process(ndmf.BuildContext context) + { + Traverse(context.AvatarRootTransform); + } + + void Traverse(Transform obj) + { + // EditorOnly objects can be used for multiple purposes - users might want a camera rig to be available in + // play mode, for example. For now, we'll prune MA components from EditorOnly objects, but otherwise leave + // them in place when in play mode. + + if (obj.CompareTag("EditorOnly")) + { + foreach (var component in obj.GetComponentsInChildren(true)) + { + UnityEngine.Object.DestroyImmediate(component); + } + } + else + { + foreach (Transform transform in obj) + { + Traverse(transform); + } + } + } + } + + class MeshSettingsPluginPass : MAPass + { + public override void Process(ndmf.BuildContext context) + { + new MeshSettingsPass(MAContext(context)).OnPreprocessAvatar(); + } + } + + class RenameParametersPluginPass : MAPass + { + public override void Process(ndmf.BuildContext context) + { + new RenameParametersHook().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); + } + } + + class MergeAnimatorPluginPass : MAPass + { + public override void Process(ndmf.BuildContext context) + { + new MergeAnimatorProcessor().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); + } + } + + class MenuInstallPluginPass : MAPass + { + public override void Process(ndmf.BuildContext context) + { + new MenuInstallHook().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); + } + } + + class MergeArmaturePluginPass : MAPass + { + public override IImmutableSet RequiredContexts => + base.RequiredContexts.Add(typeof(TrackObjectRenamesContext)); + + public override void Process(ndmf.BuildContext context) + { + // The animation database is currently only used by the merge armature hook; it should probably become + // an extension context instead. + MAContext(context).AnimationDatabase.Bootstrap(context.AvatarDescriptor); + new MergeArmatureHook().OnPreprocessAvatar(context, context.AvatarRootObject); + MAContext(context).AnimationDatabase.Commit(); + } + } + + class BoneProxyPluginPass : MAPass + { + public override IImmutableSet RequiredContexts => + base.RequiredContexts.Add(typeof(TrackObjectRenamesContext)); + + public override void Process(ndmf.BuildContext context) + { + new BoneProxyProcessor().OnPreprocessAvatar(context.AvatarRootObject); + } + } + + class VisibleHeadAccessoryPluginPass : MAPass + { + public override IImmutableSet RequiredContexts => + base.RequiredContexts.Add(typeof(TrackObjectRenamesContext)); + + public override void Process(ndmf.BuildContext context) + { + new VisibleHeadAccessoryProcessor(context.AvatarDescriptor).Process(MAContext(context)); + } + } + + class ReplaceObjectPluginPass : MAPass + { + public override IImmutableSet RequiredContexts => + base.RequiredContexts.Add(typeof(TrackObjectRenamesContext)); + + public override void Process(ndmf.BuildContext context) + { + new ReplaceObjectPass(context).Process(); + } + } + + class BlendshapeSyncAnimationPluginPass : MAPass + { + // Flush animation path remappings, since we need an up-to-date path name while adjusting blendshape animations + public override IImmutableSet CompatibleContexts => + ImmutableHashSet.Empty; + + public override void Process(ndmf.BuildContext context) + { + new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); + } + } + + class PhysbonesBlockerPluginPass : MAPass + { + public override void Process(ndmf.BuildContext context) + { + PhysboneBlockerPass.Process(context.AvatarRootObject); + } + } + + class GCGameObjectsPluginPass : MAPass + { + public override BuiltInPhase ExecutionPhase => BuiltInPhase.Optimization; + + public override void Process(ndmf.BuildContext context) + { + new GCGameObjectsPass(MAContext(context), context.AvatarRootObject).OnPreprocessAvatar(); + } + } +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs.meta new file mode 100644 index 00000000..fe2c85cd --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/PluginDefinition/PluginDefinition.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aed34bf4ee4045ff97b749ab1c36d845 +timeCreated: 1691213504 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/RemapAnimationPass.cs b/Packages/nadena.dev.modular-avatar/Editor/RemapAnimationPass.cs deleted file mode 100644 index bde77eb0..00000000 --- a/Packages/nadena.dev.modular-avatar/Editor/RemapAnimationPass.cs +++ /dev/null @@ -1,77 +0,0 @@ -using nadena.dev.modular_avatar.editor.ErrorReporting; -using UnityEditor; -using UnityEngine; -using VRC.SDK3.Avatars.Components; - -namespace nadena.dev.modular_avatar.core.editor -{ - /// - /// Remaps all animation path references based on PathMappings data. - /// - internal class RemapAnimationPass - { - private readonly VRCAvatarDescriptor _avatarDescriptor; - - public RemapAnimationPass(VRCAvatarDescriptor avatarDescriptor) - { - _avatarDescriptor = avatarDescriptor; - } - - public void Process(AnimationDatabase animDb) - { - PathMappings.ClearCache(); - animDb.ForeachClip(clip => - { - BuildReport.ReportingObject(clip.CurrentClip, () => - { - if (clip.CurrentClip is AnimationClip anim && !clip.IsProxyAnimation) - { - clip.CurrentClip = MapMotion(anim); - } - }); - }); - } - - private static string MapPath(EditorCurveBinding binding) - { - if (binding.type == typeof(Animator) && binding.path == "") - { - return ""; - } - else - { - return PathMappings.MapPath(binding.path, binding.type == typeof(Transform)); - } - } - - private AnimationClip MapMotion(AnimationClip clip) - { - AnimationClip newClip = new AnimationClip(); - newClip.name = "remapped " + clip.name; - - foreach (var binding in AnimationUtility.GetCurveBindings(clip)) - { - var newBinding = binding; - newBinding.path = MapPath(binding); - newClip.SetCurve(newBinding.path, newBinding.type, newBinding.propertyName, - AnimationUtility.GetEditorCurve(clip, binding)); - } - - foreach (var objBinding in AnimationUtility.GetObjectReferenceCurveBindings(clip)) - { - var newBinding = objBinding; - newBinding.path = MapPath(objBinding); - AnimationUtility.SetObjectReferenceCurve(newClip, newBinding, - AnimationUtility.GetObjectReferenceCurve(clip, objBinding)); - } - - newClip.wrapMode = clip.wrapMode; - newClip.legacy = clip.legacy; - newClip.frameRate = clip.frameRate; - newClip.localBounds = clip.localBounds; - AnimationUtility.SetAnimationClipSettings(newClip, AnimationUtility.GetAnimationClipSettings(clip)); - - return newClip; - } - } -} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/RemapAnimationPass.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/RemapAnimationPass.cs.meta deleted file mode 100644 index 7c4a1cc3..00000000 --- a/Packages/nadena.dev.modular-avatar/Editor/RemapAnimationPass.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 13e4085d9ace44a680228680e5e1172e -timeCreated: 1671618477 \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/ReplaceObjectPass.cs b/Packages/nadena.dev.modular-avatar/Editor/ReplaceObjectPass.cs index cf1a53d8..86163a3d 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/ReplaceObjectPass.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/ReplaceObjectPass.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using nadena.dev.ndmf.animation; using nadena.dev.modular_avatar.editor.ErrorReporting; using UnityEditor; using UnityEngine; @@ -13,9 +14,9 @@ namespace nadena.dev.modular_avatar.core.editor internal class ReplaceObjectPass { - private readonly BuildContext _buildContext; + private readonly ndmf.BuildContext _buildContext; - public ReplaceObjectPass(BuildContext context) + public ReplaceObjectPass(ndmf.BuildContext context) { _buildContext = context; } @@ -122,7 +123,8 @@ namespace nadena.dev.modular_avatar.core.editor } } - PathMappings.ReplaceObject(original, replacement); + _buildContext.Extension() + .ReplaceObject(original, replacement); // Destroy original UnityObject.DestroyImmediate(original); diff --git a/Packages/nadena.dev.modular-avatar/Editor/Util.cs b/Packages/nadena.dev.modular-avatar/Editor/Util.cs index de386645..76316e78 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/Util.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/Util.cs @@ -1,18 +1,18 @@ /* * MIT License - * + * * Copyright (c) 2022 bd_ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -193,8 +193,10 @@ namespace nadena.dev.modular_avatar.core.editor public static bool IsTemporaryAsset(Object obj) { var path = AssetDatabase.GetAssetPath(obj); + var generatedAssetsFolder = OverridePath ?? generatedAssetsPath; - return string.IsNullOrEmpty(path) || path.StartsWith(GetGeneratedAssetsFolder() + "/"); + return !EditorUtility.IsPersistent(obj) || string.IsNullOrEmpty(path) || + path.StartsWith(generatedAssetsFolder + "/"); } public static Type FindType(string typeName) diff --git a/Packages/nadena.dev.modular-avatar/Editor/nadena.dev.modular-avatar.core.editor.asmdef b/Packages/nadena.dev.modular-avatar/Editor/nadena.dev.modular-avatar.core.editor.asmdef index 172b2d69..617b4352 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/nadena.dev.modular-avatar.core.editor.asmdef +++ b/Packages/nadena.dev.modular-avatar/Editor/nadena.dev.modular-avatar.core.editor.asmdef @@ -3,7 +3,8 @@ "references": [ "GUID:fc900867c0f47cd49b6e2ae4ef907300", "GUID:5718fb738711cd34ea54e9553040911d", - "GUID:3456780c4fb2d324ab9c633d6f1b0ddb" + "GUID:3456780c4fb2d324ab9c633d6f1b0ddb", + "GUID:62ced99b048af7f4d8dfe4bed8373d76" ], "includePlatforms": [ "Editor" @@ -27,6 +28,12 @@ ], "autoReferenced": false, "defineConstraints": [], - "versionDefines": [], + "versionDefines": [ + { + "name": "nadena.dev.ndmf", + "expression": "[0.0.1,999]", + "define": "AV3_BUILD_FRAMEWORK_PRESENT" + } + ], "noEngineReferences": false } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs b/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs index ad253d01..e47a00d7 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/Activator.cs @@ -1,15 +1,13 @@ #if UNITY_EDITOR -using UnityEditor; -using UnityEditor.SceneManagement; using UnityEngine; -using UnityEngine.SceneManagement; -using VRC.SDK3.Avatars.Components; +using VRC.SDKBase; namespace nadena.dev.modular_avatar.core { /// - /// This component is used to trigger MA processing upon entering play mode (prior to Av3Emu running). + /// This component was previously used to trigger avatar processing when entering play mode. This functionality has + /// moved to NDMF, so we leave here a stub to clean up detritus left behind from older versions of MA. /// We create it on a hidden object via AvatarTagObject's OnValidate, and it will proceed to add MAAvatarActivator /// components to all avatar roots which contain MA components. This MAAvatarActivator component then performs MA /// processing on Awake. @@ -21,108 +19,19 @@ namespace nadena.dev.modular_avatar.core [AddComponentMenu("")] [ExecuteInEditMode] [DefaultExecutionOrder(-9998)] - public class Activator : MonoBehaviour + public class Activator : MonoBehaviour, IEditorOnly { - private const string TAG_OBJECT_NAME = "ModularAvatarInternal_Activator"; - - private void Awake() + private void Update() { - if (!RuntimeUtil.isPlaying || this == null) return; - - var scene = gameObject.scene; - foreach (var root in scene.GetRootGameObjects()) - { - foreach (var avatar in root.GetComponentsInChildren()) - { - if (avatar.GetComponentInChildren(true) != null) - { - avatar.gameObject.GetOrAddComponent().hideFlags = HideFlags.HideInInspector; - } - } - } + UnityEngine.Object.DestroyImmediate(gameObject); } - - private bool HasMAComponentsInScene() - { - var scene = gameObject.scene; - foreach (var root in scene.GetRootGameObjects()) - { - if (root.GetComponentInChildren(true) != null) return true; - } - - return false; - } - - private void OnValidate() - { - if (EditorApplication.isPlayingOrWillChangePlaymode) return; - - EditorApplication.delayCall += () => - { - if (this == null) return; - - gameObject.hideFlags = HIDE_FLAGS; - if (!HasMAComponentsInScene()) - { - var scene = gameObject.scene; - DestroyImmediate(gameObject); - EditorSceneManager.MarkSceneDirty(scene); - } - }; - } - - internal static void CreateIfNotPresent(Scene scene) - { - if (!scene.IsValid() || EditorSceneManager.IsPreviewScene(scene)) return; - if (EditorApplication.isPlayingOrWillChangePlaymode) return; - - bool rootPresent = false; - foreach (var root in scene.GetRootGameObjects()) - { - if (root.GetComponent() != null) - { - root.hideFlags = HIDE_FLAGS; - if (rootPresent) DestroyImmediate(root); - rootPresent = true; - } - } - - if (rootPresent) return; - - var oldActiveScene = SceneManager.GetActiveScene(); - try - { - SceneManager.SetActiveScene(scene); - var gameObject = new GameObject(TAG_OBJECT_NAME); - gameObject.AddComponent(); - gameObject.hideFlags = HIDE_FLAGS; - } - finally - { - SceneManager.SetActiveScene(oldActiveScene); - } - } - - private const HideFlags HIDE_FLAGS = HideFlags.HideInHierarchy; } [AddComponentMenu("")] [ExecuteInEditMode] [DefaultExecutionOrder(-9997)] - public class AvatarActivator : MonoBehaviour + public class AvatarActivator : MonoBehaviour, IEditorOnly { - private void Awake() - { - if (!RuntimeUtil.isPlaying || this == null) return; - RuntimeUtil.OnDemandProcessAvatar(RuntimeUtil.OnDemandSource.Awake, this); - } - - private void Start() - { - if (!RuntimeUtil.isPlaying || this == null) return; - RuntimeUtil.OnDemandProcessAvatar(RuntimeUtil.OnDemandSource.Start, this); - } - private void Update() { DestroyImmediate(this); diff --git a/Packages/nadena.dev.modular-avatar/Runtime/AvatarObjectReference.cs b/Packages/nadena.dev.modular-avatar/Runtime/AvatarObjectReference.cs index 867ec5dc..dec35e8c 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/AvatarObjectReference.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/AvatarObjectReference.cs @@ -7,6 +7,8 @@ namespace nadena.dev.modular_avatar.core [Serializable] public class AvatarObjectReference { + private long ReferencesLockedAtFrame = long.MinValue; + public static string AVATAR_ROOT = "$$$AVATAR_ROOT$$$"; public string referencePath; @@ -16,7 +18,9 @@ namespace nadena.dev.modular_avatar.core public GameObject Get(Component container) { - if (_cacheValid && _cachedPath == referencePath && _cachedReference != null) return _cachedReference; + bool cacheValid = _cacheValid || ReferencesLockedAtFrame == Time.frameCount; + + if (cacheValid && _cachedPath == referencePath && _cachedReference != null) return _cachedReference; _cacheValid = true; _cachedPath = referencePath; @@ -57,7 +61,8 @@ namespace nadena.dev.modular_avatar.core referencePath = RuntimeUtil.AvatarRootPath(target); } - _cacheValid = false; + _cachedReference = target; + _cacheValid = true; } private void InvalidateCache() diff --git a/Packages/nadena.dev.modular-avatar/Runtime/AvatarTagComponent.cs b/Packages/nadena.dev.modular-avatar/Runtime/AvatarTagComponent.cs index e3aa44dc..1453457e 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/AvatarTagComponent.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/AvatarTagComponent.cs @@ -1,18 +1,18 @@ /* * MIT License - * + * * Copyright (c) 2022 bd_ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -52,14 +52,6 @@ namespace nadena.dev.modular_avatar.core { if (RuntimeUtil.isPlaying) return; - RuntimeUtil.delayCall(() => - { - if (this == null) return; -#if UNITY_EDITOR - Activator.CreateIfNotPresent(gameObject.scene); -#endif - }); - OnChangeAction?.Invoke(); } @@ -67,5 +59,10 @@ namespace nadena.dev.modular_avatar.core { OnChangeAction?.Invoke(); } + + /// + /// Eagerly resolve all AvatarTagReferences to their destinations. + /// + internal abstract void ResolveReferences(); } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/Menu/ModularAvatarMenuGroup.cs b/Packages/nadena.dev.modular-avatar/Runtime/Menu/ModularAvatarMenuGroup.cs index d8c395ec..5f187540 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/Menu/ModularAvatarMenuGroup.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/Menu/ModularAvatarMenuGroup.cs @@ -12,5 +12,10 @@ namespace nadena.dev.modular_avatar.core { context.PushNode(new MenuNodesUnder(targetObject != null ? targetObject : gameObject)); } + + internal override void ResolveReferences() + { + // no-op + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/Menu/ModularAvatarMenuInstaller.cs b/Packages/nadena.dev.modular-avatar/Runtime/Menu/ModularAvatarMenuInstaller.cs index 1615119e..09c436a3 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/Menu/ModularAvatarMenuInstaller.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/Menu/ModularAvatarMenuInstaller.cs @@ -22,5 +22,10 @@ namespace nadena.dev.modular_avatar.core RuntimeUtil.InvalidateMenu(); } + + internal override void ResolveReferences() + { + // no-op + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarBlendshapeSync.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarBlendshapeSync.cs index fbb27947..648913dc 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarBlendshapeSync.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarBlendshapeSync.cs @@ -64,6 +64,11 @@ namespace nadena.dev.modular_avatar.core RuntimeUtil.OnHierarchyChanged -= Rebind; } + internal override void ResolveReferences() + { + // no-op + } + private void Rebind() { if (this == null) return; diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarBoneProxy.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarBoneProxy.cs index f44dd794..93686d9b 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarBoneProxy.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarBoneProxy.cs @@ -1,18 +1,18 @@ /* * MIT License - * + * * Copyright (c) 2022 bd_ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -95,6 +95,11 @@ namespace nadena.dev.modular_avatar.core public string subPath; public BoneProxyAttachmentMode attachmentMode = BoneProxyAttachmentMode.Unset; + internal override void ResolveReferences() + { + _targetCache = UpdateDynamicMapping(); + } + protected override void OnValidate() { base.OnValidate(); @@ -103,7 +108,19 @@ namespace nadena.dev.modular_avatar.core internal void ClearCache() { - _targetCache = null; + ClearCache(false); + } + + internal void ClearCache(bool immediate) + { + if (immediate) + { + _targetCache = null; + } else if (_targetCache != null) + { + RuntimeUtil.delayCall(() => { _targetCache = null; }); + } + RuntimeUtil.OnHierarchyChanged -= ClearCache; } diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMenuInstallTarget.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMenuInstallTarget.cs index 6bd98f3e..5e893b5e 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMenuInstallTarget.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMenuInstallTarget.cs @@ -22,5 +22,10 @@ namespace nadena.dev.modular_avatar.core { context.PushNode(installer); } + + internal override void ResolveReferences() + { + // no-op + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMenuItem.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMenuItem.cs index 1cb8b8fb..58ff537f 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMenuItem.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMenuItem.cs @@ -37,6 +37,11 @@ namespace nadena.dev.modular_avatar.core } } + internal override void ResolveReferences() + { + // no-op + } + public void Visit(NodeContext context) { if (Control == null) diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMergeAnimator.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMergeAnimator.cs index 51d9a236..950385bc 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMergeAnimator.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMergeAnimator.cs @@ -1,18 +1,18 @@ /* * MIT License - * + * * Copyright (c) 2022 bd_ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -41,5 +41,10 @@ namespace nadena.dev.modular_avatar.core public bool deleteAttachedAnimator; public MergeAnimatorPathMode pathMode = MergeAnimatorPathMode.Relative; public bool matchAvatarWriteDefaults; + + internal override void ResolveReferences() + { + // no-op + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMergeArmature.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMergeArmature.cs index 1c4c2468..7e7c806a 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMergeArmature.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMergeArmature.cs @@ -1,18 +1,18 @@ /* * MIT License - * + * * Copyright (c) 2022 bd_ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -89,6 +89,11 @@ namespace nadena.dev.modular_avatar.core #endif } + internal override void ResolveReferences() + { + mergeTarget?.Get(this); + } + void EditorUpdate() { if (this == null) diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMeshSettings.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMeshSettings.cs index a15e68e1..0661b111 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMeshSettings.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarMeshSettings.cs @@ -25,5 +25,11 @@ namespace nadena.dev.modular_avatar.core public InheritMode InheritBounds = InheritMode.Inherit; public AvatarObjectReference RootBone; public Bounds Bounds = DEFAULT_BOUNDS; + + internal override void ResolveReferences() + { + ProbeAnchor?.Get(this); + RootBone?.Get(this); + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarPBBlocker.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarPBBlocker.cs index 964cc3d5..d66f2e4c 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarPBBlocker.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarPBBlocker.cs @@ -1,18 +1,18 @@ /* * MIT License - * + * * Copyright (c) 2022 bd_ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,5 +30,9 @@ namespace nadena.dev.modular_avatar.core [AddComponentMenu("Modular Avatar/MA PhysBone Blocker")] public class ModularAvatarPBBlocker : AvatarTagComponent { + internal override void ResolveReferences() + { + // no-op + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarParameters.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarParameters.cs index aab67f07..b82d00ae 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarParameters.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarParameters.cs @@ -33,5 +33,10 @@ namespace nadena.dev.modular_avatar.core public class ModularAvatarParameters : AvatarTagComponent { public List parameters = new List(); + + internal override void ResolveReferences() + { + // no-op + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarReplaceObject.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarReplaceObject.cs index 2439909f..fec064e2 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarReplaceObject.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarReplaceObject.cs @@ -7,5 +7,10 @@ namespace nadena.dev.modular_avatar.core public class ModularAvatarReplaceObject : AvatarTagComponent { public AvatarObjectReference targetObject = new AvatarObjectReference(); + + internal override void ResolveReferences() + { + targetObject?.Get(this); + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarVisibleHeadAccessory.cs b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarVisibleHeadAccessory.cs index 284094fa..1cc12429 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarVisibleHeadAccessory.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/ModularAvatarVisibleHeadAccessory.cs @@ -7,5 +7,9 @@ namespace nadena.dev.modular_avatar.core public class ModularAvatarVisibleHeadAccessory : AvatarTagComponent { // no configuration needed + internal override void ResolveReferences() + { + // no-op + } } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/package.json b/Packages/nadena.dev.modular-avatar/package.json index fff4e610..7d393630 100644 --- a/Packages/nadena.dev.modular-avatar/package.json +++ b/Packages/nadena.dev.modular-avatar/package.json @@ -15,6 +15,7 @@ "com.unity.nuget.newtonsoft-json": "2.0.0" }, "vpmDependencies": { - "com.vrchat.avatars": ">=3.2.0" + "com.vrchat.avatars": ">=3.2.0", + "nadena.dev.ndmf": "=0.1.0" } } diff --git a/Packages/nadena.dev.ndmf b/Packages/nadena.dev.ndmf new file mode 160000 index 00000000..3a5f80df --- /dev/null +++ b/Packages/nadena.dev.ndmf @@ -0,0 +1 @@ +Subproject commit 3a5f80dfabd90c27c1b5a9e216f1a9093c28e092 diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index fd9c771c..5ec6c8cc 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -196,6 +196,12 @@ "com.unity.nuget.newtonsoft-json": "2.0.0" } }, + "nadena.dev.ndmf": { + "version": "file:nadena.dev.ndmf", + "depth": 0, + "source": "embedded", + "dependencies": {} + }, "vrchat.blackstartx.gesture-manager": { "version": "file:vrchat.blackstartx.gesture-manager", "depth": 0,