Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement collision in CPUParticles as a more precise (but slower) alternative to GPUParticles collision #3865

Open
Calinou opened this issue Jan 25, 2022 · 5 comments

Comments

@Calinou
Copy link
Member

Calinou commented Jan 25, 2022

Describe the project you are working on

The Godot editor 🙂

Describe the problem or limitation you are having in your project

GPUParticles collision works well, but it requires specially designed nodes to be placed within the level to function. Not only this can take a significant amount of time in complex levels, but the level of precision remains fairly low.

Moreover, since GPUParticles cannot communicate their positions back to the CPU efficiently, it's also impossible to emit a signal on collision with individual particles' positions (or when a particle expires). Emitting a signal on collision allows for many new effects, such as creating meshes, lights or decals at particles' impact points.

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

Implement precise collision for CPUParticles using the physics server. This is meant to be an alternative to GPUParticles when a high level of precision is needed. Due to the higher CPU usage, this will be limited to low amounts of particles (in the few hundreds at most).

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

CPUParticles collision performance is expected to be better than when using nodes to simulate particles (or even when using low-level servers from scripting), but it will still be significantly slower than GPUParticles.

To reduce CPU usage, collision between particles should be disabled by default. Nonetheless, since the physics server makes this possible, there should be a property to enable particle-to-particle collision (which is another feature that can't be efficiently implemented on GPUParticles).

To further reduce CPU usage, CPUParticles simulation could be made to happen at a lower rate with interpolation. Support for this will require something similar to #439.

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

No, as CPUParticles behavior is not extendable from the scripting API.

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

This is core rendering functionality.

@Calinou
Copy link
Member Author

Calinou commented May 22, 2022

I've started work on CPUParticles3D collision: https://github.com/Calinou/godot/tree/cpuparticles-add-collision

However, I get a crash whenever a particle collides with a StaticBody.

Testing project: https://0x0.st/oaO_.zip (will crash as soon as it's opened in the editor, since a particle will collide with the box below it)

Backtrace – Step [11] is the one in the code I added:

================================================================
handle_crash: Program crashed with signal 11
Engine version: Godot Engine v4.0.alpha.custom_build (260d1e5069f022d8cbddd896c90b2a166712a528)
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
[1] /lib64/libc.so.6(+0x42a70) [0x7f73a173ca70] (??:0)
[2] GodotPhysicsServer3D::_shape_col_cbk(Vector3 const&, int, Vector3 const&, int, void*) (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_physics_server_3d.cpp:1738)
[3] _CollectorCallback::call(Vector3 const&, Vector3 const&) (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_collision_solver_3d_sat.cpp:84)
[4] bin/godot.linuxbsd.tools.64.llvm() [0x8093512] (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_collision_solver_3d_sat.cpp:117)
[5] bin/godot.linuxbsd.tools.64.llvm() [0x8093215] (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_collision_solver_3d_sat.cpp:604)
[6] SeparatorAxisTest<GodotSphereShape3D, GodotBoxShape3D, false>::generate_contacts() (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_collision_solver_3d_sat.cpp:741)
[7] bin/godot.linuxbsd.tools.64.llvm() [0x807fe03] (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_collision_solver_3d_sat.cpp:830)
[8] sat_calculate_penetration(GodotShape3D const*, Transform3D const&, GodotShape3D const*, Transform3D const&, void (*)(Vector3 const&, int, Vector3 const&, int, void*), void*, bool, Vector3*, float, float) (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_collision_solver_3d_sat.cpp:2445)
[9] GodotCollisionSolver3D::solve_static(GodotShape3D const*, Transform3D const&, GodotShape3D const*, Transform3D const&, void (*)(Vector3 const&, int, Vector3 const&, int, void*), void*, Vector3*, float, float) (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_collision_solver_3d.cpp:422)
[10] GodotPhysicsDirectSpaceState3D::collide_shape(PhysicsDirectSpaceState3D::ShapeParameters const&, Vector3*, int, int&) (/home/hugo/Documents/Git/godotengine/godot/servers/physics_3d/godot_space_3d.cpp:414)
[11] CPUParticles3D::_particles_process(double) (/home/hugo/Documents/Git/godotengine/godot/scene/3d/cpu_particles_3d.cpp:1157)
[12] CPUParticles3D::_update_internal() (/home/hugo/Documents/Git/godotengine/godot/scene/3d/cpu_particles_3d.cpp:630)
[13] CPUParticles3D::_notification(int) (/home/hugo/Documents/Git/godotengine/godot/scene/3d/cpu_particles_3d.cpp:1313)
[14] CPUParticles3D::_notificationv(int, bool) (/home/hugo/Documents/Git/godotengine/godot/scene/3d/cpu_particles_3d.h:38)
[15] Object::notification(int, bool) (/home/hugo/Documents/Git/godotengine/godot/core/object/object.cpp:847)
[16] SceneTree::_notify_group_pause(StringName const&, int) (/home/hugo/Documents/Git/godotengine/godot/scene/main/scene_tree.cpp:862)
[17] SceneTree::process(double) (/home/hugo/Documents/Git/godotengine/godot/scene/main/scene_tree.cpp:453)
[18] Main::iteration() (/home/hugo/Documents/Git/godotengine/godot/main/main.cpp:2739)
[19] OS_LinuxBSD::run() (/home/hugo/Documents/Git/godotengine/godot/platform/linuxbsd/os_linuxbsd.cpp:441)
[20] bin/godot.linuxbsd.tools.64.llvm(main+0x1c0) [0x4246fb0] (/home/hugo/Documents/Git/godotengine/godot/platform/linuxbsd/godot_linuxbsd.cpp:68)
[21] /lib64/libc.so.6(+0x2d590) [0x7f73a1727590] (??:0)
[22] /lib64/libc.so.6(__libc_start_main+0x89) [0x7f73a1727649] (??:0)
[23] bin/godot.linuxbsd.tools.64.llvm(_start+0x25) [0x4246d25] (??:?)
-- END OF BACKTRACE --
================================================================

@webredraptors
Copy link

Reading through godotengine/godot#61273 it sounds like the reason this stalled was a lack of support for structs in GDScript. Now it seems that structs may not be too far off: #7329. Hopefully work on CPUParticles signals and collisions can resume soon! Would love to have these features in the projects I'm planning.

@Calinou
Copy link
Member Author

Calinou commented Aug 10, 2023

I've continued work on https://github.com/Calinou/godot/tree/cpuparticles-add-collision?rgh-link-date=2022-05-22T00%3A08%3A17Z. It now has feature parity with GPUParticles3D collision, though the Collision Use Scale property isn't implemented yet. Collision masks also haven't been implemented yet, although this should be easy to do.

simplescreenrecorder-2023-08-10_12.58.56.mp4

There's also an issue where particles occasionally sink into the ground due to gravity (despite my best efforts to prevent this), and collisions failing if a single particle hits multiple overlapping boxes at once. I've tried increasing the number of contacts reported by the shapecast, but this didn't fix the issue.

Testing project for the above branch: test_cpuparticles3d_collision.zip

@webredraptors
Copy link

webredraptors commented Aug 10, 2023

Wasn't sure if I should post this here or in godotengine/godot#61273. But thanks for continuing to work on this feature!

I substituted Godot Jolt into a build of your engine changes to try to isolate behavior of your code vs built-in physics engine quirks. Particles appeared to clip through surfaces more frequently with Jolt, so I had a hard time determining if the gravity issue or overlapping shape issue persisted. It seems that particle simulation stops much more often because Jolt is causing more frequent clipping.

However, I did notice a problem with collision that occurs consistently, both in Jolt and Godot Physics. Here I've switched back to Godot Physics and slowed down the time scale to make this more apparent:

unknown_2023.08.10-23.30.mp4

Notice how the particles mostly pass through the underside of the green shape before collision occurs. It almost seems like they are colliding with the back face of the surface. I also noticed some slight clipping with the orange shape, but zero with the blue shape.

Note that the green shape is an exact duplicate of the other shapes with a different material override. In fact, I can reposition the emitter to drop particles on the "top" side of the green shape, and collision behavior changes:

unknown_2023.08.10-23.38.mp4

When I reposition the emitter to the underside of the perfectly horizontal blue shape, particles clip entirely through the surface before collision occurs. From the top side, the opposite is true and no clipping occurs whatsoever:

unknown_2023.08.10-23.52.mp4

I believe this behavior is connected to the individual face normals of collision shapes. This may also be linked to the issues observed with gravity and overlapping boxes. I will play around more with this when I have time.

@Calinou
Copy link
Member Author

Calinou commented Aug 11, 2023

Notice how the particles mostly pass through the underside of the green shape before collision occurs. It almost seems like they are colliding with the back face of the surface. I also noticed some slight clipping with the orange shape, but zero with the blue shape.

This occurs because the raycast destination is moved to account for gravity, so that particles don't sink on the ground. This could be avoided by performing more raycasts, but it would be significantly more expensive to do so (up to 6 raycasts needed per particle, instead of 1).

There's some commented out code you can uncomment here if you want to try this: Calinou/godot@2cda412#diff-778bdd2be9e69834044d9ca57a2341cafd149c69e5d48f1b1a98321e93adb4bcR965-R1008

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants