Browse Source

Merge pull request #47266 from nekomatata/solver-kinematic-bug-fix-3.4

[3.4] Fix GodotPhysics solver with kinematic body set to report contacts
Rémi Verschelde 4 years ago
parent
commit
d5a0b2e8bf
2 changed files with 38 additions and 24 deletions
  1. 16 4
      servers/physics/body_pair_sw.cpp
  2. 22 20
      servers/physics_2d/body_pair_2d_sw.cpp

+ 16 - 4
servers/physics/body_pair_sw.cpp

@@ -211,11 +211,21 @@ real_t combine_friction(BodySW *A, BodySW *B) {
 
 
 bool BodyPairSW::setup(real_t p_step) {
 bool BodyPairSW::setup(real_t p_step) {
 	//cannot collide
 	//cannot collide
-	if (!A->test_collision_mask(B) || A->has_exception(B->get_self()) || B->has_exception(A->get_self()) || (A->get_mode() <= PhysicsServer::BODY_MODE_KINEMATIC && B->get_mode() <= PhysicsServer::BODY_MODE_KINEMATIC && A->get_max_contacts_reported() == 0 && B->get_max_contacts_reported() == 0)) {
+	if (!A->test_collision_mask(B) || A->has_exception(B->get_self()) || B->has_exception(A->get_self())) {
 		collided = false;
 		collided = false;
 		return false;
 		return false;
 	}
 	}
 
 
+	bool report_contacts_only = false;
+	if ((A->get_mode() <= PhysicsServer::BODY_MODE_KINEMATIC) && (B->get_mode() <= PhysicsServer::BODY_MODE_KINEMATIC)) {
+		if ((A->get_max_contacts_reported() > 0) || (B->get_max_contacts_reported() > 0)) {
+			report_contacts_only = true;
+		} else {
+			collided = false;
+			return false;
+		}
+	}
+
 	offset_B = B->get_transform().get_origin() - A->get_transform().get_origin();
 	offset_B = B->get_transform().get_origin() - A->get_transform().get_origin();
 
 
 	validate_contacts();
 	validate_contacts();
@@ -274,12 +284,9 @@ bool BodyPairSW::setup(real_t p_step) {
 		real_t depth = c.normal.dot(global_A - global_B);
 		real_t depth = c.normal.dot(global_A - global_B);
 
 
 		if (depth <= 0) {
 		if (depth <= 0) {
-			c.active = false;
 			continue;
 			continue;
 		}
 		}
 
 
-		c.active = true;
-
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 
 
 		if (space->is_debugging_contacts()) {
 		if (space->is_debugging_contacts()) {
@@ -303,6 +310,11 @@ bool BodyPairSW::setup(real_t p_step) {
 			B->add_contact(global_B, c.normal, depth, shape_B, global_A, shape_A, A->get_instance_id(), A->get_self(), crB);
 			B->add_contact(global_B, c.normal, depth, shape_B, global_A, shape_A, A->get_instance_id(), A->get_self(), crB);
 		}
 		}
 
 
+		if (report_contacts_only) {
+			collided = false;
+			continue;
+		}
+
 		c.active = true;
 		c.active = true;
 
 
 		// Precompute normal mass, tangent mass, and bias.
 		// Precompute normal mass, tangent mass, and bias.

+ 22 - 20
servers/physics_2d/body_pair_2d_sw.cpp

@@ -221,11 +221,21 @@ real_t combine_friction(Body2DSW *A, Body2DSW *B) {
 
 
 bool BodyPair2DSW::setup(real_t p_step) {
 bool BodyPair2DSW::setup(real_t p_step) {
 	//cannot collide
 	//cannot collide
-	if (!A->test_collision_mask(B) || A->has_exception(B->get_self()) || B->has_exception(A->get_self()) || (A->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC && B->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC && A->get_max_contacts_reported() == 0 && B->get_max_contacts_reported() == 0)) {
+	if (!A->test_collision_mask(B) || A->has_exception(B->get_self()) || B->has_exception(A->get_self())) {
 		collided = false;
 		collided = false;
 		return false;
 		return false;
 	}
 	}
 
 
+	bool report_contacts_only = false;
+	if ((A->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC) && (B->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC)) {
+		if ((A->get_max_contacts_reported() > 0) || (B->get_max_contacts_reported() > 0)) {
+			report_contacts_only = true;
+		} else {
+			collided = false;
+			return false;
+		}
+	}
+
 	//use local A coordinates to avoid numerical issues on collision detection
 	//use local A coordinates to avoid numerical issues on collision detection
 	offset_B = B->get_transform().get_origin() - A->get_transform().get_origin();
 	offset_B = B->get_transform().get_origin() - A->get_transform().get_origin();
 
 
@@ -344,6 +354,7 @@ bool BodyPair2DSW::setup(real_t p_step) {
 
 
 	for (int i = 0; i < contact_count; i++) {
 	for (int i = 0; i < contact_count; i++) {
 		Contact &c = contacts[i];
 		Contact &c = contacts[i];
+		c.active = false;
 
 
 		Vector2 global_A = xform_Au.xform(c.local_A);
 		Vector2 global_A = xform_Au.xform(c.local_A);
 		Vector2 global_B = xform_Bu.xform(c.local_B);
 		Vector2 global_B = xform_Bu.xform(c.local_B);
@@ -351,45 +362,36 @@ bool BodyPair2DSW::setup(real_t p_step) {
 		real_t depth = c.normal.dot(global_A - global_B);
 		real_t depth = c.normal.dot(global_A - global_B);
 
 
 		if (depth <= 0 || !c.reused) {
 		if (depth <= 0 || !c.reused) {
-			c.active = false;
 			continue;
 			continue;
 		}
 		}
 
 
-		c.active = true;
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 		if (space->is_debugging_contacts()) {
 		if (space->is_debugging_contacts()) {
 			space->add_debug_contact(global_A + offset_A);
 			space->add_debug_contact(global_A + offset_A);
 			space->add_debug_contact(global_B + offset_A);
 			space->add_debug_contact(global_B + offset_A);
 		}
 		}
 #endif
 #endif
-		int gather_A = A->can_report_contacts();
-		int gather_B = B->can_report_contacts();
 
 
 		c.rA = global_A;
 		c.rA = global_A;
 		c.rB = global_B - offset_B;
 		c.rB = global_B - offset_B;
 
 
-		if (gather_A | gather_B) {
-			//Vector2 crB( -B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x );
-
-			global_A += offset_A;
-			global_B += offset_A;
+		if (A->can_report_contacts()) {
+			Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x);
+			A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB + B->get_linear_velocity());
+		}
 
 
-			if (gather_A) {
-				Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x);
-				A->add_contact(global_A, -c.normal, depth, shape_A, global_B, shape_B, B->get_instance_id(), B->get_self(), crB + B->get_linear_velocity());
-			}
-			if (gather_B) {
-				Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x);
-				B->add_contact(global_B, c.normal, depth, shape_B, global_A, shape_A, A->get_instance_id(), A->get_self(), crA + A->get_linear_velocity());
-			}
+		if (B->can_report_contacts()) {
+			Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x);
+			B->add_contact(global_B + offset_A, c.normal, depth, shape_B, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA + A->get_linear_velocity());
 		}
 		}
 
 
-		if ((A->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC && B->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC)) {
-			c.active = false;
+		if (report_contacts_only) {
 			collided = false;
 			collided = false;
 			continue;
 			continue;
 		}
 		}
 
 
+		c.active = true;
+
 		// Precompute normal mass, tangent mass, and bias.
 		// Precompute normal mass, tangent mass, and bias.
 		real_t rnA = c.rA.dot(c.normal);
 		real_t rnA = c.rA.dot(c.normal);
 		real_t rnB = c.rB.dot(c.normal);
 		real_t rnB = c.rB.dot(c.normal);