Browse Source

refactor: knight to swordsman, mounted to horse (global replace and rename)

Adam Djellouli 1 month ago
parent
commit
4a0c514165
70 changed files with 425 additions and 317 deletions
  1. 4 4
      CMakeLists.txt
  2. 7 7
      app/core/game_engine.cpp
  3. 10 10
      assets.qrc
  4. 0 0
      assets/audio/voices/swordsman_voice.wav
  5. 2 2
      assets/data/nations/carthage.json
  6. 2 2
      assets/data/nations/kingdom_of_iron.json
  7. 2 2
      assets/data/nations/roman_republic.json
  8. 2 2
      assets/data/troops/base.json
  9. 4 4
      assets/maps/map_forest.json
  10. 2 2
      assets/maps/map_mountain.json
  11. 2 2
      assets/maps/map_rivers.json
  12. 0 0
      assets/shaders/horse_swordsman.frag
  13. 0 0
      assets/shaders/horse_swordsman.vert
  14. 0 0
      assets/shaders/horse_swordsman_carthage.frag
  15. 0 0
      assets/shaders/horse_swordsman_kingdom_of_iron.frag
  16. 0 0
      assets/shaders/horse_swordsman_roman_republic.frag
  17. 0 0
      assets/shaders/swordsman.frag
  18. 0 0
      assets/shaders/swordsman.vert
  19. 0 0
      assets/shaders/swordsman_carthage.frag
  20. 0 0
      assets/shaders/swordsman_kingdom_of_iron.frag
  21. 0 0
      assets/shaders/swordsman_roman_republic.frag
  22. 1 1
      assets/visuals/unit_visuals.json
  23. 1 1
      game/CMakeLists.txt
  24. 1 1
      game/units/factory.cpp
  25. 1 1
      game/units/horse_swordsman.cpp
  26. 0 0
      game/units/horse_swordsman.h
  27. 5 5
      game/units/spawn_type.h
  28. 31 31
      game/units/troop_catalog.cpp
  29. 4 4
      game/units/troop_type.h
  30. 10 10
      render/CMakeLists.txt
  31. 1 1
      render/entity/arrow_vfx_renderer.cpp
  32. 2 2
      render/entity/nations/carthage/archer_renderer.cpp
  33. 17 17
      render/entity/nations/carthage/horse_swordsman_renderer.cpp
  34. 0 0
      render/entity/nations/carthage/horse_swordsman_renderer.h
  35. 2 2
      render/entity/nations/carthage/spearman_renderer.cpp
  36. 32 32
      render/entity/nations/carthage/swordsman_renderer.cpp
  37. 1 1
      render/entity/nations/carthage/swordsman_renderer.h
  38. 5 5
      render/entity/nations/carthage/swordsman_style.cpp
  39. 1 1
      render/entity/nations/carthage/swordsman_style.h
  40. 2 2
      render/entity/nations/kingdom/archer_renderer.cpp
  41. 17 17
      render/entity/nations/kingdom/horse_swordsman_renderer.cpp
  42. 0 0
      render/entity/nations/kingdom/horse_swordsman_renderer.h
  43. 2 2
      render/entity/nations/kingdom/spearman_renderer.cpp
  44. 32 32
      render/entity/nations/kingdom/swordsman_renderer.cpp
  45. 1 1
      render/entity/nations/kingdom/swordsman_renderer.h
  46. 5 5
      render/entity/nations/kingdom/swordsman_style.cpp
  47. 1 1
      render/entity/nations/kingdom/swordsman_style.h
  48. 2 2
      render/entity/nations/roman/archer_renderer.cpp
  49. 17 17
      render/entity/nations/roman/horse_swordsman_renderer.cpp
  50. 0 0
      render/entity/nations/roman/horse_swordsman_renderer.h
  51. 2 2
      render/entity/nations/roman/spearman_renderer.cpp
  52. 32 32
      render/entity/nations/roman/swordsman_renderer.cpp
  53. 1 1
      render/entity/nations/roman/swordsman_renderer.h
  54. 5 5
      render/entity/nations/roman/swordsman_style.cpp
  55. 1 1
      render/entity/nations/roman/swordsman_style.h
  56. 6 6
      render/entity/registry.cpp
  57. 2 2
      render/gl/backend.cpp
  58. 1 1
      render/gl/backend/README.md
  59. 12 12
      render/gl/backend/character_pipeline.cpp
  60. 2 2
      render/gl/backend/character_pipeline.h
  61. 7 7
      render/gl/shader_cache.h
  62. 28 0
      render/humanoid/humanoid_math.cpp
  63. 33 0
      render/humanoid/humanoid_math.h
  64. 47 0
      render/humanoid/humanoid_specs.h
  65. 1 1
      render/humanoid/rig.cpp
  66. 1 1
      render/humanoid_math.cpp
  67. 1 1
      render/palette.cpp
  68. 9 9
      ui/qml/ProductionPanel.qml
  69. 2 2
      ui/qml/StyleGuide.qml
  70. 1 1
      ui/theme.cpp

+ 4 - 4
CMakeLists.txt

@@ -174,10 +174,10 @@ if(QT_VERSION_MAJOR EQUAL 6)
             assets/shaders/grid.frag
             assets/shaders/ground_plane.frag
             assets/shaders/ground_plane.vert
-            assets/shaders/knight.frag
-            assets/shaders/knight.vert
-            assets/shaders/mounted_knight.frag
-            assets/shaders/mounted_knight.vert
+            assets/shaders/swordsman.frag
+            assets/shaders/swordsman.vert
+            assets/shaders/horse_swordsman.frag
+            assets/shaders/horse_swordsman.vert
             assets/shaders/pine_instanced.frag
             assets/shaders/pine_instanced.vert
             assets/shaders/plant_instanced.frag

+ 7 - 7
app/core/game_engine.cpp

@@ -200,8 +200,8 @@ GameEngine::GameEngine(QObject *parent)
     qInfo() << "AudioEventHandler initialized successfully";
 
     m_audioEventHandler->loadUnitVoiceMapping("archer", "archer_voice");
-    m_audioEventHandler->loadUnitVoiceMapping("knight", "knight_voice");
-    m_audioEventHandler->loadUnitVoiceMapping("swordsman", "knight_voice");
+    m_audioEventHandler->loadUnitVoiceMapping("swordsman", "swordsman_voice");
+    m_audioEventHandler->loadUnitVoiceMapping("swordsman", "swordsman_voice");
     m_audioEventHandler->loadUnitVoiceMapping("spearman", "spearman_voice");
 
     m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::PEACEFUL,
@@ -1892,13 +1892,13 @@ void GameEngine::loadAudioResources() {
                << (base_path + "voices/archer_voice.wav");
   }
 
-  if (audio_sys.loadSound("knight_voice",
-                          (base_path + "voices/knight_voice.wav").toStdString(),
+  if (audio_sys.loadSound("swordsman_voice",
+                          (base_path + "voices/swordsman_voice.wav").toStdString(),
                           AudioCategory::VOICE)) {
-    qInfo() << "Loaded knight voice";
+    qInfo() << "Loaded swordsman voice";
   } else {
-    qWarning() << "Failed to load knight voice from:"
-               << (base_path + "voices/knight_voice.wav");
+    qWarning() << "Failed to load swordsman voice from:"
+               << (base_path + "voices/swordsman_voice.wav");
   }
 
   if (audio_sys.loadSound(

+ 10 - 10
assets.qrc

@@ -21,16 +21,16 @@
         <file>assets/shaders/grid.frag</file>
         <file>assets/shaders/ground_plane.frag</file>
         <file>assets/shaders/ground_plane.vert</file>
-        <file>assets/shaders/knight.frag</file>
-        <file>assets/shaders/knight.vert</file>
-        <file>assets/shaders/knight_kingdom_of_iron.frag</file>
-        <file>assets/shaders/knight_roman_republic.frag</file>
-        <file>assets/shaders/knight_carthage.frag</file>
-        <file>assets/shaders/mounted_knight.frag</file>
-        <file>assets/shaders/mounted_knight.vert</file>
-        <file>assets/shaders/mounted_knight_kingdom_of_iron.frag</file>
-        <file>assets/shaders/mounted_knight_roman_republic.frag</file>
-        <file>assets/shaders/mounted_knight_carthage.frag</file>
+        <file>assets/shaders/swordsman.frag</file>
+        <file>assets/shaders/swordsman.vert</file>
+        <file>assets/shaders/swordsman_kingdom_of_iron.frag</file>
+        <file>assets/shaders/swordsman_roman_republic.frag</file>
+        <file>assets/shaders/swordsman_carthage.frag</file>
+        <file>assets/shaders/horse_swordsman.frag</file>
+        <file>assets/shaders/horse_swordsman.vert</file>
+        <file>assets/shaders/horse_swordsman_kingdom_of_iron.frag</file>
+        <file>assets/shaders/horse_swordsman_roman_republic.frag</file>
+        <file>assets/shaders/horse_swordsman_carthage.frag</file>
         <file>assets/shaders/pine_instanced.frag</file>
         <file>assets/shaders/pine_instanced.vert</file>
         <file>assets/shaders/plant_instanced.frag</file>

+ 0 - 0
assets/audio/voices/knight_voice.wav → assets/audio/voices/swordsman_voice.wav


+ 2 - 2
assets/data/nations/carthage.json

@@ -110,7 +110,7 @@
       }
     },
     {
-      "id": "mounted_knight",
+      "id": "horse_swordsman",
       "display_name": "Numidian Cavalry",
       "production": {
         "cost": 150,
@@ -137,7 +137,7 @@
         "selection_ring_size": 2.1,
         "selection_ring_y_offset": 0.0,
         "selection_ring_ground_offset": 1.35,
-        "renderer_id": "troops/carthage/mounted_knight"
+        "renderer_id": "troops/carthage/horse_swordsman"
       },
       "formation": {
         "individuals_per_unit": 9,

+ 2 - 2
assets/data/nations/kingdom_of_iron.json

@@ -110,7 +110,7 @@
       }
     },
     {
-      "id": "mounted_knight",
+      "id": "horse_swordsman",
       "display_name": "Mounted Knight",
       "production": {
         "cost": 150,
@@ -137,7 +137,7 @@
         "selection_ring_size": 2.0,
         "selection_ring_y_offset": 0.0,
         "selection_ring_ground_offset": 1.35,
-        "renderer_id": "troops/kingdom/mounted_knight"
+        "renderer_id": "troops/kingdom/horse_swordsman"
       },
       "formation": {
         "individuals_per_unit": 9,

+ 2 - 2
assets/data/nations/roman_republic.json

@@ -110,7 +110,7 @@
       }
     },
     {
-      "id": "mounted_knight",
+      "id": "horse_swordsman",
       "display_name": "Equites",
       "production": {
         "cost": 165,
@@ -137,7 +137,7 @@
         "selection_ring_size": 2.05,
         "selection_ring_y_offset": 0.0,
         "selection_ring_ground_offset": 1.35,
-        "renderer_id": "troops/roman/mounted_knight"
+        "renderer_id": "troops/roman/horse_swordsman"
       },
       "formation": {
         "individuals_per_unit": 9,

+ 2 - 2
assets/data/troops/base.json

@@ -106,7 +106,7 @@
       }
     },
     {
-      "id": "mounted_knight",
+      "id": "horse_swordsman",
       "display_name": "Mounted Knight",
       "production": {
         "cost": 150,
@@ -133,7 +133,7 @@
         "selection_ring_size": 2.0,
         "selection_ring_y_offset": 0.0,
         "selection_ring_ground_offset": 1.35,
-        "renderer_id": "troops/kingdom/mounted_knight"
+        "renderer_id": "troops/kingdom/horse_swordsman"
       },
       "formation": {
         "individuals_per_unit": 9,

+ 4 - 4
assets/maps/map_forest.json

@@ -57,8 +57,8 @@
     },
     { "type": "archer", "x": 188, "z": 192, "playerId": 2 },
     { "type": "archer", "x": 192, "z": 188, "playerId": 2 },
-    { "type": "mounted_knight", "x": 185, "z": 190, "playerId": 2 },
-    { "type": "mounted_knight", "x": 190, "z": 185, "playerId": 2 },
+    { "type": "horse_swordsman", "x": 185, "z": 190, "playerId": 2 },
+    { "type": "horse_swordsman", "x": 190, "z": 185, "playerId": 2 },
     { "type": "spearman", "x": 188, "z": 188, "playerId": 2 },
     { "type": "spearman", "x": 192, "z": 192, "playerId": 2 },
     {
@@ -84,8 +84,8 @@
       "playerId": 4,
       "maxPopulation": 150
     },
-    { "type": "mounted_knight", "x": 188, "z": 62, "playerId": 4 },
-    { "type": "mounted_knight", "x": 192, "z": 58, "playerId": 4 },
+    { "type": "horse_swordsman", "x": 188, "z": 62, "playerId": 4 },
+    { "type": "horse_swordsman", "x": 192, "z": 58, "playerId": 4 },
     { "type": "spearman", "x": 190, "z": 55, "playerId": 4 }
   ],
   "firecamps": [

+ 2 - 2
assets/maps/map_mountain.json

@@ -58,8 +58,8 @@
     },
     { "type": "archer", "x": 223, "z": 227, "playerId": 2 },
     { "type": "archer", "x": 227, "z": 223, "playerId": 2 },
-    { "type": "mounted_knight", "x": 221, "z": 225, "playerId": 2 },
-    { "type": "mounted_knight", "x": 225, "z": 221, "playerId": 2 },
+    { "type": "horse_swordsman", "x": 221, "z": 225, "playerId": 2 },
+    { "type": "horse_swordsman", "x": 225, "z": 221, "playerId": 2 },
     { "type": "spearman", "x": 223, "z": 223, "playerId": 2 },
     { "type": "spearman", "x": 227, "z": 227, "playerId": 2 },
     {

+ 2 - 2
assets/maps/map_rivers.json

@@ -44,7 +44,7 @@
     },
     { "type": "archer", "x": 28, "z": 32, "playerId": 1 },
     { "type": "archer", "x": 32, "z": 28, "playerId": 1 },
-    { "type": "mounted_knight", "x": 30, "z": 35, "playerId": 1 },
+    { "type": "horse_swordsman", "x": 30, "z": 35, "playerId": 1 },
     { "type": "spearman", "x": 35, "z": 30, "playerId": 1 },
     {
       "type": "barracks",
@@ -55,7 +55,7 @@
     },
     { "type": "archer", "x": 88, "z": 92, "playerId": 2 },
     { "type": "archer", "x": 92, "z": 88, "playerId": 2 },
-    { "type": "mounted_knight", "x": 90, "z": 85, "playerId": 2 },
+    { "type": "horse_swordsman", "x": 90, "z": 85, "playerId": 2 },
     { "type": "spearman", "x": 85, "z": 90, "playerId": 2 },
     {
       "type": "barracks",

+ 0 - 0
assets/shaders/mounted_knight.frag → assets/shaders/horse_swordsman.frag


+ 0 - 0
assets/shaders/knight.vert → assets/shaders/horse_swordsman.vert


+ 0 - 0
assets/shaders/mounted_knight_carthage.frag → assets/shaders/horse_swordsman_carthage.frag


+ 0 - 0
assets/shaders/mounted_knight_kingdom_of_iron.frag → assets/shaders/horse_swordsman_kingdom_of_iron.frag


+ 0 - 0
assets/shaders/mounted_knight_roman_republic.frag → assets/shaders/horse_swordsman_roman_republic.frag


+ 0 - 0
assets/shaders/knight.frag → assets/shaders/swordsman.frag


+ 0 - 0
assets/shaders/mounted_knight.vert → assets/shaders/swordsman.vert


+ 0 - 0
assets/shaders/knight_carthage.frag → assets/shaders/swordsman_carthage.frag


+ 0 - 0
assets/shaders/knight_kingdom_of_iron.frag → assets/shaders/swordsman_kingdom_of_iron.frag


+ 0 - 0
assets/shaders/knight_roman_republic.frag → assets/shaders/swordsman_roman_republic.frag


+ 1 - 1
assets/visuals/unit_visuals.json

@@ -17,7 +17,7 @@
       "texture": "",
       "equipment": "spear_and_shield"
     },
-    "mounted_knight": {
+    "horse_swordsman": {
       "mesh": "Capsule",
       "color": [0.75, 0.75, 0.85],
       "texture": "",

+ 1 - 1
game/CMakeLists.txt

@@ -67,7 +67,7 @@ add_library(game_systems STATIC
     units/unit.cpp
     units/archer.cpp
     units/swordsman.cpp
-    units/mounted_knight.cpp
+    units/horse_swordsman.cpp
     units/spearman.cpp
     units/troop_catalog.cpp
     units/troop_catalog_loader.cpp

+ 1 - 1
game/units/factory.cpp

@@ -1,7 +1,7 @@
 #include "factory.h"
 #include "archer.h"
 #include "barracks.h"
-#include "mounted_knight.h"
+#include "horse_swordsman.h"
 #include "spearman.h"
 #include "swordsman.h"
 #include "units/spawn_type.h"

+ 1 - 1
game/units/mounted_knight.cpp → game/units/horse_swordsman.cpp

@@ -1,4 +1,4 @@
-#include "mounted_knight.h"
+#include "horse_swordsman.h"
 #include "../core/component.h"
 #include "../core/event_manager.h"
 #include "../core/world.h"

+ 0 - 0
game/units/mounted_knight.h → game/units/horse_swordsman.h


+ 5 - 5
game/units/spawn_type.h

@@ -26,7 +26,7 @@ inline auto spawn_typeToQString(SpawnType type) -> QString {
   case SpawnType::Spearman:
     return QStringLiteral("spearman");
   case SpawnType::MountedKnight:
-    return QStringLiteral("mounted_knight");
+    return QStringLiteral("horse_swordsman");
   case SpawnType::Barracks:
     return QStringLiteral("barracks");
   }
@@ -44,7 +44,7 @@ inline auto tryParseSpawnType(const QString &value, SpawnType &out) -> bool {
     return true;
   }
   if (lowered == QStringLiteral("swordsman") ||
-      lowered == QStringLiteral("knight")) {
+      lowered == QStringLiteral("swordsman")) {
     out = SpawnType::Knight;
     return true;
   }
@@ -52,7 +52,7 @@ inline auto tryParseSpawnType(const QString &value, SpawnType &out) -> bool {
     out = SpawnType::Spearman;
     return true;
   }
-  if (lowered == QStringLiteral("mounted_knight")) {
+  if (lowered == QStringLiteral("horse_swordsman")) {
     out = SpawnType::MountedKnight;
     return true;
   }
@@ -68,13 +68,13 @@ spawn_typeFromString(const std::string &str) -> std::optional<SpawnType> {
   if (str == "archer") {
     return SpawnType::Archer;
   }
-  if (str == "swordsman" || str == "knight") {
+  if (str == "swordsman" || str == "swordsman") {
     return SpawnType::Knight;
   }
   if (str == "spearman") {
     return SpawnType::Spearman;
   }
-  if (str == "mounted_knight") {
+  if (str == "horse_swordsman") {
     return SpawnType::MountedKnight;
   }
   if (str == "barracks") {

+ 31 - 31
game/units/troop_catalog.cpp

@@ -135,37 +135,37 @@ void TroopCatalog::register_defaults() {
 
   register_class(std::move(spearman));
 
-  TroopClass mounted_knight{};
-  mounted_knight.unit_type = Game::Units::TroopType::MountedKnight;
-  mounted_knight.display_name = "Mounted Knight";
-  mounted_knight.production.cost = 150;
-  mounted_knight.production.build_time = 10.0F;
-  mounted_knight.production.priority = 15;
-  mounted_knight.production.is_melee = true;
-
-  mounted_knight.combat.health = 200;
-  mounted_knight.combat.max_health = 200;
-  mounted_knight.combat.speed = 8.0F;
-  mounted_knight.combat.vision_range = 16.0F;
-  mounted_knight.combat.ranged_range = 1.5F;
-  mounted_knight.combat.ranged_damage = 5;
-  mounted_knight.combat.ranged_cooldown = 2.0F;
-  mounted_knight.combat.melee_range = 2.0F;
-  mounted_knight.combat.melee_damage = 25;
-  mounted_knight.combat.melee_cooldown = 0.8F;
-  mounted_knight.combat.can_ranged = false;
-  mounted_knight.combat.can_melee = true;
-
-  mounted_knight.visuals.render_scale = 0.8F;
-  mounted_knight.visuals.selection_ring_size = 2.0F;
-  mounted_knight.visuals.selection_ring_ground_offset = 1.35F;
-  mounted_knight.visuals.selection_ring_y_offset = 0.0F;
-  mounted_knight.visuals.renderer_id = "troops/kingdom/mounted_knight";
-
-  mounted_knight.individuals_per_unit = 9;
-  mounted_knight.max_units_per_row = 3;
-
-  register_class(std::move(mounted_knight));
+  TroopClass horse_swordsman{};
+  horse_swordsman.unit_type = Game::Units::TroopType::MountedKnight;
+  horse_swordsman.display_name = "Mounted Knight";
+  horse_swordsman.production.cost = 150;
+  horse_swordsman.production.build_time = 10.0F;
+  horse_swordsman.production.priority = 15;
+  horse_swordsman.production.is_melee = true;
+
+  horse_swordsman.combat.health = 200;
+  horse_swordsman.combat.max_health = 200;
+  horse_swordsman.combat.speed = 8.0F;
+  horse_swordsman.combat.vision_range = 16.0F;
+  horse_swordsman.combat.ranged_range = 1.5F;
+  horse_swordsman.combat.ranged_damage = 5;
+  horse_swordsman.combat.ranged_cooldown = 2.0F;
+  horse_swordsman.combat.melee_range = 2.0F;
+  horse_swordsman.combat.melee_damage = 25;
+  horse_swordsman.combat.melee_cooldown = 0.8F;
+  horse_swordsman.combat.can_ranged = false;
+  horse_swordsman.combat.can_melee = true;
+
+  horse_swordsman.visuals.render_scale = 0.8F;
+  horse_swordsman.visuals.selection_ring_size = 2.0F;
+  horse_swordsman.visuals.selection_ring_ground_offset = 1.35F;
+  horse_swordsman.visuals.selection_ring_y_offset = 0.0F;
+  horse_swordsman.visuals.renderer_id = "troops/kingdom/horse_swordsman";
+
+  horse_swordsman.individuals_per_unit = 9;
+  horse_swordsman.max_units_per_row = 3;
+
+  register_class(std::move(horse_swordsman));
 }
 
 } // namespace Game::Units

+ 4 - 4
game/units/troop_type.h

@@ -20,7 +20,7 @@ inline auto troop_typeToQString(TroopType type) -> QString {
   case TroopType::Spearman:
     return QStringLiteral("spearman");
   case TroopType::MountedKnight:
-    return QStringLiteral("mounted_knight");
+    return QStringLiteral("horse_swordsman");
   }
   return QStringLiteral("archer");
 }
@@ -36,7 +36,7 @@ inline auto tryParseTroopType(const QString &value, TroopType &out) -> bool {
     return true;
   }
   if (lowered == QStringLiteral("swordsman") ||
-      lowered == QStringLiteral("knight")) {
+      lowered == QStringLiteral("swordsman")) {
     out = TroopType::Swordsman;
     return true;
   }
@@ -44,8 +44,8 @@ inline auto tryParseTroopType(const QString &value, TroopType &out) -> bool {
     out = TroopType::Spearman;
     return true;
   }
-  if (lowered == QStringLiteral("mounted_knight") ||
-      lowered == QStringLiteral("mountedknight")) {
+  if (lowered == QStringLiteral("horse_swordsman") ||
+      lowered == QStringLiteral("horseswordsman")) {
     out = TroopType::MountedKnight;
     return true;
   }

+ 10 - 10
render/CMakeLists.txt

@@ -38,21 +38,21 @@ add_library(render_gl STATIC
     entity/nations/roman/archer_style.cpp
     entity/nations/carthage/archer_style.cpp
     entity/nations/kingdom/spearman_style.cpp
-    entity/nations/roman/knight_renderer.cpp
-    entity/nations/kingdom/knight_renderer.cpp
-    entity/nations/carthage/knight_renderer.cpp
-    entity/nations/roman/knight_style.cpp
-    entity/nations/carthage/knight_style.cpp
-    entity/nations/kingdom/knight_style.cpp
+    entity/nations/roman/swordsman_renderer.cpp
+    entity/nations/kingdom/swordsman_renderer.cpp
+    entity/nations/carthage/swordsman_renderer.cpp
+    entity/nations/roman/swordsman_style.cpp
+    entity/nations/carthage/swordsman_style.cpp
+    entity/nations/kingdom/swordsman_style.cpp
     entity/nations/roman/spearman_renderer.cpp
     entity/nations/kingdom/spearman_renderer.cpp
     entity/nations/carthage/spearman_renderer.cpp
     entity/nations/roman/spearman_style.cpp
     entity/nations/carthage/spearman_style.cpp
     horse/rig.cpp
-    entity/nations/roman/mounted_knight_renderer.cpp
-    entity/nations/kingdom/mounted_knight_renderer.cpp
-    entity/nations/carthage/mounted_knight_renderer.cpp
+    entity/nations/roman/horse_swordsman_renderer.cpp
+    entity/nations/kingdom/horse_swordsman_renderer.cpp
+    entity/nations/carthage/horse_swordsman_renderer.cpp
     entity/barracks_renderer.cpp
     # entity/arrow.cpp removed; arrow VFX renderer code moved to geom/arrow.cpp
     geom/selection_ring.cpp
@@ -61,7 +61,7 @@ add_library(render_gl STATIC
     geom/flag.cpp
     geom/patrol_flags.cpp
     geom/transforms.cpp
-    humanoid_math.cpp
+    humanoid/humanoid_math.cpp
     palette.cpp
     humanoid/rig.cpp
     humanoid/style_palette.cpp

+ 1 - 1
render/entity/arrow_vfx_renderer.cpp

@@ -8,7 +8,7 @@
 #include "../gl/mesh.h"
 #include "../gl/primitives.h"
 #include "../gl/texture.h"
-#include "../humanoid_math.h"
+#include "humanoid/humanoid_math.h"
 #include "registry.h"
 
 #include <QMatrix4x4>

+ 2 - 2
render/entity/nations/carthage/archer_renderer.cpp

@@ -10,8 +10,8 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"

+ 17 - 17
render/entity/nations/carthage/mounted_knight_renderer.cpp → render/entity/nations/carthage/horse_swordsman_renderer.cpp

@@ -1,4 +1,4 @@
-#include "mounted_knight_renderer.h"
+#include "horse_swordsman_renderer.h"
 #include "../../../../game/core/component.h"
 #include "../../../../game/core/entity.h"
 #include "../../../../game/systems/nation_id.h"
@@ -8,8 +8,8 @@
 #include "../../../gl/primitives.h"
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"
@@ -73,9 +73,9 @@ public:
       }
     }
     if (!nation.empty()) {
-      return QString::fromStdString(std::string("mounted_knight_") + nation);
+      return QString::fromStdString(std::string("horse_swordsman_") + nation);
     }
-    return QStringLiteral("mounted_knight");
+    return QStringLiteral("horse_swordsman");
   }
 
   void customizePose(const DrawContext &ctx,
@@ -731,9 +731,9 @@ private:
     const float scale_factor = 2.0F;
     const float r = 0.15F * scale_factor;
 
-    constexpr float k_mounted_shield_yaw_degrees = -70.0F;
+    constexpr float k_horse_shield_yaw_degrees = -70.0F;
     QMatrix4x4 rot;
-    rot.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+    rot.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
 
     const QVector3D n = rot.map(QVector3D(0.0F, 0.0F, 1.0F));
     const QVector3D axis_x = rot.map(QVector3D(1.0F, 0.0F, 0.0F));
@@ -748,7 +748,7 @@ private:
     {
       QMatrix4x4 m = ctx.model;
       m.translate(shield_center + n * plate_half);
-      m.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+      m.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
       m.scale(r, r, plate_full);
       out.mesh(getUnitCylinder(), m, v.palette.cloth * 1.15F, nullptr, 1.0F);
     }
@@ -756,7 +756,7 @@ private:
     {
       QMatrix4x4 m = ctx.model;
       m.translate(shield_center - n * plate_half);
-      m.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+      m.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
       m.scale(r * 0.985F, r * 0.985F, plate_full);
       out.mesh(getUnitCylinder(), m, v.palette.leather * 0.8F, nullptr, 1.0F);
     }
@@ -782,21 +782,21 @@ void registerMountedKnightRenderer(
     Render::GL::EntityRendererRegistry &registry) {
   static MountedKnightRenderer const renderer;
   registry.registerRenderer(
-      "troops/carthage/mounted_knight",
+      "troops/carthage/horse_swordsman",
       [](const DrawContext &ctx, ISubmitter &out) {
         static MountedKnightRenderer const static_renderer;
-        Shader *mounted_knight_shader = nullptr;
+        Shader *horse_swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
           QString shader_key = static_renderer.resolve_shader_key(ctx);
-          mounted_knight_shader = ctx.backend->shader(shader_key);
-          if (mounted_knight_shader == nullptr) {
-            mounted_knight_shader =
-                ctx.backend->shader(QStringLiteral("mounted_knight"));
+          horse_swordsman_shader = ctx.backend->shader(shader_key);
+          if (horse_swordsman_shader == nullptr) {
+            horse_swordsman_shader =
+                ctx.backend->shader(QStringLiteral("horse_swordsman"));
           }
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (mounted_knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(mounted_knight_shader);
+        if ((scene_renderer != nullptr) && (horse_swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(horse_swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {

+ 0 - 0
render/entity/nations/carthage/mounted_knight_renderer.h → render/entity/nations/carthage/horse_swordsman_renderer.h


+ 2 - 2
render/entity/nations/carthage/spearman_renderer.cpp

@@ -8,8 +8,8 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"

+ 32 - 32
render/entity/nations/carthage/knight_renderer.cpp → render/entity/nations/carthage/swordsman_renderer.cpp

@@ -1,4 +1,4 @@
-#include "knight_renderer.h"
+#include "swordsman_renderer.h"
 #include "../../../../game/core/component.h"
 #include "../../../../game/systems/nation_id.h"
 #include "../../../geom/math_utils.h"
@@ -8,14 +8,14 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"
 #include "../../registry.h"
 #include "../../renderer_constants.h"
-#include "knight_style.h"
+#include "swordsman_style.h"
 #include <numbers>
 #include <qmatrix4x4.h>
 #include <qstringliteral.h>
@@ -36,19 +36,19 @@ namespace Render::GL::Carthage {
 
 namespace {
 
-constexpr std::string_view k_knight_default_style_key = "default";
-constexpr float k_knight_team_mix_weight = 0.6F;
-constexpr float k_knight_style_mix_weight = 0.4F;
+constexpr std::string_view k_swordsman_default_style_key = "default";
+constexpr float k_swordsman_team_mix_weight = 0.6F;
+constexpr float k_swordsman_style_mix_weight = 0.4F;
 
-auto knight_style_registry()
+auto swordsman_style_registry()
     -> std::unordered_map<std::string, KnightStyleConfig> & {
   static std::unordered_map<std::string, KnightStyleConfig> styles;
   return styles;
 }
 
-void ensure_knight_styles_registered() {
+void ensure_swordsman_styles_registered() {
   static const bool registered = []() {
-    register_carthage_knight_style();
+    register_carthage_swordsman_style();
     return true;
   }();
   (void)registered;
@@ -56,9 +56,9 @@ void ensure_knight_styles_registered() {
 
 } // namespace
 
-void register_knight_style(const std::string &nation_id,
+void register_swordsman_style(const std::string &nation_id,
                            const KnightStyleConfig &style) {
-  knight_style_registry()[nation_id] = style;
+  swordsman_style_registry()[nation_id] = style;
 }
 
 using Render::Geom::clamp01;
@@ -852,8 +852,8 @@ private:
 
   auto
   resolve_style(const DrawContext &ctx) const -> const KnightStyleConfig & {
-    ensure_knight_styles_registered();
-    auto &styles = knight_style_registry();
+    ensure_swordsman_styles_registered();
+    auto &styles = swordsman_style_registry();
     std::string nation_id;
     if (ctx.entity != nullptr) {
       if (auto *unit =
@@ -867,7 +867,7 @@ private:
         return it->second;
       }
     }
-    auto it_default = styles.find(std::string(k_knight_default_style_key));
+    auto it_default = styles.find(std::string(k_swordsman_default_style_key));
     if (it_default != styles.end()) {
       return it_default->second;
     }
@@ -881,7 +881,7 @@ public:
     if (!style.shader_id.empty()) {
       return QString::fromStdString(style.shader_id);
     }
-    return QStringLiteral("knight");
+    return QStringLiteral("swordsman");
   }
 
 private:
@@ -891,8 +891,8 @@ private:
     auto apply_color = [&](const std::optional<QVector3D> &override_color,
                            QVector3D &target) {
       target = mix_palette_color(target, override_color, team_tint,
-                                 k_knight_team_mix_weight,
-                                 k_knight_style_mix_weight);
+                                 k_swordsman_team_mix_weight,
+                                 k_swordsman_style_mix_weight);
     };
 
     apply_color(style.cloth_color, variant.palette.cloth);
@@ -912,8 +912,8 @@ private:
     auto apply_shield_color =
         [&](const std::optional<QVector3D> &override_color, QVector3D &target) {
           target = mix_palette_color(target, override_color, team_tint,
-                                     k_knight_team_mix_weight,
-                                     k_knight_style_mix_weight);
+                                     k_swordsman_team_mix_weight,
+                                     k_swordsman_style_mix_weight);
         };
 
     apply_shield_color(style.shield_color, extras.shieldColor);
@@ -936,22 +936,22 @@ private:
 };
 
 void registerKnightRenderer(Render::GL::EntityRendererRegistry &registry) {
-  ensure_knight_styles_registered();
+  ensure_swordsman_styles_registered();
   static KnightRenderer const renderer;
   registry.registerRenderer(
       "troops/carthage/swordsman", [](const DrawContext &ctx, ISubmitter &out) {
         static KnightRenderer const static_renderer;
-        Shader *knight_shader = nullptr;
+        Shader *swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
           QString shader_key = static_renderer.resolve_shader_key(ctx);
-          knight_shader = ctx.backend->shader(shader_key);
-          if (knight_shader == nullptr) {
-            knight_shader = ctx.backend->shader(QStringLiteral("knight"));
+          swordsman_shader = ctx.backend->shader(shader_key);
+          if (swordsman_shader == nullptr) {
+            swordsman_shader = ctx.backend->shader(QStringLiteral("swordsman"));
           }
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(knight_shader);
+        if ((scene_renderer != nullptr) && (swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {
@@ -959,15 +959,15 @@ void registerKnightRenderer(Render::GL::EntityRendererRegistry &registry) {
         }
       });
   registry.registerRenderer(
-      "troops/carthage/knight", [](const DrawContext &ctx, ISubmitter &out) {
+      "troops/carthage/swordsman", [](const DrawContext &ctx, ISubmitter &out) {
         static KnightRenderer const static_renderer;
-        Shader *knight_shader = nullptr;
+        Shader *swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
-          knight_shader = ctx.backend->shader(QStringLiteral("knight"));
+          swordsman_shader = ctx.backend->shader(QStringLiteral("swordsman"));
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(knight_shader);
+        if ((scene_renderer != nullptr) && (swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {

+ 1 - 1
render/entity/nations/carthage/knight_renderer.h → render/entity/nations/carthage/swordsman_renderer.h

@@ -7,7 +7,7 @@ namespace Render::GL::Carthage {
 
 struct KnightStyleConfig;
 
-void register_knight_style(const std::string &nation_id,
+void register_swordsman_style(const std::string &nation_id,
                            const KnightStyleConfig &style);
 
 void registerKnightRenderer(EntityRendererRegistry &registry);

+ 5 - 5
render/entity/nations/carthage/knight_style.cpp → render/entity/nations/carthage/swordsman_style.cpp

@@ -1,5 +1,5 @@
-#include "knight_style.h"
-#include "knight_renderer.h"
+#include "swordsman_style.h"
+#include "swordsman_renderer.h"
 
 #include <QVector3D>
 
@@ -14,7 +14,7 @@ constexpr QVector3D k_carthage_trim{0.76F, 0.68F, 0.42F};
 
 namespace Render::GL::Carthage {
 
-void register_carthage_knight_style() {
+void register_carthage_swordsman_style() {
   KnightStyleConfig style;
   style.cloth_color = k_carthage_cloth;
   style.leather_color = k_carthage_leather;
@@ -26,9 +26,9 @@ void register_carthage_knight_style() {
   style.shield_aspect_ratio = 0.85F;
   style.has_scabbard = false;
   style.shield_cross_decal = false;
-  style.shader_id = "knight_carthage";
+  style.shader_id = "swordsman_carthage";
 
-  register_knight_style("carthage", style);
+  register_swordsman_style("carthage", style);
 }
 
 } // namespace Render::GL::Carthage

+ 1 - 1
render/entity/nations/carthage/knight_style.h → render/entity/nations/carthage/swordsman_style.h

@@ -22,6 +22,6 @@ struct KnightStyleConfig {
   std::string shader_id;
 };
 
-void register_carthage_knight_style();
+void register_carthage_swordsman_style();
 
 } // namespace Render::GL::Carthage

+ 2 - 2
render/entity/nations/kingdom/archer_renderer.cpp

@@ -10,8 +10,8 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"

+ 17 - 17
render/entity/nations/kingdom/mounted_knight_renderer.cpp → render/entity/nations/kingdom/horse_swordsman_renderer.cpp

@@ -1,4 +1,4 @@
-#include "mounted_knight_renderer.h"
+#include "horse_swordsman_renderer.h"
 #include "../../../../game/core/component.h"
 #include "../../../../game/core/entity.h"
 #include "../../../../game/systems/nation_id.h"
@@ -8,8 +8,8 @@
 #include "../../../gl/primitives.h"
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"
@@ -73,9 +73,9 @@ public:
       }
     }
     if (!nation.empty()) {
-      return QString::fromStdString(std::string("mounted_knight_") + nation);
+      return QString::fromStdString(std::string("horse_swordsman_") + nation);
     }
-    return QStringLiteral("mounted_knight");
+    return QStringLiteral("horse_swordsman");
   }
 
   void customizePose(const DrawContext &ctx,
@@ -731,9 +731,9 @@ private:
     const float scale_factor = 2.0F;
     const float r = 0.15F * scale_factor;
 
-    constexpr float k_mounted_shield_yaw_degrees = -70.0F;
+    constexpr float k_horse_shield_yaw_degrees = -70.0F;
     QMatrix4x4 rot;
-    rot.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+    rot.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
 
     const QVector3D n = rot.map(QVector3D(0.0F, 0.0F, 1.0F));
     const QVector3D axis_x = rot.map(QVector3D(1.0F, 0.0F, 0.0F));
@@ -748,7 +748,7 @@ private:
     {
       QMatrix4x4 m = ctx.model;
       m.translate(shield_center + n * plate_half);
-      m.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+      m.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
       m.scale(r, r, plate_full);
       out.mesh(getUnitCylinder(), m, v.palette.cloth * 1.15F, nullptr, 1.0F);
     }
@@ -756,7 +756,7 @@ private:
     {
       QMatrix4x4 m = ctx.model;
       m.translate(shield_center - n * plate_half);
-      m.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+      m.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
       m.scale(r * 0.985F, r * 0.985F, plate_full);
       out.mesh(getUnitCylinder(), m, v.palette.leather * 0.8F, nullptr, 1.0F);
     }
@@ -782,21 +782,21 @@ void registerMountedKnightRenderer(
     Render::GL::EntityRendererRegistry &registry) {
   static MountedKnightRenderer const renderer;
   registry.registerRenderer(
-      "troops/kingdom/mounted_knight",
+      "troops/kingdom/horse_swordsman",
       [](const DrawContext &ctx, ISubmitter &out) {
         static MountedKnightRenderer const static_renderer;
-        Shader *mounted_knight_shader = nullptr;
+        Shader *horse_swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
           QString shader_key = static_renderer.resolve_shader_key(ctx);
-          mounted_knight_shader = ctx.backend->shader(shader_key);
-          if (mounted_knight_shader == nullptr) {
-            mounted_knight_shader =
-                ctx.backend->shader(QStringLiteral("mounted_knight"));
+          horse_swordsman_shader = ctx.backend->shader(shader_key);
+          if (horse_swordsman_shader == nullptr) {
+            horse_swordsman_shader =
+                ctx.backend->shader(QStringLiteral("horse_swordsman"));
           }
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (mounted_knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(mounted_knight_shader);
+        if ((scene_renderer != nullptr) && (horse_swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(horse_swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {

+ 0 - 0
render/entity/nations/kingdom/mounted_knight_renderer.h → render/entity/nations/kingdom/horse_swordsman_renderer.h


+ 2 - 2
render/entity/nations/kingdom/spearman_renderer.cpp

@@ -8,8 +8,8 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"

+ 32 - 32
render/entity/nations/kingdom/knight_renderer.cpp → render/entity/nations/kingdom/swordsman_renderer.cpp

@@ -1,4 +1,4 @@
-#include "knight_renderer.h"
+#include "swordsman_renderer.h"
 #include "../../../../game/core/component.h"
 #include "../../../../game/systems/nation_id.h"
 #include "../../../geom/math_utils.h"
@@ -8,14 +8,14 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"
 #include "../../registry.h"
 #include "../../renderer_constants.h"
-#include "knight_style.h"
+#include "swordsman_style.h"
 #include <numbers>
 #include <qmatrix4x4.h>
 #include <qstringliteral.h>
@@ -36,19 +36,19 @@ namespace Render::GL::Kingdom {
 
 namespace {
 
-constexpr std::string_view k_knight_default_style_key = "default";
-constexpr float k_knight_team_mix_weight = 0.6F;
-constexpr float k_knight_style_mix_weight = 0.4F;
+constexpr std::string_view k_swordsman_default_style_key = "default";
+constexpr float k_swordsman_team_mix_weight = 0.6F;
+constexpr float k_swordsman_style_mix_weight = 0.4F;
 
-auto knight_style_registry()
+auto swordsman_style_registry()
     -> std::unordered_map<std::string, KnightStyleConfig> & {
   static std::unordered_map<std::string, KnightStyleConfig> styles;
   return styles;
 }
 
-void ensure_knight_styles_registered() {
+void ensure_swordsman_styles_registered() {
   static const bool registered = []() {
-    register_kingdom_knight_style();
+    register_kingdom_swordsman_style();
     return true;
   }();
   (void)registered;
@@ -56,9 +56,9 @@ void ensure_knight_styles_registered() {
 
 } // namespace
 
-void register_knight_style(const std::string &nation_id,
+void register_swordsman_style(const std::string &nation_id,
                            const KnightStyleConfig &style) {
-  knight_style_registry()[nation_id] = style;
+  swordsman_style_registry()[nation_id] = style;
 }
 
 using Render::Geom::clamp01;
@@ -852,8 +852,8 @@ private:
 
   auto
   resolve_style(const DrawContext &ctx) const -> const KnightStyleConfig & {
-    ensure_knight_styles_registered();
-    auto &styles = knight_style_registry();
+    ensure_swordsman_styles_registered();
+    auto &styles = swordsman_style_registry();
     std::string nation_id;
     if (ctx.entity != nullptr) {
       if (auto *unit =
@@ -867,7 +867,7 @@ private:
         return it->second;
       }
     }
-    auto it_default = styles.find(std::string(k_knight_default_style_key));
+    auto it_default = styles.find(std::string(k_swordsman_default_style_key));
     if (it_default != styles.end()) {
       return it_default->second;
     }
@@ -881,7 +881,7 @@ public:
     if (!style.shader_id.empty()) {
       return QString::fromStdString(style.shader_id);
     }
-    return QStringLiteral("knight");
+    return QStringLiteral("swordsman");
   }
 
 private:
@@ -891,8 +891,8 @@ private:
     auto apply_color = [&](const std::optional<QVector3D> &override_color,
                            QVector3D &target) {
       target = mix_palette_color(target, override_color, team_tint,
-                                 k_knight_team_mix_weight,
-                                 k_knight_style_mix_weight);
+                                 k_swordsman_team_mix_weight,
+                                 k_swordsman_style_mix_weight);
     };
 
     apply_color(style.cloth_color, variant.palette.cloth);
@@ -912,8 +912,8 @@ private:
     auto apply_shield_color =
         [&](const std::optional<QVector3D> &override_color, QVector3D &target) {
           target = mix_palette_color(target, override_color, team_tint,
-                                     k_knight_team_mix_weight,
-                                     k_knight_style_mix_weight);
+                                     k_swordsman_team_mix_weight,
+                                     k_swordsman_style_mix_weight);
         };
 
     apply_shield_color(style.shield_color, extras.shieldColor);
@@ -936,22 +936,22 @@ private:
 };
 
 void registerKnightRenderer(Render::GL::EntityRendererRegistry &registry) {
-  ensure_knight_styles_registered();
+  ensure_swordsman_styles_registered();
   static KnightRenderer const renderer;
   registry.registerRenderer(
       "troops/kingdom/swordsman", [](const DrawContext &ctx, ISubmitter &out) {
         static KnightRenderer const static_renderer;
-        Shader *knight_shader = nullptr;
+        Shader *swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
           QString shader_key = static_renderer.resolve_shader_key(ctx);
-          knight_shader = ctx.backend->shader(shader_key);
-          if (knight_shader == nullptr) {
-            knight_shader = ctx.backend->shader(QStringLiteral("knight"));
+          swordsman_shader = ctx.backend->shader(shader_key);
+          if (swordsman_shader == nullptr) {
+            swordsman_shader = ctx.backend->shader(QStringLiteral("swordsman"));
           }
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(knight_shader);
+        if ((scene_renderer != nullptr) && (swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {
@@ -959,15 +959,15 @@ void registerKnightRenderer(Render::GL::EntityRendererRegistry &registry) {
         }
       });
   registry.registerRenderer(
-      "troops/kingdom/knight", [](const DrawContext &ctx, ISubmitter &out) {
+      "troops/kingdom/swordsman", [](const DrawContext &ctx, ISubmitter &out) {
         static KnightRenderer const static_renderer;
-        Shader *knight_shader = nullptr;
+        Shader *swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
-          knight_shader = ctx.backend->shader(QStringLiteral("knight"));
+          swordsman_shader = ctx.backend->shader(QStringLiteral("swordsman"));
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(knight_shader);
+        if ((scene_renderer != nullptr) && (swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {

+ 1 - 1
render/entity/nations/kingdom/knight_renderer.h → render/entity/nations/kingdom/swordsman_renderer.h

@@ -7,7 +7,7 @@ namespace Render::GL::Kingdom {
 
 struct KnightStyleConfig;
 
-void register_knight_style(const std::string &nation_id,
+void register_swordsman_style(const std::string &nation_id,
                            const KnightStyleConfig &style);
 
 void registerKnightRenderer(EntityRendererRegistry &registry);

+ 5 - 5
render/entity/nations/kingdom/knight_style.cpp → render/entity/nations/kingdom/swordsman_style.cpp

@@ -1,5 +1,5 @@
-#include "knight_style.h"
-#include "knight_renderer.h"
+#include "swordsman_style.h"
+#include "swordsman_renderer.h"
 
 #include <QVector3D>
 
@@ -14,7 +14,7 @@ constexpr QVector3D k_kingdom_shield_trim{0.78F, 0.76F, 0.62F};
 
 namespace Render::GL::Kingdom {
 
-void register_kingdom_knight_style() {
+void register_kingdom_swordsman_style() {
   KnightStyleConfig style;
   style.cloth_color = k_kingdom_cloth;
   style.leather_color = k_kingdom_leather;
@@ -25,9 +25,9 @@ void register_kingdom_knight_style() {
   style.shield_radius_scale = 1.0F;
   style.shield_aspect_ratio = 1.0F;
   style.has_scabbard = true;
-  style.shader_id = "knight_kingdom_of_iron";
+  style.shader_id = "swordsman_kingdom_of_iron";
 
-  register_knight_style("kingdom_of_iron", style);
+  register_swordsman_style("kingdom_of_iron", style);
 }
 
 } // namespace Render::GL::Kingdom

+ 1 - 1
render/entity/nations/kingdom/knight_style.h → render/entity/nations/kingdom/swordsman_style.h

@@ -22,6 +22,6 @@ struct KnightStyleConfig {
   std::string shader_id;
 };
 
-void register_kingdom_knight_style();
+void register_kingdom_swordsman_style();
 
 } // namespace Render::GL::Kingdom

+ 2 - 2
render/entity/nations/roman/archer_renderer.cpp

@@ -10,8 +10,8 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"

+ 17 - 17
render/entity/nations/roman/mounted_knight_renderer.cpp → render/entity/nations/roman/horse_swordsman_renderer.cpp

@@ -1,4 +1,4 @@
-#include "mounted_knight_renderer.h"
+#include "horse_swordsman_renderer.h"
 #include "../../../../game/core/component.h"
 #include "../../../../game/core/entity.h"
 #include "../../../../game/systems/nation_id.h"
@@ -8,8 +8,8 @@
 #include "../../../gl/primitives.h"
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"
@@ -73,9 +73,9 @@ public:
       }
     }
     if (!nation.empty()) {
-      return QString::fromStdString(std::string("mounted_knight_") + nation);
+      return QString::fromStdString(std::string("horse_swordsman_") + nation);
     }
-    return QStringLiteral("mounted_knight");
+    return QStringLiteral("horse_swordsman");
   }
 
   void customizePose(const DrawContext &ctx,
@@ -731,9 +731,9 @@ private:
     const float scale_factor = 2.0F;
     const float r = 0.15F * scale_factor;
 
-    constexpr float k_mounted_shield_yaw_degrees = -70.0F;
+    constexpr float k_horse_shield_yaw_degrees = -70.0F;
     QMatrix4x4 rot;
-    rot.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+    rot.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
 
     const QVector3D n = rot.map(QVector3D(0.0F, 0.0F, 1.0F));
     const QVector3D axis_x = rot.map(QVector3D(1.0F, 0.0F, 0.0F));
@@ -748,7 +748,7 @@ private:
     {
       QMatrix4x4 m = ctx.model;
       m.translate(shield_center + n * plate_half);
-      m.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+      m.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
       m.scale(r, r, plate_full);
       out.mesh(getUnitCylinder(), m, v.palette.cloth * 1.15F, nullptr, 1.0F);
     }
@@ -756,7 +756,7 @@ private:
     {
       QMatrix4x4 m = ctx.model;
       m.translate(shield_center - n * plate_half);
-      m.rotate(k_mounted_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
+      m.rotate(k_horse_shield_yaw_degrees, 0.0F, 1.0F, 0.0F);
       m.scale(r * 0.985F, r * 0.985F, plate_full);
       out.mesh(getUnitCylinder(), m, v.palette.leather * 0.8F, nullptr, 1.0F);
     }
@@ -782,21 +782,21 @@ void registerMountedKnightRenderer(
     Render::GL::EntityRendererRegistry &registry) {
   static MountedKnightRenderer const renderer;
   registry.registerRenderer(
-      "troops/roman/mounted_knight",
+      "troops/roman/horse_swordsman",
       [](const DrawContext &ctx, ISubmitter &out) {
         static MountedKnightRenderer const static_renderer;
-        Shader *mounted_knight_shader = nullptr;
+        Shader *horse_swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
           QString shader_key = static_renderer.resolve_shader_key(ctx);
-          mounted_knight_shader = ctx.backend->shader(shader_key);
-          if (mounted_knight_shader == nullptr) {
-            mounted_knight_shader =
-                ctx.backend->shader(QStringLiteral("mounted_knight"));
+          horse_swordsman_shader = ctx.backend->shader(shader_key);
+          if (horse_swordsman_shader == nullptr) {
+            horse_swordsman_shader =
+                ctx.backend->shader(QStringLiteral("horse_swordsman"));
           }
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (mounted_knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(mounted_knight_shader);
+        if ((scene_renderer != nullptr) && (horse_swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(horse_swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {

+ 0 - 0
render/entity/nations/roman/mounted_knight_renderer.h → render/entity/nations/roman/horse_swordsman_renderer.h


+ 2 - 2
render/entity/nations/roman/spearman_renderer.cpp

@@ -8,8 +8,8 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"

+ 32 - 32
render/entity/nations/roman/knight_renderer.cpp → render/entity/nations/roman/swordsman_renderer.cpp

@@ -1,4 +1,4 @@
-#include "knight_renderer.h"
+#include "swordsman_renderer.h"
 #include "../../../../game/core/component.h"
 #include "../../../../game/systems/nation_id.h"
 #include "../../../geom/math_utils.h"
@@ -8,14 +8,14 @@
 #include "../../../gl/shader.h"
 #include "../../../humanoid/rig.h"
 #include "../../../humanoid/style_palette.h"
-#include "../../../humanoid_math.h"
-#include "../../../humanoid_specs.h"
+#include "../../../humanoid/humanoid_math.h"
+#include "../../../humanoid/humanoid_specs.h"
 #include "../../../palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"
 #include "../../registry.h"
 #include "../../renderer_constants.h"
-#include "knight_style.h"
+#include "swordsman_style.h"
 #include <numbers>
 #include <qmatrix4x4.h>
 #include <qstringliteral.h>
@@ -36,19 +36,19 @@ namespace Render::GL::Roman {
 
 namespace {
 
-constexpr std::string_view k_knight_default_style_key = "default";
-constexpr float k_knight_team_mix_weight = 0.6F;
-constexpr float k_knight_style_mix_weight = 0.4F;
+constexpr std::string_view k_swordsman_default_style_key = "default";
+constexpr float k_swordsman_team_mix_weight = 0.6F;
+constexpr float k_swordsman_style_mix_weight = 0.4F;
 
-auto knight_style_registry()
+auto swordsman_style_registry()
     -> std::unordered_map<std::string, KnightStyleConfig> & {
   static std::unordered_map<std::string, KnightStyleConfig> styles;
   return styles;
 }
 
-void ensure_knight_styles_registered() {
+void ensure_swordsman_styles_registered() {
   static const bool registered = []() {
-    register_roman_knight_style();
+    register_roman_swordsman_style();
     return true;
   }();
   (void)registered;
@@ -56,9 +56,9 @@ void ensure_knight_styles_registered() {
 
 } // namespace
 
-void register_knight_style(const std::string &nation_id,
+void register_swordsman_style(const std::string &nation_id,
                            const KnightStyleConfig &style) {
-  knight_style_registry()[nation_id] = style;
+  swordsman_style_registry()[nation_id] = style;
 }
 
 using Render::Geom::clamp01;
@@ -852,8 +852,8 @@ private:
 
   auto
   resolve_style(const DrawContext &ctx) const -> const KnightStyleConfig & {
-    ensure_knight_styles_registered();
-    auto &styles = knight_style_registry();
+    ensure_swordsman_styles_registered();
+    auto &styles = swordsman_style_registry();
     std::string nation_id;
     if (ctx.entity != nullptr) {
       if (auto *unit =
@@ -867,7 +867,7 @@ private:
         return it->second;
       }
     }
-    auto it_default = styles.find(std::string(k_knight_default_style_key));
+    auto it_default = styles.find(std::string(k_swordsman_default_style_key));
     if (it_default != styles.end()) {
       return it_default->second;
     }
@@ -881,7 +881,7 @@ public:
     if (!style.shader_id.empty()) {
       return QString::fromStdString(style.shader_id);
     }
-    return QStringLiteral("knight");
+    return QStringLiteral("swordsman");
   }
 
 private:
@@ -891,8 +891,8 @@ private:
     auto apply_color = [&](const std::optional<QVector3D> &override_color,
                            QVector3D &target) {
       target = mix_palette_color(target, override_color, team_tint,
-                                 k_knight_team_mix_weight,
-                                 k_knight_style_mix_weight);
+                                 k_swordsman_team_mix_weight,
+                                 k_swordsman_style_mix_weight);
     };
 
     apply_color(style.cloth_color, variant.palette.cloth);
@@ -912,8 +912,8 @@ private:
     auto apply_shield_color =
         [&](const std::optional<QVector3D> &override_color, QVector3D &target) {
           target = mix_palette_color(target, override_color, team_tint,
-                                     k_knight_team_mix_weight,
-                                     k_knight_style_mix_weight);
+                                     k_swordsman_team_mix_weight,
+                                     k_swordsman_style_mix_weight);
         };
 
     apply_shield_color(style.shield_color, extras.shieldColor);
@@ -936,22 +936,22 @@ private:
 };
 
 void registerKnightRenderer(Render::GL::EntityRendererRegistry &registry) {
-  ensure_knight_styles_registered();
+  ensure_swordsman_styles_registered();
   static KnightRenderer const renderer;
   registry.registerRenderer(
       "troops/roman/swordsman", [](const DrawContext &ctx, ISubmitter &out) {
         static KnightRenderer const static_renderer;
-        Shader *knight_shader = nullptr;
+        Shader *swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
           QString shader_key = static_renderer.resolve_shader_key(ctx);
-          knight_shader = ctx.backend->shader(shader_key);
-          if (knight_shader == nullptr) {
-            knight_shader = ctx.backend->shader(QStringLiteral("knight"));
+          swordsman_shader = ctx.backend->shader(shader_key);
+          if (swordsman_shader == nullptr) {
+            swordsman_shader = ctx.backend->shader(QStringLiteral("swordsman"));
           }
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(knight_shader);
+        if ((scene_renderer != nullptr) && (swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {
@@ -959,15 +959,15 @@ void registerKnightRenderer(Render::GL::EntityRendererRegistry &registry) {
         }
       });
   registry.registerRenderer(
-      "troops/roman/knight", [](const DrawContext &ctx, ISubmitter &out) {
+      "troops/roman/swordsman", [](const DrawContext &ctx, ISubmitter &out) {
         static KnightRenderer const static_renderer;
-        Shader *knight_shader = nullptr;
+        Shader *swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {
-          knight_shader = ctx.backend->shader(QStringLiteral("knight"));
+          swordsman_shader = ctx.backend->shader(QStringLiteral("swordsman"));
         }
         auto *scene_renderer = dynamic_cast<Renderer *>(&out);
-        if ((scene_renderer != nullptr) && (knight_shader != nullptr)) {
-          scene_renderer->setCurrentShader(knight_shader);
+        if ((scene_renderer != nullptr) && (swordsman_shader != nullptr)) {
+          scene_renderer->setCurrentShader(swordsman_shader);
         }
         static_renderer.render(ctx, out);
         if (scene_renderer != nullptr) {

+ 1 - 1
render/entity/nations/roman/knight_renderer.h → render/entity/nations/roman/swordsman_renderer.h

@@ -7,7 +7,7 @@ namespace Render::GL::Roman {
 
 struct KnightStyleConfig;
 
-void register_knight_style(const std::string &nation_id,
+void register_swordsman_style(const std::string &nation_id,
                            const KnightStyleConfig &style);
 
 void registerKnightRenderer(EntityRendererRegistry &registry);

+ 5 - 5
render/entity/nations/roman/knight_style.cpp → render/entity/nations/roman/swordsman_style.cpp

@@ -1,5 +1,5 @@
-#include "knight_style.h"
-#include "knight_renderer.h"
+#include "swordsman_style.h"
+#include "swordsman_renderer.h"
 
 #include <QVector3D>
 
@@ -14,7 +14,7 @@ constexpr QVector3D k_legionary_trim{0.88F, 0.66F, 0.32F};
 
 namespace Render::GL::Roman {
 
-void register_roman_knight_style() {
+void register_roman_swordsman_style() {
   KnightStyleConfig style;
   style.cloth_color = k_legionary_cloth;
   style.leather_color = k_legionary_leather;
@@ -26,9 +26,9 @@ void register_roman_knight_style() {
   style.shield_aspect_ratio = 0.65F;
   style.has_scabbard = true;
   style.shield_cross_decal = false;
-  style.shader_id = "knight_roman_republic";
+  style.shader_id = "swordsman_roman_republic";
 
-  register_knight_style("roman_republic", style);
+  register_swordsman_style("roman_republic", style);
 }
 
 } // namespace Render::GL::Roman

+ 1 - 1
render/entity/nations/roman/knight_style.h → render/entity/nations/roman/swordsman_style.h

@@ -22,6 +22,6 @@ struct KnightStyleConfig {
   std::string shader_id;
 };
 
-void register_roman_knight_style();
+void register_roman_swordsman_style();
 
 } // namespace Render::GL::Roman

+ 6 - 6
render/entity/registry.cpp

@@ -2,16 +2,16 @@
 #include "../scene_renderer.h"
 #include "barracks_renderer.h"
 #include "nations/carthage/archer_renderer.h"
-#include "nations/carthage/knight_renderer.h"
-#include "nations/carthage/mounted_knight_renderer.h"
+#include "nations/carthage/swordsman_renderer.h"
+#include "nations/carthage/horse_swordsman_renderer.h"
 #include "nations/carthage/spearman_renderer.h"
 #include "nations/kingdom/archer_renderer.h"
-#include "nations/kingdom/knight_renderer.h"
-#include "nations/kingdom/mounted_knight_renderer.h"
+#include "nations/kingdom/swordsman_renderer.h"
+#include "nations/kingdom/horse_swordsman_renderer.h"
 #include "nations/kingdom/spearman_renderer.h"
 #include "nations/roman/archer_renderer.h"
-#include "nations/roman/knight_renderer.h"
-#include "nations/roman/mounted_knight_renderer.h"
+#include "nations/roman/swordsman_renderer.h"
+#include "nations/roman/horse_swordsman_renderer.h"
 #include "nations/roman/spearman_renderer.h"
 #include <string>
 #include <utility>

+ 2 - 2
render/gl/backend.cpp

@@ -1044,8 +1044,8 @@ void Backend::execute(const DrawQueue &queue, const Camera &cam) {
           &m_characterPipeline->m_basicUniforms;
       if (active_shader == m_characterPipeline->m_archerShader) {
         uniforms = &m_characterPipeline->m_archerUniforms;
-      } else if (active_shader == m_characterPipeline->m_knightShader) {
-        uniforms = &m_characterPipeline->m_knightUniforms;
+      } else if (active_shader == m_characterPipeline->m_swordsmanShader) {
+        uniforms = &m_characterPipeline->m_swordsmanUniforms;
       } else if (active_shader == m_characterPipeline->m_spearmanShader) {
         uniforms = &m_characterPipeline->m_spearmanUniforms;
       }

+ 1 - 1
render/gl/backend/README.md

@@ -18,7 +18,7 @@ The following pipeline modules reduce backend.cpp complexity (2055 → 973 lines
 - ✅ `cylinder_pipeline.h/.cpp` - Cylinder and fog-of-war instanced rendering (350 lines)
 - ✅ `terrain_pipeline.h/.cpp` - Ground plane, terrain chunks, and grass instanced rendering (220 lines)
 - ✅ `vegetation_pipeline.h/.cpp` - Trees (stone, plant, pine) and firecamp instanced rendering (533 lines)
-- ✅ `character_pipeline.h/.cpp` - Character rendering (archer, knight, spearman, basic) (115 lines)
+- ✅ `character_pipeline.h/.cpp` - Character rendering (archer, swordsman, spearman, basic) (115 lines)
 - ✅ `water_pipeline.h/.cpp` - River, riverbank, and bridge rendering (90 lines)
 - ✅ `effects_pipeline.h/.cpp` - Grid, selection rings, selection smoke (75 lines)
 

+ 12 - 12
render/gl/backend/character_pipeline.cpp

@@ -14,7 +14,7 @@ auto CharacterPipeline::initialize() -> bool {
 
   m_basicShader = m_shaderCache->get("basic");
   m_archerShader = m_shaderCache->get("archer");
-  m_knightShader = m_shaderCache->get("knight");
+  m_swordsmanShader = m_shaderCache->get("swordsman");
   m_spearmanShader = m_shaderCache->get("spearman");
 
   if (m_basicShader == nullptr) {
@@ -23,8 +23,8 @@ auto CharacterPipeline::initialize() -> bool {
   if (m_archerShader == nullptr) {
     qWarning() << "CharacterPipeline: Failed to load archer shader";
   }
-  if (m_knightShader == nullptr) {
-    qWarning() << "CharacterPipeline: Failed to load knight shader";
+  if (m_swordsmanShader == nullptr) {
+    qWarning() << "CharacterPipeline: Failed to load swordsman shader";
   }
   if (m_spearmanShader == nullptr) {
     qWarning() << "CharacterPipeline: Failed to load spearman shader";
@@ -38,7 +38,7 @@ auto CharacterPipeline::initialize() -> bool {
 void CharacterPipeline::shutdown() {
   m_basicShader = nullptr;
   m_archerShader = nullptr;
-  m_knightShader = nullptr;
+  m_swordsmanShader = nullptr;
   m_spearmanShader = nullptr;
 }
 
@@ -51,7 +51,7 @@ void CharacterPipeline::cacheUniforms() {
 
 auto CharacterPipeline::isInitialized() const -> bool {
   return m_basicShader != nullptr && m_archerShader != nullptr &&
-         m_knightShader != nullptr && m_spearmanShader != nullptr;
+         m_swordsmanShader != nullptr && m_spearmanShader != nullptr;
 }
 
 void CharacterPipeline::cacheBasicUniforms() {
@@ -81,16 +81,16 @@ void CharacterPipeline::cacheArcherUniforms() {
 }
 
 void CharacterPipeline::cacheKnightUniforms() {
-  if (m_knightShader == nullptr) {
+  if (m_swordsmanShader == nullptr) {
     return;
   }
 
-  m_knightUniforms.mvp = m_knightShader->uniformHandle("u_mvp");
-  m_knightUniforms.model = m_knightShader->uniformHandle("u_model");
-  m_knightUniforms.texture = m_knightShader->uniformHandle("u_texture");
-  m_knightUniforms.useTexture = m_knightShader->uniformHandle("u_useTexture");
-  m_knightUniforms.color = m_knightShader->uniformHandle("u_color");
-  m_knightUniforms.alpha = m_knightShader->uniformHandle("u_alpha");
+  m_swordsmanUniforms.mvp = m_swordsmanShader->uniformHandle("u_mvp");
+  m_swordsmanUniforms.model = m_swordsmanShader->uniformHandle("u_model");
+  m_swordsmanUniforms.texture = m_swordsmanShader->uniformHandle("u_texture");
+  m_swordsmanUniforms.useTexture = m_swordsmanShader->uniformHandle("u_useTexture");
+  m_swordsmanUniforms.color = m_swordsmanShader->uniformHandle("u_color");
+  m_swordsmanUniforms.alpha = m_swordsmanShader->uniformHandle("u_alpha");
 }
 
 void CharacterPipeline::cacheSpearmanUniforms() {

+ 2 - 2
render/gl/backend/character_pipeline.h

@@ -31,12 +31,12 @@ public:
 
   GL::Shader *m_basicShader = nullptr;
   GL::Shader *m_archerShader = nullptr;
-  GL::Shader *m_knightShader = nullptr;
+  GL::Shader *m_swordsmanShader = nullptr;
   GL::Shader *m_spearmanShader = nullptr;
 
   BasicUniforms m_basicUniforms;
   BasicUniforms m_archerUniforms;
-  BasicUniforms m_knightUniforms;
+  BasicUniforms m_swordsmanUniforms;
   BasicUniforms m_spearmanUniforms;
 
 private:

+ 7 - 7
render/gl/shader_cache.h

@@ -154,10 +154,10 @@ public:
 
     const auto [archerVert, archerFrag] =
         loadBaseShader(QStringLiteral("archer"));
-    const auto [knightVert, knightFrag] =
-        loadBaseShader(QStringLiteral("knight"));
-    const auto [mountedKnightVert, mountedKnightFrag] =
-        loadBaseShader(QStringLiteral("mounted_knight"));
+    const auto [swordsmanVert, swordsmanFrag] =
+        loadBaseShader(QStringLiteral("swordsman"));
+    const auto [horseKnightVert, horseKnightFrag] =
+        loadBaseShader(QStringLiteral("horse_swordsman"));
     const auto [spearmanVert, spearmanFrag] =
         loadBaseShader(QStringLiteral("spearman"));
 
@@ -192,9 +192,9 @@ public:
 
     loadVariant(QStringLiteral("archer"), archerVert, archerFrag);
     loadVariant(QStringLiteral("spearman"), spearmanVert, spearmanFrag);
-    loadVariant(QStringLiteral("knight"), knightVert, knightFrag);
-    loadVariant(QStringLiteral("mounted_knight"), mountedKnightVert,
-                mountedKnightFrag);
+    loadVariant(QStringLiteral("swordsman"), swordsmanVert, swordsmanFrag);
+    loadVariant(QStringLiteral("horse_swordsman"), horseKnightVert,
+                horseKnightFrag);
   }
 
   void clear() {

+ 28 - 0
render/humanoid/humanoid_math.cpp

@@ -0,0 +1,28 @@
+#include "humanoid/humanoid_math.h"
+#include <algorithm>
+#include <qvectornd.h>
+
+namespace Render::GL {
+
+auto elbowBendTorso(const QVector3D &shoulder, const QVector3D &hand,
+										const QVector3D &outwardDir, float alongFrac,
+										float lateral_offset, float yBias,
+										float outwardSign) -> QVector3D {
+	QVector3D dir = hand - shoulder;
+	float const dist = std::max(dir.length(), 1e-5F);
+	dir /= dist;
+
+	QVector3D lateral = outwardDir - dir * QVector3D::dotProduct(outwardDir, dir);
+	if (lateral.lengthSquared() < 1e-8F) {
+		lateral = QVector3D::crossProduct(dir, QVector3D(0, 1, 0));
+	}
+	if (QVector3D::dotProduct(lateral, outwardDir) < 0.0F) {
+		lateral = -lateral;
+	}
+	lateral.normalize();
+
+	return shoulder + dir * (dist * alongFrac) +
+				 lateral * (lateral_offset * outwardSign) + QVector3D(0, yBias, 0);
+}
+
+} // namespace Render::GL

+ 33 - 0
render/humanoid/humanoid_math.h

@@ -0,0 +1,33 @@
+#pragma once
+
+#include "gl/render_constants.h"
+#include <QVector3D>
+#include <cmath>
+#include <cstdint>
+
+namespace Render::GL {
+
+inline auto hash_01(uint32_t x) -> float {
+	x ^= x << HashXorShift::k_xor_shift_amount_13;
+	x ^= x >> HashXorShift::k_xor_shift_amount_17;
+	x ^= x << HashXorShift::k_xor_shift_amount_5;
+	return (x & BitShift::Mask24Bit) / float(BitShift::k_mask_24bit_hex);
+}
+
+inline auto rotY(const QVector3D &v, float angle_rad) -> QVector3D {
+	const float c = std::cos(angle_rad);
+	const float s = std::sin(angle_rad);
+	return {c * v.x() + s * v.z(), v.y(), -s * v.x() + c * v.z()};
+}
+
+inline auto rightOf(const QVector3D &fwd) -> QVector3D {
+	const QVector3D UP(0.0F, 1.0F, 0.0F);
+	return QVector3D::crossProduct(UP, fwd).normalized();
+}
+
+auto elbowBendTorso(const QVector3D &shoulder, const QVector3D &hand,
+										const QVector3D &outwardDir, float alongFrac,
+										float lateral_offset, float yBias,
+										float outwardSign) -> QVector3D;
+
+} // namespace Render::GL

+ 47 - 0
render/humanoid/humanoid_specs.h

@@ -0,0 +1,47 @@
+#pragma once
+
+#include <cstdint>
+
+namespace Render::GL {
+
+struct HumanProportions {
+
+	static constexpr float TOTAL_HEIGHT = 1.80F;
+	static constexpr float HEAD_HEIGHT = 0.23F;
+
+	static constexpr float GROUND_Y = 0.0F;
+	static constexpr float HEAD_TOP_Y = GROUND_Y + TOTAL_HEIGHT;
+	static constexpr float CHIN_Y = HEAD_TOP_Y - HEAD_HEIGHT;
+	static constexpr float NECK_BASE_Y = CHIN_Y - 0.08F;
+	static constexpr float SHOULDER_Y = NECK_BASE_Y - 0.04F;
+	static constexpr float CHEST_Y = SHOULDER_Y - 0.31F;
+	static constexpr float WAIST_Y = CHEST_Y - 0.25F;
+
+	static constexpr float UPPER_LEG_LEN = 0.46F;
+	static constexpr float LOWER_LEG_LEN = 0.44F;
+	static constexpr float KNEE_Y = WAIST_Y - UPPER_LEG_LEN;
+
+	static constexpr float SHOULDER_WIDTH = HEAD_HEIGHT * 1.85F;
+	static constexpr float HEAD_RADIUS = HEAD_HEIGHT * 0.42F;
+	static constexpr float NECK_RADIUS = HEAD_RADIUS * 0.38F;
+	static constexpr float TORSO_TOP_R = HEAD_RADIUS * 1.15F;
+	static constexpr float TORSO_BOT_R = HEAD_RADIUS * 1.05F;
+	static constexpr float UPPER_ARM_R = HEAD_RADIUS * 0.38F;
+	static constexpr float FORE_ARM_R = HEAD_RADIUS * 0.30F;
+	static constexpr float HAND_RADIUS = HEAD_RADIUS * 0.28F;
+	static constexpr float UPPER_LEG_R = HEAD_RADIUS * 0.50F;
+	static constexpr float LOWER_LEG_R = HEAD_RADIUS * 0.42F;
+
+	static constexpr float UPPER_ARM_LEN = 0.28F;
+	static constexpr float FORE_ARM_LEN = 0.30F;
+};
+
+enum class MaterialType : uint8_t {
+	Cloth = 0,
+	Leather = 1,
+	Metal = 2,
+	Wood = 3,
+	Skin = 4
+};
+
+} // namespace Render::GL

+ 1 - 1
render/humanoid/rig.cpp

@@ -11,7 +11,7 @@
 #include "../geom/transforms.h"
 #include "../gl/primitives.h"
 #include "../gl/render_constants.h"
-#include "../humanoid_math.h"
+#include "humanoid_math.h"
 #include "../palette.h"
 #include "../submitter.h"
 #include <QMatrix4x4>

+ 1 - 1
render/humanoid_math.cpp

@@ -1,4 +1,4 @@
-#include "humanoid_math.h"
+#include "humanoid/humanoid_math.h"
 #include <algorithm>
 #include <qvectornd.h>
 

+ 1 - 1
render/palette.cpp

@@ -1,6 +1,6 @@
 #include "palette.h"
 #include "geom/math_utils.h"
-#include "humanoid_math.h"
+#include "humanoid/humanoid_math.h"
 #include <cstdint>
 #include <qvectornd.h>
 

+ 9 - 9
ui/qml/ProductionPanel.qml

@@ -345,7 +345,7 @@ Rectangle {
                             width: 110
                             height: 80
                             radius: 6
-                            color: isEnabled ? (knightMouseArea.containsMouse ? "#34495e" : "#2c3e50") : "#1a1a1a"
+                            color: isEnabled ? (swordsmanMouseArea.containsMouse ? "#34495e" : "#2c3e50") : "#1a1a1a"
                             border.color: isEnabled ? "#4a6572" : "#2a2a2a"
                             border.width: 2
                             opacity: isEnabled ? 1 : 0.5
@@ -356,7 +356,7 @@ Rectangle {
 
                                 Text {
                                     anchors.horizontalCenter: parent.horizontalCenter
-                                    text: (typeof StyleGuide !== 'undefined' && StyleGuide.unitIcons) ? (StyleGuide.unitIcons["swordsman"] || StyleGuide.unitIcons["knight"] || "⚔️") : "⚔️"
+                                    text: (typeof StyleGuide !== 'undefined' && StyleGuide.unitIcons) ? (StyleGuide.unitIcons["swordsman"] || StyleGuide.unitIcons["swordsman"] || "⚔️") : "⚔️"
                                     color: parent.parent.parent.isEnabled ? "#ecf0f1" : "#5a5a5a"
                                     font.pointSize: 24
                                 }
@@ -391,7 +391,7 @@ Rectangle {
                             }
 
                             MouseArea {
-                                id: knightMouseArea
+                                id: swordsmanMouseArea
 
                                 anchors.fill: parent
                                 hoverEnabled: true
@@ -406,7 +406,7 @@ Rectangle {
                             Rectangle {
                                 anchors.fill: parent
                                 color: "#ffffff"
-                                opacity: knightMouseArea.pressed ? 0.2 : 0
+                                opacity: swordsmanMouseArea.pressed ? 0.2 : 0
                                 radius: parent.radius
                             }
 
@@ -493,7 +493,7 @@ Rectangle {
                             width: 110
                             height: 80
                             radius: 6
-                            color: isEnabled ? (mountedKnightMouseArea.containsMouse ? "#34495e" : "#2c3e50") : "#1a1a1a"
+                            color: isEnabled ? (horseKnightMouseArea.containsMouse ? "#34495e" : "#2c3e50") : "#1a1a1a"
                             border.color: isEnabled ? "#4a6572" : "#2a2a2a"
                             border.width: 2
                             opacity: isEnabled ? 1 : 0.5
@@ -504,7 +504,7 @@ Rectangle {
 
                                 Text {
                                     anchors.horizontalCenter: parent.horizontalCenter
-                                    text: (typeof StyleGuide !== 'undefined' && StyleGuide.unitIcons) ? StyleGuide.unitIcons["mounted_knight"] || "🐴" : "🐴"
+                                    text: (typeof StyleGuide !== 'undefined' && StyleGuide.unitIcons) ? StyleGuide.unitIcons["horse_swordsman"] || "🐴" : "🐴"
                                     color: parent.parent.parent.isEnabled ? "#ecf0f1" : "#5a5a5a"
                                     font.pointSize: 24
                                 }
@@ -539,12 +539,12 @@ Rectangle {
                             }
 
                             MouseArea {
-                                id: mountedKnightMouseArea
+                                id: horseKnightMouseArea
 
                                 anchors.fill: parent
                                 hoverEnabled: true
                                 enabled: parent.isEnabled
-                                onClicked: productionPanel.recruitUnit("mounted_knight")
+                                onClicked: productionPanel.recruitUnit("horse_swordsman")
                                 cursorShape: parent.isEnabled ? Qt.PointingHandCursor : Qt.ForbiddenCursor
                                 ToolTip.visible: containsMouse
                                 ToolTip.text: parent.isEnabled ? qsTr("Recruit Mounted Knight\nCost: %1 villagers\nBuild time: %2s").arg(unitGridContent.prod.villagerCost || 1).arg((unitGridContent.prod.buildTime || 0).toFixed(0)) : (parent.queueTotal >= 5 ? qsTr("Queue is full (5/5)") : (unitGridContent.prod.producedCount >= unitGridContent.prod.maxUnits ? qsTr("Unit cap reached") : qsTr("Cannot recruit")))
@@ -554,7 +554,7 @@ Rectangle {
                             Rectangle {
                                 anchors.fill: parent
                                 color: "#ffffff"
-                                opacity: mountedKnightMouseArea.pressed ? 0.2 : 0
+                                opacity: horseKnightMouseArea.pressed ? 0.2 : 0
                                 radius: parent.radius
                             }
 

+ 2 - 2
ui/qml/StyleGuide.qml

@@ -126,9 +126,9 @@ QtObject {
     readonly property var unitIcons: ({
         "archer": "🏹",
         "swordsman": "⚔️",
-        "knight": "⚔️",
+        "swordsman": "⚔️",
         "spearman": "🛡️",
-        "mounted_knight": "🐴",
+        "horse_swordsman": "🐴",
         "default": "👤"
     })
 }

+ 1 - 1
ui/theme.cpp

@@ -54,7 +54,7 @@ QVariantList Theme::factions() {
 QVariantMap Theme::unitIcons() {
   QVariantMap icons;
   icons["archer"] = "🏹";
-  icons["knight"] = "⚔️";
+  icons["swordsman"] = "⚔️";
   icons["warrior"] = "⚔️";
   icons["spearman"] = "🛡️";
   icons["cavalry"] = "🐎";