From bf523a2b2aa958a4539646700f77f9375c4e8fa5 Mon Sep 17 00:00:00 2001 From: PouleyKetchoupp Date: Thu, 16 Sep 2021 18:38:17 -0700 Subject: Improved logic for CharacterBody collision recovery depth Allows 2D character controller to work without applying gravity when touching the ground (also more safely in 3D), and collision detection is more flexible with different safe margin values. Character body motion changes in 2D and 3D: -Recovery only for depth > min contact depth to help with collision detection consistency (rest info could be lost if recovery was too much) -Adaptive min contact depth (based on margin) instead of space parameter Extra CharacterBody changes: -2D: apply changes made in 3D for stop on slope and floor snap that help fixing some jittering cases -3D: fix minor inconsistencies in stop on slope and floor snap logic --- servers/physics_3d/space_3d_sw.cpp | 24 ++++++++++++------------ servers/physics_3d/space_3d_sw.h | 1 - 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'servers/physics_3d') diff --git a/servers/physics_3d/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp index cc4eab1f0b..78c4e30c83 100644 --- a/servers/physics_3d/space_3d_sw.cpp +++ b/servers/physics_3d/space_3d_sw.cpp @@ -34,6 +34,8 @@ #include "core/config/project_settings.h" #include "physics_server_3d_sw.h" +#define TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR 0.05 + _FORCE_INLINE_ static bool _can_collide_with(CollisionObject3DSW *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) { if (!(p_object->get_collision_layer() & p_collision_mask)) { return false; @@ -488,13 +490,15 @@ bool PhysicsDirectSpaceState3DSW::rest_info(RID p_shape, const Transform3D &p_sh Shape3DSW *shape = PhysicsServer3DSW::singletonsw->shape_owner.getornull(p_shape); ERR_FAIL_COND_V(!shape, 0); + real_t min_contact_depth = p_margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR; + AABB aabb = p_shape_xform.xform(shape->get_aabb()); aabb = aabb.grow(p_margin); int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, Space3DSW::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results); _RestCallbackData rcd; - rcd.min_allowed_depth = space->test_motion_min_contact_depth; + rcd.min_allowed_depth = min_contact_depth; for (int i = 0; i < amount; i++) { if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas)) { @@ -658,6 +662,8 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co body_aabb = p_from.xform(p_body->get_inv_transform().xform(body_aabb)); body_aabb = body_aabb.grow(p_margin); + real_t min_contact_depth = p_margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR; + real_t motion_length = p_motion.length(); Vector3 motion_normal = p_motion / motion_length; @@ -711,8 +717,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co break; } - Vector3 recover_motion; + recovered = true; + Vector3 recover_motion; for (int i = 0; i < cbk.amount; i++) { Vector3 a = sr[i * 2 + 0]; Vector3 b = sr[i * 2 + 1]; @@ -723,9 +730,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co // Compute depth on recovered motion. real_t depth = n.dot(a + recover_motion) - d; - if (depth > 0.0) { + if (depth > min_contact_depth + CMP_EPSILON) { // Only recover if there is penetration. - recover_motion -= n * depth * 0.4; + recover_motion -= n * (depth - min_contact_depth) * 0.4; } } @@ -734,8 +741,6 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co break; } - recovered = true; - body_transform.origin += recover_motion; body_aabb.position += recover_motion; @@ -889,7 +894,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co } // Allowed depth can't be lower than motion length, in order to handle contacts at low speed. - rcd.min_allowed_depth = MIN(motion_length, test_motion_min_contact_depth); + rcd.min_allowed_depth = MIN(motion_length, min_contact_depth); int from_shape = best_shape != -1 ? best_shape : 0; int to_shape = best_shape != -1 ? best_shape + 1 : p_body->get_shape_count(); @@ -1158,9 +1163,6 @@ void Space3DSW::set_param(PhysicsServer3D::SpaceParameter p_param, real_t p_valu case PhysicsServer3D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: constraint_bias = p_value; break; - case PhysicsServer3D::SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH: - test_motion_min_contact_depth = p_value; - break; } } @@ -1182,8 +1184,6 @@ real_t Space3DSW::get_param(PhysicsServer3D::SpaceParameter p_param) const { return body_angular_velocity_damp_ratio; case PhysicsServer3D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: return constraint_bias; - case PhysicsServer3D::SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH: - return test_motion_min_contact_depth; } return 0; } diff --git a/servers/physics_3d/space_3d_sw.h b/servers/physics_3d/space_3d_sw.h index fc2a7d304d..daa1244bf8 100644 --- a/servers/physics_3d/space_3d_sw.h +++ b/servers/physics_3d/space_3d_sw.h @@ -96,7 +96,6 @@ private: real_t contact_max_separation = 0.05; real_t contact_max_allowed_penetration = 0.01; real_t constraint_bias = 0.01; - real_t test_motion_min_contact_depth = 0.00001; enum { INTERSECTION_QUERY_MAX = 2048 -- cgit v1.2.3