From b879f2a636035291f0e83d232c40d2b112c0083c Mon Sep 17 00:00:00 2001 From: sebastian Date: Tue, 4 Aug 2026 15:36:33 +0200 Subject: [PATCH] ADD: AABB --- CMakeLists.txt | 3 + .../standardComponents/PickableComponent.h | 4 +- engine/src/ecs/systems/PickingSystem.h | 8 +- engine/src/layer/DebugLayer.cpp | 6 +- engine/src/loader/models/AABB.cpp | 76 +++++++++++++++++++ engine/src/loader/models/AABB.h | 57 ++++++++++++++ engine/src/loader/models/MeshSection.h | 2 + engine/src/loader/models/Model.cpp | 14 +++- engine/src/loader/models/Model.h | 5 ++ engine/src/loader/models/ModelUploader.cpp | 20 ++++- engine/src/loader/models/Ray.h | 21 +++++ .../src/renderer/primitives/SphereFactory.cpp | 11 ++- game/src/ludo/PiecePresenter.cpp | 2 +- .../ludo/ecs/highlight/HighlightSystem.cpp | 8 +- 14 files changed, 222 insertions(+), 15 deletions(-) create mode 100644 engine/src/loader/models/AABB.cpp create mode 100644 engine/src/loader/models/AABB.h create mode 100644 engine/src/loader/models/Ray.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fc0f9be..024d4a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,9 @@ add_library(Engine STATIC engine/src/renderer/DebugRenderer.h engine/src/layer/DebugLayer.cpp engine/src/layer/DebugLayer.h + engine/src/loader/models/AABB.cpp + engine/src/loader/models/AABB.h + engine/src/loader/models/Ray.h ) target_include_directories(Engine PUBLIC engine/src) target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui spdlog::spdlog tinyobjloader stb_image) diff --git a/engine/src/ecs/standardComponents/PickableComponent.h b/engine/src/ecs/standardComponents/PickableComponent.h index 0ac4eff..6bd2456 100644 --- a/engine/src/ecs/standardComponents/PickableComponent.h +++ b/engine/src/ecs/standardComponents/PickableComponent.h @@ -4,9 +4,11 @@ #ifndef COLORRACE_PICKABLECOMPONENT_H #define COLORRACE_PICKABLECOMPONENT_H +#include "loader/models/AABB.h" + namespace engine { struct PickableComponent { - float radius; + AABB aabb; }; } diff --git a/engine/src/ecs/systems/PickingSystem.h b/engine/src/ecs/systems/PickingSystem.h index 9a570a2..4f8465a 100644 --- a/engine/src/ecs/systems/PickingSystem.h +++ b/engine/src/ecs/systems/PickingSystem.h @@ -42,10 +42,10 @@ namespace engine { for (const auto& [entity, pickable] : registry.getPool()) { const auto transform = registry.getComponent(entity); - if (float t; raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) { - closestT = t; - hit = entity; - } + // if (float t; raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) { + // closestT = t; + // hit = entity; + // } } return hit; diff --git a/engine/src/layer/DebugLayer.cpp b/engine/src/layer/DebugLayer.cpp index 155109c..4ea265b 100644 --- a/engine/src/layer/DebugLayer.cpp +++ b/engine/src/layer/DebugLayer.cpp @@ -9,7 +9,7 @@ #include "GLFW/glfw3.h" #include "renderer/RenderQueue.h" #include "renderer/primitives/SphereFactory.h" - +#include "spdlog/spdlog.h" void engine::DebugLayer::onUpdate(float deltaTime) { @@ -27,7 +27,9 @@ void engine::DebugLayer::onRender() { for (auto& [entity, pickableComponent] : pickablePool) { if (transforms.has(entity)) { - const auto sphereTransformComp = TransformComponent(transforms.get(entity).m_position, {0,0,0}, {pickableComponent.radius, pickableComponent.radius, pickableComponent.radius}); + glm::vec3 halfExtent = pickableComponent.aabb.getHalfExtent(); + spdlog::info(std::to_string(halfExtent.x) + " " + std::to_string(halfExtent.y) + " " + std::to_string(halfExtent.z)); + const auto sphereTransformComp = TransformComponent(transforms.get(entity).m_position, {0,0,0}, halfExtent); glm::mat4 modelMatrix = sphereTransformComp.computeModelMatrix(); RenderCommand renderCommand(m_sphereModel, modelMatrix); diff --git a/engine/src/loader/models/AABB.cpp b/engine/src/loader/models/AABB.cpp new file mode 100644 index 0000000..e1334c7 --- /dev/null +++ b/engine/src/loader/models/AABB.cpp @@ -0,0 +1,76 @@ +// +// Created by sebastian on 04.08.26. +// + +#include "AABB.h" + +#include "glm/glm.hpp" + +void engine::AABB::expandToInclude(const glm::vec3 &point) { + m_min = glm::min(m_min, point); + m_max = glm::max(m_max, point); +} + +void engine::AABB::expandToInclude(const AABB &other) { + m_min = glm::min(m_min, other.getMin()); + m_max = glm::max(m_max, other.getMax()); +} + +engine::AABB engine::AABB::transformed(const glm::mat4 &transform) const { + glm::vec3 corners[8] = { + {m_min.x, m_min.y, m_min.z}, {m_max.x, m_min.y, m_min.z}, + {m_min.x, m_max.y, m_min.z}, {m_max.x, m_max.y, m_min.z}, + {m_min.x, m_min.y, m_max.z}, {m_max.x, m_min.y, m_max.z}, + {m_min.x, m_max.y, m_max.z}, {m_max.x, m_max.y, m_max.z}, + }; + + glm::vec3 newMin(std::numeric_limits::max()); + glm::vec3 newMax(std::numeric_limits::lowest()); + + for (const auto& corner : corners) { + glm::vec3 transformed = glm::vec3(transform * glm::vec4(corner, 1.0f)); + newMin = glm::min(newMin, transformed); + newMax = glm::max(newMax, transformed); + } + + return {newMin, newMax}; +} + +std::optional engine::AABB::intersects(const Ray &ray, float maxDistance) const { + // Slab-Methode: Prüft für jede Achse (x, y, z) das Intervall, in dem der Ray + // innerhalb der beiden "Slabs" (Ebenenpaare) dieser Achse liegt. Der Ray trifft + // die AABB genau dann, wenn sich alle drei Achsen-Intervalle überschneiden. + float tMin = 0.0f; + float tMax = maxDistance; + + for (int axis = 0; axis < 3; ++axis) { + float origin = ray.origin[axis]; + float dir = ray.direction[axis]; + float minBound = m_min[axis]; + float maxBound = m_max[axis]; + + if (std::abs(dir) < 1e-8f) { + // Ray verläuft parallel zu dieser Achse - kein Treffer, falls Origin außerhalb des Slabs liegt + if (origin < minBound || origin > maxBound) { + return std::nullopt; + } + } else { + float invDir = 1.0f / dir; + float t1 = (minBound - origin) * invDir; + float t2 = (maxBound - origin) * invDir; + if (t1 > t2) std::swap(t1, t2); + + tMin = std::max(tMin, t1); + tMax = std::min(tMax, t2); + + if (tMin > tMax) { + return std::nullopt; // Intervalle überschneiden sich nicht -> kein Treffer + } + } + } + + RayHit hit; + hit.distance = tMin; + hit.intersectionPoint = ray.origin + ray.direction * tMin; + return hit; +} diff --git a/engine/src/loader/models/AABB.h b/engine/src/loader/models/AABB.h new file mode 100644 index 0000000..04c9668 --- /dev/null +++ b/engine/src/loader/models/AABB.h @@ -0,0 +1,57 @@ +// +// Created by sebastian on 04.08.26. +// + +#ifndef COLORRACE_AABB_H +#define COLORRACE_AABB_H +#include + +#include "Ray.h" +#include "glm/fwd.hpp" +#include "glm/vec3.hpp" + + +namespace engine { + class AABB { + public: + AABB() = default; + AABB(const glm::vec3& min, const glm::vec3& max) : m_min(min), m_max(max) {} + + static AABB fromCenterAndHalfExtent(const glm::vec3& center, const glm::vec3& halfExtent) { + return AABB(center - halfExtent, center + halfExtent); + } + + const glm::vec3& getMin() const { return m_min; } + const glm::vec3& getMax() const { return m_max; } + + glm::vec3 getCenter() const { return (m_min + m_max) * 0.5f; } + glm::vec3 getHalfExtent() const { return (m_max - m_min) * 0.5f; } + glm::vec3 getSize() const { return m_max - m_min; } + + void expandToInclude(const glm::vec3& point); + void expandToInclude(const AABB& other); + + AABB transformed(const glm::mat4& transform) const; + + [[nodiscard]] bool contains(const glm::vec3 &point) const { + return point.x >= m_min.x && point.x <= m_max.x + && point.y >= m_min.y && point.y <= m_max.y + && point.z >= m_min.z && point.z <= m_max.z; + } + + [[nodiscard]] bool intersects(const AABB &other) const { + return m_min.x <= other.m_max.x && m_max.x >= other.m_min.x + && m_min.y <= other.m_max.y && m_max.y >= other.m_min.y + && m_min.z <= other.m_max.z && m_max.z >= other.m_min.z; + } + + std::optional intersects(const Ray &ray, float maxDistance = std::numeric_limits::max()) const; + + private: + glm::vec3 m_min{0.f}; + glm::vec3 m_max{0.f}; + }; +} + + +#endif //COLORRACE_AABB_H diff --git a/engine/src/loader/models/MeshSection.h b/engine/src/loader/models/MeshSection.h index 55c628b..a9535de 100644 --- a/engine/src/loader/models/MeshSection.h +++ b/engine/src/loader/models/MeshSection.h @@ -11,6 +11,7 @@ #include #include +#include "AABB.h" #include "Material.h" #include "glm/glm.hpp" @@ -21,6 +22,7 @@ namespace engine { unsigned int indexOffset; // in Indizes, nicht Bytes unsigned int indexCount; glm::vec3 centroid; + AABB aabb; std::shared_ptr material; }; diff --git a/engine/src/loader/models/Model.cpp b/engine/src/loader/models/Model.cpp index ed681f3..b40346c 100644 --- a/engine/src/loader/models/Model.cpp +++ b/engine/src/loader/models/Model.cpp @@ -19,7 +19,19 @@ void Model::unbind() const { } Model::Model(std::unique_ptr vao, int vertexCount, std::vector sections) - : m_vao(std::move(vao)), m_vertexCount(vertexCount), m_isIndexed(true), m_sections(std::move(sections)) {} + : m_vao(std::move(vao)), m_vertexCount(vertexCount), m_isIndexed(true), m_sections(std::move(sections)) { + if (!sections.empty()) { + spdlog::info("Calculate AABB for model {}", vao->getId()); + AABB aabb = sections[0].aabb; + for (const auto& section : sections) { + aabb.expandToInclude(section.aabb); + } + m_aabb = aabb; + } +} + +Model::Model(std::unique_ptr vao, int vertexCount, std::vector sections, AABB aabb) : m_vao(std::move(vao)), m_vertexCount(vertexCount), m_isIndexed(true), m_sections(std::move(sections)), m_aabb(aabb) { +} void Model::draw() const { bind(); diff --git a/engine/src/loader/models/Model.h b/engine/src/loader/models/Model.h index 6d7896c..22cd8d6 100644 --- a/engine/src/loader/models/Model.h +++ b/engine/src/loader/models/Model.h @@ -6,6 +6,7 @@ #define COLORRACE_MODEL_H #include +#include "AABB.h" #include "MeshSection.h" #include "utils/openglWrapper/openglObjects/Vao.h" @@ -16,6 +17,7 @@ namespace engine { Model(std::unique_ptr vao, int vertexCount, bool isIndexed); Model(std::unique_ptr vao, int vertexCount); Model(std::unique_ptr vao, int vertexCount, std::vector sections); // neu + Model(std::unique_ptr vao, int vertexCount, std::vector sections, AABB aabb); // neu void bind() const; void unbind() const; @@ -27,8 +29,11 @@ namespace engine { [[nodiscard]] bool isIndexed() const { return m_isIndexed; } [[nodiscard]] bool hasSections() const { return !m_sections.empty(); } [[nodiscard]] const std::vector& getSections() const { return m_sections; } + [[nodiscard]] const AABB& getAABB() const { return m_aabb; } + private: + AABB m_aabb; std::unique_ptr m_vao; int m_vertexCount; bool m_isIndexed; diff --git a/engine/src/loader/models/ModelUploader.cpp b/engine/src/loader/models/ModelUploader.cpp index 4e8e7c3..2495e25 100644 --- a/engine/src/loader/models/ModelUploader.cpp +++ b/engine/src/loader/models/ModelUploader.cpp @@ -12,6 +12,7 @@ #include "glm/vec3.hpp" #include "loader/textures/TextureUploader.h" #include "spdlog/spdlog.h" +#include "AABB.h" std::vector engine::ModelUploader::upload(const engine::RawModelBundle &bundle, engine::ModelSplitPolicy policy) { std::vector results; @@ -105,6 +106,15 @@ engine::Model engine::ModelUploader::uploadSections(const std::vector(section.vertices.size() / 3); + //Calculate AABB + AABB aabb( + glm::vec3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()), + glm::vec3(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()) + ); + for (size_t i = 0; i < section.vertices.size(); i += 3) { + aabb.expandToInclude(glm::vec3(section.vertices[i], section.vertices[i+1], section.vertices[i+2])); + } + vertexOffset += static_cast(section.vertices.size() / 3); meshSections.push_back(std::move(meshSection)); } @@ -120,7 +130,15 @@ engine::Model engine::ModelUploader::uploadSections(const std::vectorunbind(); - return Model(std::move(vao), static_cast(indices.size()), std::move(meshSections)); + AABB modelAABB = AABB( + glm::vec3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()), + glm::vec3(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()) + ); + for (int i = 0; i < static_cast(vertices.size()); i+=3) { + modelAABB.expandToInclude(glm::vec3(vertices[i], vertices[i+1], vertices[i+2])); + } + + return Model(std::move(vao), static_cast(indices.size()), std::move(meshSections), modelAABB); } engine::Model engine::ModelUploader::buildCombinedByObject(const std::vector &objects, const MaterialMap &materials) { diff --git a/engine/src/loader/models/Ray.h b/engine/src/loader/models/Ray.h new file mode 100644 index 0000000..0bc4591 --- /dev/null +++ b/engine/src/loader/models/Ray.h @@ -0,0 +1,21 @@ +// +// Created by sebastian on 04.08.26. +// + +#ifndef COLORRACE_RAY_H +#define COLORRACE_RAY_H +#include "glm/vec3.hpp" + +namespace engine { + struct Ray { + glm::vec3 origin; + glm::vec3 direction; + }; + + struct RayHit { + float distance; + glm::vec3 intersectionPoint; + }; +} + +#endif //COLORRACE_RAY_H diff --git a/engine/src/renderer/primitives/SphereFactory.cpp b/engine/src/renderer/primitives/SphereFactory.cpp index ed6699e..9a34062 100644 --- a/engine/src/renderer/primitives/SphereFactory.cpp +++ b/engine/src/renderer/primitives/SphereFactory.cpp @@ -63,5 +63,14 @@ std::shared_ptr engine::SphereFactory::createUVSphere(float radiu vao->createIndexBuffer(indices.data(), indices.size()); vao->unbind(); - return std::make_shared(std::move(vao), static_cast(indices.size()), true); + AABB modelAABB = AABB( + glm::vec3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()), + glm::vec3(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()) + ); + for (int i = 0; i < static_cast(vertices.size()); i+=3) { + modelAABB.expandToInclude(glm::vec3(vertices[i], vertices[i+1], vertices[i+2])); + } + + std::vector meshSections; + return std::make_shared(std::move(vao), static_cast(indices.size()), meshSections, modelAABB); } diff --git a/game/src/ludo/PiecePresenter.cpp b/game/src/ludo/PiecePresenter.cpp index b6ff05c..3af4c05 100644 --- a/game/src/ludo/PiecePresenter.cpp +++ b/game/src/ludo/PiecePresenter.cpp @@ -19,7 +19,7 @@ void ludo::PiecePresenter::spawnPieces(const ludo::LudoGameState &state) { {0.334296f, 0.334296f, 0.334296f}); m_registry.addComponent(entity, transform); m_registry.addComponent(entity, engine::MeshComponent(m_pieceModels[std::string(ludo::toString(piece.color))])); - m_registry.addComponent(entity, engine::PickableComponent(kPieceRadius)); + m_registry.addComponent(entity, engine::PickableComponent(m_pieceModels[std::string(toString(piece.color))]->getAABB())); m_pieceEntities.push_back(entity); } } diff --git a/game/src/ludo/ecs/highlight/HighlightSystem.cpp b/game/src/ludo/ecs/highlight/HighlightSystem.cpp index 4dde42d..1106e45 100644 --- a/game/src/ludo/ecs/highlight/HighlightSystem.cpp +++ b/game/src/ludo/ecs/highlight/HighlightSystem.cpp @@ -14,10 +14,10 @@ void ludo::HighlightSystem::update(const engine::Mouse& mouse, engine::EntityReg for (const auto& [entity, pickable] : registry.getPool()) { const auto transform = registry.getComponent(entity); - if (float t; engine::PickingSystem::raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) { - closestT = t; - hit = entity; - } + // if (float t; engine::PickingSystem::raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) { + // closestT = t; + // hit = entity; + // } } if (hit.has_value()) {