Преглед изворни кода

Disabling speculative contacts for sensors (#829)

Fixes #818
Jorrit Rouwe пре 1 година
родитељ
комит
3695d1f418

+ 6 - 1
Docs/ReleaseNotes.md

@@ -21,14 +21,19 @@ For breaking API changes see [this document](https://github.com/jrouwe/JoltPhysi
 ### Improvements
 * Multithreading the SetupVelocityConstraints job. This was causing a bottleneck in the case that there are a lot of constraints but very few possible collisions.
 
+### Bug fixes
+* Sensors will no longer use speculative contacts, so will no longer report contacts before an actual contact is detected.
+
 # v4.0.2
 
+### New functionality
+* Support for compiling with ninja on Windows.
+
 ### Bug fixes
 * Fixed bug in Indexify function that caused it to be really slow when passing 10K identical vertices. Also fixed a problem that could have led to some vertices not being welded.
 * Fixed bug in SixDOFConstraint::RestoreState that would cause motors to not properly turn on.
 * Fixed a determinism issue in CharacterVirtual. The order of the contacts returned by GetActiveContacts() was not deterministic.
 * Fixed issue in sample application that mouse is very sensitive when viewing with Parsec.
-* Support for compiling with ninja on Windows.
 
 ## v4.0.1
 

+ 1 - 1
Jolt/Physics/Collision/ContactListener.h

@@ -28,7 +28,7 @@ public:
 
 	RVec3					mBaseOffset;						///< Offset to which all the contact points are relative
 	Vec3					mWorldSpaceNormal;					///< Normal for this manifold, direction along which to move body 2 out of collision along the shortest path
-	float					mPenetrationDepth;					///< Penetration depth (move shape 2 by this distance to resolve the collision)
+	float					mPenetrationDepth;					///< Penetration depth (move shape 2 by this distance to resolve the collision). If this value is negative, this is a speculative contact point and may not actually result in a velocity change as during solving the bodies may not actually collide.
 	SubShapeID				mSubShapeID1;						///< Sub shapes that formed this manifold (note that when multiple manifolds are combined because they're coplanar, we lose some information here because we only keep track of one sub shape pair that we encounter, see description at Body::SetUseManifoldReduction)
 	SubShapeID				mSubShapeID2;
 	ContactPoints			mRelativeContactPointsOn1;			///< Contact points on the surface of shape 1 relative to mBaseOffset.

+ 1 - 1
Jolt/Physics/PhysicsSystem.cpp

@@ -1000,7 +1000,7 @@ void PhysicsSystem::ProcessBodyPair(ContactAllocator &ioContactAllocator, const
 		CollideShapeSettings settings;
 		settings.mCollectFacesMode = ECollectFacesMode::CollectFaces;
 		settings.mActiveEdgeMode = mPhysicsSettings.mCheckActiveEdges? EActiveEdgeMode::CollideOnlyWithActive : EActiveEdgeMode::CollideWithAll;
-		settings.mMaxSeparationDistance = mPhysicsSettings.mSpeculativeContactDistance;
+		settings.mMaxSeparationDistance = body1->IsSensor() || body2->IsSensor()? 0.0f : mPhysicsSettings.mSpeculativeContactDistance;
 		settings.mActiveEdgeMovementDirection = body1->GetLinearVelocity() - body2->GetLinearVelocity();
 
 		// Get transforms relative to body1

+ 42 - 0
UnitTests/Physics/PhysicsTests.cpp

@@ -930,6 +930,48 @@ TEST_SUITE("PhysicsTests")
 		CHECK_APPROX_EQUAL(-sphere.GetLinearVelocity(), cVelocity);
 	}
 
+	TEST_CASE("TestPhysicsInsideSpeculativeContactDistanceSensor")
+	{
+		PhysicsTestContext c;
+		Body &floor = c.CreateFloor();
+		c.ZeroGravity();
+
+		LoggingContactListener contact_listener;
+		c.GetSystem()->SetContactListener(&contact_listener);
+
+		// Create a sphere sensor just inside the speculative contact distance
+		const float cSpeculativeContactDistance = c.GetSystem()->GetPhysicsSettings().mSpeculativeContactDistance;
+		const float cRadius = 1.0f;
+		const float cDistanceAboveFloor = 0.9f * cSpeculativeContactDistance;
+		const RVec3 cInitialPosSphere(5, cRadius + cDistanceAboveFloor, 0);
+
+		// Make it move 1 m per step down
+		const Vec3 cVelocity(0, -1.0f / c.GetDeltaTime(), 0);
+
+		Body &sphere = c.CreateSphere(cInitialPosSphere, cRadius, EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING);
+		sphere.SetIsSensor(true);
+		sphere.SetLinearVelocity(cVelocity);
+
+		// Simulate a step
+		c.SimulateSingleStep();
+
+		CHECK(contact_listener.GetEntryCount() == 0); // We're inside the speculative contact distance but we're a sensor so we shouldn't trigger any contacts
+
+		// Simulate a step
+		c.SimulateSingleStep();
+
+		// Check that we're now actually intersecting
+		CHECK(contact_listener.GetEntryCount() == 2); // 1 validates and 1 contact
+		CHECK(contact_listener.Contains(LoggingContactListener::EType::Validate, sphere.GetID(), floor.GetID()));
+		CHECK(contact_listener.Contains(LoggingContactListener::EType::Add, sphere.GetID(), floor.GetID()));
+		contact_listener.Clear();
+
+		// Sensor should not be affected by the floor
+		CHECK_APPROX_EQUAL(sphere.GetPosition(), cInitialPosSphere + 2.0f * c.GetDeltaTime() * cVelocity);
+		CHECK_APPROX_EQUAL(sphere.GetLinearVelocity(), cVelocity);
+		CHECK_APPROX_EQUAL(sphere.GetAngularVelocity(), Vec3::sZero());
+	}
+
 	TEST_CASE("TestPhysicsInsideSpeculativeContactDistanceMovingAway")
 	{
 		PhysicsTestContext c;

+ 15 - 15
UnitTests/Physics/SensorTests.cpp

@@ -36,8 +36,8 @@ TEST_SUITE("SensorTests")
 		c.SimulateSingleStep();
 		CHECK(listener.GetEntryCount() == 0);
 
-		// After half a second we should be touching the sensor
-		c.Simulate(0.5f);
+		// After half a second and one step we should be touching the sensor
+		c.Simulate(0.5f + c.GetStepDeltaTime());
 		CHECK(listener.Contains(EType::Add, dynamic.GetID(), sensor_id));
 		listener.Clear();
 
@@ -48,7 +48,7 @@ TEST_SUITE("SensorTests")
 		listener.Clear();
 
 		// After 3 more seconds we should have left the sensor at the bottom side
-		c.Simulate(3.0f + c.GetDeltaTime());
+		c.Simulate(3.0f);
 		CHECK(listener.Contains(EType::Remove, dynamic.GetID(), sensor_id));
 		CHECK_APPROX_EQUAL(dynamic.GetPosition(), RVec3(0, -1.5f - 3.0f * c.GetDeltaTime(), 0), 1.0e-4f);
 	}
@@ -74,8 +74,8 @@ TEST_SUITE("SensorTests")
 		c.SimulateSingleStep();
 		CHECK(listener.GetEntryCount() == 0);
 
-		// After half a second we should be touching the sensor
-		c.Simulate(0.5f);
+		// After half a second and one step we should be touching the sensor
+		c.Simulate(0.5f + c.GetStepDeltaTime());
 		CHECK(listener.Contains(EType::Add, kinematic.GetID(), sensor_id));
 		listener.Clear();
 
@@ -86,7 +86,7 @@ TEST_SUITE("SensorTests")
 		listener.Clear();
 
 		// After 3 more seconds we should have left the sensor at the bottom side
-		c.Simulate(3.0f + c.GetDeltaTime());
+		c.Simulate(3.0f);
 		CHECK(listener.Contains(EType::Remove, kinematic.GetID(), sensor_id));
 		CHECK_APPROX_EQUAL(kinematic.GetPosition(), RVec3(0, -1.5f - 3.0f * c.GetDeltaTime(), 0), 1.0e-4f);
 	}
@@ -114,8 +114,8 @@ TEST_SUITE("SensorTests")
 		c.SimulateSingleStep();
 		CHECK(listener.GetEntryCount() == 0);
 
-		// After half a second we should be touching the sensor
-		c.Simulate(0.5f);
+		// After half a second and one step we should be touching the sensor
+		c.Simulate(0.5f + c.GetStepDeltaTime());
 		CHECK(listener.Contains(EType::Add, kinematic.GetID(), sensor_id));
 		listener.Clear();
 
@@ -126,7 +126,7 @@ TEST_SUITE("SensorTests")
 		listener.Clear();
 
 		// After 3 more seconds we should have left the sensor at the bottom side
-		c.Simulate(3.0f + c.GetDeltaTime());
+		c.Simulate(3.0f);
 		CHECK(listener.Contains(EType::Remove, kinematic.GetID(), sensor_id));
 		CHECK_APPROX_EQUAL(kinematic.GetPosition(), RVec3(0, -1.5f - 3.0f * c.GetDeltaTime(), 0), 1.0e-4f);
 	}
@@ -154,8 +154,8 @@ TEST_SUITE("SensorTests")
 		c.SimulateSingleStep();
 		CHECK(listener.GetEntryCount() == 0);
 
-		// After half a second we should be touching the sensor
-		c.Simulate(0.5f);
+		// After half a second and one step we should be touching the sensor
+		c.Simulate(0.5f + c.GetStepDeltaTime());
 		CHECK(listener.Contains(EType::Add, kinematic.GetID(), sensor_id));
 		listener.Clear();
 
@@ -166,7 +166,7 @@ TEST_SUITE("SensorTests")
 		listener.Clear();
 
 		// After 3 more seconds we should have left the sensor at the bottom side
-		c.Simulate(3.0f + c.GetDeltaTime());
+		c.Simulate(3.0f);
 		CHECK(listener.Contains(EType::Remove, kinematic.GetID(), sensor_id));
 		CHECK_APPROX_EQUAL(kinematic.GetPosition(), RVec3(0, -1.5f - 3.0f * c.GetDeltaTime(), 0), 1.0e-4f);
 	}
@@ -336,8 +336,8 @@ TEST_SUITE("SensorTests")
 			c.SimulateSingleStep();
 			CHECK(listener.GetEntryCount() == 0);
 
-			// After half a second the sensors should be touching
-			c.Simulate(0.5f);
+			// After half a second and one step the sensors should be touching
+			c.Simulate(0.5f + c.GetDeltaTime());
 			if (sensor_detects_sensor)
 				CHECK(listener.Contains(EType::Add, sensor_id1, sensor_id2));
 			else
@@ -356,7 +356,7 @@ TEST_SUITE("SensorTests")
 			listener.Clear();
 
 			// After 2 more seconds we should have left the sensor at the bottom side
-			c.Simulate(2.0f + c.GetDeltaTime());
+			c.Simulate(2.0f);
 			if (sensor_detects_sensor)
 				CHECK(listener.Contains(EType::Remove, sensor_id1, sensor_id2));
 			else