GridCollisionDetectionSystem.ixx File
Spatial-partitioning based collision detection using a uniform 3D grid. More...
Namespaces Index
| namespace | helios |
|
|
|
| namespace | engine |
|
Main engine module aggregating core infrastructure and game systems. More...
|
|
| namespace | modules |
|
Domain-specific components and systems. More...
|
|
| namespace | physics |
|
Physics simulation and collision detection subsystem for the game engine. More...
|
|
| namespace | collision |
|
|
|
| namespace | systems |
|
Collision detection and response systems. More...
|
|
Classes Index
Macro Definitions Index
| #define | HELIOS_LOG_SCOPE "helios::engine::modules::physics::systems::GridCollisionDetectionSystem" |
|
|
|
Description
Spatial-partitioning based collision detection using a uniform 3D grid.
Macro Definitions
HELIOS_LOG_SCOPE
| #define HELIOS_LOG_SCOPE "helios::engine::modules::physics::systems::GridCollisionDetectionSystem" |
|
File Listing
The file content with the documentation metadata removed is:
11#include <helios/helios_config.h>
12#include <unordered_set>
16export module helios.engine.modules.physics.collision.systems.GridCollisionDetectionSystem;
19import helios.engine.runtime.world.UpdateContext;
20import helios.engine.ecs.GameObject;
21import helios.engine.runtime.world.GameWorld;
23import helios.engine.mechanics.lifecycle.components;
25import helios.engine.modules.physics.collision.components.CollisionComponent;
26import helios.engine.modules.physics.collision.components.CollisionStateComponent;
27import helios.engine.modules.physics.collision.components.AabbColliderComponent;
29import helios.engine.modules.physics.collision.types.CollisionBehavior;
30import helios.engine.modules.physics.collision.types.HitPolicy;
32import helios.engine.ecs.EntityHandle;
34import helios.util.Guid;
37import helios.util.log;
42#define HELIOS_LOG_SCOPE "helios::engine::modules::physics::systems::GridCollisionDetectionSystem"
43import helios.engine.common.tags.SystemRole;
94 struct CollisionStruct {
95 bool isSolidCollision = false;
96 bool isTriggerCollision = false;
97 bool aIsCollisionReporter = false;
98 bool bIsCollisionReporter = false;
101 uint32_t aCollisionLayer = 0;
102 uint32_t bCollisionLayer = 0;
104 [[nodiscard]] inline constexpr bool hasAnyInteraction() const noexcept {
105 return (isSolidCollision || isTriggerCollision) && (aIsCollisionReporter || bIsCollisionReporter);
117 struct EntityHandlePairHash {
126 std::uint64_t operator()(const std::pair<helios::engine::ecs::EntityHandle, helios::engine::ecs::EntityHandle>& pair) const {
128 auto g1 = std::hash<helios::engine::ecs::EntityHandle>{}(pair.first);
129 auto g2 = std::hash<helios::engine::ecs::EntityHandle>{}(pair.second);
132 return g1 ^ (g2 << 1);
143 struct CollisionCandidate {
177 std::vector<CollisionCandidate> collisionCandidates;
183 collisionCandidates.clear();
198 std::unordered_set<std::pair<helios::engine::ecs::EntityHandle, helios::engine::ecs::EntityHandle>, EntityHandlePairHash> solvedCollisions_;
213 std::vector<GridCell> cells_;
218 unsigned int cellsX_;
223 unsigned int cellsY_;
228 unsigned int cellsZ_;
233 std::vector<size_t> trackedCells_;
245 cellsX_ = std::max(1u, static_cast<unsigned int>(std::ceil(size[0] / cellSize_)));
246 cellsY_ = std::max(1u, static_cast<unsigned int>(std::ceil(size[1] / cellSize_)));
247 cellsZ_ = std::max(1u, static_cast<unsigned int>(std::ceil(size[2] / cellSize_)));
249 const size_t cellCount = static_cast<size_t>(cellsX_) * cellsY_ * cellsZ_;
253 if (cellCount > 100'000'000) {
254 logger_.warn(std::format("Spatial Grid requested {0} cells", cellCount));
255 throw std::runtime_error("Cell count too high.");
258 trackedCells_.reserve(cellCount);
259 cells_.resize(cellCount);
268 inline void prepareCollisionDetection() {
269 for (const auto idx : trackedCells_) {
273 trackedCells_.clear();
274 solvedCollisions_.clear();
291 inline void postEvent(
295 const CollisionStruct collisionStruct,
301 bool aIsCollisionReporter = collisionStruct.aIsCollisionReporter;
302 bool bIsCollisionReporter = collisionStruct.bIsCollisionReporter;
303 bool isSolidCollision = collisionStruct.isSolidCollision;
304 bool isTriggerCollision = collisionStruct.isTriggerCollision;
305 uint32_t aCollisionLayer = collisionStruct.aCollisionLayer;
306 uint32_t bCollisionLayer = collisionStruct.bCollisionLayer;
308 assert((isSolidCollision || isTriggerCollision)
309 && (aIsCollisionReporter || bIsCollisionReporter)
310 && "Preconditions not matched for postEvent.");
313 if (isTriggerCollision || isSolidCollision) {
316 contact, isSolidCollision, isTriggerCollision, collisionStruct.aCollisionBehavior,
317 aIsCollisionReporter, match.entityHandle(), aCollisionLayer, bCollisionLayer
321 contact, isSolidCollision, isTriggerCollision, collisionStruct.bCollisionBehavior,
324 bCollisionLayer, aCollisionLayer
342 [[nodiscard]] inline CollisionStruct findCollisionType(
347 auto isSolidCollision = false;
348 auto isTriggerCollision = false;
354 if (!aIsCollisionReporter && !bIsCollisionReporter) {
355 return CollisionStruct{};
365 isSolidCollision = aCanSolidCollideWithB && bCanSolidCollideWithA;
369 if (aCanSolidCollideWithB && !bCanSolidCollideWithA) {
370 logger_.warn("Collision Asymmetry detected!");
375 isTriggerCollision = aCanTriggerCollideWithB || bCanTriggerCollideWithA;
377 return CollisionStruct {
380 aIsCollisionReporter,
381 bIsCollisionReporter,
416 cellSize_(cellSize) {
417 assert(cellSize_ > 0.0f && "cellSize must not be <= 0.0f");
435 prepareCollisionDetection();
437 for (auto [entity, cc, csc, acc, active] : updateContext.view<
444 if (!acc->boundsInitialized()) {
452 const auto& worldBounds = acc->bounds();
463 for (const auto idx : trackedCells_) {
465 if (cells_[idx].collisionCandidates.size() < 2) {
489 int xMin = static_cast<int>(std::floor(min[0] / cellSize_));
490 int yMin = static_cast<int>(std::floor(min[1] / cellSize_));
491 int zMin = static_cast<int>(std::floor(min[2] / cellSize_));
493 int xMax = static_cast<int>(std::floor(max[0] / cellSize_));
494 int yMax = static_cast<int>(std::floor(max[1] / cellSize_));
495 int zMax = static_cast<int>(std::floor(max[2] / cellSize_));
498 std::clamp(xMin, 0, static_cast<int>(cellsX_ - 1)),
499 std::clamp(yMin, 0, static_cast<int>(cellsY_ - 1)),
500 std::clamp(zMin, 0, static_cast<int>(cellsZ_ - 1)),
501 std::clamp(xMax, 0, static_cast<int>(cellsX_ - 1)),
502 std::clamp(yMax, 0, static_cast<int>(cellsY_ - 1)),
503 std::clamp(zMax, 0, static_cast<int>(cellsZ_ - 1)),
526 const auto xMin = bounds.min()[0];
527 const auto xMax = bounds.max()[0];
528 const auto yMin = bounds.min()[1];
529 const auto yMax = bounds.max()[1];
530 const auto zMin = bounds.min()[2];
531 const auto zMax = bounds.max()[2];
533 for (int x = xMin; x <= xMax; x++) {
534 for (int y = yMin; y <= yMax; y++) {
535 for (int z = zMin; z <= zMax; z++) {
536 auto& [collisionCandidates] = cell(x, y, z);
538 collisionCandidates.push_back(
541 aabbColliderComponent,
543 collisionStateComponent
548 if (collisionCandidates.size() == 1) {
550 trackedCells_.push_back(idx);
572 auto& candidates = cell.collisionCandidates;
575 bool isCollisionReporter = false;
576 for (const auto& candidate : candidates) {
577 if (candidate.collisionComponent->isCollisionReporter()) {
578 isCollisionReporter = true;
583 if (!isCollisionReporter) {
587 for (size_t i = 0; i < candidates.size(); i++) {
589 CollisionCandidate& candidate = candidates[i];
600 for (size_t j = i+1; j < candidates.size(); j++) {
602 auto& [gameObject, aabbColliderComponent, collisionComponent, collisionStateComponent] = candidates[j];
607 const auto collisionStruct = findCollisionType(cc, matchCC);
609 if (!collisionStruct.hasAnyInteraction()) {
619 auto rHandle = gameObject.entityHandle();
621 if (lHandle > rHandle) {
622 std::swap(lHandle, rHandle);
626 if (solvedCollisions_.contains({lHandle, rHandle})) {
630 solvedCollisions_.insert({lHandle, rHandle});
633 candidate.gameObject, gameObject,
636 updateContext, csc, matchCSC
655 [[nodiscard]] inline GridCell& cell(const unsigned int x, const unsigned int y, const unsigned int z) noexcept {
668 [[nodiscard]] inline constexpr size_t cellIndex(const unsigned int x, const unsigned int y, const unsigned int z) const noexcept {
669 assert (x < cellsX_ && y < cellsY_ && z < cellsZ_ && "Invalid range values");
671 return x + y * cellsX_ + (z * cellsX_ * cellsY_);
679 [[nodiscard]] unsigned int cellsX() const noexcept {
688 [[nodiscard]] unsigned int cellsY() const noexcept {
697 [[nodiscard]] unsigned int cellsZ() const noexcept {
Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.