Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ActivateDuringUpdateTest.h>
|
||||
#include <Layers.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ActivateDuringUpdateTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ActivateDuringUpdateTest, Test)
|
||||
}
|
||||
|
||||
void ActivateDuringUpdateTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
BodyCreationSettings settings;
|
||||
settings.SetShape(new BoxShape(Vec3::sReplicate(0.5f)));
|
||||
settings.mMotionType = EMotionType::Dynamic;
|
||||
settings.mObjectLayer = Layers::MOVING;
|
||||
|
||||
const int cNumBodies = 3;
|
||||
const float cPenetrationSlop = mPhysicsSystem->GetPhysicsSettings().mPenetrationSlop;
|
||||
|
||||
for (int i = 0; i < cNumBodies; ++i)
|
||||
{
|
||||
settings.mPosition = RVec3(i * (1.0f - cPenetrationSlop), 2.0f, 0);
|
||||
BodyID body_id = mBodyInterface->CreateBody(settings)->GetID();
|
||||
mBodyInterface->AddBody(body_id, EActivation::DontActivate);
|
||||
if (i == 0)
|
||||
mBodyInterface->SetLinearVelocity(body_id, Vec3(500, 0, 0));
|
||||
}
|
||||
|
||||
for (int i = 0; i < cNumBodies; ++i)
|
||||
{
|
||||
settings.mPosition = RVec3(i * (1.0f - cPenetrationSlop), 2.0f, 2.0f);
|
||||
BodyID body_id = mBodyInterface->CreateBody(settings)->GetID();
|
||||
mBodyInterface->AddBody(body_id, EActivation::DontActivate);
|
||||
if (i == cNumBodies - 1)
|
||||
mBodyInterface->SetLinearVelocity(body_id, Vec3(-500, 0, 0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class ActivateDuringUpdateTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ActivateDuringUpdateTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "Three initially colliding boxes where only 1 is awake and has a high velocity.\n"
|
||||
"The 2nd and 3rd box should wake up at the same time and not pass through each other.";
|
||||
}
|
||||
|
||||
// Initialize the test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ActiveEdgesTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Geometry/Triangle.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ActiveEdgesTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ActiveEdgesTest, Test)
|
||||
}
|
||||
|
||||
void ActiveEdgesTest::Initialize()
|
||||
{
|
||||
const float cWidth = 5.0f;
|
||||
const float cLength = 10.0f;
|
||||
|
||||
// Settings for a frictionless box
|
||||
Ref<BoxShape> box_shape = new BoxShape(Vec3(1.0f, 1.0f, 1.0f), cDefaultConvexRadius);
|
||||
BodyCreationSettings box_settings(box_shape, RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
box_settings.mFriction = 0.0f;
|
||||
box_settings.mLinearDamping = 0.0f;
|
||||
box_settings.mAllowSleeping = false;
|
||||
|
||||
// Create various triangle strips
|
||||
TriangleList triangles;
|
||||
for (int angle = -90; angle <= 90; angle++)
|
||||
{
|
||||
// Under which normal we want to place the block
|
||||
Vec3 desired_normal = angle < 0? Vec3(0, 1, -1).Normalized() : Vec3(0, 1, 0);
|
||||
float best_dot = -FLT_MAX;
|
||||
|
||||
// Place segments
|
||||
float x = cWidth * angle;
|
||||
Vec3 v1(x, 0.0f, -0.5f * cLength);
|
||||
Vec3 v2(x + cWidth, 0.0f, -0.5f * cLength);
|
||||
for (int total_angle = 0, cur_segment = 0; abs(total_angle) <= 90 && cur_segment < 90; total_angle += angle, ++cur_segment)
|
||||
{
|
||||
// Determine positions of end of this segment
|
||||
float total_angle_rad = DegreesToRadians(float(total_angle));
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisX(), total_angle_rad);
|
||||
Vec3 delta = cLength * rotation.RotateAxisZ();
|
||||
Vec3 v3 = v1 + delta;
|
||||
Vec3 v4 = v2 + delta;
|
||||
|
||||
// Check if this segment is the best segment to place the dynamic block on
|
||||
Vec3 normal = (v3 - v1).Cross(v2 - v1).Normalized();
|
||||
float dot = normal.Dot(desired_normal);
|
||||
if (dot > best_dot)
|
||||
{
|
||||
best_dot = dot;
|
||||
box_settings.mPosition = RVec3((v1 + v2 + v3 + v4) / 4 + normal);
|
||||
box_settings.mRotation = rotation;
|
||||
}
|
||||
|
||||
// Add segment
|
||||
triangles.push_back(Triangle(v1, v3, v4));
|
||||
triangles.push_back(Triangle(v1, v4, v2));
|
||||
|
||||
// Add segment mirrored in Z axis
|
||||
if (cur_segment != 0)
|
||||
{
|
||||
Vec3 flip(1, 1, -1);
|
||||
triangles.push_back(Triangle(flip * v1, flip * v4, flip * v3));
|
||||
triangles.push_back(Triangle(flip * v1, flip * v2, flip * v4));
|
||||
}
|
||||
|
||||
// The end of the segment will be the start for the next iteration
|
||||
v1 = v3;
|
||||
v2 = v4;
|
||||
}
|
||||
|
||||
// Place box on best segment
|
||||
Body &body = *mBodyInterface->CreateBody(box_settings);
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
|
||||
// For convex segments give the block a push
|
||||
if (angle >= 0)
|
||||
body.SetLinearVelocity(Vec3(0, 0, 2.0f));
|
||||
}
|
||||
|
||||
// Mesh shape
|
||||
MeshShapeSettings mesh_shape(triangles);
|
||||
mesh_shape.SetEmbedded();
|
||||
mesh_shape.mActiveEdgeCosThresholdAngle = Cos(DegreesToRadians(50.0f));
|
||||
|
||||
// Mesh
|
||||
BodyCreationSettings mesh_settings(&mesh_shape, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
|
||||
// Instead of setting mActiveEdgeCosThresholdAngle you can also set: mesh_settings.mEnhancedInternalEdgeRemoval = true
|
||||
mesh_settings.mFriction = 0.0f;
|
||||
mBodyInterface->CreateAndAddBody(mesh_settings, EActivation::DontActivate);
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/ActiveEdgesTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/ActiveEdgesTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class ActiveEdgesTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ActiveEdgesTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Boxes sliding over the ramps should not collide with internal triangle edges of the ramp (aka ghost collisions).";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/AllowedDOFsTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Constraints/DistanceConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(AllowedDOFsTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(AllowedDOFsTest, Test)
|
||||
}
|
||||
|
||||
void AllowedDOFsTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
Vec3 box_size(0.5f, 1.0f, 2.0f);
|
||||
RefConst<Shape> box_shape = new BoxShape(box_size);
|
||||
|
||||
for (int allowed_dofs = 1; allowed_dofs <= 0b111111; ++allowed_dofs)
|
||||
{
|
||||
float x = -35.0f + 10.0f * (allowed_dofs & 0b111);
|
||||
float z = -35.0f + 10.0f * ((allowed_dofs >> 3) & 0b111);
|
||||
|
||||
// Create body
|
||||
BodyCreationSettings bcs(box_shape, RVec3(x, 10, z), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
bcs.mAllowedDOFs = (EAllowedDOFs)allowed_dofs;
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(bcs, EActivation::Activate);
|
||||
mBodies.push_back(id);
|
||||
|
||||
// Create a constraint
|
||||
DistanceConstraintSettings dcs;
|
||||
dcs.mPoint1 = bcs.mPosition + Vec3(5, 5, 5);
|
||||
dcs.mPoint2 = bcs.mPosition + box_size;
|
||||
dcs.mMinDistance = 0.0f;
|
||||
dcs.mMaxDistance = sqrt(3.0f) * 5.0f + 1.0f;
|
||||
mPhysicsSystem->AddConstraint(mBodyInterface->CreateConstraint(&dcs, BodyID(), id));
|
||||
|
||||
// Draw degrees of freedom
|
||||
String allowed_dofs_str = "";
|
||||
if ((EAllowedDOFs(allowed_dofs) & EAllowedDOFs::TranslationX) == EAllowedDOFs::TranslationX)
|
||||
allowed_dofs_str += "X ";
|
||||
if ((EAllowedDOFs(allowed_dofs) & EAllowedDOFs::TranslationY) == EAllowedDOFs::TranslationY)
|
||||
allowed_dofs_str += "Y ";
|
||||
if ((EAllowedDOFs(allowed_dofs) & EAllowedDOFs::TranslationZ) == EAllowedDOFs::TranslationZ)
|
||||
allowed_dofs_str += "Z ";
|
||||
if ((EAllowedDOFs(allowed_dofs) & EAllowedDOFs::RotationX) == EAllowedDOFs::RotationX)
|
||||
allowed_dofs_str += "RX ";
|
||||
if ((EAllowedDOFs(allowed_dofs) & EAllowedDOFs::RotationY) == EAllowedDOFs::RotationY)
|
||||
allowed_dofs_str += "RY ";
|
||||
if ((EAllowedDOFs(allowed_dofs) & EAllowedDOFs::RotationZ) == EAllowedDOFs::RotationZ)
|
||||
allowed_dofs_str += "RZ ";
|
||||
SetBodyLabel(id, allowed_dofs_str);
|
||||
}
|
||||
}
|
||||
26
lib/All/JoltPhysics/Samples/Tests/General/AllowedDOFsTest.h
Normal file
26
lib/All/JoltPhysics/Samples/Tests/General/AllowedDOFsTest.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class AllowedDOFsTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, AllowedDOFsTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Shows all permutations of allowed degrees of freedom for a body (see EAllowedDOFs).\n"
|
||||
"The boxes are constrained to the world using a distance constraint, press C to show it.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
|
||||
private:
|
||||
BodyIDVector mBodies;
|
||||
};
|
||||
30
lib/All/JoltPhysics/Samples/Tests/General/BigVsSmallTest.cpp
Normal file
30
lib/All/JoltPhysics/Samples/Tests/General/BigVsSmallTest.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/BigVsSmallTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Geometry/Triangle.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(BigVsSmallTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(BigVsSmallTest, Test)
|
||||
}
|
||||
|
||||
void BigVsSmallTest::Initialize()
|
||||
{
|
||||
// Create a big triangle
|
||||
TriangleList triangles;
|
||||
triangles.push_back(Triangle(Float3(-100, 0, 0), Float3(0, 0, 100), Float3(100, 0, -100)));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new MeshShapeSettings(triangles), RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// A small box
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(0.1f, 0.1f, 0.1f)), RVec3(0, 1.0f, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/BigVsSmallTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/BigVsSmallTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class BigVsSmallTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, BigVsSmallTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "A small box falling on a big triangle to test for numerical precision errors.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/CenterOfMassTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(CenterOfMassTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(CenterOfMassTest, Test)
|
||||
}
|
||||
|
||||
void CenterOfMassTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Compound shape with center of mass offset
|
||||
Ref<StaticCompoundShapeSettings> compound_shape1 = new StaticCompoundShapeSettings;
|
||||
compound_shape1->AddShape(Vec3(10, 0, 0), Quat::sIdentity(), new SphereShape(2));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(compound_shape1, RVec3(0, 10.0f, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
|
||||
// Create box with center of mass offset
|
||||
Array<Vec3> box;
|
||||
box.push_back(Vec3(10, 10, 10));
|
||||
box.push_back(Vec3(5, 10, 10));
|
||||
box.push_back(Vec3(10, 5, 10));
|
||||
box.push_back(Vec3(5, 5, 10));
|
||||
box.push_back(Vec3(10, 10, 5));
|
||||
box.push_back(Vec3(5, 10, 5));
|
||||
box.push_back(Vec3(10, 5, 5));
|
||||
box.push_back(Vec3(5, 5, 5));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new ConvexHullShapeSettings(box), RVec3(0, 10.0f, 20.0f), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
|
||||
// Compound
|
||||
Ref<StaticCompoundShapeSettings> compound_shape2 = new StaticCompoundShapeSettings;
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI);
|
||||
compound_shape2->AddShape(Vec3(10, 0, 0), rotation, new CapsuleShape(5, 1));
|
||||
compound_shape2->AddShape(rotation * Vec3(10, -5, 0), Quat::sIdentity(), new SphereShape(4));
|
||||
compound_shape2->AddShape(rotation * Vec3(10, 5, 0), Quat::sIdentity(), new SphereShape(2));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(compound_shape2, RVec3(0, 10.0f, 40.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/CenterOfMassTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/CenterOfMassTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class CenterOfMassTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, CenterOfMassTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Spawns various shapes with the center of mass not in the center of the object.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ChangeMotionQualityTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ChangeMotionQualityTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ChangeMotionQualityTest, Test)
|
||||
}
|
||||
|
||||
void ChangeMotionQualityTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Single shape that has 4 walls to surround fast moving sphere
|
||||
BodyCreationSettings enclosing_settings;
|
||||
Ref<BoxShapeSettings> box_shape = new BoxShapeSettings(Vec3(5.0f, 1.0f, 0.1f));
|
||||
Ref<StaticCompoundShapeSettings> enclosing_shape = new StaticCompoundShapeSettings();
|
||||
enclosing_shape->AddShape(Vec3(0, 0, 5), Quat::sIdentity(), box_shape);
|
||||
enclosing_shape->AddShape(Vec3(0, 0, -5), Quat::sIdentity(), box_shape);
|
||||
enclosing_shape->AddShape(Vec3(5, 0, 0), Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI), box_shape);
|
||||
enclosing_shape->AddShape(Vec3(-5, 0, 0), Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI), box_shape);
|
||||
enclosing_settings.SetShapeSettings(enclosing_shape);
|
||||
enclosing_settings.mMotionType = EMotionType::Kinematic;
|
||||
enclosing_settings.mObjectLayer = Layers::MOVING;
|
||||
enclosing_settings.mPosition = RVec3(0, 1, 0);
|
||||
mBodyInterface->CreateAndAddBody(enclosing_settings, EActivation::Activate);
|
||||
|
||||
// Create high speed sphere inside
|
||||
BodyCreationSettings settings;
|
||||
settings.SetShape(new SphereShape(1.0f));
|
||||
settings.mPosition = RVec3(0, 0.5f, 0);
|
||||
settings.mMotionType = EMotionType::Dynamic;
|
||||
settings.mLinearVelocity = Vec3(-240, 0, -120);
|
||||
settings.mFriction = 0.0f;
|
||||
settings.mRestitution = 1.0f;
|
||||
settings.mObjectLayer = Layers::MOVING;
|
||||
mBody = mBodyInterface->CreateBody(settings);
|
||||
mBodyInterface->AddBody(mBody->GetID(), EActivation::Activate);
|
||||
|
||||
UpdateMotionQuality();
|
||||
}
|
||||
|
||||
void ChangeMotionQualityTest::UpdateMotionQuality()
|
||||
{
|
||||
static EMotionQuality qualities[] = { EMotionQuality::LinearCast, EMotionQuality::Discrete };
|
||||
static const char *labels[] = { "LinearCast", "Discrete" };
|
||||
|
||||
// Calculate desired motion quality
|
||||
int idx = int(mTime) & 1;
|
||||
mBodyInterface->SetMotionQuality(mBody->GetID(), qualities[idx]);
|
||||
SetBodyLabel(mBody->GetID(), labels[idx]);
|
||||
}
|
||||
|
||||
void ChangeMotionQualityTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
// Increment time
|
||||
mTime += inParams.mDeltaTime;
|
||||
|
||||
UpdateMotionQuality();
|
||||
}
|
||||
|
||||
void ChangeMotionQualityTest::SaveState(StateRecorder &inStream) const
|
||||
{
|
||||
inStream.Write(mTime);
|
||||
}
|
||||
|
||||
void ChangeMotionQualityTest::RestoreState(StateRecorder &inStream)
|
||||
{
|
||||
inStream.Read(mTime);
|
||||
|
||||
UpdateMotionQuality();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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/Body/Body.h>
|
||||
|
||||
class ChangeMotionQualityTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ChangeMotionQualityTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Switches a body's motion quality from linear to discrete.\n"
|
||||
"After the switch, the high speed body passes through the wall.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
virtual void SaveState(StateRecorder &inStream) const override;
|
||||
virtual void RestoreState(StateRecorder &inStream) override;
|
||||
|
||||
private:
|
||||
void UpdateMotionQuality();
|
||||
|
||||
Body * mBody = nullptr;
|
||||
float mTime = 0.0f;
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ChangeMotionTypeTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ChangeMotionTypeTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ChangeMotionTypeTest, Test)
|
||||
}
|
||||
|
||||
void ChangeMotionTypeTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Create body as static, but allow to become dynamic
|
||||
BodyCreationSettings settings;
|
||||
settings.SetShape(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)));
|
||||
settings.mPosition = RVec3(0, 10, 0);
|
||||
settings.mMotionType = EMotionType::Static;
|
||||
settings.mObjectLayer = Layers::MOVING; // Put in moving layer, this will result in some overhead when the body is static
|
||||
settings.mAllowDynamicOrKinematic = true;
|
||||
mBody = mBodyInterface->CreateBody(settings);
|
||||
mBodyInterface->AddBody(mBody->GetID(), EActivation::Activate);
|
||||
|
||||
UpdateMotionType();
|
||||
}
|
||||
|
||||
void ChangeMotionTypeTest::UpdateMotionType()
|
||||
{
|
||||
static const EMotionType cycle[] = { EMotionType::Dynamic, EMotionType::Kinematic, EMotionType::Static, EMotionType::Kinematic, EMotionType::Dynamic, EMotionType::Static };
|
||||
static const char *label[] = { "Dynamic", "Kinematic", "Static", "Kinematic", "Dynamic", "Static" };
|
||||
|
||||
// Calculate desired motion type
|
||||
int idx = int(mTime) % size(cycle);
|
||||
EMotionType motion_type = cycle[idx];
|
||||
|
||||
// Update motion type and reactivate the body
|
||||
if (motion_type != mBody->GetMotionType())
|
||||
{
|
||||
mBodyInterface->SetMotionType(mBody->GetID(), motion_type, EActivation::Activate);
|
||||
SetBodyLabel(mBody->GetID(), label[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeMotionTypeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
// Increment time
|
||||
mTime += inParams.mDeltaTime;
|
||||
|
||||
UpdateMotionType();
|
||||
|
||||
// Provide kinematic body a target
|
||||
if (mBody->IsKinematic())
|
||||
mBody->MoveKinematic(RVec3(Sin(mTime), 10, 0), Quat::sRotation(Vec3::sAxisX(), Cos(mTime)), inParams.mDeltaTime);
|
||||
}
|
||||
|
||||
void ChangeMotionTypeTest::SaveState(StateRecorder &inStream) const
|
||||
{
|
||||
inStream.Write(mTime);
|
||||
}
|
||||
|
||||
void ChangeMotionTypeTest::RestoreState(StateRecorder &inStream)
|
||||
{
|
||||
inStream.Read(mTime);
|
||||
|
||||
UpdateMotionType();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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/Body/Body.h>
|
||||
|
||||
class ChangeMotionTypeTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ChangeMotionTypeTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Switches a body's motion type between static, kinematic and dynamic.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
virtual void SaveState(StateRecorder &inStream) const override;
|
||||
virtual void RestoreState(StateRecorder &inStream) override;
|
||||
|
||||
private:
|
||||
void UpdateMotionType();
|
||||
|
||||
Body * mBody = nullptr;
|
||||
float mTime = 0.0f;
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ChangeObjectLayerTest.h>
|
||||
#include <Layers.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ChangeObjectLayerTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ChangeObjectLayerTest, Test)
|
||||
}
|
||||
|
||||
void ChangeObjectLayerTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// A dynamic box in the MOVING layer
|
||||
mMoving = mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(5, 0.1f, 5)), RVec3(0, 1.5f, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
|
||||
// Lots of dynamic objects in the DEBRIS layer
|
||||
default_random_engine random;
|
||||
uniform_real_distribution<float> position_variation(-10, 10);
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
RVec3 position(position_variation(random), 2.0f, position_variation(random));
|
||||
Quat rotation = Quat::sRandom(random);
|
||||
mDebris.push_back(mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.1f)), position, rotation, EMotionType::Dynamic, Layers::DEBRIS), EActivation::Activate));
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeObjectLayerTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
const float cSwitchTime = 2.0f;
|
||||
|
||||
// Increment time
|
||||
mTime += inParams.mDeltaTime;
|
||||
|
||||
if (mTime >= cSwitchTime)
|
||||
{
|
||||
mIsDebris = !mIsDebris;
|
||||
|
||||
// Reposition moving object
|
||||
mBodyInterface->SetPosition(mMoving, RVec3(0, 1.5f, 0), EActivation::Activate);
|
||||
|
||||
default_random_engine random;
|
||||
uniform_real_distribution<float> position_variation(-7.5f, 7.5f);
|
||||
for (BodyID id : mDebris)
|
||||
{
|
||||
// Reposition debris
|
||||
RVec3 position(position_variation(random), 2.0f, position_variation(random));
|
||||
Quat rotation = Quat::sRandom(random);
|
||||
mBodyInterface->SetPositionAndRotation(id, position, rotation, EActivation::Activate);
|
||||
|
||||
// And update layer
|
||||
mBodyInterface->SetObjectLayer(id, mIsDebris? Layers::DEBRIS : Layers::MOVING);
|
||||
}
|
||||
|
||||
mTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeObjectLayerTest::SaveState(StateRecorder &inStream) const
|
||||
{
|
||||
inStream.Write(mTime);
|
||||
inStream.Write(mIsDebris);
|
||||
}
|
||||
|
||||
void ChangeObjectLayerTest::RestoreState(StateRecorder &inStream)
|
||||
{
|
||||
inStream.Read(mTime);
|
||||
inStream.Read(mIsDebris);
|
||||
|
||||
// Restore layer
|
||||
for (BodyID id : mDebris)
|
||||
mBodyInterface->SetObjectLayer(id, mIsDebris? Layers::DEBRIS : Layers::MOVING);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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/Body/BodyID.h>
|
||||
|
||||
class ChangeObjectLayerTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ChangeObjectLayerTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "Demonstrates how to use layers to disable collisions with other objects and how to change layers on the fly.\n"
|
||||
"The small cubes will switch between the MOVING layer and the DEBRIS layer (debris only collides with the static floor).";
|
||||
}
|
||||
|
||||
// Initialize the 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:
|
||||
BodyID mMoving;
|
||||
BodyIDVector mDebris;
|
||||
bool mIsDebris = true;
|
||||
float mTime = 0.0f;
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ChangeShapeTest.h>
|
||||
#include <Layers.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/TaperedCapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Application/DebugUI.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ChangeShapeTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ChangeShapeTest, Test)
|
||||
}
|
||||
|
||||
void ChangeShapeTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Shapes
|
||||
mShapes.push_back(new BoxShape(Vec3(0.5f, 1.5f, 0.5f)));
|
||||
mShapes.push_back(new SphereShape(0.5f));
|
||||
mShapes.push_back(new CapsuleShape(1.0f, 0.5f));
|
||||
mShapes.push_back(TaperedCapsuleShapeSettings(1.0f, 0.5f, 0.3f).Create().Get());
|
||||
|
||||
// Compound with center of mass shifted (this requires a correction of the position in the body)
|
||||
StaticCompoundShapeSettings compound_settings;
|
||||
compound_settings.AddShape(Vec3(0, 1.5f, 0), Quat::sIdentity(), new CapsuleShape(1.5f, 0.5f));
|
||||
compound_settings.AddShape(Vec3(0, 3, 0), Quat::sIdentity(), new SphereShape(1));
|
||||
RefConst<Shape> compound = compound_settings.Create().Get();
|
||||
mShapes.push_back(compound);
|
||||
|
||||
// Create dynamic body that changes shape
|
||||
BodyCreationSettings settings;
|
||||
settings.SetShape(mShapes[mShapeIdx]);
|
||||
settings.mPosition = RVec3(0, 10, 0);
|
||||
settings.mMotionType = EMotionType::Dynamic;
|
||||
settings.mObjectLayer = Layers::MOVING;
|
||||
mBodyID = mBodyInterface->CreateBody(settings)->GetID();
|
||||
mBodyInterface->AddBody(mBodyID, EActivation::Activate);
|
||||
}
|
||||
|
||||
void ChangeShapeTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
const float cSwitchTime = 3.0f;
|
||||
|
||||
// Increment time
|
||||
mTime += inParams.mDeltaTime;
|
||||
|
||||
// Get new shape
|
||||
int shape_idx = int(mTime / cSwitchTime) % mShapes.size();
|
||||
|
||||
// Change shape
|
||||
if (mShapeIdx != shape_idx)
|
||||
{
|
||||
mShapeIdx = shape_idx;
|
||||
mBodyInterface->SetShape(mBodyID, mShapes[mShapeIdx], true, mActivateAfterSwitch? EActivation::Activate : EActivation::DontActivate);
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeShapeTest::SaveState(StateRecorder &inStream) const
|
||||
{
|
||||
inStream.Write(mTime);
|
||||
inStream.Write(mShapeIdx);
|
||||
}
|
||||
|
||||
void ChangeShapeTest::RestoreState(StateRecorder &inStream)
|
||||
{
|
||||
inStream.Read(mTime);
|
||||
inStream.Read(mShapeIdx);
|
||||
|
||||
// Reset the shape to what was stored
|
||||
mBodyInterface->SetShape(mBodyID, mShapes[mShapeIdx], true, EActivation::DontActivate);
|
||||
}
|
||||
|
||||
void ChangeShapeTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
|
||||
{
|
||||
inUI->CreateCheckBox(inSubMenu, "Activate Body After Switch", mActivateAfterSwitch, [this](UICheckBox::EState inState) { mActivateAfterSwitch = inState == UICheckBox::STATE_CHECKED; });
|
||||
}
|
||||
|
||||
42
lib/All/JoltPhysics/Samples/Tests/General/ChangeShapeTest.h
Normal file
42
lib/All/JoltPhysics/Samples/Tests/General/ChangeShapeTest.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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/Body/BodyID.h>
|
||||
#include <Jolt/Physics/Collision/Shape/Shape.h>
|
||||
|
||||
class ChangeShapeTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ChangeShapeTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "Demonstrates how to dynamically update the shape of a body.";
|
||||
}
|
||||
|
||||
// Initialize the 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;
|
||||
|
||||
// Optional settings menu
|
||||
virtual bool HasSettingsMenu() const override { return true; }
|
||||
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
|
||||
|
||||
private:
|
||||
bool mActivateAfterSwitch = true;
|
||||
BodyID mBodyID;
|
||||
Array<RefConst<Shape>> mShapes;
|
||||
float mTime = 0.0f;
|
||||
int mShapeIdx = 0;
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ContactListenerTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/EstimateCollisionResponse.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ContactListenerTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ContactListenerTest, Test)
|
||||
}
|
||||
|
||||
void ContactListenerTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3(0.5f, 1.0f, 2.0f));
|
||||
|
||||
// Dynamic body 1, this body will have restitution 1 for new contacts and restitution 0 for persisting contacts
|
||||
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<StaticCompoundShapeSettings> 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);
|
||||
|
||||
// Dynamic body 5, a cube with a bigger cube surrounding it that acts as a sensor
|
||||
Ref<StaticCompoundShapeSettings> compound_shape2 = new StaticCompoundShapeSettings;
|
||||
compound_shape2->AddShape(Vec3::sZero(), Quat::sIdentity(), new BoxShape(Vec3::sReplicate(1)));
|
||||
compound_shape2->AddShape(Vec3::sZero(), Quat::sIdentity(), new BoxShape(Vec3::sReplicate(2))); // This will become a sensor in the contact callback
|
||||
BodyCreationSettings bcs5(compound_shape2, RVec3(20, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
bcs5.mUseManifoldReduction = false; // Needed in order to prevent the physics system from combining contacts between sensor and non-sensor sub shapes
|
||||
Body &body5 = *mBodyInterface->CreateBody(bcs5);
|
||||
body5.SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(body5.GetID(), EActivation::Activate);
|
||||
|
||||
// Store bodies for later use
|
||||
mBody[0] = &body1;
|
||||
mBody[1] = &body2;
|
||||
mBody[2] = &body3;
|
||||
mBody[3] = &body4;
|
||||
mBody[4] = &body5;
|
||||
}
|
||||
|
||||
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::MakeBody5PartialSensor(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
// Make the 2nd shape of body 5 a sensor
|
||||
SubShapeID body5_subshape_1 = StaticCast<CompoundShape>(mBody[4]->GetShape())->GetSubShapeIDFromIndex(1, SubShapeIDCreator()).GetID();
|
||||
if ((&inBody1 == mBody[4] && inManifold.mSubShapeID1 == body5_subshape_1)
|
||||
|| (&inBody2 == mBody[4] && inManifold.mSubShapeID2 == body5_subshape_1))
|
||||
{
|
||||
Trace("Sensor contact detected between body %08x and body %08x", inBody1.GetID().GetIndexAndSequenceNumber(), inBody2.GetID().GetIndexAndSequenceNumber());
|
||||
ioSettings.mIsSensor = true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
MakeBody5PartialSensor(inBody1, inBody2, inManifold, ioSettings);
|
||||
|
||||
// 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 });
|
||||
}
|
||||
}
|
||||
|
||||
void ContactListenerTest::OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
MakeBody5PartialSensor(inBody1, inBody2, inManifold, ioSettings);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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/ContactListener.h>
|
||||
|
||||
class ContactListenerTest : public Test, public ContactListener
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ContactListenerTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "Demonstrates how to listen for contact events.\n"
|
||||
"Leftmost box ignores contacts with the 2nd box and overrides the restitution to 1 for non-persisted contacts.\n"
|
||||
"Rightmost box contains an inner and an outer shape, the outer shape acts as a sensor.\n"
|
||||
"The TTY will output estimated post collision velocities.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PostPhysicsUpdate(float inDeltaTime) override;
|
||||
|
||||
// If this test implements a contact listener, it should be returned here
|
||||
virtual ContactListener *GetContactListener() override { return this; }
|
||||
|
||||
// See: ContactListener
|
||||
virtual ValidateResult OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) override;
|
||||
virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
|
||||
private:
|
||||
void MakeBody5PartialSensor(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings);
|
||||
|
||||
// The 5 bodies that we create
|
||||
Body * mBody[5];
|
||||
|
||||
// Tracks predicted velocities so we can compare them with the actual velocities after time step
|
||||
struct PredictedVelocity
|
||||
{
|
||||
BodyID mBodyID;
|
||||
Vec3 mLinearVelocity;
|
||||
Vec3 mAngularVelocity;
|
||||
};
|
||||
Mutex mPredictedVelocitiesMutex;
|
||||
Array<PredictedVelocity> mPredictedVelocities;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ContactManifoldTest.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ContactManifoldTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ContactManifoldTest, Test)
|
||||
}
|
||||
|
||||
void ContactManifoldTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> big_box = new BoxShape(Vec3(4, 4, 4), 0.0f);
|
||||
RefConst<Shape> capsule = new CapsuleShape(5, 2);
|
||||
RefConst<Shape> long_box = new BoxShape(Vec3(2, 7, 2));
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
// Create a box
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(big_box, RVec3(-20.0f + i * 10.0f, 4, -20.0f + j * 40.0f), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// Place a dynamic body on it
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(j == 0? capsule : long_box, RVec3(-20.0f + i * 10.0f, 12, -5.0f + i * 5.0f - 20.0f + j * 40.0f), Quat::sRotation(Vec3::sAxisY(), 0.1f * JPH_PI) * Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class ContactManifoldTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ContactManifoldTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Spawns objects at an angle to test if the contact manifold is calculated correctly.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
111
lib/All/JoltPhysics/Samples/Tests/General/ConveyorBeltTest.cpp
Normal file
111
lib/All/JoltPhysics/Samples/Tests/General/ConveyorBeltTest.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ConveyorBeltTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ConveyorBeltTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ConveyorBeltTest, Test)
|
||||
}
|
||||
|
||||
void ConveyorBeltTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Create conveyor belts
|
||||
const float cBeltWidth = 10.0f;
|
||||
const float cBeltLength = 50.0f;
|
||||
BodyCreationSettings belt_settings(new BoxShape(Vec3(cBeltWidth, 0.1f, cBeltLength)), RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
belt_settings.mFriction = 0.25f * (i + 1);
|
||||
belt_settings.mRotation = Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI * i) * Quat::sRotation(Vec3::sAxisX(), DegreesToRadians(1.0f));
|
||||
belt_settings.mPosition = RVec3(belt_settings.mRotation * Vec3(cBeltLength, 6.0f, cBeltWidth));
|
||||
mLinearBelts.push_back(mBodyInterface->CreateAndAddBody(belt_settings, EActivation::DontActivate));
|
||||
}
|
||||
|
||||
// Bodies with decreasing friction
|
||||
BodyCreationSettings cargo_settings(new BoxShape(Vec3::sReplicate(2.0f)), RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
cargo_settings.mPosition = RVec3(-cBeltLength + i * 10.0f, 10.0f, -cBeltLength);
|
||||
cargo_settings.mFriction = max(0.0f, 1.0f - 0.1f * i);
|
||||
mBodyInterface->CreateAndAddBody(cargo_settings, EActivation::Activate);
|
||||
}
|
||||
|
||||
// Create 2 cylinders
|
||||
BodyCreationSettings cylinder_settings(new CylinderShape(6.0f, 1.0f), RVec3(-25.0f, 1.0f, -20.0f), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
|
||||
mBodyInterface->CreateAndAddBody(cylinder_settings, EActivation::Activate);
|
||||
cylinder_settings.mPosition.SetZ(20.0f);
|
||||
mBodyInterface->CreateAndAddBody(cylinder_settings, EActivation::Activate);
|
||||
|
||||
// Let a dynamic belt rest on it
|
||||
BodyCreationSettings dynamic_belt(new BoxShape(Vec3(5.0f, 0.1f, 25.0f), 0.0f), RVec3(-25.0f, 3.0f, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
mLinearBelts.push_back(mBodyInterface->CreateAndAddBody(dynamic_belt, EActivation::Activate));
|
||||
|
||||
// Create cargo on the dynamic belt
|
||||
cargo_settings.mPosition = RVec3(-25.0f, 6.0f, 15.0f);
|
||||
cargo_settings.mFriction = 1.0f;
|
||||
mBodyInterface->CreateAndAddBody(cargo_settings, EActivation::Activate);
|
||||
|
||||
// Create an angular belt
|
||||
BodyCreationSettings angular_belt(new BoxShape(Vec3(20.0f, 0.1f, 20.0f), 0.0f), RVec3(10.0f, 3.0f, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
|
||||
mAngularBelt = mBodyInterface->CreateAndAddBody(angular_belt, EActivation::Activate);
|
||||
|
||||
// Bodies with decreasing friction dropping on the angular belt
|
||||
for (int i = 0; i <= 6; ++i)
|
||||
{
|
||||
cargo_settings.mPosition = RVec3(10.0f, 10.0f, -15.0f + 5.0f * i);
|
||||
cargo_settings.mFriction = max(0.0f, 1.0f - 0.1f * i);
|
||||
mBodyInterface->CreateAndAddBody(cargo_settings, EActivation::Activate);
|
||||
}
|
||||
}
|
||||
|
||||
void ConveyorBeltTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
// Linear belts
|
||||
bool body1_linear_belt = std::find(mLinearBelts.begin(), mLinearBelts.end(), inBody1.GetID()) != mLinearBelts.end();
|
||||
bool body2_linear_belt = std::find(mLinearBelts.begin(), mLinearBelts.end(), inBody2.GetID()) != mLinearBelts.end();
|
||||
if (body1_linear_belt || body2_linear_belt)
|
||||
{
|
||||
// Determine the world space surface velocity of both bodies
|
||||
const Vec3 cLocalSpaceVelocity(0, 0, -10.0f);
|
||||
Vec3 body1_linear_surface_velocity = body1_linear_belt? inBody1.GetRotation() * cLocalSpaceVelocity : Vec3::sZero();
|
||||
Vec3 body2_linear_surface_velocity = body2_linear_belt? inBody2.GetRotation() * cLocalSpaceVelocity : Vec3::sZero();
|
||||
|
||||
// Calculate the relative surface velocity
|
||||
ioSettings.mRelativeLinearSurfaceVelocity = body2_linear_surface_velocity - body1_linear_surface_velocity;
|
||||
}
|
||||
|
||||
// Angular belt
|
||||
bool body1_angular = inBody1.GetID() == mAngularBelt;
|
||||
bool body2_angular = inBody2.GetID() == mAngularBelt;
|
||||
if (body1_angular || body2_angular)
|
||||
{
|
||||
// Determine the world space angular surface velocity of both bodies
|
||||
const Vec3 cLocalSpaceAngularVelocity(0, DegreesToRadians(10.0f), 0);
|
||||
Vec3 body1_angular_surface_velocity = body1_angular? inBody1.GetRotation() * cLocalSpaceAngularVelocity : Vec3::sZero();
|
||||
Vec3 body2_angular_surface_velocity = body2_angular? inBody2.GetRotation() * cLocalSpaceAngularVelocity : Vec3::sZero();
|
||||
|
||||
// Note that the angular velocity is the angular velocity around body 1's center of mass, so we need to add the linear velocity of body 2's center of mass
|
||||
Vec3 body2_linear_surface_velocity = body2_angular? body2_angular_surface_velocity.Cross(Vec3(inBody1.GetCenterOfMassPosition() - inBody2.GetCenterOfMassPosition())) : Vec3::sZero();
|
||||
|
||||
// Calculate the relative angular surface velocity
|
||||
ioSettings.mRelativeLinearSurfaceVelocity = body2_linear_surface_velocity;
|
||||
ioSettings.mRelativeAngularSurfaceVelocity = body2_angular_surface_velocity - body1_angular_surface_velocity;
|
||||
}
|
||||
}
|
||||
|
||||
void ConveyorBeltTest::OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
// Same behavior as contact added
|
||||
OnContactAdded(inBody1, inBody2, inManifold, ioSettings);
|
||||
}
|
||||
33
lib/All/JoltPhysics/Samples/Tests/General/ConveyorBeltTest.h
Normal file
33
lib/All/JoltPhysics/Samples/Tests/General/ConveyorBeltTest.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class ConveyorBeltTest : public Test, public ContactListener
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ConveyorBeltTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "Demonstrates how to use a contact listener to implement a conveyor belt.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
|
||||
// If this test implements a contact listener, it should be returned here
|
||||
virtual ContactListener *GetContactListener() override { return this; }
|
||||
|
||||
// See: ContactListener
|
||||
virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
|
||||
private:
|
||||
BodyIDVector mLinearBelts;
|
||||
BodyID mAngularBelt;
|
||||
};
|
||||
44
lib/All/JoltPhysics/Samples/Tests/General/DampingTest.cpp
Normal file
44
lib/All/JoltPhysics/Samples/Tests/General/DampingTest.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/DampingTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(DampingTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(DampingTest, Test)
|
||||
}
|
||||
|
||||
void DampingTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> sphere = new SphereShape(2.0f);
|
||||
|
||||
// Bodies with increasing damping
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(sphere, RVec3(-50.0f + i * 10.0f, 2.0f, -80.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.GetMotionProperties()->SetAngularDamping(0.0f);
|
||||
body.GetMotionProperties()->SetLinearDamping(0.1f * i);
|
||||
body.SetLinearVelocity(Vec3(0, 0, 10));
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
SetBodyLabel(body.GetID(), StringFormat("Linear damping: %.1f", double(body.GetMotionProperties()->GetLinearDamping())));
|
||||
}
|
||||
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(sphere, RVec3(-50.0f + i * 10.0f, 2.0f, -90.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.GetMotionProperties()->SetLinearDamping(0.0f);
|
||||
body.GetMotionProperties()->SetAngularDamping(0.1f * i);
|
||||
body.SetAngularVelocity(Vec3(0, 10, 0));
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
SetBodyLabel(body.GetID(), StringFormat("Angular damping: %.1f", double(body.GetMotionProperties()->GetAngularDamping())));
|
||||
}
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/DampingTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/DampingTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class DampingTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, DampingTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Tests various values for linear and angular damping.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/DynamicMeshTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
#include <Utils/ShapeCreator.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(DynamicMeshTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(DynamicMeshTest, Test)
|
||||
}
|
||||
|
||||
void DynamicMeshTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
constexpr float cTorusRadius = 3.0f;
|
||||
constexpr float cTubeRadius = 1.0f;
|
||||
|
||||
// Create torus
|
||||
RefConst<Shape> mesh_shape = ShapeCreator::CreateTorusMesh(cTorusRadius, cTubeRadius);
|
||||
BodyCreationSettings settings(mesh_shape, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
|
||||
// Mesh cannot calculate its mass, we must provide it
|
||||
settings.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
|
||||
settings.mMassPropertiesOverride.SetMassAndInertiaOfSolidBox(2.0f * Vec3(cTorusRadius, cTubeRadius, cTorusRadius), 1000.0f);
|
||||
|
||||
mBodyInterface->CreateAndAddBody(settings, EActivation::Activate);
|
||||
|
||||
// Wall of blocks
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3::sReplicate(0.5f));
|
||||
for (int i = 0; i < 7; ++i)
|
||||
for (int j = i / 2; j < 7 - (i + 1) / 2; ++j)
|
||||
{
|
||||
RVec3 position(-3.5f + j * 1.0f + (i & 1? 0.5f : 0.0f), 0.5f + i * 1.0f, 0.0f);
|
||||
Body &wall = *mBodyInterface->CreateBody(BodyCreationSettings(box_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(wall.GetID(), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
23
lib/All/JoltPhysics/Samples/Tests/General/DynamicMeshTest.h
Normal file
23
lib/All/JoltPhysics/Samples/Tests/General/DynamicMeshTest.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class DynamicMeshTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, DynamicMeshTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Drops a dynamic body with a mesh shape on a pile of boxes.\n"
|
||||
"Note that mesh vs mesh collisions are currently not supported.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,236 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/EnhancedInternalEdgeRemovalTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Geometry/Triangle.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(EnhancedInternalEdgeRemovalTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(EnhancedInternalEdgeRemovalTest, Test)
|
||||
}
|
||||
|
||||
void EnhancedInternalEdgeRemovalTest::CreateSlidingObjects(RVec3Arg inStart)
|
||||
{
|
||||
// Slide the shapes over the grid of boxes
|
||||
RVec3 pos = inStart - RVec3(0, 0, 12.0_r);
|
||||
static const char *labels[] = { "Normal", "Enhanced edge removal" };
|
||||
for (int enhanced_removal = 0; enhanced_removal < 2; ++enhanced_removal)
|
||||
{
|
||||
// A box
|
||||
BodyCreationSettings box_bcs(new BoxShape(Vec3::sReplicate(2.0f)), pos, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
box_bcs.mLinearVelocity = Vec3(20, 0, 0);
|
||||
box_bcs.mEnhancedInternalEdgeRemoval = enhanced_removal == 1;
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(box_bcs, EActivation::Activate);
|
||||
SetBodyLabel(id, labels[enhanced_removal]);
|
||||
pos += RVec3(0, 0, 5.0_r);
|
||||
|
||||
// A sphere
|
||||
BodyCreationSettings sphere_bcs(new SphereShape(2.0f), pos, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
sphere_bcs.mLinearVelocity = Vec3(20, 0, 0);
|
||||
sphere_bcs.mEnhancedInternalEdgeRemoval = enhanced_removal == 1;
|
||||
id = mBodyInterface->CreateAndAddBody(sphere_bcs, EActivation::Activate);
|
||||
SetBodyLabel(id, labels[enhanced_removal]);
|
||||
pos += RVec3(0, 0, 5.0_r);
|
||||
|
||||
// Compound
|
||||
RefConst<Shape> box = new BoxShape(Vec3::sReplicate(0.1f));
|
||||
StaticCompoundShapeSettings compound;
|
||||
compound.SetEmbedded();
|
||||
for (int x = 0; x < 2; ++x)
|
||||
for (int y = 0; y < 2; ++y)
|
||||
for (int z = 0; z < 2; ++z)
|
||||
compound.AddShape(Vec3(x == 0? -1.9f : 1.9f, y == 0? -1.9f : 1.9f, z == 0? -1.9f : 1.9f), Quat::sIdentity(), box);
|
||||
BodyCreationSettings compound_bcs(&compound, pos, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
compound_bcs.mLinearVelocity = Vec3(20, 0, 0);
|
||||
compound_bcs.mEnhancedInternalEdgeRemoval = enhanced_removal == 1;
|
||||
id = mBodyInterface->CreateAndAddBody(compound_bcs, EActivation::Activate);
|
||||
SetBodyLabel(id, labels[enhanced_removal]);
|
||||
pos += RVec3(0, 0, 7.0_r);
|
||||
}
|
||||
}
|
||||
|
||||
void EnhancedInternalEdgeRemovalTest::Initialize()
|
||||
{
|
||||
// This test creates a grid of connected boxes and tests that objects don't hit the internal edges
|
||||
{
|
||||
StaticCompoundShapeSettings compound_settings;
|
||||
compound_settings.SetEmbedded();
|
||||
constexpr float size = 2.0f;
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3::sReplicate(0.5f * size));
|
||||
for (int x = -10; x < 10; ++x)
|
||||
for (int z = -10; z < 10; ++z)
|
||||
compound_settings.AddShape(Vec3(size * x, 0, size * z), Quat::sIdentity(), box_shape);
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(BodyCreationSettings(&compound_settings, RVec3(0, -1, -40), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
SetBodyLabel(id, "Dense grid of boxes");
|
||||
|
||||
CreateSlidingObjects(RVec3(-18, 1.9_r, -40.0_r));
|
||||
}
|
||||
|
||||
// This tests if objects do not collide with internal edges
|
||||
{
|
||||
// Create a dense grid of triangles so that we have a large chance of hitting an internal edge
|
||||
constexpr float size = 2.0f;
|
||||
TriangleList triangles;
|
||||
for (int x = -10; x < 10; ++x)
|
||||
for (int z = -10; z < 10; ++z)
|
||||
{
|
||||
float x1 = size * x;
|
||||
float z1 = size * z;
|
||||
float x2 = x1 + size;
|
||||
float z2 = z1 + size;
|
||||
|
||||
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.push_back(Triangle(v1, v3, v4));
|
||||
triangles.push_back(Triangle(v1, v4, v2));
|
||||
}
|
||||
|
||||
MeshShapeSettings mesh_settings(triangles);
|
||||
mesh_settings.mActiveEdgeCosThresholdAngle = -1.0f; // Turn off regular active edge determination so that we only rely on the mEnhancedInternalEdgeRemoval flag
|
||||
mesh_settings.SetEmbedded();
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(BodyCreationSettings(&mesh_settings, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
SetBodyLabel(id, "Dense triangle mesh");
|
||||
|
||||
CreateSlidingObjects(RVec3(-18, 1.9_r, 0));
|
||||
}
|
||||
|
||||
// This test tests that we only ignore edges that are shared with voided triangles
|
||||
{
|
||||
// Create an L shape mesh lying on its back
|
||||
TriangleList triangles;
|
||||
constexpr float height = 0.5f;
|
||||
constexpr float half_width = 5.0f;
|
||||
constexpr float half_length = 2.0f;
|
||||
triangles.push_back(Triangle(Float3(-half_length, 0, half_width), Float3(half_length, 0, -half_width), Float3(-half_length, 0, -half_width)));
|
||||
triangles.push_back(Triangle(Float3(-half_length, 0, half_width), Float3(half_length, 0, half_width), Float3(half_length, 0, -half_width)));
|
||||
triangles.push_back(Triangle(Float3(half_length, height, half_width), Float3(half_length, height, -half_width), Float3(half_length, 0, half_width)));
|
||||
triangles.push_back(Triangle(Float3(half_length, 0, half_width), Float3(half_length, height, -half_width), Float3(half_length, 0, -half_width)));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new MeshShapeSettings(triangles), RVec3(0, 0, 30), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// Roll a sphere towards the edge pointing upwards
|
||||
float z = 28.0f;
|
||||
for (int enhanced_removal = 0; enhanced_removal < 2; ++enhanced_removal)
|
||||
{
|
||||
// A sphere
|
||||
BodyCreationSettings sphere_bcs(new SphereShape(1.0f), RVec3(0, 1, z), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
sphere_bcs.mLinearVelocity = Vec3(20, 0, 0);
|
||||
sphere_bcs.mEnhancedInternalEdgeRemoval = enhanced_removal == 1;
|
||||
mBodyInterface->CreateAndAddBody(sphere_bcs, EActivation::Activate);
|
||||
z += 4.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// This tests that fast moving spheres rolling over a triangle will not be affected by internal edges
|
||||
{
|
||||
// Create a flat plane
|
||||
MeshShapeSettings plane_mesh({
|
||||
{
|
||||
Float3(-10, 0, -10),
|
||||
Float3(-10, 0, 10),
|
||||
Float3(10, 0, 10)
|
||||
},
|
||||
{
|
||||
Float3(-10, 0, -10),
|
||||
Float3(10, 0, 10),
|
||||
Float3(10, 0, -10)
|
||||
},
|
||||
});
|
||||
plane_mesh.SetEmbedded();
|
||||
BodyCreationSettings level_plane(&plane_mesh, RVec3(-10, 0, 50), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
|
||||
level_plane.mFriction = 1;
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(level_plane, EActivation::DontActivate);
|
||||
SetBodyLabel(id, "Dense triangle mesh");
|
||||
|
||||
// Roll a ball over it
|
||||
BodyCreationSettings level_ball(new SphereShape(0.5f), RVec3(-10, 1, 41), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
level_ball.mEnhancedInternalEdgeRemoval = true;
|
||||
level_ball.mFriction = 1;
|
||||
level_ball.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
|
||||
level_ball.mMassPropertiesOverride.mMass = 1;
|
||||
mLevelBall = mBodyInterface->CreateAndAddBody(level_ball, EActivation::Activate);
|
||||
|
||||
// Create a sloped plane
|
||||
BodyCreationSettings slope_plane(&plane_mesh, RVec3(10, 0, 50), Quat::sRotation(Vec3::sAxisX(), DegreesToRadians(45)), EMotionType::Static, Layers::NON_MOVING);
|
||||
slope_plane.mFriction = 1;
|
||||
id = mBodyInterface->CreateAndAddBody(slope_plane, EActivation::DontActivate);
|
||||
SetBodyLabel(id, "Dense triangle mesh");
|
||||
|
||||
// Roll a ball over it
|
||||
BodyCreationSettings slope_ball(new SphereShape(0.5f), RVec3(10, 8, 44), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
slope_ball.mEnhancedInternalEdgeRemoval = true;
|
||||
slope_ball.mFriction = 1;
|
||||
slope_ball.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
|
||||
slope_ball.mMassPropertiesOverride.mMass = 1;
|
||||
mBodyInterface->CreateAndAddBody(slope_ball, EActivation::Activate);
|
||||
}
|
||||
|
||||
// This tests a previous bug where a compound shape will fall through a box because features are voided by accident.
|
||||
// This is because both boxes of the compound shape collide with the top face of the static box. The big box will have a normal
|
||||
// that is aligned with the face so will be processed immediately. This will void the top face of the static box. The small box,
|
||||
// which collides with an edge of the top face will not be processed. This will cause the small box to penetrate the face.
|
||||
{
|
||||
// A box
|
||||
BodyCreationSettings box_bcs(new BoxShape(Vec3::sReplicate(2.5f)), RVec3(0, 0, 70), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
|
||||
mBodyInterface->CreateAndAddBody(box_bcs, EActivation::DontActivate);
|
||||
|
||||
// Compound
|
||||
StaticCompoundShapeSettings compound;
|
||||
compound.SetEmbedded();
|
||||
compound.AddShape(Vec3(-2.5f, 0, 0), Quat::sIdentity(), new BoxShape(Vec3(2.5f, 0.1f, 0.1f)));
|
||||
compound.AddShape(Vec3(0.1f, 0, 0), Quat::sIdentity(), new BoxShape(Vec3(0.1f, 1, 1)));
|
||||
BodyCreationSettings compound_bcs(&compound, RVec3(2, 5, 70), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
compound_bcs.mEnhancedInternalEdgeRemoval = true;
|
||||
mBodyInterface->CreateAndAddBody(compound_bcs, EActivation::Activate);
|
||||
}
|
||||
|
||||
// Create a super dense grid of triangles
|
||||
{
|
||||
constexpr float size = 0.25f;
|
||||
TriangleList triangles;
|
||||
for (int x = -100; x < 100; ++x)
|
||||
for (int z = -5; z < 5; ++z)
|
||||
{
|
||||
float x1 = size * x;
|
||||
float z1 = size * z;
|
||||
float x2 = x1 + size;
|
||||
float z2 = z1 + size;
|
||||
|
||||
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.push_back(Triangle(v1, v3, v4));
|
||||
triangles.push_back(Triangle(v1, v4, v2));
|
||||
}
|
||||
|
||||
MeshShapeSettings mesh_settings(triangles);
|
||||
mesh_settings.mActiveEdgeCosThresholdAngle = -1.0f; // Turn off regular active edge determination so that we only rely on the mEnhancedInternalEdgeRemoval flag
|
||||
mesh_settings.SetEmbedded();
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(BodyCreationSettings(&mesh_settings, RVec3(0, 0, 80), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
SetBodyLabel(id, "Dense triangle mesh");
|
||||
|
||||
BodyCreationSettings box_bcs(new BoxShape(Vec3::sReplicate(1.0f)), RVec3(-24, 0.9_r, 80), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
box_bcs.mLinearVelocity = Vec3(20, 0, 0);
|
||||
box_bcs.mEnhancedInternalEdgeRemoval = true;
|
||||
mBodyInterface->CreateAndAddBody(box_bcs, EActivation::Activate);
|
||||
}
|
||||
}
|
||||
|
||||
void EnhancedInternalEdgeRemovalTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
// Increase rotation speed of the ball on the flat plane
|
||||
mBodyInterface->AddTorque(mLevelBall, Vec3(JPH_PI * 4, 0, 0));
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class EnhancedInternalEdgeRemovalTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, EnhancedInternalEdgeRemovalTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Shows bodies using enhanced edge removal vs bodies that don't.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
private:
|
||||
void CreateSlidingObjects(RVec3Arg inStart);
|
||||
|
||||
BodyID mLevelBall;
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/FrictionPerTriangleTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
|
||||
#include <Jolt/Geometry/Triangle.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(FrictionPerTriangleTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(FrictionPerTriangleTest, Test)
|
||||
}
|
||||
|
||||
void FrictionPerTriangleTest::Initialize()
|
||||
{
|
||||
const int num_sections = 5;
|
||||
const float section_size = 50.0f;
|
||||
|
||||
// Create a strip of triangles
|
||||
TriangleList triangles;
|
||||
for (int z = 0; z <= num_sections; ++z)
|
||||
{
|
||||
float z1 = section_size * (z - 0.5f * num_sections);
|
||||
float z2 = z1 + section_size;
|
||||
|
||||
Float3 v1 = Float3(-100.0f, 0, z1);
|
||||
Float3 v2 = Float3(100.0f, 0, z1);
|
||||
Float3 v3 = Float3(-100.0f, 0, z2);
|
||||
Float3 v4 = Float3(100.0f, 0, z2);
|
||||
|
||||
triangles.push_back(Triangle(v1, v3, v4, z));
|
||||
triangles.push_back(Triangle(v1, v4, v2, z));
|
||||
}
|
||||
|
||||
// Create materials with increasing friction
|
||||
PhysicsMaterialList materials;
|
||||
for (uint i = 0; i <= num_sections; ++i)
|
||||
{
|
||||
float friction = float(i) / float(num_sections);
|
||||
materials.push_back(new MyMaterial("Friction " + ConvertToString(friction), Color::sGetDistinctColor(i), friction, 0.0f));
|
||||
}
|
||||
|
||||
// A ramp
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new MeshShapeSettings(triangles, std::move(materials)), RVec3::sZero(), Quat::sRotation(Vec3::sAxisX(), 0.2f * JPH_PI), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// A box with friction 1 that slides down the ramp
|
||||
Ref<BoxShape> box_shape = new BoxShape(Vec3(2.0f, 2.0f, 2.0f), cDefaultConvexRadius, new MyMaterial("Box Friction 1", Color::sYellow, 1.0f, 0.0f));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(0, 60.0f, -75.0f), Quat::sRotation(Vec3::sAxisX(), 0.2f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
|
||||
void FrictionPerTriangleTest::sGetFrictionAndRestitution(const Body &inBody, const SubShapeID &inSubShapeID, float &outFriction, float &outRestitution)
|
||||
{
|
||||
// Get the material that corresponds to the sub shape ID
|
||||
const PhysicsMaterial *material = inBody.GetShape()->GetMaterial(inSubShapeID);
|
||||
if (material == PhysicsMaterial::sDefault)
|
||||
{
|
||||
// This is the default material, use the settings from the body (note all bodies in our test have a material so this should not happen)
|
||||
outFriction = inBody.GetFriction();
|
||||
outRestitution = inBody.GetRestitution();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's not the default material we know its a material that we created so we can cast it and get the values
|
||||
const MyMaterial *my_material = static_cast<const MyMaterial *>(material);
|
||||
outFriction = my_material->mFriction;
|
||||
outRestitution = my_material->mRestitution;
|
||||
}
|
||||
}
|
||||
|
||||
void FrictionPerTriangleTest::sOverrideContactSettings(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
// Get the custom friction and restitution for both bodies
|
||||
float friction1, friction2, restitution1, restitution2;
|
||||
sGetFrictionAndRestitution(inBody1, inManifold.mSubShapeID1, friction1, restitution1);
|
||||
sGetFrictionAndRestitution(inBody2, inManifold.mSubShapeID2, friction2, restitution2);
|
||||
|
||||
// Use the default formulas for combining friction and restitution
|
||||
ioSettings.mCombinedFriction = sqrt(friction1 * friction2);
|
||||
ioSettings.mCombinedRestitution = max(restitution1, restitution2);
|
||||
}
|
||||
|
||||
void FrictionPerTriangleTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
sOverrideContactSettings(inBody1, inBody2, inManifold, ioSettings);
|
||||
}
|
||||
|
||||
void FrictionPerTriangleTest::OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
sOverrideContactSettings(inBody1, inBody2, inManifold, ioSettings);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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/ContactListener.h>
|
||||
#include <Jolt/Physics/Collision/PhysicsMaterialSimple.h>
|
||||
|
||||
class FrictionPerTriangleTest : public Test, public ContactListener
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, FrictionPerTriangleTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Demonstrates how you can use a contact listener and your own material definition to get friction and restitution per triangle or per sub shape of a compound shape.\n"
|
||||
"Friction increases while going down the slope. Box should eventually stop.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
|
||||
/// A custom material implementation that stores its own friction and restitution
|
||||
/// Note: Make sure you set PhysicsMaterial::sDefault to something your application understands (explicitly check PhysicsMaterial::sDefault to prevent casting to the wrong type in sGetFrictionAndRestitution)
|
||||
class MyMaterial : public PhysicsMaterialSimple
|
||||
{
|
||||
public:
|
||||
// Note: Not implementing serialization because we don't serialize this material in this example!
|
||||
|
||||
/// Constructor
|
||||
MyMaterial(const string_view &inName, ColorArg inColor, float inFriction, float inRestitution) : PhysicsMaterialSimple(inName, inColor), mFriction(inFriction), mRestitution(inRestitution) { }
|
||||
|
||||
float mFriction;
|
||||
float mRestitution;
|
||||
};
|
||||
|
||||
/// Extract custom friction and restitution from a body and sub shape ID
|
||||
static void sGetFrictionAndRestitution(const Body &inBody, const SubShapeID &inSubShapeID, float &outFriction, float &outRestitution);
|
||||
|
||||
/// Calculates and overrides friction and restitution settings for a contact between two bodies
|
||||
static void sOverrideContactSettings(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings);
|
||||
|
||||
// If this test implements a contact listener, it should be returned here
|
||||
virtual ContactListener *GetContactListener() override { return this; }
|
||||
|
||||
// See: ContactListener
|
||||
virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
};
|
||||
44
lib/All/JoltPhysics/Samples/Tests/General/FrictionTest.cpp
Normal file
44
lib/All/JoltPhysics/Samples/Tests/General/FrictionTest.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/FrictionTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(FrictionTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(FrictionTest, Test)
|
||||
}
|
||||
|
||||
void FrictionTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
Body &floor = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(100.0f, 1.0f, 100.0f), 0.0f), RVec3::sZero(), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Static, Layers::NON_MOVING));
|
||||
floor.SetFriction(1.0f);
|
||||
mBodyInterface->AddBody(floor.GetID(), EActivation::DontActivate);
|
||||
|
||||
RefConst<Shape> box = new BoxShape(Vec3(2.0f, 2.0f, 2.0f));
|
||||
RefConst<Shape> sphere = new SphereShape(2.0f);
|
||||
|
||||
// Bodies with increasing friction
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(box, RVec3(-50.0f + i * 10.0f, 55.0f, -50.0f), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.SetFriction(0.1f * i);
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
SetBodyLabel(body.GetID(), StringFormat("Friction: %.1f", double(body.GetFriction())));
|
||||
}
|
||||
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(sphere, RVec3(-50.0f + i * 10.0f, 47.0f, -40.0f), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.SetFriction(0.1f * i);
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
SetBodyLabel(body.GetID(), StringFormat("Friction: %.1f", double(body.GetFriction())));
|
||||
}
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/FrictionTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/FrictionTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class FrictionTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, FrictionTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Bodies with varying friction.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
157
lib/All/JoltPhysics/Samples/Tests/General/FunnelTest.cpp
Normal file
157
lib/All/JoltPhysics/Samples/Tests/General/FunnelTest.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/FunnelTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.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/Collision/Shape/TaperedCylinderShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/ScaledShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <random>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(FunnelTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(FunnelTest, Test)
|
||||
}
|
||||
|
||||
void FunnelTest::Initialize()
|
||||
{
|
||||
RefConst<Shape> box = new BoxShape(Vec3(50, 1, 50), 0.0f);
|
||||
|
||||
// Funnel
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI * i);
|
||||
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box, RVec3(rotation * Vec3(25, 25, 0)), rotation * Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
}
|
||||
|
||||
default_random_engine random;
|
||||
uniform_real_distribution<float> feature_size(0.1f, 2.0f);
|
||||
uniform_real_distribution<float> position_variation(-40, 40);
|
||||
uniform_real_distribution<float> scale_variation(-1.5f, 1.5f);
|
||||
|
||||
// Random scale
|
||||
Vec3 scale(scale_variation(random), scale_variation(random), scale_variation(random));
|
||||
|
||||
// Make it minimally -0.5 or 0.5 depending on the sign
|
||||
scale += Vec3::sSelect(Vec3::sReplicate(-0.5f), Vec3::sReplicate(0.5f), Vec3::sGreaterOrEqual(scale, Vec3::sZero()));
|
||||
|
||||
RefConst<Shape> shape;
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
switch (random() % 10)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
shape = new SphereShape(feature_size(random));
|
||||
scale = scale.Swizzle<SWIZZLE_X, SWIZZLE_X, SWIZZLE_X>(); // Only uniform scale supported
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
shape = new BoxShape(Vec3(feature_size(random), feature_size(random), feature_size(random)));
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
// Create random points
|
||||
Array<Vec3> points;
|
||||
for (int j = 0; j < 20; ++j)
|
||||
points.push_back(feature_size(random) * Vec3::sRandom(random));
|
||||
shape = ConvexHullShapeSettings(points).Create().Get();
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
shape = new CapsuleShape(0.5f * feature_size(random), feature_size(random));
|
||||
scale = scale.Swizzle<SWIZZLE_X, SWIZZLE_X, SWIZZLE_X>(); // Only uniform scale supported
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
float top = feature_size(random);
|
||||
float bottom = feature_size(random);
|
||||
float half_height = max(0.5f * feature_size(random), 0.5f * abs(top - bottom) + 0.001f);
|
||||
shape = TaperedCapsuleShapeSettings(half_height, top, bottom).Create().Get();
|
||||
scale = scale.Swizzle<SWIZZLE_X, SWIZZLE_X, SWIZZLE_X>(); // Only uniform scale supported
|
||||
break;
|
||||
}
|
||||
|
||||
case 5:
|
||||
{
|
||||
shape = new CylinderShape(0.5f * feature_size(random), feature_size(random));
|
||||
scale = scale.Swizzle<SWIZZLE_X, SWIZZLE_Y, SWIZZLE_X>(); // Scale X must be same as Z
|
||||
break;
|
||||
}
|
||||
|
||||
case 6:
|
||||
{
|
||||
shape = TaperedCylinderShapeSettings(0.5f * feature_size(random), feature_size(random), feature_size(random)).Create().Get();
|
||||
scale = scale.Swizzle<SWIZZLE_X, SWIZZLE_Y, SWIZZLE_X>(); // Scale X must be same as Z
|
||||
break;
|
||||
}
|
||||
|
||||
case 7:
|
||||
{
|
||||
shape = TaperedCylinderShapeSettings(0.5f * feature_size(random), 0.0f, feature_size(random), 0.0f).Create().Get();
|
||||
scale = scale.Swizzle<SWIZZLE_X, SWIZZLE_Y, SWIZZLE_X>(); // Scale X must be same as Z
|
||||
break;
|
||||
}
|
||||
|
||||
case 8:
|
||||
{
|
||||
// Simple compound
|
||||
StaticCompoundShapeSettings compound_shape_settings;
|
||||
compound_shape_settings.AddShape(Vec3::sZero(), Quat::sIdentity(), new CapsuleShape(1, 0.1f));
|
||||
compound_shape_settings.AddShape(Vec3(0, -1, 0), Quat::sIdentity(), new SphereShape(0.5f));
|
||||
compound_shape_settings.AddShape(Vec3(0, 1, 0), Quat::sIdentity(), new SphereShape(0.5f));
|
||||
shape = compound_shape_settings.Create().Get();
|
||||
scale = scale.Swizzle<SWIZZLE_X, SWIZZLE_X, SWIZZLE_X>(); // Only uniform scale supported
|
||||
break;
|
||||
}
|
||||
|
||||
case 9:
|
||||
{
|
||||
// Compound with sub compound and rotation
|
||||
Ref<StaticCompoundShapeSettings> sub_compound = new StaticCompoundShapeSettings();
|
||||
sub_compound->AddShape(Vec3(0, 0.75f, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new BoxShape(Vec3(0.75f, 0.25f, 0.2f)));
|
||||
sub_compound->AddShape(Vec3(0.75f, 0, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new CylinderShape(0.75f, 0.2f));
|
||||
sub_compound->AddShape(Vec3(0, 0, 0.75f), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), new TaperedCapsuleShapeSettings(0.75f, 0.25f, 0.2f));
|
||||
StaticCompoundShapeSettings compound_shape_settings;
|
||||
compound_shape_settings.AddShape(Vec3(0, 0, 0), Quat::sRotation(Vec3::sAxisX(), -0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), sub_compound);
|
||||
compound_shape_settings.AddShape(Vec3(0, -0.1f, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), -0.75f * JPH_PI), sub_compound);
|
||||
shape = compound_shape_settings.Create().Get();
|
||||
scale = scale.Swizzle<SWIZZLE_X, SWIZZLE_X, SWIZZLE_X>(); // Only uniform scale supported
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale the shape
|
||||
if ((random() % 3) == 0)
|
||||
shape = new ScaledShape(shape, scale);
|
||||
|
||||
RVec3 position(position_variation(random), 100.0f + position_variation(random), position_variation(random));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(shape, position, Quat::sRandom(random), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
|
||||
void FunnelTest::GetInitialCamera(CameraState &ioState) const
|
||||
{
|
||||
RVec3 cam_tgt = RVec3(0, 50, 0);
|
||||
ioState.mPos = RVec3(50, 100, 50);
|
||||
ioState.mForward = Vec3(cam_tgt - ioState.mPos).Normalized();
|
||||
}
|
||||
23
lib/All/JoltPhysics/Samples/Tests/General/FunnelTest.h
Normal file
23
lib/All/JoltPhysics/Samples/Tests/General/FunnelTest.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class FunnelTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, FunnelTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Spawns a lot of objects and drops them into a funnel to check for performance / stability issues.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void GetInitialCamera(CameraState &ioState) const override;
|
||||
};
|
||||
@@ -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/General/GravityFactorTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(GravityFactorTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(GravityFactorTest, Test)
|
||||
}
|
||||
|
||||
void GravityFactorTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> box = new BoxShape(Vec3(2.0f, 2.0f, 2.0f));
|
||||
|
||||
// Bodies with increasing gravity fraction
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(box, RVec3(-50.0f + i * 10.0f, 25.0f, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.GetMotionProperties()->SetGravityFactor(0.1f * i);
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
SetBodyLabel(body.GetID(), StringFormat("Gravity: %.1f", double(body.GetMotionProperties()->GetGravityFactor())));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class GravityFactorTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, GravityFactorTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Bodies with varying gravity factor.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/GyroscopicForceTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(GyroscopicForceTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(GyroscopicForceTest, Test)
|
||||
}
|
||||
|
||||
void GyroscopicForceTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
StaticCompoundShapeSettings compound;
|
||||
compound.AddShape(Vec3::sZero(), Quat::sIdentity(), new BoxShape(Vec3(0.5f, 5.0f, 0.5f)));
|
||||
compound.AddShape(Vec3(1.5f, 0, 0), Quat::sIdentity(), new BoxShape(Vec3(1.0f, 0.5f, 0.5f)));
|
||||
compound.SetEmbedded();
|
||||
|
||||
// One body without gyroscopic force
|
||||
BodyCreationSettings bcs(&compound, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
bcs.mLinearDamping = 0.0f;
|
||||
bcs.mAngularDamping = 0.0f;
|
||||
bcs.mAngularVelocity = Vec3(10, 1, 0);
|
||||
bcs.mGravityFactor = 0.0f;
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(bcs, EActivation::Activate);
|
||||
SetBodyLabel(id, "Gyroscopic force off");
|
||||
|
||||
// One body with gyroscopic force
|
||||
bcs.mPosition += RVec3(10, 0, 0);
|
||||
bcs.mApplyGyroscopicForce = true;
|
||||
id = mBodyInterface->CreateAndAddBody(bcs, EActivation::Activate);
|
||||
SetBodyLabel(id, "Gyroscopic force on");
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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/Body/BodyActivationListener.h>
|
||||
|
||||
class GyroscopicForceTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, GyroscopicForceTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
// See: https://en.wikipedia.org/wiki/Tennis_racket_theorem
|
||||
return "Shows how to enable gyroscopic forces to create the Dzhanibekov effect.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/HeavyOnLightTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(HeavyOnLightTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(HeavyOnLightTest, Test)
|
||||
}
|
||||
|
||||
void HeavyOnLightTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
Ref<BoxShape> box = new BoxShape(Vec3::sReplicate(5));
|
||||
box->SetDensity(1000.0f);
|
||||
|
||||
for (int i = 1; i <= 10; ++i)
|
||||
{
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(BodyCreationSettings(box, RVec3(-75.0f + i * 15.0f, 10.0f, 0.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
SetBodyLabel(id, StringFormat("Mass: %g", double(box->GetMassProperties().mMass)));
|
||||
|
||||
Ref<BoxShape> box2 = new BoxShape(Vec3::sReplicate(5));
|
||||
box2->SetDensity(5000.0f * i);
|
||||
|
||||
id = mBodyInterface->CreateAndAddBody(BodyCreationSettings(box2, RVec3(-75.0f + i * 15.0f, 30.0f, 0.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
SetBodyLabel(id, StringFormat("Mass: %g", double(box2->GetMassProperties().mMass)));
|
||||
}
|
||||
}
|
||||
23
lib/All/JoltPhysics/Samples/Tests/General/HeavyOnLightTest.h
Normal file
23
lib/All/JoltPhysics/Samples/Tests/General/HeavyOnLightTest.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class HeavyOnLightTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, HeavyOnLightTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "This test spawns a number of heavy boxes (with increasing weight) on light boxes.\n"
|
||||
"Shows that iterative solvers have issues with large mass differences.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
378
lib/All/JoltPhysics/Samples/Tests/General/HighSpeedTest.cpp
Normal file
378
lib/All/JoltPhysics/Samples/Tests/General/HighSpeedTest.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/HighSpeedTest.h>
|
||||
#include <Jolt/Physics/Collision/RayCast.h>
|
||||
#include <Jolt/Physics/Collision/CastResult.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/ScaledShape.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/Shape/ConvexHullShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/PhysicsScene.h>
|
||||
#include <Jolt/ObjectStream/ObjectStreamIn.h>
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(HighSpeedTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(HighSpeedTest, Test)
|
||||
}
|
||||
|
||||
const char *HighSpeedTest::sScenes[] =
|
||||
{
|
||||
"Simple",
|
||||
"Convex Hull On Large Triangles",
|
||||
"Convex Hull On Terrain1",
|
||||
};
|
||||
|
||||
int HighSpeedTest::sSelectedScene = 0;
|
||||
|
||||
void HighSpeedTest::CreateDominoBlocks(RVec3Arg inOffset, int inNumWalls, float inDensity, float inRadius)
|
||||
{
|
||||
BodyCreationSettings box_settings;
|
||||
Ref<BoxShape> box_shape = new BoxShape(Vec3(0.9f, 1.0f, 0.1f));
|
||||
box_shape->SetDensity(inDensity); // Make box more heavy so the bouncing ball keeps a higher velocity
|
||||
box_settings.SetShape(box_shape);
|
||||
box_settings.mObjectLayer = Layers::MOVING;
|
||||
|
||||
// U shaped set of thin boxes
|
||||
for (int i = 0; i < inNumWalls; ++i)
|
||||
{
|
||||
box_settings.mPosition = inOffset + Vec3(2.0f * i, 1, -1.1f - inRadius);
|
||||
mBodyInterface->CreateAndAddBody(box_settings, EActivation::DontActivate);
|
||||
|
||||
box_settings.mPosition = inOffset + Vec3(2.0f * i, 1, +1.1f + inRadius);
|
||||
mBodyInterface->CreateAndAddBody(box_settings, EActivation::DontActivate);
|
||||
}
|
||||
|
||||
box_settings.mPosition = inOffset + Vec3(-1.1f - inRadius, 1, 0);
|
||||
box_settings.mRotation = Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI);
|
||||
mBodyInterface->CreateAndAddBody(box_settings, EActivation::DontActivate);
|
||||
}
|
||||
|
||||
void HighSpeedTest::CreateDynamicObject(RVec3Arg inPosition, Vec3Arg inVelocity, Shape *inShape, EMotionQuality inMotionQuality)
|
||||
{
|
||||
BodyCreationSettings creation_settings;
|
||||
creation_settings.SetShape(inShape);
|
||||
creation_settings.mFriction = 0.0f;
|
||||
creation_settings.mRestitution = 1.0f;
|
||||
creation_settings.mLinearDamping = 0.0f;
|
||||
creation_settings.mAngularDamping = 0.0f;
|
||||
creation_settings.mMotionQuality = inMotionQuality;
|
||||
creation_settings.mObjectLayer = Layers::MOVING;
|
||||
creation_settings.mPosition = inPosition;
|
||||
|
||||
Body &body = *mBodyInterface->CreateBody(creation_settings);
|
||||
body.SetLinearVelocity(inVelocity);
|
||||
mBodyInterface->AddBody(body.GetID(), inVelocity.IsNearZero()? EActivation::DontActivate : EActivation::Activate);
|
||||
}
|
||||
|
||||
void HighSpeedTest::CreateSimpleScene()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
const float radius = 0.1f;
|
||||
const int num_walls = 5;
|
||||
const float density = 2000.0f;
|
||||
const float speed = 240.0f;
|
||||
|
||||
RVec3 offset(0, 0, -30);
|
||||
|
||||
{
|
||||
// U shaped set of thin walls
|
||||
TriangleList triangles;
|
||||
for (int i = 0; i < num_walls; ++i)
|
||||
{
|
||||
triangles.push_back(Triangle(Float3(2.0f*i-1,0,-1-radius), Float3(2.0f*i+1,0,-1-radius), Float3(2.0f*i,2,-1-radius)));
|
||||
triangles.push_back(Triangle(Float3(2.0f*i-1,0,1+radius), Float3(2.0f*i,2,1+radius), Float3(2.0f*i+1,0,1+radius)));
|
||||
}
|
||||
triangles.push_back(Triangle(Float3(-1-radius,0,-1), Float3(-1-radius,2,0), Float3(-1-radius,0,1)));
|
||||
Body &walls = *mBodyInterface->CreateBody(BodyCreationSettings(new MeshShapeSettings(triangles), offset, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
walls.SetRestitution(1.0f);
|
||||
walls.SetFriction(0.0f);
|
||||
mBodyInterface->AddBody(walls.GetID(), EActivation::DontActivate);
|
||||
|
||||
// Fast moving sphere against mesh
|
||||
CreateDynamicObject(offset + Vec3(2.0f * num_walls - 1, 1, 0), Vec3(-speed, 0, -speed), new SphereShape(radius));
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Create wall of domino blocks
|
||||
CreateDominoBlocks(offset, num_walls, density, radius);
|
||||
|
||||
// Fast moving sphere against domino blocks
|
||||
CreateDynamicObject(offset + Vec3(2.0f * num_walls - 1, 1, 0), Vec3(-speed, 0, -speed), new SphereShape(radius));
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Create wall of domino blocks
|
||||
CreateDominoBlocks(offset, num_walls, density, radius);
|
||||
|
||||
// Fast moving scaled box against domino blocks
|
||||
CreateDynamicObject(offset + Vec3(2.0f * num_walls - 1, 1, 0), Vec3(-speed, 0, -speed), new ScaledShape(new BoxShape(Vec3::sReplicate(0.5f * radius), 0.01f), Vec3::sReplicate(2.0f)));
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Fast moving box stuck in ground moving, one moving up, one moving down
|
||||
CreateDynamicObject(offset + Vec3(-1, 0, 0), Vec3(0, speed, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
CreateDynamicObject(offset + Vec3(1, 0, 0), Vec3(0, -speed, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Single shape that has 4 walls to surround fast moving sphere
|
||||
BodyCreationSettings enclosing_settings;
|
||||
Ref<BoxShapeSettings> box_shape = new BoxShapeSettings(Vec3(1.0f, 1.0f, 0.1f));
|
||||
Ref<StaticCompoundShapeSettings> enclosing_shape = new StaticCompoundShapeSettings();
|
||||
enclosing_shape->AddShape(Vec3(0, 0, 1), Quat::sIdentity(), box_shape);
|
||||
enclosing_shape->AddShape(Vec3(0, 0, -1), Quat::sIdentity(), box_shape);
|
||||
enclosing_shape->AddShape(Vec3(1, 0, 0), Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI), box_shape);
|
||||
enclosing_shape->AddShape(Vec3(-1, 0, 0), Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI), box_shape);
|
||||
enclosing_settings.SetShapeSettings(enclosing_shape);
|
||||
enclosing_settings.mMotionType = EMotionType::Kinematic;
|
||||
enclosing_settings.mObjectLayer = Layers::MOVING;
|
||||
enclosing_settings.mPosition = offset + Vec3(0, 1, 0);
|
||||
mBodyInterface->CreateAndAddBody(enclosing_settings, EActivation::Activate);
|
||||
|
||||
// Fast moving sphere in box
|
||||
CreateDynamicObject(offset + Vec3(0, 0.5f, 0), Vec3(-speed, 0, -0.5f * speed), new SphereShape(radius));
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Two boxes on a collision course
|
||||
CreateDynamicObject(offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
CreateDynamicObject(offset + Vec3(-1, 0.5f, 0), Vec3(speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Two boxes on a collision course, off center
|
||||
CreateDynamicObject(offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
CreateDynamicObject(offset + Vec3(-1, 0.5f, radius), Vec3(speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Two boxes on a collision course, one discrete
|
||||
CreateDynamicObject(offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
CreateDynamicObject(offset + Vec3(-1, 0.5f, 0), Vec3(60.0f, 0, 0), new BoxShape(Vec3::sReplicate(radius)), EMotionQuality::Discrete);
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Two boxes on a collision course, one inactive
|
||||
CreateDynamicObject(offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
CreateDynamicObject(offset + Vec3(0, 0.5f, 0), Vec3::sZero(), new BoxShape(Vec3::sReplicate(radius)));
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Two boxes on a collision course, one inactive and discrete
|
||||
CreateDynamicObject(offset + Vec3(1, 0.5f, 0), Vec3(-speed, 0, 0), new BoxShape(Vec3::sReplicate(radius)));
|
||||
CreateDynamicObject(offset + Vec3(0, 0.5f, 0), Vec3::sZero(), new BoxShape(Vec3::sReplicate(radius)), EMotionQuality::Discrete);
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Create long thin shape
|
||||
BoxShapeSettings box_settings(Vec3(0.05f, 0.8f, 0.03f), 0.015f);
|
||||
box_settings.SetEmbedded();
|
||||
BodyCreationSettings body_settings(&box_settings, offset + Vec3(0, 1, 0), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
|
||||
body_settings.mMotionQuality = EMotionQuality::LinearCast;
|
||||
body_settings.mRestitution = 0.0f;
|
||||
body_settings.mFriction = 1.0f;
|
||||
|
||||
Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
|
||||
body.SetLinearVelocity(Vec3(0, -100.0f, 0));
|
||||
mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Create long thin shape under 45 degrees
|
||||
BoxShapeSettings box_settings(Vec3(0.05f, 0.8f, 0.03f), 0.015f);
|
||||
box_settings.SetEmbedded();
|
||||
BodyCreationSettings body_settings(&box_settings, offset + Vec3(0, 1, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
|
||||
body_settings.mMotionQuality = EMotionQuality::LinearCast;
|
||||
body_settings.mRestitution = 0.0f;
|
||||
body_settings.mFriction = 1.0f;
|
||||
|
||||
Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
|
||||
body.SetLinearVelocity(Vec3(0, -100.0f, 0));
|
||||
mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Create long thin shape with restitution
|
||||
BoxShapeSettings box_settings(Vec3(0.05f, 0.8f, 0.03f), 0.015f);
|
||||
box_settings.SetEmbedded();
|
||||
BodyCreationSettings body_settings(&box_settings, offset + Vec3(0, 1, 0), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
|
||||
body_settings.mMotionQuality = EMotionQuality::LinearCast;
|
||||
body_settings.mRestitution = 1.0f;
|
||||
body_settings.mFriction = 1.0f;
|
||||
|
||||
Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
|
||||
body.SetLinearVelocity(Vec3(0, -100.0f, 0));
|
||||
mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
|
||||
}
|
||||
|
||||
offset += Vec3(0, 0, 5);
|
||||
|
||||
{
|
||||
// Create long thin shape under 45 degrees with restitution
|
||||
BoxShapeSettings box_settings(Vec3(0.05f, 0.8f, 0.03f), 0.015f);
|
||||
box_settings.SetEmbedded();
|
||||
BodyCreationSettings body_settings(&box_settings, offset + Vec3(0, 1, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING);
|
||||
body_settings.mMotionQuality = EMotionQuality::LinearCast;
|
||||
body_settings.mRestitution = 1.0f;
|
||||
body_settings.mFriction = 1.0f;
|
||||
|
||||
Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
|
||||
body.SetLinearVelocity(Vec3(0, -100.0f, 0));
|
||||
mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
|
||||
void HighSpeedTest::CreateFastSmallConvexObjects()
|
||||
{
|
||||
// Create small convex hull
|
||||
Array<Vec3> vertices = {
|
||||
Vec3(-0.044661f, 0.001230f, 0.003877f),
|
||||
Vec3(-0.024743f, -0.042562f, 0.003877f),
|
||||
Vec3(-0.012336f, -0.021073f, 0.048484f),
|
||||
Vec3(0.016066f, 0.028121f, -0.049904f),
|
||||
Vec3(-0.023734f, 0.043275f, -0.024153f),
|
||||
Vec3(0.020812f, 0.036341f, -0.019530f),
|
||||
Vec3(0.012495f, 0.021936f, 0.045288f),
|
||||
Vec3(0.026750f, 0.001230f, 0.049273f),
|
||||
Vec3(0.045495f, 0.001230f, -0.022077f),
|
||||
Vec3(0.022193f, -0.036274f, -0.021126f),
|
||||
Vec3(0.022781f, -0.037291f, 0.029558f),
|
||||
Vec3(0.014691f, -0.023280f, 0.052897f),
|
||||
Vec3(-0.012187f, -0.020815f, -0.040214f),
|
||||
Vec3(0.000541f, 0.001230f, -0.056224f),
|
||||
Vec3(-0.039882f, 0.001230f, -0.019461f),
|
||||
Vec3(0.000541f, 0.001230f, 0.056022f),
|
||||
Vec3(-0.020614f, -0.035411f, -0.020551f),
|
||||
Vec3(-0.019485f, 0.035916f, 0.027001f),
|
||||
Vec3(-0.023968f, 0.043680f, 0.003877f),
|
||||
Vec3(-0.020051f, 0.001230f, 0.039543f),
|
||||
Vec3(0.026213f, 0.001230f, -0.040589f),
|
||||
Vec3(-0.010797f, 0.020868f, 0.043152f),
|
||||
Vec3(-0.012378f, 0.023607f, -0.040876f)
|
||||
};
|
||||
ConvexHullShapeSettings convex_settings(vertices);
|
||||
convex_settings.SetEmbedded();
|
||||
BodyCreationSettings body_settings(&convex_settings, RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
body_settings.mMotionQuality = EMotionQuality::LinearCast;
|
||||
|
||||
// Create many instances with high velocity
|
||||
default_random_engine rnd;
|
||||
uniform_real_distribution<float> restitution_distrib(0.0f, 0.1f);
|
||||
uniform_real_distribution<float> velocity_distrib(-10.0f, 10.0f);
|
||||
for (int x = -25; x < 25; ++x)
|
||||
for (int y = -25 ; y < 25; ++y)
|
||||
{
|
||||
// Cast a ray to find the terrain
|
||||
RVec3 origin(Real(x), 100.0_r, Real(y));
|
||||
Vec3 direction(0, -100.0f, 0);
|
||||
RRayCast ray { origin, direction };
|
||||
RayCastResult hit;
|
||||
if (mPhysicsSystem->GetNarrowPhaseQuery().CastRay(ray, hit, SpecifiedBroadPhaseLayerFilter(BroadPhaseLayers::NON_MOVING), SpecifiedObjectLayerFilter(Layers::NON_MOVING)))
|
||||
{
|
||||
// Place 10m above terrain
|
||||
body_settings.mPosition = ray.GetPointOnRay(hit.mFraction) + RVec3(0, 10, 0);
|
||||
body_settings.mRotation = Quat::sRandom(rnd);
|
||||
body_settings.mRestitution = restitution_distrib(rnd);
|
||||
|
||||
Body &body = *mPhysicsSystem->GetBodyInterface().CreateBody(body_settings);
|
||||
body.SetLinearVelocity(Vec3(velocity_distrib(rnd), -100.0f, velocity_distrib(rnd)));
|
||||
mPhysicsSystem->GetBodyInterface().AddBody(body.GetID(), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HighSpeedTest::CreateConvexOnLargeTriangles()
|
||||
{
|
||||
// Create floor
|
||||
CreateLargeTriangleFloor();
|
||||
|
||||
CreateFastSmallConvexObjects();
|
||||
}
|
||||
|
||||
void HighSpeedTest::CreateConvexOnTerrain1()
|
||||
{
|
||||
#ifdef JPH_OBJECT_STREAM
|
||||
// Load scene
|
||||
Ref<PhysicsScene> scene;
|
||||
AssetStream stream("terrain1.bof", std::ios::in | std::ios::binary);
|
||||
if (!ObjectStreamIn::sReadObject(stream.Get(), scene))
|
||||
FatalError("Failed to load scene");
|
||||
for (BodyCreationSettings &body : scene->GetBodies())
|
||||
body.mObjectLayer = Layers::NON_MOVING;
|
||||
scene->FixInvalidScales();
|
||||
scene->CreateBodies(mPhysicsSystem);
|
||||
#else
|
||||
CreateFloor();
|
||||
#endif // JPH_OBJECT_STREAM
|
||||
|
||||
CreateFastSmallConvexObjects();
|
||||
}
|
||||
|
||||
void HighSpeedTest::Initialize()
|
||||
{
|
||||
switch (sSelectedScene)
|
||||
{
|
||||
case 0:
|
||||
CreateSimpleScene();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
CreateConvexOnLargeTriangles();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
CreateConvexOnTerrain1();
|
||||
break;
|
||||
|
||||
default:
|
||||
JPH_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HighSpeedTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
|
||||
{
|
||||
inUI->CreateTextButton(inSubMenu, "Select Scene", [this, inUI]() {
|
||||
UIElement *scene_name = inUI->CreateMenu();
|
||||
for (uint i = 0; i < size(sScenes); ++i)
|
||||
inUI->CreateTextButton(scene_name, sScenes[i], [this, i]() { sSelectedScene = i; RestartTest(); });
|
||||
inUI->ShowMenu(scene_name);
|
||||
});
|
||||
}
|
||||
41
lib/All/JoltPhysics/Samples/Tests/General/HighSpeedTest.h
Normal file
41
lib/All/JoltPhysics/Samples/Tests/General/HighSpeedTest.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class HighSpeedTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, HighSpeedTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Spawns a number of high speed objects to check that they don't tunnel through geometry.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
|
||||
// Number used to scale the terrain and camera movement to the scene
|
||||
virtual float GetWorldScale() const override { return sSelectedScene == 0? 1.0f : 0.2f; }
|
||||
|
||||
// Optional settings menu
|
||||
virtual bool HasSettingsMenu() const override { return true; }
|
||||
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
|
||||
|
||||
private:
|
||||
void CreateDynamicObject(RVec3Arg inPosition, Vec3Arg inVelocity, Shape *inShape, EMotionQuality inMotionQuality = EMotionQuality::LinearCast);
|
||||
void CreateDominoBlocks(RVec3Arg inOffset, int inNumWalls, float inDensity, float inRadius);
|
||||
void CreateFastSmallConvexObjects();
|
||||
void CreateSimpleScene();
|
||||
void CreateConvexOnLargeTriangles();
|
||||
void CreateConvexOnTerrain1();
|
||||
|
||||
static const char * sScenes[];
|
||||
|
||||
static int sSelectedScene;
|
||||
};
|
||||
32
lib/All/JoltPhysics/Samples/Tests/General/IslandTest.cpp
Normal file
32
lib/All/JoltPhysics/Samples/Tests/General/IslandTest.cpp
Normal 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/General/IslandTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(IslandTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(IslandTest, Test)
|
||||
}
|
||||
|
||||
void IslandTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
// Walls
|
||||
for (int i = 0; i < 10; ++i)
|
||||
for (int j = i / 2; j < 10 - (i + 1) / 2; ++j)
|
||||
for (int k = 0; k < 8; ++k)
|
||||
{
|
||||
RVec3 position(-10 + j * 2.0f + (i & 1? 1.0f : 0.0f), 1.0f + i * 2.0f, 8.0f * (k - 4));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/IslandTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/IslandTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class IslandTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, IslandTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Creates a number of disjoint piles of blocks to see if the islands are properly determined and that the simulation spreads them out over multiple CPUs.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
50
lib/All/JoltPhysics/Samples/Tests/General/KinematicTest.cpp
Normal file
50
lib/All/JoltPhysics/Samples/Tests/General/KinematicTest.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/KinematicTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(KinematicTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(KinematicTest, Test)
|
||||
}
|
||||
|
||||
void KinematicTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Wall
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3(1.0f, 1.0f, 1.0f));
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (int j = i / 2; j < 10 - (i + 1) / 2; ++j)
|
||||
{
|
||||
RVec3 position(-10.0f + j * 2.0f + (i & 1? 1.0f : 0.0f), 1.0f + i * 2.0f, 0);
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::DontActivate);
|
||||
}
|
||||
|
||||
// Kinematic object
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
mKinematic[i] = mBodyInterface->CreateBody(BodyCreationSettings(new SphereShape(1.0f), RVec3(-10.0f, 2.0f, i == 0? 5.0f : -5.0f), Quat::sIdentity(), EMotionType::Kinematic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(mKinematic[i]->GetID(), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
|
||||
void KinematicTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
RVec3 com = mKinematic[i]->GetCenterOfMassPosition();
|
||||
if (com.GetZ() >= 5.0f)
|
||||
mKinematic[i]->SetLinearVelocity(Vec3(2.0f, 0, -10.0f));
|
||||
else if (com.GetZ() <= -5.0f)
|
||||
mKinematic[i]->SetLinearVelocity(Vec3(2.0f, 0, 10.0f));
|
||||
}
|
||||
}
|
||||
27
lib/All/JoltPhysics/Samples/Tests/General/KinematicTest.h
Normal file
27
lib/All/JoltPhysics/Samples/Tests/General/KinematicTest.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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/Body/Body.h>
|
||||
|
||||
class KinematicTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, KinematicTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Tests kinematic objects against a pile of dynamic boxes.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
private:
|
||||
Body * mKinematic[2];
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/LoadSaveBinaryTest.h>
|
||||
#include <Tests/General/LoadSaveSceneTest.h>
|
||||
#include <Jolt/Physics/PhysicsScene.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Jolt/Core/StreamWrapper.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(LoadSaveBinaryTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(LoadSaveBinaryTest, Test)
|
||||
}
|
||||
|
||||
void LoadSaveBinaryTest::Initialize()
|
||||
{
|
||||
// Create scene
|
||||
Ref<PhysicsScene> scene = LoadSaveSceneTest::sCreateScene();
|
||||
|
||||
{
|
||||
// Create a new scene by instantiating the scene in a physics system and then converting it back to a scene
|
||||
PhysicsSystem system;
|
||||
BPLayerInterfaceImpl layer_interface;
|
||||
ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter;
|
||||
ObjectLayerPairFilterImpl object_vs_object_layer_filter;
|
||||
system.Init(mPhysicsSystem->GetMaxBodies(), 0, 1024, 1024, layer_interface, object_vs_broadphase_layer_filter, object_vs_object_layer_filter);
|
||||
scene->CreateBodies(&system);
|
||||
Ref<PhysicsScene> scene_copy = new PhysicsScene();
|
||||
scene_copy->FromPhysicsSystem(&system);
|
||||
|
||||
// Replace the original scene
|
||||
scene = scene_copy;
|
||||
}
|
||||
|
||||
stringstream data;
|
||||
|
||||
// Write scene
|
||||
{
|
||||
StreamOutWrapper stream_out(data);
|
||||
scene->SaveBinaryState(stream_out, true, true);
|
||||
}
|
||||
|
||||
// Clear scene
|
||||
scene = nullptr;
|
||||
|
||||
// Read scene back in
|
||||
{
|
||||
StreamInWrapper stream_in(data);
|
||||
PhysicsScene::PhysicsSceneResult result = PhysicsScene::sRestoreFromBinaryState(stream_in);
|
||||
if (result.HasError())
|
||||
FatalError(result.GetError().c_str());
|
||||
scene = result.Get();
|
||||
}
|
||||
|
||||
// Instantiate scene
|
||||
scene->CreateBodies(mPhysicsSystem);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class LoadSaveBinaryTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, LoadSaveBinaryTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Tests the binary serialization system by creating a number of shapes, storing them, loading them and then simulating them.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
236
lib/All/JoltPhysics/Samples/Tests/General/LoadSaveSceneTest.cpp
Normal file
236
lib/All/JoltPhysics/Samples/Tests/General/LoadSaveSceneTest.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/LoadSaveSceneTest.h>
|
||||
#include <External/Perlin.h>
|
||||
#include <Jolt/ObjectStream/ObjectStreamOut.h>
|
||||
#include <Jolt/ObjectStream/ObjectStreamIn.h>
|
||||
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/ScaledShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.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/Collision/Shape/TaperedCylinderShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/MutableCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/HeightFieldShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/TriangleShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/EmptyShape.h>
|
||||
#include <Jolt/Physics/Collision/PhysicsMaterialSimple.h>
|
||||
#include <Jolt/Physics/Constraints/DistanceConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/SoftBodyCreator.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(LoadSaveSceneTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(LoadSaveSceneTest, Test)
|
||||
}
|
||||
|
||||
static const float cMaxHeight = 4.0f;
|
||||
|
||||
static MeshShapeSettings *sCreateMesh()
|
||||
{
|
||||
const int n = 10;
|
||||
const float cell_size = 2.0f;
|
||||
|
||||
// Create heights
|
||||
float heights[n + 1][n + 1];
|
||||
for (int x = 0; x <= n; ++x)
|
||||
for (int z = 0; z <= n; ++z)
|
||||
heights[x][z] = cMaxHeight * PerlinNoise3(float(x) / n, 0, float(z) / n, 256, 256, 256);
|
||||
|
||||
// Create 'wall' around grid
|
||||
for (int x = 0; x <= n; ++x)
|
||||
{
|
||||
heights[x][0] += 2.0f;
|
||||
heights[x][n] += 2.0f;
|
||||
}
|
||||
|
||||
for (int y = 1; y < n; ++y)
|
||||
{
|
||||
heights[0][y] += 2.0f;
|
||||
heights[n][y] += 2.0f;
|
||||
}
|
||||
|
||||
// Create regular grid of triangles
|
||||
uint32 max_material_index = 0;
|
||||
TriangleList triangles;
|
||||
for (int x = 0; x < n; ++x)
|
||||
for (int z = 0; z < n; ++z)
|
||||
{
|
||||
float center = n * cell_size / 2;
|
||||
|
||||
float x1 = cell_size * x - center;
|
||||
float z1 = cell_size * z - center;
|
||||
float x2 = x1 + cell_size;
|
||||
float z2 = z1 + cell_size;
|
||||
|
||||
Float3 v1 = Float3(x1, heights[x][z], z1);
|
||||
Float3 v2 = Float3(x2, heights[x + 1][z], z1);
|
||||
Float3 v3 = Float3(x1, heights[x][z + 1], z2);
|
||||
Float3 v4 = Float3(x2, heights[x + 1][z + 1], z2);
|
||||
|
||||
uint32 material_index = uint32((Vec3(v1) + Vec3(v2) + Vec3(v3) + Vec3(v4)).Length() / 4.0f / cell_size);
|
||||
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("Mesh Material " + ConvertToString(i), Color::sGetDistinctColor(i)));
|
||||
|
||||
return new MeshShapeSettings(triangles, std::move(materials));
|
||||
}
|
||||
|
||||
static HeightFieldShapeSettings *sCreateHeightField()
|
||||
{
|
||||
const int n = 32;
|
||||
const float cell_size = 1.0f;
|
||||
|
||||
// Create height samples
|
||||
float heights[n * n];
|
||||
for (int y = 0; y < n; ++y)
|
||||
for (int x = 0; x < n; ++x)
|
||||
heights[y * n + x] = cMaxHeight * PerlinNoise3(float(x) / n, 0, float(y) / n, 256, 256, 256);
|
||||
|
||||
// Make a hole
|
||||
heights[2 * n + 2] = HeightFieldShapeConstants::cNoCollisionValue;
|
||||
|
||||
// Make material indices
|
||||
uint8 max_material_index = 0;
|
||||
uint8 material_indices[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);
|
||||
material_indices[y * (n - 1) + x] = material_index;
|
||||
}
|
||||
|
||||
// Create materials
|
||||
PhysicsMaterialList materials;
|
||||
for (uint8 i = 0; i <= max_material_index; ++i)
|
||||
materials.push_back(new PhysicsMaterialSimple("HeightField Material " + ConvertToString(uint(i)), Color::sGetDistinctColor(i)));
|
||||
|
||||
// Create height field
|
||||
return new HeightFieldShapeSettings(heights, Vec3(-0.5f * cell_size * n, 0.0f, -0.5f * cell_size * n), Vec3(cell_size, 1.0f, cell_size), n, material_indices, materials);
|
||||
}
|
||||
|
||||
Ref<PhysicsScene> LoadSaveSceneTest::sCreateScene()
|
||||
{
|
||||
int color = 0;
|
||||
auto next_color = [&color]() { return Color::sGetDistinctColor(color++); };
|
||||
RVec3 pos(0, cMaxHeight, 0);
|
||||
auto next_pos = [&pos]() { pos += RVec3(0, 1.0f, 0); return pos; };
|
||||
|
||||
// Create scene
|
||||
Ref<PhysicsScene> scene = new PhysicsScene();
|
||||
|
||||
// A scaled mesh floor
|
||||
scene->AddBody(BodyCreationSettings(new ScaledShapeSettings(sCreateMesh(), Vec3(2.5f, 1.0f, 1.5f)), RVec3(0, 0, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
|
||||
// A heightfield floor
|
||||
scene->AddBody(BodyCreationSettings(sCreateHeightField(), RVec3(50, 0, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
|
||||
// Some simple primitives
|
||||
scene->AddBody(BodyCreationSettings(new TriangleShapeSettings(Vec3(-2, 0, 0), Vec3(0, 1, 0), Vec3(2, 0, 0), 0.0f, new PhysicsMaterialSimple("Triangle Material", next_color())), next_pos(), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Static, Layers::NON_MOVING));
|
||||
scene->AddBody(BodyCreationSettings(new SphereShapeSettings(0.2f, new PhysicsMaterialSimple("Sphere Material", next_color())), next_pos(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
scene->AddBody(BodyCreationSettings(new BoxShapeSettings(Vec3(0.2f, 0.2f, 0.4f), 0.01f, new PhysicsMaterialSimple("Box Material", next_color())), next_pos(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
scene->AddBody(BodyCreationSettings(new CapsuleShapeSettings(1.5f, 0.2f, new PhysicsMaterialSimple("Capsule Material", next_color())), next_pos(), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
scene->AddBody(BodyCreationSettings(new TaperedCapsuleShapeSettings(0.5f, 0.1f, 0.2f, new PhysicsMaterialSimple("Tapered Capsule Material", next_color())), next_pos(), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
scene->AddBody(BodyCreationSettings(new CylinderShapeSettings(0.5f, 0.2f, cDefaultConvexRadius, new PhysicsMaterialSimple("Cylinder Material", next_color())), next_pos(), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
scene->AddBody(BodyCreationSettings(new TaperedCylinderShapeSettings(0.5f, 0.2f, 0.4f, cDefaultConvexRadius, new PhysicsMaterialSimple("Tapered Cylinder Material", next_color())), next_pos(), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
scene->AddBody(BodyCreationSettings(new TaperedCylinderShapeSettings(0.5f, 0.4f, 0.0f, 0.0f, new PhysicsMaterialSimple("Cone Material", next_color())), next_pos(), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
scene->AddBody(BodyCreationSettings(new EmptyShapeSettings(), next_pos(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
|
||||
// Compound with sub compound and rotation
|
||||
StaticCompoundShapeSettings *sub_compound = new StaticCompoundShapeSettings();
|
||||
sub_compound->AddShape(Vec3(0, 0.5f, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new BoxShapeSettings(Vec3(0.5f, 0.1f, 0.2f), cDefaultConvexRadius, new PhysicsMaterialSimple("Compound Box Material", next_color())));
|
||||
sub_compound->AddShape(Vec3(0.5f, 0, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new CylinderShapeSettings(0.5f, 0.2f, cDefaultConvexRadius, new PhysicsMaterialSimple("Compound Cylinder Material", next_color())));
|
||||
sub_compound->AddShape(Vec3(0, 0, 0.5f), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), new TaperedCapsuleShapeSettings(0.5f, 0.1f, 0.2f, new PhysicsMaterialSimple("Compound Tapered Capsule Material", next_color())));
|
||||
StaticCompoundShapeSettings *compound_shape = new StaticCompoundShapeSettings();
|
||||
compound_shape->AddShape(Vec3(0, 0, 0), Quat::sRotation(Vec3::sAxisX(), -0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), sub_compound);
|
||||
compound_shape->AddShape(Vec3(0, -0.1f, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI) * Quat::sRotation(Vec3::sAxisZ(), -0.75f * JPH_PI), sub_compound);
|
||||
scene->AddBody(BodyCreationSettings(compound_shape, next_pos(), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
|
||||
// Convex hull shape
|
||||
Array<Vec3> tetrahedron;
|
||||
tetrahedron.push_back(Vec3(-0.5f, 0, -0.5f));
|
||||
tetrahedron.push_back(Vec3(0, 0, 0.5f));
|
||||
tetrahedron.push_back(Vec3(0.5f, 0, -0.5f));
|
||||
tetrahedron.push_back(Vec3(0, -0.5f, 0));
|
||||
Ref<ConvexHullShapeSettings> convex_hull = new ConvexHullShapeSettings(tetrahedron, cDefaultConvexRadius, new PhysicsMaterialSimple("Convex Hull Material", next_color()));
|
||||
scene->AddBody(BodyCreationSettings(convex_hull, next_pos(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
|
||||
// Rotated convex hull
|
||||
scene->AddBody(BodyCreationSettings(new RotatedTranslatedShapeSettings(Vec3::sReplicate(0.5f), Quat::sRotation(Vec3::sAxisZ(), 0.25f * JPH_PI), convex_hull), next_pos(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
|
||||
// Mutable compound
|
||||
MutableCompoundShapeSettings *mutable_compound = new MutableCompoundShapeSettings();
|
||||
mutable_compound->AddShape(Vec3(0, 0.5f, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new BoxShapeSettings(Vec3(0.5f, 0.1f, 0.2f), cDefaultConvexRadius, new PhysicsMaterialSimple("MutableCompound Box Material", next_color())));
|
||||
mutable_compound->AddShape(Vec3(0.5f, 0, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), new CapsuleShapeSettings(0.5f, 0.1f, new PhysicsMaterialSimple("MutableCompound Capsule Material", next_color())));
|
||||
mutable_compound->AddShape(Vec3(0, 0, 0.5f), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), new TaperedCapsuleShapeSettings(0.5f, 0.2f, 0.1f, new PhysicsMaterialSimple("MutableCompound Tapered Capsule Material", next_color())));
|
||||
scene->AddBody(BodyCreationSettings(mutable_compound, next_pos(), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
|
||||
// Connect the first two dynamic bodies with a distance constraint
|
||||
DistanceConstraintSettings *dist_constraint = new DistanceConstraintSettings();
|
||||
dist_constraint->mSpace = EConstraintSpace::LocalToBodyCOM;
|
||||
scene->AddConstraint(dist_constraint, 3, 4);
|
||||
|
||||
// Add soft body cube
|
||||
Ref<SoftBodySharedSettings> sb_cube_settings = SoftBodySharedSettings::sCreateCube(5, 0.2f);
|
||||
sb_cube_settings->mMaterials = { new PhysicsMaterialSimple("Soft Body Cube Material", next_color()) };
|
||||
SoftBodyCreationSettings sb_cube(sb_cube_settings, next_pos(), Quat::sIdentity(), Layers::MOVING);
|
||||
scene->AddSoftBody(sb_cube);
|
||||
|
||||
// Add the same shape again to test sharing
|
||||
sb_cube.mPosition = next_pos();
|
||||
scene->AddSoftBody(sb_cube);
|
||||
|
||||
// Add soft body sphere
|
||||
Ref<SoftBodySharedSettings> sb_sphere_settings = SoftBodyCreator::CreateSphere(0.5f);
|
||||
sb_sphere_settings->mMaterials = { new PhysicsMaterialSimple("Soft Body Sphere Material", next_color()) };
|
||||
SoftBodyCreationSettings sb_sphere(sb_sphere_settings, next_pos(), Quat::sIdentity(), Layers::MOVING);
|
||||
sb_sphere.mPressure = 2000.0f;
|
||||
scene->AddSoftBody(sb_sphere);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
void LoadSaveSceneTest::Initialize()
|
||||
{
|
||||
#ifdef JPH_OBJECT_STREAM
|
||||
Ref<PhysicsScene> scene = sCreateScene();
|
||||
|
||||
stringstream data;
|
||||
|
||||
// Write scene
|
||||
if (!ObjectStreamOut::sWriteObject(data, ObjectStream::EStreamType::Text, *scene))
|
||||
FatalError("Failed to save scene");
|
||||
|
||||
// Clear scene
|
||||
scene = nullptr;
|
||||
|
||||
// Read scene back in
|
||||
if (!ObjectStreamIn::sReadObject(data, scene))
|
||||
FatalError("Failed to load scene");
|
||||
|
||||
// Ensure that the soft body shared settings have been optimized (this is not saved to a text file)
|
||||
for (SoftBodyCreationSettings &soft_body : scene->GetSoftBodies())
|
||||
const_cast<SoftBodySharedSettings *>(soft_body.mSettings.GetPtr())->Optimize();
|
||||
|
||||
// Instantiate scene
|
||||
scene->CreateBodies(mPhysicsSystem);
|
||||
#endif // JPH_OBJECT_STREAM
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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/PhysicsScene.h>
|
||||
|
||||
class LoadSaveSceneTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, LoadSaveSceneTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "Tests the object stream serialization system by creating a number of shapes, storing them, loading them and then simulating them.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
|
||||
// Create a test scene
|
||||
static Ref<PhysicsScene> sCreateScene();
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ManifoldReductionTest.h>
|
||||
#include <External/Perlin.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/Shape/ScaledShape.h>
|
||||
#include <Jolt/Physics/Collision/PhysicsMaterialSimple.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ManifoldReductionTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ManifoldReductionTest, Test)
|
||||
}
|
||||
|
||||
void ManifoldReductionTest::Initialize()
|
||||
{
|
||||
constexpr float cPerturbance = 0.02f;
|
||||
|
||||
// Create mesh of regular grid of triangles
|
||||
TriangleList triangles;
|
||||
for (int x = -10; x < 10; ++x)
|
||||
for (int z = -10; z < 10; ++z)
|
||||
{
|
||||
float x1 = 0.1f * x;
|
||||
float z1 = 0.1f * z;
|
||||
float x2 = x1 + 0.1f;
|
||||
float z2 = z1 + 0.1f;
|
||||
|
||||
Float3 v1 = Float3(x1, cPerturbance * PerlinNoise3(x1, 0, z1, 256, 256, 256), z1);
|
||||
Float3 v2 = Float3(x2, cPerturbance * PerlinNoise3(x2, 0, z1, 256, 256, 256), z1);
|
||||
Float3 v3 = Float3(x1, cPerturbance * PerlinNoise3(x1, 0, z2, 256, 256, 256), z2);
|
||||
Float3 v4 = Float3(x2, cPerturbance * PerlinNoise3(x2, 0, z2, 256, 256, 256), z2);
|
||||
|
||||
triangles.push_back(Triangle(v1, v3, v4, 0));
|
||||
triangles.push_back(Triangle(v1, v4, v2, 0));
|
||||
}
|
||||
PhysicsMaterialList materials;
|
||||
materials.push_back(new PhysicsMaterialSimple());
|
||||
Ref<ShapeSettings> mesh_shape = new MeshShapeSettings(triangles, std::move(materials));
|
||||
|
||||
// Floor
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new ScaledShapeSettings(mesh_shape, Vec3::sReplicate(20)), RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// Create a box made of meshes
|
||||
Ref<StaticCompoundShapeSettings> mesh_box_shape = new StaticCompoundShapeSettings;
|
||||
mesh_box_shape->AddShape(Vec3(0, -1, 0), Quat::sRotation(Vec3::sAxisX(), JPH_PI), mesh_shape);
|
||||
mesh_box_shape->AddShape(Vec3(0, 1, 0), Quat::sIdentity(), mesh_shape);
|
||||
mesh_box_shape->AddShape(Vec3(-1, 0, 0), Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), mesh_shape);
|
||||
mesh_box_shape->AddShape(Vec3(1, 0, 0), Quat::sRotation(Vec3::sAxisZ(), -0.5f * JPH_PI), mesh_shape);
|
||||
mesh_box_shape->AddShape(Vec3(0, 0, -1), Quat::sRotation(Vec3::sAxisX(), -0.5f * JPH_PI), mesh_shape);
|
||||
mesh_box_shape->AddShape(Vec3(0, 0, 1), Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI), mesh_shape);
|
||||
|
||||
// A convex box
|
||||
RefConst<ShapeSettings> box_shape = new BoxShapeSettings(Vec3(1, 1, 1), 0.0f);
|
||||
|
||||
{
|
||||
// Create a compound of 3 mesh boxes
|
||||
Ref<StaticCompoundShapeSettings> three_mesh_box_shape = new StaticCompoundShapeSettings;
|
||||
three_mesh_box_shape->AddShape(Vec3(-2.1f, 0, 0), Quat::sIdentity(), mesh_box_shape);
|
||||
three_mesh_box_shape->AddShape(Vec3(0, -1, 0), Quat::sIdentity(), mesh_box_shape);
|
||||
three_mesh_box_shape->AddShape(Vec3(2.1f, 0, 0), Quat::sIdentity(), mesh_box_shape);
|
||||
|
||||
|
||||
// Create a compound of 3 convex boxes
|
||||
Ref<StaticCompoundShapeSettings> three_box_shape = new StaticCompoundShapeSettings;
|
||||
three_box_shape->AddShape(Vec3(-2.1f, 0, 0), Quat::sIdentity(), box_shape);
|
||||
three_box_shape->AddShape(Vec3(0, -1.1f, 0), Quat::sIdentity(), box_shape);
|
||||
three_box_shape->AddShape(Vec3(2.1f, 0, 0), Quat::sIdentity(), box_shape);
|
||||
|
||||
// A set of 3 mesh boxes to rest on
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(three_mesh_box_shape, RVec3(0, 1, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// A set of 3 boxes that are dynamic where the middle one penetrates more than the other two
|
||||
BodyCreationSettings box_settings(three_box_shape, RVec3(0, 2.95f, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
box_settings.mAllowSleeping = false;
|
||||
mBodyInterface->CreateAndAddBody(box_settings, EActivation::Activate);
|
||||
}
|
||||
|
||||
{
|
||||
// Create a compound of 2 mesh boxes
|
||||
Ref<StaticCompoundShapeSettings> two_mesh_box_shape = new StaticCompoundShapeSettings;
|
||||
two_mesh_box_shape->AddShape(Vec3(-2.1f, 0, 0), Quat::sIdentity(), mesh_box_shape);
|
||||
two_mesh_box_shape->AddShape(Vec3(0, -1, 0), Quat::sIdentity(), mesh_box_shape);
|
||||
|
||||
// Create a compound of 2 convex boxes
|
||||
Ref<StaticCompoundShapeSettings> two_box_shape = new StaticCompoundShapeSettings;
|
||||
two_box_shape->AddShape(Vec3(-2.1f, 0, 0), Quat::sIdentity(), box_shape);
|
||||
two_box_shape->AddShape(Vec3(0, -1, 0), Quat::sIdentity(), box_shape);
|
||||
|
||||
|
||||
// A set of 2 mesh boxes to rest on
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(two_mesh_box_shape, RVec3(0, 1, 4), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// A set of 2 boxes that are dynamic, one is lower than the other
|
||||
BodyCreationSettings box_settings(two_box_shape, RVec3(0, 4, 4), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
box_settings.mAllowSleeping = false;
|
||||
mBodyInterface->CreateAndAddBody(box_settings, EActivation::Activate);
|
||||
}
|
||||
|
||||
{
|
||||
// Create a compound of 2 meshes under small angle, small enough to combine the manifolds.
|
||||
Ref<StaticCompoundShapeSettings> two_mesh_shape = new StaticCompoundShapeSettings;
|
||||
two_mesh_shape->AddShape(Vec3(1, 0, 0), Quat::sRotation(Vec3::sAxisZ(), DegreesToRadians(2)), mesh_shape);
|
||||
two_mesh_shape->AddShape(Vec3(-1, 0, 0), Quat::sRotation(Vec3::sAxisZ(), DegreesToRadians(-2)), mesh_shape);
|
||||
|
||||
// A set of 2 meshes to rest on
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(two_mesh_shape, RVec3(0, 1, -4), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// A box that is dynamic, resting on the slightly sloped surface. The surface normals are close enough so that the manifold should be merged.
|
||||
BodyCreationSettings box_settings(box_shape, RVec3(0, 4, -4), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
box_settings.mAllowSleeping = false;
|
||||
mBodyInterface->CreateAndAddBody(box_settings, EActivation::Activate);
|
||||
}
|
||||
|
||||
{
|
||||
// Create a compound of 2 meshes under small angle, but bigger than the limit to combine the manifolds.
|
||||
Ref<StaticCompoundShapeSettings> two_mesh_shape = new StaticCompoundShapeSettings();
|
||||
two_mesh_shape->AddShape(Vec3(1, 0, 0), Quat::sRotation(Vec3::sAxisZ(), DegreesToRadians(3)), mesh_shape);
|
||||
two_mesh_shape->AddShape(Vec3(-1, 0, 0), Quat::sRotation(Vec3::sAxisZ(), DegreesToRadians(-3)), mesh_shape);
|
||||
|
||||
// A set of 2 meshes to rest on
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(two_mesh_shape, RVec3(0, 1, -8), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// A box that is dynamic, resting on the slightly sloped surface. The surface normals are not close enough so that the manifold should be merged.
|
||||
BodyCreationSettings box_settings(box_shape, RVec3(0, 4, -8), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
box_settings.mAllowSleeping = false;
|
||||
mBodyInterface->CreateAndAddBody(box_settings, EActivation::Activate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class ManifoldReductionTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ManifoldReductionTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "This test shows how many coplanar triangles are reduced to a single contact manifold.\n"
|
||||
"The static geometry in this test consists of a high density triangle grid.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
112
lib/All/JoltPhysics/Samples/Tests/General/ModifyMassTest.cpp
Normal file
112
lib/All/JoltPhysics/Samples/Tests/General/ModifyMassTest.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ModifyMassTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ModifyMassTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ModifyMassTest, Test)
|
||||
}
|
||||
|
||||
void ModifyMassTest::ResetBodies(int inCycle)
|
||||
{
|
||||
mBodyInterface->SetPositionAndRotation(mBodies[0], RVec3(-5, 5, 0), Quat::sIdentity(), EActivation::Activate);
|
||||
mBodyInterface->SetLinearAndAngularVelocity(mBodies[0], Vec3(10, 0, 0), Vec3::sZero());
|
||||
mBodyInterface->SetUserData(mBodies[0], inCycle << 1);
|
||||
|
||||
mBodyInterface->SetPositionAndRotation(mBodies[1], RVec3(5, 5, 0), Quat::sIdentity(), EActivation::Activate);
|
||||
mBodyInterface->SetLinearAndAngularVelocity(mBodies[1], Vec3(-10, 0, 0), Vec3::sZero());
|
||||
mBodyInterface->SetUserData(mBodies[1], (inCycle << 1) + 1);
|
||||
}
|
||||
|
||||
void ModifyMassTest::UpdateLabels()
|
||||
{
|
||||
for (BodyID id : mBodies)
|
||||
{
|
||||
BodyLockRead body_lock(mPhysicsSystem->GetBodyLockInterface(), id);
|
||||
if (body_lock.Succeeded())
|
||||
{
|
||||
const Body &body = body_lock.GetBody();
|
||||
SetBodyLabel(id, StringFormat("Inv mass scale: %.1f\nVelocity X: %.1f", (double)sGetInvMassScale(body), (double)body.GetLinearVelocity().GetX()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModifyMassTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Create two spheres on a collision course
|
||||
BodyCreationSettings bcs(new SphereShape(1.0f), RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
bcs.mRestitution = 1.0f;
|
||||
mBodies[0] = mBodyInterface->CreateAndAddBody(bcs, EActivation::Activate);
|
||||
mBodies[1] = mBodyInterface->CreateAndAddBody(bcs, EActivation::Activate);
|
||||
ResetBodies(0);
|
||||
UpdateLabels();
|
||||
}
|
||||
|
||||
void ModifyMassTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
constexpr float cTimeBetweenTests = 2.0f;
|
||||
|
||||
int old_cycle = (int)(mTime / cTimeBetweenTests);
|
||||
mTime += inParams.mDeltaTime;
|
||||
int new_cycle = (int)(mTime / cTimeBetweenTests);
|
||||
if (old_cycle != new_cycle)
|
||||
ResetBodies(new_cycle);
|
||||
|
||||
UpdateLabels();
|
||||
}
|
||||
|
||||
float ModifyMassTest::sGetInvMassScale(const Body &inBody)
|
||||
{
|
||||
uint64 ud = inBody.GetUserData();
|
||||
int index = ((ud & 1) != 0? (ud >> 1) : (ud >> 3)) & 0b11;
|
||||
float mass_overrides[] = { 1.0f, 0.0f, 0.5f, 2.0f };
|
||||
return mass_overrides[index];
|
||||
}
|
||||
|
||||
void ModifyMassTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
// We're only concerned with dynamic bodies (floor gets normal collision response)
|
||||
if (!inBody1.IsDynamic() || !inBody2.IsDynamic())
|
||||
return;
|
||||
|
||||
// Override the mass of body 1
|
||||
float scale1 = sGetInvMassScale(inBody1);
|
||||
ioSettings.mInvMassScale1 = scale1;
|
||||
ioSettings.mInvInertiaScale1 = scale1;
|
||||
|
||||
// Override the mass of body 2
|
||||
float scale2 = sGetInvMassScale(inBody2);
|
||||
ioSettings.mInvMassScale2 = scale2;
|
||||
ioSettings.mInvInertiaScale2 = scale2;
|
||||
}
|
||||
|
||||
void ModifyMassTest::OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
OnContactAdded(inBody1, inBody2, inManifold, ioSettings);
|
||||
}
|
||||
|
||||
void ModifyMassTest::SaveState(StateRecorder &inStream) const
|
||||
{
|
||||
Test::SaveState(inStream);
|
||||
|
||||
inStream.Write(mTime);
|
||||
}
|
||||
|
||||
void ModifyMassTest::RestoreState(StateRecorder &inStream)
|
||||
{
|
||||
Test::RestoreState(inStream);
|
||||
|
||||
inStream.Read(mTime);
|
||||
}
|
||||
|
||||
48
lib/All/JoltPhysics/Samples/Tests/General/ModifyMassTest.h
Normal file
48
lib/All/JoltPhysics/Samples/Tests/General/ModifyMassTest.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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/ContactListener.h>
|
||||
|
||||
class ModifyMassTest : public Test, public ContactListener
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ModifyMassTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "Uses a contact listener to modify the mass of bodies per contacting body pair.\n"
|
||||
"Can be used to e.g. make a dynamic body respond normally to one body and appear to have infinite mass for another.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
virtual void SaveState(StateRecorder &inStream) const override;
|
||||
virtual void RestoreState(StateRecorder &inStream) override;
|
||||
|
||||
// If this test implements a contact listener, it should be returned here
|
||||
virtual ContactListener * GetContactListener() override { return this; }
|
||||
|
||||
// See: ContactListener
|
||||
virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
|
||||
private:
|
||||
// Get the scale factor for a body based on its user data
|
||||
static float sGetInvMassScale(const Body &inBody);
|
||||
|
||||
// Reset the bodies to their initial states
|
||||
void ResetBodies(int inCycle);
|
||||
|
||||
// Update the labels on the bodies
|
||||
void UpdateLabels();
|
||||
|
||||
float mTime = 0.0f;
|
||||
|
||||
BodyID mBodies[2];
|
||||
};
|
||||
268
lib/All/JoltPhysics/Samples/Tests/General/MultithreadedTest.cpp
Normal file
268
lib/All/JoltPhysics/Samples/Tests/General/MultithreadedTest.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/MultithreadedTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/RayCast.h>
|
||||
#include <Jolt/Physics/Collision/CastResult.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Skeleton/Skeleton.h>
|
||||
#include <Jolt/Skeleton/SkeletalAnimation.h>
|
||||
#include <Jolt/Skeleton/SkeletonPose.h>
|
||||
#include <Jolt/Physics/Ragdoll/Ragdoll.h>
|
||||
#include <Jolt/ObjectStream/ObjectStreamIn.h>
|
||||
#include <Layers.h>
|
||||
#include <Utils/RagdollLoader.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(MultithreadedTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(MultithreadedTest, Test)
|
||||
}
|
||||
|
||||
MultithreadedTest::~MultithreadedTest()
|
||||
{
|
||||
// Quit the threads
|
||||
mIsQuitting = true;
|
||||
mBoxSpawnerThread.join();
|
||||
mRagdollSpawnerThread.join();
|
||||
mCasterThread.join();
|
||||
}
|
||||
|
||||
void MultithreadedTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Start threads
|
||||
mBoxSpawnerThread = thread([this]() { BoxSpawner(); });
|
||||
mRagdollSpawnerThread = thread([this]() { RagdollSpawner(); });
|
||||
mCasterThread = thread([this]() { CasterMain(); });
|
||||
}
|
||||
|
||||
void MultithreadedTest::Execute(default_random_engine &ioRandom, const char *inName, function<void()> inFunction)
|
||||
{
|
||||
uniform_real_distribution<float> chance(0, 1);
|
||||
if (chance(ioRandom) < 0.5f)
|
||||
{
|
||||
// Execute as a job and wait for it
|
||||
JobHandle handle = mJobSystem->CreateJob(inName, Color::sGreen, inFunction);
|
||||
while (!handle.IsDone())
|
||||
this_thread::sleep_for(1ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Execute in this separate thread (not part of the job system)
|
||||
JPH_PROFILE(inName);
|
||||
inFunction();
|
||||
}
|
||||
}
|
||||
|
||||
void MultithreadedTest::BoxSpawner()
|
||||
{
|
||||
JPH_PROFILE_THREAD_START("BoxSpawner");
|
||||
|
||||
#ifdef JPH_DEBUG
|
||||
const int cMaxObjects = 100;
|
||||
#else
|
||||
const int cMaxObjects = 1000;
|
||||
#endif
|
||||
|
||||
default_random_engine random;
|
||||
|
||||
Array<BodyID> bodies;
|
||||
|
||||
while (!mIsQuitting)
|
||||
{
|
||||
// Ensure there are enough objects at all times
|
||||
if (bodies.size() < cMaxObjects)
|
||||
{
|
||||
BodyID body_id;
|
||||
Execute(random, "AddBody", [this, &body_id, &random]() {
|
||||
uniform_real_distribution<float> from_y(0, 10);
|
||||
uniform_real_distribution<float> from_xz(-5, 5);
|
||||
RVec3 position(from_xz(random), 1.0f + from_y(random), from_xz(random));
|
||||
Quat orientation = Quat::sRandom(random);
|
||||
Vec3 velocity = Vec3::sRandom(random);
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(0.5f, 0.2f, 0.3f)), position, orientation, EMotionType::Dynamic, Layers::MOVING));
|
||||
body.SetLinearVelocity(velocity);
|
||||
body_id = body.GetID();
|
||||
mBodyInterface->AddBody(body_id, EActivation::Activate);
|
||||
});
|
||||
|
||||
Execute(random, "Remove/AddBody", [this, body_id]() {
|
||||
// Undo/redo add to trigger more race conditions
|
||||
mBodyInterface->RemoveBody(body_id);
|
||||
mBodyInterface->AddBody(body_id, EActivation::Activate);
|
||||
});
|
||||
|
||||
bodies.push_back(body_id);
|
||||
}
|
||||
|
||||
uniform_real_distribution<float> chance(0, 1);
|
||||
if (bodies.size() > 0 && chance(random) < 0.5f)
|
||||
{
|
||||
// Pick random body
|
||||
uniform_int_distribution<size_t> element(0, bodies.size() - 1);
|
||||
size_t index = element(random);
|
||||
BodyID body_id = bodies[index];
|
||||
bodies.erase(bodies.begin() + index);
|
||||
|
||||
Execute(random, "Remove/DestroyBody", [this, body_id]() {
|
||||
// Remove it
|
||||
mBodyInterface->RemoveBody(body_id);
|
||||
mBodyInterface->DestroyBody(body_id);
|
||||
});
|
||||
}
|
||||
|
||||
this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
JPH_PROFILE_THREAD_END();
|
||||
}
|
||||
|
||||
void MultithreadedTest::RagdollSpawner()
|
||||
{
|
||||
JPH_PROFILE_THREAD_START("RagdollSpawner");
|
||||
|
||||
#ifdef JPH_DEBUG
|
||||
const int cMaxRagdolls = 10;
|
||||
#else
|
||||
const int cMaxRagdolls = 50;
|
||||
#endif
|
||||
|
||||
#ifdef JPH_OBJECT_STREAM
|
||||
// Load ragdoll
|
||||
Ref<RagdollSettings> ragdoll_settings = RagdollLoader::sLoad("Human.tof", EMotionType::Dynamic);
|
||||
if (ragdoll_settings == nullptr)
|
||||
FatalError("Could not load ragdoll");
|
||||
#else
|
||||
// Create a ragdoll from code
|
||||
Ref<RagdollSettings> ragdoll_settings = RagdollLoader::sCreate();
|
||||
#endif // JPH_OBJECT_STREAM
|
||||
|
||||
// Create pose
|
||||
SkeletonPose ragdoll_pose;
|
||||
ragdoll_pose.SetSkeleton(ragdoll_settings->GetSkeleton());
|
||||
{
|
||||
#ifdef JPH_OBJECT_STREAM
|
||||
Ref<SkeletalAnimation> animation;
|
||||
AssetStream stream("Human/dead_pose1.tof", std::ios::in);
|
||||
if (!ObjectStreamIn::sReadObject(stream.Get(), animation))
|
||||
FatalError("Could not open animation");
|
||||
animation->Sample(0.0f, ragdoll_pose);
|
||||
#else
|
||||
Ref<Ragdoll> temp_ragdoll = ragdoll_settings->CreateRagdoll(0, 0, mPhysicsSystem);
|
||||
temp_ragdoll->GetPose(ragdoll_pose);
|
||||
ragdoll_pose.CalculateJointStates();
|
||||
#endif // JPH_OBJECT_STREAM
|
||||
}
|
||||
|
||||
default_random_engine random;
|
||||
uniform_real_distribution<float> from_y(0, 10);
|
||||
uniform_real_distribution<float> from_xz(-5, 5);
|
||||
|
||||
CollisionGroup::GroupID group_id = 1;
|
||||
|
||||
Array<Ref<Ragdoll>> ragdolls;
|
||||
|
||||
while (!mIsQuitting)
|
||||
{
|
||||
// Ensure there are enough objects at all times
|
||||
if (ragdolls.size() < cMaxRagdolls)
|
||||
{
|
||||
// Create ragdoll
|
||||
Ref<Ragdoll> ragdoll = ragdoll_settings->CreateRagdoll(group_id++, 0, mPhysicsSystem);
|
||||
|
||||
// Override root
|
||||
SkeletonPose::JointState &root = ragdoll_pose.GetJoint(0);
|
||||
root.mRotation = Quat::sRandom(random);
|
||||
ragdoll_pose.SetRootOffset(RVec3(from_xz(random), 1.0f + from_y(random), from_xz(random)));
|
||||
ragdoll_pose.CalculateJointMatrices();
|
||||
|
||||
// Drive to pose
|
||||
ragdoll->SetPose(ragdoll_pose);
|
||||
ragdoll->DriveToPoseUsingMotors(ragdoll_pose);
|
||||
|
||||
Execute(random, "Activate", [ragdoll]() {
|
||||
// Activate the ragdoll
|
||||
ragdoll->AddToPhysicsSystem(EActivation::Activate);
|
||||
});
|
||||
|
||||
Execute(random, "Deactivate/Activate", [ragdoll]() {
|
||||
// Undo/redo add to trigger more race conditions
|
||||
ragdoll->RemoveFromPhysicsSystem();
|
||||
ragdoll->AddToPhysicsSystem(EActivation::Activate);
|
||||
});
|
||||
|
||||
ragdolls.push_back(ragdoll);
|
||||
}
|
||||
|
||||
uniform_real_distribution<float> chance(0, 1);
|
||||
if (ragdolls.size() > 0 && chance(random) < 0.1f)
|
||||
{
|
||||
// Pick random body
|
||||
uniform_int_distribution<size_t> element(0, ragdolls.size() - 1);
|
||||
size_t index = element(random);
|
||||
Ref<Ragdoll> ragdoll = ragdolls[index];
|
||||
ragdolls.erase(ragdolls.begin() + index);
|
||||
|
||||
Execute(random, "Deactivate", [ragdoll]() {
|
||||
// Deactivate it
|
||||
ragdoll->RemoveFromPhysicsSystem();
|
||||
});
|
||||
}
|
||||
|
||||
this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
for (Ragdoll *r : ragdolls)
|
||||
r->RemoveFromPhysicsSystem();
|
||||
|
||||
JPH_PROFILE_THREAD_END();
|
||||
}
|
||||
|
||||
void MultithreadedTest::CasterMain()
|
||||
{
|
||||
JPH_PROFILE_THREAD_START("CasterMain");
|
||||
|
||||
default_random_engine random;
|
||||
|
||||
Array<BodyID> bodies;
|
||||
|
||||
while (!mIsQuitting)
|
||||
{
|
||||
Execute(random, "CastRay", [this, &random]() {
|
||||
// Cast a random ray
|
||||
uniform_real_distribution<float> from_y(0, 10);
|
||||
uniform_real_distribution<float> from_xz(-5, 5);
|
||||
RVec3 from(from_xz(random), from_y(random), from_xz(random));
|
||||
RVec3 to(from_xz(random), from_y(random), from_xz(random));
|
||||
RRayCast ray { from, Vec3(to - from) };
|
||||
RayCastResult hit;
|
||||
if (mPhysicsSystem->GetNarrowPhaseQuery().CastRay(ray, hit, SpecifiedBroadPhaseLayerFilter(BroadPhaseLayers::MOVING), SpecifiedObjectLayerFilter(Layers::MOVING)))
|
||||
{
|
||||
// Draw hit position
|
||||
RVec3 hit_position_world = ray.GetPointOnRay(hit.mFraction);
|
||||
mDebugRenderer->DrawMarker(hit_position_world, Color::sYellow, 0.2f);
|
||||
|
||||
BodyLockRead lock(mPhysicsSystem->GetBodyLockInterface(), hit.mBodyID);
|
||||
if (lock.SucceededAndIsInBroadPhase())
|
||||
{
|
||||
// Draw normal
|
||||
const Body &hit_body = lock.GetBody();
|
||||
RMat44 inv_com = hit_body.GetInverseCenterOfMassTransform();
|
||||
Vec3 normal = inv_com.Multiply3x3Transposed(hit_body.GetShape()->GetSurfaceNormal(hit.mSubShapeID2, Vec3(inv_com * hit_position_world))).Normalized();
|
||||
mDebugRenderer->DrawArrow(hit_position_world, hit_position_world + normal, Color::sGreen, 0.1f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
JPH_PROFILE_THREAD_END();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class MultithreadedTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, MultithreadedTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "This test spawns boxes and ragdolls and performs ray cast tests from threads / jobs to see if the simulation is thread safe.";
|
||||
}
|
||||
|
||||
// Destructor
|
||||
virtual ~MultithreadedTest() override;
|
||||
|
||||
// Number used to scale the terrain and camera movement to the scene
|
||||
virtual float GetWorldScale() const override { return 0.2f; }
|
||||
|
||||
// Initialization
|
||||
virtual void Initialize() override;
|
||||
|
||||
// Test will never be deterministic since various threads are trying to concurrently add / remove bodies
|
||||
virtual bool IsDeterministic() const override { return false; }
|
||||
|
||||
private:
|
||||
// Execute a lambda either on this thread or in a separate job
|
||||
void Execute(default_random_engine &ioRandom, const char *inName, function<void()> inFunction);
|
||||
|
||||
// Thread main function that spawns boxes
|
||||
void BoxSpawner();
|
||||
|
||||
// Thread main function that spawns ragdolls
|
||||
void RagdollSpawner();
|
||||
|
||||
// Thread main function that casts rays
|
||||
void CasterMain();
|
||||
|
||||
thread mBoxSpawnerThread;
|
||||
thread mRagdollSpawnerThread;
|
||||
thread mCasterThread;
|
||||
atomic<bool> mIsQuitting = false;
|
||||
};
|
||||
37
lib/All/JoltPhysics/Samples/Tests/General/PyramidTest.cpp
Normal file
37
lib/All/JoltPhysics/Samples/Tests/General/PyramidTest.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/PyramidTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(PyramidTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(PyramidTest, Test)
|
||||
}
|
||||
|
||||
void PyramidTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
const float cBoxSize = 2.0f;
|
||||
const float cBoxSeparation = 0.5f;
|
||||
const float cHalfBoxSize = 0.5f * cBoxSize;
|
||||
const int cPyramidHeight = 15;
|
||||
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3::sReplicate(cHalfBoxSize));
|
||||
|
||||
// Pyramid
|
||||
for (int i = 0; i < cPyramidHeight; ++i)
|
||||
for (int j = i / 2; j < cPyramidHeight - (i + 1) / 2; ++j)
|
||||
for (int k = i / 2; k < cPyramidHeight - (i + 1) / 2; ++k)
|
||||
{
|
||||
RVec3 position(-cPyramidHeight + cBoxSize * j + (i & 1? cHalfBoxSize : 0.0f), 1.0f + (cBoxSize + cBoxSeparation) * i, -cPyramidHeight + cBoxSize * k + (i & 1? cHalfBoxSize : 0.0f));
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
23
lib/All/JoltPhysics/Samples/Tests/General/PyramidTest.h
Normal file
23
lib/All/JoltPhysics/Samples/Tests/General/PyramidTest.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class PyramidTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, PyramidTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Tests a large pyramid of boxes to check stacking and performance behavior.\n"
|
||||
"The large island splitter should ensure that contacts are solved on multiple CPUs in parallel.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/RestitutionTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(RestitutionTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(RestitutionTest, Test)
|
||||
}
|
||||
|
||||
void RestitutionTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> sphere = new SphereShape(2.0f);
|
||||
RefConst<Shape> box = new BoxShape(Vec3(2.0f, 2.0f, 2.0f));
|
||||
|
||||
// Bodies with increasing restitution
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
BodyCreationSettings settings(sphere, RVec3(-50.0f + i * 10.0f, 20.0f, -20.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
settings.mRestitution = 0.1f * i;
|
||||
settings.mLinearDamping = 0.0f;
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(settings, EActivation::Activate);
|
||||
SetBodyLabel(id, StringFormat("Restitution: %.1f", double(settings.mRestitution)));
|
||||
}
|
||||
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
{
|
||||
BodyCreationSettings settings(box, RVec3(-50.0f + i * 10.0f, 20.0f, 20.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
settings.mRestitution = 0.1f * i;
|
||||
settings.mLinearDamping = 0.0f;
|
||||
BodyID id = mBodyInterface->CreateAndAddBody(settings, EActivation::Activate);
|
||||
SetBodyLabel(id, StringFormat("Restitution: %.1f", double(settings.mRestitution)));
|
||||
}
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/RestitutionTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/RestitutionTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class RestitutionTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, RestitutionTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Bodies with varying restitutions.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
251
lib/All/JoltPhysics/Samples/Tests/General/SensorTest.cpp
Normal file
251
lib/All/JoltPhysics/Samples/Tests/General/SensorTest.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/SensorTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/ObjectStream/ObjectStreamIn.h>
|
||||
#include <Utils/RagdollLoader.h>
|
||||
#include <Utils/Log.h>
|
||||
#include <Utils/AssetStream.h>
|
||||
#include <Layers.h>
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(SensorTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(SensorTest, Test)
|
||||
}
|
||||
|
||||
SensorTest::~SensorTest()
|
||||
{
|
||||
// Destroy the ragdoll
|
||||
mRagdoll->RemoveFromPhysicsSystem();
|
||||
mRagdoll = nullptr;
|
||||
}
|
||||
|
||||
void SensorTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor(400.0f);
|
||||
|
||||
{
|
||||
// A static sensor that attracts dynamic bodies that enter its area
|
||||
BodyCreationSettings sensor_settings(new SphereShape(10.0f), RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Static, Layers::SENSOR);
|
||||
sensor_settings.mIsSensor = true;
|
||||
mSensorID[StaticAttractor] = mBodyInterface->CreateAndAddBody(sensor_settings, EActivation::DontActivate);
|
||||
SetBodyLabel(mSensorID[StaticAttractor], "Static sensor that attracts dynamic bodies");
|
||||
}
|
||||
|
||||
{
|
||||
// A static sensor that only detects active bodies
|
||||
BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(5.0f)), RVec3(-10, 5.1f, 0), Quat::sIdentity(), EMotionType::Static, Layers::SENSOR);
|
||||
sensor_settings.mIsSensor = true;
|
||||
mSensorID[StaticSensor] = mBodyInterface->CreateAndAddBody(sensor_settings, EActivation::DontActivate);
|
||||
SetBodyLabel(mSensorID[StaticSensor], "Static sensor that detects active dynamic bodies");
|
||||
}
|
||||
|
||||
{
|
||||
// A kinematic sensor that also detects sleeping bodies
|
||||
BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(5.0f)), RVec3(10, 5.1f, 0), Quat::sIdentity(), EMotionType::Kinematic, Layers::SENSOR);
|
||||
sensor_settings.mIsSensor = true;
|
||||
mSensorID[KinematicSensor] = mBodyInterface->CreateAndAddBody(sensor_settings, EActivation::Activate);
|
||||
SetBodyLabel(mSensorID[KinematicSensor], "Kinematic sensor that also detects sleeping bodies");
|
||||
}
|
||||
|
||||
{
|
||||
// A kinematic sensor that also detects static bodies
|
||||
BodyCreationSettings sensor_settings(new BoxShape(Vec3::sReplicate(5.0f)), RVec3(25, 5.1f, 0), Quat::sIdentity(), EMotionType::Kinematic, Layers::MOVING); // Put in a layer that collides with static
|
||||
sensor_settings.mIsSensor = true;
|
||||
sensor_settings.mCollideKinematicVsNonDynamic = true;
|
||||
mSensorID[SensorDetectingStatic] = mBodyInterface->CreateAndAddBody(sensor_settings, EActivation::Activate);
|
||||
SetBodyLabel(mSensorID[SensorDetectingStatic], "Kinematic sensor that also detects sleeping and static bodies");
|
||||
}
|
||||
|
||||
// Dynamic bodies
|
||||
for (int i = 0; i < 15; ++i)
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(0.1f, 0.5f, 0.2f)), RVec3(-15.0f + i * 3.0f, 25, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
|
||||
// Static bodies
|
||||
RVec3 static_positions[] = { RVec3(-14, 1, 4), RVec3(6, 1, 4), RVec3(21, 1, 4) };
|
||||
for (const RVec3 &p : static_positions)
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.5f)), p, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::Activate);
|
||||
|
||||
#ifdef JPH_OBJECT_STREAM
|
||||
// Load ragdoll
|
||||
Ref<RagdollSettings> ragdoll_settings = RagdollLoader::sLoad("Human.tof", EMotionType::Dynamic);
|
||||
if (ragdoll_settings == nullptr)
|
||||
FatalError("Could not load ragdoll");
|
||||
#else
|
||||
Ref<RagdollSettings> ragdoll_settings = RagdollLoader::sCreate();
|
||||
#endif // JPH_OBJECT_STREAM
|
||||
|
||||
// Create pose
|
||||
SkeletonPose ragdoll_pose;
|
||||
ragdoll_pose.SetSkeleton(ragdoll_settings->GetSkeleton());
|
||||
{
|
||||
#ifdef JPH_OBJECT_STREAM
|
||||
Ref<SkeletalAnimation> animation;
|
||||
AssetStream stream("Human/dead_pose1.tof", std::ios::in);
|
||||
if (!ObjectStreamIn::sReadObject(stream.Get(), animation))
|
||||
FatalError("Could not open animation");
|
||||
animation->Sample(0.0f, ragdoll_pose);
|
||||
#else
|
||||
Ref<Ragdoll> temp_ragdoll = ragdoll_settings->CreateRagdoll(0, 0, mPhysicsSystem);
|
||||
temp_ragdoll->GetPose(ragdoll_pose);
|
||||
ragdoll_pose.CalculateJointStates();
|
||||
#endif // JPH_OBJECT_STREAM
|
||||
}
|
||||
ragdoll_pose.SetRootOffset(RVec3(0, 30, 0));
|
||||
ragdoll_pose.CalculateJointMatrices();
|
||||
|
||||
// Create ragdoll
|
||||
mRagdoll = ragdoll_settings->CreateRagdoll(1, 0, mPhysicsSystem);
|
||||
mRagdoll->SetPose(ragdoll_pose);
|
||||
mRagdoll->AddToPhysicsSystem(EActivation::Activate);
|
||||
|
||||
// Create kinematic body
|
||||
BodyCreationSettings kinematic_settings(new BoxShape(Vec3(0.25f, 0.5f, 1.0f)), RVec3(-20, 10, 0), Quat::sIdentity(), EMotionType::Kinematic, Layers::MOVING);
|
||||
Body &kinematic = *mBodyInterface->CreateBody(kinematic_settings);
|
||||
mKinematicBodyID = kinematic.GetID();
|
||||
mBodyInterface->AddBody(kinematic.GetID(), EActivation::Activate);
|
||||
}
|
||||
|
||||
void SensorTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
// Update time
|
||||
mTime += inParams.mDeltaTime;
|
||||
|
||||
// Move kinematic body
|
||||
RVec3 kinematic_pos = RVec3(-20.0f * Cos(mTime), 10, 0);
|
||||
mBodyInterface->MoveKinematic(mKinematicBodyID, kinematic_pos, Quat::sIdentity(), inParams.mDeltaTime);
|
||||
|
||||
// Draw if body is in sensor
|
||||
Color sensor_color[] = { Color::sRed, Color::sGreen, Color::sBlue, Color::sPurple };
|
||||
for (int sensor = 0; sensor < NumSensors; ++sensor)
|
||||
for (const BodyAndCount &body_and_count : mBodiesInSensor[sensor])
|
||||
{
|
||||
AABox bounds = mBodyInterface->GetTransformedShape(body_and_count.mBodyID).GetWorldSpaceBounds();
|
||||
bounds.ExpandBy(Vec3::sReplicate(0.01f * sensor));
|
||||
mDebugRenderer->DrawWireBox(bounds, sensor_color[sensor]);
|
||||
}
|
||||
|
||||
// Apply forces to dynamic bodies in sensor
|
||||
lock_guard lock(mMutex);
|
||||
|
||||
RVec3 center(0, 10, 0);
|
||||
float centrifugal_force = 10.0f;
|
||||
Vec3 gravity = mPhysicsSystem->GetGravity();
|
||||
|
||||
for (const BodyAndCount &body_and_count : mBodiesInSensor[StaticAttractor])
|
||||
{
|
||||
BodyLockWrite body_lock(mPhysicsSystem->GetBodyLockInterface(), body_and_count.mBodyID);
|
||||
if (body_lock.Succeeded())
|
||||
{
|
||||
Body &body = body_lock.GetBody();
|
||||
if (body.IsKinematic())
|
||||
continue;
|
||||
|
||||
// Calculate centrifugal acceleration
|
||||
Vec3 acceleration = Vec3(center - body.GetPosition());
|
||||
float length = acceleration.Length();
|
||||
if (length > 0.0f)
|
||||
acceleration *= centrifugal_force / length;
|
||||
else
|
||||
acceleration = Vec3::sZero();
|
||||
|
||||
// Draw acceleration
|
||||
mDebugRenderer->DrawArrow(body.GetPosition(), body.GetPosition() + acceleration, Color::sGreen, 0.1f);
|
||||
|
||||
// Cancel gravity
|
||||
acceleration -= gravity;
|
||||
|
||||
// Apply as force
|
||||
body.AddForce(acceleration / body.GetMotionProperties()->GetInverseMass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SensorTest::OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings)
|
||||
{
|
||||
for (int sensor = 0; sensor < NumSensors; ++sensor)
|
||||
{
|
||||
BodyID sensor_id = mSensorID[sensor];
|
||||
|
||||
// Check which body is the sensor
|
||||
BodyID body_id;
|
||||
if (inBody1.GetID() == sensor_id)
|
||||
body_id = inBody2.GetID();
|
||||
else if (inBody2.GetID() == sensor_id)
|
||||
body_id = inBody1.GetID();
|
||||
else
|
||||
continue;
|
||||
|
||||
lock_guard lock(mMutex);
|
||||
|
||||
// Add to list and make sure that the list remains sorted for determinism (contacts can be added from multiple threads)
|
||||
BodyAndCount body_and_count { body_id, 1 };
|
||||
BodiesInSensor &bodies_in_sensor = mBodiesInSensor[sensor];
|
||||
BodiesInSensor::iterator b = lower_bound(bodies_in_sensor.begin(), bodies_in_sensor.end(), body_and_count);
|
||||
if (b != bodies_in_sensor.end() && b->mBodyID == body_id)
|
||||
{
|
||||
// This is the right body, increment reference
|
||||
b->mCount++;
|
||||
return;
|
||||
}
|
||||
bodies_in_sensor.insert(b, body_and_count);
|
||||
}
|
||||
}
|
||||
|
||||
void SensorTest::OnContactRemoved(const SubShapeIDPair &inSubShapePair)
|
||||
{
|
||||
for (int sensor = 0; sensor < NumSensors; ++sensor)
|
||||
{
|
||||
BodyID sensor_id = mSensorID[sensor];
|
||||
|
||||
// Check which body is the sensor
|
||||
BodyID body_id;
|
||||
if (inSubShapePair.GetBody1ID() == sensor_id)
|
||||
body_id = inSubShapePair.GetBody2ID();
|
||||
else if (inSubShapePair.GetBody2ID() == sensor_id)
|
||||
body_id = inSubShapePair.GetBody1ID();
|
||||
else
|
||||
continue;
|
||||
|
||||
lock_guard lock(mMutex);
|
||||
|
||||
// Remove from list
|
||||
BodyAndCount body_and_count { body_id, 1 };
|
||||
BodiesInSensor &bodies_in_sensor = mBodiesInSensor[sensor];
|
||||
BodiesInSensor::iterator b = lower_bound(bodies_in_sensor.begin(), bodies_in_sensor.end(), body_and_count);
|
||||
if (b != bodies_in_sensor.end() && b->mBodyID == body_id)
|
||||
{
|
||||
// This is the right body, increment reference
|
||||
JPH_ASSERT(b->mCount > 0);
|
||||
b->mCount--;
|
||||
|
||||
// When last reference remove from the list
|
||||
if (b->mCount == 0)
|
||||
bodies_in_sensor.erase(b);
|
||||
return;
|
||||
}
|
||||
JPH_ASSERT(false, "Body pair not found");
|
||||
}
|
||||
}
|
||||
|
||||
void SensorTest::SaveState(StateRecorder &inStream) const
|
||||
{
|
||||
inStream.Write(mTime);
|
||||
for (const BodiesInSensor &b : mBodiesInSensor)
|
||||
inStream.Write(b);
|
||||
}
|
||||
|
||||
void SensorTest::RestoreState(StateRecorder &inStream)
|
||||
{
|
||||
inStream.Read(mTime);
|
||||
for (BodiesInSensor &b : mBodiesInSensor)
|
||||
inStream.Read(b);
|
||||
}
|
||||
72
lib/All/JoltPhysics/Samples/Tests/General/SensorTest.h
Normal file
72
lib/All/JoltPhysics/Samples/Tests/General/SensorTest.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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/Ragdoll/Ragdoll.h>
|
||||
|
||||
class SensorTest : public Test, public ContactListener
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SensorTest)
|
||||
|
||||
virtual ~SensorTest() override;
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Different types of sensors. See the description of each sensor.";
|
||||
}
|
||||
|
||||
// Number used to scale the terrain and camera movement to the scene
|
||||
virtual float GetWorldScale() const override { return 0.2f; }
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
// If this test implements a contact listener, it should be returned here
|
||||
virtual ContactListener *GetContactListener() override { return this; }
|
||||
|
||||
// See: ContactListener
|
||||
virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
virtual void OnContactRemoved(const SubShapeIDPair &inSubShapePair) override;
|
||||
|
||||
// Saving / restoring state for replay
|
||||
virtual void SaveState(StateRecorder &inStream) const override;
|
||||
virtual void RestoreState(StateRecorder &inStream) override;
|
||||
|
||||
private:
|
||||
float mTime = 0.0f; // Total elapsed time
|
||||
|
||||
enum
|
||||
{
|
||||
StaticAttractor, // A static sensor that attracts dynamic bodies that enter its area
|
||||
StaticSensor, // A static sensor that only detects active bodies
|
||||
KinematicSensor, // A kinematic sensor that also detects sleeping bodies
|
||||
SensorDetectingStatic, // A kinematic sensor that detects static bodies
|
||||
NumSensors
|
||||
};
|
||||
|
||||
BodyID mSensorID[NumSensors]; // Body ID of the various sensors
|
||||
|
||||
Ref<Ragdoll> mRagdoll; // Ragdoll that is falling into the sensor
|
||||
|
||||
BodyID mKinematicBodyID; // Body ID of a kinematic body that is animating in and out of the sensor
|
||||
|
||||
Mutex mMutex; // Mutex that protects mBodiesInSensor and mKinematicBodyInSensor
|
||||
|
||||
// Structure that keeps track of how many contact point each body has with the sensor
|
||||
struct BodyAndCount
|
||||
{
|
||||
BodyID mBodyID;
|
||||
int mCount;
|
||||
|
||||
bool operator < (const BodyAndCount &inRHS) const { return mBodyID < inRHS.mBodyID; }
|
||||
};
|
||||
|
||||
using BodiesInSensor = Array<BodyAndCount>;
|
||||
BodiesInSensor mBodiesInSensor[NumSensors]; // Dynamic bodies that are currently inside the sensor
|
||||
};
|
||||
112
lib/All/JoltPhysics/Samples/Tests/General/ShapeFilterTest.cpp
Normal file
112
lib/All/JoltPhysics/Samples/Tests/General/ShapeFilterTest.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/ShapeFilterTest.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/ShapeCast.h>
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ShapeFilterTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ShapeFilterTest, Test)
|
||||
}
|
||||
|
||||
void ShapeFilterTest::Initialize()
|
||||
{
|
||||
// Create geometry to cast against
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(20, 1, 3)), RVec3(0, -1, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::Activate);
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(3)), RVec3(0, 3, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING), EActivation::Activate);
|
||||
|
||||
// Create shape to cast
|
||||
Ref box_shape = new BoxShapeSettings(Vec3::sReplicate(1));
|
||||
box_shape->mUserData = (uint64)ShapeIdentifier::Box;
|
||||
|
||||
Ref sphere_shape = new SphereShapeSettings(1);
|
||||
sphere_shape->mUserData = (uint64)ShapeIdentifier::Sphere;
|
||||
|
||||
StaticCompoundShapeSettings cast_shape;
|
||||
cast_shape.AddShape(Vec3(3, 2, 0), Quat::sIdentity(), box_shape);
|
||||
cast_shape.AddShape(Vec3(0, 0, 0), Quat::sIdentity(), sphere_shape);
|
||||
cast_shape.mUserData = (uint64)ShapeIdentifier::Compound;
|
||||
|
||||
mCastShape = cast_shape.Create().Get();
|
||||
}
|
||||
|
||||
void ShapeFilterTest::PostPhysicsUpdate(float inDeltaTime)
|
||||
{
|
||||
mElapsedTime += inDeltaTime;
|
||||
float phase = mElapsedTime;
|
||||
|
||||
const RVec3 cast_origin = RVec3(Cos(phase) * 10, 10, 0);
|
||||
const Vec3 cast_motion = Vec3(0, -15, 0);
|
||||
|
||||
ClosestHitCollisionCollector<CastShapeCollector> cast_shape_collector;
|
||||
|
||||
class MyShapeFilter : public ShapeFilter
|
||||
{
|
||||
public:
|
||||
virtual bool ShouldCollide(const Shape *inShape1, const SubShapeID &inSubShapeID1, const Shape *inShape2, const SubShapeID &inSubShapeID2) const override
|
||||
{
|
||||
return inShape1->GetUserData() != mUserDataOfShapeToIgnore;
|
||||
}
|
||||
|
||||
// We're not interested in the other overload as it is not used by ray casts
|
||||
using ShapeFilter::ShouldCollide;
|
||||
|
||||
uint64 mUserDataOfShapeToIgnore = (uint64)ShapeIdentifier::Sphere;
|
||||
};
|
||||
|
||||
MyShapeFilter shape_filter;
|
||||
|
||||
// Select which shape to ignore
|
||||
float shape_select = fmod(phase, 6.0f * JPH_PI);
|
||||
const char *text;
|
||||
if (shape_select < 2.0f * JPH_PI)
|
||||
{
|
||||
shape_filter.mUserDataOfShapeToIgnore = (uint64)ShapeIdentifier::Box;
|
||||
text = "Box";
|
||||
}
|
||||
else if (shape_select < 4.0f * JPH_PI)
|
||||
{
|
||||
shape_filter.mUserDataOfShapeToIgnore = (uint64)ShapeIdentifier::Sphere;
|
||||
text = "Sphere";
|
||||
}
|
||||
else
|
||||
{
|
||||
shape_filter.mUserDataOfShapeToIgnore = (uint64)ShapeIdentifier::Compound;
|
||||
text = "Compound";
|
||||
}
|
||||
mDebugRenderer->DrawText3D(cast_origin, StringFormat("Ignoring shape: %s", text), Color::sWhite);
|
||||
|
||||
// Do the cast
|
||||
mPhysicsSystem->GetNarrowPhaseQuery().CastShape(
|
||||
RShapeCast(mCastShape, Vec3::sReplicate(1), RMat44::sTranslation(cast_origin), cast_motion),
|
||||
ShapeCastSettings(),
|
||||
RVec3::sZero(),
|
||||
cast_shape_collector,
|
||||
{ },
|
||||
{ },
|
||||
{ },
|
||||
shape_filter
|
||||
);
|
||||
|
||||
// Show the result
|
||||
RVec3 cast_point;
|
||||
Color color;
|
||||
if (cast_shape_collector.HadHit())
|
||||
{
|
||||
cast_point = cast_origin + cast_motion * cast_shape_collector.mHit.mFraction;
|
||||
color = Color::sGreen;
|
||||
}
|
||||
else
|
||||
{
|
||||
cast_point = cast_origin + cast_motion;
|
||||
color = Color::sRed;
|
||||
}
|
||||
mDebugRenderer->DrawArrow(cast_origin, cast_point, Color::sOrange, 0.1f);
|
||||
JPH_IF_DEBUG_RENDERER(mCastShape->Draw(mDebugRenderer, RMat44::sTranslation(RVec3(cast_point)), Vec3::sOne(), color, false, true);)
|
||||
}
|
||||
31
lib/All/JoltPhysics/Samples/Tests/General/ShapeFilterTest.h
Normal file
31
lib/All/JoltPhysics/Samples/Tests/General/ShapeFilterTest.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class ShapeFilterTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ShapeFilterTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Demonstrates how to use a shape filter to filter out shapes during a collision query.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PostPhysicsUpdate(float inDeltaTime) override;
|
||||
|
||||
private:
|
||||
/// A value used as user data for a shape
|
||||
enum class ShapeIdentifier : uint64
|
||||
{
|
||||
Box = 42,
|
||||
Sphere = 43,
|
||||
Compound = 44
|
||||
};
|
||||
|
||||
float mElapsedTime = 0.0f;
|
||||
ShapeRefC mCastShape;
|
||||
};
|
||||
@@ -0,0 +1,178 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class SimCollideBodyVsBodyTest : public Test, public ContactListener
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SimCollideBodyVsBodyTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Overrides the collide body vs body function on the simulation to reduce the number of contact points generated between sensors and other objects in the simulation.\n"
|
||||
"This can be useful to improve performance if you don't need to know about all contact points and are only interested in an overlap/no-overlap result.\n"
|
||||
"The static world consists of a single compound shape with many pyramid sub shapes.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
// If this test implements a contact listener, it should be returned here
|
||||
virtual ContactListener *GetContactListener() override { return this; }
|
||||
|
||||
// See: ContactListener
|
||||
virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override;
|
||||
|
||||
// Saving / restoring state for replay
|
||||
virtual void SaveState(StateRecorder &inStream) const override;
|
||||
virtual void RestoreState(StateRecorder &inStream) override;
|
||||
|
||||
private:
|
||||
int mPrevMode = -1; // Previous mode
|
||||
float mTime = 0.0f; // Total elapsed time
|
||||
|
||||
BodyID mSensorID; // Body ID of the sensor
|
||||
BodyIDVector mBodyIDs; // List of dynamic bodies
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/SimShapeFilterTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>
|
||||
#include <Utils/SoftBodyCreator.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(SimShapeFilterTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(SimShapeFilterTest, Test)
|
||||
}
|
||||
|
||||
SimShapeFilterTest::~SimShapeFilterTest()
|
||||
{
|
||||
// Unregister shape filter
|
||||
mPhysicsSystem->SetSimShapeFilter(nullptr);
|
||||
}
|
||||
|
||||
void SimShapeFilterTest::Initialize()
|
||||
{
|
||||
// Register shape filter
|
||||
mPhysicsSystem->SetSimShapeFilter(&mShapeFilter);
|
||||
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Platform
|
||||
mShapeFilter.mPlatformID = mBodyInterface->CreateAndAddBody(BodyCreationSettings(new BoxShape(Vec3(5.0f, 0.5f, 5.0f)), RVec3(0, 7.5f, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// Compound shape
|
||||
Ref<Shape> capsule = new CapsuleShape(2, 0.1f);
|
||||
capsule->SetUserData(1); // Don't want the capsule to collide with the platform
|
||||
Ref<Shape> sphere = new SphereShape(0.5f);
|
||||
sphere->SetUserData(1); // Don't want the sphere to collide with the platform
|
||||
Ref<Shape> box = new BoxShape(Vec3::sReplicate(0.5f));
|
||||
Ref<StaticCompoundShapeSettings> compound = new StaticCompoundShapeSettings;
|
||||
compound->AddShape(Vec3::sZero(), Quat::sIdentity(), capsule);
|
||||
compound->AddShape(Vec3(0, -2, 0), Quat::sIdentity(), sphere);
|
||||
compound->AddShape(Vec3(0, 2, 0), Quat::sIdentity(), box);
|
||||
|
||||
// Create compound above the platform
|
||||
BodyCreationSettings compound_body(compound, RVec3(0, 15, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
mShapeFilter.mCompoundID[0] = mBodyInterface->CreateAndAddBody(compound_body, EActivation::Activate);
|
||||
|
||||
// Create cloth that's fixated at the corners
|
||||
SoftBodyCreationSettings cloth(SoftBodyCreator::CreateClothWithFixatedCorners(20, 20, 0.2f), RVec3(10, 10.0f, 0), Quat::sIdentity(), Layers::MOVING);
|
||||
mShapeFilter.mClothID = mBodyInterface->CreateAndAddSoftBody(cloth, EActivation::Activate);
|
||||
|
||||
// Create compound above the cloth
|
||||
compound_body.mPosition = RVec3(10, 15, 0);
|
||||
mShapeFilter.mCompoundID[1] = mBodyInterface->CreateAndAddBody(compound_body, EActivation::Activate);
|
||||
}
|
||||
|
||||
bool SimShapeFilterTest::Filter::ShouldCollide(const Body &inBody1, const Shape *inShape1, const SubShapeID &inSubShapeIDOfShape1, const Body &inBody2, const Shape *inShape2, const SubShapeID &inSubShapeIDOfShape2) const
|
||||
{
|
||||
// If the platform/cloth is colliding with the compound, filter out collisions where the shape has user data 1
|
||||
if (inBody1.GetID() == mPlatformID || inBody1.GetID() == mClothID)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
if (inBody2.GetID() == mCompoundID[i])
|
||||
return inShape2->GetUserData() != 1;
|
||||
}
|
||||
else if (inBody2.GetID() == mPlatformID || inBody2.GetID() == mClothID)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
if (inBody1.GetID() == mCompoundID[i])
|
||||
return inShape1->GetUserData() != 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
#include <Jolt/Physics/Collision/SimShapeFilter.h>
|
||||
|
||||
class SimShapeFilterTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SimShapeFilterTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Shows how to use a shape filter during the simulation to disable contacts between certain sub shapes.\n"
|
||||
"The rod and sphere of the dynamic bodies only collide with the floor.";
|
||||
}
|
||||
|
||||
// Destructor
|
||||
virtual ~SimShapeFilterTest() override;
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
|
||||
private:
|
||||
// A simulation shape filter
|
||||
class Filter : public SimShapeFilter
|
||||
{
|
||||
public:
|
||||
virtual bool ShouldCollide(const Body &inBody1, const Shape *inShape1, const SubShapeID &inSubShapeIDOfShape1, const Body &inBody2, const Shape *inShape2, const SubShapeID &inSubShapeIDOfShape2) const override;
|
||||
|
||||
BodyID mPlatformID;
|
||||
BodyID mClothID;
|
||||
BodyID mCompoundID[2];
|
||||
};
|
||||
|
||||
Filter mShapeFilter;
|
||||
};
|
||||
43
lib/All/JoltPhysics/Samples/Tests/General/SimpleTest.cpp
Normal file
43
lib/All/JoltPhysics/Samples/Tests/General/SimpleTest.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/SimpleTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/Body/BodyActivationListener.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(SimpleTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(SimpleTest, Test)
|
||||
}
|
||||
|
||||
SimpleTest::~SimpleTest()
|
||||
{
|
||||
// Unregister activation listener
|
||||
mPhysicsSystem->SetBodyActivationListener(nullptr);
|
||||
}
|
||||
|
||||
void SimpleTest::Initialize()
|
||||
{
|
||||
// Register activation listener
|
||||
mPhysicsSystem->SetBodyActivationListener(&mBodyActivationListener);
|
||||
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3(0.5f, 1.0f, 2.0f));
|
||||
|
||||
// Dynamic body 1
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(0, 10, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
|
||||
// Dynamic body 2
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(5, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
|
||||
// Dynamic body 3
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(new SphereShape(2.0f), RVec3(10, 10, 0), Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
44
lib/All/JoltPhysics/Samples/Tests/General/SimpleTest.h
Normal file
44
lib/All/JoltPhysics/Samples/Tests/General/SimpleTest.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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/Body/BodyActivationListener.h>
|
||||
|
||||
class SimpleTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SimpleTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Very basic test that just drops a few objects on the floor.";
|
||||
}
|
||||
|
||||
// Destructor
|
||||
virtual ~SimpleTest() override;
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
|
||||
private:
|
||||
// A demo of the activation listener
|
||||
class Listener : public BodyActivationListener
|
||||
{
|
||||
public:
|
||||
virtual void OnBodyActivated(const BodyID &inBodyID, uint64 inBodyUserData) override
|
||||
{
|
||||
Trace("Body %d activated", inBodyID.GetIndex());
|
||||
}
|
||||
|
||||
virtual void OnBodyDeactivated(const BodyID &inBodyID, uint64 inBodyUserData) override
|
||||
{
|
||||
Trace("Body %d deactivated", inBodyID.GetIndex());
|
||||
}
|
||||
};
|
||||
|
||||
Listener mBodyActivationListener;
|
||||
};
|
||||
34
lib/All/JoltPhysics/Samples/Tests/General/StackTest.cpp
Normal file
34
lib/All/JoltPhysics/Samples/Tests/General/StackTest.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/StackTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(StackTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(StackTest, Test)
|
||||
}
|
||||
|
||||
void StackTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3(0.5f, 1.0f, 2.0f));
|
||||
|
||||
// Dynamic body stack
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
Quat rotation;
|
||||
if ((i & 1) != 0)
|
||||
rotation = Quat::sRotation(Vec3::sAxisY(), 0.5f * JPH_PI);
|
||||
else
|
||||
rotation = Quat::sIdentity();
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, RVec3(10, 1.0f + i * 2.1f, 0), rotation, EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/StackTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/StackTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class StackTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, StackTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Stacks a number of boxes to see if the simulation is stable.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
45
lib/All/JoltPhysics/Samples/Tests/General/TwoDFunnelTest.cpp
Normal file
45
lib/All/JoltPhysics/Samples/Tests/General/TwoDFunnelTest.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2022 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/TwoDFunnelTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(TwoDFunnelTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(TwoDFunnelTest, Test)
|
||||
}
|
||||
|
||||
void TwoDFunnelTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> wall = new BoxShape(Vec3(0.1f, 10, 1));
|
||||
|
||||
// 2D funnel
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(wall, RVec3(-12, 8, -5), Quat::sRotation(Vec3::sAxisZ(), 0.2f * JPH_PI), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(wall, RVec3(12, 8, -5), Quat::sRotation(Vec3::sAxisZ(), -0.2f * JPH_PI), EMotionType::Static, Layers::NON_MOVING), EActivation::DontActivate);
|
||||
|
||||
// Shapes falling in 2D funnel
|
||||
Ref<Shape> shapes[] = {
|
||||
new SphereShape(0.5f),
|
||||
new BoxShape(Vec3::sReplicate(0.5f)),
|
||||
new CapsuleShape(0.2f, 0.3f)
|
||||
};
|
||||
BodyCreationSettings bcs(shapes[0], RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
|
||||
bcs.mAllowedDOFs = EAllowedDOFs::Plane2D;
|
||||
for (int x = 0; x < 20; ++x)
|
||||
for (int y = 0; y < 10; ++y)
|
||||
{
|
||||
bcs.SetShape(shapes[(x * y) % size(shapes)]);
|
||||
bcs.mPosition = RVec3(-10.0_r + x, 10.0_r + y, -5.0_r);
|
||||
mBodyInterface->CreateAndAddBody(bcs, EActivation::Activate);
|
||||
}
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/TwoDFunnelTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/TwoDFunnelTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2022 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class TwoDFunnelTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, TwoDFunnelTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Shows how to create a 2D simulation.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
31
lib/All/JoltPhysics/Samples/Tests/General/WallTest.cpp
Normal file
31
lib/All/JoltPhysics/Samples/Tests/General/WallTest.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/General/WallTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(WallTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(WallTest, Test)
|
||||
}
|
||||
|
||||
void WallTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
RefConst<Shape> box_shape = new BoxShape(Vec3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
// Wall
|
||||
for (int i = 0; i < 10; ++i)
|
||||
for (int j = i / 2; j < 50 - (i + 1) / 2; ++j)
|
||||
{
|
||||
RVec3 position(-50 + j * 2.0f + (i & 1? 1.0f : 0.0f), 1.0f + i * 3.0f, 0);
|
||||
mBodyInterface->CreateAndAddBody(BodyCreationSettings(box_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING), EActivation::Activate);
|
||||
}
|
||||
}
|
||||
22
lib/All/JoltPhysics/Samples/Tests/General/WallTest.h
Normal file
22
lib/All/JoltPhysics/Samples/Tests/General/WallTest.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class WallTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, WallTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Tests a large pile of boxes to check stacking and performance behavior.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
Reference in New Issue
Block a user