diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 46efab8267..73b5a3f915 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -56,7 +56,7 @@ set(CUBOS_ENGINE_SOURCE "src/cubos/engine/collisions/shapes/capsule.cpp" "src/cubos/engine/collisions/broad_phase/plugin.cpp" "src/cubos/engine/collisions/broad_phase/sweep_and_prune.cpp" - "src/cubos/engine/collisions/broad_phase/candidates.cpp" + "src/cubos/engine/collisions/broad_phase/potentially_colliding_with.cpp" "src/cubos/engine/physics/plugin.cpp" "src/cubos/engine/physics/gravity.cpp" diff --git a/engine/include/cubos/engine/collisions/broad_phase/candidates.hpp b/engine/include/cubos/engine/collisions/broad_phase/candidates.hpp deleted file mode 100644 index 78c4a61e55..0000000000 --- a/engine/include/cubos/engine/collisions/broad_phase/candidates.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/// @file -/// @brief Resource @ref cubos::engine::Candidates. -/// @ingroup broad-phase-collisions-plugin - -// FIXME: This should be private, but it's used in the sample. - -#pragma once - -#include -#include -#include - -#include -#include - -namespace cubos::engine -{ - /// @brief Resource which stores candidates found in broad phase collision detection. - /// @ingroup broad-phase-collisions-plugin - struct BroadPhaseCandidates - { - /// @brief Pair of entities that may collide. - using Candidate = std::pair; - - /// @brief Hash function to allow Candidates to be used as keys in an unordered_set. - struct CandidateHash - { - std::size_t operator()(const Candidate& candidate) const - { - return core::ecs::EntityHash()(candidate.first) ^ core::ecs::EntityHash()(candidate.second); - } - }; - - /// @brief Collision type for each pair of colliders. - enum class CollisionType - { - BoxBox = 0, - BoxCapsule, - CapsuleCapsule, - - Count ///< Number of collision types. - }; - - /// @brief Sets of collision candidates for each collision type. The index of the array is - /// the collision type. - std::unordered_set candidatesPerType[static_cast(CollisionType::Count)]; - - /// @brief Adds a collision candidate to the list of candidates for a specific collision type. - /// @param type Collision type. - /// @param candidate Collision candidate. - void addCandidate(CollisionType type, Candidate candidate); - - /// @brief Gets the collision candidates for a specific collision type. - /// @param type Collision type. - /// @return Collision candidates. - const std::unordered_set& candidates(CollisionType type) const; - - /// @brief Clears the list of collision candidates. - void clearCandidates(); - }; -} // namespace cubos::engine diff --git a/engine/include/cubos/engine/collisions/broad_phase/potentially_colliding_with.hpp b/engine/include/cubos/engine/collisions/broad_phase/potentially_colliding_with.hpp new file mode 100644 index 0000000000..4f9f459d85 --- /dev/null +++ b/engine/include/cubos/engine/collisions/broad_phase/potentially_colliding_with.hpp @@ -0,0 +1,19 @@ +/// @file +/// @brief Component @ref cubos::engine::PotentiallyCollidingWith. +/// @ingroup collisions-plugin + +// FIXME: This should be private, but it's used in the sample. + +#pragma once + +#include + +namespace cubos::engine +{ + /// @brief Component which represents a potential collisions. + /// @ingroup collisions-plugin + struct PotentiallyCollidingWith + { + CUBOS_REFLECT; + }; +} // namespace cubos::engine diff --git a/engine/samples/collisions/main.cpp b/engine/samples/collisions/main.cpp index e0babe5679..427da2a6ce 100644 --- a/engine/samples/collisions/main.cpp +++ b/engine/samples/collisions/main.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include @@ -103,18 +103,13 @@ static void updateTransform(State& state, const Input& input, Query query, State& state, const BroadPhaseCandidates& candidates) +static void updateCollided(Query query, State& state) { - for (auto [entity, collider] : query) + for (auto [ent, colliding, ent1] : query) { - for (const auto& [collider1, collider2] : - candidates.candidates(BroadPhaseCandidates::CollisionType::BoxCapsule)) + if ((ent == state.a && ent1 == state.b) || (ent == state.b && ent1 == state.a)) { - if (collider1 == entity || collider2 == entity) - { - state.collided = true; - break; - } + state.collided = true; } } } diff --git a/engine/src/cubos/engine/collisions/broad_phase/candidates.cpp b/engine/src/cubos/engine/collisions/broad_phase/candidates.cpp deleted file mode 100644 index a4a91484b2..0000000000 --- a/engine/src/cubos/engine/collisions/broad_phase/candidates.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include - -using namespace cubos::engine; - -using Candidate = BroadPhaseCandidates::Candidate; -using CandidateHash = BroadPhaseCandidates::CandidateHash; - -void BroadPhaseCandidates::addCandidate(CollisionType type, Candidate candidate) -{ - candidatesPerType[static_cast(type)].insert(candidate); -} - -const std::unordered_set& BroadPhaseCandidates::candidates(CollisionType type) const -{ - return candidatesPerType[static_cast(type)]; -} - -void BroadPhaseCandidates::clearCandidates() -{ - for (auto& candidates : candidatesPerType) - { - candidates.clear(); - } -} diff --git a/engine/src/cubos/engine/collisions/broad_phase/plugin.cpp b/engine/src/cubos/engine/collisions/broad_phase/plugin.cpp index a92862df5e..702f728ffa 100644 --- a/engine/src/cubos/engine/collisions/broad_phase/plugin.cpp +++ b/engine/src/cubos/engine/collisions/broad_phase/plugin.cpp @@ -1,7 +1,5 @@ - #include "plugin.hpp" -#include #include #include #include @@ -108,42 +106,18 @@ static void sweepSystem(BroadPhaseSweepAndPrune& sweepAndPrune) } } -BroadPhaseCandidates::CollisionType getCollisionType(bool box, bool capsule) -{ - if (box && capsule) - { - return BroadPhaseCandidates::CollisionType::BoxCapsule; - } - - if (box) - { - return BroadPhaseCandidates::CollisionType::BoxBox; - } - - return BroadPhaseCandidates::CollisionType::CapsuleCapsule; -} - /// @brief Finds all pairs of colliders which may be colliding. -/// -/// @details -/// TODO: This query is disgusting. We need a way to find if a component is present without reading it. -static void findPairsSystem( - Query, Opt, const Collider&> query, - const BroadPhaseSweepAndPrune& sweepAndPrune, BroadPhaseCandidates& candidates) +static void findPairsSystem(Commands cmds, Query query, + const BroadPhaseSweepAndPrune& sweepAndPrune) { - candidates.clearCandidates(); - for (glm::length_t axis = 0; axis < 3; axis++) { for (const auto& [entity, overlaps] : sweepAndPrune.sweepOverlapMaps[axis]) { - auto [_entity, box, capsule, collider] = *query.at(entity); + auto [_entity, collider] = *query.at(entity); for (const auto& other : overlaps) { - auto [_other, otherBox, otherCapsule, otherCollider] = *query.at(other); - - // TODO: Should this be inside the if statement? - auto type = getCollisionType(box || otherBox, capsule || otherCapsule); + auto [_other, otherCollider] = *query.at(other); switch (axis) { @@ -151,21 +125,21 @@ static void findPairsSystem( if (collider.worldAABB.overlapsY(otherCollider.worldAABB) && collider.worldAABB.overlapsZ(otherCollider.worldAABB)) { - candidates.addCandidate(type, {entity, other}); + cmds.relate(entity, other, PotentiallyCollidingWith{}); } break; case 1: // Y if (collider.worldAABB.overlapsX(otherCollider.worldAABB) && collider.worldAABB.overlapsZ(otherCollider.worldAABB)) { - candidates.addCandidate(type, {entity, other}); + cmds.relate(entity, other, PotentiallyCollidingWith{}); } break; case 2: // Z if (collider.worldAABB.overlapsX(otherCollider.worldAABB) && collider.worldAABB.overlapsY(otherCollider.worldAABB)) { - candidates.addCandidate(type, {entity, other}); + cmds.relate(entity, other, PotentiallyCollidingWith{}); } break; } @@ -174,10 +148,19 @@ static void findPairsSystem( } } +/// @brief Removes all pairs of entities found to be potentially colliding. +static void cleanPotentiallyCollidingPairs(Commands cmds, Query query) +{ + for (auto [entity, collidingWith, other] : query) + { + cmds.unrelate(entity, other); + } +} + void cubos::engine::broadPhaseCollisionsPlugin(Cubos& cubos) { - cubos.addResource(); cubos.addResource(); + cubos.addRelation(); cubos.system(trackNewCollidersSystem).tagged("cubos.collisions.aabb.setup"); @@ -189,5 +172,8 @@ void cubos::engine::broadPhaseCollisionsPlugin(Cubos& cubos) cubos.system(updateMarkersSystem).tagged("cubos.collisions.broad.markers").after("cubos.collisions.aabb.update"); cubos.system(sweepSystem).tagged("cubos.collisions.broad.sweep").after("cubos.collisions.broad.markers"); + cubos.system(cleanPotentiallyCollidingPairs) + .tagged("cubos.collisions.broad.clean") + .before("cubos.collisions.broad"); cubos.system(findPairsSystem).tagged("cubos.collisions.broad").after("cubos.collisions.broad.sweep"); } diff --git a/engine/src/cubos/engine/collisions/broad_phase/plugin.hpp b/engine/src/cubos/engine/collisions/broad_phase/plugin.hpp index 42be4d8daa..dcd261f18c 100644 --- a/engine/src/cubos/engine/collisions/broad_phase/plugin.hpp +++ b/engine/src/cubos/engine/collisions/broad_phase/plugin.hpp @@ -7,6 +7,7 @@ #pragma once +#include #include namespace cubos::engine @@ -15,8 +16,10 @@ namespace cubos::engine /// @ingroup engine /// @brief Adds broad-phase collision detection to @b CUBOS. /// + /// ## Relations + /// - @ref PotentiallyCollidingWith - relates two entities that may be colliding + /// /// ## Resources - /// - @ref BroadPhaseCandidates - stores broad phase collision data. /// - @ref BroadPhaseSweepAndPrune - stores sweep and prune markers. /// @brief Plugin entry function. diff --git a/engine/src/cubos/engine/collisions/broad_phase/potentially_colliding_with.cpp b/engine/src/cubos/engine/collisions/broad_phase/potentially_colliding_with.cpp new file mode 100644 index 0000000000..fd63f42bd5 --- /dev/null +++ b/engine/src/cubos/engine/collisions/broad_phase/potentially_colliding_with.cpp @@ -0,0 +1,11 @@ +#include +#include + +#include + +CUBOS_REFLECT_IMPL(cubos::engine::PotentiallyCollidingWith) +{ + return core::ecs::TypeBuilder("cubos::engine::PotentiallyCollidingWith") + .symmetric() + .build(); +} \ No newline at end of file diff --git a/engine/src/cubos/engine/collisions/plugin.cpp b/engine/src/cubos/engine/collisions/plugin.cpp index c2702dfecd..2e95d1413f 100644 --- a/engine/src/cubos/engine/collisions/plugin.cpp +++ b/engine/src/cubos/engine/collisions/plugin.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -51,53 +50,6 @@ static void setupNewCapsulesSystem(Query query, const BroadPhaseCandidates& candidates, - EventWriter events) -{ - for (const auto& [ent1, ent2] : candidates.candidates(BroadPhaseCandidates::CollisionType::BoxBox)) - { - auto [collider1] = *query.at(ent1); - auto [collider2] = *query.at(ent2); - - auto c1 = collider1.worldAABB.center(); - auto b1 = collider1.worldAABB.box(); - - auto c2 = collider2.worldAABB.center(); - auto b2 = collider2.worldAABB.box(); - - auto d = c2 - c1; - auto p = (b2.halfSize + b1.halfSize) - glm::abs(d); - - auto s = glm::sign(d); - - float penetration = 0.0F; - glm::vec3 normal{0.0F}; - glm::vec3 pos{0.0F}; - - if (p.x < p.y && p.x < p.z) - { - penetration = p.x; - normal.x = s.x; - pos.x = c1.x + (b1.halfSize.x * s.x); - } - else if (p.y < p.x && p.y < p.z) - { - penetration = p.y; - normal.y = s.y; - pos.y = c1.y + (b1.halfSize.y * s.y); - } - else - { - penetration = p.z; - normal.z = s.z; - pos.z = c1.z + (b1.halfSize.z * s.z); - } - - events.push(CollisionEvent{ent1, ent2, penetration, normal, pos}); - } -} - void cubos::engine::collisionsPlugin(Cubos& cubos) { cubos.addPlugin(transformPlugin); @@ -112,5 +64,4 @@ void cubos::engine::collisionsPlugin(Cubos& cubos) cubos.system(setupNewBoxesSystem).tagged("cubos.collisions.setup"); cubos.system(setupNewCapsulesSystem).tagged("cubos.collisions.setup"); - cubos.system(emitCollisionEventSystem).tagged("cubos.collisions").after("cubos.collisions.setup"); }