From fd531c930cf4f1088c275affdf515dfa4651dcce Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 16 Mar 2026 02:21:24 +0300 Subject: [PATCH 1/6] added spacer --- include/omath/hud/entity_overlay_widgets.hpp | 8 +++++++- source/hud/entity_overlay.cpp | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/omath/hud/entity_overlay_widgets.hpp b/include/omath/hud/entity_overlay_widgets.hpp index 0a4b3e6a..248100e5 100644 --- a/include/omath/hud/entity_overlay_widgets.hpp +++ b/include/omath/hud/entity_overlay_widgets.hpp @@ -99,11 +99,17 @@ namespace omath::hud::widget template Centered(W) -> Centered; + /// Empty gap that advances the side cursor without drawing. + struct Spacer + { + float size; + }; + // ── Side widget variant ─────────────────────────────────────────────────── struct None { }; ///< No-op placeholder — used by widget::when for disabled elements. - using SideWidget = std::variant>; + using SideWidget = std::variant, Spacer>; // ── Side containers ─────────────────────────────────────────────────────── // Storing std::initializer_list is safe here: the backing array diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index 11651368..ff60295e 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -509,6 +509,10 @@ namespace omath::hud { add_right_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, + [this](const widget::Spacer& w) + { + m_text_cursor_right.x += w.size; + }, }, child); } @@ -538,6 +542,10 @@ namespace omath::hud { add_left_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, + [this](const widget::Spacer& w) + { + m_text_cursor_left.x -= w.size; + }, }, child); } @@ -567,6 +575,10 @@ namespace omath::hud { add_centered_top_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, + [this](const widget::Spacer& w) + { + m_text_cursor_top.y -= w.size; + }, }, child); } @@ -596,6 +608,10 @@ namespace omath::hud add_centered_bottom_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, + [this](const widget::Spacer& w) + { + m_text_cursor_bottom.y += w.size; + }, }, child); } From d8632dc74c8526f9f280455cc6924575c95ec8e9 Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 16 Mar 2026 03:03:23 +0300 Subject: [PATCH 2/6] added progress ring --- examples/example_hud/gui/main_window.cpp | 18 ++- examples/example_hud/gui/main_window.hpp | 6 + include/omath/hud/entity_overlay.hpp | 17 +++ include/omath/hud/entity_overlay_widgets.hpp | 13 +- include/omath/hud/hud_renderer_interface.hpp | 10 ++ .../renderer_realizations/imgui_renderer.hpp | 6 + source/hud/entity_overlay.cpp | 113 +++++++++++++++++- .../renderer_realizations/imgui_renderer.cpp | 19 +++ 8 files changed, 196 insertions(+), 6 deletions(-) diff --git a/examples/example_hud/gui/main_window.cpp b/examples/example_hud/gui/main_window.cpp index 05c022f1..c0da3c3c 100644 --- a/examples/example_hud/gui/main_window.cpp +++ b/examples/example_hud/gui/main_window.cpp @@ -138,6 +138,17 @@ namespace imgui_desktop::gui ImGui::SliderFloat("Thick##skel", &m_skel_thickness, 0.5f, 5.f); } + if (ImGui::CollapsingHeader("Progress Ring")) + { + ImGui::Checkbox("Show##ring", &m_show_ring); + ImGui::ColorEdit4("Color##ring", reinterpret_cast(&m_ring_color), ImGuiColorEditFlags_NoInputs); + ImGui::ColorEdit4("BG##ring", reinterpret_cast(&m_ring_bg), ImGuiColorEditFlags_NoInputs); + ImGui::SliderFloat("Radius##ring", &m_ring_radius, 4.f, 30.f); + ImGui::SliderFloat("Value##ring", &m_ring_ratio, 0.f, 1.f); + ImGui::SliderFloat("Thick##ring", &m_ring_thickness, 0.5f, 6.f); + ImGui::SliderFloat("Offset##ring", &m_ring_offset, 0.f, 15.f); + } + if (ImGui::CollapsingHeader("Snap Line")) { ImGui::Checkbox("Show##snap", &m_show_snap); @@ -166,7 +177,6 @@ namespace imgui_desktop::gui when(m_show_cornered_box, CorneredBox{omath::Color::from_rgba(255, 0, 255, 255), m_box_fill, m_corner_ratio, m_box_thickness}), when(m_show_dashed_box, DashedBox{m_dash_color, m_dash_len, m_dash_gap, m_dash_thickness}), - RightSide{ when(m_show_right_bar, bar), when(m_show_right_dashed_bar, dbar), @@ -176,6 +186,12 @@ namespace imgui_desktop::gui Label{{1.f, 0.f, 0.f, 1.f}, m_label_offset, m_outlined, "Shield: 125/125"}), when(m_show_right_labels, Label{{1.f, 0.f, 1.f, 1.f}, m_label_offset, m_outlined, "*LOCKED*"}), + + Spacer{10}, + when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio, + m_ring_thickness, m_ring_offset}), + when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio, + m_ring_thickness, m_ring_offset}), }, LeftSide{ when(m_show_left_bar, bar), diff --git a/examples/example_hud/gui/main_window.hpp b/examples/example_hud/gui/main_window.hpp index 6572bdf4..d7eb540e 100644 --- a/examples/example_hud/gui/main_window.hpp +++ b/examples/example_hud/gui/main_window.hpp @@ -61,6 +61,12 @@ namespace imgui_desktop::gui float m_skel_thickness = 1.f; bool m_show_skeleton = false; + // Progress ring + omath::Color m_ring_color = omath::Color::from_rgba(0, 200, 255, 255); + omath::Color m_ring_bg{0.3f, 0.3f, 0.3f, 0.5f}; + float m_ring_radius = 10.f, m_ring_ratio = 0.65f, m_ring_thickness = 2.5f, m_ring_offset = 5.f; + bool m_show_ring = false; + // Snap line omath::Color m_snap_color = omath::Color::from_rgba(255, 50, 50, 255); float m_snap_width = 1.5f; diff --git a/include/omath/hud/entity_overlay.hpp b/include/omath/hud/entity_overlay.hpp index 2bfb873c..ac8acef6 100644 --- a/include/omath/hud/entity_overlay.hpp +++ b/include/omath/hud/entity_overlay.hpp @@ -118,6 +118,22 @@ namespace omath::hud std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))}); } + // ── Spacers ───────────────────────────────────────────────────── + EntityOverlay& add_right_spacer(float size); + EntityOverlay& add_left_spacer(float size); + EntityOverlay& add_top_spacer(float size); + EntityOverlay& add_bottom_spacer(float size); + + // ── Progress rings ────────────────────────────────────────────── + EntityOverlay& add_right_progress_ring(const Color& color, const Color& bg, float radius, float ratio, + float thickness = 2.f, float offset = 5.f, int segments = 0); + EntityOverlay& add_left_progress_ring(const Color& color, const Color& bg, float radius, float ratio, + float thickness = 2.f, float offset = 5.f, int segments = 0); + EntityOverlay& add_top_progress_ring(const Color& color, const Color& bg, float radius, float ratio, + float thickness = 2.f, float offset = 5.f, int segments = 0); + EntityOverlay& add_bottom_progress_ring(const Color& color, const Color& bg, float radius, float ratio, + float thickness = 2.f, float offset = 5.f, int segments = 0); + // ── Misc ───────────────────────────────────────────────────────── EntityOverlay& add_snap_line(const Vector2& start_pos, const Color& color, float width); @@ -151,6 +167,7 @@ namespace omath::hud void dispatch(const widget::BottomSide& bottom_side); void dispatch(const widget::Skeleton& skeleton); void dispatch(const widget::SnapLine& snap_line); + void draw_progress_ring(const Vector2& center, const widget::ProgressRing& ring); void draw_outlined_text(const Vector2& position, const Color& color, const std::string_view& text); void draw_dashed_line(const Vector2& from, const Vector2& to, const Color& color, float dash_len, float gap_len, float thickness) const; diff --git a/include/omath/hud/entity_overlay_widgets.hpp b/include/omath/hud/entity_overlay_widgets.hpp index 248100e5..eaf2fc47 100644 --- a/include/omath/hud/entity_overlay_widgets.hpp +++ b/include/omath/hud/entity_overlay_widgets.hpp @@ -105,11 +105,22 @@ namespace omath::hud::widget float size; }; + struct ProgressRing + { + Color color; + Color bg{0.3f, 0.3f, 0.3f, 0.5f}; + float radius = 12.f; + float ratio; + float thickness = 2.f; + float offset = 5.f; + int segments = 32; + }; + // ── Side widget variant ─────────────────────────────────────────────────── struct None { }; ///< No-op placeholder — used by widget::when for disabled elements. - using SideWidget = std::variant, Spacer>; + using SideWidget = std::variant, Spacer, ProgressRing>; // ── Side containers ─────────────────────────────────────────────────────── // Storing std::initializer_list is safe here: the backing array diff --git a/include/omath/hud/hud_renderer_interface.hpp b/include/omath/hud/hud_renderer_interface.hpp index 8d625324..e22b9b97 100644 --- a/include/omath/hud/hud_renderer_interface.hpp +++ b/include/omath/hud/hud_renderer_interface.hpp @@ -24,6 +24,16 @@ namespace omath::hud virtual void add_filled_rectangle(const Vector2& min, const Vector2& max, const Color& color) = 0; + virtual void add_circle(const Vector2& center, float radius, const Color& color, float thickness, + int segments = 0) = 0; + + virtual void add_filled_circle(const Vector2& center, float radius, const Color& color, + int segments = 0) = 0; + + /// Draw an arc (partial circle outline). Angles in radians, 0 = right (+X), counter-clockwise. + virtual void add_arc(const Vector2& center, float radius, float a_min, float a_max, const Color& color, + float thickness, int segments = 0) = 0; + virtual void add_text(const Vector2& position, const Color& color, const std::string_view& text) = 0; [[nodiscard]] diff --git a/include/omath/hud/renderer_realizations/imgui_renderer.hpp b/include/omath/hud/renderer_realizations/imgui_renderer.hpp index 5645de2e..bc1442c6 100644 --- a/include/omath/hud/renderer_realizations/imgui_renderer.hpp +++ b/include/omath/hud/renderer_realizations/imgui_renderer.hpp @@ -17,6 +17,12 @@ namespace omath::hud void add_filled_polyline(const std::span>& vertexes, const Color& color) override; void add_rectangle(const Vector2& min, const Vector2& max, const Color& color) override; void add_filled_rectangle(const Vector2& min, const Vector2& max, const Color& color) override; + void add_circle(const Vector2& center, float radius, const Color& color, float thickness, + int segments = 0) override; + void add_filled_circle(const Vector2& center, float radius, const Color& color, + int segments = 0) override; + void add_arc(const Vector2& center, float radius, float a_min, float a_max, const Color& color, + float thickness, int segments = 0) override; void add_text(const Vector2& position, const Color& color, const std::string_view& text) override; [[nodiscard]] virtual Vector2 calc_text_size(const std::string_view& text) override; diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index ff60295e..a3977076 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -457,6 +457,76 @@ namespace omath::hud m_text_cursor_left(m_canvas.top_left_corner), m_renderer(renderer) { } + // ── Spacers ───────────────────────────────────────────────────────────────── + EntityOverlay& EntityOverlay::add_right_spacer(const float size) + { + m_text_cursor_right.x += size; + return *this; + } + + EntityOverlay& EntityOverlay::add_left_spacer(const float size) + { + m_text_cursor_left.x -= size; + return *this; + } + + EntityOverlay& EntityOverlay::add_top_spacer(const float size) + { + m_text_cursor_top.y -= size; + return *this; + } + + EntityOverlay& EntityOverlay::add_bottom_spacer(const float size) + { + m_text_cursor_bottom.y += size; + return *this; + } + + // ── Progress rings ────────────────────────────────────────────────────────── + EntityOverlay& EntityOverlay::add_right_progress_ring(const Color& color, const Color& bg, const float radius, + const float ratio, const float thickness, const float offset, + const int segments) + { + const auto cx = m_text_cursor_right.x + offset + radius; + const auto cy = m_text_cursor_right.y + radius; + draw_progress_ring({cx, cy}, widget::ProgressRing{color, bg, radius, ratio, thickness, offset, segments}); + m_text_cursor_right.y += radius * 2.f; + return *this; + } + + EntityOverlay& EntityOverlay::add_left_progress_ring(const Color& color, const Color& bg, const float radius, + const float ratio, const float thickness, const float offset, + const int segments) + { + const auto cx = m_text_cursor_left.x - offset - radius; + const auto cy = m_text_cursor_left.y + radius; + draw_progress_ring({cx, cy}, widget::ProgressRing{color, bg, radius, ratio, thickness, offset, segments}); + m_text_cursor_left.y += radius * 2.f; + return *this; + } + + EntityOverlay& EntityOverlay::add_top_progress_ring(const Color& color, const Color& bg, const float radius, + const float ratio, const float thickness, const float offset, + const int segments) + { + m_text_cursor_top.y -= radius * 2.f; + const auto cx = m_text_cursor_top.x + radius; + const auto cy = m_text_cursor_top.y - offset + radius; + draw_progress_ring({cx, cy}, widget::ProgressRing{color, bg, radius, ratio, thickness, offset, segments}); + return *this; + } + + EntityOverlay& EntityOverlay::add_bottom_progress_ring(const Color& color, const Color& bg, const float radius, + const float ratio, const float thickness, const float offset, + const int segments) + { + const auto cx = m_text_cursor_bottom.x + radius; + const auto cy = m_text_cursor_bottom.y + offset + radius; + draw_progress_ring({cx, cy}, widget::ProgressRing{color, bg, radius, ratio, thickness, offset, segments}); + m_text_cursor_bottom.y += radius * 2.f; + return *this; + } + // ── widget dispatch ─────────────────────────────────────────────────────── void EntityOverlay::dispatch(const widget::Box& box) { @@ -483,6 +553,21 @@ namespace omath::hud add_snap_line(snap_line.start, snap_line.color, snap_line.width); } + void EntityOverlay::draw_progress_ring(const Vector2& center, const widget::ProgressRing& ring) + { + constexpr auto pi = std::numbers::pi_v; + const float ratio = std::clamp(ring.ratio, 0.f, 1.f); + + m_renderer->add_circle(center, ring.radius, ring.bg, ring.thickness, ring.segments); + + if (ratio > 0.f) + { + const float a_min = -pi / 2.f; + const float a_max = a_min + ratio * 2.f * pi; + m_renderer->add_arc(center, ring.radius, a_min, a_max, ring.color, ring.thickness, ring.segments); + } + } + // ── Side container dispatch ─────────────────────────────────────────────── void EntityOverlay::dispatch(const widget::RightSide& s) { @@ -511,7 +596,12 @@ namespace omath::hud }, [this](const widget::Spacer& w) { - m_text_cursor_right.x += w.size; + add_right_spacer(w.size); + }, + [this](const widget::ProgressRing& w) + { + add_right_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, + w.segments); }, }, child); @@ -544,7 +634,12 @@ namespace omath::hud }, [this](const widget::Spacer& w) { - m_text_cursor_left.x -= w.size; + add_left_spacer(w.size); + }, + [this](const widget::ProgressRing& w) + { + add_left_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, + w.segments); }, }, child); @@ -577,7 +672,12 @@ namespace omath::hud }, [this](const widget::Spacer& w) { - m_text_cursor_top.y -= w.size; + add_top_spacer(w.size); + }, + [this](const widget::ProgressRing& w) + { + add_top_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, + w.segments); }, }, child); @@ -610,7 +710,12 @@ namespace omath::hud }, [this](const widget::Spacer& w) { - m_text_cursor_bottom.y += w.size; + add_bottom_spacer(w.size); + }, + [this](const widget::ProgressRing& w) + { + add_bottom_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, + w.segments); }, }, child); diff --git a/source/hud/renderer_realizations/imgui_renderer.cpp b/source/hud/renderer_realizations/imgui_renderer.cpp index 5d397d4d..d7d0c918 100644 --- a/source/hud/renderer_realizations/imgui_renderer.cpp +++ b/source/hud/renderer_realizations/imgui_renderer.cpp @@ -42,6 +42,25 @@ namespace omath::hud ImGui::GetBackgroundDrawList()->AddRectFilled(min.to_im_vec2(), max.to_im_vec2(), color.to_im_color()); } + void ImguiHudRenderer::add_circle(const Vector2& center, const float radius, const Color& color, + const float thickness, const int segments) + { + ImGui::GetBackgroundDrawList()->AddCircle(center.to_im_vec2(), radius, color.to_im_color(), segments, thickness); + } + + void ImguiHudRenderer::add_filled_circle(const Vector2& center, const float radius, const Color& color, + const int segments) + { + ImGui::GetBackgroundDrawList()->AddCircleFilled(center.to_im_vec2(), radius, color.to_im_color(), segments); + } + + void ImguiHudRenderer::add_arc(const Vector2& center, const float radius, const float a_min, const float a_max, + const Color& color, const float thickness, const int segments) + { + ImGui::GetBackgroundDrawList()->PathArcTo(center.to_im_vec2(), radius, a_min, a_max, segments); + ImGui::GetBackgroundDrawList()->PathStroke(color.to_im_color(), ImDrawFlags_None, thickness); + } + void ImguiHudRenderer::add_text(const Vector2& position, const Color& color, const std::string_view& text) { ImGui::GetBackgroundDrawList()->AddText(position.to_im_vec2(), color.to_im_color(), text.data(), From 2e8a74aaaf5ebb4d4f53f7c870069554467939f0 Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 16 Mar 2026 03:06:14 +0300 Subject: [PATCH 3/6] imroved spacer --- examples/example_hud/gui/main_window.cpp | 4 +- include/omath/hud/entity_overlay.hpp | 12 ++-- include/omath/hud/entity_overlay_widgets.hpp | 12 +++- source/hud/entity_overlay.cpp | 64 ++++++++++++++++---- 4 files changed, 70 insertions(+), 22 deletions(-) diff --git a/examples/example_hud/gui/main_window.cpp b/examples/example_hud/gui/main_window.cpp index c0da3c3c..2f242f6e 100644 --- a/examples/example_hud/gui/main_window.cpp +++ b/examples/example_hud/gui/main_window.cpp @@ -187,9 +187,7 @@ namespace imgui_desktop::gui when(m_show_right_labels, Label{{1.f, 0.f, 1.f, 1.f}, m_label_offset, m_outlined, "*LOCKED*"}), - Spacer{10}, - when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio, - m_ring_thickness, m_ring_offset}), + SpaceVertical{10}, when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio, m_ring_thickness, m_ring_offset}), }, diff --git a/include/omath/hud/entity_overlay.hpp b/include/omath/hud/entity_overlay.hpp index ac8acef6..1922ca46 100644 --- a/include/omath/hud/entity_overlay.hpp +++ b/include/omath/hud/entity_overlay.hpp @@ -119,10 +119,14 @@ namespace omath::hud } // ── Spacers ───────────────────────────────────────────────────── - EntityOverlay& add_right_spacer(float size); - EntityOverlay& add_left_spacer(float size); - EntityOverlay& add_top_spacer(float size); - EntityOverlay& add_bottom_spacer(float size); + EntityOverlay& add_right_space_vertical(float size); + EntityOverlay& add_right_space_horizontal(float size); + EntityOverlay& add_left_space_vertical(float size); + EntityOverlay& add_left_space_horizontal(float size); + EntityOverlay& add_top_space_vertical(float size); + EntityOverlay& add_top_space_horizontal(float size); + EntityOverlay& add_bottom_space_vertical(float size); + EntityOverlay& add_bottom_space_horizontal(float size); // ── Progress rings ────────────────────────────────────────────── EntityOverlay& add_right_progress_ring(const Color& color, const Color& bg, float radius, float ratio, diff --git a/include/omath/hud/entity_overlay_widgets.hpp b/include/omath/hud/entity_overlay_widgets.hpp index eaf2fc47..8a370cdc 100644 --- a/include/omath/hud/entity_overlay_widgets.hpp +++ b/include/omath/hud/entity_overlay_widgets.hpp @@ -99,8 +99,14 @@ namespace omath::hud::widget template Centered(W) -> Centered; - /// Empty gap that advances the side cursor without drawing. - struct Spacer + /// Empty vertical gap that advances the Y cursor without drawing. + struct SpaceVertical + { + float size; + }; + + /// Empty horizontal gap that advances the X cursor without drawing. + struct SpaceHorizontal { float size; }; @@ -120,7 +126,7 @@ namespace omath::hud::widget struct None { }; ///< No-op placeholder — used by widget::when for disabled elements. - using SideWidget = std::variant, Spacer, ProgressRing>; + using SideWidget = std::variant, SpaceVertical, SpaceHorizontal, ProgressRing>; // ── Side containers ─────────────────────────────────────────────────────── // Storing std::initializer_list is safe here: the backing array diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index a3977076..c5691cf3 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -458,30 +458,54 @@ namespace omath::hud { } // ── Spacers ───────────────────────────────────────────────────────────────── - EntityOverlay& EntityOverlay::add_right_spacer(const float size) + EntityOverlay& EntityOverlay::add_right_space_vertical(const float size) + { + m_text_cursor_right.y += size; + return *this; + } + + EntityOverlay& EntityOverlay::add_right_space_horizontal(const float size) { m_text_cursor_right.x += size; return *this; } - EntityOverlay& EntityOverlay::add_left_spacer(const float size) + EntityOverlay& EntityOverlay::add_left_space_vertical(const float size) + { + m_text_cursor_left.y += size; + return *this; + } + + EntityOverlay& EntityOverlay::add_left_space_horizontal(const float size) { m_text_cursor_left.x -= size; return *this; } - EntityOverlay& EntityOverlay::add_top_spacer(const float size) + EntityOverlay& EntityOverlay::add_top_space_vertical(const float size) { m_text_cursor_top.y -= size; return *this; } - EntityOverlay& EntityOverlay::add_bottom_spacer(const float size) + EntityOverlay& EntityOverlay::add_top_space_horizontal(const float size) + { + m_text_cursor_top.x += size; + return *this; + } + + EntityOverlay& EntityOverlay::add_bottom_space_vertical(const float size) { m_text_cursor_bottom.y += size; return *this; } + EntityOverlay& EntityOverlay::add_bottom_space_horizontal(const float size) + { + m_text_cursor_bottom.x += size; + return *this; + } + // ── Progress rings ────────────────────────────────────────────────────────── EntityOverlay& EntityOverlay::add_right_progress_ring(const Color& color, const Color& bg, const float radius, const float ratio, const float thickness, const float offset, @@ -594,9 +618,13 @@ namespace omath::hud { add_right_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, - [this](const widget::Spacer& w) + [this](const widget::SpaceVertical& w) + { + add_right_space_vertical(w.size); + }, + [this](const widget::SpaceHorizontal& w) { - add_right_spacer(w.size); + add_right_space_horizontal(w.size); }, [this](const widget::ProgressRing& w) { @@ -632,9 +660,13 @@ namespace omath::hud { add_left_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, - [this](const widget::Spacer& w) + [this](const widget::SpaceVertical& w) { - add_left_spacer(w.size); + add_left_space_vertical(w.size); + }, + [this](const widget::SpaceHorizontal& w) + { + add_left_space_horizontal(w.size); }, [this](const widget::ProgressRing& w) { @@ -670,9 +702,13 @@ namespace omath::hud { add_centered_top_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, - [this](const widget::Spacer& w) + [this](const widget::SpaceVertical& w) + { + add_top_space_vertical(w.size); + }, + [this](const widget::SpaceHorizontal& w) { - add_top_spacer(w.size); + add_top_space_horizontal(w.size); }, [this](const widget::ProgressRing& w) { @@ -708,9 +744,13 @@ namespace omath::hud add_centered_bottom_label(w.child.color, w.child.offset, w.child.outlined, w.child.text); }, - [this](const widget::Spacer& w) + [this](const widget::SpaceVertical& w) + { + add_bottom_space_vertical(w.size); + }, + [this](const widget::SpaceHorizontal& w) { - add_bottom_spacer(w.size); + add_bottom_space_horizontal(w.size); }, [this](const widget::ProgressRing& w) { From b6b0d4db133729baf7a1f173541c28e23574b7aa Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 16 Mar 2026 03:24:53 +0300 Subject: [PATCH 4/6] added aim dot --- examples/example_hud/gui/main_window.cpp | 17 ++++++++++++ examples/example_hud/gui/main_window.hpp | 11 ++++++++ include/omath/hud/entity_overlay.hpp | 2 ++ include/omath/hud/entity_overlay_widgets.hpp | 15 +++++++++++ source/hud/entity_overlay.cpp | 28 ++++++++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/examples/example_hud/gui/main_window.cpp b/examples/example_hud/gui/main_window.cpp index 2f242f6e..4a71f0c4 100644 --- a/examples/example_hud/gui/main_window.cpp +++ b/examples/example_hud/gui/main_window.cpp @@ -149,6 +149,21 @@ namespace imgui_desktop::gui ImGui::SliderFloat("Offset##ring", &m_ring_offset, 0.f, 15.f); } + if (ImGui::CollapsingHeader("Scan Marker")) + { + ImGui::Checkbox("Show##scan", &m_show_scan); + ImGui::ColorEdit4("Fill##scan", reinterpret_cast(&m_scan_color), ImGuiColorEditFlags_NoInputs); + ImGui::ColorEdit4("Outline##scan", reinterpret_cast(&m_scan_outline), ImGuiColorEditFlags_NoInputs); + ImGui::SliderFloat("Thick##scan", &m_scan_outline_thickness, 0.5f, 5.f); + } + + if (ImGui::CollapsingHeader("Aim Dot")) + { + ImGui::Checkbox("Show##aim", &m_show_aim); + ImGui::ColorEdit4("Color##aim", reinterpret_cast(&m_aim_color), ImGuiColorEditFlags_NoInputs); + ImGui::SliderFloat("Radius##aim", &m_aim_radius, 1.f, 10.f); + } + if (ImGui::CollapsingHeader("Snap Line")) { ImGui::Checkbox("Show##snap", &m_show_snap); @@ -217,6 +232,8 @@ namespace imgui_desktop::gui when(m_show_bottom_labels, Label{omath::Color::from_rgba(200, 200, 0, 255), m_label_offset, m_outlined, "42m"}), }, + when(m_show_aim, AimDot{{m_entity_x, m_entity_top_y+40.f}, m_aim_color, m_aim_radius}), + when(m_show_scan, ScanMarker{m_scan_color, m_scan_outline, m_scan_outline_thickness}), when(m_show_skeleton, Skeleton{m_skel_color, m_skel_thickness}), when(m_show_snap, SnapLine{{vp->Size.x / 2.f, vp->Size.y}, m_snap_color, m_snap_width})); } diff --git a/examples/example_hud/gui/main_window.hpp b/examples/example_hud/gui/main_window.hpp index d7eb540e..8b622bf2 100644 --- a/examples/example_hud/gui/main_window.hpp +++ b/examples/example_hud/gui/main_window.hpp @@ -67,6 +67,17 @@ namespace imgui_desktop::gui float m_ring_radius = 10.f, m_ring_ratio = 0.65f, m_ring_thickness = 2.5f, m_ring_offset = 5.f; bool m_show_ring = false; + // Scan marker + omath::Color m_scan_color = omath::Color::from_rgba(255, 200, 0, 150); + omath::Color m_scan_outline = omath::Color::from_rgba(255, 200, 0, 255); + float m_scan_outline_thickness = 2.f; + bool m_show_scan = false; + + // Aim dot + omath::Color m_aim_color = omath::Color::from_rgba(255, 0, 0, 255); + float m_aim_radius = 3.f; + bool m_show_aim = false; + // Snap line omath::Color m_snap_color = omath::Color::from_rgba(255, 50, 50, 255); float m_snap_width = 1.5f; diff --git a/include/omath/hud/entity_overlay.hpp b/include/omath/hud/entity_overlay.hpp index 1922ca46..4fe7ed1b 100644 --- a/include/omath/hud/entity_overlay.hpp +++ b/include/omath/hud/entity_overlay.hpp @@ -171,6 +171,8 @@ namespace omath::hud void dispatch(const widget::BottomSide& bottom_side); void dispatch(const widget::Skeleton& skeleton); void dispatch(const widget::SnapLine& snap_line); + void dispatch(const widget::ScanMarker& scan_marker); + void dispatch(const widget::AimDot& aim_dot); void draw_progress_ring(const Vector2& center, const widget::ProgressRing& ring); void draw_outlined_text(const Vector2& position, const Color& color, const std::string_view& text); void draw_dashed_line(const Vector2& from, const Vector2& to, const Color& color, float dash_len, diff --git a/include/omath/hud/entity_overlay_widgets.hpp b/include/omath/hud/entity_overlay_widgets.hpp index 8a370cdc..db290fbf 100644 --- a/include/omath/hud/entity_overlay_widgets.hpp +++ b/include/omath/hud/entity_overlay_widgets.hpp @@ -56,6 +56,21 @@ namespace omath::hud::widget float width; }; + struct ScanMarker + { + Color color; + Color outline{0.f, 0.f, 0.f, 0.f}; + float outline_thickness = 1.f; + }; + + /// Dot at an absolute screen position. + struct AimDot + { + Vector2 position; + Color color; + float radius = 3.f; + }; + // ── Side-agnostic widgets (used inside XxxSide containers) ──────────────── /// A filled bar. `size` is width for left/right sides, height for top/bottom. diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index c5691cf3..5330e3f2 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -577,6 +577,34 @@ namespace omath::hud add_snap_line(snap_line.start, snap_line.color, snap_line.width); } + void EntityOverlay::dispatch(const widget::ScanMarker& scan_marker) + { + const auto box_width = std::abs(m_canvas.top_right_corner.x - m_canvas.top_left_corner.x); + const auto box_height = std::abs(m_canvas.bottom_left_corner.y - m_canvas.top_left_corner.y); + + const auto center_x = (m_canvas.top_left_corner.x + m_canvas.top_right_corner.x) / 2.f; + const auto center_y = m_canvas.top_left_corner.y + box_height * 0.44f; + + const auto side = std::min(box_width, box_height) * 0.5f; + const auto h = side * std::sqrt(3.f) / 2.f; + + const std::array, 3> tri = { + Vector2{center_x, center_y - h * 2.f / 3.f}, + Vector2{center_x - side / 2.f, center_y + h / 3.f}, + Vector2{center_x + side / 2.f, center_y + h / 3.f}, + }; + + m_renderer->add_filled_polyline({tri.data(), tri.size()}, scan_marker.color); + + if (scan_marker.outline.value().w > 0.f) + m_renderer->add_polyline({tri.data(), tri.size()}, scan_marker.outline, scan_marker.outline_thickness); + } + + void EntityOverlay::dispatch(const widget::AimDot& aim_dot) + { + m_renderer->add_filled_circle(aim_dot.position, aim_dot.radius, aim_dot.color); + } + void EntityOverlay::draw_progress_ring(const Vector2& center, const widget::ProgressRing& ring) { constexpr auto pi = std::numbers::pi_v; From 1117eb37f19a636acb6da304efa7b569c729ea9d Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 16 Mar 2026 13:13:41 +0300 Subject: [PATCH 5/6] added icon --- include/omath/hud/entity_overlay.hpp | 10 ++++ include/omath/hud/entity_overlay_widgets.hpp | 13 ++++- include/omath/hud/hud_renderer_interface.hpp | 5 ++ .../renderer_realizations/imgui_renderer.hpp | 2 + source/hud/entity_overlay.cpp | 53 +++++++++++++++++++ .../renderer_realizations/imgui_renderer.cpp | 7 +++ 6 files changed, 89 insertions(+), 1 deletion(-) diff --git a/include/omath/hud/entity_overlay.hpp b/include/omath/hud/entity_overlay.hpp index 4fe7ed1b..94d076c5 100644 --- a/include/omath/hud/entity_overlay.hpp +++ b/include/omath/hud/entity_overlay.hpp @@ -138,6 +138,16 @@ namespace omath::hud EntityOverlay& add_bottom_progress_ring(const Color& color, const Color& bg, float radius, float ratio, float thickness = 2.f, float offset = 5.f, int segments = 0); + // ── Icons ──────────────────────────────────────────────────────── + EntityOverlay& add_right_icon(const std::any& texture_id, float width, float height, + const Color& tint = Color{1.f, 1.f, 1.f, 1.f}, float offset = 5.f); + EntityOverlay& add_left_icon(const std::any& texture_id, float width, float height, + const Color& tint = Color{1.f, 1.f, 1.f, 1.f}, float offset = 5.f); + EntityOverlay& add_top_icon(const std::any& texture_id, float width, float height, + const Color& tint = Color{1.f, 1.f, 1.f, 1.f}, float offset = 5.f); + EntityOverlay& add_bottom_icon(const std::any& texture_id, float width, float height, + const Color& tint = Color{1.f, 1.f, 1.f, 1.f}, float offset = 5.f); + // ── Misc ───────────────────────────────────────────────────────── EntityOverlay& add_snap_line(const Vector2& start_pos, const Color& color, float width); diff --git a/include/omath/hud/entity_overlay_widgets.hpp b/include/omath/hud/entity_overlay_widgets.hpp index db290fbf..52757bab 100644 --- a/include/omath/hud/entity_overlay_widgets.hpp +++ b/include/omath/hud/entity_overlay_widgets.hpp @@ -4,6 +4,7 @@ #pragma once #include "omath/linear_algebra/vector2.hpp" #include "omath/utility/color.hpp" +#include #include #include #include @@ -137,11 +138,21 @@ namespace omath::hud::widget int segments = 32; }; + struct Icon + { + std::any texture_id; + float width; + float height; + Color tint{1.f, 1.f, 1.f, 1.f}; + float offset = 5.f; + }; + // ── Side widget variant ─────────────────────────────────────────────────── struct None { }; ///< No-op placeholder — used by widget::when for disabled elements. - using SideWidget = std::variant, SpaceVertical, SpaceHorizontal, ProgressRing>; + using SideWidget = + std::variant, SpaceVertical, SpaceHorizontal, ProgressRing, Icon>; // ── Side containers ─────────────────────────────────────────────────────── // Storing std::initializer_list is safe here: the backing array diff --git a/include/omath/hud/hud_renderer_interface.hpp b/include/omath/hud/hud_renderer_interface.hpp index e22b9b97..5a25e036 100644 --- a/include/omath/hud/hud_renderer_interface.hpp +++ b/include/omath/hud/hud_renderer_interface.hpp @@ -4,6 +4,7 @@ #pragma once #include "omath/linear_algebra/vector2.hpp" #include "omath/utility/color.hpp" +#include #include namespace omath::hud @@ -34,6 +35,10 @@ namespace omath::hud virtual void add_arc(const Vector2& center, float radius, float a_min, float a_max, const Color& color, float thickness, int segments = 0) = 0; + /// Draw a textured quad. texture_id is renderer-specific (e.g. ImTextureID for ImGui). + virtual void add_image(const std::any& texture_id, const Vector2& min, const Vector2& max, + const Color& tint = Color{1.f, 1.f, 1.f, 1.f}) = 0; + virtual void add_text(const Vector2& position, const Color& color, const std::string_view& text) = 0; [[nodiscard]] diff --git a/include/omath/hud/renderer_realizations/imgui_renderer.hpp b/include/omath/hud/renderer_realizations/imgui_renderer.hpp index bc1442c6..8b8190b4 100644 --- a/include/omath/hud/renderer_realizations/imgui_renderer.hpp +++ b/include/omath/hud/renderer_realizations/imgui_renderer.hpp @@ -23,6 +23,8 @@ namespace omath::hud int segments = 0) override; void add_arc(const Vector2& center, float radius, float a_min, float a_max, const Color& color, float thickness, int segments = 0) override; + void add_image(const std::any& texture_id, const Vector2& min, const Vector2& max, + const Color& tint = Color{1.f, 1.f, 1.f, 1.f}) override; void add_text(const Vector2& position, const Color& color, const std::string_view& text) override; [[nodiscard]] virtual Vector2 calc_text_size(const std::string_view& text) override; diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index 5330e3f2..328dc7e8 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -551,6 +551,43 @@ namespace omath::hud return *this; } + // ── Icons ──────────────────────────────────────────────────────────────────── + EntityOverlay& EntityOverlay::add_right_icon(const std::any& texture_id, const float width, const float height, + const Color& tint, const float offset) + { + const auto pos = m_text_cursor_right + Vector2{offset, 0.f}; + m_renderer->add_image(texture_id, pos, pos + Vector2{width, height}, tint); + m_text_cursor_right.y += height; + return *this; + } + + EntityOverlay& EntityOverlay::add_left_icon(const std::any& texture_id, const float width, const float height, + const Color& tint, const float offset) + { + const auto pos = m_text_cursor_left + Vector2{-(offset + width), 0.f}; + m_renderer->add_image(texture_id, pos, pos + Vector2{width, height}, tint); + m_text_cursor_left.y += height; + return *this; + } + + EntityOverlay& EntityOverlay::add_top_icon(const std::any& texture_id, const float width, const float height, + const Color& tint, const float offset) + { + m_text_cursor_top.y -= height; + const auto pos = m_text_cursor_top + Vector2{0.f, -offset}; + m_renderer->add_image(texture_id, pos, pos + Vector2{width, height}, tint); + return *this; + } + + EntityOverlay& EntityOverlay::add_bottom_icon(const std::any& texture_id, const float width, const float height, + const Color& tint, const float offset) + { + const auto pos = m_text_cursor_bottom + Vector2{0.f, offset}; + m_renderer->add_image(texture_id, pos, pos + Vector2{width, height}, tint); + m_text_cursor_bottom.y += height; + return *this; + } + // ── widget dispatch ─────────────────────────────────────────────────────── void EntityOverlay::dispatch(const widget::Box& box) { @@ -659,6 +696,10 @@ namespace omath::hud add_right_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, w.segments); }, + [this](const widget::Icon& w) + { + add_right_icon(w.texture_id, w.width, w.height, w.tint, w.offset); + }, }, child); } @@ -701,6 +742,10 @@ namespace omath::hud add_left_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, w.segments); }, + [this](const widget::Icon& w) + { + add_left_icon(w.texture_id, w.width, w.height, w.tint, w.offset); + }, }, child); } @@ -743,6 +788,10 @@ namespace omath::hud add_top_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, w.segments); }, + [this](const widget::Icon& w) + { + add_top_icon(w.texture_id, w.width, w.height, w.tint, w.offset); + }, }, child); } @@ -785,6 +834,10 @@ namespace omath::hud add_bottom_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, w.segments); }, + [this](const widget::Icon& w) + { + add_bottom_icon(w.texture_id, w.width, w.height, w.tint, w.offset); + }, }, child); } diff --git a/source/hud/renderer_realizations/imgui_renderer.cpp b/source/hud/renderer_realizations/imgui_renderer.cpp index d7d0c918..41943519 100644 --- a/source/hud/renderer_realizations/imgui_renderer.cpp +++ b/source/hud/renderer_realizations/imgui_renderer.cpp @@ -61,6 +61,13 @@ namespace omath::hud ImGui::GetBackgroundDrawList()->PathStroke(color.to_im_color(), ImDrawFlags_None, thickness); } + void ImguiHudRenderer::add_image(const std::any& texture_id, const Vector2& min, const Vector2& max, + const Color& tint) + { + ImGui::GetBackgroundDrawList()->AddImage(std::any_cast(texture_id), min.to_im_vec2(), + max.to_im_vec2(), {0, 0}, {1, 1}, tint.to_im_color()); + } + void ImguiHudRenderer::add_text(const Vector2& position, const Color& color, const std::string_view& text) { ImGui::GetBackgroundDrawList()->AddText(position.to_im_vec2(), color.to_im_color(), text.data(), From 29a2743728d31cfdf8302646952ff6cae356264c Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 16 Mar 2026 13:17:16 +0300 Subject: [PATCH 6/6] renamed args --- source/hud/entity_overlay.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index 328dc7e8..c866427f 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -658,9 +658,9 @@ namespace omath::hud } // ── Side container dispatch ─────────────────────────────────────────────── - void EntityOverlay::dispatch(const widget::RightSide& s) + void EntityOverlay::dispatch(const widget::RightSide& right_side) { - for (const auto& child : s.children) + for (const auto& child : right_side.children) std::visit( widget::Overloaded{ [](const widget::None&) @@ -704,9 +704,9 @@ namespace omath::hud child); } - void EntityOverlay::dispatch(const widget::LeftSide& s) + void EntityOverlay::dispatch(const widget::LeftSide& left_side) { - for (const auto& child : s.children) + for (const auto& child : left_side.children) std::visit( widget::Overloaded{ [](const widget::None&)