Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer

This commit is contained in:
Tom Ray
2026-03-22 00:28:03 +01:00
parent 6695d46bcd
commit 48348936a8
1147 changed files with 214331 additions and 353 deletions

View File

@@ -0,0 +1,28 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/BoxShapeTest.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(BoxShapeTest)
{
JPH_ADD_BASE_CLASS(BoxShapeTest, Test)
}
void BoxShapeTest::Initialize()
{
// Floor
CreateFloor();
// Different sized boxes
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(20, 1, 1)), RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(2, 3, 4)), RVec3(0, 10, 10), Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(0.5f, 0.75f, 1.0f)), RVec3(0, 10, 20), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class BoxShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, BoxShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,52 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/CapsuleShapeTest.h>
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(CapsuleShapeTest)
{
JPH_ADD_BASE_CLASS(CapsuleShapeTest, Test)
}
void CapsuleShapeTest::Initialize()
{
// Floor
CreateFloor();
RefConst<Shape> big_capsule = new CapsuleShape(2.5f, 2);
// Capsule on outer sphere
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_capsule, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Capsule on cylinder
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_capsule, RVec3(10, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
RefConst<Shape> long_capsule = new CapsuleShape(5, 1);
// Tower of capsules
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < 2; ++j)
{
RVec3 position;
Quat rotation;
if (i & 1)
{
position = RVec3(-4.0f + 8.0f * j, 2.0f + 3.0f * i, -20.0f);
rotation = Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI);
}
else
{
position = RVec3(0, 2.0f + 3.0f * i, -20.0f - 4.0f + 8.0f * j);
rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI);
}
mBodyInterface->CreateAndAddBody(BodyCreationSettings(long_capsule, position, rotation, EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}
}
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class CapsuleShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, CapsuleShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,124 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/ConvexHullShapeTest.h>
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(ConvexHullShapeTest)
{
JPH_ADD_BASE_CLASS(ConvexHullShapeTest, Test)
}
void ConvexHullShapeTest::Initialize()
{
// Floor
CreateFloor();
// Create tetrahedron
Array<Vec3> tetrahedron;
tetrahedron.push_back(Vec3(-5, 0, -5));
tetrahedron.push_back(Vec3(0, 0, 5));
tetrahedron.push_back(Vec3(5, 0, -5));
tetrahedron.push_back(Vec3(0, -5, 0));
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new ConvexHullShapeSettings(tetrahedron), RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Create box
Array<Vec3> box;
box.push_back(Vec3(5, 5, 5));
box.push_back(Vec3(-5, 5, 5));
box.push_back(Vec3(5, -5, 5));
box.push_back(Vec3(-5, -5, 5));
box.push_back(Vec3(5, 5, -5));
box.push_back(Vec3(-5, 5, -5));
box.push_back(Vec3(5, -5, -5));
box.push_back(Vec3(-5, -5, -5));
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new ConvexHullShapeSettings(box), RVec3(20, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Add a sphere of many points
Array<Vec3> sphere;
for (float theta = 0.0f; theta <= JPH_PI; theta += JPH_PI / 20.0f)
for (float phi = 0.0f; phi <= 2.0f * JPH_PI; phi += 2.0f * JPH_PI / 20.0f)
sphere.push_back(5.0f * Vec3::sUnitSpherical(theta, phi));
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new ConvexHullShapeSettings(sphere), RVec3(40, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Add a tapered cylinder of many points
Array<Vec3> tapered_cylinder;
for (float theta = 0.0f; theta <= 2.0f * JPH_PI; theta += JPH_PI / 128.0f)
{
tapered_cylinder.push_back(4.0f * Vec3(-0.1f, Sin(theta), Cos(theta)));
tapered_cylinder.push_back(4.5f * Vec3(0.1f, Sin(theta), Cos(theta)));
}
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new ConvexHullShapeSettings(tapered_cylinder), RVec3(60, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Create convex hull with on one side nearly coplanar faces
Array<Vec3> coplanar;
coplanar.push_back(Vec3(1.04298747f, 4.68531752f, 0.858853102f));
coplanar.push_back(Vec3(-1.00753999f, 4.63935566f, -0.959064901f));
coplanar.push_back(Vec3(-1.01861656f, 4.72096348f, 0.846121550f));
coplanar.push_back(Vec3(-2.37996006f, 1.26311386f, -1.10994697f));
coplanar.push_back(Vec3(0.213164970f, 0.0198628306f, -1.70677519f));
coplanar.push_back(Vec3(-2.27295995f, -0.899001241f, -0.472913086f));
coplanar.push_back(Vec3(-1.85078228f, -1.25204790f, 2.42339849f));
coplanar.push_back(Vec3(1.91183412f, -1.25204790f, 2.42339849f));
coplanar.push_back(Vec3(-2.75279832f, 3.25019693f, 1.67055058f));
coplanar.push_back(Vec3(-0.0697868019f, -2.78841114f, -0.422013819f));
coplanar.push_back(Vec3(2.26410985f, -0.918261647f, -0.493922710f));
coplanar.push_back(Vec3(0.765828013f, -2.82050991f, 1.91100550f));
coplanar.push_back(Vec3(2.33326006f, 1.26643038f, -1.18808103f));
coplanar.push_back(Vec3(-0.591650009f, 2.27845216f, -1.87628603f));
coplanar.push_back(Vec3(-2.22145009f, 3.04359150f, 0.234738767f));
coplanar.push_back(Vec3(-1.00753999f, 4.39097166f, -1.27783847f));
coplanar.push_back(Vec3(0.995577991f, 4.39734173f, -1.27900386f));
coplanar.push_back(Vec3(0.995577991f, 4.64572525f, -0.960230291f));
coplanar.push_back(Vec3(2.74527335f, 3.06491613f, 1.77647924f));
coplanar.push_back(Vec3(-1.53122997f, -2.18120861f, 2.31516361f));
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new ConvexHullShapeSettings(coplanar), RVec3(80, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Bodies with random convex shapes
default_random_engine random;
uniform_real_distribution<float> hull_size(0.1f, 10.0f);
for (int i = 0; i < 10; ++i)
{
// Create random points
Array<Vec3> points;
for (int j = 0; j < 20; ++j)
points.push_back(hull_size(random) * Vec3::sRandom(random));
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new ConvexHullShapeSettings(points), RVec3(-90.0f + i * 18.0f, 10, 20), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}
// Bodies with random convex polygons (this is not something you should be doing, but this tests the 2D convex hull shape generation and allows you to test the probe against them)
for (int i = 0; i < 10; ++i)
{
// Create random points
Array<Vec3> points;
for (int j = 0; j < 20; ++j)
{
Vec3 v = hull_size(random) * Vec3::sRandom(random);
v.SetZ(0.0f);
points.push_back(v);
}
// Convex hull needs to be created with convex radius of 0 because the shape has no volume, so we cannot move the planes backwards to make space for the convex radius
Ref<ShapeSettings> shape_settings = new ConvexHullShapeSettings(points, 0.0f);
BodyCreationSettings creation_settings(shape_settings, RVec3(-90.0f + i * 18.0f, 10, 40), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
// The polygon has no volume, so we need to provide a dummy mass and inertia for this shape
creation_settings.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
creation_settings.mMassPropertiesOverride.mMass = 1.0f;
creation_settings.mMassPropertiesOverride.mInertia = Mat44::sIdentity();
mBodyInterface->CreateAndAddBody(creation_settings, EActivation::Activate);
}
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class ConvexHullShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ConvexHullShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,55 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/CylinderShapeTest.h>
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(CylinderShapeTest)
{
JPH_ADD_BASE_CLASS(CylinderShapeTest, Test)
}
void CylinderShapeTest::Initialize()
{
// Floor
CreateFloor();
// Cylinder on flat part
RefConst<Shape> big_cylinder = new CylinderShape(2.5f, 2);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_cylinder, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Cylinder on round part
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_cylinder, RVec3(10, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Tower of cylinders
RefConst<Shape> long_cylinder = new CylinderShape(5, 1);
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < 2; ++j)
{
RVec3 position;
Quat rotation;
if (i & 1)
{
position = RVec3(-4.0f + 8.0f * j, 2.0f + 3.0f * i, -20.0f);
rotation = Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI);
}
else
{
position = RVec3(0, 2.0f + 3.0f * i, -20.0f - 4.0f + 8.0f * j);
rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI);
}
mBodyInterface->CreateAndAddBody(BodyCreationSettings(long_cylinder, position, rotation, EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}
}
// Tower of thin cylinders
RefConst<Shape> thin_cylinder = new CylinderShape(0.1f, 5.0f);
for (int i = 0; i < 10; ++i)
mBodyInterface->CreateAndAddBody(BodyCreationSettings(thin_cylinder, RVec3(20.0f, 10.0f - 1.0f * i, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class CylinderShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, CylinderShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,125 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/DeformedHeightFieldShapeTest.h>
#include <External/Perlin.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Collision/ShapeCast.h>
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(DeformedHeightFieldShapeTest)
{
JPH_ADD_BASE_CLASS(DeformedHeightFieldShapeTest, Test)
}
void DeformedHeightFieldShapeTest::Initialize()
{
constexpr float cCellSize = 1.0f;
constexpr float cMaxHeight = 2.5f;
constexpr float cSphereRadius = 2.0f;
// Create height samples
mHeightSamples.resize(cSampleCount * cSampleCount);
for (int y = 0; y < cSampleCount; ++y)
for (int x = 0; x < cSampleCount; ++x)
mHeightSamples[y * cSampleCount + x] = cMaxHeight * PerlinNoise3(float(x) * 8.0f / cSampleCount, 0, float(y) * 8.0f / cSampleCount, 256, 256, 256);
// Determine scale and offset of the terrain
Vec3 offset(-0.5f * cCellSize * cSampleCount, 0, -0.5f * cCellSize * cSampleCount);
Vec3 scale(cCellSize, 1.0f, cCellSize);
// Create height field
HeightFieldShapeSettings settings(mHeightSamples.data(), offset, scale, cSampleCount);
settings.mBlockSize = cBlockSize;
settings.mBitsPerSample = 8;
settings.mMinHeightValue = -15.0f;
mHeightField = StaticCast<HeightFieldShape>(settings.Create().Get());
mHeightFieldID = mBodyInterface->CreateAndAddBody(BodyCreationSettings(mHeightField, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
// Spheres on top of the terrain
RefConst<Shape> sphere_shape = new SphereShape(cSphereRadius);
for (float t = 0.2f; t < 12.4f; t += 0.1f)
{
// Get the center of the path
Vec3 center = offset + GetPathCenter(t);
// Cast a ray onto the terrain
RShapeCast shape_cast(sphere_shape, Vec3::sOne(), RMat44::sTranslation(RVec3(0, 10, 0) + center), Vec3(0, -20, 0));
ClosestHitCollisionCollector<CastShapeCollector> collector;
mPhysicsSystem->GetNarrowPhaseQuery().CastShape(shape_cast, { }, RVec3::sZero(), collector);
if (collector.mHit.mBodyID2 == mHeightFieldID)
{
// Create sphere on terrain
BodyCreationSettings bcs(sphere_shape, shape_cast.GetPointOnRay(collector.mHit.mFraction), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
mBodyInterface->CreateAndAddBody(bcs, EActivation::DontActivate);
}
}
}
Vec3 DeformedHeightFieldShapeTest::GetPathCenter(float inTime) const
{
constexpr float cOffset = 5.0f;
constexpr float cRadiusX = 60.0f;
constexpr float cRadiusY = 25.0f;
constexpr float cFallOff = 0.1f;
constexpr float cAngularSpeed = 2.0f;
constexpr float cDisplacementSpeed = 10.0f;
float fall_off = exp(-cFallOff * inTime);
float angle = cAngularSpeed * inTime;
return Vec3(cRadiusX * Cos(angle) * fall_off + 64.0f, 0, cOffset + cDisplacementSpeed * inTime + cRadiusY * Sin(angle) * fall_off);
}
void DeformedHeightFieldShapeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
{
constexpr float cPitRadius = 6.0f;
constexpr float cPitHeight = 1.0f;
constexpr float cSpeedScale = 2.0f;
// Calculate center of pit
Vec3 center = GetPathCenter(cSpeedScale * mTime);
mTime += inParams.mDeltaTime;
// Calculate affected area
int start_x = max((int)floor(center.GetX() - cPitRadius) & ~cBlockMask, 0);
int start_y = max((int)floor(center.GetZ() - cPitRadius) & ~cBlockMask, 0);
int count_x = min(((int)ceil(center.GetX() + cPitRadius) + cBlockMask) & ~cBlockMask, cSampleCount) - start_x;
int count_y = min(((int)ceil(center.GetZ() + cPitRadius) + cBlockMask) & ~cBlockMask, cSampleCount) - start_y;
if (count_x > 0 && count_y > 0)
{
// Remember COM before we change the height field
Vec3 old_com = mHeightField->GetCenterOfMass();
// A function to calculate the delta height at a certain distance from the center of the pit
constexpr float cHalfPi = 0.5f * JPH_PI;
auto pit_shape = [=](float inDistanceX, float inDistanceY) { return Cos(min(sqrt(Square(inDistanceX) + Square(inDistanceY)) * cHalfPi / cPitRadius, cHalfPi)); };
AABox affected_area;
for (int y = 0; y < count_y; ++y)
for (int x = 0; x < count_x; ++x)
{
// Update the height field
float delta = pit_shape(float(start_x) + x - center.GetX(), float(start_y) + y - center.GetZ()) * cPitHeight;
mHeightSamples[(start_y + y) * cSampleCount + start_x + x] -= delta;
// Keep track of affected area to wake up bodies
affected_area.Encapsulate(mHeightField->GetPosition(start_x + x, start_y + y));
}
mHeightField->SetHeights(start_x, start_y, count_x, count_y, mHeightSamples.data() + start_y * cSampleCount + start_x, cSampleCount, *mTempAllocator);
// Notify the shape that it has changed its bounding box
mBodyInterface->NotifyShapeChanged(mHeightFieldID, old_com, false, EActivation::DontActivate);
// Activate bodies in the affected area (a change in the height field doesn't wake up bodies)
affected_area.ExpandBy(Vec3::sReplicate(0.1f));
DefaultBroadPhaseLayerFilter broadphase_layer_filter = mPhysicsSystem->GetDefaultBroadPhaseLayerFilter(Layers::MOVING);
DefaultObjectLayerFilter object_layer_filter = mPhysicsSystem->GetDefaultLayerFilter(Layers::MOVING);
mBodyInterface->ActivateBodiesInAABox(affected_area, broadphase_layer_filter, object_layer_filter);
}
}

View File

@@ -0,0 +1,54 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
#include <Jolt/Physics/Collision/Shape/HeightFieldShape.h>
class DeformedHeightFieldShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, DeformedHeightFieldShapeTest)
// Description of the test
virtual const char * GetDescription() const override
{
return "Shows how to deform a height field shape after it has been created.";
}
// Initialize the test
virtual void Initialize() override;
// Update the test, called before the physics update
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
// Test will never be deterministic since we're modifying the height field shape and not saving it
virtual bool IsDeterministic() const override { return false; }
private:
// Get the center of the path at time inTime, this follows a path that resembles the Jolt logo
Vec3 GetPathCenter(float inTime) const;
// Size of the terrain
static constexpr int cSampleCount = 128;
// Size of a block in the terrain
static constexpr int cBlockSize = 4;
// Bits to mask out index within a block
static constexpr int cBlockMask = cBlockSize - 1;
// The list of original height samples, we keep this to avoid precision loss of repeatedly decompressing and recompressing height samples
Array<float> mHeightSamples;
// The height field shape
Ref<HeightFieldShape> mHeightField;
// ID of the height field body
BodyID mHeightFieldID;
// Current time
float mTime = 0.0f;
};

View File

@@ -0,0 +1,24 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/EmptyShapeTest.h>
#include <Jolt/Physics/Collision/Shape/EmptyShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(EmptyShapeTest)
{
JPH_ADD_BASE_CLASS(EmptyShapeTest, Test)
}
void EmptyShapeTest::Initialize()
{
// Floor
CreateFloor();
// Empty shape
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new EmptyShape(), RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class EmptyShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, EmptyShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,265 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/HeightFieldShapeTest.h>
#include <External/Perlin.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/Collision/PhysicsMaterialSimple.h>
#include <Jolt/Physics/Collision/CastResult.h>
#include <Jolt/Physics/Collision/RayCast.h>
#include <Application/DebugUI.h>
#include <Utils/ReadData.h>
#include <Utils/Log.h>
#include <Utils/DebugRendererSP.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(HeightFieldShapeTest)
{
JPH_ADD_BASE_CLASS(HeightFieldShapeTest, Test)
}
static int sTerrainType = 0;
static const char *sTerrainTypes[] = {
"Procedural Terrain 2^N",
"Procedural Terrain 2^N + 1",
"Heightfield 1",
"Flat 2^N",
"Flat 2^N + 1",
"No Collision 2^N",
"No Collision 2^N + 1"
};
void HeightFieldShapeTest::Initialize()
{
if (sTerrainType == 0 || sTerrainType == 1)
{
const int n = sTerrainType == 0? 128 : 129;
const float cell_size = 1.0f;
const float max_height = 5.0f;
// Create height samples
mTerrainSize = n;
mTerrain.resize(n * n);
for (int y = 0; y < n; ++y)
for (int x = 0; x < n; ++x)
mTerrain[y * n + x] = max_height * PerlinNoise3(float(x) * 8.0f / n, 0, float(y) * 8.0f / n, 256, 256, 256);
// Make some holes
mTerrain[2 * n + 2] = HeightFieldShapeConstants::cNoCollisionValue;
for (int y = 4; y < 33; ++y)
for (int x = 4; x < 33; ++x)
mTerrain[y * n + x] = HeightFieldShapeConstants::cNoCollisionValue;
// Make material indices
uint8 max_material_index = 0;
mMaterialIndices.resize(Square(n - 1));
for (int y = 0; y < n - 1; ++y)
for (int x = 0; x < n - 1; ++x)
{
uint8 material_index = uint8(round((Vec3(x * cell_size, 0, y * cell_size) - Vec3(n * cell_size / 2, 0, n * cell_size / 2)).Length() / 10.0f));
max_material_index = max(max_material_index, material_index);
mMaterialIndices[y * (n - 1) + x] = material_index;
}
// Mark the corners to validate that materials and heights match
mTerrain[0] = 0.0f;
mTerrain[n - 1] = 10.0f;
mTerrain[(n - 1) * n] = 20.0f;
mTerrain[n * n - 1] = 30.0f;
mMaterialIndices[0] = 0;
mMaterialIndices[n - 2] = 1;
mMaterialIndices[(n - 2) * (n - 1)] = 2;
mMaterialIndices[Square(n - 1) - 1] = 3;
// Create materials
for (uint8 i = 0; i <= max_material_index; ++i)
mMaterials.push_back(new PhysicsMaterialSimple("Material " + ConvertToString(uint(i)), Color::sGetDistinctColor(i)));
// Determine scale and offset (deliberately apply extra offset and scale in Y direction)
mTerrainOffset = Vec3(-0.5f * cell_size * n, -2.0f, -0.5f * cell_size * n);
mTerrainScale = Vec3(cell_size, 1.5f, cell_size);
}
else if (sTerrainType == 2)
{
const int n = 1024;
const float cell_size = 0.5f;
// Get height samples
Array<uint8> data = ReadData("heightfield1.bin");
if (data.size() != sizeof(float) * n * n)
FatalError("Invalid file size");
mTerrainSize = n;
mTerrain.resize(n * n);
memcpy(mTerrain.data(), data.data(), n * n * sizeof(float));
// Determine scale and offset
mTerrainOffset = Vec3(-0.5f * cell_size * n, 0.0f, -0.5f * cell_size * n);
mTerrainScale = Vec3(cell_size, 1.0f, cell_size);
}
else if (sTerrainType == 3 || sTerrainType == 4)
{
const int n = sTerrainType == 3? 128 : 129;
const float cell_size = 1.0f;
const float height = JPH_PI;
// Determine scale and offset
mTerrainOffset = Vec3(-0.5f * cell_size * n, 0.0f, -0.5f * cell_size * n);
mTerrainScale = Vec3(cell_size, 1.0f, cell_size);
// Mark the entire terrain as single height
mTerrainSize = n;
mTerrain.resize(n * n);
for (float &v : mTerrain)
v = height;
}
else if (sTerrainType == 5 || sTerrainType == 6)
{
const int n = sTerrainType == 4? 128 : 129;
const float cell_size = 1.0f;
// Determine scale and offset
mTerrainOffset = Vec3(-0.5f * cell_size * n, 0.0f, -0.5f * cell_size * n);
mTerrainScale = Vec3(cell_size, 1.0f, cell_size);
// Mark the entire terrain as no collision
mTerrainSize = n;
mTerrain.resize(n * n);
for (float &v : mTerrain)
v = HeightFieldShapeConstants::cNoCollisionValue;
}
// Create height field
HeightFieldShapeSettings settings(mTerrain.data(), mTerrainOffset, mTerrainScale, mTerrainSize, mMaterialIndices.data(), mMaterials);
settings.mBlockSize = 1 << sBlockSizeShift;
settings.mBitsPerSample = sBitsPerSample;
mHeightField = StaticCast<HeightFieldShape>(settings.Create().Get());
mBodyInterface->CreateAndAddBody(BodyCreationSettings(mHeightField, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
// Validate it
float max_diff = -1.0f;
uint max_diff_x = 0, max_diff_y = 0;
float min_height = FLT_MAX, max_height = -FLT_MAX, avg_diff = 0.0f;
for (uint y = 0; y < mTerrainSize; ++y)
for (uint x = 0; x < mTerrainSize; ++x)
{
float h1 = mTerrain[y * mTerrainSize + x];
if (h1 != HeightFieldShapeConstants::cNoCollisionValue)
{
h1 = mTerrainOffset.GetY() + mTerrainScale.GetY() * h1;
if (mHeightField->IsNoCollision(x, y))
FatalError("No collision where there should be");
float h2 = mHeightField->GetPosition(x, y).GetY();
float diff = abs(h2 - h1);
if (diff > max_diff)
{
max_diff = diff;
max_diff_x = x;
max_diff_y = y;
}
min_height = min(min_height, h1);
max_height = max(max_height, h1);
avg_diff += diff;
}
else
{
if (!mHeightField->IsNoCollision(x, y))
FatalError("Collision where there shouldn't be");
}
}
// Calculate relative error
float rel_error = min_height < max_height? 100.0f * max_diff / (max_height - min_height) : 0.0f;
// Max error we expect given sBitsPerSample (normally the error should be much lower because we quantize relative to the block rather than the full height)
float max_error = 0.5f * 100.0f / ((1 << sBitsPerSample) - 1);
// Calculate average
avg_diff /= mTerrainSize * mTerrainSize;
// Calculate amount of memory used
Shape::Stats stats = mHeightField->GetStats();
// Trace stats
Trace("Block size: %d, bits per sample: %d, min height: %g, max height: %g, avg diff: %g, max diff: %g at (%d, %d), relative error: %g%%, size: %u bytes", 1 << sBlockSizeShift, sBitsPerSample, (double)min_height, (double)max_height, (double)avg_diff, (double)max_diff, max_diff_x, max_diff_y, (double)rel_error, stats.mSizeBytes);
if (rel_error > max_error)
FatalError("Error too big!");
// Determine terrain height
RayCastResult result;
RVec3 start(0, 1000, 0);
Vec3 direction(0, -2000, 0);
RRayCast ray { start, direction };
if (mPhysicsSystem->GetNarrowPhaseQuery().CastRay(ray, result, SpecifiedBroadPhaseLayerFilter(BroadPhaseLayers::NON_MOVING), SpecifiedObjectLayerFilter(Layers::NON_MOVING)))
mHitPos = ray.GetPointOnRay(result.mFraction);
// Dynamic body
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)), mHitPos + Vec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}
void HeightFieldShapeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
{
// Test the 'GetHeight' function and draw a marker on the surface
Vec3 test_pos = Vec3(inParams.mCameraState.mPos) + 10.0f * inParams.mCameraState.mForward, surface_pos;
SubShapeID sub_shape_id;
if (mHeightField->ProjectOntoSurface(test_pos, surface_pos, sub_shape_id))
{
Vec3 surface_normal = mHeightField->GetSurfaceNormal(sub_shape_id, surface_pos);
DrawMarkerSP(mDebugRenderer, surface_pos, Color::sWhite, 1.0f);
DrawArrowSP(mDebugRenderer, surface_pos, surface_pos + surface_normal, Color::sRed, 0.1f);
}
// Draw the original uncompressed terrain
if (sShowOriginalTerrain)
for (uint y = 0; y < mTerrainSize; ++y)
for (uint x = 0; x < mTerrainSize; ++x)
{
// Get original height
float h = mTerrain[y * mTerrainSize + x];
if (h == HeightFieldShapeConstants::cNoCollisionValue)
continue;
// Get original position
Vec3 original = mTerrainOffset + mTerrainScale * Vec3(float(x), h, float(y));
// Get compressed position
Vec3 compressed = mHeightField->GetPosition(x, y);
// Draw marker that is red when error is too big and green when not
const float cMaxError = 0.1f;
float error = (original - compressed).Length();
uint8 c = uint8(round(255.0f * min(error / cMaxError, 1.0f)));
DrawMarkerSP(mDebugRenderer, original, Color(c, 255 - c, 0, 255), 0.1f);
}
}
void HeightFieldShapeTest::GetInitialCamera(CameraState &ioState) const
{
// Correct camera pos for hit position
ioState.mPos += mHitPos;
}
void HeightFieldShapeTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
{
inUI->CreateTextButton(inSubMenu, "Select Terrain", [this, inUI]() {
UIElement *terrain_name = inUI->CreateMenu();
for (uint i = 0; i < size(sTerrainTypes); ++i)
inUI->CreateTextButton(terrain_name, sTerrainTypes[i], [this, i]() { sTerrainType = i; RestartTest(); });
inUI->ShowMenu(terrain_name);
});
inUI->CreateTextButton(inSubMenu, "Configuration Settings", [this, inUI]() {
UIElement *terrain_settings = inUI->CreateMenu();
inUI->CreateComboBox(terrain_settings, "Block Size", { "2", "4", "8" }, sBlockSizeShift - 1, [=](int inItem) { sBlockSizeShift = inItem + 1; });
inUI->CreateSlider(terrain_settings, "Bits Per Sample", (float)sBitsPerSample, 1.0f, 8.0f, 1.0f, [=](float inValue) { sBitsPerSample = (int)inValue; });
inUI->CreateTextButton(terrain_settings, "Accept", [this]() { RestartTest(); });
inUI->ShowMenu(terrain_settings);
});
inUI->CreateCheckBox(inSubMenu, "Show Original Terrain", sShowOriginalTerrain, [](UICheckBox::EState inState) { sShowOriginalTerrain = inState == UICheckBox::STATE_CHECKED; });
}

View File

@@ -0,0 +1,48 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
#include <Jolt/Physics/Collision/Shape/HeightFieldShape.h>
class HeightFieldShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, HeightFieldShapeTest)
// Initialize the test
virtual void Initialize() override;
// Update the test, called before the physics update
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
// Override to specify the initial camera state (local to GetCameraPivot)
virtual void GetInitialCamera(CameraState &ioState) const override;
// Optional settings menu
virtual bool HasSettingsMenu() const override { return true; }
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
// Original (uncompressed) terrain
Array<float> mTerrain;
PhysicsMaterialList mMaterials;
Array<uint8> mMaterialIndices;
uint mTerrainSize;
Vec3 mTerrainOffset;
Vec3 mTerrainScale;
// Block size = 1 << sBlockSizeShift
inline static int sBlockSizeShift = 2;
// Bits per sample
inline static int sBitsPerSample = 8;
// Draw the terrain
inline static bool sShowOriginalTerrain = false;
RefConst<HeightFieldShape> mHeightField;
RVec3 mHitPos = RVec3::sZero();
};

View File

@@ -0,0 +1,57 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/MeshShapeTest.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
#include <Jolt/Physics/Collision/PhysicsMaterialSimple.h>
#include <Jolt/Geometry/Triangle.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(MeshShapeTest)
{
JPH_ADD_BASE_CLASS(MeshShapeTest, Test)
}
void MeshShapeTest::Initialize()
{
// Create regular grid of triangles
uint32 max_material_index = 0;
TriangleList triangles;
for (int x = -10; x < 10; ++x)
for (int z = -10; z < 10; ++z)
{
float x1 = 10.0f * x;
float z1 = 10.0f * z;
float x2 = x1 + 10.0f;
float z2 = z1 + 10.0f;
Float3 v1 = Float3(x1, 0, z1);
Float3 v2 = Float3(x2, 0, z1);
Float3 v3 = Float3(x1, 0, z2);
Float3 v4 = Float3(x2, 0, z2);
uint32 material_index = uint32((Vec3(v1) + Vec3(v2) + Vec3(v3) + Vec3(v4)).Length() / 40.0f);
max_material_index = max(max_material_index, material_index);
triangles.push_back(Triangle(v1, v3, v4, material_index));
triangles.push_back(Triangle(v1, v4, v2, material_index));
}
// Create materials
PhysicsMaterialList materials;
for (uint i = 0; i <= max_material_index; ++i)
materials.push_back(new PhysicsMaterialSimple("Material " + ConvertToString(i), Color::sGetDistinctColor(i)));
// Floor
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new MeshShapeSettings(triangles, std::move(materials)), RVec3::sZero(), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
// 1 body with zero friction to test active edge detection
Ref<BoxShape> box_shape = new BoxShape(Vec3(2.0f, 2.0f, 2.0f), cDefaultConvexRadius, new PhysicsMaterialSimple("Box Material", Color::sYellow));
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(box_shape, RVec3(0, 55.0f, -50.0f), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
body.SetFriction(0.0f);
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class MeshShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, MeshShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,95 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/MeshShapeUserDataTest.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/RayCast.h>
#include <Jolt/Physics/Collision/CastResult.h>
#include <Jolt/Geometry/Triangle.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
#include <Renderer/DebugRendererImp.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(MeshShapeUserDataTest)
{
JPH_ADD_BASE_CLASS(MeshShapeUserDataTest, Test)
}
void MeshShapeUserDataTest::Initialize()
{
std::default_random_engine random;
// Create regular grid of triangles
uint32 user_data = 0;
TriangleList triangles[2];
for (int x = -10; x < 10; ++x)
for (int z = -10; z < 10; ++z)
{
float x1 = 10.0f * x;
float z1 = 10.0f * z;
float x2 = x1 + 10.0f;
float z2 = z1 + 10.0f;
Float3 v1 = Float3(x1, 0, z1);
Float3 v2 = Float3(x2, 0, z1);
Float3 v3 = Float3(x1, 0, z2);
Float3 v4 = Float3(x2, 0, z2);
triangles[random() & 1].push_back(Triangle(v1, v3, v4, 0, user_data++));
triangles[random() & 1].push_back(Triangle(v1, v4, v2, 0, user_data++));
}
// Create a compound with 2 meshes
StaticCompoundShapeSettings compound_settings;
compound_settings.SetEmbedded();
for (TriangleList &t : triangles)
{
// Shuffle the triangles
std::shuffle(t.begin(), t.end(), random);
// Create mesh
MeshShapeSettings mesh_settings(t);
mesh_settings.mPerTriangleUserData = true;
compound_settings.AddShape(Vec3::sZero(), Quat::sIdentity(), mesh_settings.Create().Get());
}
// Create body
mBodyInterface->CreateAndAddBody(BodyCreationSettings(&compound_settings, RVec3::sZero(), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
// 1 body with zero friction
BodyCreationSettings bcs(new BoxShape(Vec3::sReplicate(2.0f)), RVec3(0, 55.0f, -50.0f), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
bcs.mFriction = 0.0f;
bcs.mEnhancedInternalEdgeRemoval = true; // Needed because the 2 meshes have a lot of active edges
mBodyInterface->CreateAndAddBody(bcs, EActivation::Activate);
}
void MeshShapeUserDataTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
{
// Cast a ray
RayCastResult hit;
RRayCast ray(inParams.mCameraState.mPos, inParams.mCameraState.mForward * 100.0f);
mPhysicsSystem->GetNarrowPhaseQuery().CastRay(ray, hit);
// Get body (if there was a hit)
BodyLockRead lock(mPhysicsSystem->GetBodyLockInterface(), hit.mBodyID);
if (lock.SucceededAndIsInBroadPhase())
{
// Get the leaf shape (mesh shape in this case)
SubShapeID remainder;
const Shape *shape = lock.GetBody().GetShape()->GetLeafShape(hit.mSubShapeID2, remainder);
if (shape->GetType() == EShapeType::Mesh)
{
// Get user data from the triangle that was hit
uint32 user_data = static_cast<const MeshShape *>(shape)->GetTriangleUserData(remainder);
// Draw it on screen
RVec3 hit_pos = ray.GetPointOnRay(hit.mFraction);
mDebugRenderer->DrawText3D(hit_pos, StringFormat("UserData: %d", user_data).c_str());
}
}
}

View File

@@ -0,0 +1,23 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class MeshShapeUserDataTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, MeshShapeUserDataTest)
// Description of the test
virtual const char *GetDescription() const override
{
return "Shows how to store per triangle user data in a mesh shape and how to retrieve it.";
}
// See: Test
virtual void Initialize() override;
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
};

View File

@@ -0,0 +1,172 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/MutableCompoundShapeTest.h>
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
#include <Jolt/Physics/Collision/Shape/MutableCompoundShape.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h>
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Core/StreamWrapper.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(MutableCompoundShapeTest)
{
JPH_ADD_BASE_CLASS(MutableCompoundShapeTest, Test)
}
void MutableCompoundShapeTest::Initialize()
{
// Floor (extra thick because we can randomly add sub shapes that then may stick out underneath the floor and cause objects to be pushed through)
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(100.0f, 10.0f, 100.0f), 0.0f), RVec3(0.0f, -10.0f, 0.0f), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
// Compound with sub compound and rotation
StaticCompoundShapeSettings sub_compound_settings;
sub_compound_settings.AddShape(Vec3(0, 1.5f, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new BoxShape(Vec3(1.5f, 0.25f, 0.2f)));
sub_compound_settings.AddShape(Vec3(1.5f, 0, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new CylinderShape(1.5f, 0.2f));
sub_compound_settings.AddShape(Vec3(0, 0, 1.5f), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), new TaperedCapsuleShapeSettings(1.5f, 0.25f, 0.2f));
mSubCompound = sub_compound_settings.Create().Get();
for (int i = 0; i < 10; ++i)
{
// Create a mutable compound per body and fill it up with 2 shapes initially
Ref<MutableCompoundShapeSettings> compound_shape = new MutableCompoundShapeSettings;
compound_shape->AddShape(Vec3::sZero(), Quat::sRotation(Vec3::sAxisX(), -0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), mSubCompound);
compound_shape->AddShape(Vec3::sZero(), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), -0.75f * JPH_PI), mSubCompound);
// Create a body
BodyID body_id = mBodyInterface->CreateAndAddBody(BodyCreationSettings(compound_shape, RVec3(0, 10.0f + 5.0f * i, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
mBodyIDs.push_back(body_id);
}
}
void MutableCompoundShapeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
{
BodyInterface &no_lock = mPhysicsSystem->GetBodyInterfaceNoLock();
uniform_real_distribution<float> roll_distribution(0, 1);
for (BodyID id : mBodyIDs)
{
BodyLockWrite lock(mPhysicsSystem->GetBodyLockInterface(), id);
if (lock.Succeeded())
{
Body &body = lock.GetBody();
// Get the shape
MutableCompoundShape *shape = static_cast<MutableCompoundShape *>(const_cast<Shape *>(body.GetShape()));
// Remember center of mass from before changes
Vec3 old_com = shape->GetCenterOfMass();
// Consistently seeded random engine so that bodies move in a predictable way
default_random_engine consistent_random;
// Simulate an engine data structure with strided positions/rotations
struct PositionRotation
{
Vec3 mPosition;
Quat mRotation;
};
Array<PositionRotation> pos_rot;
// Animate sub shapes
uint count = shape->GetNumSubShapes();
for (uint i = 0; i < count; ++i)
{
const CompoundShape::SubShape &sub_shape = shape->GetSubShape(i);
pos_rot.push_back({ Vec3::sZero(), (Quat::sRotation(Vec3::sRandom(consistent_random), DegreesToRadians(10.0f) * inParams.mDeltaTime) * sub_shape.GetRotation()).Normalized() });
}
// Set the new rotations/orientations on the sub shapes
shape->ModifyShapes(0, count, &pos_rot.front().mPosition, &pos_rot.front().mRotation, sizeof(PositionRotation), sizeof(PositionRotation));
// Initialize frame dependent random number generator
// Note: Explicitly using the Mersenne Twister random generator as on some platforms you get the seed back as the first random number
mt19937 frame_random(mFrameNumber++);
// Roll the dice
float roll = roll_distribution(frame_random);
if (roll < 0.001f && count > 1)
{
// Remove a random shape
uniform_int_distribution<uint> index_distribution(0, count - 1);
shape->RemoveShape(index_distribution(frame_random));
}
else if (roll < 0.002f && count < 10)
{
// Add a shape in a random rotation
shape->AddShape(Vec3::sZero(), Quat::sRandom(frame_random), mSubCompound);
}
// Ensure that the center of mass is updated
shape->AdjustCenterOfMass();
// Since we're already locking the body, we don't need to lock it again
// We always update the mass properties of the shape because we're reorienting them every frame
no_lock.NotifyShapeChanged(id, old_com, true, EActivation::Activate);
}
}
}
void MutableCompoundShapeTest::SaveState(StateRecorder &inStream) const
{
inStream.Write(mFrameNumber);
for (BodyID id : mBodyIDs)
{
BodyLockRead lock(mPhysicsSystem->GetBodyLockInterface(), id);
if (lock.Succeeded())
{
const Body &body = lock.GetBody();
// Write the shape as a binary string
stringstream data;
StreamOutWrapper stream_out(data);
body.GetShape()->SaveBinaryState(stream_out);
inStream.Write(data.str());
}
}
}
void MutableCompoundShapeTest::RestoreState(StateRecorder &inStream)
{
inStream.Read(mFrameNumber);
for (BodyID id : mBodyIDs)
{
BodyLockWrite lock(mPhysicsSystem->GetBodyLockInterface(), id);
if (lock.Succeeded())
{
Body &body = lock.GetBody();
// Read the shape as a binary string
string str;
if (inStream.IsValidating())
{
stringstream data;
StreamOutWrapper stream_out(data);
body.GetShape()->SaveBinaryState(stream_out);
str = data.str();
}
inStream.Read(str);
// Deserialize the shape
stringstream data(str);
StreamInWrapper stream_in(data);
Shape::ShapeResult result = Shape::sRestoreFromBinaryState(stream_in);
MutableCompoundShape *shape = StaticCast<MutableCompoundShape>(result.Get());
// Restore the pointers to the sub compound
ShapeList sub_shapes(shape->GetNumSubShapes(), mSubCompound);
shape->RestoreSubShapeState(sub_shapes.data(), (uint)sub_shapes.size());
// Update the shape (we're under lock protection, so use the no lock interface)
mPhysicsSystem->GetBodyInterfaceNoLock().SetShape(id, shape, false, EActivation::DontActivate);
}
}
}

View File

@@ -0,0 +1,30 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class MutableCompoundShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, MutableCompoundShapeTest)
// See: Test
virtual void Initialize() override;
// Update the test, called before the physics update
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
// Saving / restoring state for replay
virtual void SaveState(StateRecorder &inStream) const override;
virtual void RestoreState(StateRecorder &inStream) override;
private:
BodyIDVector mBodyIDs;
RefConst<Shape> mSubCompound;
int mFrameNumber = 0;
};

View File

@@ -0,0 +1,57 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/OffsetCenterOfMassShapeTest.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(OffsetCenterOfMassShapeTest)
{
JPH_ADD_BASE_CLASS(OffsetCenterOfMassShapeTest, Test)
}
void OffsetCenterOfMassShapeTest::Initialize()
{
// Floor
Body &floor = CreateFloor();
floor.SetFriction(1.0f);
Ref<ShapeSettings> sphere = new SphereShapeSettings(1.0f);
Ref<OffsetCenterOfMassShapeSettings> left = new OffsetCenterOfMassShapeSettings(Vec3(-1, 0, 0), sphere);
Ref<OffsetCenterOfMassShapeSettings> right = new OffsetCenterOfMassShapeSettings(Vec3(1, 0, 0), sphere);
// Sphere with center of mass moved to the left side
Body &body_left = *mBodyInterface->CreateBody(BodyCreationSettings(left, RVec3(-5, 5, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
body_left.SetFriction(1.0f);
mBodyInterface->AddBody(body_left.GetID(), EActivation::Activate);
// Sphere with center of mass centered
Body &body_center = *mBodyInterface->CreateBody(BodyCreationSettings(sphere, RVec3(0, 5, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
body_center.SetFriction(1.0f);
mBodyInterface->AddBody(body_center.GetID(), EActivation::Activate);
// Sphere with center of mass moved to the right side
Body &body_right = *mBodyInterface->CreateBody(BodyCreationSettings(right, RVec3(5, 5, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
body_right.SetFriction(1.0f);
mBodyInterface->AddBody(body_right.GetID(), EActivation::Activate);
// Create body and apply a large angular impulse so see that it spins around the COM
BodyCreationSettings bcs(new OffsetCenterOfMassShapeSettings(Vec3(-3, 0, 0), new SphereShapeSettings(1.0f)), RVec3(-5, 5, 10), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
bcs.mGravityFactor = 0.0f;
bcs.mLinearDamping = 0.0f;
bcs.mAngularDamping = 0.0f;
Body *body_rotating1 = mBodyInterface->CreateBody(bcs);
mBodyInterface->AddBody(body_rotating1->GetID(), EActivation::Activate);
body_rotating1->AddAngularImpulse(Vec3(0, 1.0e6f, 0));
// Create the same body but this time apply a torque
bcs.mPosition = RVec3(5, 5, 10);
Body *body_rotating2 = mBodyInterface->CreateBody(bcs);
mBodyInterface->AddBody(body_rotating2->GetID(), EActivation::Activate);
body_rotating2->AddTorque(Vec3(0, 1.0e6f * 60.0f, 0)); // Assuming physics sim is at 60Hz here, otherwise the bodies won't rotate with the same speed
}

View File

@@ -0,0 +1,17 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
// Tests the OffsetCenterOfMass shape
class OffsetCenterOfMassShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, OffsetCenterOfMassShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,27 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/PlaneShapeTest.h>
#include <Jolt/Physics/Collision/Shape/PlaneShape.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(PlaneShapeTest)
{
JPH_ADD_BASE_CLASS(PlaneShapeTest, Test)
}
void PlaneShapeTest::Initialize()
{
// Create a plane as floor
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new PlaneShape(Plane(Vec3(0.1f, 1.0f, 0.0f).Normalized(), 1.0f), nullptr, 100), RVec3(0, 0, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
// Add some shapes
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new SphereShape(0.5f), RVec3(0, 1, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.5f)), RVec3(2, 1, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class PlaneShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, PlaneShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,35 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/RotatedTranslatedShapeTest.h>
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(RotatedTranslatedShapeTest)
{
JPH_ADD_BASE_CLASS(RotatedTranslatedShapeTest, Test)
}
void RotatedTranslatedShapeTest::Initialize()
{
// Floor
CreateFloor();
// Create a cone centered on the origin with the point pointing upwards
Array<Vec3> points;
points.push_back(Vec3(0, 2.5f, 0));
for (float a = 0; a < DegreesToRadians(360); a += DegreesToRadians(36))
points.push_back(Vec3(Sin(a), -2.5f, Cos(a)));
Ref<ConvexHullShapeSettings> convex_hull = new ConvexHullShapeSettings(points);
// Offset and rotate so that the cone is upside down on its point
Ref<RotatedTranslatedShapeSettings> rot_trans = new RotatedTranslatedShapeSettings(Vec3(0, 2.5f, 0), Quat::sRotation(Vec3::sAxisX(), JPH_PI), convex_hull);
// Place at 0 so that the point touches the floor
mBodyInterface->CreateAndAddBody(BodyCreationSettings(rot_trans, RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}

View File

@@ -0,0 +1,17 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
// Tests the RotatedTranslated shape
class RotatedTranslatedShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, RotatedTranslatedShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,32 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/SphereShapeTest.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(SphereShapeTest)
{
JPH_ADD_BASE_CLASS(SphereShapeTest, Test)
}
void SphereShapeTest::Initialize()
{
// Floor
CreateFloor();
// Create different sized spheres
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new SphereShape(1.0f), RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new SphereShape(2.0f), RVec3(0, 10, 10), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new SphereShape(0.5f), RVec3(0, 10, 20), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Tower of spheres
for (int i = 0; i < 10; ++i)
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new SphereShape(0.5f), RVec3(10, 10 + 1.5f * i, 0), Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class SphereShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SphereShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,62 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/StaticCompoundShapeTest.h>
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
#include <Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h>
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(StaticCompoundShapeTest)
{
JPH_ADD_BASE_CLASS(StaticCompoundShapeTest, Test)
}
void StaticCompoundShapeTest::Initialize()
{
// Floor
CreateFloor();
// Simple compound
Ref<StaticCompoundShapeSettings> compound_shape1 = new StaticCompoundShapeSettings;
compound_shape1->AddShape(Vec3::sZero(), Quat::sIdentity(), new CapsuleShape(5, 1));
compound_shape1->AddShape(Vec3(0, -5, 0), Quat::sIdentity(), new SphereShape(2));
compound_shape1->AddShape(Vec3(0, 5, 0), Quat::sIdentity(), new SphereShape(2));
// Compound with sub compound and rotation
Ref<StaticCompoundShapeSettings> sub_compound = new StaticCompoundShapeSettings;
sub_compound->AddShape(Vec3(0, 1.5f, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new BoxShape(Vec3(1.5f, 0.25f, 0.2f)));
sub_compound->AddShape(Vec3(1.5f, 0, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new CylinderShape(1.5f, 0.2f));
sub_compound->AddShape(Vec3(0, 0, 1.5f), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), new TaperedCapsuleShapeSettings(1.5f, 0.25f, 0.2f));
Ref<StaticCompoundShapeSettings> compound_shape2 = new StaticCompoundShapeSettings;
compound_shape2->AddShape(Vec3(0, 0, 0), Quat::sRotation(Vec3::sAxisX(), -0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), sub_compound);
compound_shape2->AddShape(Vec3(0, -0.1f, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), -0.75f * JPH_PI), sub_compound);
// Compound with large amount of sub shapes
Ref<StaticCompoundShapeSettings> compound_shape3 = new StaticCompoundShapeSettings;
for (int y = -2; y <= 2; ++y)
for (int x = -2; x <= 2; ++x)
for (int z = -2; z <= 2; ++z)
compound_shape3->AddShape(Vec3(0.5f * x, 0.5f * y, 0.5f * z), Quat::sRotation(Vec3::sAxisX(), -0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), new BoxShape(Vec3::sReplicate(0.5f)));
Ref<StaticCompoundShapeSettings> shapes[] = { compound_shape1, compound_shape2, compound_shape3 };
for (int i = 0; i < 10; ++i)
for (int j = 0; j < 3; ++j)
{
Quat rotation;
if ((i & 1) == 0)
rotation = Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI);
else
rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(shapes[j], RVec3(0, 10.0f + 4.0f * i, j * 20.0f), rotation, EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class StaticCompoundShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, StaticCompoundShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,56 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/TaperedCapsuleShapeTest.h>
#include <Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(TaperedCapsuleShapeTest)
{
JPH_ADD_BASE_CLASS(TaperedCapsuleShapeTest, Test)
}
void TaperedCapsuleShapeTest::Initialize()
{
// Floor
CreateFloor();
RefConst<ShapeSettings> big_taperedcapsule = new TaperedCapsuleShapeSettings(2.0f, 1.0f, 3.0f);
RefConst<ShapeSettings> big_taperedcapsule2 = new TaperedCapsuleShapeSettings(2.0f, 3.0f, 1.0f);
// Tapered capsule on outer sphere
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_taperedcapsule, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Tapered capsule on other outer sphere
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_taperedcapsule2, RVec3(10, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Tapered capsule on side
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_taperedcapsule, RVec3(20, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
RefConst<ShapeSettings> long_taperedcapsule = new TaperedCapsuleShapeSettings(5, 0.5f, 1.0f);
// Tower of tapered capsules
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < 2; ++j)
{
RVec3 position;
Quat rotation;
if (i & 1)
{
position = RVec3(-4.0f + 8.0f * j, 2.0f + 3.0f * i, -20.0f);
rotation = Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI + (j & 1) * JPH_PI);
}
else
{
position = RVec3(0, 2.0f + 3.0f * i, -20.0f - 4.0f + 8.0f * j);
rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI + (j & 1) * JPH_PI);
}
mBodyInterface->CreateAndAddBody(BodyCreationSettings(long_taperedcapsule, position, rotation, EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}
}
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class TaperedCapsuleShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, TaperedCapsuleShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,68 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/TaperedCylinderShapeTest.h>
#include <Jolt/Physics/Collision/Shape/TaperedCylinderShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(TaperedCylinderShapeTest)
{
JPH_ADD_BASE_CLASS(TaperedCylinderShapeTest, Test)
}
void TaperedCylinderShapeTest::Initialize()
{
// Floor
CreateFloor();
RefConst<ShapeSettings> big_taperedcylinder = new TaperedCylinderShapeSettings(2.0f, 1.0f, 3.0f);
RefConst<ShapeSettings> big_taperedcylinder2 = new TaperedCylinderShapeSettings(2.0f, 3.0f, 1.0f);
// Tapered cylinder on large radius
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_taperedcylinder, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Tapered cylinder on small radius
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_taperedcylinder2, RVec3(10, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Tapered cylinder on side
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_taperedcylinder, RVec3(20, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
RefConst<ShapeSettings> big_cone = new TaperedCylinderShapeSettings(2.0f, 0.0f, 3.0f, 0.0f);
RefConst<ShapeSettings> big_cone2 = new TaperedCylinderShapeSettings(2.0f, 3.0f, 0.0f, 0.0f);
// Cone on large radius
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_cone, RVec3(0, 10, 10), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Cone on small radius
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_cone2, RVec3(10, 10, 10), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
// Cone on side
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_cone, RVec3(20, 10, 10), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
RefConst<ShapeSettings> long_taperedcylinder = new TaperedCylinderShapeSettings(5, 0.5f, 1.0f);
// Tower of tapered cylinders
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < 2; ++j)
{
RVec3 position;
Quat rotation;
if (i & 1)
{
position = RVec3(-4.0f + 8.0f * j, 2.0f + 3.0f * i, -20.0f);
rotation = Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI + (j & 1) * JPH_PI);
}
else
{
position = RVec3(0, 2.0f + 3.0f * i, -20.0f - 4.0f + 8.0f * j);
rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI + (j & 1) * JPH_PI);
}
mBodyInterface->CreateAndAddBody(BodyCreationSettings(long_taperedcylinder, position, rotation, EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}
}
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class TaperedCylinderShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, TaperedCylinderShapeTest)
// See: Test
virtual void Initialize() override;
};

View File

@@ -0,0 +1,27 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <TestFramework.h>
#include <Tests/Shapes/TriangleShapeTest.h>
#include <Jolt/Physics/Collision/Shape/TriangleShape.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Layers.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(TriangleShapeTest)
{
JPH_ADD_BASE_CLASS(TriangleShapeTest, Test)
}
void TriangleShapeTest::Initialize()
{
// Single triangle
RefConst<TriangleShape> triangle_shape = new TriangleShape(Vec3(-10, -1, 0), Vec3(0, 1, 10), Vec3(10, -2, -10), 0.01f);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(triangle_shape, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
// Create a box above the triangle
RefConst<Shape> box_shape = new BoxShape(Vec3(0.2f, 0.2f, 0.4f), 0.01f);
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(0, 5, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
}

View File

@@ -0,0 +1,16 @@
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Tests/Test.h>
class TriangleShapeTest : public Test
{
public:
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, TriangleShapeTest)
// See: Test
virtual void Initialize() override;
};