diff --git a/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs b/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs index 035591c1..a4429ae6 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/MergeArmatureHook.cs @@ -283,6 +283,8 @@ namespace nadena.dev.modular_avatar.core.editor mergedSrcBone.transform.localScale = src.transform.localScale; mergedSrcBone.transform.SetParent(newParent.transform, true); + if (zipMerge) PruneDuplicatePhysBones(newParent, src); + bool retain = HasAdditionalComponents(src, out var constraintType); if (constraintType != null) { @@ -359,5 +361,40 @@ namespace nadena.dev.modular_avatar.core.editor return retain; } + + /** + * Sometimes outfit authors copy the entire armature, including PhysBones components. If we merge these and + * end up with multiple PB components referencing the same target, PB refuses to animate the bone. So detect + * and prune this case. + * + * For simplicity - we currently only detect the case where the physbone references the component it's on. + * TODO - detect duplicate colliders, contacts, et - these can cause perf issues but usually not quite as large + * of a correctness issue. + */ + private void PruneDuplicatePhysBones(GameObject baseBone, GameObject mergeBone) + { + bool hasSelfReferencePB = false; + + foreach (var pb in baseBone.GetComponents()) + { + var target = pb.rootTransform; + if (target == null || target == baseBone.transform) + { + hasSelfReferencePB = true; + break; + } + } + + if (!hasSelfReferencePB) return; + + foreach (var pb in mergeBone.GetComponents()) + { + var target = pb.rootTransform; + if (target == null || target == baseBone.transform) + { + Object.DestroyImmediate(pb); + } + } + } } } \ No newline at end of file