Hoimar / Planet-Generator

A procedural planet generator addon for Godot with terrain LOD.
Other
209 stars 23 forks source link

Planetary collisions #1

Open creative-brain opened 3 years ago

creative-brain commented 3 years ago

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?

Hoimar commented 3 years ago

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.

creative-brain commented 3 years ago

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.

Hoimar commented 3 years ago

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 100100=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.

creative-brain commented 3 years ago

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! ;)

Hoimar commented 3 years ago

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.

creative-brain commented 3 years ago

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.

Hoimar commented 3 years ago

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.

creative-brain commented 3 years ago

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 ;)

Hoimar commented 3 years ago

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.

creative-brain commented 3 years ago

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.

Hoimar commented 3 years ago

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.

creative-brain commented 3 years ago

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.

Hoimar commented 3 years ago

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 commented 3 years ago

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.

creative-brain commented 3 years ago

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

Hoimar commented 3 years ago

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:

Hoimar commented 3 years ago

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.

creative-brain commented 3 years ago

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...

Hoimar commented 3 years ago

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.

Valinakova commented 3 years ago

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.

Hoimar commented 3 years ago

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!

creative-brain commented 3 years ago

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.

Hoimar commented 3 years ago

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.

Susihiisi commented 2 years ago

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.

Hoimar commented 1 year ago

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.

VecterraSoft commented 11 months ago

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?

Hoimar commented 11 months ago

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?

VecterraSoft commented 10 months ago

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 commented 9 months ago

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

VecterraSoft commented 9 months ago

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 commented 9 months ago

Hi again guys, just thought id share the progress im doing with my planetary rts game. image_2024-02-12_105343921

So i got physics working and units wondering around. I ran into a problem with generating the collision shape when the terrain patches were re-generating, the game would crash. So the work around i did was to just have1 LOD (6 patches) for for the entire planet at a detail level of 64 or 128, depending on size. It seems to work great like that, no stutter on mobile devices. If anyone has any insight into that crash it would be great! I can have 350 npcs all going around at around 40fps on mobile devices, i can still tweak more performance out of it. Also i was wondering how could i go about editing the vertices of the terrain to just raise or lower it at run time. I want the player the ability to change the terrain so that they can create their own maps, or maybe a missel projectile can cause it to deform when hitting the planet. Is that at all feasible? maybe edit the vertArray right before it generates the patch? Not too good with math :(

Thanks again for all the work you put into this project, will def be putting all contributors names in the credits!

Hoimar commented 9 months ago

Great to hear you could make collisions work on Godot 4.

If you experience crashes you might want to try performing all operations on the PhysicsServer on the main Godot thread instead of another thread: https://github.com/Hoimar/Planet-Generator/blob/a6626b9d600eebcba909183395e34c319e49369e/addons/hoimar.planetgen/scripts/terrain/terrain_patch.gd#L115 I thought the servers were thread safe but apparently they aren't yet. I ran into similar issues with crashes on Godot 3.x.

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

Apparently Godot supports a flat shading mode when passing flat into the shader.

The normals are calculated here. Each vertex normal is just the average of the edges leading to it:

https://github.com/Hoimar/Planet-Generator/blob/a6626b9d600eebcba909183395e34c319e49369e/addons/hoimar.planetgen/scripts/terrain/terrain_patch.gd#L157

VecterraSoft commented 9 months ago

Regarding the Physics server, i ended up ripping all the physics server stuff from the the code, as i did not understand how it works or even what the physics server is. I imagined it was meant for large scale planets or more complicated stuff and my game is more like a toy. I really only needed the physics to keep the units point down at the planet and once in a while ( every 60 frames more or less depending on the distance to the camera) check if they are floating to push them down to the surface. I added a collision shape to the terrain_patch as a child and the error i get is this:

E 0:00:01:0785 terrain_job.gd:23 @ run(): This function in this node (CollisionShape3D) can only be accessed from either the main thread or a thread group. Use call_deferred() instead. <C++ Error> Condition "!is_readable_from_caller_thread()" is true. Returning: (Transform3D()) <C++ Source> scene/3d/node_3d.cpp:333 @ get_transform()

terrain_job.gd:23 @ run() worker_thread.gd:25 @ work() I have no clue what any of that means. ( Only been using godot for like 2 months, came over from the Libgdx framework) ill read up on how to pass the flat keyword into the shader. Thanks!
Hoimar commented 6 months ago

I have no clue what any of that means. ( Only been using godot for like 2 months, came over from the Libgdx framework)

@VecterraSoft this means that the get_transform() function is being called from a thread which isn't allowed to read it just like that. Probably a change inside Godot's threading which affects how scripts can use threads while interacting with nodes, is my guess from this and the Godot 4 changelogs.

I'll have to look at it in detail, but I'm not sure if I can reproduce it because I'd need a MVCE from you for that.

VecterraSoft commented 6 months ago

I have no clue what any of that means. ( Only been using godot for like 2 months, came over from the Libgdx framework)

@VecterraSoft this means that the get_transform() function is being called from a thread which isn't allowed to read it just like that. Probably a change inside Godot's threading which affects how scripts can use threads while interacting with nodes, is my guess from this and the Godot 4 changelogs.

I'll have to look at it in detail, but I'm not sure if I can reproduce it because I'd need a MVCE from you for that.

OH! i see. Okay so limiting the worker threads to just a single one eliminated all crashes when generating terrain and the physics mesh. Usually when i play around it crashes within a 1 -2 minutes if moving around the planet. Now if i set _num_workers = 1 It has not crashed at all. Thanks!

Also i was able to figure out how you store the verts so i could calculate my own flat normals! Haven't decided if i will stick with this look yet. Thanks again! planetsam

Edit:

For got to ask you. Would it be easy to have the patches saved and just hide them when not in use? To avoid re generating them again. For my small planets its not that many patches. The MAX_TREE_DEPTH is 4. So its probably a total of 380 patches. Patch detail is 16. I want to maybe preload some on start up. Since this game is for mobile devices.