Files
CosmicEngine/lib/All/JoltPhysics/Samples/Tests/General/SimCollideBodyVsBodyTest.cpp

179 lines
7.7 KiB
C++
Raw Permalink Normal View History

// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/General/SimCollideBodyVsBodyTest.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
#include <Jolt/Physics/Collision/CollisionDispatch.h>
#include <Jolt/Physics/Collision/CollideShapeVsShapePerLeaf.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
#include <Renderer/DebugRendererImp.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(SimCollideBodyVsBodyTest)
{
JPH_ADD_BASE_CLASS(SimCollideBodyVsBodyTest, Test)
}
template <class LeafCollector>
static void sCollideBodyVsBodyPerBody(const Body &inBody1, const Body &inBody2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, CollideShapeSettings &ioCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
{
if (inBody1.IsSensor() || inBody2.IsSensor())
{
// A sensor will return max 1 hit per body pair
LeafCollector collector;
SubShapeIDCreator part1, part2;
CollisionDispatch::sCollideShapeVsShape(inBody1.GetShape(), inBody2.GetShape(), Vec3::sOne(), Vec3::sOne(), inCenterOfMassTransform1, inCenterOfMassTransform2, part1, part2, ioCollideShapeSettings, collector);
if (collector.HadHit())
ioCollector.AddHit(collector.mHit);
}
else
{
// If not a sensor: fall back to the default
PhysicsSystem::sDefaultSimCollideBodyVsBody(inBody1, inBody2, inCenterOfMassTransform1, inCenterOfMassTransform2, ioCollideShapeSettings, ioCollector, inShapeFilter);
}
}
template <class LeafCollector>
static void sCollideBodyVsBodyPerLeaf(const Body &inBody1, const Body &inBody2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, CollideShapeSettings &ioCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
{
if (inBody1.IsSensor() || inBody2.IsSensor())
{
// A sensor will return 1 hit per leaf shape pair
SubShapeIDCreator part1, part2;
CollideShapeVsShapePerLeaf<LeafCollector>(inBody1.GetShape(), inBody2.GetShape(), Vec3::sOne(), Vec3::sOne(), inCenterOfMassTransform1, inCenterOfMassTransform2, part1, part2, ioCollideShapeSettings, ioCollector, inShapeFilter);
}
else
{
// If not a sensor: fall back to the default
PhysicsSystem::sDefaultSimCollideBodyVsBody(inBody1, inBody2, inCenterOfMassTransform1, inCenterOfMassTransform2, ioCollideShapeSettings, ioCollector, inShapeFilter);
}
}
void SimCollideBodyVsBodyTest::Initialize()
{
// Create pyramid with flat top
MeshShapeSettings pyramid;
pyramid.mTriangleVertices = { Float3(1, 0, 1), Float3(1, 0, -1), Float3(-1, 0, -1), Float3(-1, 0, 1), Float3(0.1f, 1, 0.1f), Float3(0.1f, 1, -0.1f), Float3(-0.1f, 1, -0.1f), Float3(-0.1f, 1, 0.1f) };
pyramid.mIndexedTriangles = { IndexedTriangle(0, 1, 4), IndexedTriangle(4, 1, 5), IndexedTriangle(1, 2, 5), IndexedTriangle(2, 6, 5), IndexedTriangle(2, 3, 6), IndexedTriangle(3, 7, 6), IndexedTriangle(3, 0, 7), IndexedTriangle(0, 4, 7), IndexedTriangle(4, 5, 6), IndexedTriangle(4, 6, 7) };
pyramid.SetEmbedded();
// Create floor of many pyramids
StaticCompoundShapeSettings compound;
for (int x = -10; x <= 10; ++x)
for (int z = -10; z <= 10; ++z)
compound.AddShape(Vec3(x * 2.0f, 0, z * 2.0f), Quat::sIdentity(), &pyramid);
compound.SetEmbedded();
mBodyInterface->CreateAndAddBody(BodyCreationSettings(&compound, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
// A kinematic sensor that also detects static bodies
BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(10.0f)), RVec3(0, 5, 0), Quat::sIdentity(), EMotionType::Kinematic, Layers::MOVING); // Put in a layer that collides with static
sensor_settings.mIsSensor = true;
sensor_settings.mCollideKinematicVsNonDynamic = true;
sensor_settings.mUseManifoldReduction = false;
mSensorID = mBodyInterface->CreateAndAddBody(sensor_settings, EActivation::Activate);
// Dynamic bodies
for (int i = 0; i < 10; ++i)
mBodyIDs.push_back(mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(0.1f, 0.5f, 0.2f)), RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate));
}
void SimCollideBodyVsBodyTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
{
// Update time
mTime += inParams.mDeltaTime;
const char *mode_string = nullptr;
int mode = int(mTime / 3.0f) % 5;
switch (mode)
{
default:
mode_string = "Sensor: Collect all contact points";
mPhysicsSystem->SetSimCollideBodyVsBody(&PhysicsSystem::sDefaultSimCollideBodyVsBody);
break;
case 1:
mode_string = "Sensor: Collect any contact point per body";
mPhysicsSystem->SetSimCollideBodyVsBody(&sCollideBodyVsBodyPerBody<AnyHitCollisionCollector<CollideShapeCollector>>);
break;
case 2:
mode_string = "Sensor: Collect deepest contact point per body";
mPhysicsSystem->SetSimCollideBodyVsBody(&sCollideBodyVsBodyPerBody<ClosestHitCollisionCollector<CollideShapeCollector>>);
break;
case 3:
mode_string = "Sensor: Collect any contact point per leaf shape";
mPhysicsSystem->SetSimCollideBodyVsBody(&sCollideBodyVsBodyPerLeaf<AnyHitCollisionCollector<CollideShapeCollector>>);
break;
case 4:
mode_string = "Sensor: Collect deepest contact point per leaf shape";
mPhysicsSystem->SetSimCollideBodyVsBody(&sCollideBodyVsBodyPerLeaf<ClosestHitCollisionCollector<CollideShapeCollector>>);
break;
}
DebugRenderer::sInstance->DrawText3D(RVec3(0, 5, 0), mode_string);
// If the mode changes
if (mode != mPrevMode)
{
// Start all bodies from the top
for (int i = 0; i < (int)mBodyIDs.size(); ++i)
{
BodyID id = mBodyIDs[i];
mBodyInterface->SetPositionRotationAndVelocity(id, RVec3(-4.9_r + i * 1.0_r, 5.0_r, 0), Quat::sIdentity(), Vec3::sZero(), Vec3::sZero());
mBodyInterface->ActivateBody(id);
}
// Invalidate collisions with sensor to refresh contacts
mBodyInterface->InvalidateContactCache(mSensorID);
mPrevMode = mode;
}
}
void SimCollideBodyVsBodyTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
{
if (!inBody1.IsSensor())
{
mDebugRenderer->DrawWirePolygon(RMat44::sTranslation(inManifold.mBaseOffset), inManifold.mRelativeContactPointsOn1, Color::sGreen, 0.01f);
Vec3 average = Vec3::sZero();
for (const Vec3 &p : inManifold.mRelativeContactPointsOn1)
average += p;
average /= (float)inManifold.mRelativeContactPointsOn1.size();
mDebugRenderer->DrawArrow(inManifold.mBaseOffset + average, inManifold.mBaseOffset + average - inManifold.mWorldSpaceNormal, Color::sYellow, 0.1f);
}
if (!inBody2.IsSensor())
{
mDebugRenderer->DrawWirePolygon(RMat44::sTranslation(inManifold.mBaseOffset), inManifold.mRelativeContactPointsOn2, Color::sGreen, 0.01f);
Vec3 average = Vec3::sZero();
for (const Vec3 &p : inManifold.mRelativeContactPointsOn2)
average += p;
average /= (float)inManifold.mRelativeContactPointsOn2.size();
mDebugRenderer->DrawArrow(inManifold.mBaseOffset + average, inManifold.mBaseOffset + average + inManifold.mWorldSpaceNormal, Color::sYellow, 0.1f);
}
}
void SimCollideBodyVsBodyTest::OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
{
OnContactAdded(inBody1, inBody2, inManifold, ioSettings);
}
void SimCollideBodyVsBodyTest::SaveState(StateRecorder &inStream) const
{
inStream.Write(mPrevMode);
inStream.Write(mTime);
}
void SimCollideBodyVsBodyTest::RestoreState(StateRecorder &inStream)
{
inStream.Read(mPrevMode);
inStream.Read(mTime);
}