Skip to content

Commit

Permalink
Fix multiple issues with one-way collisions
Browse files Browse the repository at this point in the history
For RigidBodies, uses the collision normal determined by relative motion
to determine whether or not a one-way collision has occurred.

For KinematicBodies, performs additional checks to ensure a one-way
collision has occurred, and averages the recovery step over all collision
shapes.

Co-authored-by:    Sergej Gureev <[email protected]>
  • Loading branch information
madmiraal and Sergej Gureev committed Jan 7, 2021
1 parent a18df71 commit 1061bb3
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 60 deletions.
4 changes: 2 additions & 2 deletions servers/physics/space_sw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ int SpaceSW::test_body_ray_separation(BodySW *p_body, const Transform &p_transfo
Vector3 a = sr[k * 2 + 0];
Vector3 b = sr[k * 2 + 1];

recover_motion += (b - a) * 0.4;
recover_motion += (b - a) / cbk.amount;

float depth = a.distance_to(b);
if (depth > result.collision_depth) {
Expand Down Expand Up @@ -791,7 +791,7 @@ bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Ve

Vector3 a = sr[i * 2 + 0];
Vector3 b = sr[i * 2 + 1];
recover_motion += (b - a) * 0.4;
recover_motion += (b - a) / cbk.amount;
}

if (recover_motion == Vector3()) {
Expand Down
39 changes: 16 additions & 23 deletions servers/physics_2d/body_pair_2d_sw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,19 +295,15 @@ bool BodyPair2DSW::setup(real_t p_step) {
if (A->is_shape_set_as_one_way_collision(shape_A)) {
Vector2 direction = xform_A.get_axis(1).normalized();
bool valid = false;
if (B->get_linear_velocity().dot(direction) >= 0) {
for (int i = 0; i < contact_count; i++) {
Contact &c = contacts[i];
if (!c.reused)
continue;
if (c.normal.dot(direction) > 0) //greater (normal inverted)
continue;

valid = true;
break;
}
for (int i = 0; i < contact_count; i++) {
Contact &c = contacts[i];
if (!c.reused)
continue;
if (c.normal.dot(direction) > -CMP_EPSILON) //greater (normal inverted)
continue;
valid = true;
break;
}

if (!valid) {
collided = false;
oneway_disabled = true;
Expand All @@ -318,17 +314,14 @@ bool BodyPair2DSW::setup(real_t p_step) {
if (B->is_shape_set_as_one_way_collision(shape_B)) {
Vector2 direction = xform_B.get_axis(1).normalized();
bool valid = false;
if (A->get_linear_velocity().dot(direction) >= 0) {
for (int i = 0; i < contact_count; i++) {
Contact &c = contacts[i];
if (!c.reused)
continue;
if (c.normal.dot(direction) < 0) //less (normal ok)
continue;

valid = true;
break;
}
for (int i = 0; i < contact_count; i++) {
Contact &c = contacts[i];
if (!c.reused)
continue;
if (c.normal.dot(direction) < CMP_EPSILON) //less (normal ok)
continue;
valid = true;
break;
}
if (!valid) {
collided = false;
Expand Down
32 changes: 13 additions & 19 deletions servers/physics_2d/physics_2d_server_sw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,19 @@ void Physics2DServerSW::_shape_col_cbk(const Vector2 &p_point_A, const Vector2 &
if (cbk->max == 0)
return;

Vector2 rel_dir = (p_point_A - p_point_B);
real_t rel_length2 = rel_dir.length_squared();
if (cbk->valid_dir != Vector2()) {
if (p_point_A.distance_squared_to(p_point_B) > cbk->valid_depth * cbk->valid_depth) {
cbk->invalid_by_dir++;
return;
}
Vector2 rel_dir = (p_point_A - p_point_B).normalized();

if (cbk->valid_dir.dot(rel_dir) < Math_SQRT12) { //sqrt(2)/2.0 - 45 degrees
cbk->invalid_by_dir++;

/*
print_line("A: "+p_point_A);
print_line("B: "+p_point_B);
print_line("discard too angled "+rtos(cbk->valid_dir.dot((p_point_A-p_point_B))));
print_line("resnorm: "+(p_point_A-p_point_B).normalized());
print_line("distance: "+rtos(p_point_A.distance_to(p_point_B)));
*/
return;
if (cbk->valid_depth < 10e20) {
if (rel_length2 > cbk->valid_depth * cbk->valid_depth ||
(rel_length2 > CMP_EPSILON && cbk->valid_dir.dot(rel_dir.normalized()) < CMP_EPSILON)) {
cbk->invalid_by_dir++;
return;
}
} else {
if (rel_length2 > 0 && cbk->valid_dir.dot(rel_dir.normalized()) < CMP_EPSILON) {
return;
}
}
}

Expand All @@ -201,8 +196,7 @@ void Physics2DServerSW::_shape_col_cbk(const Vector2 &p_point_A, const Vector2 &
}
}

real_t d = p_point_A.distance_squared_to(p_point_B);
if (d < min_depth)
if (rel_length2 < min_depth)
return;
cbk->ptr[min_depth_idx * 2 + 0] = p_point_A;
cbk->ptr[min_depth_idx * 2 + 1] = p_point_B;
Expand Down
28 changes: 12 additions & 16 deletions servers/physics_2d/space_2d_sw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,24 +370,24 @@ struct _RestCallbackData2D {
Vector2 best_normal;
real_t best_len;
Vector2 valid_dir;
real_t valid_depth;
real_t min_allowed_depth;
};

static void _rest_cbk_result(const Vector2 &p_point_A, const Vector2 &p_point_B, void *p_userdata) {

_RestCallbackData2D *rd = (_RestCallbackData2D *)p_userdata;

if (rd->valid_dir != Vector2()) {
if (p_point_A.distance_squared_to(p_point_B) > rd->valid_depth * rd->valid_depth)
return;
if (rd->valid_dir.dot((p_point_A - p_point_B).normalized()) < Math_PI * 0.25)
return;
}

Vector2 contact_rel = p_point_B - p_point_A;
real_t len = contact_rel.length();

if (len == 0)
return;

Vector2 normal = contact_rel / len;

if (rd->valid_dir != Vector2() && rd->valid_dir.dot(normal) > -CMP_EPSILON)
return;

if (len < rd->min_allowed_depth)
return;

Expand All @@ -396,7 +396,7 @@ static void _rest_cbk_result(const Vector2 &p_point_A, const Vector2 &p_point_B,

rd->best_len = len;
rd->best_contact = p_point_B;
rd->best_normal = contact_rel / len;
rd->best_normal = normal;
rd->best_object = rd->object;
rd->best_shape = rd->shape;
rd->best_local_shape = rd->local_shape;
Expand Down Expand Up @@ -431,7 +431,6 @@ bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Transform2D &p_sh
continue;

rcd.valid_dir = Vector2();
rcd.valid_depth = 0;
rcd.object = col_obj;
rcd.shape = shape_idx;
rcd.local_shape = 0;
Expand Down Expand Up @@ -638,7 +637,7 @@ int Space2DSW::test_body_ray_separation(Body2DSW *p_body, const Transform2D &p_t
Vector2 a = sr[k * 2 + 0];
Vector2 b = sr[k * 2 + 1];

recover_motion += (b - a) * 0.4;
recover_motion += (b - a) / cbk.amount;

float depth = a.distance_to(b);
if (depth > result.collision_depth) {
Expand Down Expand Up @@ -849,7 +848,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co

Vector2 a = sr[i * 2 + 0];
Vector2 b = sr[i * 2 + 1];
recover_motion += (b - a) * 0.4;
recover_motion += (b - a) / cbk.amount;
}

if (recover_motion == Vector2()) {
Expand Down Expand Up @@ -1010,7 +1009,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
best_shape = -1; //no best shape with cast, reset to -1
}

{
if (safe < 1) {

//it collided, let's get the rest info in unsafe advance
Transform2D ugt = body_transform;
Expand Down Expand Up @@ -1070,12 +1069,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);

if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {

rcd.valid_dir = col_obj_shape_xform.get_axis(1).normalized();
rcd.valid_depth = 10e20;
} else {
rcd.valid_dir = Vector2();
rcd.valid_depth = 0;
}

rcd.object = col_obj;
Expand Down

0 comments on commit 1061bb3

Please sign in to comment.