Ajout de Jolt Physics + 1ere version des factory entitecomposants - camera, transform, rigidbody, collider, renderer
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/ConeConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/ConeConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ConeConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ConeConstraintTest, Test)
|
||||
}
|
||||
|
||||
void ConeConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
float half_cylinder_height = 2.5f;
|
||||
|
||||
const int cChainLength = 5;
|
||||
|
||||
// Build a collision group filter that disables collision between adjacent bodies
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(cChainLength);
|
||||
for (CollisionGroup::SubGroupID i = 0; i < cChainLength - 1; ++i)
|
||||
group_filter->DisableCollision(i, i + 1);
|
||||
|
||||
// Bodies attached through cone constraints
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
Body *prev = nullptr;
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI);
|
||||
RVec3 position(0, 20.0f, 10.0f * j);
|
||||
for (int i = 0; i < cChainLength; ++i)
|
||||
{
|
||||
position += Vec3(2.0f * half_cylinder_height, 0, 0);
|
||||
|
||||
Body &segment = *mBodyInterface->CreateBody(BodyCreationSettings(new CapsuleShape(half_cylinder_height, 1), position, Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI * i) * rotation, i == 0? EMotionType::Static : EMotionType::Dynamic, i == 0? Layers::NON_MOVING : Layers::MOVING));
|
||||
segment.SetCollisionGroup(CollisionGroup(group_filter, CollisionGroup::GroupID(j), CollisionGroup::SubGroupID(i)));
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
|
||||
if (prev != nullptr)
|
||||
{
|
||||
ConeConstraintSettings settings;
|
||||
settings.mPoint1 = settings.mPoint2 = position + Vec3(-half_cylinder_height, 0, 0);
|
||||
settings.mTwistAxis1 = settings.mTwistAxis2 = Vec3(1, 0, 0);
|
||||
if (j == 0)
|
||||
settings.mHalfConeAngle = 0.0f;
|
||||
else
|
||||
settings.mHalfConeAngle = DegreesToRadians(20);
|
||||
|
||||
mPhysicsSystem->AddConstraint(settings.Create(*prev, segment));
|
||||
}
|
||||
|
||||
prev = &segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class ConeConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ConeConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/ConstraintPriorityTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
#include <Renderer/DebugRendererImp.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ConstraintPriorityTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ConstraintPriorityTest, Test)
|
||||
}
|
||||
|
||||
void ConstraintPriorityTest::Initialize()
|
||||
{
|
||||
float box_size = 1.0f;
|
||||
RefConst<Shape> box = new BoxShape(Vec3(0.5f * box_size, 0.2f, 0.2f));
|
||||
|
||||
const int num_bodies = 20;
|
||||
|
||||
// Bodies attached through fixed constraints
|
||||
for (int priority = 0; priority < 2; ++priority)
|
||||
{
|
||||
RVec3 position(0, 10.0f, 0.2f * priority);
|
||||
Body &top = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
mBodyInterface->AddBody(top.GetID(), EActivation::DontActivate);
|
||||
|
||||
Body *prev = ⊤
|
||||
for (int i = 1; i < num_bodies; ++i)
|
||||
{
|
||||
position += Vec3(box_size, 0, 0);
|
||||
|
||||
Body &segment = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::NON_MOVING)); // Putting all bodies in the NON_MOVING layer so they won't collide
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
|
||||
FixedConstraintSettings settings;
|
||||
settings.mAutoDetectPoint = true;
|
||||
settings.mConstraintPriority = priority == 0? i : num_bodies - i; // Priority is reversed for one chain compared to the other
|
||||
Ref<Constraint> c = settings.Create(*prev, segment);
|
||||
mPhysicsSystem->AddConstraint(c);
|
||||
mConstraints.push_back(StaticCast<FixedConstraint>(c));
|
||||
|
||||
prev = &segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConstraintPriorityTest::PostPhysicsUpdate(float inDeltaTime)
|
||||
{
|
||||
for (FixedConstraint *c : mConstraints)
|
||||
mDebugRenderer->DrawText3D(0.5f * (c->GetBody1()->GetCenterOfMassPosition() + c->GetBody2()->GetCenterOfMassPosition()), StringFormat("Priority: %d", c->GetConstraintPriority()), Color::sWhite, 0.2f);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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/Constraints/FixedConstraint.h>
|
||||
|
||||
class ConstraintPriorityTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ConstraintPriorityTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "Tests constraint priority system to demonstrate that the order of solving can have an effect on the simulation.\n"
|
||||
"Solving the root first will make the system stiffer.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PostPhysicsUpdate(float inDeltaTime) override;
|
||||
|
||||
private:
|
||||
Array<Ref<FixedConstraint>> mConstraints;
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/ConstraintSingularityTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/HingeConstraint.h>
|
||||
#include <Jolt/Physics/Constraints/FixedConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ConstraintSingularityTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ConstraintSingularityTest, Test)
|
||||
}
|
||||
|
||||
void ConstraintSingularityTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
float box_size = 4.0f;
|
||||
RefConst<Shape> box = new BoxShape(Vec3::sReplicate(0.5f * box_size));
|
||||
|
||||
const int num_constraint_types = 2;
|
||||
const int num_configurations = 4;
|
||||
|
||||
// Create group filter
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable;
|
||||
CollisionGroup::GroupID group_id = 0;
|
||||
|
||||
for (int constraint_type = 0; constraint_type < num_constraint_types; ++constraint_type)
|
||||
for (int configuration = 0; configuration < num_configurations; ++configuration)
|
||||
{
|
||||
RVec3 test_position(10.0f * constraint_type, 10.0f + 10.0f * configuration, 0);
|
||||
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box, test_position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
body1.SetCollisionGroup(CollisionGroup(group_filter, group_id, 0));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::DontActivate);
|
||||
|
||||
Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(box, test_position + Vec3(box_size, 0, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body2.SetCollisionGroup(CollisionGroup(group_filter, group_id, 0));
|
||||
mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
|
||||
|
||||
Constraint *constraint;
|
||||
switch (constraint_type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
HingeConstraintSettings settings;
|
||||
settings.mPoint1 = settings.mPoint2 = test_position + Vec3(0.5f * box_size, 0, 0.5f * box_size);
|
||||
settings.mHingeAxis1 = settings.mHingeAxis2 = Vec3::sAxisY();
|
||||
settings.mNormalAxis1 = settings.mNormalAxis2 = Vec3::sAxisX();
|
||||
settings.mLimitsMin = -0.01f;
|
||||
settings.mLimitsMax = 0.01f;
|
||||
constraint = settings.Create(body1, body2);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
FixedConstraintSettings settings;
|
||||
settings.mAutoDetectPoint = true;
|
||||
constraint = settings.Create(body1, body2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mPhysicsSystem->AddConstraint(constraint);
|
||||
|
||||
RVec3 position;
|
||||
Quat orientation;
|
||||
switch (configuration)
|
||||
{
|
||||
case 0:
|
||||
position = test_position + Vec3(0, 0, box_size);
|
||||
orientation = Quat::sRotation(Vec3::sAxisY(), DegreesToRadians(180.0f));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
position = test_position + Vec3(0, 0, box_size);
|
||||
orientation = Quat::sRotation(Vec3::sAxisY(), DegreesToRadians(-90.0f)) * Quat::sRotation(Vec3::sAxisX(), DegreesToRadians(180.0f));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
position = test_position + Vec3(box_size, 0, 0);
|
||||
orientation = Quat::sRotation(Vec3::sAxisY(), DegreesToRadians(90.0f)) * Quat::sRotation(Vec3::sAxisZ(), DegreesToRadians(90.0f));
|
||||
break;
|
||||
|
||||
default:
|
||||
JPH_ASSERT(configuration == 3);
|
||||
position = test_position + Vec3(-box_size, 0, 0);
|
||||
orientation = Quat::sRotation(Vec3::sAxisY(), DegreesToRadians(90.0f)) * Quat::sRotation(Vec3::sAxisZ(), DegreesToRadians(90.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
mBodyInterface->SetPositionAndRotation(body2.GetID(), position, orientation, EActivation::DontActivate);
|
||||
|
||||
++group_id;
|
||||
}
|
||||
}
|
||||
@@ -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 ConstraintSingularityTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ConstraintSingularityTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char *GetDescription() const override
|
||||
{
|
||||
return "Starts constraints in a configuration where there are multiple directions to move in to satisfy the constraint.";
|
||||
}
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/ConstraintVsCOMChangeTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/MutableCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/HingeConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(ConstraintVsCOMChangeTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(ConstraintVsCOMChangeTest, Test)
|
||||
}
|
||||
|
||||
void ConstraintVsCOMChangeTest::Initialize()
|
||||
{
|
||||
constexpr int cChainLength = 15;
|
||||
constexpr float cMinAngle = DegreesToRadians(-10.0f);
|
||||
constexpr float cMaxAngle = DegreesToRadians(20.0f);
|
||||
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Create box shape
|
||||
mBox = new BoxShape(Vec3::sReplicate(0.5f * cBoxSize));
|
||||
|
||||
// Build a collision group filter that disables collision between adjacent bodies
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(cChainLength);
|
||||
for (CollisionGroup::SubGroupID i = 0; i < cChainLength - 1; ++i)
|
||||
group_filter->DisableCollision(i, i + 1);
|
||||
|
||||
// Create chain of bodies
|
||||
RVec3 position(0, 25, 0);
|
||||
for (int i = 0; i < cChainLength; ++i)
|
||||
{
|
||||
position += Vec3(cBoxSize, 0, 0);
|
||||
Quat rotation = Quat::sIdentity();
|
||||
|
||||
// Create compound shape specific for this body
|
||||
MutableCompoundShapeSettings compound_shape;
|
||||
compound_shape.SetEmbedded();
|
||||
compound_shape.AddShape(Vec3::sZero(), Quat::sIdentity(), mBox);
|
||||
|
||||
// Create body
|
||||
Body& segment = *mBodyInterface->CreateBody(BodyCreationSettings(&compound_shape, position, rotation, i == 0 ? EMotionType::Static : EMotionType::Dynamic, i == 0 ? Layers::NON_MOVING : Layers::MOVING));
|
||||
segment.SetCollisionGroup(CollisionGroup(group_filter, 0, CollisionGroup::SubGroupID(i)));
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
// Create hinge
|
||||
HingeConstraintSettings settings;
|
||||
settings.mPoint1 = settings.mPoint2 = position + Vec3(-0.5f * cBoxSize, -0.5f * cBoxSize, 0);
|
||||
settings.mHingeAxis1 = settings.mHingeAxis2 = Vec3::sAxisZ();
|
||||
settings.mNormalAxis1 = settings.mNormalAxis2 = Vec3::sAxisX();
|
||||
settings.mLimitsMin = cMinAngle;
|
||||
settings.mLimitsMax = cMaxAngle;
|
||||
Constraint* constraint = settings.Create(*mBodies.back(), segment);
|
||||
mPhysicsSystem->AddConstraint(constraint);
|
||||
|
||||
mConstraints.push_back(constraint);
|
||||
}
|
||||
|
||||
mBodies.push_back(&segment);
|
||||
}
|
||||
}
|
||||
|
||||
void ConstraintVsCOMChangeTest::PrePhysicsUpdate(const PreUpdateParams& inParams)
|
||||
{
|
||||
// Increment time
|
||||
mTime += inParams.mDeltaTime;
|
||||
|
||||
UpdateShapes();
|
||||
}
|
||||
|
||||
void ConstraintVsCOMChangeTest::SaveState(StateRecorder &inStream) const
|
||||
{
|
||||
inStream.Write(mTime);
|
||||
}
|
||||
|
||||
void ConstraintVsCOMChangeTest::RestoreState(StateRecorder &inStream)
|
||||
{
|
||||
inStream.Read(mTime);
|
||||
|
||||
UpdateShapes();
|
||||
}
|
||||
|
||||
void ConstraintVsCOMChangeTest::UpdateShapes()
|
||||
{
|
||||
// Check if we need to change the configuration
|
||||
int num_shapes = int(mTime) & 1? 2 : 1;
|
||||
if (mNumShapes != num_shapes)
|
||||
{
|
||||
mNumShapes = num_shapes;
|
||||
|
||||
// Change the COM of the bodies
|
||||
for (int i = 1; i < (int)mBodies.size(); i += 2)
|
||||
{
|
||||
Body *b = mBodies[i];
|
||||
MutableCompoundShape *s = static_cast<MutableCompoundShape *>(const_cast<Shape *>(b->GetShape()));
|
||||
|
||||
// Remember the center of mass before the change
|
||||
Vec3 prev_com = s->GetCenterOfMass();
|
||||
|
||||
// First remove all existing shapes
|
||||
for (int j = s->GetNumSubShapes() - 1; j >= 0; --j)
|
||||
s->RemoveShape(j);
|
||||
|
||||
// Then create the desired number of shapes
|
||||
for (int j = 0; j < num_shapes; ++j)
|
||||
s->AddShape(Vec3(0, 0, (1.0f + cBoxSize) * j), Quat::sIdentity(), mBox);
|
||||
|
||||
// Update the center of mass to account for the new box configuration
|
||||
s->AdjustCenterOfMass();
|
||||
|
||||
// Notify the physics system that the shape has changed
|
||||
mBodyInterface->NotifyShapeChanged(b->GetID(), prev_com, true, EActivation::Activate);
|
||||
|
||||
// Notify the constraints that the shape has changed (this could be done more efficient as we know which constraints are affected)
|
||||
Vec3 delta_com = s->GetCenterOfMass() - prev_com;
|
||||
for (Constraint *c : mConstraints)
|
||||
c->NotifyShapeChanged(b->GetID(), delta_com);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class ConstraintVsCOMChangeTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, ConstraintVsCOMChangeTest)
|
||||
|
||||
// Description of the test
|
||||
virtual const char * GetDescription() const override
|
||||
{
|
||||
return "This test demonstrates how to notify a constraint that the center of mass of a body changed.\n"
|
||||
"Constraints store their attachment points in center of mass space.";
|
||||
}
|
||||
|
||||
// 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 UpdateShapes();
|
||||
|
||||
RefConst<Shape> mBox;
|
||||
Array<Body *> mBodies;
|
||||
Array<Ref<Constraint>> mConstraints;
|
||||
|
||||
static constexpr float cBoxSize = 2.0f;
|
||||
|
||||
float mTime = 0.0f;
|
||||
int mNumShapes = -1;
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/DistanceConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Constraints/DistanceConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(DistanceConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(DistanceConstraintTest, Test)
|
||||
}
|
||||
|
||||
void DistanceConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
float half_cylinder_height = 2.5f;
|
||||
|
||||
// Variation 0: Fixed distance
|
||||
// Variation 1: Min/max distance
|
||||
for (int variation = 0; variation < 2; ++variation)
|
||||
{
|
||||
// Bodies attached through distance constraints
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI);
|
||||
RVec3 position(0, 75, 10.0f * variation);
|
||||
Body &top = *mBodyInterface->CreateBody(BodyCreationSettings(new CapsuleShape(half_cylinder_height, 1), position, rotation, EMotionType::Static, Layers::NON_MOVING));
|
||||
mBodyInterface->AddBody(top.GetID(), EActivation::DontActivate);
|
||||
|
||||
Body *prev = ⊤
|
||||
for (int i = 1; i < 15; ++i)
|
||||
{
|
||||
position += Vec3(5.0f + 2.0f * half_cylinder_height, 0, 0);
|
||||
|
||||
Body &segment = *mBodyInterface->CreateBody(BodyCreationSettings(new CapsuleShape(half_cylinder_height, 1), position, rotation, EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
|
||||
DistanceConstraintSettings settings;
|
||||
settings.mPoint1 = position - Vec3(5.0f + half_cylinder_height, 0, 0);
|
||||
settings.mPoint2 = position - Vec3(half_cylinder_height, 0, 0);
|
||||
|
||||
if (variation == 1)
|
||||
{
|
||||
// Default distance is 5, override min/max range
|
||||
settings.mMinDistance = 4.0f;
|
||||
settings.mMaxDistance = 8.0f;
|
||||
}
|
||||
|
||||
mPhysicsSystem->AddConstraint(settings.Create(*prev, segment));
|
||||
|
||||
prev = &segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class DistanceConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, DistanceConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,165 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/FixedConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/FixedConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(FixedConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(FixedConstraintTest, Test)
|
||||
}
|
||||
|
||||
void FixedConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
float box_size = 4.0f;
|
||||
RefConst<Shape> box = new BoxShape(Vec3::sReplicate(0.5f * box_size));
|
||||
|
||||
const int num_bodies = 10;
|
||||
|
||||
// Build a collision group filter that disables collision between adjacent bodies
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(num_bodies);
|
||||
for (CollisionGroup::SubGroupID i = 0; i < num_bodies - 1; ++i)
|
||||
group_filter->DisableCollision(i, i + 1);
|
||||
|
||||
// Bodies attached through fixed constraints
|
||||
for (int randomness = 0; randomness < 2; ++randomness)
|
||||
{
|
||||
CollisionGroup::GroupID group_id = CollisionGroup::GroupID(randomness);
|
||||
|
||||
RVec3 position(0, 25.0f, -randomness * 20.0f);
|
||||
Body &top = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
top.SetCollisionGroup(CollisionGroup(group_filter, group_id, 0));
|
||||
mBodyInterface->AddBody(top.GetID(), EActivation::DontActivate);
|
||||
|
||||
default_random_engine random;
|
||||
uniform_real_distribution<float> displacement(-1.0f, 1.0f);
|
||||
|
||||
Body *prev = ⊤
|
||||
for (int i = 1; i < num_bodies; ++i)
|
||||
{
|
||||
Quat rotation;
|
||||
if (randomness == 0)
|
||||
{
|
||||
position += Vec3(box_size, 0, 0);
|
||||
rotation = Quat::sIdentity();
|
||||
}
|
||||
else
|
||||
{
|
||||
position += Vec3(box_size + abs(displacement(random)), displacement(random), displacement(random));
|
||||
rotation = Quat::sRandom(random);
|
||||
}
|
||||
|
||||
Body &segment = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, rotation, EMotionType::Dynamic, Layers::MOVING));
|
||||
segment.SetCollisionGroup(CollisionGroup(group_filter, group_id, CollisionGroup::SubGroupID(i)));
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
|
||||
FixedConstraintSettings settings;
|
||||
settings.mAutoDetectPoint = true;
|
||||
Ref<Constraint> c = settings.Create(*prev, segment);
|
||||
mPhysicsSystem->AddConstraint(c);
|
||||
|
||||
prev = &segment;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Two light bodies attached to a heavy body (gives issues if the wrong anchor point is chosen)
|
||||
Body *light1 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.1f)), RVec3(-5.0f, 7.0f, -5.2f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(light1->GetID(), EActivation::Activate);
|
||||
Body *heavy = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(5.0f)), RVec3(-5.0f, 7.0f, 0.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(heavy->GetID(), EActivation::Activate);
|
||||
Body *light2 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.1f)), RVec3(-5.0f, 7.0f, 5.2f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(light2->GetID(), EActivation::Activate);
|
||||
|
||||
FixedConstraintSettings light1_heavy;
|
||||
light1_heavy.mAutoDetectPoint = true;
|
||||
mPhysicsSystem->AddConstraint(light1_heavy.Create(*light1, *heavy));
|
||||
|
||||
FixedConstraintSettings heavy_light2;
|
||||
heavy_light2.mAutoDetectPoint = true;
|
||||
mPhysicsSystem->AddConstraint(heavy_light2.Create(*heavy, *light2));
|
||||
}
|
||||
|
||||
{
|
||||
// A tower of beams and crossbeams (note that it is not recommended to make constructs with this many fixed constraints, this is not always stable)
|
||||
RVec3 base_position(0, 25, -40.0f);
|
||||
Quat base_rotation = Quat::sRotation(Vec3::sAxisZ(), -0.5f * JPH_PI);
|
||||
|
||||
Ref<BoxShape> pillar = new BoxShape(Vec3(0.1f, 1.0f, 0.1f), 0.0f);
|
||||
Ref<BoxShape> beam = new BoxShape(Vec3(0.01f, 1.5f, 0.1f), 0.0f);
|
||||
|
||||
Body *prev_pillars[4] = { &Body::sFixedToWorld, &Body::sFixedToWorld, &Body::sFixedToWorld, &Body::sFixedToWorld };
|
||||
|
||||
Vec3 center = Vec3::sZero();
|
||||
for (int y = 0; y < 10; ++y)
|
||||
{
|
||||
// Create pillars
|
||||
Body *pillars[4];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisY(), i * 0.5f * JPH_PI);
|
||||
|
||||
pillars[i] = mBodyInterface->CreateBody(BodyCreationSettings(pillar, base_position + base_rotation * (center + rotation * Vec3(1.0f, 1.0f, 1.0f)), base_rotation, EMotionType::Dynamic, Layers::MOVING));
|
||||
pillars[i]->SetCollisionGroup(CollisionGroup(group_filter, 0, 0)); // For convenience, we disable collisions between all objects in the tower
|
||||
mBodyInterface->AddBody(pillars[i]->GetID(), EActivation::Activate);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisY(), i * 0.5f * JPH_PI);
|
||||
|
||||
// Create cross beam
|
||||
Body *cross = mBodyInterface->CreateBody(BodyCreationSettings(beam, base_position + base_rotation * (center + rotation * Vec3(1.105f, 1.0f, 0.0f)), base_rotation * rotation * Quat::sRotation(Vec3::sAxisX(), 0.3f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
|
||||
cross->SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(cross->GetID(), EActivation::Activate);
|
||||
|
||||
// Attach cross beam to pillars
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
FixedConstraintSettings constraint;
|
||||
constraint.mAutoDetectPoint = true;
|
||||
constraint.mNumVelocityStepsOverride = 64; // This structure needs more solver steps to be stable
|
||||
constraint.mNumPositionStepsOverride = JPH_IF_NOT_DEBUG(64) JPH_IF_DEBUG(8); // In debug mode use less steps to preserve framerate (at the cost of stability)
|
||||
mPhysicsSystem->AddConstraint(constraint.Create(*pillars[(i + j) % 4], *cross));
|
||||
}
|
||||
|
||||
// Attach to previous pillar
|
||||
if (prev_pillars[i] != nullptr)
|
||||
{
|
||||
FixedConstraintSettings constraint;
|
||||
constraint.mAutoDetectPoint = true;
|
||||
constraint.mNumVelocityStepsOverride = 64;
|
||||
constraint.mNumPositionStepsOverride = JPH_IF_NOT_DEBUG(64) JPH_IF_DEBUG(8);
|
||||
mPhysicsSystem->AddConstraint(constraint.Create(*prev_pillars[i], *pillars[i]));
|
||||
}
|
||||
|
||||
prev_pillars[i] = pillars[i];
|
||||
}
|
||||
|
||||
center += Vec3(0.0f, 2.0f, 0.0f);
|
||||
}
|
||||
|
||||
// Create top
|
||||
Body *top = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(1.2f, 0.1f, 1.2f)), base_position + base_rotation * (center + Vec3(0.0f, 0.1f, 0.0f)), base_rotation, EMotionType::Dynamic, Layers::MOVING));
|
||||
top->SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(top->GetID(), EActivation::Activate);
|
||||
|
||||
// Attach top to pillars
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
FixedConstraintSettings constraint;
|
||||
constraint.mAutoDetectPoint = true;
|
||||
mPhysicsSystem->AddConstraint(constraint.Create(*prev_pillars[i], *top));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class FixedConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, FixedConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/GearConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/HingeConstraint.h>
|
||||
#include <Jolt/Physics/Constraints/GearConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(GearConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(GearConstraintTest, Test)
|
||||
}
|
||||
|
||||
void GearConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
constexpr float cGearHalfWidth = 0.05f;
|
||||
|
||||
constexpr float cGear1Radius = 0.5f;
|
||||
constexpr int cGear1NumTeeth = 100;
|
||||
|
||||
constexpr float cGear2Radius = 2.0f;
|
||||
constexpr int cGear2NumTeeth = int(cGear1NumTeeth * cGear2Radius / cGear1Radius);
|
||||
|
||||
constexpr float cToothThicknessBottom = 0.01f;
|
||||
constexpr float cToothThicknessTop = 0.005f;
|
||||
constexpr float cToothHeight = 0.02f;
|
||||
|
||||
// Create a tooth
|
||||
Array<Vec3> tooth_points = {
|
||||
Vec3(0, cGearHalfWidth, cToothThicknessBottom),
|
||||
Vec3(0, -cGearHalfWidth, cToothThicknessBottom),
|
||||
Vec3(0, cGearHalfWidth, -cToothThicknessBottom),
|
||||
Vec3(0, -cGearHalfWidth, -cToothThicknessBottom),
|
||||
Vec3(cToothHeight, -cGearHalfWidth, cToothThicknessTop),
|
||||
Vec3(cToothHeight, cGearHalfWidth, cToothThicknessTop),
|
||||
Vec3(cToothHeight, -cGearHalfWidth, -cToothThicknessTop),
|
||||
Vec3(cToothHeight, cGearHalfWidth, -cToothThicknessTop),
|
||||
};
|
||||
ConvexHullShapeSettings tooth_settings(tooth_points);
|
||||
tooth_settings.SetEmbedded();
|
||||
|
||||
// Create gear 1
|
||||
CylinderShapeSettings gear1_cylinder(cGearHalfWidth, cGear1Radius);
|
||||
gear1_cylinder.SetEmbedded();
|
||||
|
||||
StaticCompoundShapeSettings gear1_settings;
|
||||
gear1_settings.SetEmbedded();
|
||||
|
||||
gear1_settings.AddShape(Vec3::sZero(), Quat::sIdentity(), &gear1_cylinder);
|
||||
for (int i = 0; i < cGear1NumTeeth; ++i)
|
||||
{
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisY(), 2.0f * JPH_PI * i / cGear1NumTeeth);
|
||||
gear1_settings.AddShape(rotation * Vec3(cGear1Radius, 0, 0), rotation, &tooth_settings);
|
||||
}
|
||||
|
||||
RVec3 gear1_initial_p(0, 3.0f, 0);
|
||||
Quat gear1_initial_r = Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI);
|
||||
Body *gear1 = mBodyInterface->CreateBody(BodyCreationSettings(&gear1_settings, gear1_initial_p, gear1_initial_r, EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(gear1->GetID(), EActivation::Activate);
|
||||
|
||||
// Create gear 2
|
||||
CylinderShapeSettings gear2_cylinder(cGearHalfWidth, cGear2Radius);
|
||||
gear2_cylinder.SetEmbedded();
|
||||
|
||||
StaticCompoundShapeSettings gear2_settings;
|
||||
gear2_settings.SetEmbedded();
|
||||
|
||||
gear2_settings.AddShape(Vec3::sZero(), Quat::sIdentity(), &gear2_cylinder);
|
||||
for (int i = 0; i < cGear2NumTeeth; ++i)
|
||||
{
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisY(), 2.0f * JPH_PI * (i + 0.5f) / cGear2NumTeeth);
|
||||
gear2_settings.AddShape(rotation * Vec3(cGear2Radius, 0, 0), rotation, &tooth_settings);
|
||||
}
|
||||
|
||||
RVec3 gear2_initial_p = gear1_initial_p + Vec3(cGear1Radius + cGear2Radius + cToothHeight, 0, 0);
|
||||
Quat gear2_initial_r = gear1_initial_r;
|
||||
Body *gear2 = mBodyInterface->CreateBody(BodyCreationSettings(&gear2_settings, gear2_initial_p, gear2_initial_r, EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(gear2->GetID(), EActivation::Activate);
|
||||
|
||||
// Create a hinge for gear 1
|
||||
HingeConstraintSettings hinge1;
|
||||
hinge1.mPoint1 = hinge1.mPoint2 = gear1_initial_p;
|
||||
hinge1.mHingeAxis1 = hinge1.mHingeAxis2 = Vec3::sAxisZ();
|
||||
hinge1.mNormalAxis1 = hinge1.mNormalAxis2 = Vec3::sAxisX();
|
||||
Constraint *hinge1_constraint = hinge1.Create(Body::sFixedToWorld, *gear1);
|
||||
mPhysicsSystem->AddConstraint(hinge1_constraint);
|
||||
|
||||
// Create a hinge for gear 1
|
||||
HingeConstraintSettings hinge2;
|
||||
hinge2.mPoint1 = hinge2.mPoint2 = gear2_initial_p;
|
||||
hinge2.mHingeAxis1 = hinge2.mHingeAxis2 = Vec3::sAxisZ();
|
||||
hinge2.mNormalAxis1 = hinge2.mNormalAxis2 = Vec3::sAxisX();
|
||||
Constraint *hinge2_constraint = hinge2.Create(Body::sFixedToWorld, *gear2);
|
||||
mPhysicsSystem->AddConstraint(hinge2_constraint);
|
||||
|
||||
// Disable collision between gears
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(2);
|
||||
group_filter->DisableCollision(0, 1);
|
||||
gear1->SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
gear2->SetCollisionGroup(CollisionGroup(group_filter, 0, 1));
|
||||
|
||||
// Create gear constraint to constrain the two bodies
|
||||
GearConstraintSettings gear;
|
||||
gear.mHingeAxis1 = hinge1.mHingeAxis1;
|
||||
gear.mHingeAxis2 = hinge2.mHingeAxis1;
|
||||
gear.SetRatio(cGear1NumTeeth, cGear2NumTeeth);
|
||||
GearConstraint *gear_constraint = static_cast<GearConstraint *>(gear.Create(*gear1, *gear2));
|
||||
gear_constraint->SetConstraints(hinge1_constraint, hinge2_constraint);
|
||||
mPhysicsSystem->AddConstraint(gear_constraint);
|
||||
|
||||
// Give the gear a spin
|
||||
gear2->SetAngularVelocity(Vec3(0, 0, 3.0f));
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
// This test demonstrates the use of a gear constraint
|
||||
class GearConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, GearConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/HingeConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/HingeConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(HingeConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(HingeConstraintTest, Test)
|
||||
}
|
||||
|
||||
void HingeConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
float box_size = 4.0f;
|
||||
RefConst<Shape> box = new BoxShape(Vec3::sReplicate(0.5f * box_size));
|
||||
|
||||
constexpr int cChainLength = 15;
|
||||
constexpr float cMinAngle = DegreesToRadians(-10.0f);
|
||||
constexpr float cMaxAngle = DegreesToRadians(20.0f);
|
||||
|
||||
// Build a collision group filter that disables collision between adjacent bodies
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(cChainLength);
|
||||
for (CollisionGroup::SubGroupID i = 0; i < cChainLength - 1; ++i)
|
||||
group_filter->DisableCollision(i, i + 1);
|
||||
|
||||
// Bodies attached through hinge constraints
|
||||
for (int randomness = 0; randomness < 2; ++randomness)
|
||||
{
|
||||
CollisionGroup::GroupID group_id = CollisionGroup::GroupID(randomness);
|
||||
|
||||
RVec3 position(0, 50, -randomness * 20.0f);
|
||||
Body &top = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
top.SetCollisionGroup(CollisionGroup(group_filter, group_id, 0));
|
||||
mBodyInterface->AddBody(top.GetID(), EActivation::DontActivate);
|
||||
|
||||
default_random_engine random;
|
||||
uniform_real_distribution<float> displacement(-1.0f, 1.0f);
|
||||
|
||||
Body *prev = ⊤
|
||||
for (int i = 1; i < cChainLength; ++i)
|
||||
{
|
||||
Quat rotation;
|
||||
if (randomness == 0)
|
||||
{
|
||||
position += Vec3(box_size, 0, 0);
|
||||
rotation = Quat::sIdentity();
|
||||
}
|
||||
else
|
||||
{
|
||||
position += Vec3(box_size + abs(displacement(random)), displacement(random), displacement(random));
|
||||
rotation = Quat::sRandom(random);
|
||||
}
|
||||
|
||||
Body &segment = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, rotation, EMotionType::Dynamic, Layers::MOVING));
|
||||
segment.SetCollisionGroup(CollisionGroup(group_filter, group_id, CollisionGroup::SubGroupID(i)));
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
|
||||
HingeConstraintSettings settings;
|
||||
if ((i & 1) == 0)
|
||||
{
|
||||
settings.mPoint1 = settings.mPoint2 = position + Vec3(-0.5f * box_size, 0, 0.5f * box_size);
|
||||
settings.mHingeAxis1 = settings.mHingeAxis2 = Vec3::sAxisY();
|
||||
settings.mNormalAxis1 = settings.mNormalAxis2 = Vec3::sAxisX();
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.mPoint1 = settings.mPoint2 = position + Vec3(-0.5f * box_size, -0.5f * box_size, 0);
|
||||
settings.mHingeAxis1 = settings.mHingeAxis2 = Vec3::sAxisZ();
|
||||
settings.mNormalAxis1 = settings.mNormalAxis2 = Vec3::sAxisX();
|
||||
}
|
||||
settings.mLimitsMin = cMinAngle;
|
||||
settings.mLimitsMax = cMaxAngle;
|
||||
mPhysicsSystem->AddConstraint(settings.Create(*prev, segment));
|
||||
|
||||
prev = &segment;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Two bodies connected with a hard hinge
|
||||
Body *body1 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sOne()), RVec3(4, 5, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
body1->SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(body1->GetID(), EActivation::DontActivate);
|
||||
Body *body2 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sOne()), RVec3(6, 5, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body2->SetCollisionGroup(CollisionGroup(group_filter, 0, 1));
|
||||
mBodyInterface->AddBody(body2->GetID(), EActivation::Activate);
|
||||
|
||||
HingeConstraintSettings hinge;
|
||||
hinge.mPoint1 = hinge.mPoint2 = RVec3(5, 4, 0);
|
||||
hinge.mHingeAxis1 = hinge.mHingeAxis2 = Vec3::sAxisZ();
|
||||
hinge.mNormalAxis1 = hinge.mNormalAxis2 = Vec3::sAxisY();
|
||||
hinge.mLimitsMin = DegreesToRadians(-10.0f);
|
||||
hinge.mLimitsMax = DegreesToRadians(110.0f);
|
||||
mPhysicsSystem->AddConstraint(hinge.Create(*body1, *body2));
|
||||
}
|
||||
|
||||
{
|
||||
// Two bodies connected with a soft hinge
|
||||
Body *body1 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sOne()), RVec3(10, 5, 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
body1->SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(body1->GetID(), EActivation::DontActivate);
|
||||
Body *body2 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sOne()), RVec3(12, 5, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body2->SetCollisionGroup(CollisionGroup(group_filter, 0, 1));
|
||||
mBodyInterface->AddBody(body2->GetID(), EActivation::Activate);
|
||||
|
||||
HingeConstraintSettings hinge;
|
||||
hinge.mPoint1 = hinge.mPoint2 = RVec3(11, 4, 0);
|
||||
hinge.mHingeAxis1 = hinge.mHingeAxis2 = Vec3::sAxisZ();
|
||||
hinge.mNormalAxis1 = hinge.mNormalAxis2 = Vec3::sAxisY();
|
||||
hinge.mLimitsMin = DegreesToRadians(-10.0f);
|
||||
hinge.mLimitsMax = DegreesToRadians(110.0f);
|
||||
hinge.mLimitsSpringSettings.mFrequency = 1.0f;
|
||||
hinge.mLimitsSpringSettings.mDamping = 0.5f;
|
||||
mPhysicsSystem->AddConstraint(hinge.Create(*body1, *body2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class HingeConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, HingeConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/PathConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Constraints/PathConstraintPathHermite.h>
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(PathConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(PathConstraintTest, Test)
|
||||
}
|
||||
|
||||
EPathRotationConstraintType PathConstraintTest::sOrientationType = EPathRotationConstraintType::Free;
|
||||
|
||||
void PathConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
{
|
||||
// Create spiral path
|
||||
Ref<PathConstraintPathHermite> path = new PathConstraintPathHermite;
|
||||
Vec3 normal(0, 1, 0);
|
||||
Array<Vec3> positions;
|
||||
for (float a = -0.1f * JPH_PI; a < 4.0f * JPH_PI; a += 0.1f * JPH_PI)
|
||||
positions.push_back(Vec3(5.0f * Cos(a), -a, 5.0f * Sin(a)));
|
||||
for (int i = 1; i < int(positions.size() - 1); ++i)
|
||||
{
|
||||
Vec3 tangent = 0.5f * (positions[i + 1] - positions[i - 1]);
|
||||
path->AddPoint(positions[i], tangent, normal);
|
||||
}
|
||||
mPaths[0] = path;
|
||||
|
||||
// Dynamic base plate to which the path attaches
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(5, 0.5f, 5)), RVec3(-10, 1, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::Activate);
|
||||
|
||||
// Dynamic body attached to the path
|
||||
Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)), RVec3(-5, 15, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body2.SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
|
||||
|
||||
// Create path constraint
|
||||
PathConstraintSettings settings;
|
||||
settings.mPath = path;
|
||||
settings.mPathPosition = Vec3(0, 15, 0);
|
||||
settings.mRotationConstraintType = sOrientationType;
|
||||
mConstraints[0] = static_cast<PathConstraint *>(settings.Create(body1, body2));
|
||||
mPhysicsSystem->AddConstraint(mConstraints[0]);
|
||||
}
|
||||
|
||||
{
|
||||
// Create circular path
|
||||
Ref<PathConstraintPathHermite> path = new PathConstraintPathHermite;
|
||||
path->SetIsLooping(true);
|
||||
Vec3 normal(0, 1, 0);
|
||||
Array<Vec3> positions;
|
||||
for (int i = -1; i < 11; ++i)
|
||||
{
|
||||
float a = 2.0f * JPH_PI * i / 10.0f;
|
||||
positions.push_back(Vec3(5.0f * Cos(a), 0.0f, 5.0f * Sin(a)));
|
||||
}
|
||||
for (int i = 1; i < int(positions.size() - 1); ++i)
|
||||
{
|
||||
Vec3 tangent = 0.5f * (positions[i + 1] - positions[i - 1]);
|
||||
path->AddPoint(positions[i], tangent, normal);
|
||||
}
|
||||
mPaths[1] = path;
|
||||
|
||||
// Dynamic base plate to which the path attaches
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(5, 0.5f, 5)), RVec3(10, 1, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::Activate);
|
||||
|
||||
// Dynamic body attached to the path
|
||||
Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)), RVec3(15, 5, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body2.SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
|
||||
|
||||
// Create path constraint
|
||||
PathConstraintSettings settings;
|
||||
settings.mPath = path;
|
||||
settings.mPathPosition = Vec3(0, 5, 0);
|
||||
settings.mPathRotation = Quat::sRotation(Vec3::sAxisX(), -0.1f * JPH_PI);
|
||||
settings.mRotationConstraintType = sOrientationType;
|
||||
mConstraints[1] = static_cast<PathConstraint *>(settings.Create(body1, body2));
|
||||
mPhysicsSystem->AddConstraint(mConstraints[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PathConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
for (PathConstraint *c : mConstraints)
|
||||
{
|
||||
MotorSettings &motor_settings = c->GetPositionMotorSettings();
|
||||
motor_settings.SetForceLimit(sMaxMotorAcceleration / c->GetBody2()->GetMotionProperties()->GetInverseMass()); // F = m * a
|
||||
motor_settings.mSpringSettings.mFrequency = sFrequency;
|
||||
motor_settings.mSpringSettings.mDamping = sDamping;
|
||||
c->SetMaxFrictionForce(sMaxFrictionAcceleration / c->GetBody2()->GetMotionProperties()->GetInverseMass());
|
||||
}
|
||||
}
|
||||
|
||||
void PathConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
|
||||
{
|
||||
static Array<String> constraint_types = { "Free", "Tangent", "Normal", "Binormal", "Path", "Full" };
|
||||
|
||||
inUI->CreateTextButton(inSubMenu, "Configuration Settings", [this, inUI]() {
|
||||
UIElement *configuration_settings = inUI->CreateMenu();
|
||||
inUI->CreateComboBox(configuration_settings, "Rotation Constraint", constraint_types, (int)sOrientationType, [=](int inItem) { sOrientationType = (EPathRotationConstraintType)inItem; });
|
||||
inUI->CreateTextButton(configuration_settings, "Accept Changes", [this]() { RestartTest(); });
|
||||
inUI->ShowMenu(configuration_settings);
|
||||
});
|
||||
|
||||
inUI->CreateTextButton(inSubMenu, "Runtime Settings", [this, inUI]() {
|
||||
UIElement *runtime_settings = inUI->CreateMenu();
|
||||
inUI->CreateComboBox(runtime_settings, "Motor", { "Off", "Velocity", "Position" }, (int)mConstraints[0]->GetPositionMotorState(), [this](int inItem) { for (PathConstraint *c : mConstraints) c->SetPositionMotorState((EMotorState)inItem); });
|
||||
inUI->CreateSlider(runtime_settings, "Target Velocity (m/s)", mConstraints[0]->GetTargetVelocity(), -10.0f, 10.0f, 0.1f, [this](float inValue) { for (PathConstraint *c : mConstraints) c->SetTargetVelocity(inValue); });
|
||||
inUI->CreateSlider(runtime_settings, "Target Path Fraction", mConstraints[0]->GetTargetPathFraction(), 0, mPaths[0]->GetPathMaxFraction(), 0.1f, [this](float inValue) { for (PathConstraint *c : mConstraints) c->SetTargetPathFraction(inValue); });
|
||||
inUI->CreateSlider(runtime_settings, "Max Acceleration (m/s^2)", sMaxMotorAcceleration, 0.0f, 100.0f, 1.0f, [](float inValue) { sMaxMotorAcceleration = inValue; });
|
||||
inUI->CreateSlider(runtime_settings, "Frequency (Hz)", sFrequency, 0.0f, 20.0f, 0.1f, [](float inValue) { sFrequency = inValue; });
|
||||
inUI->CreateSlider(runtime_settings, "Damping", sDamping, 0.0f, 2.0f, 0.01f, [](float inValue) { sDamping = inValue; });
|
||||
inUI->CreateSlider(runtime_settings, "Max Friction Acceleration (m/s^2)", sMaxFrictionAcceleration, 0.0f, 10.0f, 0.1f, [](float inValue) { sMaxFrictionAcceleration = inValue; });
|
||||
inUI->ShowMenu(runtime_settings);
|
||||
});
|
||||
|
||||
inUI->ShowMenu(inSubMenu);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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/Constraints/PathConstraint.h>
|
||||
|
||||
class PathConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, PathConstraintTest)
|
||||
|
||||
virtual void Initialize() override;
|
||||
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
virtual bool HasSettingsMenu() const override { return true; }
|
||||
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
|
||||
|
||||
private:
|
||||
Ref<PathConstraintPath> mPaths[2];
|
||||
Ref<PathConstraint> mConstraints[2];
|
||||
|
||||
inline static float sMaxMotorAcceleration = 20.0f;
|
||||
inline static float sMaxFrictionAcceleration = 0.0f;
|
||||
inline static float sFrequency = 2.0f;
|
||||
inline static float sDamping = 1.0f;
|
||||
static EPathRotationConstraintType sOrientationType; ///< The orientation constraint type for the path constraint
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/PointConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/PointConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(PointConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(PointConstraintTest, Test)
|
||||
}
|
||||
|
||||
void PointConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
float half_cylinder_height = 2.5f;
|
||||
|
||||
const int cChainLength = 15;
|
||||
|
||||
// Build a collision group filter that disables collision between adjacent bodies
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(cChainLength);
|
||||
for (CollisionGroup::SubGroupID i = 0; i < cChainLength - 1; ++i)
|
||||
group_filter->DisableCollision(i, i + 1);
|
||||
|
||||
// Bodies attached through point constraints
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI);
|
||||
RVec3 position(0, 50, 0);
|
||||
Body &top = *mBodyInterface->CreateBody(BodyCreationSettings(new CapsuleShape(half_cylinder_height, 1), position, rotation, EMotionType::Static, Layers::NON_MOVING));
|
||||
top.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(top.GetID(), EActivation::DontActivate);
|
||||
|
||||
Body *prev = ⊤
|
||||
for (int i = 1; i < cChainLength; ++i)
|
||||
{
|
||||
position += Vec3(2.0f * half_cylinder_height, 0, 0);
|
||||
|
||||
Body &segment = *mBodyInterface->CreateBody(BodyCreationSettings(new CapsuleShape(half_cylinder_height, 1), position, rotation, EMotionType::Dynamic, Layers::MOVING));
|
||||
segment.SetCollisionGroup(CollisionGroup(group_filter, 0, CollisionGroup::SubGroupID(i)));
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
|
||||
PointConstraintSettings settings;
|
||||
settings.mPoint1 = settings.mPoint2 = position + Vec3(-half_cylinder_height, 0, 0);
|
||||
mPhysicsSystem->AddConstraint(settings.Create(*prev, segment));
|
||||
|
||||
prev = &segment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class PointConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, PointConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/PoweredHingeConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(PoweredHingeConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(PoweredHingeConstraintTest, Test)
|
||||
}
|
||||
|
||||
void PoweredHingeConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Create group filter
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable;
|
||||
|
||||
// Create box
|
||||
float box_size = 4.0f;
|
||||
RefConst<Shape> box = new BoxShape(Vec3::sReplicate(0.5f * box_size));
|
||||
|
||||
RVec3 body1_position(0, 10, 0);
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box, body1_position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
body1.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::DontActivate);
|
||||
|
||||
RVec3 body2_position = body1_position + Vec3(box_size, 0, 0);
|
||||
Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(box, body2_position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body2.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
body2.GetMotionProperties()->SetLinearDamping(0.0f);
|
||||
body2.GetMotionProperties()->SetAngularDamping(0.0f);
|
||||
body2.SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
|
||||
|
||||
RVec3 constraint_position = body1_position + Vec3(0.5f * box_size, 0, 0.5f * box_size);
|
||||
|
||||
HingeConstraintSettings settings;
|
||||
settings.mPoint1 = settings.mPoint2 = constraint_position;
|
||||
settings.mHingeAxis1 = settings.mHingeAxis2 = Vec3::sAxisY();
|
||||
settings.mNormalAxis1 = settings.mNormalAxis2 = Vec3::sAxisX();
|
||||
mConstraint = static_cast<HingeConstraint *>(settings.Create(body1, body2));
|
||||
mConstraint->SetMotorState(EMotorState::Velocity);
|
||||
mConstraint->SetTargetAngularVelocity(DegreesToRadians(25));
|
||||
mPhysicsSystem->AddConstraint(mConstraint);
|
||||
|
||||
// Calculate inertia of body 2 as seen from the constraint
|
||||
MassProperties body2_inertia_from_constraint;
|
||||
body2_inertia_from_constraint.mMass = 1.0f / body2.GetMotionProperties()->GetInverseMass();
|
||||
body2_inertia_from_constraint.mInertia = body2.GetMotionProperties()->GetLocalSpaceInverseInertia().Inversed3x3();
|
||||
body2_inertia_from_constraint.Translate(Vec3(body2_position - constraint_position));
|
||||
mInertiaBody2AsSeenFromConstraint = (body2_inertia_from_constraint.mInertia * Vec3::sAxisY()).Length();
|
||||
}
|
||||
|
||||
void PoweredHingeConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
// Torque = Inertia * Angular Acceleration (alpha)
|
||||
MotorSettings &motor_settings = mConstraint->GetMotorSettings();
|
||||
motor_settings.SetTorqueLimit(mInertiaBody2AsSeenFromConstraint * sMaxAngularAcceleration);
|
||||
motor_settings.mSpringSettings.mFrequency = sFrequency;
|
||||
motor_settings.mSpringSettings.mDamping = sDamping;
|
||||
mConstraint->SetMaxFrictionTorque(mInertiaBody2AsSeenFromConstraint * sMaxFrictionAngularAcceleration);
|
||||
}
|
||||
|
||||
void PoweredHingeConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
|
||||
{
|
||||
inUI->CreateComboBox(inSubMenu, "Motor", { "Off", "Velocity", "Position" }, (int)mConstraint->GetMotorState(), [this](int inItem) { mConstraint->SetMotorState((EMotorState)inItem); });
|
||||
inUI->CreateSlider(inSubMenu, "Target Angular Velocity (deg/s)", RadiansToDegrees(mConstraint->GetTargetAngularVelocity()), -90.0f, 90.0f, 1.0f, [this](float inValue) { mConstraint->SetTargetAngularVelocity(DegreesToRadians(inValue)); });
|
||||
inUI->CreateSlider(inSubMenu, "Target Angle (deg)", RadiansToDegrees(mConstraint->GetTargetAngle()), -180.0f, 180.0f, 1.0f, [this](float inValue) { mConstraint->SetTargetAngle(DegreesToRadians(inValue)); });
|
||||
inUI->CreateSlider(inSubMenu, "Max Angular Acceleration (deg/s^2)", RadiansToDegrees(sMaxAngularAcceleration), 0.0f, 3600.0f, 10.0f, [](float inValue) { sMaxAngularAcceleration = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(inSubMenu, "Frequency (Hz)", sFrequency, 0.0f, 20.0f, 0.1f, [](float inValue) { sFrequency = inValue; });
|
||||
inUI->CreateSlider(inSubMenu, "Damping", sDamping, 0.0f, 2.0f, 0.01f, [](float inValue) { sDamping = inValue; });
|
||||
inUI->CreateSlider(inSubMenu, "Max Friction Angular Acceleration (deg/s^2)", RadiansToDegrees(sMaxFrictionAngularAcceleration), 0.0f, 90.0f, 1.0f, [](float inValue) { sMaxFrictionAngularAcceleration = DegreesToRadians(inValue); });
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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/Constraints/HingeConstraint.h>
|
||||
|
||||
class PoweredHingeConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, PoweredHingeConstraintTest)
|
||||
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
virtual bool HasSettingsMenu() const override { return true; }
|
||||
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
|
||||
|
||||
private:
|
||||
inline static float sMaxAngularAcceleration = DegreesToRadians(3600.0f);
|
||||
inline static float sMaxFrictionAngularAcceleration = 0.0f;
|
||||
inline static float sFrequency = 2.0f;
|
||||
inline static float sDamping = 1.0f;
|
||||
|
||||
HingeConstraint * mConstraint = nullptr;
|
||||
float mInertiaBody2AsSeenFromConstraint;
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/PoweredSliderConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/SliderConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(PoweredSliderConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(PoweredSliderConstraintTest, Test)
|
||||
}
|
||||
|
||||
void PoweredSliderConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Create group filter
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable;
|
||||
|
||||
// Create box
|
||||
float box_size = 4.0f;
|
||||
RefConst<Shape> box = new BoxShape(Vec3::sReplicate(0.5f * box_size));
|
||||
|
||||
RVec3 position(0, 10, 0);
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
body1.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::DontActivate);
|
||||
|
||||
position += Vec3(box_size + 10.0f, 0, 0);
|
||||
|
||||
mBody2 = mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBody2->SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBody2->GetMotionProperties()->SetLinearDamping(0.0f);
|
||||
mBody2->SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(mBody2->GetID(), EActivation::Activate);
|
||||
|
||||
SliderConstraintSettings settings;
|
||||
settings.mAutoDetectPoint = true;
|
||||
settings.SetSliderAxis(Vec3::sAxisX());
|
||||
settings.mLimitsMin = -5.0f;
|
||||
settings.mLimitsMax = 100.0f;
|
||||
mConstraint = static_cast<SliderConstraint *>(settings.Create(body1, *mBody2));
|
||||
mConstraint->SetMotorState(EMotorState::Velocity);
|
||||
mConstraint->SetTargetVelocity(1);
|
||||
mPhysicsSystem->AddConstraint(mConstraint);
|
||||
}
|
||||
|
||||
void PoweredSliderConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
MotorSettings &motor_settings = mConstraint->GetMotorSettings();
|
||||
motor_settings.SetForceLimit(sMaxMotorAcceleration / mBody2->GetMotionProperties()->GetInverseMass()); // F = m * a
|
||||
motor_settings.mSpringSettings.mFrequency = sFrequency;
|
||||
motor_settings.mSpringSettings.mDamping = sDamping;
|
||||
mConstraint->SetMaxFrictionForce(sMaxFrictionAcceleration / mBody2->GetMotionProperties()->GetInverseMass());
|
||||
}
|
||||
|
||||
void PoweredSliderConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
|
||||
{
|
||||
inUI->CreateComboBox(inSubMenu, "Motor", { "Off", "Velocity", "Position" }, (int)mConstraint->GetMotorState(), [this](int inItem) { mConstraint->SetMotorState((EMotorState)inItem); });
|
||||
inUI->CreateSlider(inSubMenu, "Target Velocity (m/s)", mConstraint->GetTargetVelocity(), -10.0f, 10.0f, 0.1f, [this](float inValue) { mConstraint->SetTargetVelocity(inValue); });
|
||||
inUI->CreateSlider(inSubMenu, "Target Position (m)", mConstraint->GetTargetPosition(), -5.0f, 20.0f, 0.1f, [this](float inValue) { mConstraint->SetTargetPosition(inValue); });
|
||||
inUI->CreateSlider(inSubMenu, "Max Acceleration (m/s^2)", sMaxMotorAcceleration, 0.0f, 250.0f, 1.0f, [](float inValue) { sMaxMotorAcceleration = inValue; });
|
||||
inUI->CreateSlider(inSubMenu, "Frequency (Hz)", sFrequency, 0.0f, 20.0f, 0.1f, [](float inValue) { sFrequency = inValue; });
|
||||
inUI->CreateSlider(inSubMenu, "Damping", sDamping, 0.0f, 2.0f, 0.01f, [](float inValue) { sDamping = inValue; });
|
||||
inUI->CreateSlider(inSubMenu, "Max Friction Acceleration (m/s^2)", sMaxFrictionAcceleration, 0.0f, 10.0f, 0.1f, [](float inValue) { sMaxFrictionAcceleration = inValue; });
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
#include <Jolt/Physics/Constraints/SliderConstraint.h>
|
||||
|
||||
class PoweredSliderConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, PoweredSliderConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
virtual bool HasSettingsMenu() const override { return true; }
|
||||
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
|
||||
|
||||
private:
|
||||
inline static float sMaxMotorAcceleration = 250.0f;
|
||||
inline static float sMaxFrictionAcceleration = 0.0f;
|
||||
inline static float sFrequency = 2.0f;
|
||||
inline static float sDamping = 1.0f;
|
||||
|
||||
Body * mBody2 = nullptr;
|
||||
SliderConstraint * mConstraint = nullptr;
|
||||
};
|
||||
@@ -0,0 +1,142 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/PoweredSwingTwistConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(PoweredSwingTwistConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(PoweredSwingTwistConstraintTest, Test)
|
||||
}
|
||||
|
||||
Vec3 PoweredSwingTwistConstraintTest::sBodyRotation[] = { Vec3::sZero(), Vec3::sZero() };
|
||||
|
||||
void PoweredSwingTwistConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Create group filter
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable;
|
||||
|
||||
float half_box_height = 1.5f;
|
||||
RefConst<Shape> box = new BoxShape(Vec3(0.25f, half_box_height, 0.5f));
|
||||
Quat body1_rotation = Quat::sEulerAngles(sBodyRotation[0]);
|
||||
Quat body2_rotation = Quat::sEulerAngles(sBodyRotation[1]) * body1_rotation;
|
||||
|
||||
RVec3 body1_position(0, 20, 0);
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box, body1_position, body1_rotation, EMotionType::Static, Layers::NON_MOVING));
|
||||
body1.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::DontActivate);
|
||||
|
||||
RVec3 constraint_position = body1_position + body1_rotation * Vec3(0, -half_box_height, 0);
|
||||
|
||||
RVec3 body2_position = constraint_position + body2_rotation * Vec3(0, -half_box_height, 0);
|
||||
Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(box, body2_position, body2_rotation, EMotionType::Dynamic, Layers::MOVING));
|
||||
body2.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
body2.GetMotionProperties()->SetLinearDamping(0.0f);
|
||||
body2.GetMotionProperties()->SetAngularDamping(0.0f);
|
||||
body2.SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
|
||||
|
||||
Ref<SwingTwistConstraintSettings> settings = new SwingTwistConstraintSettings;
|
||||
settings->mNormalHalfConeAngle = sNormalHalfConeAngle;
|
||||
settings->mPlaneHalfConeAngle = sPlaneHalfConeAngle;
|
||||
settings->mTwistMinAngle = sTwistMinAngle;
|
||||
settings->mTwistMaxAngle = sTwistMaxAngle;
|
||||
|
||||
settings->mPosition1 = settings->mPosition2 = constraint_position;
|
||||
settings->mTwistAxis1 = settings->mTwistAxis2 = -body1_rotation.RotateAxisY();
|
||||
settings->mPlaneAxis1 = settings->mPlaneAxis2 = body1_rotation.RotateAxisX();
|
||||
|
||||
mConstraint = static_cast<SwingTwistConstraint *>(settings->Create(body1, body2));
|
||||
mPhysicsSystem->AddConstraint(mConstraint);
|
||||
|
||||
// Calculate inertia along the axis of the box, so that the acceleration of the motor / friction are correct for twist
|
||||
Mat44 body2_inertia = body2.GetMotionProperties()->GetLocalSpaceInverseInertia().Inversed3x3();
|
||||
mInertiaBody2AsSeenFromConstraint = (body2_inertia * Vec3::sAxisY()).Length();
|
||||
}
|
||||
|
||||
void PoweredSwingTwistConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
// Torque = Inertia * Angular Acceleration (alpha)
|
||||
mConstraint->SetMaxFrictionTorque(mInertiaBody2AsSeenFromConstraint * sMaxFrictionAngularAcceleration);
|
||||
|
||||
mConstraint->SetNormalHalfConeAngle(sNormalHalfConeAngle);
|
||||
mConstraint->SetPlaneHalfConeAngle(sPlaneHalfConeAngle);
|
||||
mConstraint->SetTwistMinAngle(sTwistMinAngle);
|
||||
mConstraint->SetTwistMaxAngle(sTwistMaxAngle);
|
||||
|
||||
mConstraint->SetSwingMotorState(sSwingMotorState);
|
||||
mConstraint->SetTwistMotorState(sTwistMotorState);
|
||||
mConstraint->SetTargetAngularVelocityCS(sTargetVelocityCS);
|
||||
mConstraint->SetTargetOrientationCS(Quat::sEulerAngles(sTargetOrientationCS));
|
||||
|
||||
MotorSettings &swing = mConstraint->GetSwingMotorSettings();
|
||||
swing.SetTorqueLimit(mInertiaBody2AsSeenFromConstraint * sMaxAngularAcceleration);
|
||||
swing.mSpringSettings.mFrequency = sFrequency;
|
||||
swing.mSpringSettings.mDamping = sDamping;
|
||||
|
||||
MotorSettings &twist = mConstraint->GetTwistMotorSettings();
|
||||
twist.SetTorqueLimit(mInertiaBody2AsSeenFromConstraint * sMaxAngularAcceleration);
|
||||
twist.mSpringSettings.mFrequency = sFrequency;
|
||||
twist.mSpringSettings.mDamping = sDamping;
|
||||
}
|
||||
|
||||
void PoweredSwingTwistConstraintTest::GetInitialCamera(CameraState &ioState) const
|
||||
{
|
||||
ioState.mPos = RVec3(4, 25, 4);
|
||||
ioState.mForward = Vec3(-1, -1, -1).Normalized();
|
||||
}
|
||||
|
||||
void PoweredSwingTwistConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
|
||||
{
|
||||
Array<String> axis_label = { "X", "Y", "Z" };
|
||||
Array<String> constraint_label = { "Twist", "Plane", "Normal" };
|
||||
|
||||
inUI->CreateTextButton(inSubMenu, "Configuration Settings", [this, inUI, axis_label]() {
|
||||
UIElement *configuration_settings = inUI->CreateMenu();
|
||||
|
||||
for (int body = 0; body < 2; ++body)
|
||||
for (int axis = 0; axis < 3; ++axis)
|
||||
inUI->CreateSlider(configuration_settings, "Body " + ConvertToString(body + 1) + " Rotation " + axis_label[axis] + " (deg)", RadiansToDegrees(sBodyRotation[body][axis]), -180.0f, 180.0f, 1.0f, [=](float inValue) { sBodyRotation[body].SetComponent(axis, DegreesToRadians(inValue)); });
|
||||
|
||||
inUI->CreateTextButton(configuration_settings, "Accept Changes", [this]() { RestartTest(); });
|
||||
|
||||
inUI->ShowMenu(configuration_settings);
|
||||
});
|
||||
|
||||
inUI->CreateTextButton(inSubMenu, "Runtime Settings", [=]() {
|
||||
UIElement *runtime_settings = inUI->CreateMenu();
|
||||
|
||||
inUI->CreateSlider(runtime_settings, "Min Twist Angle (deg)", RadiansToDegrees(sTwistMinAngle), -180.0f, 180.0f, 1.0f, [=](float inValue) { sTwistMinAngle = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(runtime_settings, "Max Twist Angle (deg)", RadiansToDegrees(sTwistMaxAngle), -180.0f, 180.0f, 1.0f, [=](float inValue) { sTwistMaxAngle = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(runtime_settings, "Normal Half Cone Angle (deg)", RadiansToDegrees(sNormalHalfConeAngle), 0.0f, 180.0f, 1.0f, [=](float inValue) { sNormalHalfConeAngle = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(runtime_settings, "Plane Half Cone Angle (deg)", RadiansToDegrees(sPlaneHalfConeAngle), 0.0f, 180.0f, 1.0f, [=](float inValue) { sPlaneHalfConeAngle = DegreesToRadians(inValue); });
|
||||
|
||||
inUI->CreateComboBox(runtime_settings, "Swing Motor", { "Off", "Velocity", "Position" }, (int)sSwingMotorState, [=](int inItem) { sSwingMotorState = (EMotorState)inItem; });
|
||||
inUI->CreateComboBox(runtime_settings, "Twist Motor", { "Off", "Velocity", "Position" }, (int)sTwistMotorState, [=](int inItem) { sTwistMotorState = (EMotorState)inItem; });
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
inUI->CreateSlider(runtime_settings, "Target Angular Velocity " + constraint_label[i] + " (deg/s)", RadiansToDegrees(sTargetVelocityCS[i]), -90.0f, 90.0f, 1.0f, [i](float inValue) { sTargetVelocityCS.SetComponent(i, DegreesToRadians(inValue)); });
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
inUI->CreateSlider(runtime_settings, "Target Angle " + constraint_label[i] + " (deg)", RadiansToDegrees(sTargetOrientationCS[i]), -180, 180.0f, 1.0f, [i](float inValue) {
|
||||
sTargetOrientationCS.SetComponent(i, DegreesToRadians(Clamp(inValue, -179.99f, 179.99f))); // +/- 180 degrees is ambiguous, so add a little bit of a margin
|
||||
});
|
||||
|
||||
inUI->CreateSlider(runtime_settings, "Max Angular Acceleration (deg/s^2)", RadiansToDegrees(sMaxAngularAcceleration), 0.0f, 36000.0f, 100.0f, [=](float inValue) { sMaxAngularAcceleration = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(runtime_settings, "Frequency (Hz)", sFrequency, 0.0f, 20.0f, 0.1f, [=](float inValue) { sFrequency = inValue; });
|
||||
inUI->CreateSlider(runtime_settings, "Damping", sDamping, 0.0f, 2.0f, 0.01f, [=](float inValue) { sDamping = inValue; });
|
||||
inUI->CreateSlider(runtime_settings, "Max Friction Angular Acceleration (deg/s^2)", RadiansToDegrees(sMaxFrictionAngularAcceleration), 0.0f, 900.0f, 1.0f, [=](float inValue) { sMaxFrictionAngularAcceleration = DegreesToRadians(inValue); });
|
||||
|
||||
inUI->ShowMenu(runtime_settings);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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/Constraints/SwingTwistConstraint.h>
|
||||
|
||||
class PoweredSwingTwistConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, PoweredSwingTwistConstraintTest)
|
||||
|
||||
virtual void Initialize() override;
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
virtual void GetInitialCamera(CameraState &ioState) const override;
|
||||
|
||||
virtual bool HasSettingsMenu() const override { return true; }
|
||||
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
|
||||
|
||||
private:
|
||||
static Vec3 sBodyRotation[2];
|
||||
inline static EMotorState sSwingMotorState = EMotorState::Velocity;
|
||||
inline static EMotorState sTwistMotorState = EMotorState::Velocity;
|
||||
inline static Vec3 sTargetVelocityCS = Vec3(DegreesToRadians(90), 0, 0);
|
||||
inline static Vec3 sTargetOrientationCS = Vec3::sZero();
|
||||
inline static float sMaxAngularAcceleration = DegreesToRadians(36000.0f);
|
||||
inline static float sMaxFrictionAngularAcceleration = 0.0f;
|
||||
inline static float sNormalHalfConeAngle = DegreesToRadians(60);
|
||||
inline static float sPlaneHalfConeAngle = DegreesToRadians(45);
|
||||
inline static float sTwistMinAngle = DegreesToRadians(-180);
|
||||
inline static float sTwistMaxAngle = DegreesToRadians(180);
|
||||
inline static float sFrequency = 10.0f;
|
||||
inline static float sDamping = 2.0f;
|
||||
|
||||
SwingTwistConstraint * mConstraint = nullptr;
|
||||
float mInertiaBody2AsSeenFromConstraint;
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2022 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/PulleyConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Constraints/PulleyConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(PulleyConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(PulleyConstraintTest, Test)
|
||||
}
|
||||
|
||||
void PulleyConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Variation 0: Max length (rope)
|
||||
// Variation 1: Fixed length (rigid rod)
|
||||
// Variation 2: Min/max length
|
||||
// Variation 3: With ratio (block and tackle)
|
||||
for (int variation = 0; variation < 4; ++variation)
|
||||
{
|
||||
RVec3 position1(-10, 10, -10.0f * variation);
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.5f)), position1, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::Activate);
|
||||
|
||||
RVec3 position2(10, 10, -10.0f * variation);
|
||||
Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.5f)), position2, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
|
||||
|
||||
PulleyConstraintSettings settings;
|
||||
settings.mBodyPoint1 = position1 + Vec3(0, 0.5f, 0); // Connect at the top of the block
|
||||
settings.mBodyPoint2 = position2 + Vec3(0, 0.5f, 0);
|
||||
settings.mFixedPoint1 = settings.mBodyPoint1 + Vec3(0, 10, 0);
|
||||
settings.mFixedPoint2 = settings.mBodyPoint2 + Vec3(0, 10, 0);
|
||||
|
||||
switch (variation)
|
||||
{
|
||||
case 0:
|
||||
// Can't extend but can contract
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Fixed size
|
||||
settings.mMinLength = settings.mMaxLength = -1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// With range
|
||||
settings.mMinLength = 18.0f;
|
||||
settings.mMaxLength = 22.0f;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// With ratio
|
||||
settings.mRatio = 4.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
mPhysicsSystem->AddConstraint(settings.Create(body1, body2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2022 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
// Demonstrates the pulley constraint
|
||||
class PulleyConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, PulleyConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/RackAndPinionConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/HingeConstraint.h>
|
||||
#include <Jolt/Physics/Constraints/SliderConstraint.h>
|
||||
#include <Jolt/Physics/Constraints/RackAndPinionConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(RackAndPinionConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(RackAndPinionConstraintTest, Test)
|
||||
}
|
||||
|
||||
void RackAndPinionConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
constexpr float cGearRadius = 0.5f;
|
||||
constexpr float cGearHalfWidth = 0.05f;
|
||||
constexpr int cGearNumTeeth = 100;
|
||||
|
||||
constexpr float cRackLength = 10.0f;
|
||||
constexpr float cRackHalfHeight = 0.1f;
|
||||
constexpr float cRackHalfWidth = 0.05f;
|
||||
constexpr int cRackNumTeeth = int(cRackLength * cGearNumTeeth / (2.0f * JPH_PI * cGearRadius));
|
||||
|
||||
constexpr float cToothThicknessBottom = 0.01f;
|
||||
constexpr float cToothThicknessTop = 0.005f;
|
||||
constexpr float cToothHeight = 0.02f;
|
||||
|
||||
// Create a tooth
|
||||
Array<Vec3> tooth_points = {
|
||||
Vec3(0, cGearHalfWidth, cToothThicknessBottom),
|
||||
Vec3(0, -cGearHalfWidth, cToothThicknessBottom),
|
||||
Vec3(0, cGearHalfWidth, -cToothThicknessBottom),
|
||||
Vec3(0, -cGearHalfWidth, -cToothThicknessBottom),
|
||||
Vec3(cToothHeight, -cGearHalfWidth, cToothThicknessTop),
|
||||
Vec3(cToothHeight, cGearHalfWidth, cToothThicknessTop),
|
||||
Vec3(cToothHeight, -cGearHalfWidth, -cToothThicknessTop),
|
||||
Vec3(cToothHeight, cGearHalfWidth, -cToothThicknessTop),
|
||||
};
|
||||
ConvexHullShapeSettings tooth_settings(tooth_points);
|
||||
tooth_settings.SetEmbedded();
|
||||
|
||||
// Create gear
|
||||
CylinderShapeSettings gear_cylinder(cGearHalfWidth, cGearRadius);
|
||||
gear_cylinder.SetEmbedded();
|
||||
|
||||
StaticCompoundShapeSettings gear_settings;
|
||||
gear_settings.SetEmbedded();
|
||||
|
||||
gear_settings.AddShape(Vec3::sZero(), Quat::sIdentity(), &gear_cylinder);
|
||||
for (int i = 0; i < cGearNumTeeth; ++i)
|
||||
{
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisY(), 2.0f * JPH_PI * i / cGearNumTeeth);
|
||||
gear_settings.AddShape(rotation * Vec3(cGearRadius, 0, 0), rotation, &tooth_settings);
|
||||
}
|
||||
|
||||
RVec3 gear_initial_p(0, 2.0f, 0);
|
||||
Quat gear_initial_r = Quat::sRotation(Vec3::sAxisX(), 0.5f * JPH_PI);
|
||||
Body *gear = mBodyInterface->CreateBody(BodyCreationSettings(&gear_settings, gear_initial_p, gear_initial_r, EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(gear->GetID(), EActivation::Activate);
|
||||
|
||||
// Create rack
|
||||
BoxShapeSettings rack_box(Vec3(cRackHalfHeight, cRackHalfWidth, 0.5f * cRackLength), 0.0f);
|
||||
rack_box.SetEmbedded();
|
||||
|
||||
StaticCompoundShapeSettings rack_settings;
|
||||
rack_settings.SetEmbedded();
|
||||
|
||||
rack_settings.AddShape(Vec3::sZero(), Quat::sIdentity(), &rack_box);
|
||||
for (int i = 0; i < cRackNumTeeth; ++i)
|
||||
rack_settings.AddShape(Vec3(cRackHalfHeight, 0, -0.5f * cRackLength + (i + 0.5f) * cRackLength / cRackNumTeeth), Quat::sIdentity(), &tooth_settings);
|
||||
|
||||
RVec3 slider_initial_p = gear_initial_p - Vec3(0, cGearRadius + cRackHalfHeight + cToothHeight, 0);
|
||||
Quat slider_initial_r = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI) * gear_initial_r;
|
||||
Body *rack = mBodyInterface->CreateBody(BodyCreationSettings(&rack_settings, slider_initial_p, slider_initial_r, EMotionType::Dynamic, Layers::MOVING));
|
||||
mBodyInterface->AddBody(rack->GetID(), EActivation::Activate);
|
||||
|
||||
// Create a hinge for the gear
|
||||
HingeConstraintSettings hinge;
|
||||
hinge.mPoint1 = hinge.mPoint2 = gear_initial_p;
|
||||
hinge.mHingeAxis1 = hinge.mHingeAxis2 = Vec3::sAxisZ();
|
||||
hinge.mNormalAxis1 = hinge.mNormalAxis2 = Vec3::sAxisX();
|
||||
Constraint *hinge_constraint = hinge.Create(Body::sFixedToWorld, *gear);
|
||||
mPhysicsSystem->AddConstraint(hinge_constraint);
|
||||
|
||||
// Create a slider for the rack
|
||||
SliderConstraintSettings slider;
|
||||
slider.mPoint1 = slider.mPoint2 = gear_initial_p;
|
||||
slider.mSliderAxis1 = slider.mSliderAxis2 = Vec3::sAxisX();
|
||||
slider.mNormalAxis1 = slider.mNormalAxis2 = Vec3::sAxisZ();
|
||||
slider.mLimitsMin = -0.5f * cRackLength;
|
||||
slider.mLimitsMax = 0.5f * cRackLength;
|
||||
Constraint *slider_constraint = slider.Create(Body::sFixedToWorld, *rack);
|
||||
mPhysicsSystem->AddConstraint(slider_constraint);
|
||||
|
||||
// Disable collision between rack and gear (we want the rack and pinion constraint to take care of the relative movement)
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(2);
|
||||
group_filter->DisableCollision(0, 1);
|
||||
gear->SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
rack->SetCollisionGroup(CollisionGroup(group_filter, 0, 1));
|
||||
|
||||
// Create rack and pinion constraint to constrain the two bodies
|
||||
RackAndPinionConstraintSettings randp;
|
||||
randp.mHingeAxis = hinge.mHingeAxis1;
|
||||
randp.mSliderAxis = slider.mSliderAxis2;
|
||||
randp.SetRatio(cRackNumTeeth, cRackLength, cGearNumTeeth);
|
||||
RackAndPinionConstraint *randp_constraint = static_cast<RackAndPinionConstraint *>(randp.Create(*gear, *rack));
|
||||
randp_constraint->SetConstraints(hinge_constraint, slider_constraint);
|
||||
mPhysicsSystem->AddConstraint(randp_constraint);
|
||||
|
||||
// Give the gear a spin
|
||||
gear->SetAngularVelocity(Vec3(0, 0, 6.0f));
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
// This test demonstrates the use of a rack and pinion constraint
|
||||
class RackAndPinionConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, RackAndPinionConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/SixDOFConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(SixDOFConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(SixDOFConstraintTest, Test)
|
||||
}
|
||||
|
||||
float SixDOFConstraintTest::sLimitMin[EAxis::Num] = { 0, 0, 0, 0, 0, 0 };
|
||||
float SixDOFConstraintTest::sLimitMax[EAxis::Num] = { 0, 0, 0, 0, 0, 0 };
|
||||
bool SixDOFConstraintTest::sEnableLimits[EAxis::Num] = { true, true, true, true, true, true };
|
||||
|
||||
SixDOFConstraintTest::SettingsRef SixDOFConstraintTest::sSettings = []() {
|
||||
static SixDOFConstraintSettings settings;
|
||||
settings.SetEmbedded();
|
||||
settings.mAxisX1 = settings.mAxisX2 = -Vec3::sAxisY();
|
||||
settings.mAxisY1 = settings.mAxisY2 = Vec3::sAxisZ();
|
||||
for (int i = 0; i < 6; ++i)
|
||||
settings.mMotorSettings[i] = MotorSettings(10.0f, 2.0f);
|
||||
return &settings;
|
||||
}();
|
||||
|
||||
void SixDOFConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Convert limits to settings class
|
||||
for (int i = 0; i < EAxis::Num; ++i)
|
||||
if (sEnableLimits[i])
|
||||
sSettings->SetLimitedAxis((EAxis)i, sLimitMin[i], sLimitMax[i]);
|
||||
else
|
||||
sSettings->MakeFreeAxis((EAxis)i);
|
||||
|
||||
// Create group filter
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable;
|
||||
|
||||
// Create box
|
||||
float half_box_height = 1.5f;
|
||||
RVec3 position(0, 25, 0);
|
||||
RefConst<BoxShape> box = new BoxShape(Vec3(0.5f, half_box_height, 0.25f));
|
||||
|
||||
// Create static body
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
body1.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::DontActivate);
|
||||
|
||||
// Create dynamic body
|
||||
Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(box, position - Vec3(0, 2.0f * half_box_height, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body2.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
body2.SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
|
||||
|
||||
// Set constraint position
|
||||
sSettings->mPosition1 = sSettings->mPosition2 = position - Vec3(0, half_box_height, 0);
|
||||
|
||||
// Set force limits
|
||||
const float max_acceleration = 1.0f;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
sSettings->mMotorSettings[i].SetForceLimit(max_acceleration / body2.GetMotionProperties()->GetInverseMass());
|
||||
|
||||
// Create constraint
|
||||
mConstraint = static_cast<SixDOFConstraint *>(sSettings->Create(body1, body2));
|
||||
mPhysicsSystem->AddConstraint(mConstraint);
|
||||
}
|
||||
|
||||
void SixDOFConstraintTest::GetInitialCamera(CameraState &ioState) const
|
||||
{
|
||||
ioState.mPos = RVec3(4, 30, 4);
|
||||
ioState.mForward = Vec3(-1, -1, -1).Normalized();
|
||||
}
|
||||
|
||||
void SixDOFConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
|
||||
{
|
||||
Array<String> labels = { "Translation X", "Translation Y", "Translation Z", "Rotation X", "Rotation Y", "Rotation Z" };
|
||||
Array<String> motor_states = { "Off", "Velocity", "Position" };
|
||||
Array<String> swing_types = { "Cone", "Pyramid" };
|
||||
|
||||
inUI->CreateTextButton(inSubMenu, "Configuration Settings (Limits)", [this, inUI, labels, swing_types]() {
|
||||
UIElement *configuration_settings = inUI->CreateMenu();
|
||||
|
||||
inUI->CreateComboBox(configuration_settings, "Swing Type", swing_types, (int)sSettings->mSwingType, [](int inItem) { sSettings->mSwingType = (ESwingType)inItem; });
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
inUI->CreateCheckBox(configuration_settings, "Enable Limits " + labels[i], sEnableLimits[i], [=](UICheckBox::EState inState) { sEnableLimits[i] = inState == UICheckBox::STATE_CHECKED; });
|
||||
inUI->CreateSlider(configuration_settings, "Limit Min", sLimitMin[i], -5.0f, 5.0f, 0.1f, [=](float inValue) { sLimitMin[i] = inValue; });
|
||||
inUI->CreateSlider(configuration_settings, "Limit Max", sLimitMax[i], -5.0f, 5.0f, 0.1f, [=](float inValue) { sLimitMax[i] = inValue; });
|
||||
inUI->CreateSlider(configuration_settings, "Limit Frequency (Hz)", sSettings->mLimitsSpringSettings[i].mFrequency, 0.0f, 20.0f, 0.1f, [=](float inValue) { sSettings->mLimitsSpringSettings[i].mFrequency = inValue; });
|
||||
inUI->CreateSlider(configuration_settings, "Limit Damping", sSettings->mLimitsSpringSettings[i].mDamping, 0.0f, 2.0f, 0.01f, [=](float inValue) { sSettings->mLimitsSpringSettings[i].mDamping = inValue; });
|
||||
}
|
||||
|
||||
for (int i = 3; i < 6; ++i)
|
||||
{
|
||||
inUI->CreateCheckBox(configuration_settings, "Enable Limits " + labels[i], sEnableLimits[i], [=](UICheckBox::EState inState) { sEnableLimits[i] = inState == UICheckBox::STATE_CHECKED; });
|
||||
inUI->CreateSlider(configuration_settings, "Limit Min", RadiansToDegrees(sLimitMin[i]), -180.0f, 180.0f, 1.0f, [=](float inValue) { sLimitMin[i] = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(configuration_settings, "Limit Max", RadiansToDegrees(sLimitMax[i]), -180.0f, 180.0f, 1.0f, [=](float inValue) { sLimitMax[i] = DegreesToRadians(inValue); });
|
||||
}
|
||||
|
||||
inUI->CreateTextButton(configuration_settings, "Accept Changes", [this]() { RestartTest(); });
|
||||
|
||||
inUI->ShowMenu(configuration_settings);
|
||||
});
|
||||
|
||||
inUI->CreateTextButton(inSubMenu, "Configuration Settings (Other)", [this, inUI, labels]() {
|
||||
UIElement *configuration_settings = inUI->CreateMenu();
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
inUI->CreateSlider(configuration_settings, "Max Friction " + labels[i], sSettings->mMaxFriction[i], 0.0f, 500.0f, 1.0f, [=](float inValue) { sSettings->mMaxFriction[i] = inValue; });
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
inUI->CreateSlider(configuration_settings, "Motor Frequency " + labels[i] + " (Hz)", sSettings->mMotorSettings[i].mSpringSettings.mFrequency, 0.0f, 20.0f, 0.1f, [i](float inValue) { sSettings->mMotorSettings[i].mSpringSettings.mFrequency = inValue; });
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
inUI->CreateSlider(configuration_settings, "Motor Damping " + labels[i], sSettings->mMotorSettings[i].mSpringSettings.mDamping, 0.0f, 2.0f, 0.01f, [i](float inValue) { sSettings->mMotorSettings[i].mSpringSettings.mDamping = inValue; });
|
||||
|
||||
inUI->CreateTextButton(configuration_settings, "Accept Changes", [this]() { RestartTest(); });
|
||||
|
||||
inUI->ShowMenu(configuration_settings);
|
||||
});
|
||||
|
||||
inUI->CreateTextButton(inSubMenu, "Runtime Settings", [this, inUI, labels, motor_states]() {
|
||||
UIElement *runtime_settings = inUI->CreateMenu();
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
EAxis axis = EAxis(EAxis::TranslationX + i);
|
||||
|
||||
UIComboBox *combo = inUI->CreateComboBox(runtime_settings, "Motor " + labels[i], motor_states, (int)mConstraint->GetMotorState(axis), [this, axis](int inItem) { mConstraint->SetMotorState(axis, (EMotorState)inItem); });
|
||||
combo->SetDisabled(sSettings->IsFixedAxis(axis));
|
||||
|
||||
UISlider *velocity = inUI->CreateSlider(runtime_settings, "Target Velocity", mConstraint->GetTargetVelocityCS()[i], -10.0f, 10.0f, 0.1f, [this, i](float inValue) {
|
||||
Vec3 v = mConstraint->GetTargetVelocityCS();
|
||||
v.SetComponent(i, inValue);
|
||||
mConstraint->SetTargetVelocityCS(v); });
|
||||
velocity->SetDisabled(sSettings->IsFixedAxis(axis));
|
||||
|
||||
UISlider *position = inUI->CreateSlider(runtime_settings, "Target Position", mConstraint->GetTargetPositionCS()[i], -10.0f, 10.0f, 0.1f, [this, i](float inValue) {
|
||||
Vec3 v = mConstraint->GetTargetPositionCS();
|
||||
v.SetComponent(i, inValue);
|
||||
mConstraint->SetTargetPositionCS(v); });
|
||||
position->SetDisabled(sSettings->IsFixedAxis(axis));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
EAxis axis = EAxis(EAxis::RotationX + i);
|
||||
|
||||
inUI->CreateComboBox(runtime_settings, "Motor " + labels[axis], motor_states, (int)mConstraint->GetMotorState(axis), [this, axis](int inItem) { mConstraint->SetMotorState(axis, (EMotorState)inItem); });
|
||||
|
||||
inUI->CreateSlider(runtime_settings, "Target Velocity", RadiansToDegrees(mConstraint->GetTargetAngularVelocityCS()[i]), -90.0f, 90.0f, 1.0f, [this, i](float inValue) {
|
||||
Vec3 v = mConstraint->GetTargetAngularVelocityCS();
|
||||
v.SetComponent(i, DegreesToRadians(inValue));
|
||||
mConstraint->SetTargetAngularVelocityCS(v); });
|
||||
|
||||
inUI->CreateSlider(runtime_settings, "Target Position", RadiansToDegrees(mTargetOrientationCS[i]), -180.0f, 180.0f, 1.0f, [this, i](float inValue) {
|
||||
mTargetOrientationCS.SetComponent(i, DegreesToRadians(Clamp(inValue, -179.99f, 179.99f))); // +/- 180 degrees is ambiguous, so add a little bit of a margin
|
||||
mConstraint->SetTargetOrientationCS(Quat::sEulerAngles(mTargetOrientationCS)); });
|
||||
}
|
||||
|
||||
inUI->ShowMenu(runtime_settings);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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/Constraints/SixDOFConstraint.h>
|
||||
|
||||
class SixDOFConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SixDOFConstraintTest)
|
||||
|
||||
virtual void Initialize() override;
|
||||
|
||||
virtual void GetInitialCamera(CameraState &ioState) const override;
|
||||
|
||||
virtual bool HasSettingsMenu() const override { return true; }
|
||||
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
|
||||
|
||||
private:
|
||||
using SettingsRef = Ref<SixDOFConstraintSettings>;
|
||||
using EAxis = SixDOFConstraintSettings::EAxis;
|
||||
|
||||
static float sLimitMin[EAxis::Num];
|
||||
static float sLimitMax[EAxis::Num];
|
||||
static bool sEnableLimits[EAxis::Num];
|
||||
static SettingsRef sSettings;
|
||||
|
||||
Vec3 mTargetOrientationCS = Vec3::sZero();
|
||||
|
||||
Ref<SixDOFConstraint> mConstraint;
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/SliderConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/SliderConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(SliderConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(SliderConstraintTest, Test)
|
||||
}
|
||||
|
||||
void SliderConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
const int cChainLength = 10;
|
||||
|
||||
// Create group filter
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(cChainLength);
|
||||
for (CollisionGroup::SubGroupID i = 0; i < cChainLength - 1; ++i)
|
||||
group_filter->DisableCollision(i, i + 1);
|
||||
CollisionGroup::GroupID group_id = 0;
|
||||
|
||||
// Create box
|
||||
float box_size = 4.0f;
|
||||
RefConst<Shape> box = new BoxShape(Vec3::sReplicate(0.5f * box_size));
|
||||
|
||||
// Bodies attached through slider constraints
|
||||
for (int randomness = 0; randomness < 2; ++randomness)
|
||||
{
|
||||
RVec3 position(0, 25.0f, -randomness * 20.0f);
|
||||
Body &top = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
top.SetCollisionGroup(CollisionGroup(group_filter, group_id, 0));
|
||||
mBodyInterface->AddBody(top.GetID(), EActivation::DontActivate);
|
||||
|
||||
default_random_engine random;
|
||||
uniform_real_distribution<float> displacement(-1.0f, 1.0f);
|
||||
|
||||
Body *prev = ⊤
|
||||
for (int i = 1; i < cChainLength; ++i)
|
||||
{
|
||||
Quat rotation;
|
||||
Vec3 slider_axis;
|
||||
if (randomness == 0)
|
||||
{
|
||||
position += Vec3(box_size, 0, 0);
|
||||
rotation = Quat::sIdentity();
|
||||
slider_axis = Quat::sRotation(Vec3::sAxisZ(), -DegreesToRadians(10)).RotateAxisX();
|
||||
}
|
||||
else
|
||||
{
|
||||
position += Vec3(box_size + abs(displacement(random)), displacement(random), displacement(random));
|
||||
rotation = Quat::sRandom(random);
|
||||
slider_axis = Quat::sRotation(Vec3::sAxisY(), displacement(random) * DegreesToRadians(20)) * Quat::sRotation(Vec3::sAxisZ(), -DegreesToRadians(10)).RotateAxisX();
|
||||
}
|
||||
|
||||
Body &segment = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, rotation, EMotionType::Dynamic, Layers::MOVING));
|
||||
segment.SetCollisionGroup(CollisionGroup(group_filter, group_id, CollisionGroup::SubGroupID(i)));
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
|
||||
SliderConstraintSettings settings;
|
||||
settings.mAutoDetectPoint = true;
|
||||
settings.SetSliderAxis(slider_axis);
|
||||
settings.mLimitsMin = -5.0f;
|
||||
settings.mLimitsMax = 10.0f;
|
||||
mPhysicsSystem->AddConstraint(settings.Create(*prev, segment));
|
||||
|
||||
prev = &segment;
|
||||
}
|
||||
|
||||
group_id++;
|
||||
}
|
||||
|
||||
{
|
||||
// Two light bodies attached to a heavy body (gives issues if the wrong anchor point is chosen)
|
||||
Body *light1 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.1f)), RVec3(-5.0f, 7.0f, -5.2f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
light1->SetCollisionGroup(CollisionGroup(group_filter, group_id, 0));
|
||||
mBodyInterface->AddBody(light1->GetID(), EActivation::Activate);
|
||||
Body *heavy = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(5.0f)), RVec3(-5.0f, 7.0f, 0.0f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
heavy->SetCollisionGroup(CollisionGroup(group_filter, group_id, 1));
|
||||
mBodyInterface->AddBody(heavy->GetID(), EActivation::Activate);
|
||||
Body *light2 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.1f)), RVec3(-5.0f, 7.0f, 5.2f), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
light2->SetCollisionGroup(CollisionGroup(group_filter, group_id, 2));
|
||||
mBodyInterface->AddBody(light2->GetID(), EActivation::Activate);
|
||||
++group_id;
|
||||
|
||||
// Note: This violates the recommendation that body 1 is heavier than body 2, therefore this constraint will not work well (the rotation constraint will not be solved accurately)
|
||||
SliderConstraintSettings slider1;
|
||||
slider1.mAutoDetectPoint = true;
|
||||
slider1.SetSliderAxis(Vec3::sAxisZ());
|
||||
slider1.mLimitsMin = 0.0f;
|
||||
slider1.mLimitsMax = 1.0f;
|
||||
mPhysicsSystem->AddConstraint(slider1.Create(*light1, *heavy));
|
||||
|
||||
// This constraint has the heavy body as body 1 so will work fine
|
||||
SliderConstraintSettings slider2;
|
||||
slider2.mAutoDetectPoint = true;
|
||||
slider2.SetSliderAxis(Vec3::sAxisZ());
|
||||
slider2.mLimitsMin = 0.0f;
|
||||
slider2.mLimitsMax = 1.0f;
|
||||
mPhysicsSystem->AddConstraint(slider2.Create(*heavy, *light2));
|
||||
}
|
||||
|
||||
{
|
||||
// Two bodies vertically stacked with a slider constraint
|
||||
Body *vert1 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sOne()), RVec3(5, 9, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
vert1->SetCollisionGroup(CollisionGroup(group_filter, group_id, 0));
|
||||
mBodyInterface->AddBody(vert1->GetID(), EActivation::Activate);
|
||||
Body *vert2 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sOne()), RVec3(5, 3, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
vert2->SetCollisionGroup(CollisionGroup(group_filter, group_id, 1));
|
||||
mBodyInterface->AddBody(vert2->GetID(), EActivation::Activate);
|
||||
++group_id;
|
||||
|
||||
SliderConstraintSettings slider;
|
||||
slider.mAutoDetectPoint = true;
|
||||
slider.SetSliderAxis(Vec3::sAxisY());
|
||||
slider.mLimitsMin = 0.0f;
|
||||
slider.mLimitsMax = 2.0f;
|
||||
mPhysicsSystem->AddConstraint(slider.Create(*vert1, *vert2));
|
||||
}
|
||||
|
||||
{
|
||||
// Two bodies vertically stacked with a slider constraint using soft limits
|
||||
Body *vert1 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sOne()), RVec3(10, 9, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
vert1->SetCollisionGroup(CollisionGroup(group_filter, group_id, 0));
|
||||
mBodyInterface->AddBody(vert1->GetID(), EActivation::Activate);
|
||||
Body *vert2 = mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sOne()), RVec3(10, 3, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
vert2->SetCollisionGroup(CollisionGroup(group_filter, group_id, 1));
|
||||
mBodyInterface->AddBody(vert2->GetID(), EActivation::Activate);
|
||||
++group_id;
|
||||
|
||||
SliderConstraintSettings slider;
|
||||
slider.mAutoDetectPoint = true;
|
||||
slider.SetSliderAxis(Vec3::sAxisY());
|
||||
slider.mLimitsMin = 0.0f;
|
||||
slider.mLimitsMax = 2.0f;
|
||||
slider.mLimitsSpringSettings.mFrequency = 1.0f;
|
||||
slider.mLimitsSpringSettings.mDamping = 0.5f;
|
||||
mPhysicsSystem->AddConstraint(slider.Create(*vert1, *vert2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class SliderConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SliderConstraintTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
94
lib/All/JoltPhysics/Samples/Tests/Constraints/SpringTest.cpp
Normal file
94
lib/All/JoltPhysics/Samples/Tests/Constraints/SpringTest.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/SpringTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Constraints/DistanceConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(SpringTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(SpringTest, Test)
|
||||
}
|
||||
|
||||
void SpringTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Top fixed body
|
||||
RVec3 position(0, 75, 0);
|
||||
Body &top = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(100.0f, 1.0f, 1.0f)), position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
mBodyInterface->AddBody(top.GetID(), EActivation::DontActivate);
|
||||
|
||||
// Bodies attached with spring with different string lengths, same frequency and no damping
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
// Create body
|
||||
RVec3 attachment_point = position + Vec3(-100.0f + i * 5.0f, 0, 0);
|
||||
RVec3 body_position = attachment_point - Vec3(0, 10.0f + i * 2.5f, 0);
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.75f)), body_position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.GetMotionProperties()->SetAngularDamping(0.0f);
|
||||
body.GetMotionProperties()->SetLinearDamping(0.0f);
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
|
||||
// Attach spring
|
||||
DistanceConstraintSettings settings;
|
||||
settings.mPoint1 = attachment_point;
|
||||
settings.mPoint2 = body_position;
|
||||
settings.mLimitsSpringSettings.mFrequency = 0.33f;
|
||||
mPhysicsSystem->AddConstraint(settings.Create(top, body));
|
||||
|
||||
// Move the body up so that it can start oscillating
|
||||
mBodyInterface->SetPositionAndRotation(body.GetID(), attachment_point - Vec3(0, 5, 0), Quat::sIdentity(), EActivation::DontActivate);
|
||||
}
|
||||
|
||||
// Bodies attached with spring with different frequency and no damping
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
// Create body
|
||||
RVec3 attachment_point = position + Vec3(-25.0f + i * 5.0f, 0, 0);
|
||||
RVec3 body_position = attachment_point - Vec3(0, 25.0f, 0);
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.75f)), body_position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.GetMotionProperties()->SetAngularDamping(0.0f);
|
||||
body.GetMotionProperties()->SetLinearDamping(0.0f);
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
|
||||
// Attach spring
|
||||
DistanceConstraintSettings settings;
|
||||
settings.mPoint1 = attachment_point;
|
||||
settings.mPoint2 = body_position;
|
||||
settings.mLimitsSpringSettings.mFrequency = 0.1f + 0.1f * i;
|
||||
mPhysicsSystem->AddConstraint(settings.Create(top, body));
|
||||
|
||||
// Move the body up so that it can start oscillating
|
||||
mBodyInterface->SetPositionAndRotation(body.GetID(), attachment_point - Vec3(0, 5, 0), Quat::sIdentity(), EActivation::DontActivate);
|
||||
}
|
||||
|
||||
// Bodies attached with spring with same spring length, same frequency and different damping
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
// Create body
|
||||
RVec3 attachment_point = position + Vec3(50.0f + i * 5.0f, 0, 0);
|
||||
RVec3 body_position = attachment_point - Vec3(0, 25.0f, 0);
|
||||
Body &body = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3::sReplicate(0.75f)), body_position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body.GetMotionProperties()->SetAngularDamping(0.0f);
|
||||
body.GetMotionProperties()->SetLinearDamping(0.0f);
|
||||
mBodyInterface->AddBody(body.GetID(), EActivation::Activate);
|
||||
|
||||
// Attach spring
|
||||
DistanceConstraintSettings settings;
|
||||
settings.mPoint1 = attachment_point;
|
||||
settings.mPoint2 = body_position;
|
||||
settings.mLimitsSpringSettings.mFrequency = 0.33f;
|
||||
settings.mLimitsSpringSettings.mDamping = (1.0f / 9.0f) * i;
|
||||
mPhysicsSystem->AddConstraint(settings.Create(top, body));
|
||||
|
||||
// Move the body up so that it can start oscillating
|
||||
mBodyInterface->SetPositionAndRotation(body.GetID(), attachment_point - Vec3(0, 5, 0), Quat::sIdentity(), EActivation::DontActivate);
|
||||
}
|
||||
}
|
||||
16
lib/All/JoltPhysics/Samples/Tests/Constraints/SpringTest.h
Normal file
16
lib/All/JoltPhysics/Samples/Tests/Constraints/SpringTest.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Tests/Test.h>
|
||||
|
||||
class SpringTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SpringTest)
|
||||
|
||||
// See: Test
|
||||
virtual void Initialize() override;
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/SwingTwistConstraintFrictionTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Constraints/SwingTwistConstraint.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(SwingTwistConstraintFrictionTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(SwingTwistConstraintFrictionTest, Test)
|
||||
}
|
||||
|
||||
void SwingTwistConstraintFrictionTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
// Create group filter
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable;
|
||||
|
||||
float half_cylinder_height = 1.5f;
|
||||
RefConst<Shape> capsule = new CapsuleShape(half_cylinder_height, 0.5f);
|
||||
|
||||
RVec3 body1_position(0, 10, 0);
|
||||
Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(capsule, body1_position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
|
||||
body1.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
mBodyInterface->AddBody(body1.GetID(), EActivation::DontActivate);
|
||||
|
||||
RVec3 body2_position = body1_position + Vec3(0, -2.0f * half_cylinder_height, 0);
|
||||
Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(capsule, body2_position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
|
||||
body2.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
|
||||
body2.GetMotionProperties()->SetLinearDamping(0.0f);
|
||||
body2.GetMotionProperties()->SetAngularDamping(0.0f);
|
||||
body2.SetAllowSleeping(false);
|
||||
mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
|
||||
|
||||
SwingTwistConstraintSettings settings;
|
||||
settings.mPosition1 = settings.mPosition2 = body1_position + Vec3(0, -half_cylinder_height, 0);
|
||||
settings.mTwistAxis1 = settings.mTwistAxis2 = Vec3(0, -1, 0);
|
||||
settings.mPlaneAxis1 = settings.mPlaneAxis2 = Vec3::sAxisX();
|
||||
settings.mNormalHalfConeAngle = DegreesToRadians(90);
|
||||
settings.mPlaneHalfConeAngle = DegreesToRadians(90);
|
||||
settings.mTwistMinAngle = -JPH_PI;
|
||||
settings.mTwistMaxAngle = JPH_PI;
|
||||
|
||||
float body2_inertia = (body2.GetMotionProperties()->GetLocalSpaceInverseInertia().Inversed3x3() * Vec3::sAxisY()).Length();
|
||||
constexpr float max_angular_acceleration = DegreesToRadians(90.0f); // rad/s^2
|
||||
settings.mMaxFrictionTorque = body2_inertia * max_angular_acceleration;
|
||||
|
||||
settings.mTwistMotorSettings = settings.mSwingMotorSettings = MotorSettings(10.0f, 2.0f);
|
||||
|
||||
mConstraint = static_cast<SwingTwistConstraint *>(settings.Create(body1, body2));
|
||||
mPhysicsSystem->AddConstraint(mConstraint);
|
||||
}
|
||||
|
||||
void SwingTwistConstraintFrictionTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
mTime += inParams.mDeltaTime;
|
||||
|
||||
bool pause = fmod(mTime, 5.0f) > 2.5f;
|
||||
|
||||
if (pause)
|
||||
{
|
||||
mConstraint->SetSwingMotorState(EMotorState::Off);
|
||||
mConstraint->SetTwistMotorState(EMotorState::Off);
|
||||
}
|
||||
else
|
||||
{
|
||||
mConstraint->SetSwingMotorState(EMotorState::Velocity);
|
||||
mConstraint->SetTwistMotorState(EMotorState::Velocity);
|
||||
mConstraint->SetTargetAngularVelocityCS(Vec3(DegreesToRadians(180.0f), 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void SwingTwistConstraintFrictionTest::SaveState(StateRecorder &inStream) const
|
||||
{
|
||||
inStream.Write(mTime);
|
||||
}
|
||||
|
||||
void SwingTwistConstraintFrictionTest::RestoreState(StateRecorder &inStream)
|
||||
{
|
||||
inStream.Read(mTime);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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/Constraints/SwingTwistConstraint.h>
|
||||
|
||||
class SwingTwistConstraintFrictionTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SwingTwistConstraintFrictionTest)
|
||||
|
||||
// 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:
|
||||
float mTime = 0.0f;
|
||||
SwingTwistConstraint * mConstraint = nullptr;
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <TestFramework.h>
|
||||
|
||||
#include <Tests/Constraints/SwingTwistConstraintTest.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/GroupFilterTable.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Application/DebugUI.h>
|
||||
#include <Layers.h>
|
||||
|
||||
JPH_IMPLEMENT_RTTI_VIRTUAL(SwingTwistConstraintTest)
|
||||
{
|
||||
JPH_ADD_BASE_CLASS(SwingTwistConstraintTest, Test)
|
||||
}
|
||||
|
||||
void SwingTwistConstraintTest::Initialize()
|
||||
{
|
||||
// Floor
|
||||
CreateFloor();
|
||||
|
||||
float half_cylinder_height = 1.5f;
|
||||
|
||||
const int cChainLength = 10;
|
||||
|
||||
// Build a collision group filter that disables collision between adjacent bodies
|
||||
Ref<GroupFilterTable> group_filter = new GroupFilterTable(cChainLength);
|
||||
for (CollisionGroup::SubGroupID i = 0; i < cChainLength - 1; ++i)
|
||||
group_filter->DisableCollision(i, i + 1);
|
||||
|
||||
Body *prev = nullptr;
|
||||
Quat rotation = Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI);
|
||||
RVec3 position(0, 25, 0);
|
||||
for (int i = 0; i < cChainLength; ++i)
|
||||
{
|
||||
position += Vec3(2.0f * half_cylinder_height, 0, 0);
|
||||
|
||||
Body &segment = *mBodyInterface->CreateBody(BodyCreationSettings(new CapsuleShape(half_cylinder_height, 0.5f), position, Quat::sRotation(Vec3::sAxisX(), 0.25f * JPH_PI * i) * rotation, i == 0? EMotionType::Static : EMotionType::Dynamic, i == 0? Layers::NON_MOVING : Layers::MOVING));
|
||||
segment.SetCollisionGroup(CollisionGroup(group_filter, 0, CollisionGroup::SubGroupID(i)));
|
||||
mBodyInterface->AddBody(segment.GetID(), EActivation::Activate);
|
||||
if (i != 0)
|
||||
segment.SetAllowSleeping(false);
|
||||
|
||||
if (prev != nullptr)
|
||||
{
|
||||
Ref<SwingTwistConstraintSettings> settings = new SwingTwistConstraintSettings;
|
||||
settings->mPosition1 = settings->mPosition2 = position + Vec3(-half_cylinder_height, 0, 0);
|
||||
settings->mTwistAxis1 = settings->mTwistAxis2 = Vec3::sAxisX();
|
||||
settings->mPlaneAxis1 = settings->mPlaneAxis2 = Vec3::sAxisY();
|
||||
settings->mNormalHalfConeAngle = sNormalHalfConeAngle;
|
||||
settings->mPlaneHalfConeAngle = sPlaneHalfConeAngle;
|
||||
settings->mTwistMinAngle = sTwistMinAngle;
|
||||
settings->mTwistMaxAngle = sTwistMaxAngle;
|
||||
|
||||
Ref<SwingTwistConstraint> constraint = static_cast<SwingTwistConstraint *>(settings->Create(*prev, segment));
|
||||
mPhysicsSystem->AddConstraint(constraint);
|
||||
mConstraints.push_back(constraint);
|
||||
}
|
||||
|
||||
prev = &segment;
|
||||
}
|
||||
}
|
||||
|
||||
void SwingTwistConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
|
||||
{
|
||||
for (SwingTwistConstraint *c : mConstraints)
|
||||
{
|
||||
c->SetNormalHalfConeAngle(sNormalHalfConeAngle);
|
||||
c->SetPlaneHalfConeAngle(sPlaneHalfConeAngle);
|
||||
c->SetTwistMinAngle(sTwistMinAngle);
|
||||
c->SetTwistMaxAngle(sTwistMaxAngle);
|
||||
}
|
||||
}
|
||||
|
||||
void SwingTwistConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
|
||||
{
|
||||
inUI->CreateSlider(inSubMenu, "Min Twist Angle (deg)", RadiansToDegrees(sTwistMinAngle), -180.0f, 180.0f, 1.0f, [=](float inValue) { sTwistMinAngle = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(inSubMenu, "Max Twist Angle (deg)", RadiansToDegrees(sTwistMaxAngle), -180.0f, 180.0f, 1.0f, [=](float inValue) { sTwistMaxAngle = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(inSubMenu, "Normal Half Cone Angle (deg)", RadiansToDegrees(sNormalHalfConeAngle), 0.0f, 180.0f, 1.0f, [=](float inValue) { sNormalHalfConeAngle = DegreesToRadians(inValue); });
|
||||
inUI->CreateSlider(inSubMenu, "Plane Half Cone Angle (deg)", RadiansToDegrees(sPlaneHalfConeAngle), 0.0f, 180.0f, 1.0f, [=](float inValue) { sPlaneHalfConeAngle = DegreesToRadians(inValue); });
|
||||
|
||||
inUI->ShowMenu(inSubMenu);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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/Constraints/SwingTwistConstraint.h>
|
||||
|
||||
class SwingTwistConstraintTest : public Test
|
||||
{
|
||||
public:
|
||||
JPH_DECLARE_RTTI_VIRTUAL(JPH_NO_EXPORT, SwingTwistConstraintTest)
|
||||
|
||||
virtual void Initialize() override;
|
||||
|
||||
virtual void PrePhysicsUpdate(const PreUpdateParams &inParams) override;
|
||||
|
||||
virtual bool HasSettingsMenu() const override { return true; }
|
||||
virtual void CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
|
||||
|
||||
private:
|
||||
Array<Ref<SwingTwistConstraint>> mConstraints;
|
||||
|
||||
inline static float sNormalHalfConeAngle = DegreesToRadians(60);
|
||||
inline static float sPlaneHalfConeAngle = DegreesToRadians(20);
|
||||
inline static float sTwistMinAngle = DegreesToRadians(-10);
|
||||
inline static float sTwistMaxAngle = DegreesToRadians(20);
|
||||
};
|
||||
Reference in New Issue
Block a user