Skip to content

Fix #2592: avoid export recursion when an ancestor is bone-parented to the skinning armature#2712

Open
Builder106 wants to merge 1 commit into
KhronosGroup:mainfrom
Builder106:fix/2592-bone-parented-ancestor-skin-recursion
Open

Fix #2592: avoid export recursion when an ancestor is bone-parented to the skinning armature#2712
Builder106 wants to merge 1 commit into
KhronosGroup:mainfrom
Builder106:fix/2592-bone-parented-ancestor-skin-recursion

Conversation

@Builder106

Copy link
Copy Markdown

Fixes #2592.

Problem

Exporting a scene where a skinned mesh has an ancestor that is bone-parented to the same armature raises RecursionError, and the export fails.

Minimal repro: Armature (bone Bone) → Empty bone-parented to BoneCube object-parented to the Empty, with a vertex group Bone + an Armature modifier targeting Armature. Exporting to glTF raises RecursionError: maximum recursion depth exceeded.

Cause

gather_skin (exp/nodes.py) already guards the direct case: a mesh bone-parented to a bone of its own skinning armature would make the skin's joint an ancestor of the mesh node, forming a cycle, so the skin is dropped. But the guard only checked blender_object.parent. When the bone-parented node is an ancestor (the Empty) rather than the mesh itself, the guard misses it, the skin is created, and the exported node graph contains a cycle:

Cube.skin -> joint (Bone) -> Bone.children = [Empty] -> Empty.children = [Cube] -> Cube.skin -> ...

The exporter's node traversal follows .skin and .children, so it recurses until RecursionError.

Fix

Walk the whole ancestor chain instead of only the direct parent; if any ancestor is bone-parented to the skinning armature, drop the skin (and keep the bone parenting) — identical behavior to the existing guard, just generalized past the direct parent.

Verification

Headless Blender 5.1.1 (the reported version), export_format='GLTF_SEPARATE':

scene before after
repro (skinned mesh, bone-parented ancestor) RecursionError, no file written FINISHED · skins: [] · Cube node has mesh, no skin
control (normal skinning, mesh parented to the armature) skins length 1, Cube skinned skins length 1, Cube skinned (unchanged)

So the cycle is broken (skin dropped, mesh + bone parenting preserved) and normal skinning is unaffected.

Two points for your call: I kept the existing name-based armature comparison for a minimal diff — happy to switch it to identity (ancestor.parent == modifiers["ARMATURE"].object) to harden against same-named linked/duplicated armatures if you'd prefer. I can also add a tests/ fixture for this topology if that's useful.

…e-parented to the skinning armature

gather_skin already drops the skin (keeping the bone parenting) when a
skinned mesh is itself bone-parented to a bone of its own armature, to
avoid a cyclic exported node graph. But that check only looked at the
mesh's direct parent, so the cycle still formed when the bone-parented
node was an ANCESTOR rather than the mesh itself (e.g. a mesh
object-parented to an empty that is bone-parented to the armature).

The exporter's node traversal then followed
skin -> joint -> children -> ... -> mesh -> skin endlessly and raised
RecursionError. Walk the whole ancestor chain instead of just the direct
parent.
@Builder106

Copy link
Copy Markdown
Author

Hey @julienduroure, since you filed #2592 yourself, figured this one might be worth a look. It's been about three weeks. The existing cycle-guard in gather_skin only checks the mesh's direct parent, so it misses the case where a bone-parented object is further up the ancestor chain instead of being the mesh itself, that's what still triggers the RecursionError. This generalizes the guard to walk the whole chain. I verified both the repro (RecursionError to FINISHED, skin correctly dropped) and a normal-skinning control case so regular skinning stays unaffected. There are two smaller PRs open too, #2710 and #2711, in case it's useful to look at them together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug infinite recursive

1 participant