Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split Collider and ColliderShape #666

Merged
merged 9 commits into from
Oct 5, 2023
4 changes: 2 additions & 2 deletions engine/include/cubos/engine/collisions/aabb.hpp
luishfonseca marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

namespace cubos::engine
{
/// @brief Component which stores the AABB of an entity with a collider component.
/// @brief AABB of a collider.
/// @ingroup collisions-plugin
struct [[cubos::component("cubos/aabb", VecStorage)]] ColliderAABB
struct ColliderAABB
{
/// @brief Minimum point of the diagonal of the AABB.
glm::vec3 min = glm::vec3{-INFINITY};
Expand Down
29 changes: 29 additions & 0 deletions engine/include/cubos/engine/collisions/collider.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// @file
/// @brief Component @ref cubos::engine::Collider.
/// @ingroup collisions-plugin

#pragma once

#include <glm/mat4x4.hpp>

#include <cubos/engine/collisions/aabb.hpp>

namespace cubos::engine
{
/// @brief Component which adds a collider to an entity.
/// @ingroup collisions-plugin
struct [[cubos::component("cubos/collider", VecStorage)]] Collider
{
glm::mat4 transform{1.0F}; ///< Transform of the collider.

ColliderAABB localAABB; ///< Local space AABB of the collider.
ColliderAABB worldAABB; ///< World space AABB of the collider.

/// @brief Margin of the collider.
///
/// When the collider shape has sharp edges, a margin is needed.
float margin;
luishfonseca marked this conversation as resolved.
Show resolved Hide resolved

bool fresh = true; ///< Whether the collider is fresh. This is an hack and should be done in ECS.
};
} // namespace cubos::engine
31 changes: 0 additions & 31 deletions engine/include/cubos/engine/collisions/colliders/box.hpp

This file was deleted.

25 changes: 0 additions & 25 deletions engine/include/cubos/engine/collisions/colliders/capsule.hpp

This file was deleted.

17 changes: 17 additions & 0 deletions engine/include/cubos/engine/collisions/shapes/box.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// @file
/// @brief Component @ref cubos::engine::BoxCollisionShape.
/// @ingroup collisions-plugin

#pragma once

#include <cubos/core/geom/box.hpp>

namespace cubos::engine
{
/// @brief Component which adds a box collision shape to an entity, used with a collider component.
luishfonseca marked this conversation as resolved.
Show resolved Hide resolved
/// @ingroup collisions-plugin
struct [[cubos::component("cubos/box_collision_shape", VecStorage)]] BoxCollisionShape
{
cubos::core::geom::Box shape; ///< Box shape.
};
} // namespace cubos::engine
17 changes: 17 additions & 0 deletions engine/include/cubos/engine/collisions/shapes/capsule.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// @file
/// @brief Component @ref cubos::engine::CapsuleCollisionShape.
/// @ingroup collisions-plugin

#pragma once

#include <cubos/core/geom/capsule.hpp>

namespace cubos::engine
{
/// @brief Component which adds a capsule collision shape to an entity, used with a collider component.
luishfonseca marked this conversation as resolved.
Show resolved Hide resolved
/// @ingroup collisions-plugin
struct [[cubos::component("cubos/capsule_collision_shape", VecStorage)]] CapsuleCollisionShape
{
cubos::core::geom::Capsule shape; ///< Capsule shape.
};
} // namespace cubos::engine
22 changes: 13 additions & 9 deletions engine/samples/collisions/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

#include <cubos/engine/collisions/aabb.hpp>
#include <cubos/engine/collisions/broad_phase_collisions.hpp>
#include <cubos/engine/collisions/colliders/box.hpp>
#include <cubos/engine/collisions/collider.hpp>
#include <cubos/engine/collisions/plugin.hpp>
#include <cubos/engine/collisions/shapes/box.hpp>
#include <cubos/engine/input/plugin.hpp>
#include <cubos/engine/renderer/plugin.hpp>
#include <cubos/engine/settings/settings.hpp>
Expand Down Expand Up @@ -60,15 +61,17 @@ static void init(Commands commands, Write<Input> input, Write<ActiveCameras> cam
static void addColliders(Write<State> state, Commands commands)
{
state->a = commands.create()
.add(BoxCollider{})
.add(Collider{})
.add(BoxCollisionShape{})
.add(LocalToWorld{})
.add(Position{glm::vec3{0.0F, 0.0F, -2.0F}})
.add(Rotation{})
.entity();
state->aRotationAxis = glm::sphericalRand(1.0F);

state->b = commands.create()
.add(BoxCollider{})
.add(Collider{})
.add(BoxCollisionShape{})
.add(LocalToWorld{})
.add(Position{glm::vec3{0.0F, 0.0F, 2.0F}})
.add(Rotation{})
Expand Down Expand Up @@ -105,7 +108,7 @@ static void updateTransform(Write<State> state, Read<Input> input, Query<Write<P
bPos->vec += glm::vec3{0.0F, 0.0F, -0.01F};
}

static void updateCollided(Query<Read<BoxCollider>> query, Write<State> state, Read<BroadPhaseCollisions> collisions)
static void updateCollided(Query<Read<Collider>> query, Write<State> state, Read<BroadPhaseCollisions> collisions)
{
for (auto [entity, collider] : query)
{
Expand All @@ -120,12 +123,13 @@ static void updateCollided(Query<Read<BoxCollider>> query, Write<State> state, R
}
}

static void render(Query<Read<LocalToWorld>, Read<BoxCollider>, Read<ColliderAABB>> query)
static void render(Query<Read<LocalToWorld>, Read<Collider>, Read<BoxCollisionShape>> query)
{
for (auto [entity, localToWorld, collider, aabb] : query)
for (auto [entity, localToWorld, collider, shape] : query)
{
cubos::core::gl::Debug::drawWireBox(collider->shape, localToWorld->mat * collider->transform);
cubos::core::gl::Debug::drawWireBox(aabb->box(), glm::translate(glm::mat4{1.0}, aabb->center()),
cubos::core::gl::Debug::drawWireBox(shape->shape, localToWorld->mat * collider->transform);
cubos::core::gl::Debug::drawWireBox(collider->worldAABB.box(),
glm::translate(glm::mat4{1.0}, collider->worldAABB.center()),
glm::vec3{1.0, 0.0, 0.0});
}
}
Expand All @@ -146,7 +150,7 @@ int main()
cubos.startupSystem(addColliders);

cubos.system(updateTransform).before("cubos.transform.update");
cubos.system(updateCollided).tagged("updated").after("cubos.collisions");
cubos.system(updateCollided).tagged("updated").after("cubos.collisions.broad");
cubos.system(render).after("updated");

cubos.run();
Expand Down
68 changes: 45 additions & 23 deletions engine/src/cubos/engine/collisions/broad_phase.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,41 @@
#include "broad_phase.hpp"

using cubos::engine::ColliderAABB;

using CollisionType = BroadPhaseCollisions::CollisionType;

void updateBoxAABBs(Query<Read<LocalToWorld>, Read<BoxCollider>, Write<ColliderAABB>> query)
void setupNewBoxes(Query<Read<BoxCollisionShape>, Write<Collider>> query, Write<BroadPhaseCollisions> collisions)
{
for (auto [entity, localToWorld, collider, aabb] : query)
for (auto [entity, shape, collider] : query)
{
if (collider->fresh)
{
collisions->addEntity(entity);

glm::vec3 diag[2];
shape->shape.diag(diag);
collider->localAABB = ColliderAABB{diag[0], diag[1]};

collider->margin = 0.04F;

collider->fresh = false;
}
}
}

void setupNewCapsules(Query<Read<CapsuleCollisionShape>, Write<Collider>> query, Write<BroadPhaseCollisions> collisions)
{
(void)query;
(void)collisions;
}

void updateAABBs(Query<Read<LocalToWorld>, Write<Collider>> query)
{
for (auto [entity, localToWorld, collider] : query)
{
// Get the 4 points of the collider.
glm::vec3 corners[4];
collider->shape.corners4(corners);
collider->localAABB.box().corners4(corners);

// Pack the 3 points of the collider into a matrix.
auto points = glm::mat4{glm::vec4{corners[0], 1.0F}, glm::vec4{corners[1], 1.0F}, glm::vec4{corners[2], 1.0F},
Expand All @@ -33,19 +60,11 @@ void updateBoxAABBs(Query<Read<LocalToWorld>, Read<BoxCollider>, Write<ColliderA
max += glm::vec3{collider->margin};

// Set the AABB.
aabb->max = translation + max;
aabb->min = translation - max;
collider->worldAABB = ColliderAABB{translation - max, translation + max};
}
}

void updateCapsuleAABBs(Query<Read<LocalToWorld>, Read<CapsuleCollider>, Write<ColliderAABB>> query,
Write<BroadPhaseCollisions> collisions)
{
(void)query;
(void)collisions;
}

void updateMarkers(Query<Read<ColliderAABB>> query, Write<BroadPhaseCollisions> collisions)
void updateMarkers(Query<Read<Collider>> query, Write<BroadPhaseCollisions> collisions)
{
// TODO: This is parallelizable.
for (glm::length_t axis = 0; axis < 3; axis++)
Expand All @@ -54,10 +73,10 @@ void updateMarkers(Query<Read<ColliderAABB>> query, Write<BroadPhaseCollisions>
std::sort(
collisions->markersPerAxis[axis].begin(), collisions->markersPerAxis[axis].end(),
[axis, &query](const BroadPhaseCollisions::SweepMarker& a, const BroadPhaseCollisions::SweepMarker& b) {
auto [aAABB] = query[a.entity].value();
auto [bAABB] = query[b.entity].value();
auto aPos = a.isMin ? aAABB->min : aAABB->max;
auto bPos = b.isMin ? bAABB->min : bAABB->max;
auto [aCollider] = query[a.entity].value();
auto [bCollider] = query[b.entity].value();
auto aPos = a.isMin ? aCollider->worldAABB.min : aCollider->worldAABB.max;
auto bPos = b.isMin ? bCollider->worldAABB.min : bCollider->worldAABB.max;
return aPos[axis] < bPos[axis];
});
}
Expand Down Expand Up @@ -106,7 +125,7 @@ CollisionType getCollisionType(bool box, bool capsule)
return CollisionType::CapsuleCapsule;
}

void findPairs(Query<OptRead<BoxCollider>, OptRead<CapsuleCollider>, Read<ColliderAABB>> query,
void findPairs(Query<OptRead<BoxCollisionShape>, OptRead<CapsuleCollisionShape>, Read<Collider>> query,
Write<BroadPhaseCollisions> collisions)
{
collisions->clearCandidates();
Expand All @@ -115,30 +134,33 @@ void findPairs(Query<OptRead<BoxCollider>, OptRead<CapsuleCollider>, Read<Collid
{
for (auto& [entity, overlaps] : collisions->sweepOverlapMaps[axis])
{
auto [box, capsule, aabb] = query[entity].value();
auto [box, capsule, collider] = query[entity].value();
for (auto& other : overlaps)
{
auto [otherBox, otherCapsule, otherAabb] = query[other].value();
auto [otherBox, otherCapsule, otherCollider] = query[other].value();

// TODO: Should this be inside the if statement?
auto type = getCollisionType(box || otherBox, capsule || otherCapsule);

switch (axis)
{
case 0: // X
if (aabb->overlapsY(*otherAabb) && aabb->overlapsZ(*otherAabb))
if (collider->worldAABB.overlapsY(otherCollider->worldAABB) &&
collider->worldAABB.overlapsZ(otherCollider->worldAABB))
{
collisions->addCandidate(type, {entity, other});
}
break;
case 1: // Y
if (aabb->overlapsX(*otherAabb) && aabb->overlapsZ(*otherAabb))
if (collider->worldAABB.overlapsX(otherCollider->worldAABB) &&
collider->worldAABB.overlapsZ(otherCollider->worldAABB))
{
collisions->addCandidate(type, {entity, other});
}
break;
case 2: // Z
if (aabb->overlapsX(*otherAabb) && aabb->overlapsY(*otherAabb))
if (collider->worldAABB.overlapsX(otherCollider->worldAABB) &&
collider->worldAABB.overlapsY(otherCollider->worldAABB))
{
collisions->addCandidate(type, {entity, other});
}
Expand Down
Loading
Loading