Skip to content

Instance collision#699

Draft
aidandavey wants to merge 12 commits intoTokisanGames:mainfrom
aidandavey:Instance-collision
Draft

Instance collision#699
aidandavey wants to merge 12 commits intoTokisanGames:mainfrom
aidandavey:Instance-collision

Conversation

@aidandavey
Copy link
Copy Markdown
Contributor

@aidandavey aidandavey commented May 23, 2025

This PR aims to add collision for the foliage instancer, mentioned in issue #43

The headlines :

  • Physics shapes exclusively handled using the PhysicsServer
  • Only creates and destroys edge cells
  • Provides visualizations via the Rendering Server
  • Works for scene files with multiple shapes
  • Supports Game and Editor modes
  • Allows teleporting

Some further details:

It will read any CollisionShape3Ds found in the Terrain3DMeshAsset scene file and store their Shape3Ds and transforms.

When terrain collision is rebuilt in Terrain3DCollision it also spawns shapes for the instances using data read from the data/region/instances dictionary and the Shape3D and transforms stored in the MA.

In Dynamic mode:

  1. Build an array of cell positions to build, skipping cells which already exist.

  2. Build a dictionary of instances in said cells.

  3. Decompose cells outside of the collision radius and store the instances within for recycling.

  4. Compare the list of recyclable instances against the list of instances to build. Decompose unneeded instances to their component shapes, store for recycling.

  5. Generate the new instances, preferably:

    A. Recycle an old instance, simply transforming the shapes. Or...
    B. Reuse spare shapes, transform and update base meshes. Or...
    C. Generate brand new shapes.

  6. Destroy and remaining shapes and update an internal RID/shape_id map.

Full mode for instance collision was shelved.

For both Editor and Game modes, the PhysicsServer3D is used. The main reason for this was I kept hitting the 2^18 object limit when stress testing Full mode.

For Editor modes, visualizations are provided via the RenderingServer.

To test it, add a new Terrain3DMeshAsset and use RockA.tscn as the scene file. Paint some rocks using the foliage instancer and run the scene.

Future plans:

  1. Have a body per MA - allow individual layers and other physics settings
  2. Manual mode - allow the user to enable and destroy instance collision by cell through the API
  3. Roll up of the shape transforms - currently it takes the local transform only
  4. Enable update by MA per cell, to improve performance in the editor (if needed)
  5. Use hash maps instead of Dictionaries

@TokisanGames TokisanGames added the enhancement New feature or request label May 24, 2025
@CavemanIke
Copy link
Copy Markdown

CavemanIke commented May 24, 2025

When collision is rebuilt in Terrain3DCollision it also spawns bodies and shapes for the instances using data read from the data/region/instances dictionary and the Shape3D stored in the MA.

If this is creating a body with the shape for each instance, I think an optimization can be had here. If a single body is used and the same shape is added to it multiple times with transforms matching the instances, it will use significantly less memory. Through my tests, adding 250K bodies using the same shape RID results in ~400 megabytes of additional RAM usage. Using a single body, but adding 250K of the same shape to it at different transforms was only ~100 megabytes extra RAM usage. Rebuilding shapes on a single body is also extremely fast compared to creating multiple bodies.

Comment thread src/terrain_3d_collision.cpp Outdated
@aidandavey aidandavey force-pushed the Instance-collision branch from 4d2ace6 to b4ed412 Compare May 24, 2025 12:48
@aidandavey
Copy link
Copy Markdown
Contributor Author

Work on only updating edge cells, and spawning body per cell, shape per instance

This will be helpful:

Terrain3DInstancer::_get_cell(const Vector3 &p_global_position, const int p_region_size) will give the cell index for the current region.

so cell_local_pos.xz = instance_pos.xz - cell * cell_size.

@aidandavey aidandavey force-pushed the Instance-collision branch from 92fb6c4 to a397db9 Compare May 28, 2025 16:03
@aidandavey
Copy link
Copy Markdown
Contributor Author

@TokisanGames I think this working nicely now. Could you please review and let me have your feedback?

@TokisanGames
Copy link
Copy Markdown
Owner

Thanks, I will look when I can. I am a bit behind.

You could add edit the collision document, and document the new functions in the XML (generate it then edit it) if you like.

Merge branch 'TokisanGames:main' into Instance-collision

Never merge. Only rebase. See the contributing doc.

@aidandavey
Copy link
Copy Markdown
Contributor Author

Thanks, I will look when I can. I am a bit behind.

You could add edit the collision document, and document the new functions in the XML (generate it then edit it) if you like.

Merge branch 'TokisanGames:main' into Instance-collision

Never merge. Only rebase. See the contributing doc.

Thanks and no worries, I actually dreamed an improvement last night so I need another day or so to try it out.

I'll also take a look into editing the docs and sorry for the merge.

@TokisanGames
Copy link
Copy Markdown
Owner

As the user traverses the instancer cells and they leave behind 10 instances of TreeA and approach 10 instances of TreeA, it would be more efficient to just move the transforms, and store any remaining in an unused cache rather than delete and recreate them every time. The latter is slower, will likely fragment memory, and probably run into any limits faster. The former is what the terrain collision does. It builds a fixed number of shapes at initialization, then never creates nor destroys any until shutdown.

Merge branch 'TokisanGames:main' into Instance-collision
Revert "Merge branch 'TokisanGames:main' into Instance-collision

This also isn't what we want. Do an interactive rebase, drop both of these commits. Rebase your PR, and force push.

@aidandavey
Copy link
Copy Markdown
Contributor Author

As the user traverses the instancer cells and they leave behind 10 instances of TreeA and approach 10 instances of TreeA, it would be more efficient to just move the transforms, and store any remaining in an unused cache rather than delete and recreate them every time. The latter is slower, will likely fragment memory, and probably run into any limits faster. The former is what the terrain collision does. It builds a fixed number of shapes at initialization, then never creates nor destroys any until shutdown.

Merge branch 'TokisanGames:main' into Instance-collision
Revert "Merge branch 'TokisanGames:main' into Instance-collision

This also isn't what we want. Do an interactive rebase, drop both of these commits. Rebase your PR, and force push.

Yes I saw the fixed number of shapes in the terrain collision which is great when there is a fixed number of shapes of the same type.

I can manage active and inactive shapes for each shape type, but how many to create of each?

What do you think about creating shapes only when there are no unused ones?

@TokisanGames
Copy link
Copy Markdown
Owner

What do you think about creating shapes only when there are no unused ones?

Yes, and in the cleanup, save 10 and delete the rest. Or, we have a fixed cell size and a calculated mesh density. It could be a number based on density, clamped between 5-20.

Or, if the camera has moved to a new cell block, triggering an update, we know how many are needed in the new blocks, and how many are extra in the old blocks. You could just move all of those, create as needed, free the rest, store none.

The latter is probably best. In OOTA, we have some 60+ meshes and growing. We don't need 600 unused shapes hanging around. The same types of foliage grow together in an area, so in this scenario, it doesn't store unnecessarily, and the reuse count is high, and it gradually changes retained types as the planted foliage changes.

@TokisanGames
Copy link
Copy Markdown
Owner

This PR still isn't rebased properly. None of these commits should be in your branch.

{1A141DDB-0842-454C-9916-F78B82C8BC91}

You should never use git merge, nor sync on github desktop as detailed in the contributing guide. The Files changed tab in your PR shows a lot of files and changes that are not part of your work.

  • Read the link above and the linked Godot PR workflow.
  • Backup the files you're working on.
  • Make a new branch called instance-collision-back, for a backup, then return to the instance-collision branch for this work.
  • Do an interactive rebase:
    git rebase -i HEAD~25 where 25 is the number commits in your branch.
    Your editor opens with the list of commits like,
pick abcedef Some commit desc 1
pick defb233 Another desc
pick ab2f444 Yet another
  • Find the commits that are not yours, confirm with this list, and look at each one on the commit tab in your PR. Change pick to drop.
  • Save and quit the editor, and address any conflicts or complaints it has, until git status reports you're back to normal - not in the middle of a rebase.
  • Run a diff of your modified files against your backups and confirm they are the same. If not, fix them with a new commit.
  • Run git diff main to compare your current branch against your local main. Confirm this has all of, and only the changes you want.
  • git push -f to force push your branch into this PR, and review the Commit and Files tabs to make sure you don't have extra commits and changes not made by you.
  • Properly rebase off of upstream main: git pull --rebase upstream main
  • Do another interactive rebase and compare against your Commit tab. Change pick to fixup on commits that don't need to be separate and it will roll them into the previous commit. Generally, you should have one commit per:
    • feature
    • fix for changes prior to this PR
    • demo changes
    • doc changes
  • Commits that fix your previous commits in this PR should be squashed.
  • For now though, keep the first 4 intact and separate, which have the code for the non-physics server node creation.
    You can do the interactive rebase multiple times, so take it slow, and do a little bit at a time, and check your work.
  • When finished, force push again and review your Commit and Files tabs.
  • Remove your backup up files and branch.

Watch this.
https://www.youtube.com/live/Kel3ZQDx7kM?si=34KdMIJj-j9vB26A

@TokisanGames TokisanGames marked this pull request as draft June 2, 2025 10:18
@aidandavey aidandavey force-pushed the Instance-collision branch 16 times, most recently from d751a7b to 86e32b0 Compare June 4, 2025 07:50
@TokisanGames
Copy link
Copy Markdown
Owner

This PR has a build script and a godot-cpp that it should not have. That's probably why android is failling.

@aidandavey aidandavey force-pushed the Instance-collision branch 6 times, most recently from 6c74762 to d19a6da Compare December 15, 2025 23:00
@aidandavey aidandavey marked this pull request as ready for review December 15, 2025 23:00
@aidandavey aidandavey changed the title WIP: Instance collision Instance collision Dec 15, 2025
@aidandavey
Copy link
Copy Markdown
Contributor Author

OK I'm finally happy with this

@TokisanGames TokisanGames moved this from In Progress to 1.2 in Terrain3D Roadmap Dec 19, 2025
@TokisanGames TokisanGames modified the milestones: 1.1, 1.2 Dec 20, 2025
@aidandavey aidandavey marked this pull request as draft December 20, 2025 20:28
aidandavey and others added 12 commits March 3, 2026 14:14
(cherry picked from commit 8bd8728)
(cherry picked from commit f7316bc)
Squashed commit:

[f8062bd] Rename visual -> debug_meshes

[80d7fcb] Fix dissapearing debug_meshes

[aac0b9c] Fix collision is persistant on changing scenes (+2 squashed commit)

Squashed commit:

[3afa186] Update logging message

[620c5fa] Do not update visual instance where none exists (+3 squashed commit)

Squashed commit:

[bdca4d8] Rebuild collision on MMI updates (sync MMIs with instance collision)

[b0a1716] Add consts, remove whitespace

[5a3c11f] add more consts (+3 squashed commit)

Squashed commit:

[303d111] Fix instancer/collision debug shapes race

[493568e] Cleanup terrain_3d_collision.cpp

[1a95405] Fix distance_to always == 0.0 (+3 squashed commit)

Squashed commit:

[db1782a] improve instance collision

[2a1af32] update terrain_3d_collision.cpp

[2ae3a9e] Build from cell audit and shape recycling

(cherry picked from commit 44858e3)
(cherry picked from commit 23cdbb2)
- This would previously load 32x32 cells per region, and
  would attempt to load regions that don't exist if region
  size was smaller than 32x32 cells. This also loads faster
  now as a result.
Squashed commit:

[86d0e7e] Separate instance collision modes and other improvements

[d47e74c] Do not clear debug mesh array before queued updates are processed

[f4e5ab9] Fix distance check XOR error, don't pass native types as pointers and pass in radius as a param
- Track cell locations as terrain-relative cell positions,
  similar to how region locations are stored
- Restructure instance data dictionary to reduce memory
  usage for transforms
- Optimize some calculations related to cell positions
…(+2 squashed commit)

Squashed commit:

[07f467b] Instance collision set_is_dirty() default value = true
@aidandavey aidandavey force-pushed the Instance-collision branch from 1cbc4cb to 467ffda Compare March 3, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request important High priority

Projects

Development

Successfully merging this pull request may close these issues.

6 participants