// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) // SPDX-FileCopyrightText: 2021 Jorrit Rouwe // SPDX-License-Identifier: MIT #include #include #include #include #include #include #include #include #include #include JPH_IMPLEMENT_RTTI_VIRTUAL(ContactListenerTest) { JPH_ADD_BASE_CLASS(ContactListenerTest, Test) } void ContactListenerTest::Initialize() { // Floor CreateFloor(); RefConst box_shape = new BoxShape(Vec3(0.5f, 1.0f, 2.0f)); // Dynamic body 1 Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box_shape, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING)); body1.SetAllowSleeping(false); mBodyInterface->AddBody(body1.GetID(), EActivation::Activate); // Dynamic body 2 Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(box_shape, RVec3(5, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING)); body2.SetAllowSleeping(false); mBodyInterface->AddBody(body2.GetID(), EActivation::Activate); // Dynamic body 3 Body &body3 = *mBodyInterface->CreateBody(BodyCreationSettings(new SphereShape(2.0f), RVec3(10, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING)); body3.SetAllowSleeping(false); mBodyInterface->AddBody(body3.GetID(), EActivation::Activate); // Dynamic body 4 Ref compound_shape = new StaticCompoundShapeSettings; compound_shape->AddShape(Vec3::sZero(), Quat::sIdentity(), new CapsuleShape(5, 1)); compound_shape->AddShape(Vec3(0, -5, 0), Quat::sIdentity(), new SphereShape(2)); compound_shape->AddShape(Vec3(0, 5, 0), Quat::sIdentity(), new SphereShape(2)); Body &body4 = *mBodyInterface->CreateBody(BodyCreationSettings(compound_shape, RVec3(15, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING)); body4.SetAllowSleeping(false); mBodyInterface->AddBody(body4.GetID(), EActivation::Activate); // Store bodies for later use mBody[0] = &body1; mBody[1] = &body2; mBody[2] = &body3; mBody[3] = &body4; } void ContactListenerTest::PostPhysicsUpdate(float inDeltaTime) { // Check that predicted velocities match actual velocities lock_guard lock(mPredictedVelocitiesMutex); for (const PredictedVelocity &v : mPredictedVelocities) { BodyLockRead body_lock(mPhysicsSystem->GetBodyLockInterface(), v.mBodyID); if (body_lock.Succeeded()) { const Body &body = body_lock.GetBody(); Vec3 linear_velocity = body.GetLinearVelocity(); Vec3 angular_velocity = body.GetAngularVelocity(); float diff_v = (v.mLinearVelocity - linear_velocity).Length(); float diff_w = (v.mAngularVelocity - angular_velocity).Length(); if (diff_v > 1.0e-3f || diff_w > 1.0e-3f) Trace("Mispredicted collision for body: %08x, v=%s, w=%s, predicted_v=%s, predicted_w=%s, diff_v=%f, diff_w=%f", body.GetID().GetIndex(), ConvertToString(linear_velocity).c_str(), ConvertToString(angular_velocity).c_str(), ConvertToString(v.mLinearVelocity).c_str(), ConvertToString(v.mAngularVelocity).c_str(), (double)diff_v, (double)diff_w); } } mPredictedVelocities.clear(); } ValidateResult ContactListenerTest::OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) { // Body 1 and 2 should never collide return ((&inBody1 == mBody[0] && &inBody2 == mBody[1]) || (&inBody1 == mBody[1] && &inBody2 == mBody[0]))? ValidateResult::RejectAllContactsForThisBodyPair : ValidateResult::AcceptAllContactsForThisBodyPair; } void ContactListenerTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) { // Make body 1 bounce only when a new contact point is added but not when it is persisted (its restitution is normally 0) if (&inBody1 == mBody[0] || &inBody2 == mBody[0]) { JPH_ASSERT(ioSettings.mCombinedRestitution == 0.0f); ioSettings.mCombinedRestitution = 1.0f; } // Estimate the contact impulses. CollisionEstimationResult result; EstimateCollisionResponse(inBody1, inBody2, inManifold, result, ioSettings.mCombinedFriction, ioSettings.mCombinedRestitution); // Trace the result String impulses_str; for (const CollisionEstimationResult::Impulse &impulse : result.mImpulses) impulses_str += StringFormat("(%f, %f, %f) ", (double)impulse.mContactImpulse, (double)impulse.mFrictionImpulse1, (double)impulse.mFrictionImpulse2); Trace("Estimated velocity after collision, body1: %08x, v=%s, w=%s, body2: %08x, v=%s, w=%s, impulses: %s", inBody1.GetID().GetIndex(), ConvertToString(result.mLinearVelocity1).c_str(), ConvertToString(result.mAngularVelocity1).c_str(), inBody2.GetID().GetIndex(), ConvertToString(result.mLinearVelocity2).c_str(), ConvertToString(result.mAngularVelocity2).c_str(), impulses_str.c_str()); // Log predicted velocities { lock_guard lock(mPredictedVelocitiesMutex); mPredictedVelocities.push_back({ inBody1.GetID(), result.mLinearVelocity1, result.mAngularVelocity1 }); mPredictedVelocities.push_back({ inBody2.GetID(), result.mLinearVelocity2, result.mAngularVelocity2 }); } }