Browse Source

Improved handling of mass override for soft bodies (#1066)

- If inverse mass override of both the soft body vertex and the colliding body are 0, the collision will be ignored
- If the inverse mass override of the soft body vertex is 0 and the colliding body is kinematic, the collision will be ignored
- If the sensor flag is set, the collision will be reported but not handled
Jorrit Rouwe 1 year ago
parent
commit
3e13b2c097

+ 66 - 49
Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp

@@ -126,32 +126,47 @@ void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateCont
 				if (body.IsRigidBody() // TODO: We should support soft body vs soft body
 					&& soft_body.GetCollisionGroup().CanCollide(body.GetCollisionGroup()))
 				{
-					// Call the contact listener to see if we should accept this contact
-					// If there is no contact listener then we can ignore the contact if the other body is a sensor
 					SoftBodyContactSettings settings;
 					settings.mIsSensor = body.IsSensor();
-					if (mContext.mContactListener == nullptr? !settings.mIsSensor : mContext.mContactListener->OnSoftBodyContactValidate(soft_body, body, settings) == SoftBodyValidateResult::AcceptContact)
+
+					if (mContext.mContactListener == nullptr)
 					{
-						CollidingShape cs;
-						cs.mCenterOfMassTransform = (mInverseTransform * body.GetCenterOfMassTransform()).ToMat44();
-						cs.mShape = body.GetShape();
-						cs.mBodyID = inResult;
-						cs.mMotionType = body.GetMotionType();
-						cs.mIsSensor = settings.mIsSensor;
-						cs.mUpdateVelocities = false;
-						cs.mFriction = mCombineFriction(soft_body, SubShapeID(), body, SubShapeID());
-						cs.mRestitution = mCombineRestitution(soft_body, SubShapeID(), body, SubShapeID());
-						if (cs.mMotionType == EMotionType::Dynamic)
-						{
-							const MotionProperties *mp = body.GetMotionProperties();
-							cs.mInvMass = settings.mInvMassScale2 * mp->GetInverseMass();
-							cs.mInvInertia = settings.mInvInertiaScale2 * mp->GetInverseInertiaForRotation(cs.mCenterOfMassTransform.GetRotation());
-							cs.mSoftBodyInvMassScale = settings.mInvMassScale1;
-							cs.mOriginalLinearVelocity = cs.mLinearVelocity = mInverseTransform.Multiply3x3(mp->GetLinearVelocity());
-							cs.mOriginalAngularVelocity = cs.mAngularVelocity = mInverseTransform.Multiply3x3(mp->GetAngularVelocity());
-						}
-						mHits.push_back(cs);
+						// If we have no contact listener, we can ignore sensors
+						if (settings.mIsSensor)
+							return;
+					}
+					else
+					{
+						// Call the contact listener to see if we should accept this contact
+						if (mContext.mContactListener->OnSoftBodyContactValidate(soft_body, body, settings) != SoftBodyValidateResult::AcceptContact)
+							return;
+
+						// Check if there will be any interaction
+						if (!settings.mIsSensor
+							&& settings.mInvMassScale1 == 0.0f
+							&& (body.GetMotionType() != EMotionType::Dynamic || settings.mInvMassScale2 == 0.0f))
+							return;
+					}
+
+					CollidingShape cs;
+					cs.mCenterOfMassTransform = (mInverseTransform * body.GetCenterOfMassTransform()).ToMat44();
+					cs.mShape = body.GetShape();
+					cs.mBodyID = inResult;
+					cs.mMotionType = body.GetMotionType();
+					cs.mIsSensor = settings.mIsSensor;
+					cs.mUpdateVelocities = false;
+					cs.mFriction = mCombineFriction(soft_body, SubShapeID(), body, SubShapeID());
+					cs.mRestitution = mCombineRestitution(soft_body, SubShapeID(), body, SubShapeID());
+					cs.mSoftBodyInvMassScale = settings.mInvMassScale1;
+					if (cs.mMotionType == EMotionType::Dynamic)
+					{
+						const MotionProperties *mp = body.GetMotionProperties();
+						cs.mInvMass = settings.mInvMassScale2 * mp->GetInverseMass();
+						cs.mInvInertia = settings.mInvInertiaScale2 * mp->GetInverseInertiaForRotation(cs.mCenterOfMassTransform.GetRotation());
+						cs.mOriginalLinearVelocity = cs.mLinearVelocity = mInverseTransform.Multiply3x3(mp->GetLinearVelocity());
+						cs.mOriginalAngularVelocity = cs.mAngularVelocity = mInverseTransform.Multiply3x3(mp->GetAngularVelocity());
 					}
+					mHits.push_back(cs);
 				}
 			}
 		}
@@ -576,34 +591,36 @@ void SoftBodyMotionProperties::ApplyCollisionConstraintsAndUpdateVelocities(cons
 							Vec3 r2_cross_n = r2.Cross(contact_normal);
 							float w2 = cs.mInvMass + r2_cross_n.Dot(cs.mInvInertia * r2_cross_n);
 							float w1_plus_w2 = vertex_inv_mass + w2;
-
-							// Calculate delta relative velocity due to friction (modified equation 31)
-							Vec3 dv;
-							if (v_tangential_length > 0.0f)
-								dv = v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f);
-							else
-								dv = Vec3::sZero();
-
-							// Calculate delta relative velocity due to restitution (equation 35)
-							dv += v_normal;
-							float prev_v_normal = (prev_v - v2).Dot(contact_normal);
-							if (prev_v_normal < restitution_treshold)
-								dv += cs.mRestitution * prev_v_normal * contact_normal;
-
-							// Calculate impulse
-							Vec3 p = dv / w1_plus_w2;
-
-							// Apply impulse to particle
-							v.mVelocity -= p * vertex_inv_mass;
-
-							// Apply impulse to rigid body
-							cs.mLinearVelocity += p * cs.mInvMass;
-							cs.mAngularVelocity += cs.mInvInertia * r2.Cross(p);
-
-							// Mark that the velocities of the body we hit need to be updated
-							cs.mUpdateVelocities = true;
+							if (w1_plus_w2 > 0.0f)
+							{
+								// Calculate delta relative velocity due to friction (modified equation 31)
+								Vec3 dv;
+								if (v_tangential_length > 0.0f)
+									dv = v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f);
+								else
+									dv = Vec3::sZero();
+
+								// Calculate delta relative velocity due to restitution (equation 35)
+								dv += v_normal;
+								float prev_v_normal = (prev_v - v2).Dot(contact_normal);
+								if (prev_v_normal < restitution_treshold)
+									dv += cs.mRestitution * prev_v_normal * contact_normal;
+
+								// Calculate impulse
+								Vec3 p = dv / w1_plus_w2;
+
+								// Apply impulse to particle
+								v.mVelocity -= p * vertex_inv_mass;
+
+								// Apply impulse to rigid body
+								cs.mLinearVelocity += p * cs.mInvMass;
+								cs.mAngularVelocity += cs.mInvInertia * r2.Cross(p);
+
+								// Mark that the velocities of the body we hit need to be updated
+								cs.mUpdateVelocities = true;
+							}
 						}
-						else
+						else if (cs.mSoftBodyInvMassScale > 0.0f)
 						{
 							// Body is not movable, equations are simpler
 

+ 26 - 3
Samples/Tests/SoftBody/SoftBodyContactListenerTest.cpp

@@ -36,7 +36,7 @@ void SoftBodyContactListenerTest::PrePhysicsUpdate(const PreUpdateParams &inPara
 	if (mTime > 2.5f)
 	{
 		// Next cycle
-		mCycle = (mCycle + 1) % 7;
+		mCycle = (mCycle + 1) % 10;
 		mTime = 0.0f;
 
 		// Remove the old scene
@@ -50,7 +50,7 @@ void SoftBodyContactListenerTest::PrePhysicsUpdate(const PreUpdateParams &inPara
 	}
 
 	// Draw current state
-	const char *cycle_names[] = { "Accept contact", "Sphere 10x mass", "Cloth 10x mass", "Sphere infinite mass", "Cloth infinite mass", "Sensor contact", "Reject contact" };
+	const char *cycle_names[] = { "Accept contact", "Sphere 10x mass", "Cloth 10x mass", "Sphere infinite mass", "Cloth infinite mass", "Sensor contact", "Reject contact", "Kinematic Sphere", "Kinematic Sphere, cloth infinite mass", "Kinematic sphere, sensor contact", "Kinematic Sphere, reject contact" };
 	mDebugRenderer->DrawText3D(mBodyInterface->GetPosition(mOtherBodyID), cycle_names[mCycle], Color::sWhite, 1.0f);
 }
 
@@ -65,10 +65,15 @@ void SoftBodyContactListenerTest::StartCycle()
 	cloth.mMakeRotationIdentity = false; // Test explicitly checks if soft bodies with a rotation collide with shapes properly
 	mSoftBodyID = mBodyInterface->CreateAndAddSoftBody(cloth, EActivation::Activate);
 
+	// If we want a kinematic sphere
+	bool kinematic = mCycle > 6;
+
 	// Create sphere
-	BodyCreationSettings bcs(new SphereShape(1.0f), RVec3(0, 7, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
+	BodyCreationSettings bcs(new SphereShape(1.0f), RVec3(0, 7, 0), Quat::sIdentity(), kinematic? EMotionType::Kinematic : EMotionType::Dynamic, Layers::MOVING);
 	bcs.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
 	bcs.mMassPropertiesOverride.mMass = 100.0f;
+	if (kinematic)
+		bcs.mLinearVelocity = Vec3(0, -2.5f, 0);
 	mOtherBodyID = mBodyInterface->CreateAndAddBody(bcs, EActivation::Activate);
 }
 
@@ -107,6 +112,24 @@ SoftBodyValidateResult SoftBodyContactListenerTest::OnSoftBodyContactValidate(co
 		ioSettings.mIsSensor = true;
 		return SoftBodyValidateResult::AcceptContact;
 
+	case 6:
+		// No contacts
+		return SoftBodyValidateResult::RejectContact;
+
+	case 7:
+		// Kinematic sphere
+		return SoftBodyValidateResult::AcceptContact;
+
+	case 8:
+		// Kinematic sphere, cloth infinite mass
+		ioSettings.mInvMassScale1 = 0.0f;
+		return SoftBodyValidateResult::AcceptContact;
+
+	case 9:
+		// Kinematic sphere, sensor contact
+		ioSettings.mIsSensor = true;
+		return SoftBodyValidateResult::AcceptContact;
+
 	default:
 		// No contacts
 		return SoftBodyValidateResult::RejectContact;