Planet-Generator icon indicating copy to clipboard operation
Planet-Generator copied to clipboard

Planetary collisions

Open creative-brain opened this issue 4 years ago • 35 comments
trafficstars

Any idea, how to implement fast enough collisions? I've managed to generate a convex collision shape from mesh, but it's slow and inaccurate. I am now trying to add a CollisionShape on runtime using PhysicsServer but with no luck so far?

creative-brain avatar Mar 23 '21 15:03 creative-brain

Hello, thanks for your interest in this project.

I thought about collisions briefly but didn't put too much time into it (yet).

I don't know where the performance hit comes from, did you try to use the Godot profiler to find out what's using up cycles? When you say inaccurate, did you use the generated LOD (subdivided) terrain meshes to generate matching convex collision shapes? Because if you only used e.g. the first, top level terrain mesh, you'd indeed end up with a very inaccurate collision shape which wouldn't match the subdivided meshes.

Hoimar avatar Mar 23 '21 19:03 Hoimar

I simply called mesh.create_trimesh_collision() for each generated face on its creation. The problem is that collision shapes contain too many vertices and therefore the collision calculation has to process a lot of data. The solution would be (I guess) to generate a multiple collision shapes for each face, which would allow for hierarchical search of close collision shapes (i.e. ship colliders and only nearby terrain colliders). Godot has a build-in function for that, unfortunately it not accessible via GDScript. Only from engine code.

So the solution would be either implement a wrapper function into the GDScript to create the access to the method from the source. Or, one could create a c++ module and access it directly in the engine code.

creative-brain avatar Apr 03 '21 19:04 creative-brain

For general, inaccurate collisions, (think planet vs. planet) a sphere shape would suffice.

Well if you create a collision trimesh for every generated chunk, that's a lot of high resolution colliders to check against. If you have a mesh resolution of 100, that's 100*100=10000 vertices or (99^2)*2=19602 triangles to check against for a single terrain patch!

Also, you'll have to disable the colliders of invisible lower LOD meshes or you'll collide with those as well, which might be part of the innaccuracy and performance hit.

So as you can see, a performant terrain collision system has to be thoroughly planned, I've thought about ways to implement it and it's definitely on my list. Help is welcome!

I'll push some big changes in the next days by the way, many parts of the now-addon have been overhauled.

Hoimar avatar Apr 05 '21 08:04 Hoimar

Indeed. Maybe I don't understand the collision system correctly but I did a simple experiment. I set the generation of the collision shape only for the closest planetary face. Then in the editor, I created the ship collision shape using a "single convex collision sibling" made from its mesh and tried a collision of the ship and the planet face. I got barely a single fps. Then again in the editor, I changed it to "multiple convex collision siblings" and I got a dramatic performance gain - if the ship stayed inside the actual terrain face (no generation of the new ones) I got solid 50-60 fps. Therefore, I concluded that the collision detection is only performed between the closest collision shapes (which would make sense). If this is correct then when the faces are generated as multiple collision siblings as well, maybe the performance would increase even more. But I could be wrong of course.

It is possible to use a build-in method for this as I stated in the comment before or maybe it could be done manually based on the generated face vertices that you already have. I also found a hint on the godot forum where they used PhysicsServer to set the faces of the collision shape manually but as I stated in my first comment, I didn't make it work.

But yeah, you can always create the collision shape only for the closest planetary face and leave the global collisions to a single (planet sized) sphere collision shape - that would indeed be sufficient for a planet-planet collision while the player collision with the surface would be accurate enough.

Another way would be to pre-generate both the vertices and collision shapes for each face LOD beforehand and only load those that are currently needed in run-time. That would even speed-up the performance of the whole planet as random generators are notorious performance eaters.

Anyway, I am really looking forward to your planet overhaul! ;)

creative-brain avatar Apr 05 '21 21:04 creative-brain

The engine by default does a broad phase and a narrow phase, it should only check against nearby collision shapes anyway. I'll have to look into it soon.

By the way, heightmap collision shapes just have been implemented for Godot Physics: https://twitter.com/PouleyKetchoup/status/1379603423398535174 Perhaps this could be beneficial? Though the terrain here is a curved mesh, so it won't be that easy.

Hoimar avatar Apr 07 '21 11:04 Hoimar

We can definitelly make some iterative experimentation here and try to find the best solution. I've created a planetary_collisions branch as a playground for this purpose.

creative-brain avatar Apr 07 '21 15:04 creative-brain

So I'm still working on a more robust multithreaded job system for better maintainability and performance. It's kind of functional now, but I'm still debugging some hard to narrow down issues with the threads (multithreading is a beast). After that I'm going to add physics "back in", probably using the PhysicsServer like we talked about because I don't want to generate physics shapes on the main thread.

Hoimar avatar Apr 14 '21 06:04 Hoimar

I was struggling with the collisions lately. Don't have a valid solution the way we discussed it yet. Maybe I'll focus on something else and leave this one on you ;)

creative-brain avatar Apr 14 '21 11:04 creative-brain

Oh, you actually continued to work on it? Which route did you take, PhysicsServer or physics nodes? I think the latter are not multithreading-friendly at all, I couldn't get it to work reliably using threads in my tests.

Hoimar avatar Apr 14 '21 11:04 Hoimar

Well I created a separate scene with StaticBody and Collision shape and instantiated it for each TerrainPatch. Then I tried to inject the mesh data into CollisionShape but it didn't work and as I was in a separate thread I couldn't debug it directly. I did't even got to the PhysicsServer part. But I have a working example of PhysicsServer. I'll send it to you.

creative-brain avatar Apr 14 '21 19:04 creative-brain

Okay. In the old version of terrain_patch.gd, there's a flag for threads on/off. Yeah, creating physics nodes doesn't seem to work in a thread as far as my tests went, so either it would have to be done in a call_deferred() function or using the PhysicsServer, which may also be faster. You may also push to the planet_collisions branch if you want, I've locally worked there but then merged all changes into the master branch, so we should be good.

Hoimar avatar Apr 14 '21 19:04 Hoimar

I created a skeleton of the solution using PhysicsServer but as before, it doesn't seem to work. I pushed the current state to the planet_collisions branch so you can have a look if you want. Maybe you'll see something I missed. If I somehow make it work, I'll let you know.

creative-brain avatar Apr 17 '21 14:04 creative-brain

I had a quick look at the code, it's looking good already, but yeah, apparently there are no collision shapes yet and it throws a lot of physics errors (also had it crash once).

One problem is probably that it's not possible to instantiate and use any physics nodes from another thread as far as my research went. We'll have to use the PhysicsServer for everything, so we wouldn't even instance these nodes but use only the PhysicsServer to add bodies to the physics world.

I also think that we can't use a heightmap collision shape, because the vertices of a terrain patch only look like they're flat, but are in fact warped onto the planet sphere. Also, a heightmap may only be able to face upwards. A trimesh (convex collision shape) should work better, like you did before in your first version of this, it was rudimentary but collisions were there.

By the way, you can either use the uvs array for elevation values, or use the elevations array to pass to the calc_uvs() function :)

Hoimar avatar Apr 17 '21 17:04 Hoimar

Alright, I built upon your work to create a physics shape (concave polygon shape) and body at runtime using PhysicsServer. After working out the quirks, it's running really stable and performant (apart from the occasional crashes that I described in #9). Commit b5d0a22. After the changes proposed in the commit message we could merge planet_collisions into master as for me. I think it's ready after that.

Edit: Oh and also, after you restructured the terrain_patch scene, it was throwing errors when building the mesh about gles3_rasterizer....cpp … surfaces.size() == 0 or something like that. Either way it looked like a timing issue when instancing the MeshInstance as child node instead of working on the instance itself. I don't really know why that happens, only found a few closed Godot issues regarding similar timing issues. So I reverted to the way it was before again.

Hoimar avatar Apr 18 '21 18:04 Hoimar

Brilliant, you made it work! That's great news. I see you set the space, I was going to do that next. Nevertheless, I found another bug with collisions. When you set the planet scale to a higher value (i.e. 650) you can get through the surface. See the screenshot. image

creative-brain avatar Apr 19 '21 10:04 creative-brain

Now that you mention it, I'm not quite sure whether setting the space is actually needed, I didn't test without it yet. Yes, I also got stuck below terrain once when I flew at really high speeds at the usual planet scale (200?), it somehow glitched through the terrain. Multiple things could be the issue here:

  1. the scale of the ship etc. is too small. I'm planning on fixing the scaling soon, everything is extremely tiny right now. Scale should be allowed to be a lot bigger, but the current atmosphere shader doesn't play nicely with that, and the lack of 32bit floating point precision will also become noticeable sooner
  2. CCD (continuous collision detection) doesn't work reliably, there are a few Godot issues about that. Would be noticeable at higher speeds (11km/s, 😄)
  3. the collision margin is too low and may fuel into 1. or 2.
  4. there may be an issue with the collision vertices of the concave polygon shapes (unlikely)

For 1. it may help to just limit the speed at low altitudes / when close to the planet, which makes sense anyway.


Two other pending things are:

  • a toggle for collisions from code (globally) and per planet (planet settings)
  • investigating and fixing performance issues at small planets. The profiler showed that _physics_process of the ship.gd becomes incredibly expensive when colliding against several small, dense meshes, up to 40ms per frame

Hoimar avatar Apr 19 '21 12:04 Hoimar

Okay, I implemented the collision toggles ~~and merged planet_collisions back into master for better maintainability. But I'll leave the branch still to have a space for experiments.~~

Actually, I reverted the merge because performance is not yet stable, also the multithreading crashes from #9 will have to be solved first, then we can properly take care of making physics better.

Hoimar avatar Apr 20 '21 15:04 Hoimar

Right. It's crucial feature. Let's not rush this.

I'm sorry for not helping too much lately. I am really busy these days but as soon I can, I'll participate more often again. Btw look what I found:

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

Maybe we could create another type of planets - Gas giants...

creative-brain avatar Apr 20 '21 21:04 creative-brain

Yes. In the latest commits I was able to fix the crashes that occured before, though I'm not quite sure how to best continue with physics, because these complex trimesh collision shapes tend to really tank performance. Perhaps scaling the planets up to a reasonable level (e.g. 1 GD unit = 1km given the single precision floats) will remediate some of the issues as colliders get less dense. I started to work on that (scaling) first in another branch for now.

I'm sorry for not helping too much lately. I am really busy these days but as soon I can, I'll participate more often again.

Hey that's not a problem at all, I'm not expecting anything but am happy about any help.

Hoimar avatar Apr 26 '21 06:04 Hoimar

Just out of curiosity, are you two programmers who work full time? I'm currently learning to program (I'm a beginner). I will try sharing this project with others who may find it interesting who can contribute to it.

Valinakova avatar Apr 26 '21 08:04 Valinakova

Just out of curiosity, are you two programmers who work full time? I'm currently learning to program (I'm a beginner). I will try sharing this project with others who may find it interesting who can contribute to it.

I work full time, but I'm not a programmer by definition, altough I do it a lot for work.

Thanks for sharing the project, that's great!

Hoimar avatar Apr 26 '21 12:04 Hoimar

Just out of curiosity, are you two programmers who work full time? I'm currently learning to program (I'm a beginner). I will try sharing this project with others who may find it interesting who can contribute to it.

I also work full time and apart from other things I also do some programming.

creative-brain avatar Apr 26 '21 14:04 creative-brain

So I found that PhysicsServer is not completely multithreading-friendly in 3.x, but at least that should be fixed in 4.0: https://github.com/godotengine/godot/pull/45852 In the meantime I'm using a Mutex in terrain_patch.gd to create physics shapes and bodies from multiple threads, which is of course slower but at least it doesn't throw random errors and doesn't crash.

The glitching (collider jumps below terrain trimesh) was also fixed by a higher collision margin, by the way.

Hoimar avatar May 01 '21 13:05 Hoimar

Making collision for whole terrain chunks is quite slow and gets trickier with bigger planets. Fast workaround could be to create collider object which follows the player/enemy/whatmeow, but also sticks to the planet surface and rotates to aproximate slopes as well as it can. Should also ignore all other objects to avoid strange collisions. Creating that for too many objects could of course be performance issue and I don't know what would be the answer for that (bullets for example).

I've used that kind of collider in Unity with Space Graphics Toolkit with realistic sized planets (Earth, Mars) quite successfully. It's not perfect, but it does the job when the planet is big and the slopes aren't too steep or detailed.

//Edit: Space Graphics Toolkit has feature which allows you to stick object to the planet surface and I think it uses heightmap data and noise to do that efficiently. Or whatever data is used to generate the planet. Seems pretty neat as that way you can calculate the position without generating the chunk as they are generated with the same data. It also samples the surrounding (I don't know how) within user specified distance and uses that for rotation, bigger distance gives smoother rotation, but if it's too big you might lose local accuracy. I use that maybe in a bit hacky way by first making the collider object copy the player position and then snappin it to the planet surface, could possibly be made better somehow.

I have no idea how to actually implement any of that, but kind of an idea for a workaround with planetary collisions.

Susihiisi avatar Jul 16 '22 09:07 Susihiisi

Collisions need to be looked at again on 4.x. For now, collisions are disabled on the dev branch.

I'll leave this issue open for that as well.

Hoimar avatar Jul 26 '23 10:07 Hoimar

IMG-20231206-WA0000 Hi guys, i modified the engine so that i can get small SPORE like planets. Not intrested in the realistic sizes. So i wanted to know if there was a a way i can get collisions to work with a small planet. My game will use planets with radius size of 24 max and moons of 12. I read the engine has physics and collisions, how would i get this to work so that i can have my character walk on the planet? (no spaceships in my game). Also, i set it up so that the planets only have two LODs, the 6 patch and the one after it, no more. at a resolution of 32 to hopefully make it easier on the collision mesh. Any ideas?

VecterraSoft avatar Dec 07 '23 14:12 VecterraSoft

Hey, nice to see that you are finding the library useful.

in theory, setting the flag: https://github.com/Hoimar/Planet-Generator/blob/a6626b9d600eebcba909183395e34c319e49369e/addons/hoimar.planetgen/scripts/constants.gd#L3 activates collision meshes to be built and pushed to the physics server. This caused issues with instability and crashes in Godot 3, however.

In practice, I haven't tested this yet on Godot 4.

What did you try so far?

Hoimar avatar Dec 07 '23 18:12 Hoimar

Hey, nice to see that you are finding the library useful.

in theory, setting the flag:

https://github.com/Hoimar/Planet-Generator/blob/a6626b9d600eebcba909183395e34c319e49369e/addons/hoimar.planetgen/scripts/constants.gd#L3

activates collision meshes to be built and pushed to the physics server. This caused issues with instability and crashes in Godot 3, however. In practice, I haven't tested this yet on Godot 4.

What did you try so far?

Hi, thanks for taking the time to reply. I did try enabling the collisions and physics thru that script you mentioned. However any rigidbody3D I placed does not get pulled towards the planet , and even if the rigidbody3d collided with the planet it just falls through. Am I missing something? I added an Area3D and set point gravity to have things pulled towards the area. However I'm not sure that's the right move here since your engine probably requires its own implementation of gravity that is used with your collision system. The game I'm making is an rts type game where units will move along the planet and fight , so it pro a ly does not need to be so accurate if that helps. I must have spent like 5 hours trying to get the collisions to work. Any help is welcomed.

VecterraSoft avatar Jan 06 '24 14:01 VecterraSoft

Also, how can i make the normals on the mesh flat shaded ? like a low poly style type game. Thanks again, great project!

VecterraSoft avatar Feb 05 '24 22:02 VecterraSoft

Okay so I managed to fix my physics collisions by just adding my own static collision shape to the terrain patch scene by pulling the mesh data from the terrain patch mesharray after it generates. It works flawlessly now. Still would appreciate a how to for flat shaded normals. Thanks guys for making this project. It's by far the least bloated of the planet engines and it runs the fastest on mobile devices.

VecterraSoft avatar Feb 10 '24 20:02 VecterraSoft