ADD: AABB

This commit is contained in:
sebastian 2026-08-04 15:36:33 +02:00
parent 023016c6a2
commit b879f2a636
14 changed files with 222 additions and 15 deletions

View File

@ -166,6 +166,9 @@ add_library(Engine STATIC
engine/src/renderer/DebugRenderer.h engine/src/renderer/DebugRenderer.h
engine/src/layer/DebugLayer.cpp engine/src/layer/DebugLayer.cpp
engine/src/layer/DebugLayer.h 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_include_directories(Engine PUBLIC engine/src)
target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui spdlog::spdlog tinyobjloader stb_image) target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui spdlog::spdlog tinyobjloader stb_image)

View File

@ -4,9 +4,11 @@
#ifndef COLORRACE_PICKABLECOMPONENT_H #ifndef COLORRACE_PICKABLECOMPONENT_H
#define COLORRACE_PICKABLECOMPONENT_H #define COLORRACE_PICKABLECOMPONENT_H
#include "loader/models/AABB.h"
namespace engine { namespace engine {
struct PickableComponent { struct PickableComponent {
float radius; AABB aabb;
}; };
} }

View File

@ -42,10 +42,10 @@ namespace engine {
for (const auto& [entity, pickable] : registry.getPool<PickableComponent>()) { for (const auto& [entity, pickable] : registry.getPool<PickableComponent>()) {
const auto transform = registry.getComponent<TransformComponent>(entity); const auto transform = registry.getComponent<TransformComponent>(entity);
if (float t; raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) { // if (float t; raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) {
closestT = t; // closestT = t;
hit = entity; // hit = entity;
} // }
} }
return hit; return hit;

View File

@ -9,7 +9,7 @@
#include "GLFW/glfw3.h" #include "GLFW/glfw3.h"
#include "renderer/RenderQueue.h" #include "renderer/RenderQueue.h"
#include "renderer/primitives/SphereFactory.h" #include "renderer/primitives/SphereFactory.h"
#include "spdlog/spdlog.h"
void engine::DebugLayer::onUpdate(float deltaTime) { void engine::DebugLayer::onUpdate(float deltaTime) {
@ -27,7 +27,9 @@ void engine::DebugLayer::onRender() {
for (auto& [entity, pickableComponent] : pickablePool) { for (auto& [entity, pickableComponent] : pickablePool) {
if (transforms.has(entity)) { 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(); glm::mat4 modelMatrix = sphereTransformComp.computeModelMatrix();
RenderCommand renderCommand(m_sphereModel, modelMatrix); RenderCommand renderCommand(m_sphereModel, modelMatrix);

View File

@ -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<float>::max());
glm::vec3 newMax(std::numeric_limits<float>::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::RayHit> 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;
}

View File

@ -0,0 +1,57 @@
//
// Created by sebastian on 04.08.26.
//
#ifndef COLORRACE_AABB_H
#define COLORRACE_AABB_H
#include <optional>
#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<RayHit> intersects(const Ray &ray, float maxDistance = std::numeric_limits<float>::max()) const;
private:
glm::vec3 m_min{0.f};
glm::vec3 m_max{0.f};
};
}
#endif //COLORRACE_AABB_H

View File

@ -11,6 +11,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include "AABB.h"
#include "Material.h" #include "Material.h"
#include "glm/glm.hpp" #include "glm/glm.hpp"
@ -21,6 +22,7 @@ namespace engine {
unsigned int indexOffset; // in Indizes, nicht Bytes unsigned int indexOffset; // in Indizes, nicht Bytes
unsigned int indexCount; unsigned int indexCount;
glm::vec3 centroid; glm::vec3 centroid;
AABB aabb;
std::shared_ptr<Material> material; std::shared_ptr<Material> material;
}; };

View File

@ -19,7 +19,19 @@ void Model::unbind() const {
} }
Model::Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> sections) Model::Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> 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> vao, int vertexCount, std::vector<MeshSection> 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 { void Model::draw() const {
bind(); bind();

View File

@ -6,6 +6,7 @@
#define COLORRACE_MODEL_H #define COLORRACE_MODEL_H
#include <memory> #include <memory>
#include "AABB.h"
#include "MeshSection.h" #include "MeshSection.h"
#include "utils/openglWrapper/openglObjects/Vao.h" #include "utils/openglWrapper/openglObjects/Vao.h"
@ -16,6 +17,7 @@ namespace engine {
Model(std::unique_ptr<Vao> vao, int vertexCount, bool isIndexed); Model(std::unique_ptr<Vao> vao, int vertexCount, bool isIndexed);
Model(std::unique_ptr<Vao> vao, int vertexCount); Model(std::unique_ptr<Vao> vao, int vertexCount);
Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> sections); // neu Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> sections); // neu
Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> sections, AABB aabb); // neu
void bind() const; void bind() const;
void unbind() const; void unbind() const;
@ -27,8 +29,11 @@ namespace engine {
[[nodiscard]] bool isIndexed() const { return m_isIndexed; } [[nodiscard]] bool isIndexed() const { return m_isIndexed; }
[[nodiscard]] bool hasSections() const { return !m_sections.empty(); } [[nodiscard]] bool hasSections() const { return !m_sections.empty(); }
[[nodiscard]] const std::vector<MeshSection>& getSections() const { return m_sections; } [[nodiscard]] const std::vector<MeshSection>& getSections() const { return m_sections; }
[[nodiscard]] const AABB& getAABB() const { return m_aabb; }
private: private:
AABB m_aabb;
std::unique_ptr<Vao> m_vao; std::unique_ptr<Vao> m_vao;
int m_vertexCount; int m_vertexCount;
bool m_isIndexed; bool m_isIndexed;

View File

@ -12,6 +12,7 @@
#include "glm/vec3.hpp" #include "glm/vec3.hpp"
#include "loader/textures/TextureUploader.h" #include "loader/textures/TextureUploader.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "AABB.h"
std::vector<engine::LoadedAsset> engine::ModelUploader::upload(const engine::RawModelBundle &bundle, engine::ModelSplitPolicy policy) { std::vector<engine::LoadedAsset> engine::ModelUploader::upload(const engine::RawModelBundle &bundle, engine::ModelSplitPolicy policy) {
std::vector<engine::LoadedAsset> results; std::vector<engine::LoadedAsset> results;
@ -105,6 +106,15 @@ engine::Model engine::ModelUploader::uploadSections(const std::vector<RawMeshSec
} }
meshSection.centroid = centroidSum / static_cast<float>(section.vertices.size() / 3); meshSection.centroid = centroidSum / static_cast<float>(section.vertices.size() / 3);
//Calculate AABB
AABB aabb(
glm::vec3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
glm::vec3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::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<unsigned int>(section.vertices.size() / 3); vertexOffset += static_cast<unsigned int>(section.vertices.size() / 3);
meshSections.push_back(std::move(meshSection)); meshSections.push_back(std::move(meshSection));
} }
@ -120,7 +130,15 @@ engine::Model engine::ModelUploader::uploadSections(const std::vector<RawMeshSec
vao->unbind(); vao->unbind();
return Model(std::move(vao), static_cast<int>(indices.size()), std::move(meshSections)); AABB modelAABB = AABB(
glm::vec3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
glm::vec3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest())
);
for (int i = 0; i < static_cast<int>(vertices.size()); i+=3) {
modelAABB.expandToInclude(glm::vec3(vertices[i], vertices[i+1], vertices[i+2]));
}
return Model(std::move(vao), static_cast<int>(indices.size()), std::move(meshSections), modelAABB);
} }
engine::Model engine::ModelUploader::buildCombinedByObject(const std::vector<RawObjectData> &objects, const MaterialMap &materials) { engine::Model engine::ModelUploader::buildCombinedByObject(const std::vector<RawObjectData> &objects, const MaterialMap &materials) {

View File

@ -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

View File

@ -63,5 +63,14 @@ std::shared_ptr<engine::Model> engine::SphereFactory::createUVSphere(float radiu
vao->createIndexBuffer(indices.data(), indices.size()); vao->createIndexBuffer(indices.data(), indices.size());
vao->unbind(); vao->unbind();
return std::make_shared<Model>(std::move(vao), static_cast<int>(indices.size()), true); AABB modelAABB = AABB(
glm::vec3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
glm::vec3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest())
);
for (int i = 0; i < static_cast<int>(vertices.size()); i+=3) {
modelAABB.expandToInclude(glm::vec3(vertices[i], vertices[i+1], vertices[i+2]));
}
std::vector<MeshSection> meshSections;
return std::make_shared<Model>(std::move(vao), static_cast<int>(indices.size()), meshSections, modelAABB);
} }

View File

@ -19,7 +19,7 @@ void ludo::PiecePresenter::spawnPieces(const ludo::LudoGameState &state) {
{0.334296f, 0.334296f, 0.334296f}); {0.334296f, 0.334296f, 0.334296f});
m_registry.addComponent<engine::TransformComponent>(entity, transform); m_registry.addComponent<engine::TransformComponent>(entity, transform);
m_registry.addComponent<engine::MeshComponent>(entity, engine::MeshComponent(m_pieceModels[std::string(ludo::toString(piece.color))])); m_registry.addComponent<engine::MeshComponent>(entity, engine::MeshComponent(m_pieceModels[std::string(ludo::toString(piece.color))]));
m_registry.addComponent<engine::PickableComponent>(entity, engine::PickableComponent(kPieceRadius)); m_registry.addComponent<engine::PickableComponent>(entity, engine::PickableComponent(m_pieceModels[std::string(toString(piece.color))]->getAABB()));
m_pieceEntities.push_back(entity); m_pieceEntities.push_back(entity);
} }
} }

View File

@ -14,10 +14,10 @@ void ludo::HighlightSystem::update(const engine::Mouse& mouse, engine::EntityReg
for (const auto& [entity, pickable] : registry.getPool<engine::PickableComponent>()) { for (const auto& [entity, pickable] : registry.getPool<engine::PickableComponent>()) {
const auto transform = registry.getComponent<engine::TransformComponent>(entity); const auto transform = registry.getComponent<engine::TransformComponent>(entity);
if (float t; engine::PickingSystem::raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) { // if (float t; engine::PickingSystem::raySphereIntersect(origin, dir, transform.m_position, pickable.radius, t) && t < closestT) {
closestT = t; // closestT = t;
hit = entity; // hit = entity;
} // }
} }
if (hit.has_value()) { if (hit.has_value()) {