Implemented a custom physics engine #316
|
@ -4,7 +4,7 @@
|
||||||
NumberOfChildren: 0
|
NumberOfChildren: 0
|
||||||
Components:
|
Components:
|
||||||
Transform Component:
|
Transform Component:
|
||||||
Translate: {x: 0, y: 3, z: 0}
|
Translate: {x: 0.186280191, y: 4.3224473, z: 0}
|
||||||
Rotate: {x: -0, y: 0, z: -0}
|
Rotate: {x: -0, y: 0, z: -0}
|
||||||
Scale: {x: 1, y: 1, z: 1}
|
Scale: {x: 1, y: 1, z: 1}
|
||||||
IsActive: true
|
IsActive: true
|
||||||
|
|
|
@ -115,7 +115,7 @@ namespace SHADE
|
||||||
|
|
||||||
bool SHSphere::Contains(const SHSphere& rhs) const noexcept
|
bool SHSphere::Contains(const SHSphere& rhs) const noexcept
|
||||||
{
|
{
|
||||||
return BoundingSphere::Contains(rhs);
|
return BoundingSphere::Contains(rhs) == CONTAINS;
|
||||||
}
|
}
|
||||||
|
|
||||||
float SHSphere::Volume() const noexcept
|
float SHSphere::Volume() const noexcept
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace SHADE
|
||||||
SHCollisionShape::SHCollisionShape(SHCollisionShapeID id, Type colliderType)
|
SHCollisionShape::SHCollisionShape(SHCollisionShapeID id, Type colliderType)
|
||||||
: id { id }
|
: id { id }
|
||||||
, flags { 0 }
|
, flags { 0 }
|
||||||
, parentTransform { nullptr }
|
, collider { nullptr }
|
||||||
, collisionTag { SHCollisionTagMatrix::GetTag(0) }
|
, collisionTag { SHCollisionTagMatrix::GetTag(0) }
|
||||||
{
|
{
|
||||||
flags |= 1U << SHUtilities::ConvertEnum(colliderType);
|
flags |= 1U << SHUtilities::ConvertEnum(colliderType);
|
||||||
|
@ -135,11 +135,6 @@ namespace SHADE
|
||||||
material = newMaterial;
|
material = newMaterial;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHCollisionShape::SetParentTransform(SHTransform& parentTF) noexcept
|
|
||||||
{
|
|
||||||
parentTransform = &parentTF;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept
|
void SHCollisionShape::SetPositionOffset(const SHVec3& posOffset) noexcept
|
||||||
{
|
{
|
||||||
transform.position = posOffset;
|
transform.position = posOffset;
|
||||||
|
|
|
@ -22,6 +22,12 @@
|
||||||
|
|
||||||
namespace SHADE
|
namespace SHADE
|
||||||
{
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Forward Declarations */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
class SHRigidBody;
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
/* Type Definitions */
|
/* Type Definitions */
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
@ -34,10 +40,11 @@ namespace SHADE
|
||||||
/* Friends */
|
/* Friends */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
friend class SHCollider;
|
friend class SHCollider;
|
||||||
friend class SHColliderComponent;
|
friend class SHColliderComponent;
|
||||||
friend class SHCollisionShapeFactory;
|
friend class SHCollisionShapeFactory;
|
||||||
friend class SHCollisionSpace;
|
friend class SHCollisionSpace;
|
||||||
|
friend struct SHManifold;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
@ -108,8 +115,6 @@ namespace SHADE
|
||||||
void SetDensity (float density) noexcept;
|
void SetDensity (float density) noexcept;
|
||||||
void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept;
|
void SetMaterial (const SHPhysicsMaterial& newMaterial) noexcept;
|
||||||
|
|
||||||
void SetParentTransform (SHTransform& parentTF) noexcept;
|
|
||||||
|
|
||||||
void SetPositionOffset (const SHVec3& posOffset) noexcept;
|
void SetPositionOffset (const SHVec3& posOffset) noexcept;
|
||||||
void SetRotationOffset (const SHVec3& rotOffset) noexcept;
|
void SetRotationOffset (const SHVec3& rotOffset) noexcept;
|
||||||
|
|
||||||
|
@ -137,13 +142,13 @@ namespace SHADE
|
||||||
|
|
||||||
SHPhysicsMaterial material;
|
SHPhysicsMaterial material;
|
||||||
|
|
||||||
SHTransform* parentTransform;
|
SHCollider* collider; // The collider it belongs to.
|
||||||
SHTransform transform;
|
SHTransform transform;
|
||||||
|
|
||||||
// Needed for conversion to euler angles
|
// Needed for conversion to euler angles
|
||||||
SHVec3 rotationOffset;
|
SHVec3 rotationOffset;
|
||||||
|
|
||||||
uint8_t flags; // 0 0 0 isColliding trigger capsule sphere box
|
uint8_t flags; // 0 0 0 isColliding trigger capsule sphere box
|
||||||
SHCollisionTag* collisionTag;
|
SHCollisionTag* collisionTag;
|
||||||
|
|
||||||
RTTR_ENABLE()
|
RTTR_ENABLE()
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
// Project Headers
|
// Project Headers
|
||||||
#include "Math/SHMathHelpers.h"
|
#include "Math/SHMathHelpers.h"
|
||||||
#include "Math/SHMatrix.h"
|
#include "Math/SHMatrix.h"
|
||||||
|
#include "Physics/Collision/SHCollider.h"
|
||||||
|
|
||||||
namespace SHADE
|
namespace SHADE
|
||||||
{
|
{
|
||||||
|
@ -38,7 +39,7 @@ namespace SHADE
|
||||||
{
|
{
|
||||||
|
|
||||||
material = rhs.material;
|
material = rhs.material;
|
||||||
parentTransform = rhs.parentTransform;
|
collider = rhs.collider;
|
||||||
transform = rhs.transform;
|
transform = rhs.transform;
|
||||||
rotationOffset = rhs.rotationOffset;
|
rotationOffset = rhs.rotationOffset;
|
||||||
flags = rhs.flags;
|
flags = rhs.flags;
|
||||||
|
@ -54,7 +55,7 @@ namespace SHADE
|
||||||
{
|
{
|
||||||
|
|
||||||
material = rhs.material;
|
material = rhs.material;
|
||||||
parentTransform = rhs.parentTransform;
|
collider = rhs.collider;
|
||||||
transform = rhs.transform;
|
transform = rhs.transform;
|
||||||
rotationOffset = rhs.rotationOffset;
|
rotationOffset = rhs.rotationOffset;
|
||||||
flags = rhs.flags;
|
flags = rhs.flags;
|
||||||
|
@ -75,7 +76,7 @@ namespace SHADE
|
||||||
|
|
||||||
id = rhs.id;
|
id = rhs.id;
|
||||||
material = rhs.material;
|
material = rhs.material;
|
||||||
parentTransform = rhs.parentTransform;
|
collider = rhs.collider;
|
||||||
transform = rhs.transform;
|
transform = rhs.transform;
|
||||||
rotationOffset = rhs.rotationOffset;
|
rotationOffset = rhs.rotationOffset;
|
||||||
flags = rhs.flags;
|
flags = rhs.flags;
|
||||||
|
@ -101,7 +102,7 @@ namespace SHADE
|
||||||
|
|
||||||
id = rhs.id;
|
id = rhs.id;
|
||||||
material = rhs.material;
|
material = rhs.material;
|
||||||
parentTransform = rhs.parentTransform;
|
collider = rhs.collider;
|
||||||
transform = rhs.transform;
|
transform = rhs.transform;
|
||||||
rotationOffset = rhs.rotationOffset;
|
rotationOffset = rhs.rotationOffset;
|
||||||
flags = rhs.flags;
|
flags = rhs.flags;
|
||||||
|
@ -179,12 +180,14 @@ namespace SHADE
|
||||||
|
|
||||||
void SHSphereCollisionShape::ComputeTransforms() noexcept
|
void SHSphereCollisionShape::ComputeTransforms() noexcept
|
||||||
{
|
{
|
||||||
const float SPHERE_SCALE = std::fabs(SHMath::Max({ parentTransform->scale.x, parentTransform->scale.y, parentTransform->scale.z }));
|
const SHTransform& PARENT_TRANSFORM = collider->GetTransform();
|
||||||
|
|
||||||
|
const float SPHERE_SCALE = std::fabs(SHMath::Max({ PARENT_TRANSFORM.scale.x, PARENT_TRANSFORM.scale.y, PARENT_TRANSFORM.scale.z }));
|
||||||
SetScale(SPHERE_SCALE);
|
SetScale(SPHERE_SCALE);
|
||||||
|
|
||||||
// Recompute center
|
// Recompute center
|
||||||
const SHQuaternion FINAL_ROT = parentTransform->orientation * transform.orientation;
|
const SHQuaternion FINAL_ROT = PARENT_TRANSFORM.orientation * transform.orientation;
|
||||||
const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(parentTransform->position);
|
const SHMatrix TRS = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(PARENT_TRANSFORM.position);
|
||||||
|
|
||||||
Center = SHVec3::Transform(transform.position, TRS);
|
Center = SHVec3::Transform(transform.position, TRS);
|
||||||
}
|
}
|
||||||
|
@ -212,7 +215,8 @@ namespace SHADE
|
||||||
|
|
||||||
SHMatrix SHSphereCollisionShape::ComputeWorldTransform() const noexcept
|
SHMatrix SHSphereCollisionShape::ComputeWorldTransform() const noexcept
|
||||||
{
|
{
|
||||||
const SHQuaternion ROTATION = parentTransform->orientation * transform.orientation;
|
const SHTransform& PARENT_TRANSFORM = collider->GetTransform();
|
||||||
|
const SHQuaternion ROTATION = PARENT_TRANSFORM.orientation * transform.orientation;
|
||||||
const SHVec3 SCALE{ Radius };
|
const SHVec3 SCALE{ Radius };
|
||||||
|
|
||||||
return SHMatrix::Transform
|
return SHMatrix::Transform
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace SHADE
|
||||||
uint8_t typeB;
|
uint8_t typeB;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t key = 0;
|
uint32_t key = std::numeric_limits<uint32_t>::max();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,8 +49,11 @@ namespace SHADE
|
||||||
// We only need 4 contact points to build a stable manifold.
|
// We only need 4 contact points to build a stable manifold.
|
||||||
static constexpr int MAX_NUM_CONTACTS = 4;
|
static constexpr int MAX_NUM_CONTACTS = 4;
|
||||||
|
|
||||||
SHCollisionShape* A;
|
SHCollisionShape* shapeA;
|
||||||
SHCollisionShape* B;
|
SHCollisionShape* shapeB;
|
||||||
|
|
||||||
|
SHRigidBody* bodyA = nullptr;
|
||||||
|
SHRigidBody* bodyB = nullptr;
|
||||||
|
|
||||||
uint32_t numContacts = 0;
|
uint32_t numContacts = 0;
|
||||||
SHCollisionState state = SHCollisionState::INVALID;
|
SHCollisionState state = SHCollisionState::INVALID;
|
||||||
|
|
|
@ -20,13 +20,18 @@ namespace SHADE
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
inline SHManifold::SHManifold(SHCollisionShape* a, SHCollisionShape* b) noexcept
|
inline SHManifold::SHManifold(SHCollisionShape* a, SHCollisionShape* b) noexcept
|
||||||
: A { a }
|
: shapeA { a }
|
||||||
, B { b }
|
, shapeB { b }
|
||||||
{}
|
{
|
||||||
|
bodyA = shapeA->collider->rigidBody;
|
||||||
|
bodyB = shapeB->collider->rigidBody;
|
||||||
|
}
|
||||||
|
|
||||||
inline SHManifold::SHManifold(const SHManifold& rhs) noexcept
|
inline SHManifold::SHManifold(const SHManifold& rhs) noexcept
|
||||||
: A { rhs.A }
|
: shapeA { rhs.shapeA }
|
||||||
, B { rhs.B }
|
, shapeB { rhs.shapeB }
|
||||||
|
, bodyA { rhs.bodyA }
|
||||||
|
, bodyB { rhs.bodyB }
|
||||||
, numContacts { rhs.numContacts }
|
, numContacts { rhs.numContacts }
|
||||||
, state { rhs.state }
|
, state { rhs.state }
|
||||||
, normal { rhs.normal }
|
, normal { rhs.normal }
|
||||||
|
@ -39,8 +44,10 @@ namespace SHADE
|
||||||
}
|
}
|
||||||
|
|
||||||
inline SHManifold::SHManifold(SHManifold&& rhs) noexcept
|
inline SHManifold::SHManifold(SHManifold&& rhs) noexcept
|
||||||
: A { rhs.A }
|
: shapeA { rhs.shapeA }
|
||||||
, B { rhs.B }
|
, shapeB { rhs.shapeB }
|
||||||
|
, bodyA { rhs.bodyA }
|
||||||
|
, bodyB { rhs.bodyB }
|
||||||
, numContacts { rhs.numContacts }
|
, numContacts { rhs.numContacts }
|
||||||
, state { rhs.state }
|
, state { rhs.state }
|
||||||
, normal { rhs.normal }
|
, normal { rhs.normal }
|
||||||
|
@ -61,8 +68,10 @@ namespace SHADE
|
||||||
if (this == &rhs)
|
if (this == &rhs)
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
A = rhs.A;
|
shapeA = rhs.shapeA;
|
||||||
B = rhs.B;
|
shapeB = rhs.shapeB;
|
||||||
|
bodyA = rhs.bodyA;
|
||||||
|
bodyB = rhs.bodyB;
|
||||||
numContacts = rhs.numContacts;
|
numContacts = rhs.numContacts;
|
||||||
state = rhs.state;
|
state = rhs.state;
|
||||||
normal = rhs.normal;
|
normal = rhs.normal;
|
||||||
|
@ -78,8 +87,10 @@ namespace SHADE
|
||||||
|
|
||||||
inline SHManifold& SHManifold::operator=(SHManifold&& rhs) noexcept
|
inline SHManifold& SHManifold::operator=(SHManifold&& rhs) noexcept
|
||||||
{
|
{
|
||||||
A = rhs.A;
|
shapeA = rhs.shapeA;
|
||||||
B = rhs.B;
|
shapeB = rhs.shapeB;
|
||||||
|
bodyA = rhs.bodyA;
|
||||||
|
bodyB = rhs.bodyB;
|
||||||
numContacts = rhs.numContacts;
|
numContacts = rhs.numContacts;
|
||||||
state = rhs.state;
|
state = rhs.state;
|
||||||
normal = rhs.normal;
|
normal = rhs.normal;
|
||||||
|
|
|
@ -307,7 +307,7 @@ namespace SHADE
|
||||||
SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO);
|
SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO);
|
||||||
|
|
||||||
// Set offsets
|
// Set offsets
|
||||||
sphere->SetParentTransform(transform);
|
sphere->collider = this;
|
||||||
sphere->SetPositionOffset(posOffset);
|
sphere->SetPositionOffset(posOffset);
|
||||||
sphere->SetRotationOffset(rotOffset);
|
sphere->SetRotationOffset(rotOffset);
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ namespace SHADE
|
||||||
/* Friends */
|
/* Friends */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
friend class SHCollisionSpace;
|
friend class SHCollisionSpace;
|
||||||
|
friend struct SHManifold;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -28,10 +28,7 @@ namespace SHADE
|
||||||
|
|
||||||
// Use the entity IDs to map resolved constraints back to the bodies
|
// Use the entity IDs to map resolved constraints back to the bodies
|
||||||
|
|
||||||
EntityID idA = MAX_EID;
|
SHCollisionKey key;
|
||||||
EntityID idB = MAX_EID;
|
|
||||||
|
|
||||||
uint32_t numContacts = 0;
|
|
||||||
|
|
||||||
// Material Data
|
// Material Data
|
||||||
|
|
||||||
|
@ -52,6 +49,8 @@ namespace SHADE
|
||||||
SHVec3 normal;
|
SHVec3 normal;
|
||||||
SHVec3 tangents[SHContact::NUM_TANGENTS];
|
SHVec3 tangents[SHContact::NUM_TANGENTS];
|
||||||
SHContact contacts[SHManifold::MAX_NUM_CONTACTS];
|
SHContact contacts[SHManifold::MAX_NUM_CONTACTS];
|
||||||
|
|
||||||
|
uint32_t numContacts = 0;
|
||||||
};
|
};
|
||||||
} // namespace SHADE
|
} // namespace SHADE
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,42 @@ namespace SHADE
|
||||||
/* Public Member Functions Definitions */
|
/* Public Member Functions Definitions */
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void SHContactManager::AddTrigger(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept
|
||||||
|
{
|
||||||
|
// If id not found, emplace new trigger.
|
||||||
|
auto trigger = triggers.find(key);
|
||||||
|
if (trigger == triggers.end())
|
||||||
|
triggers.emplace(key, Trigger{ A, B, SHCollisionState::INVALID });
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHContactManager::AddManifold(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept
|
||||||
|
{
|
||||||
|
// If id not found, emplace new manifold
|
||||||
|
auto manifold = manifolds.find(key);
|
||||||
|
if (manifold == manifolds.end())
|
||||||
|
manifolds.emplace(key, SHManifold{ A, B });
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHContactManager::RemoveInvalidatedTrigger(EntityID eid) noexcept
|
||||||
|
{
|
||||||
|
removeInvalidObject(triggers, eid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHContactManager::RemoveInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) noexcept
|
||||||
|
{
|
||||||
|
removeInvalidObject(triggers, eid, shapeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHContactManager::RemoveInvalidatedManifold(EntityID eid) noexcept
|
||||||
|
{
|
||||||
|
removeInvalidObject(manifolds, eid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHContactManager::RemoveInvalidatedManifold(EntityID eid, uint32_t shapeIndex) noexcept
|
||||||
|
{
|
||||||
|
removeInvalidObject(manifolds, eid, shapeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
void SHContactManager::Update() noexcept
|
void SHContactManager::Update() noexcept
|
||||||
{
|
{
|
||||||
// Clear expired or invalid collisions. If not, test collision.
|
// Clear expired or invalid collisions. If not, test collision.
|
||||||
|
@ -92,9 +128,9 @@ namespace SHADE
|
||||||
SHManifold& manifold = manifoldPair->second;
|
SHManifold& manifold = manifoldPair->second;
|
||||||
SHManifold oldManifold = manifold;
|
SHManifold oldManifold = manifold;
|
||||||
|
|
||||||
const bool IS_COLLIDING = SHCollisionDispatcher::Collide(manifold, *manifold.A, *manifold.B);
|
const bool IS_COLLIDING = SHCollisionDispatcher::Collide(manifold, *manifold.shapeA, *manifold.shapeB);
|
||||||
|
|
||||||
auto& collisionState = oldManifold.state;
|
auto& collisionState = manifold.state;
|
||||||
updateCollisionState(IS_COLLIDING, collisionState);
|
updateCollisionState(IS_COLLIDING, collisionState);
|
||||||
|
|
||||||
const bool IS_INVALID = collisionState == SHCollisionState::INVALID;
|
const bool IS_INVALID = collisionState == SHCollisionState::INVALID;
|
||||||
|
@ -128,41 +164,45 @@ namespace SHADE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHContactManager::AddTrigger(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept
|
void SHContactManager::SolveCollisions(int numIterations, float dt) noexcept
|
||||||
{
|
{
|
||||||
// If id not found, emplace new trigger.
|
static constexpr int SIZE_CONTACTS = sizeof(SHContact) * SHManifold::MAX_NUM_CONTACTS;
|
||||||
auto trigger = triggers.find(key);
|
|
||||||
if (trigger == triggers.end())
|
// Build constraints
|
||||||
triggers.emplace(key, Trigger{ A, B, SHCollisionState::INVALID }).first;
|
for (auto& [key, manifold] : manifolds)
|
||||||
|
contactSolver.AddContact(key, manifold);
|
||||||
|
|
||||||
|
// Solve contacts
|
||||||
|
contactSolver.SolveContacts(numIterations, dt);
|
||||||
|
|
||||||
|
// Map impulses back to manifolds
|
||||||
|
const auto& CONTACT_CONSTRAINTS = contactSolver.GetContantConstraints();
|
||||||
|
const auto& VELOCITY_STATES = contactSolver.GetVelocities();
|
||||||
|
|
||||||
|
for (auto& [key, contactConstraint] : CONTACT_CONSTRAINTS)
|
||||||
|
{
|
||||||
|
SHManifold& manifold = manifolds.find(key)->second;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < contactConstraint.numContacts; ++i)
|
||||||
|
manifold.contacts[i] = contactConstraint.contacts[i];
|
||||||
|
|
||||||
|
// Assign velocities back to the bodies
|
||||||
|
SHRigidBody* bodyA = manifold.bodyA;
|
||||||
|
SHRigidBody* bodyB = manifold.bodyB;
|
||||||
|
|
||||||
|
const auto& STATE_A = VELOCITY_STATES.find(key.GetEntityA())->second;
|
||||||
|
const auto& STATE_B = VELOCITY_STATES.find(key.GetEntityB())->second;
|
||||||
|
|
||||||
|
bodyA->SetLinearVelocity(STATE_A.linearVelocity);
|
||||||
|
bodyB->SetLinearVelocity(STATE_B.linearVelocity);
|
||||||
|
|
||||||
|
bodyA->SetAngularVelocity(STATE_A.angularVelocity);
|
||||||
|
bodyB->SetAngularVelocity(STATE_B.angularVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
contactSolver.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHContactManager::AddManifold(const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept
|
|
||||||
{
|
|
||||||
// If id not found, emplace new manifold
|
|
||||||
auto manifold = manifolds.find(key);
|
|
||||||
if (manifold == manifolds.end())
|
|
||||||
manifolds.emplace(key, SHManifold{ A, B }).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SHContactManager::RemoveInvalidatedTrigger(EntityID eid) noexcept
|
|
||||||
{
|
|
||||||
removeInvalidObject(triggers, eid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SHContactManager::RemoveInvalidatedTrigger(EntityID eid, uint32_t shapeIndex) noexcept
|
|
||||||
{
|
|
||||||
removeInvalidObject(triggers, eid, shapeIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SHContactManager::RemoveInvalidatedManifold(EntityID eid) noexcept
|
|
||||||
{
|
|
||||||
removeInvalidObject(manifolds, eid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SHContactManager::RemoveInvalidatedManifold(EntityID eid, uint32_t shapeIndex) noexcept
|
|
||||||
{
|
|
||||||
removeInvalidObject(manifolds, eid, shapeIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
/* Private Member Functions Definitions */
|
/* Private Member Functions Definitions */
|
||||||
|
@ -191,8 +231,6 @@ namespace SHADE
|
||||||
|
|
||||||
void SHContactManager::updateManifold(SHManifold& manifold, const SHManifold& oldManifold) noexcept
|
void SHContactManager::updateManifold(SHManifold& manifold, const SHManifold& oldManifold) noexcept
|
||||||
{
|
{
|
||||||
manifold.state = oldManifold.state;
|
|
||||||
|
|
||||||
// Early out since exiting a collision does not require an update beyond updating the state
|
// Early out since exiting a collision does not require an update beyond updating the state
|
||||||
if (manifold.state == SHCollisionState::EXIT)
|
if (manifold.state == SHCollisionState::EXIT)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "Physics/Collision/Contacts/SHCollisionEvents.h"
|
#include "Physics/Collision/Contacts/SHCollisionEvents.h"
|
||||||
#include "Physics/Collision/Contacts/SHCollisionKey.h"
|
#include "Physics/Collision/Contacts/SHCollisionKey.h"
|
||||||
#include "Physics/Collision/Contacts/SHManifold.h"
|
#include "Physics/Collision/Contacts/SHManifold.h"
|
||||||
|
#include "SHContactSolver.h"
|
||||||
|
|
||||||
namespace SHADE
|
namespace SHADE
|
||||||
{
|
{
|
||||||
|
@ -64,14 +65,6 @@ namespace SHADE
|
||||||
/* Member Functions */
|
/* Member Functions */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Removes any invalidated contacts and triggers, then performs narrowphase collision
|
|
||||||
* detection on existing triggers and manifolds.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
void Update () noexcept;
|
|
||||||
|
|
||||||
void AddTrigger (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept;
|
void AddTrigger (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept;
|
||||||
void AddManifold (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept;
|
void AddManifold (const SHCollisionKey& key, SHCollisionShape* A, SHCollisionShape* B) noexcept;
|
||||||
|
|
||||||
|
@ -80,6 +73,20 @@ namespace SHADE
|
||||||
void RemoveInvalidatedManifold (EntityID eid) noexcept;
|
void RemoveInvalidatedManifold (EntityID eid) noexcept;
|
||||||
void RemoveInvalidatedManifold (EntityID eid, uint32_t shapeIndex) noexcept;
|
void RemoveInvalidatedManifold (EntityID eid, uint32_t shapeIndex) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Removes any invalidated contacts and triggers, then performs narrowphase collision
|
||||||
|
* detection on existing triggers and manifolds.
|
||||||
|
*/
|
||||||
|
void Update () noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Builds contact constraints and solves them. Results are stored in the corresponding
|
||||||
|
* manifolds abiding by the sequential impulse method.
|
||||||
|
*/
|
||||||
|
void SolveCollisions (int numIterations, float dt) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Type Definitions */
|
/* Type Definitions */
|
||||||
|
@ -103,6 +110,8 @@ namespace SHADE
|
||||||
Manifolds manifolds;
|
Manifolds manifolds;
|
||||||
Triggers triggers;
|
Triggers triggers;
|
||||||
|
|
||||||
|
SHContactSolver contactSolver;
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Member Functions */
|
/* Member Functions */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -36,40 +36,39 @@ namespace SHADE
|
||||||
/* Public Member Functions Definitions */
|
/* Public Member Functions Definitions */
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void SHContactSolver::AddContact(const SHManifold& manifold, const SHRigidBody* rigidBodyA, const SHRigidBody* rigidBodyB) noexcept
|
void SHContactSolver::AddContact(const SHCollisionKey& key, const SHManifold& manifold) noexcept
|
||||||
{
|
{
|
||||||
SHContactConstraint& newConstraint = contactConstraints.emplace_back(SHContactConstraint{});
|
SHContactConstraint& newConstraint = contactConstraints.emplace(key, SHContactConstraint{}).first->second;
|
||||||
|
|
||||||
const auto* SHAPE_A = manifold.A;
|
const auto* SHAPE_A = manifold.shapeA;
|
||||||
const auto* SHAPE_B = manifold.B;
|
const auto* SHAPE_B = manifold.shapeB;
|
||||||
|
|
||||||
newConstraint.idA = SHAPE_A->GetEntityID();
|
const auto* BODY_A = manifold.bodyA;
|
||||||
newConstraint.idB = SHAPE_B->GetEntityID();
|
const auto* BODY_B = manifold.bodyB;
|
||||||
|
|
||||||
// Add velocities if it doesn't already exist
|
// Add velocities if it doesn't already exist
|
||||||
velocityStates.emplace(newConstraint.idA, VelocityState{ rigidBodyA->linearVelocity, rigidBodyB->angularVelocity });
|
velocityStates.emplace(key.GetEntityA(), VelocityState{ BODY_A->linearVelocity, BODY_A->angularVelocity });
|
||||||
velocityStates.emplace(newConstraint.idB, VelocityState{ rigidBodyB->linearVelocity, rigidBodyB->angularVelocity });
|
velocityStates.emplace(key.GetEntityB(), VelocityState{ BODY_B->linearVelocity, BODY_B->angularVelocity });
|
||||||
|
|
||||||
// Mix friction & restitution
|
// Mix friction & restitution
|
||||||
const float FRICTION_A = manifold.A->GetFriction();
|
const float FRICTION_A = SHAPE_A->GetFriction();
|
||||||
const float RESTITUTION_A = manifold.A->GetBounciness();
|
const float RESTITUTION_A = SHAPE_A->GetBounciness();
|
||||||
|
|
||||||
const float FRICTION_B = manifold.B->GetFriction();
|
const float FRICTION_B = SHAPE_B->GetFriction();
|
||||||
const float RESTITUTION_B = manifold.B->GetBounciness();
|
const float RESTITUTION_B = SHAPE_B->GetBounciness();
|
||||||
|
|
||||||
newConstraint.friction = std::sqrtf(FRICTION_A * FRICTION_B);
|
newConstraint.friction = std::sqrtf(FRICTION_A * FRICTION_B);
|
||||||
newConstraint.restitution = std::max(RESTITUTION_A, RESTITUTION_B);
|
newConstraint.restitution = std::max(RESTITUTION_A, RESTITUTION_B);
|
||||||
|
|
||||||
// Mass data
|
// Mass data
|
||||||
|
|
||||||
newConstraint.invMassA = rigidBodyA->invMass;
|
newConstraint.invMassA = BODY_A->invMass;
|
||||||
newConstraint.invMassB = rigidBodyB->invMass;
|
newConstraint.invInertiaA = BODY_A->worldInvInertia;
|
||||||
|
newConstraint.centerOfMassA = BODY_A->worldCentroid;
|
||||||
|
|
||||||
newConstraint.invInertiaA = rigidBodyA->worldInvInertia;
|
newConstraint.invMassB = BODY_B->invMass;
|
||||||
newConstraint.invInertiaB = rigidBodyB->worldInvInertia;
|
newConstraint.invInertiaB = BODY_B->worldInvInertia;
|
||||||
|
newConstraint.centerOfMassB = BODY_B->worldCentroid;
|
||||||
newConstraint.centerOfMassA = rigidBodyA->worldCentroid;
|
|
||||||
newConstraint.centerOfMassB = rigidBodyB->worldCentroid;
|
|
||||||
|
|
||||||
// Collision data
|
// Collision data
|
||||||
|
|
||||||
|
@ -91,8 +90,9 @@ namespace SHADE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHContactSolver::ClearContacts() noexcept
|
void SHContactSolver::Reset() noexcept
|
||||||
{
|
{
|
||||||
|
velocityStates.clear();
|
||||||
contactConstraints.clear();
|
contactConstraints.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,14 +112,14 @@ namespace SHADE
|
||||||
{
|
{
|
||||||
const float INV_DT = 1.0f / dt;
|
const float INV_DT = 1.0f / dt;
|
||||||
|
|
||||||
for (auto& constraint : contactConstraints)
|
for (auto& [key, constraint] : contactConstraints)
|
||||||
{
|
{
|
||||||
const float INV_MASS_SUM = constraint.invMassA + constraint.invMassB;
|
const float INV_MASS_SUM = constraint.invMassA + constraint.invMassB;
|
||||||
|
|
||||||
SHVec3 vA = velocityStates[constraint.idA].linearVelocity;
|
SHVec3 vA = velocityStates[key.GetEntityA()].linearVelocity;
|
||||||
SHVec3 wA = velocityStates[constraint.idA].angularVelocity;
|
SHVec3 wA = velocityStates[key.GetEntityA()].angularVelocity;
|
||||||
SHVec3 vB = velocityStates[constraint.idB].linearVelocity;
|
SHVec3 vB = velocityStates[key.GetEntityB()].linearVelocity;
|
||||||
SHVec3 wB = velocityStates[constraint.idB].angularVelocity;
|
SHVec3 wB = velocityStates[key.GetEntityB()].angularVelocity;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < constraint.numContacts; ++i)
|
for (uint32_t i = 0; i < constraint.numContacts; ++i)
|
||||||
{
|
{
|
||||||
|
@ -156,7 +156,7 @@ namespace SHADE
|
||||||
contact.normalMass += SHVec3::Dot(RB_CROSS_N, constraint.invInertiaB * RB_CROSS_N);
|
contact.normalMass += SHVec3::Dot(RB_CROSS_N, constraint.invInertiaB * RB_CROSS_N);
|
||||||
|
|
||||||
// Invert the normal mass (we want the actual mass, not the inverse mass)
|
// Invert the normal mass (we want the actual mass, not the inverse mass)
|
||||||
contact.normalMass = contact.normalMass == 0.0f ? 0.0f : 1.0f / contact.normalMass;
|
contact.normalMass = 1.0f / contact.normalMass;
|
||||||
|
|
||||||
// Effective mass along tangents (same steps as above)
|
// Effective mass along tangents (same steps as above)
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ namespace SHADE
|
||||||
contact.tangentMass[j] += SHVec3::Dot(RA_CROSS_T, constraint.invInertiaA * RA_CROSS_T);
|
contact.tangentMass[j] += SHVec3::Dot(RA_CROSS_T, constraint.invInertiaA * RA_CROSS_T);
|
||||||
contact.tangentMass[j] += SHVec3::Dot(RB_CROSS_T, constraint.invInertiaB * RB_CROSS_T);
|
contact.tangentMass[j] += SHVec3::Dot(RB_CROSS_T, constraint.invInertiaB * RB_CROSS_T);
|
||||||
|
|
||||||
contact.tangentMass[j] = contact.tangentMass[j] == 0.0f ? 0.0f : 1.0f / contact.tangentMass[j];
|
contact.tangentMass[j] = 1.0f / contact.tangentMass[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warm starting
|
// Warm starting
|
||||||
|
@ -195,28 +195,27 @@ namespace SHADE
|
||||||
const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB);
|
const SHVec3 RV_B = vB + SHVec3::Cross(wB, contact.rB);
|
||||||
const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal);
|
const float RV_N = SHVec3::Dot(RV_B - RV_A, constraint.normal);
|
||||||
|
|
||||||
const float ERROR_BIAS = BAUMGARTE_FACTOR * INV_DT * contact.penetration;
|
const float ERROR_BIAS = BAUMGARTE_FACTOR * INV_DT * std::min(0.0f, -contact.penetration + PENETRATION_SLOP);
|
||||||
const float RESTITUTION_BIAS = -constraint.restitution * RV_N;
|
const float RESTITUTION_BIAS = -constraint.restitution * RV_N;
|
||||||
|
|
||||||
contact.bias = ERROR_BIAS + RESTITUTION_BIAS;
|
contact.bias = ERROR_BIAS + RESTITUTION_BIAS;
|
||||||
}
|
}
|
||||||
|
|
||||||
velocityStates[constraint.idA].linearVelocity = vA;
|
velocityStates[key.GetEntityA()].linearVelocity = vA;
|
||||||
velocityStates[constraint.idA].angularVelocity = wA;
|
velocityStates[key.GetEntityA()].angularVelocity = wA;
|
||||||
velocityStates[constraint.idB].linearVelocity = vB;
|
velocityStates[key.GetEntityB()].linearVelocity = vB;
|
||||||
velocityStates[constraint.idB].angularVelocity = wB;
|
velocityStates[key.GetEntityB()].angularVelocity = wB;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHContactSolver::solve() noexcept
|
void SHContactSolver::solve() noexcept
|
||||||
{
|
{
|
||||||
for (auto& constraint : contactConstraints)
|
for (auto& [key, constraint] : contactConstraints)
|
||||||
{
|
{
|
||||||
SHVec3 vA = velocityStates[constraint.idA].linearVelocity;
|
SHVec3 vA = velocityStates[key.GetEntityA()].linearVelocity;
|
||||||
SHVec3 wA = velocityStates[constraint.idA].angularVelocity;
|
SHVec3 wA = velocityStates[key.GetEntityA()].angularVelocity;
|
||||||
SHVec3 vB = velocityStates[constraint.idB].linearVelocity;
|
SHVec3 vB = velocityStates[key.GetEntityB()].linearVelocity;
|
||||||
SHVec3 wB = velocityStates[constraint.idB].angularVelocity;
|
SHVec3 wB = velocityStates[key.GetEntityB()].angularVelocity;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < constraint.numContacts; ++i)
|
for (uint32_t i = 0; i < constraint.numContacts; ++i)
|
||||||
{
|
{
|
||||||
|
@ -279,10 +278,10 @@ namespace SHADE
|
||||||
wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE);
|
wB += constraint.invInertiaB * SHVec3::Cross(contact.rB, NORMAL_IMPULSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
velocityStates[constraint.idA].linearVelocity = vA;
|
velocityStates[key.GetEntityA()].linearVelocity = vA;
|
||||||
velocityStates[constraint.idA].angularVelocity = wA;
|
velocityStates[key.GetEntityA()].angularVelocity = wA;
|
||||||
velocityStates[constraint.idB].linearVelocity = vB;
|
velocityStates[key.GetEntityB()].linearVelocity = vB;
|
||||||
velocityStates[constraint.idB].angularVelocity = wB;
|
velocityStates[key.GetEntityB()].angularVelocity = wB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
// Project Headers
|
// Project Headers
|
||||||
#include "Constraints/SHContactConstraint.h"
|
#include "Constraints/SHContactConstraint.h"
|
||||||
#include "SHContactManager.h"
|
|
||||||
|
|
||||||
namespace SHADE
|
namespace SHADE
|
||||||
{
|
{
|
||||||
|
@ -41,7 +40,7 @@ namespace SHADE
|
||||||
};
|
};
|
||||||
|
|
||||||
using VelocityStates = std::unordered_map<EntityID, VelocityState>;
|
using VelocityStates = std::unordered_map<EntityID, VelocityState>;
|
||||||
using ContactConstraints = std::vector<SHContactConstraint>;
|
using ContactConstraints = std::unordered_map<SHCollisionKey, SHContactConstraint, SHCollisionKeyHash>;
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Constructors & Destructor */
|
/* Constructors & Destructor */
|
||||||
|
@ -66,14 +65,10 @@ namespace SHADE
|
||||||
* Build a contact constraint from a new manifold.
|
* Build a contact constraint from a new manifold.
|
||||||
* @param manifold
|
* @param manifold
|
||||||
* A manifold to build a contact constraint from.
|
* A manifold to build a contact constraint from.
|
||||||
* @param rigidBodyA
|
|
||||||
* The rigid body belonging to the first collision shape.
|
|
||||||
* @param rigidBodyB
|
|
||||||
* The rigid body belonging to the second collision shape.
|
|
||||||
*/
|
*/
|
||||||
void AddContact (const SHManifold& manifold, const SHRigidBody* rigidBodyA, const SHRigidBody* rigidBodyB) noexcept;
|
void AddContact (const SHCollisionKey& key, const SHManifold& manifold) noexcept;
|
||||||
|
|
||||||
void ClearContacts () noexcept;
|
void Reset () noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
|
|
|
@ -103,32 +103,11 @@ namespace SHADE
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* TODO: A lot of this needs to be cleaned up.
|
||||||
* Resolve Contacts
|
* Resolve Contacts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Build constraints
|
contactManager.SolveCollisions(settings.numVelocitySolverIterations, dt);
|
||||||
for (auto& [id, manifold] : contactManager.manifolds)
|
|
||||||
{
|
|
||||||
SHRigidBody* bodyA = rigidBodies[id.GetEntityA()];
|
|
||||||
SHRigidBody* bodyB = rigidBodies[id.GetEntityB()];
|
|
||||||
|
|
||||||
contactSolver.AddContact(manifold, bodyA, bodyB);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solve contacts
|
|
||||||
contactSolver.SolveContacts(settings.numVelocitySolverIterations, dt);
|
|
||||||
|
|
||||||
// Map velocities back to bodies
|
|
||||||
const auto& VELOCITY_STATES = contactSolver.GetVelocities();
|
|
||||||
for (auto& [id, velocityState] : VELOCITY_STATES)
|
|
||||||
{
|
|
||||||
SHRigidBody* body = rigidBodies[id];
|
|
||||||
body->linearVelocity = velocityState.linearVelocity;
|
|
||||||
body->angularVelocity = velocityState.angularVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear contacts
|
|
||||||
contactSolver.ClearContacts();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Integrate Velocities
|
* Integrate Velocities
|
||||||
|
|
Loading…
Reference in New Issue