diff --git a/include/omath/3d_primitives/aabb.hpp b/include/omath/3d_primitives/aabb.hpp index afd1872..2744e4b 100644 --- a/include/omath/3d_primitives/aabb.hpp +++ b/include/omath/3d_primitives/aabb.hpp @@ -4,17 +4,23 @@ #pragma once #include "omath/linear_algebra/vector3.hpp" +#include namespace omath::primitives { enum class UpAxis { X, Y, Z }; - template + template struct Aabb final { Vector3 min; Vector3 max; + [[nodiscard]] + static consteval UpAxis get_up_axis() + { + return Up; + } [[nodiscard]] constexpr Vector3 center() const noexcept { @@ -57,6 +63,17 @@ namespace omath::primitives std::unreachable(); } + [[nodiscard]] + constexpr std::array, 8> vertices() const noexcept + { + return { + Vector3{min.x, min.y, min.z}, Vector3{max.x, min.y, min.z}, + Vector3{min.x, max.y, min.z}, Vector3{max.x, max.y, min.z}, + Vector3{min.x, min.y, max.z}, Vector3{max.x, min.y, max.z}, + Vector3{min.x, max.y, max.z}, Vector3{max.x, max.y, max.z}, + }; + } + [[nodiscard]] constexpr bool is_collide(const Aabb& other) const noexcept { diff --git a/include/omath/hud/canvas_box.hpp b/include/omath/hud/canvas_box.hpp index fa06b92..7bb8664 100644 --- a/include/omath/hud/canvas_box.hpp +++ b/include/omath/hud/canvas_box.hpp @@ -9,6 +9,7 @@ namespace omath::hud class CanvasBox final { public: + CanvasBox() = default; CanvasBox(Vector2 top, Vector2 bottom, float ratio = 4.f); [[nodiscard("You have to use array")]] diff --git a/include/omath/hud/entity_overlay.hpp b/include/omath/hud/entity_overlay.hpp index 30c34f2..4e57cd5 100644 --- a/include/omath/hud/entity_overlay.hpp +++ b/include/omath/hud/entity_overlay.hpp @@ -5,11 +5,11 @@ #include "canvas_box.hpp" #include "entity_overlay_widgets.hpp" #include "hud_renderer_interface.hpp" +#include "omath/3d_primitives/aabb.hpp" #include "omath/linear_algebra/vector2.hpp" #include "omath/utility/color.hpp" #include #include - namespace omath::hud { class EntityOverlay final @@ -57,13 +57,17 @@ namespace omath::hud float offset = 5.f); // ── Labels ─────────────────────────────────────────────────────── - EntityOverlay& add_right_label(const Color& color, float offset, widget::Outlined outlined, const std::string_view& text); + EntityOverlay& add_right_label(const Color& color, float offset, widget::Outlined outlined, + const std::string_view& text); - EntityOverlay& add_left_label(const Color& color, float offset, widget::Outlined outlined, const std::string_view& text); + EntityOverlay& add_left_label(const Color& color, float offset, widget::Outlined outlined, + const std::string_view& text); - EntityOverlay& add_top_label(const Color& color, float offset, widget::Outlined outlined, std::string_view text); + EntityOverlay& add_top_label(const Color& color, float offset, widget::Outlined outlined, + std::string_view text); - EntityOverlay& add_bottom_label(const Color& color, float offset, widget::Outlined outlined, std::string_view text); + EntityOverlay& add_bottom_label(const Color& color, float offset, widget::Outlined outlined, + std::string_view text); EntityOverlay& add_centered_top_label(const Color& color, float offset, widget::Outlined outlined, const std::string_view& text); @@ -72,24 +76,24 @@ namespace omath::hud const std::string_view& text); template - EntityOverlay& add_right_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string fmt, - Args&&... args) + EntityOverlay& add_right_label(const Color& color, const float offset, const widget::Outlined outlined, + std::format_string fmt, Args&&... args) { return add_right_label(color, offset, outlined, std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))}); } template - EntityOverlay& add_left_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string fmt, - Args&&... args) + EntityOverlay& add_left_label(const Color& color, const float offset, const widget::Outlined outlined, + std::format_string fmt, Args&&... args) { return add_left_label(color, offset, outlined, std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))}); } template - EntityOverlay& add_top_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string fmt, - Args&&... args) + EntityOverlay& add_top_label(const Color& color, const float offset, const widget::Outlined outlined, + std::format_string fmt, Args&&... args) { return add_top_label(color, offset, outlined, std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))}); @@ -112,8 +116,9 @@ namespace omath::hud } template - EntityOverlay& add_centered_bottom_label(const Color& color, const float offset, const widget::Outlined outlined, - std::format_string fmt, Args&&... args) + EntityOverlay& add_centered_bottom_label(const Color& color, const float offset, + const widget::Outlined outlined, std::format_string fmt, + Args&&... args) { return add_centered_bottom_label(color, offset, outlined, std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))}); @@ -164,6 +169,44 @@ namespace omath::hud return *this; } + template + static std::expected + from_aabb(const Camera& camera, const Aabb& aabb, const float aspect, + const std::shared_ptr& renderer) + { + Vector3 top; + Vector3 bottom; + bool has_projected_vertex = false; + + for (const auto& vertex: aabb.vertices()) + { + if (auto projected = camera.world_to_screen_unclipped(vertex)) + { + if (!has_projected_vertex) + { + top = *projected; + bottom = *projected; + has_projected_vertex = true; + continue; + } + + if (projected->y < top.y) + top = *projected; + if (projected->y > bottom.y) + bottom = *projected; + } + } + + if (!has_projected_vertex) + return std::unexpected(""); + + auto center = camera.world_to_screen_unclipped(aabb.center()); + if (!center) + return std::unexpected(""); + + return EntityOverlay{{center->x, top.y}, {center->x, bottom.y}, aspect, renderer}; + } + private: // optional dispatch — enables when() conditional widgets template diff --git a/tests/general/unit_test_aabb.cpp b/tests/general/unit_test_aabb.cpp index 02d3a90..d7188ef 100644 --- a/tests/general/unit_test_aabb.cpp +++ b/tests/general/unit_test_aabb.cpp @@ -162,6 +162,23 @@ TEST(AabbTests, TopAndBottomAreSymmetric) EXPECT_FLOAT_EQ(box.top().z, -box.bottom().z); } +// --- vertices() --- + +TEST(AabbTests, VerticesReturnsAllCorners) +{ + constexpr AABB box{{1.f, 2.f, 3.f}, {4.f, 6.f, 8.f}}; + constexpr auto v = box.vertices(); + + EXPECT_EQ(v[0], Vec3(1.f, 2.f, 3.f)); + EXPECT_EQ(v[1], Vec3(4.f, 2.f, 3.f)); + EXPECT_EQ(v[2], Vec3(1.f, 6.f, 3.f)); + EXPECT_EQ(v[3], Vec3(4.f, 6.f, 3.f)); + EXPECT_EQ(v[4], Vec3(1.f, 2.f, 8.f)); + EXPECT_EQ(v[5], Vec3(4.f, 2.f, 8.f)); + EXPECT_EQ(v[6], Vec3(1.f, 6.f, 8.f)); + EXPECT_EQ(v[7], Vec3(4.f, 6.f, 8.f)); +} + // --- is_collide() --- TEST(AabbTests, OverlappingBoxesCollide)