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;
18import helios.engine.ecs.System;
19import helios.engine.runtime.world.UpdateContext;
20import helios.engine.ecs.GameObject;
21import helios.engine.runtime.world.GameWorld;
23import helios.engine.mechanics.lifecycle.components.Active;
25import helios.engine.modules.physics.collision.events.TriggerCollisionEvent;
26import helios.engine.modules.physics.collision.events.SolidCollisionEvent;
28import helios.engine.modules.physics.collision.components.CollisionComponent;
29import helios.engine.modules.physics.collision.components.CollisionStateComponent;
30import helios.engine.modules.physics.collision.components.AabbColliderComponent;
32import helios.engine.modules.physics.collision.types.CollisionBehavior;
33import helios.engine.modules.physics.collision.types.HitPolicy;
35import helios.engine.ecs.EntityHandle;
37import helios.util.Guid;
40import helios.util.log;
45#define HELIOS_LOG_SCOPE "helios::engine::modules::physics::systems::GridCollisionDetectionSystem"
97 struct CollisionStruct {
98 bool isSolidCollision = false;
99 bool isTriggerCollision = false;
100 bool aIsCollisionReporter = false;
101 bool bIsCollisionReporter = false;
104 uint32_t collisionLayer = 0;
105 uint32_t otherCollisionLayer = 0;
107 [[nodiscard]] inline constexpr bool hasAnyInteraction() const noexcept {
108 return (isSolidCollision || isTriggerCollision) && (aIsCollisionReporter || bIsCollisionReporter);
120 struct EntityHandlePairHash {
129 std::uint64_t operator()(const std::pair<helios::engine::ecs::EntityHandle, helios::engine::ecs::EntityHandle>& pair) const {
131 auto g1 = std::hash<helios::engine::ecs::EntityHandle>{}(pair.first);
132 auto g2 = std::hash<helios::engine::ecs::EntityHandle>{}(pair.second);
135 return g1 ^ (g2 << 1);
146 struct CollisionCandidate {
180 std::vector<CollisionCandidate> collisionCandidates;
186 collisionCandidates.clear();
201 std::unordered_set<std::pair<helios::engine::ecs::EntityHandle, helios::engine::ecs::EntityHandle>, EntityHandlePairHash> solvedCollisions_;
216 std::vector<GridCell> cells_;
221 unsigned int cellsX_;
226 unsigned int cellsY_;
231 unsigned int cellsZ_;
236 std::vector<size_t> trackedCells_;
248 cellsX_ = std::max(1u, static_cast<unsigned int>(std::ceil(size[0] / cellSize_)));
249 cellsY_ = std::max(1u, static_cast<unsigned int>(std::ceil(size[1] / cellSize_)));
250 cellsZ_ = std::max(1u, static_cast<unsigned int>(std::ceil(size[2] / cellSize_)));
252 const size_t cellCount = static_cast<size_t>(cellsX_) * cellsY_ * cellsZ_;
256 if (cellCount > 100'000'000) {
257 logger_.warn(std::format("Spatial Grid requested {0} cells", cellCount));
258 throw std::runtime_error("Cell count too high.");
261 trackedCells_.reserve(cellCount);
262 cells_.resize(cellCount);
271 inline void prepareCollisionDetection() {
272 for (const auto idx : trackedCells_) {
276 trackedCells_.clear();
277 solvedCollisions_.clear();
295 inline void postEvent(
299 const CollisionStruct collisionStruct,
305 bool aIsCollisionReporter = collisionStruct.aIsCollisionReporter;
306 bool bIsCollisionReporter = collisionStruct.bIsCollisionReporter;
307 bool isSolidCollision = collisionStruct.isSolidCollision;
308 bool isTriggerCollision = collisionStruct.isTriggerCollision;
309 uint32_t collisionLayer = collisionStruct.collisionLayer;
310 uint32_t otherCollisionLayer = collisionStruct.otherCollisionLayer;
312 assert((isSolidCollision || isTriggerCollision)
313 && (aIsCollisionReporter || bIsCollisionReporter)
314 && "Preconditions not matched for postEvent.");
317 if (isTriggerCollision || isSolidCollision) {
320 contact, isSolidCollision, isTriggerCollision, collisionStruct.aCollisionBehavior,
321 aIsCollisionReporter, match.entityHandle(), collisionLayer, otherCollisionLayer
325 contact, isSolidCollision, isTriggerCollision, collisionStruct.bCollisionBehavior,
326 bIsCollisionReporter, candidate.entityHandle(), collisionLayer, otherCollisionLayer
344 [[nodiscard]] inline CollisionStruct findCollisionType(
349 auto isSolidCollision = false;
350 auto isTriggerCollision = false;
356 if (!aIsCollisionReporter && !bIsCollisionReporter) {
357 return CollisionStruct{};
367 isSolidCollision = aCanSolidCollideWithB && bCanSolidCollideWithA;
371 if (aCanSolidCollideWithB && !bCanSolidCollideWithA) {
372 logger_.warn("Collision Asymmetry detected!");
377 isTriggerCollision = aCanTriggerCollideWithB || bCanTriggerCollideWithA;
379 return CollisionStruct {
382 aIsCollisionReporter,
383 bIsCollisionReporter,
415 ) : gridBounds_(bounds),
416 cellSize_(cellSize) {
417 assert(cellSize_ > 0.0f && "cellSize must not be <= 0.0f");
435 prepareCollisionDetection();
437 for (auto [entity, cc, csc, acc, active] : gameWorld_->view<
442 >().whereEnabled()) {
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.