Browse Source

Add all four mode indicators with correct meshes and colors

Updated mode indicators to support all four modes:
- Attack mode: red crossed swords (was incorrectly labeled as hold)
- Guard mode: blue shield
- Hold mode: orange anchor (new mesh)
- Patrol mode: gray footsteps (new mesh)

Changes:
- Added k_mode_type_attack, k_mode_type_patrol constants
- Updated colors: attack=red, guard=blue, hold=orange, patrol=gray
- Created anchor mesh for hold mode
- Created footsteps mesh for patrol mode
- Updated scene renderer to check AttackComponent and PatrolComponent
- Updated backend to handle all four mode types
- Mode priority: attack > guard > hold > patrol

Co-authored-by: djeada <[email protected]>
copilot-swe-agent[bot] 1 day ago
parent
commit
ce663545a4
4 changed files with 201 additions and 19 deletions
  1. 154 6
      render/geom/mode_indicator.cpp
  2. 18 6
      render/geom/mode_indicator.h
  3. 6 2
      render/gl/backend.cpp
  4. 23 5
      render/scene_renderer.cpp

+ 154 - 6
render/geom/mode_indicator.cpp

@@ -12,10 +12,13 @@ constexpr float k_pi = 3.14159265358979323846F;
 
 namespace Render::Geom {
 
-std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_hold_mesh;
+std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_attack_mesh;
 std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_guard_mesh;
+std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_hold_mesh;
+std::unique_ptr<Render::GL::Mesh> ModeIndicator::s_patrol_mesh;
 
-auto ModeIndicator::create_hold_mode_mesh()
+// Attack mode: crossed swords (reusing existing create_hold_mode_mesh)
+auto ModeIndicator::create_attack_mode_mesh()
     -> std::unique_ptr<Render::GL::Mesh> {
   using namespace Render::GL;
   std::vector<Vertex> verts;
@@ -147,11 +150,142 @@ auto ModeIndicator::create_guard_mode_mesh()
   return std::make_unique<Mesh>(verts, idx);
 }
 
-auto ModeIndicator::get_hold_mode_mesh() -> Render::GL::Mesh * {
-  if (!s_hold_mesh) {
-    s_hold_mesh = create_hold_mode_mesh();
+// Hold mode: anchor shape
+auto ModeIndicator::create_hold_mode_mesh()
+    -> std::unique_ptr<Render::GL::Mesh> {
+  using namespace Render::GL;
+  std::vector<Vertex> verts;
+  std::vector<unsigned int> idx;
+
+  constexpr float anchor_width = 0.5F;
+  constexpr float anchor_height = 0.7F;
+  constexpr float ring_radius = 0.15F;
+  constexpr float shank_width = 0.08F;
+  constexpr float fluke_width = 0.25F;
+  constexpr float fluke_height = 0.2F;
+
+  QVector3D const n(0, 0, 1);
+
+  // Ring at top (circle)
+  constexpr int ring_segments = 12;
+  size_t const ring_center = verts.size();
+  verts.push_back({{0.0F, anchor_height * 0.7F, 0.0F},
+                   {n.x(), n.y(), n.z()},
+                   {0.5F, 0.5F}});
+
+  for (int i = 0; i <= ring_segments; ++i) {
+    float const angle = (i / float(ring_segments)) * 2.0F * k_pi;
+    float const x = ring_radius * std::cos(angle);
+    float const y = anchor_height * 0.7F + ring_radius * std::sin(angle);
+    verts.push_back({{x, y, 0.0F}, {n.x(), n.y(), n.z()}, {0.5F, 1.0F}});
   }
-  return s_hold_mesh.get();
+
+  for (int i = 0; i < ring_segments; ++i) {
+    idx.push_back(ring_center);
+    idx.push_back(ring_center + 1 + i);
+    idx.push_back(ring_center + 1 + i + 1);
+  }
+
+  // Shank (vertical bar)
+  float const shank_half = shank_width * 0.5F;
+  size_t const shank_base = verts.size();
+  verts.push_back({{-shank_half, anchor_height * 0.6F, 0.0F},
+                   {n.x(), n.y(), n.z()},
+                   {0.0F, 1.0F}});
+  verts.push_back({{shank_half, anchor_height * 0.6F, 0.0F},
+                   {n.x(), n.y(), n.z()},
+                   {1.0F, 1.0F}});
+  verts.push_back(
+      {{shank_half, -anchor_height * 0.3F, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 0.0F}});
+  verts.push_back({{-shank_half, -anchor_height * 0.3F, 0.0F},
+                   {n.x(), n.y(), n.z()},
+                   {0.0F, 0.0F}});
+
+  idx.push_back(shank_base + 0);
+  idx.push_back(shank_base + 1);
+  idx.push_back(shank_base + 2);
+  idx.push_back(shank_base + 2);
+  idx.push_back(shank_base + 3);
+  idx.push_back(shank_base + 0);
+
+  // Flukes (arms at bottom)
+  float const fluke_y = -anchor_height * 0.3F;
+
+  // Left fluke
+  size_t const left_fluke_base = verts.size();
+  verts.push_back({{-shank_half, fluke_y, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 0.5F}});
+  verts.push_back(
+      {{-fluke_width, fluke_y - fluke_height, 0.0F}, {n.x(), n.y(), n.z()}, {0.0F, 0.0F}});
+  verts.push_back({{-shank_half, fluke_y - fluke_height * 0.5F, 0.0F},
+                   {n.x(), n.y(), n.z()},
+                   {0.5F, 0.0F}});
+
+  idx.push_back(left_fluke_base + 0);
+  idx.push_back(left_fluke_base + 1);
+  idx.push_back(left_fluke_base + 2);
+
+  // Right fluke
+  size_t const right_fluke_base = verts.size();
+  verts.push_back({{shank_half, fluke_y, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 0.5F}});
+  verts.push_back(
+      {{fluke_width, fluke_y - fluke_height, 0.0F}, {n.x(), n.y(), n.z()}, {1.0F, 0.0F}});
+  verts.push_back({{shank_half, fluke_y - fluke_height * 0.5F, 0.0F},
+                   {n.x(), n.y(), n.z()},
+                   {0.5F, 0.0F}});
+
+  idx.push_back(right_fluke_base + 0);
+  idx.push_back(right_fluke_base + 1);
+  idx.push_back(right_fluke_base + 2);
+
+  return std::make_unique<Mesh>(verts, idx);
+}
+
+// Patrol mode: footsteps
+auto ModeIndicator::create_patrol_mode_mesh()
+    -> std::unique_ptr<Render::GL::Mesh> {
+  using namespace Render::GL;
+  std::vector<Vertex> verts;
+  std::vector<unsigned int> idx;
+
+  constexpr float foot_length = 0.25F;
+  constexpr float foot_width = 0.15F;
+  constexpr float step_offset = 0.2F;
+
+  QVector3D const n(0, 0, 1);
+
+  // Create two footprints offset from each other
+  for (int foot = 0; foot < 2; ++foot) {
+    float const x_offset = (foot == 0) ? -step_offset : step_offset;
+    float const y_offset = (foot == 0) ? 0.15F : -0.15F;
+
+    size_t const base = verts.size();
+
+    // Footprint shape (ellipse)
+    constexpr int segments = 8;
+    verts.push_back({{x_offset, y_offset, 0.0F}, {n.x(), n.y(), n.z()}, {0.5F, 0.5F}});
+
+    for (int i = 0; i <= segments; ++i) {
+      float const angle = (i / float(segments)) * 2.0F * k_pi;
+      float const x = x_offset + (foot_width * 0.5F) * std::cos(angle);
+      float const y = y_offset + (foot_length * 0.5F) * std::sin(angle);
+      verts.push_back({{x, y, 0.0F}, {n.x(), n.y(), n.z()}, {0.5F, 1.0F}});
+    }
+
+    for (int i = 0; i < segments; ++i) {
+      idx.push_back(base);
+      idx.push_back(base + 1 + i);
+      idx.push_back(base + 1 + i + 1);
+    }
+  }
+
+  return std::make_unique<Mesh>(verts, idx);
+}
+
+auto ModeIndicator::get_attack_mode_mesh() -> Render::GL::Mesh * {
+  if (!s_attack_mesh) {
+    s_attack_mesh = create_attack_mode_mesh();
+  }
+  return s_attack_mesh.get();
 }
 
 auto ModeIndicator::get_guard_mode_mesh() -> Render::GL::Mesh * {
@@ -161,4 +295,18 @@ auto ModeIndicator::get_guard_mode_mesh() -> Render::GL::Mesh * {
   return s_guard_mesh.get();
 }
 
+auto ModeIndicator::get_hold_mode_mesh() -> Render::GL::Mesh * {
+  if (!s_hold_mesh) {
+    s_hold_mesh = create_hold_mode_mesh();
+  }
+  return s_hold_mesh.get();
+}
+
+auto ModeIndicator::get_patrol_mode_mesh() -> Render::GL::Mesh * {
+  if (!s_patrol_mesh) {
+    s_patrol_mesh = create_patrol_mode_mesh();
+  }
+  return s_patrol_mesh.get();
+}
+
 } // namespace Render::Geom

+ 18 - 6
render/geom/mode_indicator.h

@@ -6,8 +6,11 @@
 
 namespace Render::Geom {
 
-constexpr int k_mode_type_hold = 0;
+// Mode type constants
+constexpr int k_mode_type_attack = 0;
 constexpr int k_mode_type_guard = 1;
+constexpr int k_mode_type_hold = 2;
+constexpr int k_mode_type_patrol = 3;
 
 constexpr float k_indicator_height_base = 2.0F;
 constexpr float k_indicator_size = 0.4F;
@@ -15,20 +18,29 @@ constexpr float k_indicator_alpha = 0.85F;
 constexpr float k_indicator_height_multiplier = 2.0F;
 constexpr float k_frustum_cull_margin = 1.5F;
 
-const QVector3D k_hold_mode_color(1.0F, 0.3F, 0.3F);
-const QVector3D k_guard_mode_color(0.3F, 0.5F, 1.0F);
+// Mode indicator colors
+const QVector3D k_attack_mode_color(1.0F, 0.3F, 0.3F);  // Red
+const QVector3D k_guard_mode_color(0.3F, 0.5F, 1.0F);   // Blue
+const QVector3D k_hold_mode_color(1.0F, 0.6F, 0.2F);    // Orange
+const QVector3D k_patrol_mode_color(0.5F, 0.5F, 0.5F);  // Gray
 
 class ModeIndicator {
 public:
-  static auto get_hold_mode_mesh() -> Render::GL::Mesh *;
+  static auto get_attack_mode_mesh() -> Render::GL::Mesh *;
   static auto get_guard_mode_mesh() -> Render::GL::Mesh *;
+  static auto get_hold_mode_mesh() -> Render::GL::Mesh *;
+  static auto get_patrol_mode_mesh() -> Render::GL::Mesh *;
 
 private:
-  static auto create_hold_mode_mesh() -> std::unique_ptr<Render::GL::Mesh>;
+  static auto create_attack_mode_mesh() -> std::unique_ptr<Render::GL::Mesh>;
   static auto create_guard_mode_mesh() -> std::unique_ptr<Render::GL::Mesh>;
+  static auto create_hold_mode_mesh() -> std::unique_ptr<Render::GL::Mesh>;
+  static auto create_patrol_mode_mesh() -> std::unique_ptr<Render::GL::Mesh>;
 
-  static std::unique_ptr<Render::GL::Mesh> s_hold_mesh;
+  static std::unique_ptr<Render::GL::Mesh> s_attack_mesh;
   static std::unique_ptr<Render::GL::Mesh> s_guard_mesh;
+  static std::unique_ptr<Render::GL::Mesh> s_hold_mesh;
+  static std::unique_ptr<Render::GL::Mesh> s_patrol_mesh;
 };
 
 } // namespace Render::Geom

+ 6 - 2
render/gl/backend.cpp

@@ -1604,10 +1604,14 @@ void Backend::execute(const DrawQueue &queue, const Camera &cam) {
       }
 
       Mesh *indicator_mesh = nullptr;
-      if (mc.mode_type == Render::Geom::k_mode_type_hold) {
-        indicator_mesh = Render::Geom::ModeIndicator::get_hold_mode_mesh();
+      if (mc.mode_type == Render::Geom::k_mode_type_attack) {
+        indicator_mesh = Render::Geom::ModeIndicator::get_attack_mode_mesh();
       } else if (mc.mode_type == Render::Geom::k_mode_type_guard) {
         indicator_mesh = Render::Geom::ModeIndicator::get_guard_mode_mesh();
+      } else if (mc.mode_type == Render::Geom::k_mode_type_hold) {
+        indicator_mesh = Render::Geom::ModeIndicator::get_hold_mode_mesh();
+      } else if (mc.mode_type == Render::Geom::k_mode_type_patrol) {
+        indicator_mesh = Render::Geom::ModeIndicator::get_patrol_mode_mesh();
       }
 
       if (indicator_mesh == nullptr) {

+ 23 - 5
render/scene_renderer.cpp

@@ -449,14 +449,21 @@ void Renderer::enqueue_mode_indicator(
     return;
   }
 
-  auto *hold_mode = entity->get_component<Engine::Core::HoldModeComponent>();
-  bool const has_hold_mode = (hold_mode != nullptr) && hold_mode->active;
+  // Check for all mode types
+  auto *attack_comp = entity->get_component<Engine::Core::AttackComponent>();
+  bool const has_attack = (attack_comp != nullptr) && attack_comp->in_melee_lock;
 
   auto *guard_mode = entity->get_component<Engine::Core::GuardModeComponent>();
   bool const has_guard_mode = (guard_mode != nullptr) && guard_mode->active;
 
+  auto *hold_mode = entity->get_component<Engine::Core::HoldModeComponent>();
+  bool const has_hold_mode = (hold_mode != nullptr) && hold_mode->active;
+
+  auto *patrol_comp = entity->get_component<Engine::Core::PatrolComponent>();
+  bool const has_patrol = (patrol_comp != nullptr) && patrol_comp->is_patrolling;
+
   // Only render if unit has an active mode
-  if (!has_hold_mode && !has_guard_mode) {
+  if (!has_attack && !has_guard_mode && !has_hold_mode && !has_patrol) {
     return;
   }
 
@@ -519,14 +526,25 @@ void Renderer::enqueue_mode_indicator(
     indicator_model.rotate(yaw * 180.0F / k_pi, 0, 1, 0);
   }
 
-  int mode_type = Render::Geom::k_mode_type_hold;
-  QVector3D color = Render::Geom::k_hold_mode_color;
+  // Determine mode type and color (priority: attack > guard > hold > patrol)
+  int mode_type = Render::Geom::k_mode_type_patrol;
+  QVector3D color = Render::Geom::k_patrol_mode_color;
+
+  if (has_hold_mode) {
+    mode_type = Render::Geom::k_mode_type_hold;
+    color = Render::Geom::k_hold_mode_color;
+  }
 
   if (has_guard_mode) {
     mode_type = Render::Geom::k_mode_type_guard;
     color = Render::Geom::k_guard_mode_color;
   }
 
+  if (has_attack) {
+    mode_type = Render::Geom::k_mode_type_attack;
+    color = Render::Geom::k_attack_mode_color;
+  }
+
   mode_indicator(indicator_model, mode_type, color,
                  Render::Geom::k_indicator_alpha);
 }