Browse Source

Fixed bug when a body with limited DOFs collides with static (#840)

If the resulting contact had an infinite effective mass, we would divide by zero and crash.

See: godot-jolt/godot-jolt#734
Jorrit Rouwe 1 year ago
parent
commit
5337b184eb

+ 8 - 2
Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h

@@ -226,9 +226,15 @@ public:
 	template <EMotionType Type1, EMotionType Type2>
 	JPH_INLINE void				TemplatedCalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
 	{
-		mEffectiveMass = 1.0f / TemplatedCalculateInverseEffectiveMass<Type1, Type2>(inInvMass1, inInvI1, inR1PlusU, inInvMass2, inInvI2, inR2, inWorldSpaceAxis);
+		float inv_effective_mass = TemplatedCalculateInverseEffectiveMass<Type1, Type2>(inInvMass1, inInvI1, inR1PlusU, inInvMass2, inInvI2, inR2, inWorldSpaceAxis);
 
-		mSpringPart.CalculateSpringPropertiesWithBias(inBias);
+		if (inv_effective_mass == 0.0f)
+			Deactivate();
+		else
+		{
+			mEffectiveMass = 1.0f / inv_effective_mass;
+			mSpringPart.CalculateSpringPropertiesWithBias(inBias);
+		}
 
 		JPH_DET_LOG("TemplatedCalculateConstraintProperties: invM1: " << inInvMass1 << " invI1: " << inInvI1 << " r1PlusU: " << inR1PlusU << " invM2: " << inInvMass2 << " invI2: " << inInvI2 << " r2: " << inR2 << " bias: " << inBias << " r1PlusUxAxis: " << mR1PlusUxAxis << " r2xAxis: " << mR2xAxis << " invI1_R1PlusUxAxis: " << mInvI1_R1PlusUxAxis << " invI2_R2xAxis: " << mInvI2_R2xAxis << " effectiveMass: " << mEffectiveMass << " totalLambda: " << mTotalLambda);
 	}

+ 38 - 0
UnitTests/Physics/PhysicsTests.cpp

@@ -1522,6 +1522,44 @@ TEST_SUITE("PhysicsTests")
 		}
 	}
 
+	TEST_CASE("TestAllowedDOFsVsCollision")
+	{
+		PhysicsTestContext c;
+		Body &floor = c.CreateFloor();
+		floor.SetFriction(1.0f);
+
+		LoggingContactListener contact_listener;
+		c.GetSystem()->SetContactListener(&contact_listener);
+
+		// Create box that can only rotate around Y that intersects with the floor
+		RVec3 initial_position(0, 0.99f, 0);
+		BodyCreationSettings box_settings(new BoxShape(Vec3::sReplicate(1.0f)), initial_position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
+		box_settings.mAllowedDOFs = EAllowedDOFs::RotationY;
+		box_settings.mAngularDamping = 0.0f; // No damping to make the calculation for expected angular velocity simple
+		box_settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
+		box_settings.mMassPropertiesOverride.mMass = 1.0f;
+		box_settings.mFriction = 1.0f; // High friction so that if the collision is processed, we'll slow down the rotation
+		Body *body = c.GetBodyInterface().CreateBody(box_settings);
+		c.GetBodyInterface().AddBody(body->GetID(), EActivation::Activate);
+
+		// Make the box rotate around Y
+		const Vec3 torque(0, 100.0f, 0);
+		body->AddTorque(torque);
+
+		// Simulate a step, this will make the box collide with the floor but should not result in the floor stopping the body
+		// but will cause the effective mass of the contact to become infinite so is a test if we are properly ignoring the contact in this case
+		c.SimulateSingleStep();
+
+		// Check that we did detect the collision
+		CHECK(contact_listener.Contains(LoggingContactListener::EType::Add, floor.GetID(), body->GetID()));
+
+		// Check that we have the correct angular velocity
+		Vec3 expected_angular_velocity = torque * c.GetDeltaTime() * body->GetInverseInertia()(1, 1);
+		CHECK_APPROX_EQUAL(body->GetAngularVelocity(), expected_angular_velocity);
+		CHECK(body->GetLinearVelocity() == Vec3::sZero());
+		CHECK(body->GetPosition() == initial_position);
+	}
+
 	TEST_CASE("TestSelectiveStateSaveAndRestore")
 	{
 		class MyFilter : public StateRecorderFilter