Переглянути джерело

CharacterVirtual will now receive an OnContactAdded callback when it collides with a sensor (but will have no further interaction). (#861)

See discussion #856
Jorrit Rouwe 1 рік тому
батько
коміт
fb778c568d

+ 1 - 0
Docs/ReleaseNotes.md

@@ -18,6 +18,7 @@ For breaking API changes see [this document](https://github.com/jrouwe/JoltPhysi
 * Added ability to disable the lean steering limit for the motorcycle, turning this off makes the motorcycle more unstable, but gives you more control over the final steering angle.
 * Added function to query the bounding box of all bodies in the physics system, see PhysicsSystem::GetBounds.
 * Renamed SensorDetectsStatic to CollideKinematicVsNonDynamic and made it work for non-sensors. This means that kinematic bodies can now get collision callbacks when they collide with other static / kinematic objects.
+* CharacterVirtual will now receive an OnContactAdded callback when it collides with a sensor (but will have no further interaction).
 
 ### 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.

+ 11 - 13
Jolt/Physics/Character/CharacterVirtual.cpp

@@ -96,6 +96,7 @@ void CharacterVirtual::sFillContactProperties(const CharacterVirtual *inCharacte
 	outContact.mBodyB = inResult.mBodyID2;
 	outContact.mSubShapeIDB = inResult.mSubShapeID2;
 	outContact.mMotionTypeB = inBody.GetMotionType();
+	outContact.mIsSensorB = inBody.IsSensor();
 	outContact.mUserData = inBody.GetUserData();
 	outContact.mMaterial = inCollector.GetContext()->GetMaterial(inResult.mSubShapeID2);
 }
@@ -159,16 +160,10 @@ void CharacterVirtual::ContactCollector::AddHit(const CollideShapeResult &inResu
 	BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2);
 	if (lock.SucceededAndIsInBroadPhase())
 	{
-		// We don't collide with sensors, note that you should set up your collision layers so that sensors don't collide with the character.
-		// Rejecting the contact here means a lot of extra work for the collision detection system.
-		const Body &body = lock.GetBody();
-		if (!body.IsSensor())
-		{
-			mContacts.emplace_back();
-			Contact &contact = mContacts.back();
-			sFillContactProperties(mCharacter, contact, body, mUp, mBaseOffset, *this, inResult);
-			contact.mFraction = 0.0f;
-		}
+		mContacts.emplace_back();
+		Contact &contact = mContacts.back();
+		sFillContactProperties(mCharacter, contact, lock.GetBody(), mUp, mBaseOffset, *this, inResult);
+		contact.mFraction = 0.0f;
 	}
 }
 
@@ -193,8 +188,7 @@ void CharacterVirtual::ContactCastCollector::AddHit(const ShapeCastResult &inRes
 			if (!lock.SucceededAndIsInBroadPhase())
 				return;
 
-			// We don't collide with sensors, note that you should set up your collision layers so that sensors don't collide with the character.
-			// Rejecting the contact here means a lot of extra work for the collision detection system.
+			// Sweeps don't result in OnContactAdded callbacks so we can ignore sensors here
 			const Body &body = lock.GetBody();
 			if (body.IsSensor())
 				return;
@@ -444,6 +438,10 @@ bool CharacterVirtual::HandleContact(Vec3Arg inVelocity, Constraint &ioConstrain
 		mListener->OnContactAdded(this, contact.mBodyB, contact.mSubShapeIDB, contact.mPosition, -contact.mContactNormal, settings);
 	contact.mCanPushCharacter = settings.mCanPushCharacter;
 
+	// We don't have any further interaction with sensors beyond an OnContactAdded notification
+	if (contact.mIsSensorB)
+		return false;
+
 	// If body B cannot receive an impulse, we're done
 	if (!settings.mCanReceiveImpulses || contact.mMotionTypeB != EMotionType::Dynamic)
 		return true;
@@ -762,7 +760,7 @@ void CharacterVirtual::UpdateSupportingContact(bool inSkipContactVelocityCheck,
 			&& c.mDistance < mCollisionTolerance
 			&& (inSkipContactVelocityCheck || c.mSurfaceNormal.Dot(mLinearVelocity - c.mLinearVelocity) <= 1.0e-4f))
 		{
-			if (ValidateContact(c))
+			if (ValidateContact(c) && !c.mIsSensorB)
 				c.mHadCollision = true;
 			else
 				c.mWasDiscarded = true;

+ 1 - 0
Jolt/Physics/Character/CharacterVirtual.h

@@ -299,6 +299,7 @@ public:
 		BodyID							mBodyB;													///< ID of body we're colliding with
 		SubShapeID						mSubShapeIDB;											///< Sub shape ID of body we're colliding with
 		EMotionType						mMotionTypeB;											///< Motion type of B, used to determine the priority of the contact
+		bool							mIsSensorB;												///< If B is a sensor
 		uint64							mUserData;												///< User data of B
 		const PhysicsMaterial *			mMaterial;												///< Material of B
 		bool							mHadCollision = false;									///< If the character actually collided with the contact (can be false if a predictive contact never becomes a real one)

+ 7 - 0
Samples/Tests/Character/CharacterVirtualTest.cpp

@@ -214,6 +214,13 @@ void CharacterVirtualTest::OnAdjustBodyVelocity(const CharacterVirtual *inCharac
 
 void CharacterVirtualTest::OnContactAdded(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings)
 {
+	// Draw a box around the character when it enters the sensor
+	if (inBodyID2 == mSensorBody)
+	{
+		AABox box = inCharacter->GetShape()->GetWorldSpaceBounds(inCharacter->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f));
+		mDebugRenderer->DrawBox(box, Color::sGreen, DebugRenderer::ECastShadow::Off, DebugRenderer::EDrawMode::Wireframe);
+	}
+
 	// Dynamic boxes on the ramp go through all permutations
 	Array<BodyID>::const_iterator i = find(mRampBlocks.begin(), mRampBlocks.end(), inBodyID2);
 	if (i != mRampBlocks.end())