Repository navigation
fix(flame_3d): Invalidate the AABBs of nested components when an ancestor moves - #4167
Merged
Merged
Conversation
…stor moves When a Component3D's transform changed, only the world transform dirty flag was propagated down to its descendants, while the AABB dirty flag was only propagated up. Descendants kept the AABB cached for their old world position and were frustum culled based on it. The AABB is now marked dirty together with the world transform when propagating down the subtree, and the parent's AABB is marked dirty from the component whose transform changed.
3 of 4 tasks
wolfenrain
approved these changes
Oct 31, 2026
ufrshubham
added a commit
that referenced
this pull request
Oct 31, 2026
…or removed (#4180) A `Component3D`'s AABB is the hull of its own geometry and the AABBs of its children, but adding or removing a child didn't invalidate the parent's cached AABB. `Object3D.renderTree` culls a component together with its whole subtree using its AABB, so a child added inside the view was never rendered when the parent's cached AABB was outside of it, for example a spawner or pool that adds children to itself at runtime. This PR marks the parent's AABB as dirty when a component is mounted, so new children are included in it. It does the same when a component is removed, which isn't visible but keeps the parent's AABB from staying bigger than its children. Marking stops at the first ancestor that is already dirty and the AABB is only recomputed lazily, so this costs the same as a child moving, which already marks its ancestors dirty. The tests cover a child added to a parent, a child added deeper in the hierarchy and a child being removed, and all of them fail without the fix. I also verified the fix with the repro from #4164 on Windows. This PR is stacked on top of #4167, as both add the same test file. It will be retargeted to `main` once #4167 is merged. https://github.com/user-attachments/assets/9630091a-7c4c-43e9-aea4-68fdbcf92e2b https://github.com/user-attachments/assets/2751d5e7-8c61-4acb-8ea7-e540fc899c70
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When the transform of a
Component3Dchanged, only the world transform dirty flag was propagated down to its descendants, while the AABB dirty flag was only propagated up. The descendants kept the AABB cached for their old world position, so they were frustum culled based on where they used to be. This affected regularMeshComponents andModelComponents inside any group that moves.This PR marks the AABB dirty together with the world transform when propagating down the subtree in
_markWorldTransformDirty, and marks the parent's AABB dirty from_onTransformChanged(callingmarkAabbDirty()on the component itself would now return early, since the downward propagation already marked it).The early return in
_markWorldTransformDirtynow only happens when both flags are already dirty, since the world transform and the AABB are recomputed independently and one can be dirty while the other is not. This relies on a recomputed AABB also refreshing the world transforms of its ancestors, which holds for every component with geometry. A component without geometry or children still uses its local position for its AABB (#4165), so it doesn't depend on its ancestors yet, and it will once that is fixed.The tests cover a nested child, a deeply nested child and repeated moves where only the leaf is read in between, which exercises that early return. All of them fail without the fix. I also verified the fix with the repro from #4163 on Windows.
before_4167_annotated.mp4
after_4167_annotated.mp4
Checklist
docsand added dartdoc comments with///.examplesordocs.Breaking Change?
Related Issues
Closes #4163