diff --git a/Assets/Scenes/PhysicsSandbox.shade b/Assets/Scenes/PhysicsSandbox.shade index 95434172..b07549cc 100644 --- a/Assets/Scenes/PhysicsSandbox.shade +++ b/Assets/Scenes/PhysicsSandbox.shade @@ -66,7 +66,7 @@ NumberOfChildren: 0 Components: Transform Component: - Translate: {x: -1.45715916, y: 7, z: 0.319963396} + Translate: {x: -1.45715916, y: 7, z: 0.64831841} Rotate: {x: -0, y: 0, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h index 14ff5509..0c65a94f 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHCollision.h @@ -84,9 +84,27 @@ namespace SHADE // Sphere VS Convex - static FaceQuery findClosestFace (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept; - static int32_t findClosestPoint (const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept; - static int32_t findVoronoiRegion (const SHSphereCollisionShape& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept; + static FaceQuery findClosestFace + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + ) noexcept; + + static int32_t findClosestPoint + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + , int32_t faceIndex + ) noexcept; + + static int32_t findVoronoiRegion + ( + const SHSphereCollisionShape& sphere + , const SHVec3& faceVertex + , const SHVec3& faceNormal + , const SHVec3& tangent1 + , const SHVec3& tangent2 + ) noexcept; // Capsule VS Convex diff --git a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp index 3827dc2f..8245d672 100644 --- a/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp +++ b/SHADE_Engine/src/Physics/Collision/Narrowphase/SHSphereVsConvex.cpp @@ -185,7 +185,11 @@ namespace SHADE /* Private Member Functions Definitions */ /*-----------------------------------------------------------------------------------*/ - SHCollision::FaceQuery SHCollision::findClosestFace(const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron) noexcept + SHCollision::FaceQuery SHCollision::findClosestFace + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + ) noexcept { FaceQuery faceQuery; @@ -227,7 +231,12 @@ namespace SHADE return faceQuery; } - int32_t SHCollision::findClosestPoint(const SHSphereCollisionShape& sphere, const SHConvexPolyhedronCollisionShape& polyhedron, int32_t faceIndex) noexcept + int32_t SHCollision::findClosestPoint + ( + const SHSphereCollisionShape& sphere + , const SHConvexPolyhedronCollisionShape& polyhedron + , int32_t faceIndex + ) noexcept { // Find closest point on face int32_t closestPointIndex = -1; @@ -253,7 +262,14 @@ namespace SHADE return closestPointIndex; } - int32_t SHCollision::findVoronoiRegion(const SHSphereCollisionShape& sphere, const SHVec3& faceVertex, const SHVec3& faceNormal, const SHVec3& tangent1, const SHVec3& tangent2) noexcept + int32_t SHCollision::findVoronoiRegion + ( + const SHSphereCollisionShape& sphere + , const SHVec3& faceVertex + , const SHVec3& faceNormal + , const SHVec3& tangent1 + , const SHVec3& tangent2 + ) noexcept { static constexpr int NUM_TANGENTS = 2; diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp index cbca890e..4dcc7cb7 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.cpp @@ -160,44 +160,53 @@ namespace SHADE contactManager->Update(); } - const SHCollisionSpace::RaycastHits& SHCollisionSpace::Raycast(const SHRay& ray, float distance, uint16_t layer) noexcept + const SHCollisionSpace::RaycastResults& SHCollisionSpace::Raycast(const RaycastInfo& info) noexcept { - raycastHits.clear(); + static RaycastResults results; + results.clear(); + + const bool FILTER_COLLIDER = info.colliderEntityID.has_value(); + + // Cast ray into the broadphase scene + const auto& POTENTIAL_HITS = broadphase.Query(info.ray, info.distance); - const auto& POTENTIAL_HITS = broadphase.Query(ray, distance); if (POTENTIAL_HITS.empty()) - return raycastHits; + return results; - // Test potential hits individually - // Cull entities that are on different layers - for (auto& shapeID : POTENTIAL_HITS) + // Iterate through all potential hits + const int NUM_HITS = static_cast(POTENTIAL_HITS.size()); + for (const int i : std::ranges::views::iota(0, NUM_HITS)) { + const auto HIT_ID = POTENTIAL_HITS[i]; + + const EntityID EID = HIT_ID.GetEntityID(); + + if (FILTER_COLLIDER && EID == info.colliderEntityID.value()) + continue; + // Get shape - const EntityID EID = shapeID.GetEntityID(); - const uint32_t IDX = shapeID.GetShapeIndex(); + const uint32_t IDX = HIT_ID.GetShapeIndex(); + const auto* SHAPE = colliders.find(EID)->second->GetCollisionShape(IDX); - const auto* COLLIDER = colliders.find(EID)->second; - const auto* SHAPE = COLLIDER->GetCollisionShape(IDX); - - // Cull by layer - const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & layer; + // Filter the layers + const bool LAYER_MATCH = SHAPE->GetCollisionTag().GetMask() & info.layers; 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 + // We cast to the underlying shape. THis is done because a convex hull will not have an inherited raycast method. + // Kinda awkward oversight...oops - SHRaycastResult result; + SHRaycastResult baseResult; switch (SHAPE->GetType()) { case SHCollisionShape::Type::SPHERE: { - result = dynamic_cast(SHAPE)->Raycast(ray); + baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); break; } case SHCollisionShape::Type::BOX: { - result = dynamic_cast(SHAPE)->Raycast(ray); + baseResult = dynamic_cast(SHAPE)->Raycast(info.ray); break; } case SHCollisionShape::Type::CAPSULE: @@ -205,118 +214,25 @@ namespace SHADE // TODO break; } - default: break; + default: continue; // Redundant case } - // If distance is greater than specified, skip this result - if (!result || result.distance > distance) + if (!baseResult || baseResult.distance > info.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; + // Copy to a physics raycast result + SHPhysicsRaycastResult result; + memcpy_s(&result, sizeof(SHRaycastResult), &baseResult, sizeof(SHRaycastResult)); - raycastHits.emplace_back(physicsResult); + result.entityHit = EID; + result.shapeIndex = IDX; + + results.emplace_back(result); } - // Sort by distance - std::ranges::sort(raycastHits.begin(), raycastHits.end(), [](const SHPhysicsRaycastResult& lhs, const SHPhysicsRaycastResult& rhs) - { - return lhs.distance < rhs.distance; - }); - - return raycastHits; + return results; } - 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(SHAPE)->Raycast(ray); - break; - } - case SHCollisionShape::Type::BOX: - { - result = dynamic_cast(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 */ diff --git a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h index 9d52a140..8607cf45 100644 --- a/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h +++ b/SHADE_Engine/src/Physics/Collision/SHCollisionSpace.h @@ -13,12 +13,15 @@ #pragma once +#include + // Project Headers #include "Broadphase/SHDynamicAABBTree.h" #include "Physics/Dynamics/SHContactManager.h" #include "SHCollider.h" #include "SHPhysicsRaycastResult.h" #include "CollisionTags/SHCollisionTags.h" +#include "CollisionTags/SHCollisionTags.h" namespace SHADE { @@ -38,7 +41,52 @@ namespace SHADE /* Type Definitions */ /*---------------------------------------------------------------------------------*/ - using RaycastHits = std::vector; + /** + * @brief + * Contains information to cast a ray into the collision space. + * The collider entityID and shape index is optional. + */ + struct RaycastInfo + { + private: + /*-------------------------------------------------------------------------------*/ + /* Friends */ + /*-------------------------------------------------------------------------------*/ + + friend class SHCollisionSpace; + + public: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + bool continuous = false; + uint16_t layers = static_cast(SHCollisionTag::Layer::ALL); + float distance = std::numeric_limits::infinity(); + SHRay ray; + + /*-------------------------------------------------------------------------------*/ + /* Setter Functions */ + /*-------------------------------------------------------------------------------*/ + + /** + * @brief + * Sets the collider ID for the raycast. Setting this specifies that the ray + * should ignore this collider. + * @param eid + * The entity ID of the collider. + */ + void SetColliderID(EntityID eid) noexcept { colliderEntityID = eid; } + + private: + /*-------------------------------------------------------------------------------*/ + /* Data Members */ + /*-------------------------------------------------------------------------------*/ + + std::optional colliderEntityID; + }; + + using RaycastResults = std::vector; /*---------------------------------------------------------------------------------*/ /* Constructors & Destructor */ @@ -98,68 +146,13 @@ namespace SHADE /** * @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. + * @param info + * Contains the information for the raycast. * @return - * A container of all the objects the raycast hit, ordered by distance.
- * The first object in the container is the first object hit etc. + * A container of the objects hit by the ray. If nothing was hit, the container + * will be empty. */ - [[nodiscard]] const RaycastHits& Raycast - ( - const SHRay& ray - , float distance = std::numeric_limits::infinity() - , uint16_t layer = static_cast(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.
- * 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(SHCollisionTag::Layer::ALL) - ) noexcept; - - /** - * @brief - * Casts a ray into the collision space from a collider's position.
- * 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.
- * The position of the ray is the position offset from the collider's position.
- * 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.
- * 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::infinity() - , uint16_t layer = static_cast(SHCollisionTag::Layer::ALL) - ) noexcept; + [[nodiscard]] const RaycastResults& Raycast(const RaycastInfo& info) noexcept; private: /*---------------------------------------------------------------------------------*/ @@ -184,25 +177,20 @@ namespace SHADE Colliders colliders; NarrowphaseBatch narrowphaseBatch; - RaycastHits raycastHits; // Reusable container for raycast results - SHAABBTree broadphase; - - /*---------------------------------------------------------------------------------*/ /* Member Functions */ /*---------------------------------------------------------------------------------*/ // Broadphase helpers - void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; + void broadphaseQuery (SHRigidBody::Type rigidBodyType, SHCollider* collider) noexcept; // Narrowphase helpers - void collideTriggers (const SHCollisionKey& key, NarrowphasePair& narrowphasePair) const noexcept; - void collideManifolds (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; }; } // namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp index 508c19ad..f9e7a3cd 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.cpp @@ -19,6 +19,7 @@ #include "ECS_Base/Managers/SHEntityManager.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "Editor/SHEditor.h" +#include "Physics/Collision/SHCollisionSpace.h" #include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h" #include "Physics/Interface/SHColliderComponent.h" #include "Scripting/SHScriptEngine.h" @@ -189,6 +190,25 @@ namespace SHADE } } + const std::vector& SHPhysicsSystem::Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept + { + auto& results = collisionSpace->Raycast(info); + + // Load start and end points into the container for debug drawing + #ifdef SHEDITOR + + SHVec3 endPos = info.ray.position + info.ray.direction * SHRay::MAX_RAYCAST_DIST; + + if (!results.empty()) + endPos = results.back().position; + + raycastHits.emplace_back(info.ray.position, endPos); + + #endif + + return results; + } + /*-----------------------------------------------------------------------------------*/ /* Private Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h index c7dff6c6..712727ec 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystem.h @@ -76,8 +76,25 @@ namespace SHADE */ void Init() override; void Exit() override; + + /** + * @brief + * Forces the world to take a single step. Interpolation of bodies cannot be done when + * forcing an update. + */ void ForceUpdate(); + /** + * @brief + * Casts a ray into the collision space. + * @param info + * Contains the information for the raycast. + * @return + * A container of the objects hit by the ray. If nothing was hit, the container + * will be empty. + */ + [[nodiscard]] const std::vector& Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept; + /*---------------------------------------------------------------------------------*/ /* System Routines */ /*---------------------------------------------------------------------------------*/ @@ -129,6 +146,12 @@ namespace SHADE using EventFunctionPair = std::pair; + struct RaycastHit + { + SHVec3 start; + SHVec3 end; + }; + /*---------------------------------------------------------------------------------*/ /* Data Members */ /*---------------------------------------------------------------------------------*/ @@ -149,6 +172,11 @@ namespace SHADE SHCollisionSpace* collisionSpace; SHPhysicsObjectManager physicsObjectManager; + // For the debug drawer to draw rays + #ifdef SHEDITOR + std::vector raycastHits; + #endif + /*---------------------------------------------------------------------------------*/ /* Function Members */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp index f72eb271..b6ed9d56 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.cpp @@ -67,4 +67,16 @@ namespace SHADE return 0.0; } + const std::vector& SHPhysicsSystemInterface::Raycast(const SHCollisionSpace::RaycastInfo& info) noexcept + { + static std::vector emptyVec; + + auto* physicsSystem = SHSystemManager::GetSystem(); + if (physicsSystem) + return physicsSystem->Raycast(info); + + SHLOGV_WARNING("Failed to get fixed update rate. 0.0 returned instead."); + return emptyVec; + } + } diff --git a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h index e6103e87..8a1f6be8 100644 --- a/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h +++ b/SHADE_Engine/src/Physics/System/SHPhysicsSystemInterface.h @@ -14,6 +14,7 @@ of DigiPen Institute of Technology is prohibited. // Project Headers #include "ECS_Base/Entity/SHEntity.h" +#include "Physics/Collision/SHCollisionSpace.h" #include "Physics/Collision/Contacts/SHCollisionEvents.h" @@ -26,6 +27,7 @@ namespace SHADE class SHVec3; struct SHRay; struct SHPhysicsRaycastResult; + struct SHCollisionSpace::RaycastInfo; /*-----------------------------------------------------------------------------------*/ @@ -47,9 +49,12 @@ namespace SHADE /* Static Usage Functions */ /*---------------------------------------------------------------------------------*/ - [[nodiscard]] static const std::vector& GetCollisionInfo () noexcept; - [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; - [[nodiscard]] static double GetFixedDT () noexcept; - [[nodiscard]] static int GetFixedUpdateRate () noexcept; + [[nodiscard]] static const std::vector& GetCollisionInfo () noexcept; + [[nodiscard]] static const std::vector& GetTriggerInfo () noexcept; + [[nodiscard]] static double GetFixedDT () noexcept; + [[nodiscard]] static int GetFixedUpdateRate () noexcept; + [[nodiscard]] static const std::vector& Raycast (const SHCollisionSpace::RaycastInfo& info) noexcept; + + }; } diff --git a/SHADE_Managed/src/Components/Collider.cxx b/SHADE_Managed/src/Components/Collider.cxx index 79140d2b..9ed46289 100644 --- a/SHADE_Managed/src/Components/Collider.cxx +++ b/SHADE_Managed/src/Components/Collider.cxx @@ -128,39 +128,19 @@ namespace SHADE /*---------------------------------------------------------------------------------*/ Vector3 BoxCollider::Center::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetCenter()); - return Vector3::Zero; - } - void BoxCollider::Center::set(Vector3 value) - { - //getNativeCollisionShape().SetCenter(Convert::ToNative(value)); + return Convert::ToCLI(getNativeCollisionShape().GetCenter()); } Vector3 BoxCollider::HalfExtents::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetExtents()); - return Vector3::Zero; + return Convert::ToCLI(getNativeCollisionShape().GetWorldExtents()); } void BoxCollider::HalfExtents::set(Vector3 value) { - //getNativeCollisionShape().SetExtents(Convert::ToNative(value)); + getNativeCollisionShape().SetWorldExtents(Convert::ToNative(value)); } - Vector3 BoxCollider::Min::get() + Quaternion BoxCollider::Orientation::get() { - //return Convert::ToCLI(getNativeCollisionShape().GetMin()); - return Vector3::Zero; - } - void BoxCollider::Min::set(Vector3 value) - { - //getNativeCollisionShape().SetMin(Convert::ToNative(value)); - } - Vector3 BoxCollider::Max::get() - { - //return Convert::ToCLI(getNativeCollisionShape().GetMax()); - return Vector3::Zero; - } - void BoxCollider::Max::set(Vector3 value) - { - //getNativeCollisionShape().SetMax(Convert::ToNative(value)); + return Convert::ToCLI(getNativeCollisionShape().GetOrientation()); } /*---------------------------------------------------------------------------------*/ @@ -184,10 +164,6 @@ namespace SHADE { return Convert::ToCLI(getNativeCollisionShape().GetCenter()); } - void SphereCollider::Center::set(Vector3 value) - { - getNativeCollisionShape().SetCenter(Convert::ToNative(value)); - } float SphereCollider::Radius::get() { return getNativeCollisionShape().GetWorldRadius(); diff --git a/SHADE_Managed/src/Components/Collider.hxx b/SHADE_Managed/src/Components/Collider.hxx index a649483f..19235571 100644 --- a/SHADE_Managed/src/Components/Collider.hxx +++ b/SHADE_Managed/src/Components/Collider.hxx @@ -142,15 +142,14 @@ namespace SHADE /* Properties */ /*-----------------------------------------------------------------------------*/ /// - /// Center of the Bounding Box formed by this bound. + /// Center of the box collider. /// property Vector3 Center { Vector3 get(); - void set(Vector3 value); } /// - /// Half of the scale of the Bounding Box formed by this bound. + /// Half of the scale of the box collider. /// property Vector3 HalfExtents { @@ -158,22 +157,11 @@ namespace SHADE void set(Vector3 value); } /// - /// Position of the bottom left back corner of the Bounding Box formed by this - /// bound. + /// The orientation of the box. /// - property Vector3 Min + property Quaternion Orientation { - Vector3 get(); - void set(Vector3 value); - } - /// - /// Position of the top right front corner of the Bounding Box formed by this - /// bound. - /// - property Vector3 Max - { - Vector3 get(); - void set(Vector3 value); + Quaternion get(); } /*-----------------------------------------------------------------------------*/ @@ -201,12 +189,11 @@ namespace SHADE /* Properties */ /*-----------------------------------------------------------------------------*/ /// - /// Center of the Bounding Sphere formed by this bound. + /// Center of the sphere. /// property Vector3 Center { Vector3 get(); - void set(Vector3 value); } /// /// Radius of the Bounding Sphere formed by this bound. diff --git a/SHADE_Managed/src/Physics/Physics.cxx b/SHADE_Managed/src/Physics/Physics.cxx index fc54f527..bfb79b75 100644 --- a/SHADE_Managed/src/Physics/Physics.cxx +++ b/SHADE_Managed/src/Physics/Physics.cxx @@ -15,10 +15,15 @@ // External Dependencies #include "Physics/System/SHPhysicsSystemInterface.h" // Project Header +#include "Components/Collider.hxx" +#include "Components/Transform.hxx" #include "Engine/GameObject.hxx" +#include "Physics/Collision/SHCollisionSpace.h" #include "Utility/Convert.hxx" #include "Utility/Debug.hxx" +using namespace System::Collections::Generic; + namespace SHADE { /*-----------------------------------------------------------------------------------*/ @@ -41,49 +46,301 @@ namespace SHADE /* Raycast Function Member Definitions */ /*-----------------------------------------------------------------------------------*/ - RaycastHit Physics::Raycast(Ray ray) + List^ Physics::Raycast(Ray ray, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Cast natively + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.continuous = continuous; + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::Raycast(Ray ray, float distance) + List^ Physics::Raycast(Ray ray, float distance, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Cast natively + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.distance = distance; + raycastInfo.continuous = continuous; + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::Linecast(Vector3 start, Vector3 end) + List^ Physics::Linecast(Vector3 start, Vector3 end, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Cast natively + Vector3 direction = end - start; + direction.Normalise(); + const Ray CLI_RAY( start, direction ); + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(CLI_RAY); + raycastInfo.distance = (end - start).GetMagnitude(); + raycastInfo.continuous = continuous; + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderRaycast(GameObject object, Ray ray) + List^ Physics::ColliderRaycast(GameObject object, Ray ray, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position (same as the transform) + Transform^ managedTransform = object.GetComponent(); + if (!managedTransform) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); + + const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; + ray.Position += COLLIDER_POS; + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.continuous = continuous; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderRaycast(GameObject object, Ray ray, float distance) + List^ Physics::ColliderRaycast(GameObject object, Ray ray, float distance, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position (same as the transform) + Transform^ managedTransform = object.GetComponent(); + if (!managedTransform) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); + + const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; + ray.Position += COLLIDER_POS; + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.distance = distance; + raycastInfo.continuous = continuous; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray) + List^ Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position + Vector3 shapePos = Vector3::Zero; + Collider^ managedCollider = object.GetComponent(); + if (!managedCollider) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); + + CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); + if (!managedShape) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); + + const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); + switch (NATIVE_SHAPE.GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + case SHCollisionShape::Type::BOX: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + default: break; + } + + ray.Position += shapePos; + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.continuous = continuous; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, float distance) + List^ Physics::ColliderRaycast(GameObject object, int shapeIndex, Ray ray, float distance, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position + Vector3 shapePos = Vector3::Zero; + Collider^ managedCollider = object.GetComponent(); + if (!managedCollider) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); + + CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); + if (!managedShape) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); + + const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); + switch (NATIVE_SHAPE.GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + case SHCollisionShape::Type::BOX: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + default: break; + } + + ray.Position += shapePos; + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(ray); + raycastInfo.continuous = continuous; + raycastInfo.distance = distance; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderLineCast(GameObject object, Vector3 start, Vector3 end) + List^ Physics::ColliderLineCast(GameObject object, Vector3 start, Vector3 end, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position (same as the transform) + Transform^ managedTransform = object.GetComponent(); + if (!managedTransform) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Transform."); + + const Vector3 COLLIDER_POS = managedTransform->GlobalPosition; + start += COLLIDER_POS; + + Vector3 direction = end - start; + direction.Normalise(); + const Ray CLI_RAY( start, direction ); + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(CLI_RAY); + raycastInfo.distance = (end - start).GetMagnitude(); + raycastInfo.continuous = continuous; + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } - RaycastHit Physics::ColliderLineCast(GameObject object, int shapeIndex, Vector3 start, Vector3 end) + List^ Physics::ColliderLineCast(GameObject object, int shapeIndex, Vector3 start, Vector3 end, bool continuous) { - return RaycastHit{}; + List^ results = gcnew List(); + + // Get the collider's position + Vector3 shapePos = Vector3::Zero; + Collider^ managedCollider = object.GetComponent(); + if (!managedCollider) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid Collider."); + + CollisionShape^ managedShape = managedCollider->GetCollisionShape(shapeIndex); + if (!managedShape) + throw gcnew System::InvalidOperationException("Attempted to retrieve invalid CollisionShape."); + + const auto& NATIVE_SHAPE = managedShape->getNativeCollisionShape(); + switch (NATIVE_SHAPE.GetType()) + { + case SHCollisionShape::Type::SPHERE: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + case SHCollisionShape::Type::BOX: + { + shapePos = Convert::ToCLI(dynamic_cast(NATIVE_SHAPE).GetCenter()); + break; + } + default: break; + } + + start += shapePos; + + Vector3 direction = end - start; + direction.Normalise(); + const Ray CLI_RAY( start, direction ); + + SHCollisionSpace::RaycastInfo raycastInfo; + raycastInfo.ray = Convert::ToNative(CLI_RAY); + raycastInfo.continuous = continuous; + raycastInfo.distance = (end - start).GetMagnitude(); + raycastInfo.SetColliderID(object.EntityId); + + const auto& NATIVE_RESULTS = SHPhysicsSystemInterface::Raycast(raycastInfo); + if (!NATIVE_RESULTS.empty()) + { + for (const auto& nativeResult : NATIVE_RESULTS) + results->Add(Convert::ToCLI(nativeResult)); + } + + return results; } /*-----------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Physics/Physics.hxx b/SHADE_Managed/src/Physics/Physics.hxx index f13e5952..abbd1891 100644 --- a/SHADE_Managed/src/Physics/Physics.hxx +++ b/SHADE_Managed/src/Physics/Physics.hxx @@ -10,6 +10,7 @@ #pragma once + // Project Includes #include "Math/Ray.hxx" #include "RaycastHit.hxx" @@ -39,83 +40,146 @@ namespace SHADE /* Raycast Function Members */ /*---------------------------------------------------------------------------------*/ + // TODO(Diren): Add layers for raycasting + /// - /// Casts an infinite ray into the world. + /// Casts an infinite ray into the world.
+ /// This raycast will stop at the first object hit. ///
/// The ray to cast. - /// The result of the raycast. - static RaycastHit Raycast (Ray ray); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ Raycast (Ray ray, bool continuous); /// /// Casts a ray for a given distance into the world. /// /// The ray to cast. /// The distance to cast the ray. - /// The result of the raycast. - static RaycastHit Raycast (Ray ray, float distance); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ Raycast (Ray ray, float distance, bool continuous); /// /// Casts a bounded ray into the world. /// /// The start of the bounded ray. /// The end of the bounded ray. - /// The result of the raycast. - static RaycastHit Linecast (Vector3 start, Vector3 end); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ Linecast (Vector3 start, Vector3 end, bool continuous); /// /// Casts an infinite ray w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// The ray to cast. - /// The result of the raycast. - static RaycastHit ColliderRaycast (GameObject object, Ray ray); + /// + /// The ray to cast.
+ /// The position of the ray is offset from the collider's position. + /// + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderRaycast (GameObject object, Ray ray, bool continuous); /// /// Casts a ray for a given distance w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// The ray to cast. + /// + /// The ray to cast.
+ /// The position of the ray is offset from the collider's position. + /// /// The distance to cast the ray. - /// The result of the raycast. - static RaycastHit ColliderRaycast (GameObject object, Ray ray, float distance); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderRaycast (GameObject object, Ray ray, float distance, bool continuous); /// /// Casts an infinite ray w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// The ray to cast. - /// The result of the raycast. - static RaycastHit ColliderRaycast (GameObject object, int shapeIndex, Ray ray); + /// + /// The ray to cast.
+ /// The position of the ray is offset from the collider's position. + /// + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderRaycast (GameObject object, int shapeIndex, Ray ray, bool continuous); /// /// Casts a ray for a given distance w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// The ray to cast. + /// + /// The ray to cast.
+ /// The position of the ray is offset from the collider's position. + /// /// The distance to cast the ray. - /// The result of the raycast. - static RaycastHit ColliderRaycast (GameObject object, int shapeIndex, Ray ray, float distance); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderRaycast (GameObject object, int shapeIndex, Ray ray, float distance, bool continuous); /// /// Casts a bounded ray w.r.t a GameObject. /// /// The GameObject to cast the ray to. - /// The start of the bounded ray. + /// + /// The start of the bounded ray.
+ /// The start of the ray is offset from the collider's position. /// - /// The result of the raycast. - static RaycastHit ColliderLineCast (GameObject object, Vector3 start, Vector3 end); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned. + /// + static System::Collections::Generic::List^ ColliderLineCast (GameObject object, Vector3 start, Vector3 end, bool continuous); /// /// Casts a bounded ray w.r.t a specific collider on a GameObject. /// /// The GameObject to cast the ray to. /// The collision shape index on the collider to cast to. - /// The start of the bounded ray. + /// + /// The start of the bounded ray.
+ /// The start of the ray is offset from the collider's position. /// The end of the bounded ray. - /// The result of the raycast. - static RaycastHit ColliderLineCast (GameObject object, int shapeIndex, Vector3 start, Vector3 end); + /// + /// Whether or not the raycast should stop at the first object hit. + /// + /// + /// The results of the raycast. If nothing was hit, an empty list is returned./// + static System::Collections::Generic::List^ ColliderLineCast (GameObject object, int shapeIndex, Vector3 start, Vector3 end, bool continuous); private: /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Utility/Convert.cxx b/SHADE_Managed/src/Utility/Convert.cxx index 1d277274..e3a53661 100644 --- a/SHADE_Managed/src/Utility/Convert.cxx +++ b/SHADE_Managed/src/Utility/Convert.cxx @@ -103,6 +103,39 @@ namespace SHADE /* Physics Conversions */ /*---------------------------------------------------------------------------------*/ + SHPhysicsRaycastResult Convert::ToNative(RaycastHit cli) + { + // This function shouldn't be used anyway, so we leave the entityHit empty. + + SHPhysicsRaycastResult native; + + native.hit = cli.Hit; + native.position = ToNative(cli.Position); + native.normal = ToNative(cli.Normal); + native.distance = cli.Distance; + native.shapeIndex = cli.CollisionShapeIndex; + + return native; + } + + RaycastHit Convert::ToCLI(const SHPhysicsRaycastResult& native) + { + RaycastHit cli; + + cli.Hit = native.hit; + cli.Position = ToCLI(native.position); + cli.Normal = ToCLI(native.normal); + cli.Distance = native.distance; + cli.CollisionShapeIndex = native.shapeIndex; + + cli.Other = SHEntityManager::IsValidEID(native.entityHit) + ? GameObject(native.entityHit) + : System::Nullable(); + + return cli; + } + + /*---------------------------------------------------------------------------------*/ /* Handle Conversions */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Managed/src/Utility/Convert.hxx b/SHADE_Managed/src/Utility/Convert.hxx index 6fa4d935..f48eb66d 100644 --- a/SHADE_Managed/src/Utility/Convert.hxx +++ b/SHADE_Managed/src/Utility/Convert.hxx @@ -141,6 +141,20 @@ namespace SHADE /* Physics Conversions */ /*-----------------------------------------------------------------------------*/ + /// + /// Converts from a managed RaycastHit to a native SHPhysicsRaycastResult + /// + /// The managed RaycastHit to convert from. + /// Native copy of a managed RaycastHit. + static SHPhysicsRaycastResult ToNative(RaycastHit cli); + + /// + /// Converts from a native SHPhysicsRaycastResult to a managed RaycastHit + /// + /// The native SHPhysicsRaycastResult to convert from. + /// Managed copy of a native SHPhysicsRaycastResult. + static RaycastHit ToCLI(const SHPhysicsRaycastResult& native); + /*-----------------------------------------------------------------------------*/ /* Handle Conversions */ /*-----------------------------------------------------------------------------*/