godot icon indicating copy to clipboard operation
godot copied to clipboard

Improper collision detection at edges of trimesh collision shapes

Open Gamepro5 opened this issue 3 years ago • 9 comments

Godot version

4.0 Beta 6

System information

Windows 10; Intel(R) Core(TM) i7-10875H CPU @ 2.30GHz 2.30 GHz

Issue description

Collision shapes have do not properly detect the collision normal of things they collide with when the collision occurred along an "edge". The edge is the line that connects two faces. The attached sample project shows the most extreme example of this (where the shape literally passes straight through because of the improper normal detection, but this has implications for all projects with all shapes. For example, when walking up a slope that is this shape, when the bottom of the corner of my square collision hull touches the line, the normals are briefly different. This isn't a huge issue for my character controller since it uses several move_and_collides() and the shape is much larger than the pill in this example, but it still causes a quick hiccup of velocity since the player thinks they are hitting a wall for a brief second while touching this line.

https://user-images.githubusercontent.com/23440295/203867419-f7a5f2dd-88e1-4896-aa90-7555871e6964.mp4

Increasing the physics tick rate to 240 solves this issue. It's a sloppy solution since the people don't advise you increase physics FPS that much.

Steps to reproduce

  1. Download the project
  2. Enable visible collision shapes
  3. Click play
  4. Watch as the middle pill projectile falls straight through the floor.

(Remember that this is just an easy to demonstrate example. This happens with all shapes and all physics objects. move_and_collide() itself doesn't return the proper surface normals. That's just chaos waiting to happen...)

Minimal reproduction project

FailedCollisionAtEdge.zip

Gamepro5 avatar Nov 24 '22 21:11 Gamepro5

Thanks for the clear minimal reproduction project.

The flow of the physics code is the following: the physics simulation GodotStep3D::step calls GodotBodyPair3D::setup for the (capsule, floor) pair, which calls GodotCollisionSolver3D::solve_static to check for a collision and determine the contact points, which calls the special case GodotCollisionSolver3D::solve_concave, which ends up calling GodotCollisionSolver3D::concave_callback, which finally calls the SAT collision solver sat_calculate_penetration, which calls _collision_capsule_face to determine the best separating axis, and generates contacts based on that.

Now with the setup in the minimal reproduction project, the physics tick rate and the speed of the rigid body capsule are such that the first time a collision is detected, the capsule is already so far through the face that the cylinder part of the capsule is intersecting with the face, and the SAT collision solver determines that the best separating axis is horizontal (i.e. the easiest way to push the capsule out of the triangle is by pushing it further toward the edge); this leads to a horizontal contact normal and causes a horizontal impulse to be applied to the rigid body, which does nothing to keep the body from going through the floor, and so it continues to pass through the floor.

Increasing the physics tick rate causes the first collision to be detected earlier, when the lower ball part of the capsule is intersecting with the face, and the SAT collision solver determines that the best separating axis is vertical (i.e. the easiest way to push the capsule out of the triangle is by pushing it back up); this leads to a vertical contact normal, hence a vertical upward impulse on the rigid body, pushing it out of the floor.

Likewise, moving the capsule further away from the triangle edge makes it so that the the best separating axis is again vertical rather than horizontal, again causing the correct vertical upward impulse on the rigid body.

The way to avoid this problem is to enable continuous collision detection (continuous_cd) on the RigidBody3D. This information should be added to the documentation of RigidBody3D and ConcavePolygonShape3D, and we should check if there remain any issues when continuous_cd = true.

rburing avatar Nov 26 '22 13:11 rburing

@rburing Does the CCD work at last? The issue claiming it's broken has been open for years...

Zireael07 avatar Nov 26 '22 14:11 Zireael07

@Zireael07 It does work in this case, i.e. when enabled in the above minimal reproduction project. Enabling it should be the correct solution to this issue. CCD seems to have been fixed to some extent in https://github.com/godotengine/godot/pull/55773.

rburing avatar Nov 26 '22 14:11 rburing

@rburing I also noticed that decreasing the frame rate to a low value like 10 makes it clip through regardless of the proximity to the face edge. This may or may not be related, though. Also, shouldn't continuous_cd be enabled by default? Are there downsides to using it?

The issue also happens with character bodies, but those don't have the continuous_cd value. What do I do then?

EDIT: Nevermind, I just tested it with continuous_cd on the rigidbody and the issue still occurs, just not in this sample project on that shape. It might be because my other staticbody mesh is scaled evenly. It appears that scaling evenly still has problems.

Gamepro5 avatar Nov 26 '22 22:11 Gamepro5

Tunneling at low FPS (regardless of proximity to the edge) is also expected, because at high enough speed the capsule won't even intersect the face during any of the (few) physics frames. The continuous_cd option is disabled by default because it has a performance cost, though we could consider changing it for usability reasons.

Could you create a modification of the minimal project, where it tunnels even with continuous_cd = true (and preferably without scaling bodies), and attach it to a comment? Then I can debug the continuous collision detection.

rburing avatar Nov 26 '22 23:11 rburing

@rburing, The shape I observed it on needs to be scaled proportionally because it's simply too small for godot to handle reasonably. Here is an updated sample project with continuous_cd.

Characterbody doesn't have continuous_cd, so even if this fix did work, I don't know how it would apply to characterbodies.

Once again, this issue is fixed by cranking up the physics tick rate.

project.zip

Gamepro5 avatar Nov 27 '22 01:11 Gamepro5

@Gamepro5 Generating a trimesh collision shape for the unscaled mesh and scaling it up (especially 200x as in your case) probably creates artifacts like seams between the faces (it would be good to confirm this). So uniformly scaling a static body with a trimesh collision shape should probably also be avoided in general (instead prepare the mesh at the right scale, or apply the scale on import), and we should document this.

With the OBJ file selected you can go to the Import tab (next to the Scene tab), set the scale to 200 on all axes, and press Reimport. Then you can set the scale of the static body back to 1 on all axes, delete the old collision shape, and generate a new trimesh collision shape. This fixes the tunneling issue.

Taking the project and replacing the rigid bodies by character bodies does not result in any tunneling, so that case needs a separate minimal reproduction project. For character bodies I don't think we have any CCD built in; one could try a custom implementation in the character controller and/or make a proposal for a general-purpose API (though I'm not sure it would be a very common use case).

rburing avatar Nov 27 '22 12:11 rburing

@rburing how do other engines handle scaling physics objects? It seems like so many problems with Godot physics could be fixed if the team figured out a way to make scaling physics shapes not buggy.

You are right, though. Re-importing it with a better scale value instead of changing the real scale value does fix the issue. As for the characterbody3d, I'll add a project to this bug report that shows the improper collision detection at edges.

Gamepro5 avatar Nov 27 '22 21:11 Gamepro5

@Gamepro5 Most engines forbid scaling physics objects just like Godot does

Zireael07 avatar Nov 28 '22 07:11 Zireael07

https://user-images.githubusercontent.com/55007230/207264055-ea7dd749-7efe-4c5d-9f49-ad9a3f15ca31.mp4

this issue of the character body getting stuck also seems to happen more the steeper the slope or if you move slowly to the edge of a face

TuxTheAstronaut avatar Dec 13 '22 08:12 TuxTheAstronaut

using a cylinder can prevents this from happening as often but still happens often enough to be noticeable

TuxTheAstronaut avatar Dec 13 '22 08:12 TuxTheAstronaut