diff --git a/Assets/_ModularAvatar/EditModeTests/AvatarObjectReferenceTest.cs b/Assets/_ModularAvatar/EditModeTests/AvatarObjectReferenceTest.cs new file mode 100644 index 00000000..2db8268a --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/AvatarObjectReferenceTest.cs @@ -0,0 +1,21 @@ +using modular_avatar_tests; +using nadena.dev.modular_avatar.core; +using NUnit.Framework; + +public class AvatarObjectReferenceTest : TestBase +{ + [Test] + public void whenFakeArmaturePresent_bindsToRealArmature() + { + var root = CreateRoot("root"); + var fake_armature = CreateChild(root, "Armature"); + var real_armature = CreateChild(root, "Armature"); + + var hips = CreateChild(real_armature, "Hips"); + + var reference = new AvatarObjectReference(); + reference.referencePath = "Armature"; + + Assert.AreEqual(real_armature, reference.Get(root.transform)); + } +} diff --git a/Assets/_ModularAvatar/EditModeTests/AvatarObjectReferenceTest.cs.meta b/Assets/_ModularAvatar/EditModeTests/AvatarObjectReferenceTest.cs.meta new file mode 100644 index 00000000..55886711 --- /dev/null +++ b/Assets/_ModularAvatar/EditModeTests/AvatarObjectReferenceTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bb23d6c06929440e999c855d3b9f70fc +timeCreated: 1694775902 \ No newline at end of file diff --git a/Assets/_ModularAvatar/EditModeTests/GameObjectGC/GameObjectGC.cs b/Assets/_ModularAvatar/EditModeTests/GameObjectGC/GameObjectGC.cs index 4e36c2d3..711cad4e 100644 --- a/Assets/_ModularAvatar/EditModeTests/GameObjectGC/GameObjectGC.cs +++ b/Assets/_ModularAvatar/EditModeTests/GameObjectGC/GameObjectGC.cs @@ -1,4 +1,5 @@ -using modular_avatar_tests; +using System.Linq; +using modular_avatar_tests; using nadena.dev.modular_avatar.core.editor; using NUnit.Framework; using UnityEngine; @@ -38,4 +39,22 @@ public class GameObjectGC : TestBase Assert.True(bone2 != null); Assert.True(bone3 == null); } + + [Test] + public void RetainArmatureHack() + { + var fake_humanoid = CreatePrefab("FakeHumanoid.prefab"); + var avdesc = fake_humanoid.GetComponent(); + + var armature = new GameObject(); + armature.name = "Armature"; + armature.transform.parent = fake_humanoid.transform; + armature.transform.SetSiblingIndex(0); + + new GCGameObjectsPass(new BuildContext(avdesc), fake_humanoid).OnPreprocessAvatar(); + AvatarProcessor.ProcessAvatar(fake_humanoid); + + Assert.AreEqual(2, + avdesc.GetComponentsInChildren().Count(t => t.gameObject.name == "Armature")); + } } \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/OptimizationPasses/GCGameObjectsPass.cs b/Packages/nadena.dev.modular-avatar/Editor/OptimizationPasses/GCGameObjectsPass.cs index a982ff09..23c2e939 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/OptimizationPasses/GCGameObjectsPass.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/OptimizationPasses/GCGameObjectsPass.cs @@ -99,6 +99,28 @@ namespace nadena.dev.modular_avatar.core.editor MarkObject(t.gameObject); } } + + // https://github.com/bdunderscore/modular-avatar/issues/308 + // If we have duplicate Armature bones, retain them all in order to deal with some horrible hacks that are + // in use in the wild. + try + { + var trueArmature = animator?.GetBoneTransform(HumanBodyBones.Hips)?.parent; + if (trueArmature != null) + { + foreach (Transform t in _root.transform) + { + if (t.name == trueArmature.name) + { + MarkObject(t.gameObject); + } + } + } + } + catch (MissingComponentException e) + { + // No animator? weird. Move on. + } } private void MarkPhysBone(VRCPhysBone pb) diff --git a/Packages/nadena.dev.modular-avatar/Runtime/AvatarObjectReference.cs b/Packages/nadena.dev.modular-avatar/Runtime/AvatarObjectReference.cs index 867ec5dc..65708e49 100644 --- a/Packages/nadena.dev.modular-avatar/Runtime/AvatarObjectReference.cs +++ b/Packages/nadena.dev.modular-avatar/Runtime/AvatarObjectReference.cs @@ -39,7 +39,27 @@ namespace nadena.dev.modular_avatar.core return _cachedReference; } - return (_cachedReference = avatar.transform.Find(referencePath)?.gameObject); + _cachedReference = avatar.transform.Find(referencePath)?.gameObject; + if (_cachedReference == null) return null; + + // https://github.com/bdunderscore/modular-avatar/issues/308 + // Some avatars have multiple "Armature" objects in order to confuse VRChat into changing the avatar eye + // position. We need to be smarter than VRChat and find the "true" armature in this case. + var targetName = _cachedReference.name; + var parent = _cachedReference.transform.parent; + if (targetName == "Armature" && parent != null && _cachedReference.transform.childCount == 0) + { + foreach (Transform possibleTarget in parent) + { + if (possibleTarget.gameObject.name == targetName && possibleTarget.childCount > 0) + { + _cachedReference = possibleTarget.gameObject; + break; + } + } + } + + return _cachedReference; } public void Set(GameObject target)