Implemented backbone for collision detection with broadphase
This commit is contained in:
parent
d55a965e32
commit
cf9d4ef04b
|
@ -4,7 +4,7 @@
|
||||||
NumberOfChildren: 0
|
NumberOfChildren: 0
|
||||||
Components:
|
Components:
|
||||||
Transform Component:
|
Transform Component:
|
||||||
Translate: {x: 0, y: 1.77475965, z: 0}
|
Translate: {x: 0, y: 3, 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
|
||||||
|
@ -59,3 +59,43 @@
|
||||||
Perspective: true
|
Perspective: true
|
||||||
IsActive: true
|
IsActive: true
|
||||||
Scripts: ~
|
Scripts: ~
|
||||||
|
- EID: 2
|
||||||
|
Name: Default
|
||||||
|
IsActive: true
|
||||||
|
NumberOfChildren: 0
|
||||||
|
Components:
|
||||||
|
Transform Component:
|
||||||
|
Translate: {x: 0, y: 1, z: 0}
|
||||||
|
Rotate: {x: 0, y: 0, z: 0}
|
||||||
|
Scale: {x: 1, y: 1, z: 1}
|
||||||
|
IsActive: true
|
||||||
|
RigidBody Component:
|
||||||
|
Type: Static
|
||||||
|
Auto Mass: false
|
||||||
|
Mass: .inf
|
||||||
|
Drag: 0.00999999978
|
||||||
|
Angular Drag: 0.00999999978
|
||||||
|
Use Gravity: true
|
||||||
|
Gravity Scale: 1
|
||||||
|
Interpolate: true
|
||||||
|
Sleeping Enabled: true
|
||||||
|
Freeze Position X: false
|
||||||
|
Freeze Position Y: false
|
||||||
|
Freeze Position Z: false
|
||||||
|
Freeze Rotation X: false
|
||||||
|
Freeze Rotation Y: false
|
||||||
|
Freeze Rotation Z: false
|
||||||
|
IsActive: true
|
||||||
|
Collider Component:
|
||||||
|
DrawColliders: true
|
||||||
|
Colliders:
|
||||||
|
- Is Trigger: false
|
||||||
|
Type: Sphere
|
||||||
|
Radius: 1
|
||||||
|
Friction: 0.400000006
|
||||||
|
Bounciness: 0
|
||||||
|
Density: 1
|
||||||
|
Position Offset: {x: 0, y: 0, z: 0}
|
||||||
|
Rotation Offset: {x: 0, y: 0, z: 0}
|
||||||
|
IsActive: true
|
||||||
|
Scripts: ~
|
|
@ -77,7 +77,7 @@ namespace SHADE
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
[[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override;
|
[[nodiscard]] bool TestPoint (const SHVec3& point) const noexcept override;
|
||||||
[[nodiscard]] SHRaycastResult Raycast(const SHRay& ray) const noexcept override;
|
[[nodiscard]] SHRaycastResult Raycast (const SHRay& ray) const noexcept override;
|
||||||
|
|
||||||
[[nodiscard]] bool Contains (const SHBox& rhs) const noexcept;
|
[[nodiscard]] bool Contains (const SHBox& rhs) const noexcept;
|
||||||
[[nodiscard]] float Volume () const noexcept;
|
[[nodiscard]] float Volume () const noexcept;
|
||||||
|
|
|
@ -0,0 +1,607 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHDynamicAABBTree.cpp
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Implementation for a Dynamic AABB Tree for broadphase collision detection.
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#include <SHpch.h>
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
// Primary Header
|
||||||
|
#include "SHDynamicAABBTree.h"
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
|
#include "Math/SHMathHelpers.h"
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Constructors & Destructor Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHAABBTree::SHAABBTree() noexcept
|
||||||
|
: root { NULL_NODE }
|
||||||
|
, nodes { nullptr }
|
||||||
|
, nodeCount { 0 }
|
||||||
|
, capacity { 1024 }
|
||||||
|
, freeList { NULL_NODE }
|
||||||
|
{
|
||||||
|
// Build initial tree
|
||||||
|
nodes = new Node[1024];
|
||||||
|
|
||||||
|
addToFreeList(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SHAABBTree::~SHAABBTree() noexcept
|
||||||
|
{
|
||||||
|
delete[] nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHAABBTree::Node::Node() noexcept
|
||||||
|
: id { MAX_EID, std::numeric_limits<uint32_t>::max() }
|
||||||
|
, parent { NULL_NODE }
|
||||||
|
, left { NULL_NODE }
|
||||||
|
, right { NULL_NODE }
|
||||||
|
, height { NULL_NODE }
|
||||||
|
{}
|
||||||
|
|
||||||
|
SHAABBTree::Node::Node(const Node& rhs) noexcept
|
||||||
|
: AABB { rhs.AABB }
|
||||||
|
, id { rhs.id }
|
||||||
|
, next { rhs.next }
|
||||||
|
, left { rhs.left }
|
||||||
|
, right { rhs.right }
|
||||||
|
, height { rhs.height }
|
||||||
|
{}
|
||||||
|
|
||||||
|
SHAABBTree::Node::Node(Node&& rhs) noexcept
|
||||||
|
: AABB { rhs.AABB }
|
||||||
|
, id { rhs.id }
|
||||||
|
, next { rhs.next }
|
||||||
|
, left { rhs.left }
|
||||||
|
, right { rhs.right }
|
||||||
|
, height { rhs.height }
|
||||||
|
{}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Operator Overload Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHAABBTree::Node& SHAABBTree::Node::operator=(const Node& rhs) noexcept
|
||||||
|
{
|
||||||
|
if (this == &rhs)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
AABB = rhs.AABB;
|
||||||
|
id = rhs.id;
|
||||||
|
parent = rhs.parent;
|
||||||
|
next = rhs.next;
|
||||||
|
left = rhs.left;
|
||||||
|
right = rhs.right;
|
||||||
|
height = rhs.height;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHAABBTree::Node& SHAABBTree::Node::operator=(Node&& rhs) noexcept
|
||||||
|
{
|
||||||
|
AABB = std::move(rhs.AABB);
|
||||||
|
id = std::move(rhs.id);
|
||||||
|
parent = rhs.parent;
|
||||||
|
next = rhs.next;
|
||||||
|
left = rhs.left;
|
||||||
|
right = rhs.right;
|
||||||
|
height = rhs.height;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Getter Functions Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
const std::vector<SHBox>& SHAABBTree::GetAABBs() const noexcept
|
||||||
|
{
|
||||||
|
static std::vector<SHBox> aabbs;
|
||||||
|
static std::stack<int32_t> nodeIndices;
|
||||||
|
|
||||||
|
aabbs.clear();
|
||||||
|
|
||||||
|
nodeIndices.push(root);
|
||||||
|
while (!nodeIndices.empty())
|
||||||
|
{
|
||||||
|
// Pop the top node
|
||||||
|
const int INDEX = nodeIndices.top();
|
||||||
|
nodeIndices.pop();
|
||||||
|
|
||||||
|
// Skip null nodes
|
||||||
|
if (INDEX == NULL_NODE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Node& CURRENT_NODE = nodes[INDEX];
|
||||||
|
|
||||||
|
aabbs.emplace_back(CURRENT_NODE.AABB);
|
||||||
|
|
||||||
|
if (!isLeaf(INDEX))
|
||||||
|
{
|
||||||
|
nodeIndices.push(CURRENT_NODE.left);
|
||||||
|
nodeIndices.push(CURRENT_NODE.right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aabbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Public Member Functions Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void SHAABBTree::Insert(SHCollisionShapeID id, const SHBox& AABB)
|
||||||
|
{
|
||||||
|
const int32_t NEW_INDEX = allocateNode();
|
||||||
|
|
||||||
|
if (!nodeMap.emplace(id, NEW_INDEX).second)
|
||||||
|
{
|
||||||
|
// Attempted to add a duplicate node
|
||||||
|
freeNode(NEW_INDEX);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node& newNode = nodes[NEW_INDEX];
|
||||||
|
newNode.AABB = AABB;
|
||||||
|
newNode.id = id;
|
||||||
|
newNode.height = 0;
|
||||||
|
|
||||||
|
// Fatten the AABB
|
||||||
|
const SHVec3 EXTENSION{ AABB_EXTENSION };
|
||||||
|
|
||||||
|
const SHVec3 newMin = newNode.AABB.GetMin() - EXTENSION;
|
||||||
|
const SHVec3 newMax = newNode.AABB.GetMax() + EXTENSION;
|
||||||
|
|
||||||
|
newNode.AABB.SetMin(newMin);
|
||||||
|
newNode.AABB.SetMax(newMax);
|
||||||
|
|
||||||
|
insertLeaf(NEW_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHAABBTree::Update(SHCollisionShapeID id, const SHBox& AABB)
|
||||||
|
{
|
||||||
|
// Get node index
|
||||||
|
const int32_t INDEX_TO_UPDATE = nodeMap[id];
|
||||||
|
|
||||||
|
Node& nodeToUpdate = nodes[INDEX_TO_UPDATE];
|
||||||
|
|
||||||
|
// If new AABB has not moved enough, skip.
|
||||||
|
if (nodeToUpdate.AABB.Contains(AABB))
|
||||||
|
return;
|
||||||
|
|
||||||
|
removeLeaf(INDEX_TO_UPDATE);
|
||||||
|
|
||||||
|
nodeToUpdate.AABB = AABB;
|
||||||
|
|
||||||
|
// Fatten the AABB
|
||||||
|
const SHVec3 EXTENSION{ AABB_EXTENSION };
|
||||||
|
|
||||||
|
const SHVec3 newMin = nodeToUpdate.AABB.GetMin() - EXTENSION;
|
||||||
|
const SHVec3 newMax = nodeToUpdate.AABB.GetMax() + EXTENSION;
|
||||||
|
|
||||||
|
nodeToUpdate.AABB.SetMin(newMin);
|
||||||
|
nodeToUpdate.AABB.SetMax(newMax);
|
||||||
|
|
||||||
|
insertLeaf(INDEX_TO_UPDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHAABBTree::Remove(SHCollisionShapeID id) noexcept
|
||||||
|
{
|
||||||
|
// Get node index
|
||||||
|
const int32_t INDEX_TO_REMOVE = nodeMap[id];
|
||||||
|
|
||||||
|
removeLeaf(INDEX_TO_REMOVE);
|
||||||
|
freeNode(INDEX_TO_REMOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<SHCollisionShapeID>& SHAABBTree::Query(SHCollisionShapeID id, const SHBox& AABB) const noexcept
|
||||||
|
{
|
||||||
|
static std::vector<SHCollisionShapeID> potentialCollisions;
|
||||||
|
static std::stack<int32_t> nodeIndices;
|
||||||
|
|
||||||
|
potentialCollisions.clear();
|
||||||
|
|
||||||
|
// We use this to ignore shapes on the same entity
|
||||||
|
const EntityID EID = id.GetEntityID();
|
||||||
|
|
||||||
|
nodeIndices.push(root);
|
||||||
|
while (!nodeIndices.empty())
|
||||||
|
{
|
||||||
|
const int32_t INDEX = nodeIndices.top();
|
||||||
|
nodeIndices.pop();
|
||||||
|
|
||||||
|
if (INDEX == NULL_NODE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Node& NODE = nodes[INDEX];
|
||||||
|
if (!SHBox::Intersect(AABB, NODE.AABB))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Avoid checking against shapes of the same composite collider (and itself)
|
||||||
|
if (isLeaf(INDEX) && NODE.id.GetEntityID() != EID)
|
||||||
|
{
|
||||||
|
potentialCollisions.emplace_back(NODE.id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeIndices.push(NODE.left);
|
||||||
|
nodeIndices.push(NODE.right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return potentialCollisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<SHCollisionShapeID>& SHAABBTree::Query(const SHRay& ray, float distance) const noexcept
|
||||||
|
{
|
||||||
|
static std::vector<SHCollisionShapeID> potentialHits;
|
||||||
|
|
||||||
|
potentialHits.clear();
|
||||||
|
|
||||||
|
return potentialHits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Private Member Functions Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
bool SHAABBTree::isLeaf(int32_t index) const noexcept
|
||||||
|
{
|
||||||
|
const Node& NODE = nodes[index];
|
||||||
|
return NODE.left == NULL_NODE && NODE.right == NULL_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t SHAABBTree::allocateNode()
|
||||||
|
{
|
||||||
|
if (freeList == NULL_NODE)
|
||||||
|
{
|
||||||
|
// No more free nodes available, so we need to resize the tree for more nodes
|
||||||
|
capacity *= 2;
|
||||||
|
|
||||||
|
Node* newNodes = new Node[capacity];
|
||||||
|
|
||||||
|
// Copy all the nodes manually. I do this instead of memcpy to guarantee it copies properly.
|
||||||
|
for (int32_t i = 0; i < nodeCount; ++i)
|
||||||
|
{
|
||||||
|
newNodes[i].AABB = nodes[i].AABB;
|
||||||
|
newNodes[i].id = nodes[i].id;
|
||||||
|
newNodes[i].parent = nodes[i].parent;
|
||||||
|
newNodes[i].left = nodes[i].left;
|
||||||
|
newNodes[i].right = nodes[i].right;
|
||||||
|
newNodes[i].height = nodes[i].height;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] nodes;
|
||||||
|
nodes = newNodes;
|
||||||
|
|
||||||
|
addToFreeList(nodeCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t FREE_NODE = freeList;
|
||||||
|
freeList = nodes[FREE_NODE].next;
|
||||||
|
|
||||||
|
// Set node to default
|
||||||
|
Node& newNode = nodes[FREE_NODE];
|
||||||
|
newNode.parent = NULL_NODE;
|
||||||
|
newNode.left = NULL_NODE;
|
||||||
|
newNode.right = NULL_NODE;
|
||||||
|
newNode.height = NULL_NODE;
|
||||||
|
|
||||||
|
++nodeCount;
|
||||||
|
return FREE_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHAABBTree::freeNode(int32_t index) noexcept
|
||||||
|
{
|
||||||
|
SHASSERT(index >= 0 && index < capacity, "Trying to free an invalid AABB Tree node!")
|
||||||
|
|
||||||
|
nodes[index].next = NULL_NODE;
|
||||||
|
nodes[index].height = NULL_NODE;
|
||||||
|
|
||||||
|
// Put it back on the free list
|
||||||
|
freeList = index;
|
||||||
|
|
||||||
|
--nodeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHAABBTree::insertLeaf(int32_t index)
|
||||||
|
{
|
||||||
|
// If there is no root, the first insert must make the root
|
||||||
|
if (root == NULL_NODE)
|
||||||
|
{
|
||||||
|
root = index;
|
||||||
|
nodes[root].parent = NULL_NODE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find best sibling for new leaf
|
||||||
|
// Utilise Surface Area Heuristic
|
||||||
|
const SHBox& LEAF_AABB = nodes[index].AABB;
|
||||||
|
|
||||||
|
uint32_t searchIndex = root;
|
||||||
|
while (!isLeaf(searchIndex))
|
||||||
|
{
|
||||||
|
const SHBox COMBINED_AABB = SHBox::Combine(LEAF_AABB, nodes[searchIndex].AABB);
|
||||||
|
const float COMBINED_AREA = COMBINED_AABB.SurfaceArea();
|
||||||
|
|
||||||
|
const float INHERITED_COST = 2.0f * (COMBINED_AREA - nodes[searchIndex].AABB.SurfaceArea());
|
||||||
|
|
||||||
|
const int32_t LEFT_INDEX = nodes[searchIndex].left;
|
||||||
|
const int32_t RIGHT_INDEX = nodes[searchIndex].right;
|
||||||
|
|
||||||
|
float leftCost = 0.0f;
|
||||||
|
float rightCost = 0.0f;
|
||||||
|
|
||||||
|
const float LEFT_COMBINED_AREA = SHBox::Combine(LEAF_AABB, nodes[LEFT_INDEX].AABB).SurfaceArea();
|
||||||
|
const float RIGHT_COMBINED_AREA = SHBox::Combine(LEAF_AABB, nodes[RIGHT_INDEX].AABB).SurfaceArea();
|
||||||
|
|
||||||
|
// Compute cost for descending into the left
|
||||||
|
if (isLeaf(LEFT_INDEX))
|
||||||
|
leftCost = LEFT_COMBINED_AREA + INHERITED_COST;
|
||||||
|
else
|
||||||
|
leftCost = LEFT_COMBINED_AREA - nodes[LEFT_INDEX].AABB.SurfaceArea() + INHERITED_COST;
|
||||||
|
|
||||||
|
// Compute cost for descending into the right
|
||||||
|
if (isLeaf(RIGHT_INDEX))
|
||||||
|
rightCost = RIGHT_COMBINED_AREA + INHERITED_COST;
|
||||||
|
else
|
||||||
|
rightCost = RIGHT_COMBINED_AREA - nodes[RIGHT_INDEX].AABB.SurfaceArea() + INHERITED_COST;
|
||||||
|
|
||||||
|
// Early out
|
||||||
|
const float BRANCH_COST = 2.0f * COMBINED_AREA;
|
||||||
|
if (BRANCH_COST < leftCost && BRANCH_COST < rightCost)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Traverse
|
||||||
|
searchIndex = leftCost < rightCost ? LEFT_INDEX : RIGHT_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t BEST_SIBLING = searchIndex;
|
||||||
|
|
||||||
|
// Create a new parent for the leaf
|
||||||
|
const int32_t OLD_PARENT = nodes[BEST_SIBLING].parent;
|
||||||
|
const int32_t NEW_PARENT = allocateNode();
|
||||||
|
|
||||||
|
Node& newParent = nodes[NEW_PARENT];
|
||||||
|
newParent.parent = OLD_PARENT;
|
||||||
|
newParent.id = SHCollisionShapeID{ MAX_EID, std::numeric_limits<uint32_t>::max() };
|
||||||
|
newParent.AABB = SHBox::Combine(LEAF_AABB, nodes[BEST_SIBLING].AABB);
|
||||||
|
newParent.height = nodes[BEST_SIBLING].height + 1;
|
||||||
|
|
||||||
|
newParent.left = BEST_SIBLING;
|
||||||
|
newParent.right = index;
|
||||||
|
|
||||||
|
nodes[BEST_SIBLING].parent = NEW_PARENT;
|
||||||
|
nodes[index].parent = NEW_PARENT;
|
||||||
|
|
||||||
|
// If sibling was the root
|
||||||
|
if (OLD_PARENT == NULL_NODE)
|
||||||
|
root = NEW_PARENT;
|
||||||
|
else
|
||||||
|
(nodes[OLD_PARENT].left == BEST_SIBLING ? nodes[OLD_PARENT].left : nodes[OLD_PARENT].right) = NEW_PARENT;
|
||||||
|
|
||||||
|
syncHierarchy(NEW_PARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHAABBTree::removeLeaf(int32_t index)
|
||||||
|
{
|
||||||
|
if (index == root)
|
||||||
|
{
|
||||||
|
root = NULL_NODE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t PARENT = nodes[index].parent;
|
||||||
|
const int32_t GRANDPARENT = nodes[PARENT].parent;
|
||||||
|
const int32_t SIBLING = nodes[PARENT].left == index ? nodes[PARENT].right : nodes[PARENT].left;
|
||||||
|
|
||||||
|
if (GRANDPARENT != NULL_NODE)
|
||||||
|
{
|
||||||
|
// Replace parent with sibling
|
||||||
|
(nodes[GRANDPARENT].left == PARENT ? nodes[GRANDPARENT].left : nodes[GRANDPARENT].right) = SIBLING;
|
||||||
|
nodes[SIBLING].parent = GRANDPARENT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Parent was root
|
||||||
|
root = SIBLING;
|
||||||
|
nodes[SIBLING].parent = NULL_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeNode(PARENT);
|
||||||
|
syncHierarchy(GRANDPARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHAABBTree::syncHierarchy(int32_t index)
|
||||||
|
{
|
||||||
|
while (index != NULL_NODE)
|
||||||
|
{
|
||||||
|
index = balance(index);
|
||||||
|
|
||||||
|
const int32_t LEFT_INDEX = nodes[index].left;
|
||||||
|
const Node& LEFT_NODE = nodes[LEFT_INDEX];
|
||||||
|
|
||||||
|
const int32_t RIGHT_INDEX = nodes[index].right;
|
||||||
|
const Node& RIGHT_NODE = nodes[RIGHT_INDEX];
|
||||||
|
|
||||||
|
nodes[index].height = 1 + SHMath::Max(LEFT_NODE.height, RIGHT_NODE.height);
|
||||||
|
nodes[index].AABB = SHBox::Combine(LEFT_NODE.AABB, RIGHT_NODE.AABB);
|
||||||
|
|
||||||
|
// Sync up to the root
|
||||||
|
index = nodes[index].parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t SHAABBTree::balance(int32_t index)
|
||||||
|
{
|
||||||
|
if (isLeaf(index) || nodes[index].height == 1)
|
||||||
|
return index;
|
||||||
|
|
||||||
|
Node& nodeA = nodes[index];
|
||||||
|
|
||||||
|
const int32_t LEFT = nodeA.left;
|
||||||
|
const int32_t RIGHT = nodeA.right;
|
||||||
|
|
||||||
|
const int32_t DIFF = nodes[RIGHT].height - nodes[LEFT].height;
|
||||||
|
|
||||||
|
if (DIFF > 1)
|
||||||
|
return rotateLeft(index);
|
||||||
|
|
||||||
|
if (DIFF < -1)
|
||||||
|
return rotateRight(index);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t SHAABBTree::rotateLeft(int32_t index)
|
||||||
|
{
|
||||||
|
/****************************
|
||||||
|
A C
|
||||||
|
/ \ / \
|
||||||
|
B C --> A F/G
|
||||||
|
/ \ / \
|
||||||
|
F G B G/F
|
||||||
|
****************************/
|
||||||
|
|
||||||
|
// Promote C
|
||||||
|
|
||||||
|
Node& nodeA = nodes[index];
|
||||||
|
|
||||||
|
const int32_t B = nodeA.left;
|
||||||
|
const int32_t C = nodeA.right;
|
||||||
|
|
||||||
|
Node& nodeB = nodes[B];
|
||||||
|
Node& nodeC = nodes[C];
|
||||||
|
|
||||||
|
const int32_t F = nodeC.left;
|
||||||
|
const int32_t G = nodeC.right;
|
||||||
|
|
||||||
|
Node& nodeF = nodes[F];
|
||||||
|
Node& nodeG = nodes[G];
|
||||||
|
|
||||||
|
if (nodeA.parent != NULL_NODE)
|
||||||
|
(nodes[nodeA.parent].left == index ? nodes[nodeA.parent].left : nodes[nodeA.parent].right) = C;
|
||||||
|
else
|
||||||
|
root = C;
|
||||||
|
|
||||||
|
nodeC.left = index;
|
||||||
|
nodeC.parent = nodeA.parent;
|
||||||
|
nodeA.parent = C;
|
||||||
|
|
||||||
|
if (nodeF.height > nodeG.height)
|
||||||
|
{
|
||||||
|
nodeC.right = F;
|
||||||
|
nodeA.right = G;
|
||||||
|
nodeG.parent = index;
|
||||||
|
|
||||||
|
nodeA.AABB = SHBox::Combine(nodeB.AABB, nodeG.AABB);
|
||||||
|
nodeC.AABB = SHBox::Combine(nodeA.AABB, nodeF.AABB);
|
||||||
|
|
||||||
|
nodeA.height = 1 + SHMath::Max(nodeB.height, nodeG.height);
|
||||||
|
nodeC.height = 1 + SHMath::Max(nodeA.height, nodeF.height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeC.right = G;
|
||||||
|
nodeA.right = F;
|
||||||
|
nodeF.parent = index;
|
||||||
|
|
||||||
|
nodeA.AABB = SHBox::Combine(nodeB.AABB, nodeF.AABB);
|
||||||
|
nodeC.AABB = SHBox::Combine(nodeA.AABB, nodeG.AABB);
|
||||||
|
|
||||||
|
nodeA.height = 1 + SHMath::Max(nodeB.height, nodeF.height);
|
||||||
|
nodeC.height = 1 + SHMath::Max(nodeA.height, nodeG.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return C;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t SHAABBTree::rotateRight(int32_t index)
|
||||||
|
{
|
||||||
|
/*************************
|
||||||
|
A B
|
||||||
|
/ \ / \
|
||||||
|
B C --> D/E A
|
||||||
|
/ \ / \
|
||||||
|
D E E/D C
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
// Promote B
|
||||||
|
|
||||||
|
Node& nodeA = nodes[index];
|
||||||
|
|
||||||
|
const int32_t B = nodeA.left;
|
||||||
|
const int32_t C = nodeA.right;
|
||||||
|
|
||||||
|
Node& nodeB = nodes[B];
|
||||||
|
Node& nodeC = nodes[C];
|
||||||
|
|
||||||
|
const int32_t D = nodeB.left;
|
||||||
|
const int32_t E = nodeB.right;
|
||||||
|
|
||||||
|
Node& nodeD = nodes[D];
|
||||||
|
Node& nodeE = nodes[E];
|
||||||
|
|
||||||
|
if (nodeA.parent != NULL_NODE)
|
||||||
|
(nodes[nodeA.parent].left == index ? nodes[nodeA.parent].left : nodes[nodeA.parent].right) = B;
|
||||||
|
else
|
||||||
|
root = B;
|
||||||
|
|
||||||
|
nodeB.right = index;
|
||||||
|
nodeB.parent = nodeA.parent;
|
||||||
|
nodeA.parent = B;
|
||||||
|
|
||||||
|
if (nodeD.height > nodeE.height)
|
||||||
|
{
|
||||||
|
nodeB.left = D;
|
||||||
|
nodeA.left = E;
|
||||||
|
nodeE.parent = index;
|
||||||
|
|
||||||
|
nodeA.AABB = SHBox::Combine(nodeC.AABB, nodeE.AABB);
|
||||||
|
nodeB.AABB = SHBox::Combine(nodeA.AABB, nodeD.AABB);
|
||||||
|
|
||||||
|
nodeA.height = 1 + SHMath::Max(nodeC.height, nodeE.height);
|
||||||
|
nodeB.height = 1 + SHMath::Max(nodeA.height, nodeD.height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeB.left = E;
|
||||||
|
nodeA.left = D;
|
||||||
|
nodeD.parent = index;
|
||||||
|
|
||||||
|
nodeA.AABB = SHBox::Combine(nodeC.AABB, nodeD.AABB);
|
||||||
|
nodeB.AABB = SHBox::Combine(nodeA.AABB, nodeE.AABB);
|
||||||
|
|
||||||
|
nodeA.height = 1 + SHMath::Max(nodeC.height, nodeD.height);
|
||||||
|
nodeB.height = 1 + SHMath::Max(nodeA.height, nodeE.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return B;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHAABBTree::addToFreeList(int32_t index) noexcept
|
||||||
|
{
|
||||||
|
for (int32_t i = index; i < capacity; ++i)
|
||||||
|
{
|
||||||
|
nodes[i].next = i + 1;
|
||||||
|
nodes[i].height = NULL_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[capacity - 1].next = NULL_NODE;
|
||||||
|
nodes[capacity - 1].height = NULL_NODE;
|
||||||
|
|
||||||
|
freeList = index;
|
||||||
|
}
|
||||||
|
} // namespace SHADE
|
|
@ -0,0 +1,150 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHDynamicAABBTree.h
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Interface for a Dynamic AABB Tree for broadphase collision detection.
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
|
#include "Physics/Collision/CollisionShapes/SHCollisionShape.h"
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates a dynamic AABB Tree for collision detection.
|
||||||
|
*/
|
||||||
|
class SH_API SHAABBTree
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Data Members */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static constexpr int NULL_NODE = -1;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Constructors & Destructor */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHAABBTree () noexcept;
|
||||||
|
~SHAABBTree () noexcept;
|
||||||
|
|
||||||
|
SHAABBTree(const SHAABBTree& other) = delete;
|
||||||
|
SHAABBTree(SHAABBTree&& other) noexcept = delete;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Operator Overloads */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHAABBTree& operator=(const SHAABBTree& other) = delete;
|
||||||
|
SHAABBTree& operator=(SHAABBTree&& other) noexcept = delete;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Getter Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
[[nodiscard]] const std::vector<SHBox>& GetAABBs () const noexcept;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Member Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void Insert (SHCollisionShapeID id, const SHBox& AABB);
|
||||||
|
void Update (SHCollisionShapeID id, const SHBox& AABB);
|
||||||
|
void Remove (SHCollisionShapeID id) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] const std::vector<SHCollisionShapeID>& Query(SHCollisionShapeID id, const SHBox& AABB) const noexcept;
|
||||||
|
[[nodiscard]] const std::vector<SHCollisionShapeID>& Query(const SHRay& ray, float distance) const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*-------------------------------------------------------------------------------*/
|
||||||
|
/* Constructors & Destructor */
|
||||||
|
/*-------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
Node () noexcept;
|
||||||
|
Node (const Node& rhs) noexcept;
|
||||||
|
Node (Node&& rhs) noexcept;
|
||||||
|
|
||||||
|
~Node () noexcept = default;
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------------*/
|
||||||
|
/* Operator Overloads */
|
||||||
|
/*-------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
Node& operator=(const Node& rhs) noexcept;
|
||||||
|
Node& operator=(Node&& rhs) noexcept;
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------------*/
|
||||||
|
/* Data Members */
|
||||||
|
/*-------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHBox AABB;
|
||||||
|
SHCollisionShapeID id; // Used to lookup the collision shape & entity for culling against itself
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int32_t parent;
|
||||||
|
int32_t next;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int32_t left;
|
||||||
|
int32_t right;
|
||||||
|
int32_t height; // Leaves have a height of 0. Free nodes have a height of -1
|
||||||
|
};
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Data Members */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static constexpr float AABB_EXTENSION = 0.2f;
|
||||||
|
|
||||||
|
// For quick access
|
||||||
|
std::unordered_map<SHCollisionShapeID, int32_t, SHCollisionShapeIDHash> nodeMap;
|
||||||
|
|
||||||
|
int32_t root;
|
||||||
|
Node* nodes; // Dynamically allocated array of nodes. I use dynamic allocation as in the past, using a vector causes weird issues.
|
||||||
|
int32_t nodeCount;
|
||||||
|
int32_t capacity; // Used for resizing the tree.
|
||||||
|
int32_t freeList; // Stores the next available node on the free list.
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Member Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
bool isLeaf (int32_t index) const noexcept;
|
||||||
|
|
||||||
|
int32_t allocateNode ();
|
||||||
|
void freeNode (int32_t index) noexcept;
|
||||||
|
|
||||||
|
void insertLeaf (int32_t index);
|
||||||
|
void removeLeaf (int32_t index);
|
||||||
|
void syncHierarchy (int32_t index);
|
||||||
|
int32_t balance (int32_t index);
|
||||||
|
int32_t rotateLeft (int32_t index);
|
||||||
|
int32_t rotateRight (int32_t index);
|
||||||
|
|
||||||
|
void addToFreeList (int32_t index) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
#include "Physics/Collision/CollisionTags/SHCollisionTags.h"
|
#include "Physics/Collision/CollisionTags/SHCollisionTags.h"
|
||||||
#include "Physics/Collision/SHPhysicsMaterial.h"
|
#include "Physics/Collision/SHPhysicsMaterial.h"
|
||||||
#include "SHCollisionShapeID.h"
|
#include "SHCollisionShapeID.h"
|
||||||
|
#include "Math/Geometry/SHBox.h"
|
||||||
#include "Math/Transform/SHTransform.h"
|
#include "Math/Transform/SHTransform.h"
|
||||||
|
|
||||||
namespace SHADE
|
namespace SHADE
|
||||||
|
@ -33,8 +34,10 @@ namespace SHADE
|
||||||
/* Friends */
|
/* Friends */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
friend class SHCollider;
|
||||||
friend class SHColliderComponent;
|
friend class SHColliderComponent;
|
||||||
friend class SHCollisionShapeFactory;
|
friend class SHCollisionShapeFactory;
|
||||||
|
friend class SHPhysicsWorld;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
@ -120,6 +123,7 @@ namespace SHADE
|
||||||
virtual void ComputeTransforms () noexcept = 0;
|
virtual void ComputeTransforms () noexcept = 0;
|
||||||
[[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0;
|
[[nodiscard]] virtual SHMatrix GetInertiaTensor (float mass) const noexcept = 0;
|
||||||
[[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0;
|
[[nodiscard]] virtual SHMatrix ComputeWorldTransform () const noexcept = 0;
|
||||||
|
[[nodiscard]] virtual SHBox ComputeAABB () const noexcept = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
@ -136,7 +140,7 @@ namespace SHADE
|
||||||
// Needed for conversion to euler angles
|
// Needed for conversion to euler angles
|
||||||
SHVec3 rotationOffset;
|
SHVec3 rotationOffset;
|
||||||
|
|
||||||
uint8_t flags; // 0 0 wasColliding isColliding trigger capsule sphere box
|
uint8_t flags; // 0 0 0 isColliding trigger capsule sphere box
|
||||||
SHCollisionTag* collisionTag;
|
SHCollisionTag* collisionTag;
|
||||||
|
|
||||||
RTTR_ENABLE()
|
RTTR_ENABLE()
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
#include "ECS_Base/Entity/SHEntity.h"
|
#include "ECS_Base/Entity/SHEntity.h"
|
||||||
|
|
||||||
namespace SHADE
|
namespace SHADE
|
||||||
|
@ -55,11 +56,14 @@ namespace SHADE
|
||||||
SHCollisionShapeID& operator=(const SHCollisionShapeID& rhs) noexcept;
|
SHCollisionShapeID& operator=(const SHCollisionShapeID& rhs) noexcept;
|
||||||
SHCollisionShapeID& operator=(SHCollisionShapeID&& rhs) noexcept;
|
SHCollisionShapeID& operator=(SHCollisionShapeID&& rhs) noexcept;
|
||||||
|
|
||||||
|
bool operator==(const SHCollisionShapeID& rhs) const;
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Member Functions */
|
/* Getter Functions */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
bool operator==(const SHCollisionShapeID& rhs) const;
|
[[nodiscard]] EntityID GetEntityID () const noexcept;
|
||||||
|
[[nodiscard]] uint32_t GetShapeIndex () const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
@ -69,8 +73,8 @@ namespace SHADE
|
||||||
struct IDs
|
struct IDs
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EntityID entityID = MAX_EID;
|
EntityID entityID = MAX_EID;
|
||||||
uint32_t shapeID = std::numeric_limits<uint32_t>::max();
|
uint32_t shapeIndex = std::numeric_limits<uint32_t>::max();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +88,7 @@ namespace SHADE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
* Encapsulates a functor to hash a CollisionShapeKey
|
* Encapsulates a functor to hash a CollisionShapeID
|
||||||
*/
|
*/
|
||||||
struct SHCollisionShapeIDHash
|
struct SHCollisionShapeIDHash
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,11 +25,11 @@ namespace SHADE
|
||||||
{}
|
{}
|
||||||
|
|
||||||
inline SHCollisionShapeID::SHCollisionShapeID(const SHCollisionShapeID& rhs) noexcept
|
inline SHCollisionShapeID::SHCollisionShapeID(const SHCollisionShapeID& rhs) noexcept
|
||||||
: ids { rhs.ids.entityID, rhs.ids.shapeID }
|
: ids { rhs.ids.entityID, rhs.ids.shapeIndex }
|
||||||
{}
|
{}
|
||||||
|
|
||||||
inline SHCollisionShapeID::SHCollisionShapeID(SHCollisionShapeID&& rhs) noexcept
|
inline SHCollisionShapeID::SHCollisionShapeID(SHCollisionShapeID&& rhs) noexcept
|
||||||
: ids { rhs.ids.entityID, rhs.ids.shapeID }
|
: ids { rhs.ids.entityID, rhs.ids.shapeIndex }
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,6 +61,20 @@ namespace SHADE
|
||||||
|
|
||||||
inline std::size_t SHCollisionShapeIDHash::operator()(const SHCollisionShapeID& id) const
|
inline std::size_t SHCollisionShapeIDHash::operator()(const SHCollisionShapeID& id) const
|
||||||
{
|
{
|
||||||
return std::hash<uint64_t>()(id.value);
|
return std::hash<uint64_t>{}(id.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Getter Function Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
inline EntityID SHCollisionShapeID::GetEntityID() const noexcept
|
||||||
|
{
|
||||||
|
return ids.entityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t SHCollisionShapeID::GetShapeIndex() const noexcept
|
||||||
|
{
|
||||||
|
return ids.shapeIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,7 +220,12 @@ namespace SHADE
|
||||||
Center
|
Center
|
||||||
, ROTATION
|
, ROTATION
|
||||||
, SCALE
|
, SCALE
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SHBox SHSphereCollisionShape::ComputeAABB() const noexcept
|
||||||
|
{
|
||||||
|
return SHBox{ Center, SHVec3{ Radius } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ namespace SHADE
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
friend class SHCollider;
|
friend class SHCollider;
|
||||||
|
friend class SHCollision;
|
||||||
friend class SHCompositeCollider;
|
friend class SHCompositeCollider;
|
||||||
friend class SHCollisionShapeFactory;
|
friend class SHCollisionShapeFactory;
|
||||||
|
|
||||||
|
@ -127,6 +128,8 @@ namespace SHADE
|
||||||
|
|
||||||
[[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override;
|
[[nodiscard]] SHMatrix ComputeWorldTransform () const noexcept override;
|
||||||
|
|
||||||
|
[[nodiscard]] SHBox ComputeAABB () const noexcept override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Data Members */
|
/* Data Members */
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHCollisionID.h
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Interface for Collision Information for Collision & Triggers.
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
|
#include "SHCollisionID.h"
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
enum class SHCollisionState
|
||||||
|
{
|
||||||
|
ENTER
|
||||||
|
, STAY
|
||||||
|
, EXIT
|
||||||
|
|
||||||
|
, TOTAL
|
||||||
|
, INVALID = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates the event for an intersection between two collision shapes that do not
|
||||||
|
* have physical resolution.
|
||||||
|
*/
|
||||||
|
struct SH_API SHTriggerEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SHCollisionID info;
|
||||||
|
SHCollisionState state;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates the event for an intersection between two collision shapes that do
|
||||||
|
* have physical resolution.
|
||||||
|
*/
|
||||||
|
struct SH_API SHCollisionEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr int MAX_NUM_CONTACTS = 4;
|
||||||
|
|
||||||
|
SHCollisionID info;
|
||||||
|
SHCollisionState state;
|
||||||
|
SHVec3 normal;
|
||||||
|
SHVec3 contactPoints[MAX_NUM_CONTACTS];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADE
|
|
@ -0,0 +1,152 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHCollisionInfo.cpp
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Implementation for Collision Info.
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#include <SHpch.h>
|
||||||
|
|
||||||
|
// Primary Header
|
||||||
|
#include "SHCollisionID.h"
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
|
#include "Physics/Collision/SHCollider.h"
|
||||||
|
#include "Physics/Interface/SHColliderComponent.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Constructors & Destructor Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHCollisionID::SHCollisionID() noexcept
|
||||||
|
{
|
||||||
|
ids[ENTITY_A] = MAX_EID;
|
||||||
|
ids[ENTITY_B] = MAX_EID;
|
||||||
|
ids[SHAPE_INDEX_A] = std::numeric_limits<uint32_t>::max();
|
||||||
|
ids[SHAPE_INDEX_B] = std::numeric_limits<uint32_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
SHCollisionID::SHCollisionID(const SHCollisionID& rhs) noexcept
|
||||||
|
{
|
||||||
|
value[0] = rhs.value[0];
|
||||||
|
value[1] = rhs.value[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
SHCollisionID::SHCollisionID(SHCollisionID&& rhs) noexcept
|
||||||
|
{
|
||||||
|
value[0] = rhs.value[0];
|
||||||
|
value[1] = rhs.value[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Operator Overload Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHCollisionID& SHCollisionID::operator=(const SHCollisionID& rhs) noexcept
|
||||||
|
{
|
||||||
|
if (this == &rhs)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
value[0] = rhs.value[0];
|
||||||
|
value[1] = rhs.value[1];
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHCollisionID& SHCollisionID::operator=(SHCollisionID&& rhs) noexcept
|
||||||
|
{
|
||||||
|
value[0] = rhs.value[0];
|
||||||
|
value[1] = rhs.value[1];
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SHCollisionID::operator==(const SHCollisionID& rhs) const
|
||||||
|
{
|
||||||
|
// When checking for equal, check both ways.
|
||||||
|
// Exact Match (A, idxA, B, idxB)
|
||||||
|
const bool EXACT_MATCH = value[0] == rhs.value[0] && value[1] == rhs.value[1];
|
||||||
|
|
||||||
|
// Flipped Match: (B, idxB, A, idxA)
|
||||||
|
const bool FLIPPED_MATCH = value[0] == rhs.value[1] && value[1] == rhs.value[0];
|
||||||
|
|
||||||
|
return EXACT_MATCH || FLIPPED_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Getter Function Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
EntityID SHCollisionID::GetEntityA() const noexcept
|
||||||
|
{
|
||||||
|
return ids[ENTITY_A];
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityID SHCollisionID::GetEntityB() const noexcept
|
||||||
|
{
|
||||||
|
return ids[ENTITY_B];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SHCollisionID::GetShapeIndexA() const noexcept
|
||||||
|
{
|
||||||
|
return ids[SHAPE_INDEX_A];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SHCollisionID::GetShapeIndexB() const noexcept
|
||||||
|
{
|
||||||
|
return ids[SHAPE_INDEX_B];
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHRigidBodyComponent* SHCollisionID::GetRigidBodyA() const noexcept
|
||||||
|
{
|
||||||
|
return SHComponentManager::GetComponent_s<SHRigidBodyComponent>(ids[ENTITY_A]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHRigidBodyComponent* SHCollisionID::GetRigidBodyB() const noexcept
|
||||||
|
{
|
||||||
|
return SHComponentManager::GetComponent_s<SHRigidBodyComponent>(ids[ENTITY_B]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHCollisionShape* SHCollisionID::GetCollisionShapeA() const noexcept
|
||||||
|
{
|
||||||
|
const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent<SHColliderComponent>(ids[ENTITY_A]);
|
||||||
|
return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[SHAPE_INDEX_A]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHCollisionShape* SHCollisionID::GetCollisionShapeB() const noexcept
|
||||||
|
{
|
||||||
|
const auto* COLLIDER_COMPONENT = SHComponentManager::GetComponent<SHColliderComponent>(ids[ENTITY_B]);
|
||||||
|
return COLLIDER_COMPONENT->GetCollider()->GetCollisionShape(ids[SHAPE_INDEX_B]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Setter Function Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void SHCollisionID::SetEntityA(EntityID entityID) noexcept
|
||||||
|
{
|
||||||
|
ids[ENTITY_A] = entityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHCollisionID::SetEntityB(EntityID entityID) noexcept
|
||||||
|
{
|
||||||
|
ids[ENTITY_B] = entityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHCollisionID::SetCollisionShapeA(uint32_t shapeIndexA) noexcept
|
||||||
|
{
|
||||||
|
ids[SHAPE_INDEX_A] = shapeIndexA;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHCollisionID::SetCollisionShapeB(uint32_t shapeIndexB) noexcept
|
||||||
|
{
|
||||||
|
ids[SHAPE_INDEX_B] = shapeIndexB;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SHADE
|
|
@ -0,0 +1,121 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHCollisionID.h
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Interface for Collision Information for Collision & Triggers.
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
|
#include "Physics/Interface/SHColliderComponent.h"
|
||||||
|
#include "Physics/Interface/SHRigidBodyComponent.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Forward Declarations */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
struct SHCollisionIDHash;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates the information when two colliders intersect and do not have physical
|
||||||
|
* resolution.
|
||||||
|
*/
|
||||||
|
class SH_API SHCollisionID
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Friends */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
friend struct SHCollisionIDHash;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Constructors & Destructor */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHCollisionID () noexcept;
|
||||||
|
SHCollisionID (const SHCollisionID& rhs) noexcept;
|
||||||
|
SHCollisionID (SHCollisionID&& rhs) noexcept;
|
||||||
|
|
||||||
|
~SHCollisionID () noexcept = default;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Operator Overloads */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
SHCollisionID& operator= (const SHCollisionID& rhs) noexcept;
|
||||||
|
SHCollisionID& operator= (SHCollisionID&& rhs) noexcept;
|
||||||
|
|
||||||
|
bool operator==(const SHCollisionID& rhs) const;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Getter Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
[[nodiscard]] EntityID GetEntityA () const noexcept;
|
||||||
|
[[nodiscard]] EntityID GetEntityB () const noexcept;
|
||||||
|
[[nodiscard]] uint32_t GetShapeIndexA () const noexcept;
|
||||||
|
[[nodiscard]] uint32_t GetShapeIndexB () const noexcept;
|
||||||
|
[[nodiscard]] const SHRigidBodyComponent* GetRigidBodyA () const noexcept;
|
||||||
|
[[nodiscard]] const SHRigidBodyComponent* GetRigidBodyB () const noexcept;
|
||||||
|
[[nodiscard]] const SHCollisionShape* GetCollisionShapeA () const noexcept;
|
||||||
|
[[nodiscard]] const SHCollisionShape* GetCollisionShapeB () const noexcept;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Setter Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void SetEntityA (EntityID entityID) noexcept;
|
||||||
|
void SetEntityB (EntityID entityID) noexcept;
|
||||||
|
void SetCollisionShapeA (uint32_t shapeIndexA) noexcept;
|
||||||
|
void SetCollisionShapeB (uint32_t shapeIndexB) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static constexpr uint32_t ENTITY_A = 0;
|
||||||
|
static constexpr uint32_t SHAPE_INDEX_A = 1;
|
||||||
|
static constexpr uint32_t ENTITY_B = 2;
|
||||||
|
static constexpr uint32_t SHAPE_INDEX_B = 3;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Data Members */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint64_t value[2]; // EntityValue, ShapeIndexValue
|
||||||
|
uint32_t ids [4]; // EntityA, EntityB, ShapeIndexA, ShapeIndexB
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates a functor to hash a CollisionID
|
||||||
|
*/
|
||||||
|
struct SHCollisionIDHash
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Member Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
inline std::size_t operator()(const SHCollisionID& id) const
|
||||||
|
{
|
||||||
|
return std::hash<std::u32string_view>{}(std::u32string_view(reinterpret_cast<std::basic_string_view<char32_t>::const_pointer>(id.ids), 4));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SHADE
|
|
@ -0,0 +1,70 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHManifold.h
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Interface for a Collision Manifold
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Primary Header
|
||||||
|
#include "Physics/Dynamics/SHRigidBody.h"
|
||||||
|
#include "Physics/Collision/CollisionShapes/SHCollisionShape.h"
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates a value that represents the touching features of a contact.
|
||||||
|
*/
|
||||||
|
union SHContactFeatures
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Data Members */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t incomingIncident;
|
||||||
|
uint8_t outgoingIncident;
|
||||||
|
uint8_t incomingReference;
|
||||||
|
uint8_t outgoingReference;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t key = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates a physical collision contact.
|
||||||
|
*/
|
||||||
|
struct SH_API SHContact
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Data Members */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static constexpr int NUM_TANGENTS = 2;
|
||||||
|
|
||||||
|
float penetration = 0.0f;
|
||||||
|
float bias = 0.0f;
|
||||||
|
float normalImpulse = 0.0f;
|
||||||
|
float normalMass = 0.0f;
|
||||||
|
float tangentImpulse[NUM_TANGENTS] = { 0.0f };
|
||||||
|
float tangentMass[NUM_TANGENTS] = { 0.0f };
|
||||||
|
|
||||||
|
SHVec3 position;
|
||||||
|
SHContactFeatures featurePair;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma once
|
|
@ -0,0 +1,46 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHManifold.h
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Interface for a Collision Manifold
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Primary Header
|
||||||
|
#include "Physics/Dynamics/SHRigidBody.h"
|
||||||
|
#include "Physics/Collision/CollisionShapes/SHCollisionShape.h"
|
||||||
|
#include "SHContact.h"
|
||||||
|
#include "SHCollisionEvents.h"
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
struct SH_API SHManifold
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Data Members */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// We only need 4 contact points to build a stable manifold.
|
||||||
|
static constexpr int MAX_NUM_CONTACTS = 4;
|
||||||
|
|
||||||
|
SHCollisionShape* A = nullptr;
|
||||||
|
SHCollisionShape* B = nullptr;
|
||||||
|
|
||||||
|
SHVec3 normal;
|
||||||
|
SHVec3 tangents[SHContact::NUM_TANGENTS];
|
||||||
|
|
||||||
|
SHContact contacts[MAX_NUM_CONTACTS];
|
||||||
|
uint32_t numContacts = 0;
|
||||||
|
SHCollisionState state;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHCollision.h
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Interface for the Detecting Collisions between two shapes
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
|
#include "Physics/Collision/CollisionShapes/SHCollisionShape.h"
|
||||||
|
#include "Physics/Collision/Contacts/SHManifold.h"
|
||||||
|
#include "Physics/Collision/Contacts/SHCollisionID.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates static methods for testing for collision between two shapes.
|
||||||
|
*/
|
||||||
|
class SH_API SHCollision
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Member Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Spheres VS X */
|
||||||
|
|
||||||
|
[[nodiscard]] static bool SphereVsSphere (const SHCollisionShape& A, const SHCollisionShape& B) noexcept;
|
||||||
|
[[nodiscard]] static bool SphereVsSphere (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept;
|
||||||
|
|
||||||
|
|
||||||
|
/* Capsule VS X */
|
||||||
|
|
||||||
|
/* Polygon VS X */
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Member Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
};
|
||||||
|
} // namespace SHADE
|
|
@ -0,0 +1,95 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHCollisionDispatch.cpp
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Implementation for the static Collision Dispatcher
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#include <SHpch.h>
|
||||||
|
|
||||||
|
// Primary Header
|
||||||
|
#include "SHCollisionDispatch.h"
|
||||||
|
|
||||||
|
// Project Header
|
||||||
|
#include "SHCollision.h"
|
||||||
|
#include "Tools/Utilities/SHUtilities.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Static Data Member Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
const SHCollisionDispatcher::ManifoldCollide SHCollisionDispatcher::manifoldCollide[NUM_SHAPES][NUM_SHAPES]
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
// <SHAPE> vs Sphere / Box / Capsule
|
||||||
|
|
||||||
|
{ SHCollision::SphereVsSphere, nullptr, nullptr } // Sphere
|
||||||
|
, { nullptr, nullptr, nullptr } // Box
|
||||||
|
, { nullptr, nullptr, nullptr } // Capsule
|
||||||
|
};
|
||||||
|
|
||||||
|
const SHCollisionDispatcher::TriggerCollide SHCollisionDispatcher::triggerCollide[NUM_SHAPES][NUM_SHAPES]
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
// <SHAPE> vs Sphere / Box / Capsule
|
||||||
|
|
||||||
|
{ SHCollision::SphereVsSphere, nullptr, nullptr } // Sphere
|
||||||
|
, { nullptr, nullptr, nullptr } // Box
|
||||||
|
, { nullptr, nullptr, nullptr } // Capsule
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool SHCollisionDispatcher::collisionTable[NUM_TYPES][NUM_TYPES]
|
||||||
|
{
|
||||||
|
/* S ST K KT D DT */
|
||||||
|
/* S */ { false, false, false, true, true, true }
|
||||||
|
, /* ST */ { false, false, true, true, true, true }
|
||||||
|
, /* K */ { false, true, false, true, true, true }
|
||||||
|
, /* KT */ { true, true, true, true, true, true }
|
||||||
|
, /* D */ { true, true, true, true, true, true }
|
||||||
|
, /* DT */ { true, true, true, true, true, true }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Public Member Functions Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
bool SHCollisionDispatcher::ShouldCollide(const SHCollisionShape& A, const SHCollisionShape& B) noexcept
|
||||||
|
{
|
||||||
|
// Filter through collision table
|
||||||
|
const int TYPE_A = SHUtilities::ConvertEnum(A.GetType()) + A.IsTrigger() ? TYPE_OFFSET : 0;
|
||||||
|
const int TYPE_B = SHUtilities::ConvertEnum(B.GetType()) + B.IsTrigger() ? TYPE_OFFSET : 0;
|
||||||
|
|
||||||
|
if (!collisionTable[TYPE_A][TYPE_B])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Filter through tags
|
||||||
|
const uint16_t TAG_A = A.GetCollisionTag().GetMask();
|
||||||
|
const uint16_t TAG_B = B.GetCollisionTag().GetMask();
|
||||||
|
|
||||||
|
const uint16_t MATCH = TAG_A & TAG_B;
|
||||||
|
return MATCH > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SHCollisionDispatcher::Collide(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept
|
||||||
|
{
|
||||||
|
const int TYPE_A = SHUtilities::ConvertEnum(A.GetType());
|
||||||
|
const int TYPE_B = SHUtilities::ConvertEnum(B.GetType());
|
||||||
|
|
||||||
|
return manifoldCollide[TYPE_A][TYPE_B](manifold, A, B);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SHCollisionDispatcher::Collide(const SHCollisionShape& A, const SHCollisionShape& B) noexcept
|
||||||
|
{
|
||||||
|
const int TYPE_A = SHUtilities::ConvertEnum(A.GetType());
|
||||||
|
const int TYPE_B = SHUtilities::ConvertEnum(B.GetType());
|
||||||
|
|
||||||
|
return triggerCollide[TYPE_A][TYPE_B](A, B);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SHADE
|
|
@ -0,0 +1,87 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHCollisionDispatch.h
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Interface for the static Collision Dispatcher
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
|
#include "Physics/Collision/Contacts/SHManifold.h"
|
||||||
|
#include "Physics/Collision/Contacts/SHCollisionID.h"
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Encapsulates static methods for running narrow-phase collision detection.
|
||||||
|
*/
|
||||||
|
class SH_API SHCollisionDispatcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Member Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* Filters the collision through the collision table and layer matching.
|
||||||
|
* @param A
|
||||||
|
* A Collision Shape.
|
||||||
|
* @param B
|
||||||
|
* A Collision Shape.
|
||||||
|
* @return
|
||||||
|
* True if both shapes should be tested for collision.
|
||||||
|
*/
|
||||||
|
static bool ShouldCollide (const SHCollisionShape& A, const SHCollisionShape& B) noexcept;
|
||||||
|
|
||||||
|
static bool Collide (SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept;
|
||||||
|
static bool Collide (const SHCollisionShape& A, const SHCollisionShape& B) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Type Definitions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
using ManifoldCollide = bool(*)(SHManifold&, const SHCollisionShape& A, const SHCollisionShape& B);
|
||||||
|
using TriggerCollide = bool(*)(const SHCollisionShape& A, const SHCollisionShape& B);
|
||||||
|
|
||||||
|
enum class Types
|
||||||
|
{
|
||||||
|
STATIC
|
||||||
|
, KINEMATIC
|
||||||
|
, DYNAMIC
|
||||||
|
, STATIC_TRIGGER
|
||||||
|
, KINEMATIC_TRIGGER
|
||||||
|
, DYNAMIC_TRIGGER
|
||||||
|
|
||||||
|
, COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Data Members */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// Read the Types enum class, then see where it's used and it'll make sense
|
||||||
|
static constexpr int TYPE_OFFSET = 3;
|
||||||
|
|
||||||
|
static constexpr int NUM_SHAPES = static_cast<int>(SHCollisionShape::Type::COUNT);
|
||||||
|
static constexpr int NUM_TYPES = static_cast<int>(Types::COUNT);
|
||||||
|
|
||||||
|
static const ManifoldCollide manifoldCollide [NUM_SHAPES][NUM_SHAPES];
|
||||||
|
static const TriggerCollide triggerCollide [NUM_SHAPES][NUM_SHAPES];
|
||||||
|
|
||||||
|
static const bool collisionTable [NUM_TYPES][NUM_TYPES];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace SHADE
|
|
@ -0,0 +1,78 @@
|
||||||
|
/****************************************************************************************
|
||||||
|
* \file SHSphereVsSphere.cpp
|
||||||
|
* \author Diren D Bharwani, diren.dbharwani, 390002520
|
||||||
|
* \brief Implementation for the Detecting Collisions between two spheres
|
||||||
|
*
|
||||||
|
* \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or
|
||||||
|
* disclosure of this file or its contents without the prior written consent
|
||||||
|
* of DigiPen Institute of Technology is prohibited.
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#include <SHpch.h>
|
||||||
|
|
||||||
|
// Primary Header
|
||||||
|
#include "SHCollision.h"
|
||||||
|
|
||||||
|
// Project Headers
|
||||||
|
#include "Math/SHMathHelpers.h"
|
||||||
|
#include "Physics/Collision/CollisionShapes/SHSphereCollisionShape.h"
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Public Member Functions Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
bool SHCollision::SphereVsSphere(const SHCollisionShape& A, const SHCollisionShape& B) noexcept
|
||||||
|
{
|
||||||
|
const SHSphereCollisionShape& SPHERE_A = dynamic_cast<const SHSphereCollisionShape&>(A);
|
||||||
|
const SHSphereCollisionShape& SPHERE_B = dynamic_cast<const SHSphereCollisionShape&>(B);
|
||||||
|
|
||||||
|
return SHSphere::Intersect(SPHERE_A, SPHERE_B);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SHCollision::SphereVsSphere(SHManifold& manifold, const SHCollisionShape& A, const SHCollisionShape& B) noexcept
|
||||||
|
{
|
||||||
|
// Convert to spheres
|
||||||
|
const SHSphereCollisionShape& SPHERE_A = dynamic_cast<const SHSphereCollisionShape&>(A);
|
||||||
|
const SHSphereCollisionShape& SPHERE_B = dynamic_cast<const SHSphereCollisionShape&>(B);
|
||||||
|
|
||||||
|
const SHVec3 A_TO_B = SPHERE_B.GetCenter() - SPHERE_A.GetCenter();
|
||||||
|
const float DISTANCE_BETWEEN_CENTERS_SQUARED = A_TO_B.LengthSquared();
|
||||||
|
|
||||||
|
const float COMBINED_RADIUS = (SPHERE_A.GetWorldRadius() + SPHERE_B.GetWorldRadius());
|
||||||
|
const float COMBINED_RADIUS_SQUARED = COMBINED_RADIUS * COMBINED_RADIUS;
|
||||||
|
|
||||||
|
if (DISTANCE_BETWEEN_CENTERS_SQUARED > COMBINED_RADIUS_SQUARED)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Only populate the manifold if there is a collision
|
||||||
|
|
||||||
|
uint32_t numContacts = 0;
|
||||||
|
|
||||||
|
SHContact contact;
|
||||||
|
contact.featurePair.key = 0;
|
||||||
|
|
||||||
|
if (SHMath::CompareFloat(DISTANCE_BETWEEN_CENTERS_SQUARED, 0.0f))
|
||||||
|
{
|
||||||
|
manifold.normal = SHVec3::UnitY;
|
||||||
|
contact.position = SPHERE_A.GetCenter();
|
||||||
|
contact.penetration = SPHERE_B.GetWorldRadius();
|
||||||
|
|
||||||
|
manifold.contacts[numContacts++] = contact;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
manifold.normal = SHVec3::Normalise(A_TO_B);
|
||||||
|
contact.position = SPHERE_B.GetCenter() - (manifold.normal * SPHERE_B.GetWorldRadius());
|
||||||
|
contact.penetration = COMBINED_RADIUS - A_TO_B.Length();
|
||||||
|
|
||||||
|
manifold.contacts[numContacts++] = contact;
|
||||||
|
}
|
||||||
|
|
||||||
|
manifold.numContacts = numContacts;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SHADE
|
|
@ -14,6 +14,7 @@
|
||||||
#include "SHCollider.h"
|
#include "SHCollider.h"
|
||||||
|
|
||||||
// Project Headers
|
// Project Headers
|
||||||
|
#include "Broadphase/SHDynamicAABBTree.h"
|
||||||
#include "Events/SHEvent.h"
|
#include "Events/SHEvent.h"
|
||||||
#include "Math/SHMathHelpers.h"
|
#include "Math/SHMathHelpers.h"
|
||||||
#include "Physics/SHPhysicsEvents.h"
|
#include "Physics/SHPhysicsEvents.h"
|
||||||
|
@ -28,19 +29,21 @@ namespace SHADE
|
||||||
|
|
||||||
SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept
|
SHCollider::SHCollider(EntityID eid, const SHTransform& worldTransform) noexcept
|
||||||
: entityID { eid }
|
: entityID { eid }
|
||||||
, shapeIDCounter { 0 }
|
|
||||||
, debugDraw { false }
|
, debugDraw { false }
|
||||||
|
, hasMoved { true }
|
||||||
, rigidBody { nullptr }
|
, rigidBody { nullptr }
|
||||||
, shapeFactory { nullptr }
|
, shapeFactory { nullptr }
|
||||||
|
, broadphase { nullptr }
|
||||||
, transform { worldTransform }
|
, transform { worldTransform }
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SHCollider::SHCollider(const SHCollider& rhs) noexcept
|
SHCollider::SHCollider(const SHCollider& rhs) noexcept
|
||||||
: entityID { rhs.entityID }
|
: entityID { rhs.entityID }
|
||||||
, shapeIDCounter { rhs.shapeIDCounter }
|
|
||||||
, debugDraw { rhs.debugDraw }
|
, debugDraw { rhs.debugDraw }
|
||||||
|
, hasMoved { rhs.hasMoved }
|
||||||
, rigidBody { rhs.rigidBody }
|
, rigidBody { rhs.rigidBody }
|
||||||
, shapeFactory { rhs.shapeFactory }
|
, shapeFactory { rhs.shapeFactory }
|
||||||
|
, broadphase { rhs.broadphase }
|
||||||
, transform { rhs.transform }
|
, transform { rhs.transform }
|
||||||
{
|
{
|
||||||
if (!shapeFactory)
|
if (!shapeFactory)
|
||||||
|
@ -54,10 +57,11 @@ namespace SHADE
|
||||||
|
|
||||||
SHCollider::SHCollider(SHCollider&& rhs) noexcept
|
SHCollider::SHCollider(SHCollider&& rhs) noexcept
|
||||||
: entityID { rhs.entityID }
|
: entityID { rhs.entityID }
|
||||||
, shapeIDCounter { rhs.shapeIDCounter }
|
|
||||||
, debugDraw { rhs.debugDraw }
|
, debugDraw { rhs.debugDraw }
|
||||||
|
, hasMoved { rhs.hasMoved }
|
||||||
, rigidBody { rhs.rigidBody }
|
, rigidBody { rhs.rigidBody }
|
||||||
, shapeFactory { rhs.shapeFactory }
|
, shapeFactory { rhs.shapeFactory }
|
||||||
|
, broadphase { rhs.broadphase }
|
||||||
, transform { rhs.transform }
|
, transform { rhs.transform }
|
||||||
{
|
{
|
||||||
if (!shapeFactory)
|
if (!shapeFactory)
|
||||||
|
@ -98,8 +102,10 @@ namespace SHADE
|
||||||
|
|
||||||
entityID = rhs.entityID;
|
entityID = rhs.entityID;
|
||||||
debugDraw = rhs.debugDraw;
|
debugDraw = rhs.debugDraw;
|
||||||
|
hasMoved = rhs.hasMoved;
|
||||||
rigidBody = rhs.rigidBody;
|
rigidBody = rhs.rigidBody;
|
||||||
shapeFactory = rhs.shapeFactory;
|
shapeFactory = rhs.shapeFactory;
|
||||||
|
broadphase = rhs.broadphase;
|
||||||
transform = rhs.transform;
|
transform = rhs.transform;
|
||||||
|
|
||||||
copyShapes(rhs);
|
copyShapes(rhs);
|
||||||
|
@ -117,8 +123,10 @@ namespace SHADE
|
||||||
|
|
||||||
entityID = rhs.entityID;
|
entityID = rhs.entityID;
|
||||||
debugDraw = rhs.debugDraw;
|
debugDraw = rhs.debugDraw;
|
||||||
|
hasMoved = rhs.hasMoved;
|
||||||
rigidBody = rhs.rigidBody;
|
rigidBody = rhs.rigidBody;
|
||||||
shapeFactory = rhs.shapeFactory;
|
shapeFactory = rhs.shapeFactory;
|
||||||
|
broadphase = rhs.broadphase;
|
||||||
transform = rhs.transform;
|
transform = rhs.transform;
|
||||||
|
|
||||||
copyShapes(rhs);
|
copyShapes(rhs);
|
||||||
|
@ -199,21 +207,25 @@ namespace SHADE
|
||||||
|
|
||||||
void SHCollider::SetTransform(const SHTransform& newTransform) noexcept
|
void SHCollider::SetTransform(const SHTransform& newTransform) noexcept
|
||||||
{
|
{
|
||||||
|
hasMoved = true;
|
||||||
transform = newTransform;
|
transform = newTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHCollider::SetPosition(const SHVec3& newPosition) noexcept
|
void SHCollider::SetPosition(const SHVec3& newPosition) noexcept
|
||||||
{
|
{
|
||||||
|
hasMoved = true;
|
||||||
transform.position = newPosition;
|
transform.position = newPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept
|
void SHCollider::SetOrientation(const SHQuaternion& newOrientation) noexcept
|
||||||
{
|
{
|
||||||
|
hasMoved = true;
|
||||||
transform.orientation = newOrientation;
|
transform.orientation = newOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHCollider::SetScale(const SHVec3& newScale) noexcept
|
void SHCollider::SetScale(const SHVec3& newScale) noexcept
|
||||||
{
|
{
|
||||||
|
hasMoved = true;
|
||||||
transform.scale = newScale;
|
transform.scale = newScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,10 +267,10 @@ namespace SHADE
|
||||||
, .Scale = SPHERE_SCALE
|
, .Scale = SPHERE_SCALE
|
||||||
};
|
};
|
||||||
|
|
||||||
const SHCollisionShapeID NEW_SHAPE_ID{ entityID, shapeIDCounter };
|
const uint32_t NEW_INDEX = static_cast<uint32_t>(shapes.size());
|
||||||
|
const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX };
|
||||||
|
|
||||||
SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO);
|
SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO);
|
||||||
++shapeIDCounter;
|
|
||||||
|
|
||||||
// Set offsets
|
// Set offsets
|
||||||
sphere->SetParentTransform(transform);
|
sphere->SetParentTransform(transform);
|
||||||
|
@ -267,12 +279,15 @@ namespace SHADE
|
||||||
|
|
||||||
shapes.emplace_back(sphere);
|
shapes.emplace_back(sphere);
|
||||||
|
|
||||||
|
if (broadphase)
|
||||||
|
broadphase->Insert(NEW_SHAPE_ID, sphere->ComputeAABB());
|
||||||
|
|
||||||
// Broadcast Event for adding a shape
|
// Broadcast Event for adding a shape
|
||||||
const SHPhysicsColliderAddedEvent EVENT_DATA
|
const SHPhysicsColliderAddedEvent EVENT_DATA
|
||||||
{
|
{
|
||||||
.entityID = entityID
|
.entityID = entityID
|
||||||
, .colliderType = SHCollisionShape::Type::SPHERE
|
, .colliderType = SHCollisionShape::Type::SPHERE
|
||||||
, .colliderIndex = static_cast<int>(shapes.size())
|
, .colliderIndex = static_cast<int>(NEW_INDEX)
|
||||||
};
|
};
|
||||||
|
|
||||||
SHEventManager::BroadcastEvent<SHPhysicsColliderAddedEvent>(EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT);
|
SHEventManager::BroadcastEvent<SHPhysicsColliderAddedEvent>(EVENT_DATA, SH_PHYSICS_COLLIDER_ADDED_EVENT);
|
||||||
|
@ -280,7 +295,7 @@ namespace SHADE
|
||||||
if (rigidBody)
|
if (rigidBody)
|
||||||
rigidBody->ComputeMassData();
|
rigidBody->ComputeMassData();
|
||||||
|
|
||||||
return static_cast<int>(shapes.size());
|
return static_cast<int>(NEW_INDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHCollider::RemoveCollisionShape(int index)
|
void SHCollider::RemoveCollisionShape(int index)
|
||||||
|
@ -296,8 +311,8 @@ namespace SHADE
|
||||||
if (index < 0 || index >= NUM_SHAPES)
|
if (index < 0 || index >= NUM_SHAPES)
|
||||||
throw std::invalid_argument("Out-of-range index!");
|
throw std::invalid_argument("Out-of-range index!");
|
||||||
|
|
||||||
auto shapeIter = shapes.begin();
|
auto shape = shapes.begin();
|
||||||
for (int i = 0; i < NUM_SHAPES; ++i, ++shapeIter)
|
for (int i = 0; i < NUM_SHAPES; ++i, ++shape)
|
||||||
{
|
{
|
||||||
if (i == index)
|
if (i == index)
|
||||||
break;
|
break;
|
||||||
|
@ -306,15 +321,21 @@ namespace SHADE
|
||||||
const SHPhysicsColliderRemovedEvent EVENT_DATA
|
const SHPhysicsColliderRemovedEvent EVENT_DATA
|
||||||
{
|
{
|
||||||
.entityID = entityID
|
.entityID = entityID
|
||||||
, .colliderType = (*shapeIter)->GetType()
|
, .colliderType = (*shape)->GetType()
|
||||||
, .colliderIndex = index
|
, .colliderIndex = index
|
||||||
};
|
};
|
||||||
|
|
||||||
shapeFactory->DestroyShape(*shapeIter);
|
// Remove from broadphase
|
||||||
*shapeIter = nullptr;
|
if (broadphase)
|
||||||
|
broadphase->Remove((*shape)->id);
|
||||||
|
|
||||||
|
shapeFactory->DestroyShape(*shape);
|
||||||
|
*shape = nullptr;
|
||||||
|
|
||||||
// Remove the shape from the container to prevent accessing a nullptr
|
// Remove the shape from the container to prevent accessing a nullptr
|
||||||
shapeIter = shapes.erase(shapeIter);
|
shape = shapes.erase(shape);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Broadcast Event for removing a shape
|
// Broadcast Event for removing a shape
|
||||||
SHEventManager::BroadcastEvent<SHPhysicsColliderRemovedEvent>(EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT);
|
SHEventManager::BroadcastEvent<SHPhysicsColliderRemovedEvent>(EVENT_DATA, SH_PHYSICS_COLLIDER_REMOVED_EVENT);
|
||||||
|
@ -337,12 +358,8 @@ namespace SHADE
|
||||||
|
|
||||||
void SHCollider::copyShapes(const SHCollider& rhsCollider)
|
void SHCollider::copyShapes(const SHCollider& rhsCollider)
|
||||||
{
|
{
|
||||||
shapeIDCounter = 0;
|
|
||||||
for (const auto* shape : rhsCollider.shapes)
|
for (const auto* shape : rhsCollider.shapes)
|
||||||
{
|
|
||||||
copyShape(shape);
|
copyShape(shape);
|
||||||
++shapeIDCounter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHCollider::copyShape(const SHCollisionShape* rhsShape)
|
void SHCollider::copyShape(const SHCollisionShape* rhsShape)
|
||||||
|
@ -365,7 +382,8 @@ namespace SHADE
|
||||||
, .Scale = RHS_SPHERE->scale
|
, .Scale = RHS_SPHERE->scale
|
||||||
};
|
};
|
||||||
|
|
||||||
const SHCollisionShapeID NEW_SHAPE_ID{ entityID, shapeIDCounter };
|
const uint32_t NEW_INDEX = static_cast<uint32_t>(shapes.size());
|
||||||
|
const SHCollisionShapeID NEW_SHAPE_ID{ entityID, NEW_INDEX };
|
||||||
|
|
||||||
SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO);
|
SHSphereCollisionShape* sphere = shapeFactory->CreateSphere(NEW_SHAPE_ID, SPHERE_CREATE_INFO);
|
||||||
*sphere = *RHS_SPHERE;
|
*sphere = *RHS_SPHERE;
|
||||||
|
@ -380,8 +398,6 @@ namespace SHADE
|
||||||
}
|
}
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
++shapeIDCounter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace SHADE
|
} // namespace SHADE
|
|
@ -22,6 +22,7 @@ namespace SHADE
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
class SHRigidBody;
|
class SHRigidBody;
|
||||||
|
class SHAABBTree;
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
/* Type Definitions */
|
/* Type Definitions */
|
||||||
|
@ -155,12 +156,13 @@ namespace SHADE
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
EntityID entityID;
|
EntityID entityID;
|
||||||
uint32_t shapeIDCounter; // This increments everytime a shape is added to differentiate shapes.
|
|
||||||
|
|
||||||
bool debugDraw;
|
bool debugDraw;
|
||||||
|
bool hasMoved;
|
||||||
|
|
||||||
SHRigidBody* rigidBody;
|
SHRigidBody* rigidBody;
|
||||||
SHCollisionShapeFactory* shapeFactory;
|
SHCollisionShapeFactory* shapeFactory;
|
||||||
|
SHAABBTree* broadphase;
|
||||||
|
|
||||||
SHTransform transform;
|
SHTransform transform;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "SHPhysicsWorld.h"
|
#include "SHPhysicsWorld.h"
|
||||||
|
|
||||||
// Project Headers
|
// Project Headers
|
||||||
|
#include "Physics/Collision/Narrowphase/SHCollision.h"
|
||||||
|
#include "Physics/Collision/Narrowphase/SHCollisionDispatch.h"
|
||||||
|
|
||||||
|
|
||||||
namespace SHADE
|
namespace SHADE
|
||||||
|
@ -25,13 +27,52 @@ namespace SHADE
|
||||||
SHPhysicsWorld::SHPhysicsWorld(const WorldSettings& worldSettings) noexcept
|
SHPhysicsWorld::SHPhysicsWorld(const WorldSettings& worldSettings) noexcept
|
||||||
: settings { worldSettings }
|
: settings { worldSettings }
|
||||||
{
|
{
|
||||||
rigidBodies.clear();
|
|
||||||
SHLOG_INFO_D("Creating Physics World")
|
SHLOG_INFO_D("Creating Physics World")
|
||||||
}
|
}
|
||||||
|
|
||||||
SHPhysicsWorld::~SHPhysicsWorld() noexcept
|
SHPhysicsWorld::~SHPhysicsWorld() noexcept
|
||||||
{
|
{
|
||||||
rigidBodies.clear();
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
/* Getter Functions Definitions */
|
||||||
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
const SHPhysicsWorld::TriggerEvents& SHPhysicsWorld::GetTriggerEvents() const noexcept
|
||||||
|
{
|
||||||
|
static TriggerEvents triggerEvents;
|
||||||
|
|
||||||
|
triggerEvents.clear();
|
||||||
|
|
||||||
|
for (auto& [id, state] : triggers)
|
||||||
|
triggerEvents.emplace_back(SHTriggerEvent{ id, state });
|
||||||
|
|
||||||
|
return triggerEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHPhysicsWorld::CollisionEvents& SHPhysicsWorld::GetCollisionEvents() const noexcept
|
||||||
|
{
|
||||||
|
static CollisionEvents collisionEvents;
|
||||||
|
|
||||||
|
collisionEvents.clear();
|
||||||
|
|
||||||
|
for (auto& [id, manifold] : manifolds)
|
||||||
|
{
|
||||||
|
SHCollisionEvent collisionEvent
|
||||||
|
{
|
||||||
|
.info = id
|
||||||
|
, .state = manifold.state
|
||||||
|
, .normal = manifold.normal
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < manifold.numContacts; ++i)
|
||||||
|
collisionEvent.contactPoints[i] = manifold.contacts[i].position;
|
||||||
|
|
||||||
|
collisionEvents.emplace_back(collisionEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return collisionEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------*/
|
||||||
|
@ -40,7 +81,7 @@ namespace SHADE
|
||||||
|
|
||||||
void SHPhysicsWorld::AddRigidBody(SHRigidBody* rigidBody) noexcept
|
void SHPhysicsWorld::AddRigidBody(SHRigidBody* rigidBody) noexcept
|
||||||
{
|
{
|
||||||
const bool INSERTED = rigidBodies.emplace(rigidBody).second;
|
const bool INSERTED = rigidBodies.emplace(rigidBody->entityID, rigidBody).second;
|
||||||
if (!INSERTED)
|
if (!INSERTED)
|
||||||
{
|
{
|
||||||
SHLOG_WARNING_D("Attempting to add duplicate rigid body {} to the Physics World!", rigidBody->entityID)
|
SHLOG_WARNING_D("Attempting to add duplicate rigid body {} to the Physics World!", rigidBody->entityID)
|
||||||
|
@ -49,37 +90,89 @@ namespace SHADE
|
||||||
|
|
||||||
void SHPhysicsWorld::RemoveRigidBody(SHRigidBody* rigidBody) noexcept
|
void SHPhysicsWorld::RemoveRigidBody(SHRigidBody* rigidBody) noexcept
|
||||||
{
|
{
|
||||||
rigidBodies.erase(rigidBody);
|
rigidBodies.erase(rigidBody->entityID);
|
||||||
|
|
||||||
// Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep
|
// Attempt to remove any invalidated manifolds
|
||||||
|
if (manifolds.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto manifoldPair = manifolds.begin(); manifoldPair != manifolds.end();)
|
||||||
|
{
|
||||||
|
const auto& ID = manifoldPair->first;
|
||||||
|
|
||||||
|
const bool MATCHES_A = ID.GetEntityA() == rigidBody->entityID;
|
||||||
|
const bool MATCHES_B = ID.GetEntityB() == rigidBody->entityID;
|
||||||
|
|
||||||
|
if (MATCHES_A || MATCHES_B)
|
||||||
|
manifoldPair = manifolds.erase(manifoldPair);
|
||||||
|
else
|
||||||
|
++manifoldPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHPhysicsWorld::AddCollider(SHCollider* collider) noexcept
|
void SHPhysicsWorld::AddCollider(SHCollider* collider) noexcept
|
||||||
{
|
{
|
||||||
const bool INSERTED = colliders.emplace(collider).second;
|
const bool INSERTED = colliders.emplace(collider->entityID, collider).second;
|
||||||
if (!INSERTED)
|
if (!INSERTED)
|
||||||
{
|
{
|
||||||
SHLOG_WARNING_D("Attempting to add duplicate collider {} to the Physics World!", collider->entityID)
|
SHLOG_WARNING_D("Attempting to add duplicate collider {} to the Physics World!", collider->entityID)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collider->broadphase = &broadphase;
|
||||||
|
|
||||||
|
// Add all existing shapes to the broadphase
|
||||||
|
for (const auto* shape : collider->shapes)
|
||||||
|
broadphase.Insert(shape->id, shape->ComputeAABB());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHPhysicsWorld::RemoveCollider(SHCollider* collider) noexcept
|
void SHPhysicsWorld::RemoveCollider(SHCollider* collider) noexcept
|
||||||
{
|
{
|
||||||
colliders.erase(collider);
|
colliders.erase(collider->entityID);
|
||||||
|
|
||||||
|
const uint32_t NUM_SHAPES = static_cast<uint32_t>(collider->shapes.size());
|
||||||
|
if (NUM_SHAPES == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < NUM_SHAPES; ++i)
|
||||||
|
{
|
||||||
|
const SHCollisionShape* SHAPE = collider->shapes[i];
|
||||||
|
|
||||||
|
broadphase.Remove(SHAPE->id);
|
||||||
|
|
||||||
|
if (SHAPE->IsTrigger())
|
||||||
|
removeInvalidatedTrigger(collider->entityID, i);
|
||||||
|
else
|
||||||
|
removeInvalidatedManifold(collider->entityID, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* Get collider's rigid body
|
||||||
|
* Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
// Get collider's rigid body
|
|
||||||
// Run through the rigid body's contact graph and wake all of its non-static bodies that are asleep
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHPhysicsWorld::Step(float dt)
|
void SHPhysicsWorld::Step(float dt)
|
||||||
{
|
{
|
||||||
for (auto* rigidBody : rigidBodies)
|
// Clear containers of exit state collisions
|
||||||
|
updateEvents();
|
||||||
|
|
||||||
|
// TODO: Profile each of these
|
||||||
|
runBroadphase ();
|
||||||
|
runNarrowphase();
|
||||||
|
|
||||||
|
for (auto* rigidBody : rigidBodies | std::views::values)
|
||||||
{
|
{
|
||||||
rigidBody->ComputeWorldData();
|
rigidBody->ComputeWorldData();
|
||||||
integrateForces(*rigidBody, dt);
|
integrateForces(*rigidBody, dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* rigidBody : rigidBodies)
|
for (auto* rigidBody : rigidBodies | std::views::values)
|
||||||
integrateVelocities(*rigidBody, dt);
|
integrateVelocities(*rigidBody, dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,4 +251,262 @@ namespace SHADE
|
||||||
// We clear forces for static bodies as well for redundancy
|
// We clear forces for static bodies as well for redundancy
|
||||||
rigidBody.ClearForces();
|
rigidBody.ClearForces();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::runBroadphase() noexcept
|
||||||
|
{
|
||||||
|
// Update any colliders that have moved
|
||||||
|
for (auto& collider : colliders | std::views::values)
|
||||||
|
{
|
||||||
|
if (!collider->hasMoved)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Clear hasMoved flag here
|
||||||
|
collider->hasMoved = false;
|
||||||
|
|
||||||
|
// Update moved shapes in broadphase
|
||||||
|
for (auto* shape : collider->shapes)
|
||||||
|
broadphase.Update(shape->id, shape->ComputeAABB());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query: Kinematic Triggers, Awake Dynamic Bodies & Dynamic Triggers
|
||||||
|
for (auto& collider : colliders | std::views::values)
|
||||||
|
{
|
||||||
|
// Default static bodies
|
||||||
|
if (!collider->rigidBody)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Explicit static bodies
|
||||||
|
const bool IS_STATIC = collider->rigidBody->GetType() == SHRigidBody::Type::STATIC;
|
||||||
|
if (IS_STATIC)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// All remaining are kinematic or dynamic
|
||||||
|
// Iterate through shapes: if kinematic / dynamic trigger, else if dynamic & awake
|
||||||
|
if (collider->rigidBody->GetType() == SHRigidBody::Type::KINEMATIC)
|
||||||
|
queryKinematic(collider);
|
||||||
|
|
||||||
|
if (collider->rigidBody->GetType() == SHRigidBody::Type::DYNAMIC)
|
||||||
|
queryDynamic(collider);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::queryKinematic(SHCollider* collider) noexcept
|
||||||
|
{
|
||||||
|
for (auto* shape : collider->shapes)
|
||||||
|
{
|
||||||
|
// For kinematic shapes, we only query triggers against everything else
|
||||||
|
if (!shape->IsTrigger())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto& potentialCollisions = broadphase.Query(shape->id, shape->ComputeAABB());
|
||||||
|
|
||||||
|
// Build narrow-phase pairs
|
||||||
|
auto* shapeA = shape;
|
||||||
|
|
||||||
|
const EntityID ID_A = shape->id.GetEntityID();
|
||||||
|
const uint32_t INDEX_A = shape->id.GetShapeIndex();
|
||||||
|
|
||||||
|
for (auto& id : potentialCollisions)
|
||||||
|
{
|
||||||
|
// Get corresponding shape
|
||||||
|
const EntityID ID_B = id.GetEntityID();
|
||||||
|
const uint32_t INDEX_B = id.GetShapeIndex();
|
||||||
|
|
||||||
|
auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B);
|
||||||
|
|
||||||
|
// Build collision ID
|
||||||
|
SHCollisionID collisionKey;
|
||||||
|
collisionKey.SetEntityA(ID_A);
|
||||||
|
collisionKey.SetEntityB(ID_B);
|
||||||
|
collisionKey.SetCollisionShapeA(INDEX_A);
|
||||||
|
collisionKey.SetCollisionShapeB(INDEX_B);
|
||||||
|
|
||||||
|
// Check if it already exists. If it doesn't, put into batch.
|
||||||
|
auto narrowphasePair = narrowphaseBatch.find(collisionKey);
|
||||||
|
if (narrowphasePair == narrowphaseBatch.end())
|
||||||
|
narrowphaseBatch.emplace(collisionKey, NarrowphasePair{ shapeA, shapeB });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::queryDynamic(SHCollider* collider) noexcept
|
||||||
|
{
|
||||||
|
for (auto* shape : collider->shapes)
|
||||||
|
{
|
||||||
|
auto& potentialCollisions = broadphase.Query(shape->id, shape->ComputeAABB());
|
||||||
|
|
||||||
|
// Build narrow-phase pairs
|
||||||
|
auto* shapeA = shape;
|
||||||
|
|
||||||
|
const EntityID ID_A = shape->id.GetEntityID();
|
||||||
|
const uint32_t INDEX_A = shape->id.GetShapeIndex();
|
||||||
|
|
||||||
|
for (auto& id : potentialCollisions)
|
||||||
|
{
|
||||||
|
// Get corresponding shape
|
||||||
|
const EntityID ID_B = id.GetEntityID();
|
||||||
|
const uint32_t INDEX_B = id.GetShapeIndex();
|
||||||
|
|
||||||
|
auto* shapeB = colliders[ID_B]->GetCollisionShape(INDEX_B);
|
||||||
|
|
||||||
|
// Build collision ID
|
||||||
|
SHCollisionID collisionKey;
|
||||||
|
collisionKey.SetEntityA(ID_A);
|
||||||
|
collisionKey.SetEntityB(ID_B);
|
||||||
|
collisionKey.SetCollisionShapeA(INDEX_A);
|
||||||
|
collisionKey.SetCollisionShapeB(INDEX_B);
|
||||||
|
|
||||||
|
// Check if it already exists. If it doesn't, put into batch.
|
||||||
|
auto narrowphasePair = narrowphaseBatch.find(collisionKey);
|
||||||
|
if (narrowphasePair == narrowphaseBatch.end())
|
||||||
|
narrowphaseBatch.emplace(collisionKey, NarrowphasePair{ shapeA, shapeB });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::runNarrowphase() noexcept
|
||||||
|
{
|
||||||
|
for (auto& [id, narrowphasePair] : narrowphaseBatch)
|
||||||
|
{
|
||||||
|
const bool IS_A_TRIGGER = narrowphasePair.A->IsTrigger();
|
||||||
|
const bool IS_B_TRIGGER = narrowphasePair.B->IsTrigger();
|
||||||
|
|
||||||
|
// Check if ID exists in trigger
|
||||||
|
if (IS_A_TRIGGER || IS_B_TRIGGER)
|
||||||
|
collideTriggers(id, narrowphasePair);
|
||||||
|
else
|
||||||
|
collideManifolds(id, narrowphasePair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::collideTriggers(const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept
|
||||||
|
{
|
||||||
|
const auto* A = narrowphasePair.A;
|
||||||
|
const auto* B = narrowphasePair.B;
|
||||||
|
|
||||||
|
const bool COLLIDING = SHCollisionDispatcher::Collide(*A, *B);
|
||||||
|
|
||||||
|
auto trigger = triggers.find(id);
|
||||||
|
|
||||||
|
// If id not found, emplace new object.
|
||||||
|
// New object is in the invalid state
|
||||||
|
if (trigger == triggers.end())
|
||||||
|
trigger = triggers.emplace(id, SHCollisionState::INVALID).first;
|
||||||
|
|
||||||
|
SHCollisionState& state = trigger->second;
|
||||||
|
updateCollisionState(COLLIDING, state);
|
||||||
|
|
||||||
|
// If it was a false positive, remove the manifold immediately.
|
||||||
|
// Remove using iterator as it is on average faster.
|
||||||
|
if (state == SHCollisionState::INVALID)
|
||||||
|
trigger = triggers.erase(trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::collideManifolds(const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept
|
||||||
|
{
|
||||||
|
auto* A = narrowphasePair.A;
|
||||||
|
auto* B = narrowphasePair.B;
|
||||||
|
|
||||||
|
SHManifold newManifold { .A = A, .B = B };
|
||||||
|
const bool COLLIDING = SHCollisionDispatcher::Collide(newManifold, *A, *B);
|
||||||
|
|
||||||
|
auto manifold = manifolds.find(id);
|
||||||
|
|
||||||
|
// If id not found, emplace new manifold
|
||||||
|
if (manifold == manifolds.end())
|
||||||
|
manifold = manifolds.emplace(id, newManifold).first;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Update existing manifolds with new data
|
||||||
|
}
|
||||||
|
|
||||||
|
SHCollisionState& state = manifold->second.state;
|
||||||
|
updateCollisionState(COLLIDING, state);
|
||||||
|
|
||||||
|
// If it was a false positive, remove the manifold immediately.
|
||||||
|
// Remove using iterator as it is on average faster.
|
||||||
|
if (state == SHCollisionState::INVALID)
|
||||||
|
manifold = manifolds.erase(manifold);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::updateCollisionState(bool isColliding, SHCollisionState& state) noexcept
|
||||||
|
{
|
||||||
|
if (isColliding)
|
||||||
|
state = state == SHCollisionState::INVALID ? SHCollisionState::ENTER : SHCollisionState::STAY;
|
||||||
|
else
|
||||||
|
state = SHCollisionState::EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::updateEvents() noexcept
|
||||||
|
{
|
||||||
|
// Clear expired or invalid collisions
|
||||||
|
for (auto manifold = manifolds.begin(); manifold != manifolds.end();)
|
||||||
|
{
|
||||||
|
const auto COLLISION_STATE = manifold->second.state;
|
||||||
|
|
||||||
|
const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT;
|
||||||
|
const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID;
|
||||||
|
|
||||||
|
if (IS_EXIT || IS_INVALID)
|
||||||
|
manifold = manifolds.erase(manifold);
|
||||||
|
else
|
||||||
|
++manifold;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear expired or invalid triggers
|
||||||
|
for (auto trigger = triggers.begin(); trigger != triggers.end();)
|
||||||
|
{
|
||||||
|
const auto COLLISION_STATE = trigger->second;
|
||||||
|
|
||||||
|
const bool IS_EXIT = COLLISION_STATE == SHCollisionState::EXIT;
|
||||||
|
const bool IS_INVALID = COLLISION_STATE == SHCollisionState::INVALID;
|
||||||
|
|
||||||
|
if (IS_EXIT || IS_INVALID)
|
||||||
|
trigger = triggers.erase(trigger);
|
||||||
|
else
|
||||||
|
++trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::removeInvalidatedTrigger(EntityID eid, uint32_t shapeIndex)
|
||||||
|
{
|
||||||
|
if (triggers.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto invalidatedTrigger = triggers.begin(); invalidatedTrigger != triggers.end();)
|
||||||
|
{
|
||||||
|
const auto& ID = invalidatedTrigger->first;
|
||||||
|
|
||||||
|
const bool MATCHES_A = ID.GetEntityA() == eid && ID.GetShapeIndexA() == shapeIndex;
|
||||||
|
const bool MATCHES_B = ID.GetEntityB() == eid && ID.GetShapeIndexB() == shapeIndex;
|
||||||
|
|
||||||
|
if (MATCHES_A || MATCHES_B)
|
||||||
|
invalidatedTrigger = triggers.erase(invalidatedTrigger);
|
||||||
|
else
|
||||||
|
++invalidatedTrigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SHPhysicsWorld::removeInvalidatedManifold(EntityID eid, uint32_t shapeIndex)
|
||||||
|
{
|
||||||
|
if (manifolds.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto invalidatedManifold = manifolds.begin(); invalidatedManifold != manifolds.end();)
|
||||||
|
{
|
||||||
|
const auto& ID = invalidatedManifold->first;
|
||||||
|
|
||||||
|
const bool MATCHES_A = ID.GetEntityA() == eid && ID.GetShapeIndexA() == shapeIndex;
|
||||||
|
const bool MATCHES_B = ID.GetEntityB() == eid && ID.GetShapeIndexB() == shapeIndex;
|
||||||
|
|
||||||
|
if (MATCHES_A || MATCHES_B)
|
||||||
|
invalidatedManifold = manifolds.erase(invalidatedManifold);
|
||||||
|
else
|
||||||
|
++invalidatedManifold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace SHADE
|
} // namespace SHADE
|
|
@ -10,9 +10,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <unordered_set>
|
#include <unordered_map>
|
||||||
|
|
||||||
// Project Headers
|
// Project Headers
|
||||||
|
#include "Physics/Collision/Broadphase/SHDynamicAABBTree.h"
|
||||||
|
#include "Physics/Collision/Contacts/SHCollisionEvents.h"
|
||||||
|
#include "Physics/Collision/Contacts/SHManifold.h"
|
||||||
#include "Physics/Collision/SHCollider.h"
|
#include "Physics/Collision/SHCollider.h"
|
||||||
#include "SHRigidBody.h"
|
#include "SHRigidBody.h"
|
||||||
|
|
||||||
|
@ -44,6 +47,9 @@ namespace SHADE
|
||||||
bool sleepingEnabled = true;
|
bool sleepingEnabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using TriggerEvents = std::vector<SHTriggerEvent>;
|
||||||
|
using CollisionEvents = std::vector<SHCollisionEvent>;
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Constructors & Destructor */
|
/* Constructors & Destructor */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
@ -54,7 +60,6 @@ namespace SHADE
|
||||||
SHPhysicsWorld (const SHPhysicsWorld&) = delete;
|
SHPhysicsWorld (const SHPhysicsWorld&) = delete;
|
||||||
SHPhysicsWorld (SHPhysicsWorld&&) = delete;
|
SHPhysicsWorld (SHPhysicsWorld&&) = delete;
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Operator Overloads */
|
/* Operator Overloads */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
@ -63,7 +68,14 @@ namespace SHADE
|
||||||
SHPhysicsWorld& operator=(SHPhysicsWorld&&) = delete;
|
SHPhysicsWorld& operator=(SHPhysicsWorld&&) = delete;
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Function Members */
|
/* Getter Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
const TriggerEvents& GetTriggerEvents () const noexcept;
|
||||||
|
const CollisionEvents& GetCollisionEvents () const noexcept;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Member Functions */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void AddRigidBody (SHRigidBody* rigidBody) noexcept;
|
void AddRigidBody (SHRigidBody* rigidBody) noexcept;
|
||||||
|
@ -79,27 +91,67 @@ namespace SHADE
|
||||||
/* Type Definitions */
|
/* Type Definitions */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
//! I realise using a set may be dangerous with pointers. An unordered_map may be better, but I don't need the entityIDs so...
|
struct NarrowphasePair
|
||||||
|
{
|
||||||
|
SHCollisionShape* A = nullptr;
|
||||||
|
SHCollisionShape* B = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// EntityIDs are used to map resolved contraints back to bodies
|
||||||
|
using RigidBodies = std::unordered_map<EntityID, SHRigidBody*>;
|
||||||
|
using Colliders = std::unordered_map<EntityID, SHCollider*>;
|
||||||
|
|
||||||
|
// Collisions
|
||||||
|
|
||||||
|
using NarrowphaseBatch = std::unordered_map<SHCollisionID, NarrowphasePair, SHCollisionIDHash>;
|
||||||
|
using Manifolds = std::unordered_map<SHCollisionID, SHManifold, SHCollisionIDHash>;
|
||||||
|
using Triggers = std::unordered_map<SHCollisionID, SHCollisionState, SHCollisionIDHash>;
|
||||||
|
|
||||||
using Colliders = std::unordered_set<SHCollider*>;
|
|
||||||
using RigidBodies = std::unordered_set<SHRigidBody*>;
|
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Data Members */
|
/* Data Members */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
WorldSettings settings;
|
WorldSettings settings;
|
||||||
|
|
||||||
Colliders colliders;
|
// Containers
|
||||||
RigidBodies rigidBodies;
|
|
||||||
|
RigidBodies rigidBodies;
|
||||||
|
Colliders colliders;
|
||||||
|
|
||||||
|
NarrowphaseBatch narrowphaseBatch;
|
||||||
|
Manifolds manifolds;
|
||||||
|
Triggers triggers;
|
||||||
|
|
||||||
|
// World components
|
||||||
|
|
||||||
|
SHAABBTree broadphase;
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
/* Function Members */
|
/* Function Members */
|
||||||
/*---------------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept;
|
// TODO: Move to island when islands are set up
|
||||||
void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept;
|
void integrateForces (SHRigidBody& rigidBody, float dt) const noexcept;
|
||||||
|
void integrateVelocities (SHRigidBody& rigidBody, float dt) const noexcept;
|
||||||
|
|
||||||
|
// Broadphase helpers
|
||||||
|
|
||||||
|
void runBroadphase () noexcept;
|
||||||
|
void queryKinematic (SHCollider* collider) noexcept;
|
||||||
|
void queryDynamic (SHCollider* collider) noexcept;
|
||||||
|
|
||||||
|
// Narrowphase helpers
|
||||||
|
|
||||||
|
void runNarrowphase () noexcept;
|
||||||
|
void collideTriggers (const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept;
|
||||||
|
void collideManifolds (const SHCollisionID& id, NarrowphasePair& narrowphasePair) noexcept;
|
||||||
|
void updateCollisionState (bool isColliding, SHCollisionState& state) noexcept;
|
||||||
|
|
||||||
|
void updateEvents () noexcept;
|
||||||
|
void removeInvalidatedTrigger (EntityID eid, uint32_t shapeIndex);
|
||||||
|
void removeInvalidatedManifold (EntityID eid, uint32_t shapeIndex);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue