diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp index fdfcc08b..acd7d014 100644 --- a/SHADE_Application/src/Application/SBApplication.cpp +++ b/SHADE_Application/src/Application/SBApplication.cpp @@ -176,6 +176,12 @@ namespace Sandbox drawColliders = !drawColliders; SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::COLLIDER, drawColliders); } + static bool drawRays = false; + if (SHInputManager::GetKeyDown(SHInputManager::SH_KEYCODE::F11)) + { + drawRays = !drawRays; + SHSystemManager::GetSystem()->SetDebugDrawFlag(SHPhysicsDebugDrawSystem::DebugDrawFlags::RAYCASTS, drawRays); + } } // Finish all graphics jobs first graphicsSystem->AwaitGraphicsExecution(); diff --git a/SHADE_Engine/src/Math/SHRay.cpp b/SHADE_Engine/src/Math/SHRay.cpp index 87f12b81..c4931aba 100644 --- a/SHADE_Engine/src/Math/SHRay.cpp +++ b/SHADE_Engine/src/Math/SHRay.cpp @@ -30,11 +30,17 @@ namespace SHADE , direction { dir } {} + SHRay::SHRay(const reactphysics3d::Ray rp3dRay) noexcept + : position { rp3dRay.point1 } + , direction { SHVec3::Normalise(rp3dRay.point2 - rp3dRay.point1) } + {} + + /*-----------------------------------------------------------------------------------*/ /* Operator Overload Definitions */ /*-----------------------------------------------------------------------------------*/ - bool SHRay::operator==(const SHRay& rhs) noexcept + bool SHRay::operator==(const SHRay& rhs) const noexcept { const XMVECTOR LHS_POS = XMLoadFloat3(&position); const XMVECTOR RHS_POS = XMLoadFloat3(&rhs.position); @@ -45,7 +51,7 @@ namespace SHADE return XMVector3Equal(LHS_POS, RHS_POS) && XMVector3NotEqual(LHS_DIR, RHS_DIR); } - bool SHRay::operator!=(const SHRay& rhs) noexcept + bool SHRay::operator!=(const SHRay& rhs) const noexcept { const XMVECTOR LHS_POS = XMLoadFloat3(&position); const XMVECTOR RHS_POS = XMLoadFloat3(&rhs.position); @@ -56,5 +62,16 @@ namespace SHADE return XMVector3NotEqual(LHS_POS, RHS_POS) || XMVector3NotEqual(LHS_DIR, RHS_DIR); } + SHRay::operator reactphysics3d::Ray() const noexcept + { + // We use 2km. Temp solution. + return reactphysics3d::Ray{ position, position + (direction * MAX_RAYCAST_DIST) }; + } + + SHRaycastResult::operator bool() const noexcept + { + return hit; + } + } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Math/SHRay.h b/SHADE_Engine/src/Math/SHRay.h index 29d55b16..18efc224 100644 --- a/SHADE_Engine/src/Math/SHRay.h +++ b/SHADE_Engine/src/Math/SHRay.h @@ -1,7 +1,7 @@ /**************************************************************************************** * \file SHRay.h * \author Diren D Bharwani, diren.dbharwani, 390002520 - * \brief Interface for a Ray. + * \brief Interface for a Ray & Raycast Result * * \copyright Copyright (C) 2022 DigiPen Institute of Technology. Reproduction or * disclosure of this file or its contents without the prior written consent @@ -10,7 +10,7 @@ #pragma once -#include +#include // Project Headers #include "SH_API.h" @@ -29,6 +29,8 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ + static constexpr float MAX_RAYCAST_DIST = 2000.0f; // We use 2km as physics typically tends to lose accuracy beyond 2km. + SHVec3 position; SHVec3 direction; @@ -36,19 +38,46 @@ namespace SHADE /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ - SHRay() noexcept; - SHRay(const SHVec3& pos, const SHVec3& dir) noexcept; - SHRay(const SHRay& rhs) noexcept = default; - SHRay(SHRay&& rhs) noexcept = default; + SHRay () noexcept; + SHRay (const SHVec3& pos, const SHVec3& dir) noexcept; + SHRay (const reactphysics3d::Ray rp3dRay) noexcept; + + SHRay (const SHRay&) noexcept = default; + SHRay (SHRay&& ) noexcept = default; + ~SHRay() = default; /*---------------------------------------------------------------------------------*/ /* Operator Overloads */ /*---------------------------------------------------------------------------------*/ - SHRay& operator= (const SHRay& rhs) noexcept = default; - SHRay& operator= (SHRay&& rhs) noexcept = default; + SHRay& operator= (const SHRay&) noexcept = default; + SHRay& operator= (SHRay&&) noexcept = default; + + [[nodiscard]] bool operator==(const SHRay& rhs) const noexcept; + [[nodiscard]] bool operator!=(const SHRay& rhs) const noexcept; + + operator reactphysics3d::Ray() const noexcept; + }; + + struct SH_API SHRaycastResult + { + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + bool hit = false; + float distance = std::numeric_limits::infinity(); + float angle = 0.0f; + + SHVec3 position; + SHVec3 normal; + + /*---------------------------------------------------------------------------------*/ + /* Operator Overloads */ + /*---------------------------------------------------------------------------------*/ + + operator bool() const noexcept; - [[nodiscard]] bool operator==(const SHRay& rhs) noexcept; - [[nodiscard]] bool operator!=(const SHRay& rhs) noexcept; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h b/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h index 6262b946..62882556 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionListener.h @@ -32,7 +32,6 @@ namespace SHADE class SH_API SHCollisionListener final : public rp3d::EventListener { public: - /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp new file mode 100644 index 00000000..11b9fcab --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.cpp @@ -0,0 +1,350 @@ +/**************************************************************************************** + * \file SHPhysicsRaycaster.cpp + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Implementation for a Physics Raycaster. + * + * \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 + +// Primary Header +#include "SHPhysicsRaycaster.h" + +/* + * TODO(DIREN): + * Once the physics engine has been rebuilt, this whole implementation should change + * and just call PhysicsWorld.Raycast etc. + * + * SHRaycastResult can be converted to a bool when necessary. + */ + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Constructors & Destructor Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsRaycaster::SHPhysicsRaycaster() noexcept + : world { nullptr } + {} + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + const SHPhysicsRaycaster::RaycastPairs& SHPhysicsRaycaster::GetRaycasts() const noexcept + { + return raycasts; + } + + /*-----------------------------------------------------------------------------------*/ + /* Getter Function Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsRaycaster::SetObjectManager(SHPhysicsObjectManager* physicsObjectManager) noexcept + { + objectManager = physicsObjectManager; + } + + /*-----------------------------------------------------------------------------------*/ + /* Public Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + void SHPhysicsRaycaster::BindToWorld(rp3d::PhysicsWorld* physicsWorld) noexcept + { + world = physicsWorld; + } + + void SHPhysicsRaycaster::ClearFrame() noexcept + { + raycasts.clear(); + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::Raycast(const SHRay& ray, float distance) noexcept + { + // Reset temp + temp = SHPhysicsRaycastResult{}; + temp.distance = distance; + + if (!world) + { + SHLOG_ERROR("Physics world missing for raycasting!") + return temp; + } + + // If distance in infinity, cast to the default max distance of 2 km. + if (distance == std::numeric_limits::infinity()) + { + world->raycast(ray, this); + } + else + { + const SHVec3 END_POINT = ray.position + ray.direction * distance; + const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; + world->raycast(RP3D_RAY, this); + } + + // If a hit was found, populate temp info for return. + if (temp.hit) + { + temp.distance = SHVec3::Distance(ray.position, temp.position); + temp.angle = SHVec3::Angle(ray.position, temp.position); + } + + raycasts.emplace_back(ray, temp); + return temp; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::Linecast(const SHVec3& start, const SHVec3& end) noexcept + { + temp = SHPhysicsRaycastResult{}; + temp.distance = SHVec3::Distance(start, end); + + if (!world) + { + SHLOG_ERROR("Physics world missing for raycasting!") + return temp; + } + + const rp3d::Ray RP3D_RAY{ start, end }; + world->raycast(RP3D_RAY, this); + + if (temp.hit) + { + temp.distance = SHVec3::Distance(start, temp.position); + temp.angle = SHVec3::Angle(start, temp.position); + } + + raycasts.emplace_back(RP3D_RAY, temp); + return temp; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderRaycast(EntityID eid, const SHRay& ray, float distance) noexcept + { + SHPhysicsRaycastResult result; + result.distance = distance; + + // Get a valid physics object with at least 1 collider. + const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); + if (!PHYSICS_OBJECT) + return result; + + auto* rp3dBody = PHYSICS_OBJECT->GetCollisionBody(); + + // Data to populate + rp3d::RaycastInfo rp3dRaycastInfo; + bool hit = false; + + if (distance == std::numeric_limits::infinity()) + { + hit = rp3dBody->raycast(ray, rp3dRaycastInfo); + } + else + { + const SHVec3 END_POINT = ray.position + ray.direction * distance; + const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; + hit = rp3dBody->raycast(RP3D_RAY, rp3dRaycastInfo); + } + + if (hit) + { + result.hit = true; + result.position = rp3dRaycastInfo.worldPoint; + result.normal = rp3dRaycastInfo.worldPoint; + result.distance = SHVec3::Distance(ray.position, result.position); + result.angle = SHVec3::Angle(ray.position, result.position); + result.entityHit = eid; + result.shapeIndex = findColliderIndex(rp3dBody, rp3dRaycastInfo.collider->getEntity()); + } + + raycasts.emplace_back(ray, result); + return result; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderRaycast(EntityID eid, int shapeIndex, const SHRay& ray, float distance) noexcept + { + SHPhysicsRaycastResult result; + result.distance = distance; + + // Get a valid physics object with at least 1 collider. + const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); + if (!PHYSICS_OBJECT) + return result; + + // Boundary check for shape index + if (shapeIndex < 0 || shapeIndex >= static_cast(PHYSICS_OBJECT->GetCollisionBody()->getNbColliders())) + { + SHLOGV_WARNING("Invalid collision shape index passed in") + return result; + } + + auto* rp3dCollider = PHYSICS_OBJECT->GetCollisionBody()->getCollider(shapeIndex); + + rp3d::RaycastInfo rp3dRaycastInfo; + bool hit = false; + if (distance == std::numeric_limits::infinity()) + { + hit = rp3dCollider->raycast(ray, rp3dRaycastInfo); + } + else + { + const SHVec3 END_POINT = ray.position + ray.direction * distance; + const rp3d::Ray RP3D_RAY{ ray.position, END_POINT }; + hit = rp3dCollider->raycast(RP3D_RAY, rp3dRaycastInfo); + } + + if (hit) + { + result.hit = true; + result.position = rp3dRaycastInfo.worldPoint; + result.normal = rp3dRaycastInfo.worldPoint; + result.distance = SHVec3::Distance(ray.position, result.position); + result.angle = SHVec3::Angle(ray.position, result.position); + result.entityHit = eid; + result.shapeIndex = shapeIndex; + } + + raycasts.emplace_back(ray, result); + return result; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderLinecast(EntityID eid, const SHVec3& start, const SHVec3& end) noexcept + { + SHPhysicsRaycastResult result; + result.distance = SHVec3::Distance(start, end); + + const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); + if (!PHYSICS_OBJECT) + return result; + + auto* rp3dBody = PHYSICS_OBJECT->GetCollisionBody(); + + rp3d::RaycastInfo rp3dRaycastInfo; + + const rp3d::Ray RP3D_RAY{ start, end }; + if (rp3dBody->raycast(RP3D_RAY, rp3dRaycastInfo)) + { + result.hit = true; + result.position = rp3dRaycastInfo.worldPoint; + result.normal = rp3dRaycastInfo.worldPoint; + result.distance = SHVec3::Distance(start, result.position); + result.angle = SHVec3::Angle(end, result.position); + result.entityHit = eid; + result.shapeIndex = findColliderIndex(rp3dBody, rp3dRaycastInfo.collider->getEntity()); + } + + raycasts.emplace_back(RP3D_RAY, result); + return result; + } + + SHPhysicsRaycastResult SHPhysicsRaycaster::ColliderLinecast(EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) noexcept + { + SHPhysicsRaycastResult result; + result.distance = SHVec3::Distance(start, end); + + const auto* PHYSICS_OBJECT = validateColliderRaycast(eid); + if (!PHYSICS_OBJECT) + return result; + + if (shapeIndex < 0 || shapeIndex >= static_cast(PHYSICS_OBJECT->GetCollisionBody()->getNbColliders())) + { + SHLOGV_WARNING("Invalid collision shape index passed in") + return result; + } + + auto* rp3dCollider = PHYSICS_OBJECT->GetCollisionBody()->getCollider(shapeIndex); + + rp3d::RaycastInfo rp3dRaycastInfo; + + const rp3d::Ray RP3D_RAY{ start, end }; + if (rp3dCollider->raycast(RP3D_RAY, rp3dRaycastInfo)) + { + result.hit = true; + result.position = rp3dRaycastInfo.worldPoint; + result.normal = rp3dRaycastInfo.worldPoint; + result.distance = SHVec3::Distance(start, result.position); + result.angle = SHVec3::Angle(end, result.position); + result.entityHit = eid; + result.shapeIndex = shapeIndex; + } + + raycasts.emplace_back(RP3D_RAY, result); + return result; + } + + rp3d::decimal SHPhysicsRaycaster::notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) + { + temp.hit = true; + temp.position = raycastInfo.worldPoint; + temp.normal = raycastInfo.worldNormal; + + if (!objectManager) + { + SHLOGV_ERROR("No physics object manager linked with raycaster to match bodies") + return 0.0f; + } + + // Compare body IDs to find the matching physics object + const auto HIT_BODY_EID = raycastInfo.body->getEntity(); + + for (const auto& [entityID, physicsObject] : objectManager->GetPhysicsObjects()) + { + const auto RP3D_BODY = physicsObject.GetCollisionBody(); + + // Match rp3d bodies + if (RP3D_BODY->getEntity() != HIT_BODY_EID) + continue; + + temp.entityHit = entityID; + + // Find collider index + if (const int INDEX = findColliderIndex(RP3D_BODY, raycastInfo.collider->getEntity()); INDEX > -1) + { + temp.shapeIndex = INDEX; + break; + } + } + + return 0.0f; + } + + /*-----------------------------------------------------------------------------------*/ + /* Private Function Member Definitions */ + /*-----------------------------------------------------------------------------------*/ + + SHPhysicsObject* SHPhysicsRaycaster::validateColliderRaycast(EntityID eid) noexcept + { + if (!objectManager) + { + SHLOGV_ERROR("No physics object manager linked with raycaster to match bodies") + return nullptr; + } + + auto* physicsObject = objectManager->GetPhysicsObject(eid); + if (!physicsObject || physicsObject->GetCollisionBody()->getNbColliders() == 0) + { + SHLOGV_WARNING("Cannot cast ray at an entity without colliders!") + return nullptr; + } + + return physicsObject; + } + + int SHPhysicsRaycaster::findColliderIndex(const rp3d::CollisionBody* rp3dBody, rp3d::Entity rp3dColliderEID) noexcept + { + const int NUM_COLLISION_SHAPES = static_cast(rp3dBody->getNbColliders()); + for (int i = 0; i < NUM_COLLISION_SHAPES; ++i) + { + const auto COLLIDER_EID = rp3dBody->getCollider(i)->getEntity(); + if (COLLIDER_EID == rp3dColliderEID) + return i; + } + + return -1; + } + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h new file mode 100644 index 00000000..6d1e6f2d --- /dev/null +++ b/SHADE_Engine/src/Physics/Collision/SHPhysicsRaycaster.h @@ -0,0 +1,142 @@ +/**************************************************************************************** + * \file SHPhysicsRaycaster.h + * \author Diren D Bharwani, diren.dbharwani, 390002520 + * \brief Interface for a Physics Raycaster. + * + * \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 + +#include + +// Project Headers +#include "Physics/PhysicsObject/SHPhysicsObjectManager.h" +#include "Physics/SHPhysicsWorld.h" +#include "Math/SHRay.h" +#include "SH_API.h" + +namespace SHADE +{ + /*-----------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*-----------------------------------------------------------------------------------*/ + + struct SH_API SHPhysicsRaycastResult : public SHRaycastResult + { + public: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + EntityID entityHit = MAX_EID; + int shapeIndex = -1; + + }; + + class SH_API SHPhysicsRaycaster : public reactphysics3d::RaycastCallback + { + private: + /*---------------------------------------------------------------------------------*/ + /* Type Definitions */ + /*---------------------------------------------------------------------------------*/ + + using RaycastPair = std::pair; + using RaycastPairs = std::vector; + + public: + /*---------------------------------------------------------------------------------*/ + /* Constructors & Destructor */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsRaycaster() noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*---------------------------------------------------------------------------------*/ + + [[nodiscard]] const RaycastPairs& GetRaycasts() const noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*---------------------------------------------------------------------------------*/ + + void SetObjectManager(SHPhysicsObjectManager* physicsObjectManager) noexcept; + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + void BindToWorld (rp3d::PhysicsWorld* physicsWorld) noexcept; + void ClearFrame () noexcept; + + // TODO(Diren): Filtering, return all shades ray hits + + SHPhysicsRaycastResult Raycast + ( + const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + SHPhysicsRaycastResult Linecast + ( + const SHVec3& start + , const SHVec3& end + ) noexcept; + + SHPhysicsRaycastResult ColliderRaycast + ( + EntityID eid + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + SHPhysicsRaycastResult ColliderRaycast + ( + EntityID eid + , int shapeIndex + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + SHPhysicsRaycastResult ColliderLinecast + ( + EntityID eid + , const SHVec3& start + , const SHVec3& end + ) noexcept; + + SHPhysicsRaycastResult ColliderLinecast + ( + EntityID eid + , int shapeIndex + , const SHVec3& start + , const SHVec3& end + ) noexcept; + + rp3d::decimal notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) override; + + private: + /*---------------------------------------------------------------------------------*/ + /* Data Members */ + /*---------------------------------------------------------------------------------*/ + + rp3d::PhysicsWorld* world; + SHPhysicsObjectManager* objectManager; // For + SHPhysicsRaycastResult temp; // Holds the temporary result after casting into the world + RaycastPairs raycasts; // Used for debug drawing + + /*---------------------------------------------------------------------------------*/ + /* Function Members */ + /*---------------------------------------------------------------------------------*/ + + SHPhysicsObject* validateColliderRaycast (EntityID eid) noexcept; + static int findColliderIndex (const rp3d::CollisionBody* rp3dBody, rp3d::Entity rp3dColliderEID) noexcept; + }; + + +} // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp index 0a0ff201..3cfa3ec9 100644 --- a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp +++ b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObject.cpp @@ -294,7 +294,7 @@ namespace SHADE if (!rp3dBody->isActive()) return; - const int NUM_SHAPES = static_cast(component.collisionShapes.size()); + const int NUM_SHAPES = static_cast(rp3dBody->getNbColliders()); for (int i = 0; i < NUM_SHAPES; ++i) { auto& collisionShape = component.collisionShapes[i]; diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp index ffd10e0c..389983c3 100644 --- a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp +++ b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.cpp @@ -23,11 +23,10 @@ namespace SHADE /* Static Data Member Definitions */ /*-----------------------------------------------------------------------------------*/ - SHPhysicsObjectManager::CommandFunctionPtr SHPhysicsObjectManager::componentFunc[2][3] + SHPhysicsObjectManager::CommandFunctionPtr SHPhysicsObjectManager::componentFunc[3][2] { - addRigidBody , addCollider - , removeRigidBody , removeCollider - , addCollisionShape , removeCollisionShape + addRigidBody , addCollider , addCollisionShape + , removeRigidBody , removeCollider , removeCollisionShape }; /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h index 641fd9df..f41c62ad 100644 --- a/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h +++ b/SHADE_Engine/src/Physics/PhysicsObject/SHPhysicsObjectManager.h @@ -150,7 +150,7 @@ namespace SHADE /* Data Members */ /*---------------------------------------------------------------------------------*/ - static CommandFunctionPtr componentFunc[2][3]; // 2 commands, 3 components + static CommandFunctionPtr componentFunc[3][2]; // 3 components, 2 commands rp3d::PhysicsCommon* factory = nullptr; rp3d::PhysicsWorld* world = nullptr; diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp index 7ccfb225..bf1debac 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.cpp @@ -15,7 +15,6 @@ // Project Headers #include "ECS_Base/Managers/SHSystemManager.h" -#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" #include "Scene/SHSceneManager.h" namespace SHADE @@ -26,11 +25,12 @@ namespace SHADE const SHPhysicsDebugDrawSystem::DebugDrawFunction SHPhysicsDebugDrawSystem::drawFunctions[NUM_FLAGS] = { - SHPhysicsDebugDrawSystem::drawColliders - , SHPhysicsDebugDrawSystem::drawColliderAABBs - , SHPhysicsDebugDrawSystem::drawBroadPhaseAABBs - , SHPhysicsDebugDrawSystem::drawContactPoints - , SHPhysicsDebugDrawSystem::drawContactNormals + drawColliders + , drawColliderAABBs + , drawBroadPhaseAABBs + , drawContactPoints + , drawContactNormals + , drawRaycasts }; SHVec3 SHPhysicsDebugDrawSystem::boxVertices[NUM_BOX_VERTICES]; @@ -42,13 +42,13 @@ namespace SHADE SHPhysicsDebugDrawSystem::SHPhysicsDebugDrawSystem() noexcept : debugDrawFlags { 0 } , physicsSystem { nullptr } - , rp3dDebugRenderer { nullptr } { debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER)] = SHColour::GREEN; debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::COLLIDER_AABB)] = SHColour::YELLOW; debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::BROAD_PHASE_AABB)] = SHColour::CYAN; debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_POINTS)] = SHColour::RED; debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::CONTACT_NORMALS)] = SHColour::RED; + debugColours[SHUtilities::ConvertEnum(DebugDrawFlags::RAYCASTS)] = SHColour::ORANGE; } SHPhysicsDebugDrawSystem::PhysicsDebugDrawRoutine::PhysicsDebugDrawRoutine() @@ -111,19 +111,30 @@ namespace SHADE { auto* system = reinterpret_cast(GetSystem()); + auto* debugDrawSystem = SHSystemManager::GetSystem(); + if (debugDrawSystem == nullptr) + { + SHLOG_ERROR("Unable to get a debug draw system for Physics Debug Drawing!") + return; + } + for (int i = 0; i < SHUtilities::ConvertEnum(DebugDrawFlags::NUM_FLAGS); ++i) { const bool DRAW = (system->debugDrawFlags & (1U << i)) > 0; if (DRAW) - drawFunctions[i](system->rp3dDebugRenderer); + drawFunctions[i](debugDrawSystem); } + + // Automatically clear the container of raycasts despite debug drawing state + // TODO(Diren): Move this somewhere else + system->physicsSystem->raycaster.ClearFrame(); } /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - void SHPhysicsDebugDrawSystem::drawColliders(rp3d::DebugRenderer* debugRenderer) noexcept + void SHPhysicsDebugDrawSystem::drawColliders(SHDebugDrawSystem* debugRenderer) noexcept { const auto& COLLIDER_SET = SHComponentManager::GetDense(); for (const auto& COLLIDER : COLLIDER_SET) @@ -136,34 +147,58 @@ namespace SHADE { switch (collisionShape.GetType()) { - case SHCollisionShape::Type::BOX: debugDrawBox(COLLIDER, collisionShape); break; - case SHCollisionShape::Type::SPHERE: debugDrawSphere(COLLIDER, collisionShape); break; + case SHCollisionShape::Type::BOX: debugDrawBox(debugRenderer, COLLIDER, collisionShape); break; + case SHCollisionShape::Type::SPHERE: debugDrawSphere(debugRenderer, COLLIDER, collisionShape); break; default: break; } } } } - void SHPhysicsDebugDrawSystem::drawColliderAABBs(rp3d::DebugRenderer* debugRenderer) noexcept + void SHPhysicsDebugDrawSystem::drawColliderAABBs(SHDebugDrawSystem* debugRenderer) noexcept { } - void SHPhysicsDebugDrawSystem::drawBroadPhaseAABBs(rp3d::DebugRenderer* debugRenderer) noexcept + void SHPhysicsDebugDrawSystem::drawBroadPhaseAABBs(SHDebugDrawSystem* debugRenderer) noexcept { } - void SHPhysicsDebugDrawSystem::drawContactPoints(rp3d::DebugRenderer* debugRenderer) noexcept + void SHPhysicsDebugDrawSystem::drawContactPoints(SHDebugDrawSystem* debugRenderer) noexcept { } - void SHPhysicsDebugDrawSystem::drawContactNormals(rp3d::DebugRenderer* debugRenderer) noexcept + void SHPhysicsDebugDrawSystem::drawContactNormals(SHDebugDrawSystem* debugRenderer) noexcept { } + void SHPhysicsDebugDrawSystem::drawRaycasts(SHDebugDrawSystem* debugRenderer) noexcept + { + auto* physicsSystem = SHSystemManager::GetSystem(); + if (!physicsSystem) + { + SHLOG_ERROR("Unable to retrieve physics system for debug drawing raycasts!") + return; + } + + const SHColour& RAY_COLOUR = SHColour::ORANGE; + + // Draw all raycast pairs + for (const auto& [ray, raycastResult] : physicsSystem->raycaster.GetRaycasts()) + { + // If infinity, it is an infinite raycast. If not, render the distance in raycastResult. + // Ignore the hit variable as it will always correspond to the length of the raycast, hit or miss. + const float RENDER_DIST = raycastResult.distance == std::numeric_limits::infinity() ? SHRay::MAX_RAYCAST_DIST : raycastResult.distance; + const SHVec3 END_POS = ray.position + (ray.direction * RENDER_DIST); + + debugRenderer->DrawLine(RAY_COLOUR, ray.position, END_POS); + } + } + + void SHPhysicsDebugDrawSystem::generateBox() noexcept { boxVertices[0] = { 0.5f, 0.5f, -0.5f }; // TOP_RIGHT_BACK @@ -176,15 +211,8 @@ namespace SHADE boxVertices[7] = { -0.5f, -0.5f, 0.5f }; // BTM_LEFT_FRONT } - void SHPhysicsDebugDrawSystem::debugDrawBox(const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept + void SHPhysicsDebugDrawSystem::debugDrawBox(SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept { - auto* debugDrawSystem = SHSystemManager::GetSystem(); - if (debugDrawSystem == nullptr) - { - SHLOG_ERROR("Unable to get a debug draw system for Physics Debug Drawing!") - return; - } - auto* BOX = reinterpret_cast(collisionShape.GetShape()); // Calculate final position & orientation @@ -211,27 +239,20 @@ namespace SHADE transformedVertices[IDX2] = SHVec3::Transform(boxVertices[IDX2], FINAL_TRS); // Draw 4 line to connect the quads - debugDrawSystem->DrawLine(COLLIDER_COLOUR, transformedVertices[IDX1], transformedVertices[IDX2]); + debugRenderer->DrawLine(COLLIDER_COLOUR, transformedVertices[IDX1], transformedVertices[IDX2]); } // A, B, C, D std::array backQuad { transformedVertices[0], transformedVertices[1], transformedVertices[3], transformedVertices[2] }; - debugDrawSystem->DrawPoly(COLLIDER_COLOUR, backQuad.begin(), backQuad.end()); + debugRenderer->DrawPoly(COLLIDER_COLOUR, backQuad.begin(), backQuad.end()); // E, F, G, H std::array frontQuad { transformedVertices[4], transformedVertices[5], transformedVertices[7], transformedVertices[6] }; - debugDrawSystem->DrawPoly(COLLIDER_COLOUR, frontQuad.begin(), frontQuad.end()); + debugRenderer->DrawPoly(COLLIDER_COLOUR, frontQuad.begin(), frontQuad.end()); } - void SHPhysicsDebugDrawSystem::debugDrawSphere(const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept + void SHPhysicsDebugDrawSystem::debugDrawSphere(SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept { - auto* debugDrawSystem = SHSystemManager::GetSystem(); - if (debugDrawSystem == nullptr) - { - SHLOG_ERROR("Unable to get a debug draw system for Physics Debug Drawing!") - return; - } - auto* SPHERE = reinterpret_cast(collisionShape.GetShape()); const SHColour COLLIDER_COLOUR = collisionShape.IsTrigger() ? SHColour::PURPLE : SHColour::GREEN; @@ -240,7 +261,7 @@ namespace SHADE const SHQuaternion FINAL_ROT = colliderComponent.GetOrientation() * SHQuaternion::FromEuler(collisionShape.GetRotationOffset()); const SHMatrix TR = SHMatrix::Rotate(FINAL_ROT) * SHMatrix::Translate(colliderComponent.GetPosition()); - debugDrawSystem->DrawSphere(COLLIDER_COLOUR, SHVec3::Transform(collisionShape.GetPositionOffset(), TR), SPHERE->GetWorldRadius()); + debugRenderer->DrawSphere(COLLIDER_COLOUR, SHVec3::Transform(collisionShape.GetPositionOffset(), TR), SPHERE->GetWorldRadius()); } } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h index 149ed6c1..867a6e11 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsDebugDrawSystem.h @@ -14,6 +14,7 @@ // Project Headers #include "ECS_Base/System/SHSystemRoutine.h" +#include "Graphics/MiddleEnd/Interface/SHDebugDrawSystem.h" #include "Math/SHColour.h" #include "SHPhysicsSystem.h" #include "Tools/Utilities/SHUtilities.h" @@ -38,6 +39,7 @@ namespace SHADE , BROAD_PHASE_AABB , CONTACT_POINTS , CONTACT_NORMALS + , RAYCASTS , NUM_FLAGS }; @@ -91,7 +93,7 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using DebugDrawFunction = void(*)(rp3d::DebugRenderer*) noexcept; + using DebugDrawFunction = void(*)(SHDebugDrawSystem*) noexcept; /*---------------------------------------------------------------------------------*/ /* Data Members */ @@ -108,7 +110,6 @@ namespace SHADE uint8_t debugDrawFlags; SHPhysicsSystem* physicsSystem; - rp3d::DebugRenderer* rp3dDebugRenderer; SHColour debugColours[NUM_FLAGS]; /*---------------------------------------------------------------------------------*/ @@ -117,11 +118,12 @@ namespace SHADE // Generic Draw Functions - static void drawColliders (rp3d::DebugRenderer* debugRenderer) noexcept; - static void drawColliderAABBs (rp3d::DebugRenderer* debugRenderer) noexcept; - static void drawBroadPhaseAABBs (rp3d::DebugRenderer* debugRenderer) noexcept; - static void drawContactPoints (rp3d::DebugRenderer* debugRenderer) noexcept; - static void drawContactNormals (rp3d::DebugRenderer* debugRenderer) noexcept; + static void drawColliders (SHDebugDrawSystem* debugRenderer) noexcept; + static void drawColliderAABBs (SHDebugDrawSystem* debugRenderer) noexcept; + static void drawBroadPhaseAABBs (SHDebugDrawSystem* debugRenderer) noexcept; + static void drawContactPoints (SHDebugDrawSystem* debugRenderer) noexcept; + static void drawContactNormals (SHDebugDrawSystem* debugRenderer) noexcept; + static void drawRaycasts (SHDebugDrawSystem* debugRenderer) noexcept; // Shape Generation Functions @@ -129,8 +131,8 @@ namespace SHADE // Shape Draw Functions - static void debugDrawBox (const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept; - static void debugDrawSphere (const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept; + static void debugDrawBox (SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept; + static void debugDrawSphere (SHDebugDrawSystem* debugRenderer, const SHColliderComponent& colliderComponent, const SHCollisionShape& collisionShape) noexcept; }; } // namespace SHADE diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 50590e04..fd18a0f8 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -22,10 +22,6 @@ #include "Scene/SHSceneManager.h" #include "Scripting/SHScriptEngine.h" -/*-------------------------------------------------------------------------------------*/ -/* Local Helper Functions */ -/*-------------------------------------------------------------------------------------*/ - namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -115,8 +111,9 @@ namespace SHADE SHEventManager::SubscribeTo(SH_EDITOR_ON_STOP_EVENT, ON_STOP_RECEIVER_PTR); #endif - // Link Physics Object Manager with System + // Link Physics Object Manager with System & Raycaster objectManager.SetFactory(factory); + raycaster.SetObjectManager(&objectManager); // Link Collision Listener with System collisionListener.BindToSystem(this); @@ -183,6 +180,35 @@ namespace SHADE } } + SHPhysicsRaycastResult SHPhysicsSystem::Raycast(const SHRay& ray, float distance) noexcept + { + return raycaster.Raycast(ray, distance); + } + + SHPhysicsRaycastResult SHPhysicsSystem::Linecast(const SHVec3& start, const SHVec3& end) + { + return raycaster.Linecast(start, end); + } + + SHPhysicsRaycastResult SHPhysicsSystem::ColliderRaycast(EntityID eid, const SHRay& ray, float distance) noexcept + { + return raycaster.ColliderRaycast(eid, ray, distance); + } + + SHPhysicsRaycastResult SHPhysicsSystem::ColliderRaycast(EntityID eid, int shapeIndex, const SHRay& ray, float distance) + { + return raycaster.ColliderRaycast(eid, shapeIndex, ray, distance); + } + + SHPhysicsRaycastResult SHPhysicsSystem::ColliderLinecast(EntityID eid, const SHVec3& start, const SHVec3& end) noexcept + { + return raycaster.ColliderLinecast(eid, start, end); + } + + SHPhysicsRaycastResult SHPhysicsSystem::ColliderLinecast(EntityID eid, int shapeIndex, const SHVec3& start, const SHVec3& end) + { + return raycaster.ColliderLinecast(eid, shapeIndex, start, end); + } void SHPhysicsSystem::AddCollisionShape(EntityID eid, int shapeIndex) { @@ -327,8 +353,9 @@ namespace SHADE // Create physics world worldState.CreateWorld(factory); - // Link Collision Listener + // Link Collision Listener & Raycaster collisionListener.BindToWorld(worldState.world); + raycaster.BindToWorld(worldState.world); // Link with object manager & create all physics objects objectManager.SetWorld(worldState.world); diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index 9638e05c..4892ee90 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -21,11 +21,11 @@ #include "ECS_Base/System/SHFixedSystemRoutine.h" #include "Math/Transform/SHTransformComponent.h" - #include "Physics/Collision/SHCollisionListener.h" +#include "Physics/Collision/SHPhysicsRaycaster.h" #include "Physics/Interface/SHRigidBodyComponent.h" #include "Physics/Interface/SHColliderComponent.h" -#include "Physics/PhysicsObject//SHPhysicsObjectManager.h" +#include "Physics/PhysicsObject/SHPhysicsObjectManager.h" #include "Physics/SHPhysicsWorld.h" @@ -79,6 +79,90 @@ namespace SHADE void ForceUpdate (); + /** + * @brief Casts a ray into the world. + * @param ray The ray to cast. + * @param distance The distance to cast the ray. Defaults to infinity. + * @return The result of the raycast. + */ + SHPhysicsRaycastResult Raycast + ( + const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + /** + * @brief Casts a bounded ray into the world. + * @param start The starting point of the ray. + * @param end The end point of the ray. + * @return The result of the raycast. + */ + SHPhysicsRaycastResult Linecast + ( + const SHVec3& start + , const SHVec3& end + ); + + /** + * @brief Casts a ray at a body with colliders. + * @param eid The entity to cast to. + * @param ray The ray to cast. + * @param distance The distance to cast the ray. Defaults to infinity. + * @return The result of the raycast. + */ + SHPhysicsRaycastResult ColliderRaycast + ( + EntityID eid + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + ) noexcept; + + /** + * @brief Casts a ray at a collider. + * @param eid The entity to cast to. + * @param shapeIndex The index of the collision shape. + * @param ray The ray to cast. + * @param distance The distance to cast the ray. Defaults to infinity. + * @return The result of the raycast. + */ + SHPhysicsRaycastResult ColliderRaycast + ( + EntityID eid + , int shapeIndex + , const SHRay& ray + , float distance = std::numeric_limits::infinity() + ); + + /** + * @brief Casts a bounded ray at a body with colliders. + * @param eid + * @param start + * @param end + * @return The result of the raycast. + */ + SHPhysicsRaycastResult ColliderLinecast + ( + EntityID eid + , const SHVec3& start + , const SHVec3& end + ) noexcept; + + /** + * @brief + * @param eid + * @param shapeIndex + * @param start + * @param end + * @return The result of the raycast. + */ + SHPhysicsRaycastResult ColliderLinecast + ( + EntityID eid + , int shapeIndex + , const SHVec3& start + , const SHVec3& end + ); + // Specific Handling for Collision Shapes as they are not under the Component System. // This is done as events need to be sent out. // TODO(Diren): Consider using a static method through the ColliderComponent. @@ -190,6 +274,7 @@ namespace SHADE SHPhysicsWorldState worldState; SHPhysicsObjectManager objectManager; SHCollisionListener collisionListener; + SHPhysicsRaycaster raycaster; /*---------------------------------------------------------------------------------*/ /* Function Members */