Added implementation for raycasting into the collision space

This commit is contained in:
Diren D Bharwani 2023-01-01 19:39:16 +08:00
parent 7a92c2c86f
commit ddfbc71400
7 changed files with 281 additions and 12 deletions

View File

@ -159,6 +159,8 @@ namespace SHADE
{ {
result.position = ray.position + ray.direction * result.distance; result.position = ray.position + ray.direction * result.distance;
result.angle = SHVec3::Angle(ray.position, result.position); result.angle = SHVec3::Angle(ray.position, result.position);
// TODO: Compute normal
} }
return result; return result;

View File

@ -111,6 +111,8 @@ namespace SHADE
{ {
result.position = ray.position + ray.direction * result.distance; result.position = ray.position + ray.direction * result.distance;
result.angle = SHVec3::Angle(ray.position, result.position); result.angle = SHVec3::Angle(ray.position, result.position);
// TODO: Compute Normal
} }
return result; return result;

View File

@ -92,6 +92,8 @@ namespace SHADE
{ {
result.position = ray.position + ray.direction * result.distance; result.position = ray.position + ray.direction * result.distance;
result.angle = SHVec3::Angle(ray.position, result.position); result.angle = SHVec3::Angle(ray.position, result.position);
// TODO: Compute Normal
} }
return result; return result;

View File

@ -249,9 +249,37 @@ namespace SHADE
const std::vector<SHCollisionShapeID>& SHAABBTree::Query(const SHRay& ray, float distance) const noexcept const std::vector<SHCollisionShapeID>& SHAABBTree::Query(const SHRay& ray, float distance) const noexcept
{ {
static std::vector<SHCollisionShapeID> potentialHits; static std::vector<SHCollisionShapeID> potentialHits;
static std::stack<int32_t> nodeIndices;
potentialHits.clear(); potentialHits.clear();
nodeIndices.push(root);
while (!nodeIndices.empty())
{
const int32_t INDEX = nodeIndices.top();
nodeIndices.pop();
if (INDEX == NULL_NODE)
continue;
const Node& NODE = nodes[INDEX];
const auto& RESULT = NODE.AABB.Raycast(ray);
if (!RESULT || RESULT.distance > distance)
continue;
if (isLeaf(INDEX))
{
potentialHits.emplace_back(NODE.id);
}
else
{
// Non-leaf nodes need to be traversed further
nodeIndices.push(NODE.left);
nodeIndices.push(NODE.right);
}
}
return potentialHits; return potentialHits;
} }

View File

@ -19,6 +19,7 @@
#include "SHCollisionShapeID.h" #include "SHCollisionShapeID.h"
#include "Math/Geometry/SHAABB.h" #include "Math/Geometry/SHAABB.h"
#include "Math/Transform/SHTransform.h" #include "Math/Transform/SHTransform.h"
#include "Physics/Collision/SHPhysicsRaycastResult.h"
namespace SHADE namespace SHADE
{ {
@ -128,10 +129,10 @@ namespace SHADE
/* Member Functions */ /* Member Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
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 SHAABB ComputeAABB () const noexcept = 0; [[nodiscard]] virtual SHAABB ComputeAABB () const noexcept = 0;
protected: protected:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/

View File

@ -160,6 +160,163 @@ namespace SHADE
contactManager->Update(); contactManager->Update();
} }
const SHCollisionSpace::RaycastHits& SHCollisionSpace::Raycast(const SHRay& ray, float distance, uint16_t layer) noexcept
{
raycastHits.clear();
const auto& POTENTIAL_HITS = broadphase.Query(ray, distance);
if (POTENTIAL_HITS.empty())
return raycastHits;
// Test potential hits individually
// Cull entities that are on different layers
for (auto& shapeID : POTENTIAL_HITS)
{
// Get shape
const EntityID EID = shapeID.GetEntityID();
const uint32_t IDX = shapeID.GetShapeIndex();
const auto* COLLIDER = colliders.find(EID)->second;
const auto* SHAPE = COLLIDER->GetCollisionShape(IDX);
// Cull by layer
const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & layer;
if (!LAYER_MATCH)
continue;
// Well this is awkward...I honestly don't have the mental capacity to solve this oversight right now.
// Cast the underlying shape to raycast. Convex hulls do not have an inherited raycast function
SHRaycastResult result;
switch (SHAPE->GetType())
{
case SHCollisionShape::Type::SPHERE:
{
result = dynamic_cast<const SHSphereCollisionShape*>(SHAPE)->Raycast(ray);
break;
}
case SHCollisionShape::Type::BOX:
{
result = dynamic_cast<const SHBoxCollisionShape*>(SHAPE)->Raycast(ray);
break;
}
case SHCollisionShape::Type::CAPSULE:
{
// TODO
break;
}
default: break;
}
// If distance is greater than specified, skip this result
if (!result || result.distance > distance)
continue;
SHPhysicsRaycastResult physicsResult;
physicsResult.hit = result.hit;
physicsResult.distance = result.distance;
physicsResult.angle = result.angle;
physicsResult.position = result.position;
physicsResult.normal = result.normal;
physicsResult.entityHit = EID;
physicsResult.shapeIndex = IDX;
raycastHits.emplace_back(physicsResult);
}
// Sort by distance
std::ranges::sort(raycastHits.begin(), raycastHits.end(), [](const SHPhysicsRaycastResult& lhs, const SHPhysicsRaycastResult& rhs)
{
return lhs.distance < rhs.distance;
});
return raycastHits;
}
const SHCollisionSpace::RaycastHits& SHCollisionSpace::Linecast(const SHVec3& start, const SHVec3& end, uint16_t layer) noexcept
{
// Create bounded ray and reuse the raycast function
const SHRay BOUNDED_RAY{ start, SHVec3::Normalise(end - start) };
const float DISTANCE = SHVec3::Distance(end, start);
return Raycast(BOUNDED_RAY, DISTANCE, layer);
}
const SHCollisionSpace::RaycastHits& SHCollisionSpace::ColliderRaycast(EntityID colliderEID, const SHRay& ray, float distance, uint16_t layer) noexcept
{
raycastHits.clear();
const auto& POTENTIAL_HITS = broadphase.Query(ray, distance);
if (POTENTIAL_HITS.empty())
return raycastHits;
// Test potential hits individually
// Cull entities that are on different layers
for (auto& shapeID : POTENTIAL_HITS)
{
// Get shape
const EntityID EID = shapeID.GetEntityID();
const auto* COLLIDER = colliders.find(EID)->second;
// Cull any shapes on the same entity
if (EID == colliderEID)
continue;
const uint32_t IDX = shapeID.GetShapeIndex();
const auto* SHAPE = COLLIDER->GetCollisionShape(IDX);
// Cull by layer
const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & layer;
if (!LAYER_MATCH)
continue;
// Well this is awkward...I honestly don't have the mental capacity to solve this oversight right now.
// Cast the underlying shape to raycast. Convex hulls do not have an inherited raycast function
SHRaycastResult result;
switch (SHAPE->GetType())
{
case SHCollisionShape::Type::SPHERE:
{
result = dynamic_cast<const SHSphereCollisionShape*>(SHAPE)->Raycast(ray);
break;
}
case SHCollisionShape::Type::BOX:
{
result = dynamic_cast<const SHBoxCollisionShape*>(SHAPE)->Raycast(ray);
break;
}
case SHCollisionShape::Type::CAPSULE:
{
// TODO
break;
}
default: break;
}
// If distance is greater than specified, skip this result
if (!result || result.distance > distance)
continue;
SHPhysicsRaycastResult physicsResult;
physicsResult.hit = result.hit;
physicsResult.distance = result.distance;
physicsResult.angle = result.angle;
physicsResult.position = result.position;
physicsResult.normal = result.normal;
physicsResult.entityHit = EID;
physicsResult.shapeIndex = IDX;
raycastHits.emplace_back(physicsResult);
}
// Sort by distance
std::ranges::sort(raycastHits.begin(), raycastHits.end(), [](const SHPhysicsRaycastResult& lhs, const SHPhysicsRaycastResult& rhs)
{
return lhs.distance < rhs.distance;
});
return raycastHits;
}
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
/* Private Member Functions Definitions */ /* Private Member Functions Definitions */

View File

@ -15,9 +15,10 @@
// Project Headers // Project Headers
#include "Broadphase/SHDynamicAABBTree.h" #include "Broadphase/SHDynamicAABBTree.h"
#include "Contacts/SHCollisionEvents.h"
#include "Physics/Dynamics/SHContactManager.h" #include "Physics/Dynamics/SHContactManager.h"
#include "SHCollider.h" #include "SHCollider.h"
#include "SHPhysicsRaycastResult.h"
#include "CollisionTags/SHCollisionTags.h"
namespace SHADE namespace SHADE
{ {
@ -33,6 +34,12 @@ namespace SHADE
class SH_API SHCollisionSpace class SH_API SHCollisionSpace
{ {
public: public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
using RaycastHits = std::vector<SHPhysicsRaycastResult>;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Constructors & Destructor */ /* Constructors & Destructor */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -63,7 +70,7 @@ namespace SHADE
* @param collider * @param collider
* A collider to add. Duplicates will be ignored. * A collider to add. Duplicates will be ignored.
*/ */
void AddCollider (SHCollider* collider) noexcept; void AddCollider (SHCollider* collider) noexcept;
/** /**
* @brief * @brief
@ -72,21 +79,87 @@ namespace SHADE
* @param collider * @param collider
* A collider to remove. If a reference to it doesn't exist, it will be ignored. * A collider to remove. If a reference to it doesn't exist, it will be ignored.
*/ */
void RemoveCollider (SHCollider* collider) noexcept; void RemoveCollider (SHCollider* collider) noexcept;
/** /**
* @brief * @brief
* Invoke this method to update the broadphase of colliders that have been moved since * Invoke this method to update the broadphase of colliders that have been moved since
* the last frame. * the last frame.
*/ */
void UpdateBroadphase () noexcept; void UpdateBroadphase () noexcept;
/** /**
* @brief * @brief
* Detects collisions between all colliders. Results are sent to the attached contact * Detects collisions between all colliders. Results are sent to the attached contact
* manager for resolution. * manager for resolution.
*/ */
void DetectCollisions () noexcept; void DetectCollisions () noexcept;
/**
* @brief
* Casts a ray into the collision space.
* @param ray
* The ray to cast. The direction of the ray must be normalised.
* @param distance
* The distance to cast the ray. Defaults to infinity.
* @param layer
* The layer(s) the ray is casting on. Defaults to all layers.
* @return
* A container of all the objects the raycast hit, ordered by distance. <br/>
* The first object in the container is the first object hit etc.
*/
[[nodiscard]] const RaycastHits& Raycast
(
const SHRay& ray
, float distance = std::numeric_limits<float>::infinity()
, uint16_t layer = static_cast<uint16_t>(SHCollisionTag::Layer::ALL)
) noexcept;
/**
* @brief
* Casts a bounded ray into the collision space.
* @param start
* The origin of the ray.
* @param end
* The end of the ray.
* @param layer
* The layer(s) the ray is casting on. Defaults to all layers.
* @return
* A container of all the objects the raycast hit, ordered by distance. <br/>
* The first object in the container is the first object hit etc.
*/
[[nodiscard]] const RaycastHits& Linecast
(
const SHVec3& start
, const SHVec3& end
, uint16_t layer = static_cast<uint16_t>(SHCollisionTag::Layer::ALL)
) noexcept;
/**
* @brief
* Casts a ray into the collision space from a collider's position. <br/>
* The collider and all it's shapes will be ignored.
* @param colliderEID
* The entityID of the collider to cast from.
* @param ray
* The ray to cast. <br/>
* The position of the ray is the position offset from the collider's position. <br/>
* The direction of the ray must be normalised.
* @param distance
* The distance to cast the ray. Defaults to infinity.
* @param layer
* The layer(s) the ray is casting on. Defaults to all layers.
* @return
* A container of all the objects the raycast hit, ordered by distance. <br/>
* The first object in the container is the first object hit etc.
*/
[[nodiscard]] const RaycastHits& ColliderRaycast
(
EntityID colliderEID
, const SHRay& ray
, float distance = std::numeric_limits<float>::infinity()
, uint16_t layer = static_cast<uint16_t>(SHCollisionTag::Layer::ALL)
) noexcept;
private: private:
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
@ -111,20 +184,24 @@ namespace SHADE
Colliders colliders; Colliders colliders;
NarrowphaseBatch narrowphaseBatch; NarrowphaseBatch narrowphaseBatch;
RaycastHits raycastHits; // Reusable container for raycast results
SHAABBTree broadphase; SHAABBTree broadphase;
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* Member Functions */ /* Member Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
// Broadphase helpers // Broadphase helpers
void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept;
// Narrowphase helpers // Narrowphase helpers
void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept;
void collideManifolds (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; void collideManifolds (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept;
}; };