godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.16k stars 97 forks source link

Add collision detection / raycast to MultiMeshInstance3D #10828

Open KaijuKoder2 opened 1 month ago

KaijuKoder2 commented 1 month ago

Describe the project you are working on

A game where the environment is largely made up of MultiMeshInstance3Ds. Mine is a large 3D terrain landscape made of tiles, though it could equally apply to a dungeon or a city: Anything where the scene is large and has many repeated objects: ideal for multi mesh instances.

Describe the problem or limitation you are having in your project

Want the player to be able to click on a spot in that scene and know where they clicked. Say so the player's character to walk to that spot. A common enough use case.

But while MultiMeshInstance3Ds give great performance for such 3D environments, unlike MeshInstance3Ds, you can't create a create_trimesh_collision() on a MultiMeshInstance3D, which means there's no easy way to detect raycast/collision. The workarounds are very ugly and inefficient: e.g. You recreate the entire mesh just for collision which defeats the idea of having multimeshes, or you create a collision object for each and every instance in the multimesh - but there could be thousands: See https://forum.godotengine.org/t/solved-how-set-collisionshape-on-multimeshinstance/21064

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Support collisions. Effectively do what Zylann (oh wow! That's @Zlylann! The Godot Terrain guy!) suggests in https://forum.godotengine.org/t/solved-how-set-collisionshape-on-multimeshinstance/21064 but do it within Godot so the user doesn't have to (inefficiently) create and manage a potentially huge number of collision shapes - one per instance, and again - there could be thousands of them! - to achieve the same result.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Two possible solutions.

  1. Minimal API change solution: Add create_trimesh_collision() to MultiMeshInstance3D (or possibly the underlying MultiMesh since that might be reused and the geometry identical) to create mesh from said instances (just rolling the mesh through the instance transforms and combining those). Of course, that's computationally inefficient and may consume a lot of memory - defeating the whole reason we use multi instancing to begin with, though it would reduce the burden on the Godot coder doing a similar thing in Gdscript as Zlylann describes.

  2. More elegant solution suitable for collision raycasting. Analog to Zylann's example script but do it in the Godot Engine: Take the underlying mesh, iterate through the instance transforms, evaluating the raycast against each of those. Trivial memory overhead doing it this way, and if it's used for say user clicks, those are so infrequent the processing overhead is minor. Only when they click.

If this enhancement will not be used often, can it be worked around with a few lines of script?

See Zlylann's suggestion. It can be done, but not in a few lines because you have to manage all those collision shapes, which means a lot more than just a few lines of script. (I've written similar code to handle modifying individual instances whic h requires such tracking and it's several hundred lines long). The above two solutions would provide a much cleaner, efficient, simpler way of doing it.

Is there a reason why this should be core and not an add-on in the asset library?

Requires changes to underlying Godot engine code.

tetrapod00 commented 1 month ago

It's a slightly different use case, but between this and https://github.com/godotengine/godot-proposals/issues/10669 there seems to be real demand for a new MultiMeshInstance3D-like node that has more of the features of individual instances, but with performance and usability gains over using individual nodes.

My proposed design might be something like a MultipleInstance3D, which takes a single mesh and collision shape, and has an array of transforms in its inspector. Internally it does the work of setting up the RenderingServer and PhysicsServer instances/collision shapes. You would be paying the cost (and getting the benefits) of individual instances, like collision detection and culling/LODs (see the other issue). But you would not be paying for the overhead of nodes, and you would not have to manage the boilerplate of setting things up.

I think that the existing MultiMeshInstance3D should not change too much to add extra features, if it would compromise it's primary goal of rendering many meshes all at once.

KaijuKoder2 commented 1 month ago

Agreed @tetrapod00 . MultiMeshInstance3D's are wonderful for creating very large scenes, but its unfortunate that while I can see a forest I create with MultiMeshInstance3D's, I can't interact with it or otherwise work out when I bump into a tree. Same with the 'click where you want to go' use case.

I did find @Zlylann 's suggestions work, but feels weird that I have to create (in my case - I counted)14,000 individual collision shapes to manage collisions with just a very small number of MultiMeshInstance3Ds. An integrated solution as you suggest would be far better.

Thanks for the pointer to @DriverBunny37 's post https://github.com/godotengine/godot-proposals/issues/10669 That's gorgeous and the type of AAA scenery I'd love to see Godot games doing.

DriverBunny37 commented 1 month ago

Hi KaijuKoder2 ,glad to be mentioned. about your proposal i have some other thoughts. i think it's not very performant if MultiMeshInstance3D create thousands colliders. also,it's not a good idea if MultiMeshInstance3D merge all the thousands colliders into a big one collider,that could be a giant mesh with millions vertexs. it's the key point why people not very interested in the proposal.

check out this video, this is a tree renderer for Unity,what it does is only generating the colliders close to player.

https://www.youtube.com/watch?v=wTd3amF9oi8

i also did something similar to this in Unity last year and has proved it is a very performant way in my game.

https://github.com/user-attachments/assets/6aa210de-783a-4ee8-9f40-003a983fa91c

so i think the workable way is create a script to collect all the collider data from the MultiMeshInstance3D and split the data into hundreds small patches then check the player's distance to these patches at runtime to decide which patch needs to generate colliders.

KaijuKoder2 commented 2 weeks ago

@DriverBunny37: so i think the workable way is create a script to collect all the collider data from the MultiMeshInstance3D and split the data into hundreds small patches then check the player's distance to these patches at runtime to decide which patch needs to generate colliders.

Hi @DriverBunny37. With a point and click interface (click on the map to where you want a unit to go) you need all the colliders in memory at once.

@DriverBunny37: i think it's not very performant if MultiMeshInstance3D create thousands colliders. also,it's not a good idea if MultiMeshInstance3D merge all the thousands colliders into a big one collider, that could be a giant mesh with millions vertexs. it's the key point why people not very interested in the proposal.

If your game needs to detect collisions, something needs to add the colliders. If MultiMeshInstance3D doesn't help the coder to do it, then the coder must create and add them one-by-one anyway. So either way, the program ends up with collision data structures.

I ended up doing the latter - since it's the only way to do it at the moment anyway. However even though I manually created thousands of colliders, the performance was still very good. Perhaps modern PCs are so fast that even checking thousands of colliders isn't a big deal or Godot is already doing something under the hood to manage this efficiently. I'll see if performance holds up as I add more mobs to the game, and they call that code more often as they move over the terrain.