Skip to content

Commit

Permalink
Disabling speculative contacts for sensors (#829)
Browse files Browse the repository at this point in the history
Fixes #818
  • Loading branch information
jrouwe committed Dec 30, 2023
1 parent f58e2c9 commit 3695d1f
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 18 deletions.
7 changes: 6 additions & 1 deletion Docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion Jolt/Physics/Collision/ContactListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ContactManifold

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.
Expand Down
2 changes: 1 addition & 1 deletion Jolt/Physics/PhysicsSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions UnitTests/Physics/PhysicsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
30 changes: 15 additions & 15 deletions UnitTests/Physics/SensorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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);
}
Expand All @@ -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();

Expand All @@ -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);
}
Expand Down Expand Up @@ -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();

Expand All @@ -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);
}
Expand Down Expand Up @@ -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();

Expand All @@ -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);
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 3695d1f

Please sign in to comment.