Symptom
Switching Eden's three tree spawn sites from generate_tree() to generate_tree_with_roots() produces a total explosion: the world blows apart. Observed 2026-08-02, sync prep ([GPU_PREP] path=SYNC), 12 trees spawned, log /tmp/eden_roots.log.
Reverted immediately. Eden is back on generate_tree().
Why the swap was attempted
Every measurement behind the issue #38 fix (8c0e86e) was taken on generate_tree_with_roots: creation overlap 511 pairs to 8, coincident 381 to 0, canopy peak speed 1.19 m/s against a 2.0 limit. Eden calls generate_tree(), a path none of those tests cover, and Eden still dropped its canopies after the fix. Moving Eden onto the measured path was the cheapest way to test whether that was the whole explanation.
It is not, and the rooted path is worse in Eden than the one it replaced.
What is different about the two paths
generate_tree() attaches the trunk to a floor tile. generate_tree_with_roots() builds a root plate plus primary roots that rest on the turtle, with no floor tile required.
The world_z argument means different things to each. Eden passes gz from ground_here(world_x, world_y, gz, SupportMode::ROOTED), which is a floor-tile surface height. generate_tree_with_roots treats its third argument as ground_z and builds a root system DOWNWARD from it: the root plate sits around z = -0.25 with a half-thickness of 0.125, and primary roots spread below that.
On Eden's streamed terrain that puts the entire root system inside the floor tiles that already exist there. Every root is created interpenetrating a tile, and the solver does the only correct thing with an impossible world.
This is the same class as issue #38 (bodies created inside each other) but caused by the caller's frame of reference rather than by discarded positions, and at a far larger scale because it is every root of every tree against a full terrain chunk.
Not yet verified
The mechanism above is inferred from the argument semantics and from #38's measurements. It has NOT been measured: no headless capture of the explosion was taken before reverting. That should come before anyone tries this again.
What this does NOT explain
Eden's canopies still fall off on the ORIGINAL generate_tree() path, and there is an orbiting trunk. Those remain open under issue #38 and task #35. The leading remaining hypothesis is that Eden's ambient forest comes from generate_tree_space_colonization() at examples/eden/src/main.cpp:432, which uses TreeGenerator, a DIFFERENT generator that none of the #38 work touched.
If this is retried
- Capture the explosion headlessly first, with the creation-overlap scan from
tests/test_no_overlap_at_creation.cpp pointed at an Eden world rather than an isolated tree.
- Decide what
world_z should mean when a rooted tree is planted on terrain that already has floor tiles. Either the roots need the tiles removed beneath them, or the rooted path needs a variant that plants ON a surface rather than THROUGH it.
- Do not switch all three call sites at once again.
Symptom
Switching Eden's three tree spawn sites from
generate_tree()togenerate_tree_with_roots()produces a total explosion: the world blows apart. Observed 2026-08-02, sync prep ([GPU_PREP] path=SYNC), 12 trees spawned, log/tmp/eden_roots.log.Reverted immediately. Eden is back on
generate_tree().Why the swap was attempted
Every measurement behind the issue #38 fix (
8c0e86e) was taken ongenerate_tree_with_roots: creation overlap 511 pairs to 8, coincident 381 to 0, canopy peak speed 1.19 m/s against a 2.0 limit. Eden callsgenerate_tree(), a path none of those tests cover, and Eden still dropped its canopies after the fix. Moving Eden onto the measured path was the cheapest way to test whether that was the whole explanation.It is not, and the rooted path is worse in Eden than the one it replaced.
What is different about the two paths
generate_tree()attaches the trunk to a floor tile.generate_tree_with_roots()builds a root plate plus primary roots that rest on the turtle, with no floor tile required.The
world_zargument means different things to each. Eden passesgzfromground_here(world_x, world_y, gz, SupportMode::ROOTED), which is a floor-tile surface height.generate_tree_with_rootstreats its third argument asground_zand builds a root system DOWNWARD from it: the root plate sits around z = -0.25 with a half-thickness of 0.125, and primary roots spread below that.On Eden's streamed terrain that puts the entire root system inside the floor tiles that already exist there. Every root is created interpenetrating a tile, and the solver does the only correct thing with an impossible world.
This is the same class as issue #38 (bodies created inside each other) but caused by the caller's frame of reference rather than by discarded positions, and at a far larger scale because it is every root of every tree against a full terrain chunk.
Not yet verified
The mechanism above is inferred from the argument semantics and from #38's measurements. It has NOT been measured: no headless capture of the explosion was taken before reverting. That should come before anyone tries this again.
What this does NOT explain
Eden's canopies still fall off on the ORIGINAL
generate_tree()path, and there is an orbiting trunk. Those remain open under issue #38 and task #35. The leading remaining hypothesis is that Eden's ambient forest comes fromgenerate_tree_space_colonization()atexamples/eden/src/main.cpp:432, which usesTreeGenerator, a DIFFERENT generator that none of the #38 work touched.If this is retried
tests/test_no_overlap_at_creation.cpppointed at an Eden world rather than an isolated tree.world_zshould mean when a rooted tree is planted on terrain that already has floor tiles. Either the roots need the tiles removed beneath them, or the rooted path needs a variant that plants ON a surface rather than THROUGH it.