Browse Source

apply format

djeada 4 months ago
parent
commit
4e1f94de8b

+ 60 - 56
app/core/game_engine.cpp

@@ -156,7 +156,6 @@ GameEngine::GameEngine() {
   connect(m_mapCatalog.get(), &Game::Map::MapCatalog::allMapsLoaded, this,
           [this]() { emit availableMapsChanged(); });
 
-  // Initialize Audio System
   if (AudioSystem::getInstance().initialize()) {
     qInfo() << "AudioSystem initialized successfully";
     loadAudioResources();
@@ -164,23 +163,26 @@ GameEngine::GameEngine() {
     qWarning() << "Failed to initialize AudioSystem";
   }
 
-  // Initialize AudioEventHandler
-  m_audioEventHandler = std::make_unique<Game::Audio::AudioEventHandler>(m_world.get());
+  m_audioEventHandler =
+      std::make_unique<Game::Audio::AudioEventHandler>(m_world.get());
   if (m_audioEventHandler->initialize()) {
     qInfo() << "AudioEventHandler initialized successfully";
-    
-    // Register unit voice mappings
+
     m_audioEventHandler->loadUnitVoiceMapping("archer", "archer_voice");
     m_audioEventHandler->loadUnitVoiceMapping("knight", "knight_voice");
     m_audioEventHandler->loadUnitVoiceMapping("spearman", "spearman_voice");
-    
-    // Register ambient music mappings
-    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::PEACEFUL, "music_peaceful");
-    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::TENSE, "music_tense");
-    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::COMBAT, "music_combat");
-    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::VICTORY, "music_victory");
-    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::DEFEAT, "music_defeat");
-    
+
+    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::PEACEFUL,
+                                          "music_peaceful");
+    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::TENSE,
+                                          "music_tense");
+    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::COMBAT,
+                                          "music_combat");
+    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::VICTORY,
+                                          "music_victory");
+    m_audioEventHandler->loadAmbientMusic(Engine::Core::AmbientState::DEFEAT,
+                                          "music_defeat");
+
     qInfo() << "Audio mappings configured";
   } else {
     qWarning() << "Failed to initialize AudioEventHandler";
@@ -255,7 +257,7 @@ GameEngine::GameEngine() {
 }
 
 GameEngine::~GameEngine() {
-  // Shutdown audio subsystem
+
   if (m_audioEventHandler) {
     m_audioEventHandler->shutdown();
   }
@@ -515,7 +517,6 @@ void GameEngine::update(float dt) {
     dt *= m_runtime.timeScale;
   }
 
-  // Update ambient state for audio
   if (!m_runtime.paused && !m_runtime.loading) {
     updateAmbientState(dt);
   }
@@ -978,11 +979,9 @@ void GameEngine::startSkirmish(const QString &mapPath,
       }
     }
 
-    // Initialize ambient state for the new game
     m_currentAmbientState = Engine::Core::AmbientState::PEACEFUL;
     m_ambientCheckTimer = 0.0f;
-    
-    // Trigger initial peaceful music
+
     Engine::Core::EventManager::instance().publish(
         Engine::Core::AmbientStateChangedEvent(
             Engine::Core::AmbientState::PEACEFUL,
@@ -1538,19 +1537,17 @@ QVector3D GameEngine::getPatrolPreviewWaypoint() const {
 }
 
 void GameEngine::updateAmbientState(float dt) {
-  // Update ambient state check every 2 seconds to avoid overhead
+
   m_ambientCheckTimer += dt;
   const float CHECK_INTERVAL = 2.0f;
-  
+
   if (m_ambientCheckTimer < CHECK_INTERVAL) {
     return;
   }
   m_ambientCheckTimer = 0.0f;
 
-  // Determine the new ambient state based on game conditions
   Engine::Core::AmbientState newState = Engine::Core::AmbientState::PEACEFUL;
 
-  // Check victory/defeat first
   if (!m_runtime.victoryState.isEmpty()) {
     if (m_runtime.victoryState == "victory") {
       newState = Engine::Core::AmbientState::VICTORY;
@@ -1558,22 +1555,22 @@ void GameEngine::updateAmbientState(float dt) {
       newState = Engine::Core::AmbientState::DEFEAT;
     }
   } else if (isPlayerInCombat()) {
-    // Check if player units are engaged in combat
+
     newState = Engine::Core::AmbientState::COMBAT;
-  } else if (m_entityCache.enemyBarracksAlive && m_entityCache.playerBarracksAlive) {
-    // If both sides have barracks alive, it's tense
+  } else if (m_entityCache.enemyBarracksAlive &&
+             m_entityCache.playerBarracksAlive) {
+
     newState = Engine::Core::AmbientState::TENSE;
   }
 
-  // Publish state change event if state changed
   if (newState != m_currentAmbientState) {
     Engine::Core::AmbientState previousState = m_currentAmbientState;
     m_currentAmbientState = newState;
-    
+
     Engine::Core::EventManager::instance().publish(
         Engine::Core::AmbientStateChangedEvent(newState, previousState));
-    
-    qInfo() << "Ambient state changed from" << static_cast<int>(previousState) 
+
+    qInfo() << "Ambient state changed from" << static_cast<int>(previousState)
             << "to" << static_cast<int>(newState);
   }
 }
@@ -1583,7 +1580,6 @@ bool GameEngine::isPlayerInCombat() const {
     return false;
   }
 
-  // Check if any player unit has an attack target or is under attack
   auto units = m_world->getEntitiesWith<Engine::Core::UnitComponent>();
   const float COMBAT_CHECK_RADIUS = 15.0f;
 
@@ -1593,25 +1589,25 @@ bool GameEngine::isPlayerInCombat() const {
       continue;
     }
 
-    // Check if unit has an attack target
     if (entity->hasComponent<Engine::Core::AttackTargetComponent>()) {
       return true;
     }
 
-    // Check if there are enemies nearby
     auto *transform = entity->getComponent<Engine::Core::TransformComponent>();
     if (!transform) {
       continue;
     }
 
     for (auto *otherEntity : units) {
-      auto *otherUnit = otherEntity->getComponent<Engine::Core::UnitComponent>();
-      if (!otherUnit || otherUnit->ownerId == m_runtime.localOwnerId || 
+      auto *otherUnit =
+          otherEntity->getComponent<Engine::Core::UnitComponent>();
+      if (!otherUnit || otherUnit->ownerId == m_runtime.localOwnerId ||
           otherUnit->health <= 0) {
         continue;
       }
 
-      auto *otherTransform = otherEntity->getComponent<Engine::Core::TransformComponent>();
+      auto *otherTransform =
+          otherEntity->getComponent<Engine::Core::TransformComponent>();
       if (!otherTransform) {
         continue;
       }
@@ -1631,59 +1627,67 @@ bool GameEngine::isPlayerInCombat() const {
 
 void GameEngine::loadAudioResources() {
   auto &audioSys = AudioSystem::getInstance();
-  
-  // Get base path for assets
+
   QString basePath = QCoreApplication::applicationDirPath() + "/assets/audio/";
-  
-  // Load unit voice sounds
-  if (audioSys.loadSound("archer_voice", (basePath + "voices/archer_voice.wav").toStdString())) {
+
+  if (audioSys.loadSound(
+          "archer_voice",
+          (basePath + "voices/archer_voice.wav").toStdString())) {
     qInfo() << "Loaded archer voice";
   } else {
     qWarning() << "Failed to load archer voice";
   }
-  
-  if (audioSys.loadSound("knight_voice", (basePath + "voices/knight_voice.wav").toStdString())) {
+
+  if (audioSys.loadSound(
+          "knight_voice",
+          (basePath + "voices/knight_voice.wav").toStdString())) {
     qInfo() << "Loaded knight voice";
   } else {
     qWarning() << "Failed to load knight voice";
   }
-  
-  if (audioSys.loadSound("spearman_voice", (basePath + "voices/spearman_voice.wav").toStdString())) {
+
+  if (audioSys.loadSound(
+          "spearman_voice",
+          (basePath + "voices/spearman_voice.wav").toStdString())) {
     qInfo() << "Loaded spearman voice";
   } else {
     qWarning() << "Failed to load spearman voice";
   }
-  
-  // Load ambient music
-  if (audioSys.loadMusic("music_peaceful", (basePath + "music/peaceful.wav").toStdString())) {
+
+  if (audioSys.loadMusic("music_peaceful",
+                         (basePath + "music/peaceful.wav").toStdString())) {
     qInfo() << "Loaded peaceful music";
   } else {
     qWarning() << "Failed to load peaceful music";
   }
-  
-  if (audioSys.loadMusic("music_tense", (basePath + "music/tense.wav").toStdString())) {
+
+  if (audioSys.loadMusic("music_tense",
+                         (basePath + "music/tense.wav").toStdString())) {
     qInfo() << "Loaded tense music";
   } else {
     qWarning() << "Failed to load tense music";
   }
-  
-  if (audioSys.loadMusic("music_combat", (basePath + "music/combat.wav").toStdString())) {
+
+  if (audioSys.loadMusic("music_combat",
+                         (basePath + "music/combat.wav").toStdString())) {
     qInfo() << "Loaded combat music";
   } else {
     qWarning() << "Failed to load combat music";
   }
-  
-  if (audioSys.loadMusic("music_victory", (basePath + "music/victory.wav").toStdString())) {
+
+  if (audioSys.loadMusic("music_victory",
+                         (basePath + "music/victory.wav").toStdString())) {
     qInfo() << "Loaded victory music";
   } else {
     qWarning() << "Failed to load victory music";
   }
-  
-  if (audioSys.loadMusic("music_defeat", (basePath + "music/defeat.wav").toStdString())) {
+
+  if (audioSys.loadMusic("music_defeat",
+                         (basePath + "music/defeat.wav").toStdString())) {
     qInfo() << "Loaded defeat music";
   } else {
     qWarning() << "Failed to load defeat music";
   }
-  
+
   qInfo() << "Audio resources loading complete";
 }

+ 2 - 1
app/core/game_engine.h

@@ -293,7 +293,8 @@ private:
   Engine::Core::ScopedEventSubscription<Engine::Core::UnitSpawnedEvent>
       m_unitSpawnedSubscription;
   EntityCache m_entityCache;
-  Engine::Core::AmbientState m_currentAmbientState = Engine::Core::AmbientState::PEACEFUL;
+  Engine::Core::AmbientState m_currentAmbientState =
+      Engine::Core::AmbientState::PEACEFUL;
   float m_ambientCheckTimer = 0.0f;
 
   void updateAmbientState(float dt);

+ 46 - 37
assets/shaders/mounted_knight.frag

@@ -16,8 +16,8 @@ out vec4 FragColor;
 // ---------------------
 const float PI = 3.14159265359;
 
-float saturate(float x){ return clamp(x, 0.0, 1.0); }
-vec3  saturate(vec3  x){ return clamp(x, 0.0, 1.0); }
+float saturate(float x) { return clamp(x, 0.0, 1.0); }
+vec3 saturate(vec3 x) { return clamp(x, 0.0, 1.0); }
 
 float hash(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * 0.1031);
@@ -39,7 +39,7 @@ float noise(vec2 p) {
 float fbm(vec2 p) {
   float a = 0.5;
   float f = 0.0;
-  for(int i=0;i<5;++i){
+  for (int i = 0; i < 5; ++i) {
     f += a * noise(p);
     p *= 2.03;
     a *= 0.5;
@@ -78,12 +78,14 @@ float chainmailRings(vec2 p) {
   vec2 g0 = fract(uv) - 0.5;
   float r0 = length(g0);
   float fw0 = fwidth(r0) * 1.2;
-  float ring0 = smoothstep(0.30 + fw0, 0.30 - fw0, r0) - smoothstep(0.20 + fw0, 0.20 - fw0, r0);
+  float ring0 = smoothstep(0.30 + fw0, 0.30 - fw0, r0) -
+                smoothstep(0.20 + fw0, 0.20 - fw0, r0);
 
   vec2 g1 = fract(uv + vec2(0.5, 0.0)) - 0.5;
   float r1 = length(g1);
   float fw1 = fwidth(r1) * 1.2;
-  float ring1 = smoothstep(0.30 + fw1, 0.30 - fw1, r1) - smoothstep(0.20 + fw1, 0.20 - fw1, r1);
+  float ring1 = smoothstep(0.30 + fw1, 0.30 - fw1, r1) -
+                smoothstep(0.20 + fw1, 0.20 - fw1, r1);
 
   return (ring0 + ring1) * 0.15;
 }
@@ -103,9 +105,9 @@ vec3 fresnelSchlick(float cosTheta, vec3 F0) {
 }
 
 float D_GGX(float NdotH, float rough) {
-  float a  = max(0.001, rough);
+  float a = max(0.001, rough);
   float a2 = a * a;
-  float d  = (NdotH * NdotH) * (a2 - 1.0) + 1.0;
+  float d = (NdotH * NdotH) * (a2 - 1.0) + 1.0;
   return a2 / max(1e-6, (PI * d * d));
 }
 
@@ -131,7 +133,7 @@ vec3 perturbNormalWS(vec3 N, vec3 worldPos, float h, float scale) {
 
 // hemisphere ambient (sky/ground)
 vec3 hemilight(vec3 N) {
-  vec3 sky    = vec3(0.55, 0.64, 0.80);
+  vec3 sky = vec3(0.55, 0.64, 0.80);
   vec3 ground = vec3(0.23, 0.20, 0.17);
   float t = saturate(N.y * 0.5 + 0.5);
   return mix(ground, sky, t) * 0.28;
@@ -142,24 +144,28 @@ vec3 hemilight(vec3 N) {
 // ---------------------
 void main() {
   vec3 baseColor = u_color;
-  if (u_useTexture) baseColor *= texture(u_texture, v_texCoord).rgb;
+  if (u_useTexture)
+    baseColor *= texture(u_texture, v_texCoord).rgb;
 
   vec3 N = normalize(v_normal);
   vec2 uv = v_worldPos.xz * 5.0;
 
-  float avg = (baseColor.r + baseColor.g + baseColor.b) * (1.0/3.0);
-  float hueSpan = max(max(baseColor.r, baseColor.g), baseColor.b) - min(min(baseColor.r, baseColor.g), baseColor.b);
+  float avg = (baseColor.r + baseColor.g + baseColor.b) * (1.0 / 3.0);
+  float hueSpan = max(max(baseColor.r, baseColor.g), baseColor.b) -
+                  min(min(baseColor.r, baseColor.g), baseColor.b);
 
-  bool isBrass     = (baseColor.r > baseColor.g * 1.15 && baseColor.r > baseColor.b * 1.20 && avg > 0.50);
-  bool isSteel     = (avg > 0.60 && !isBrass);
-  bool isChain     = (!isSteel && !isBrass && avg > 0.40 && avg <= 0.60);
-  bool isFabric    = (!isSteel && !isBrass && !isChain && avg > 0.25);
-  bool isLeather   = (!isSteel && !isBrass && !isChain && !isFabric);
+  bool isBrass = (baseColor.r > baseColor.g * 1.15 &&
+                  baseColor.r > baseColor.b * 1.20 && avg > 0.50);
+  bool isSteel = (avg > 0.60 && !isBrass);
+  bool isChain = (!isSteel && !isBrass && avg > 0.40 && avg <= 0.60);
+  bool isFabric = (!isSteel && !isBrass && !isChain && avg > 0.25);
+  bool isLeather = (!isSteel && !isBrass && !isChain && !isFabric);
   bool isHorseHide = (avg < 0.40 && hueSpan < 0.12 && v_worldPos.y < 0.8);
 
   // lighting frame
   vec3 L = normalize(vec3(1.0, 1.2, 1.0));
-  vec3 V = normalize(vec3(0.0, 1.0, 0.5));        // stable view proxy (keeps interface unchanged)
+  vec3 V = normalize(
+      vec3(0.0, 1.0, 0.5)); // stable view proxy (keeps interface unchanged)
   vec3 H = normalize(L + V);
 
   float NdotL = saturate(dot(N, L));
@@ -173,9 +179,9 @@ void main() {
 
   // base material params
   float roughness = 0.5;
-  vec3  F0 = vec3(0.04);     // dielectric default
+  vec3 F0 = vec3(0.04); // dielectric default
   float metalness = 0.0;
-  vec3  albedo = baseColor;
+  vec3 albedo = baseColor;
 
   // micro details / masks (re-used)
   float nSmall = fbm(uv * 6.0);
@@ -193,10 +199,11 @@ void main() {
     vec3 up = vec3(0.0, 1.0, 0.0);
     vec3 T = normalize(cross(up, N) + 1e-4); // hair tangent guess
     float flowNoise = fbm(uv * 10.0);
-    float aniso = pow(saturate(dot(normalize(reflect(-L, N)), T)), 14.0) * 0.08 * (0.6 + 0.4 * flowNoise);
+    float aniso = pow(saturate(dot(normalize(reflect(-L, N)), T)), 14.0) *
+                  0.08 * (0.6 + 0.4 * flowNoise);
 
     float hideTex = horseHidePattern(v_worldPos.xz);
-    float sheen   = pow(1.0 - NdotV, 4.0) * 0.07;
+    float sheen = pow(1.0 - NdotV, 4.0) * 0.07;
 
     roughness = 0.58 - hideTex * 0.08;
     F0 = vec3(0.035);
@@ -212,15 +219,16 @@ void main() {
     // microfacet spec
     float D = D_GGX(saturate(dot(N, H)), roughness);
     float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
-    vec3  F = fresnelSchlick(VdotH, F0);
-    vec3  spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+    vec3 F = fresnelSchlick(VdotH, F0);
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
     col += NdotL_wrap * (albedo * (1.0 - F) * 0.95) + spec * 0.8;
     col += aniso + sheen;
 
   } else if (isSteel) {
-    float brushed = abs(sin(v_worldPos.y * 95.0)) * 0.02 + noise(uv * 35.0) * 0.015;
-    float dents   = noise(uv * 8.0) * 0.03;
-    float plates  = armorPlates(v_worldPos.xz, v_worldPos.y);
+    float brushed =
+        abs(sin(v_worldPos.y * 95.0)) * 0.02 + noise(uv * 35.0) * 0.015;
+    float dents = noise(uv * 8.0) * 0.03;
+    float plates = armorPlates(v_worldPos.xz, v_worldPos.y);
 
     // bump from brushing
     float h = fbm(vec2(v_worldPos.y * 25.0, v_worldPos.z * 6.0));
@@ -239,8 +247,8 @@ void main() {
     // microfacet spec only for metals
     float D = D_GGX(saturate(dot(N, H)), roughness);
     float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
-    vec3  F = fresnelSchlick(VdotH, F0 * albedo); // slight tint
-    vec3  spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+    vec3 F = fresnelSchlick(VdotH, F0 * albedo); // slight tint
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
 
     col += ambient * 0.3; // metals rely more on spec
     col += NdotL_wrap * spec * 1.5;
@@ -261,8 +269,8 @@ void main() {
 
     float D = D_GGX(saturate(dot(N, H)), roughness);
     float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
-    vec3  F = fresnelSchlick(VdotH, F0);
-    vec3  spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+    vec3 F = fresnelSchlick(VdotH, F0);
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
 
     col += ambient * 0.25;
     col += NdotL_wrap * spec * 1.35;
@@ -282,8 +290,8 @@ void main() {
 
     float D = D_GGX(saturate(dot(N, H)), roughness);
     float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
-    vec3  F = fresnelSchlick(VdotH, F0);
-    vec3  spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+    vec3 F = fresnelSchlick(VdotH, F0);
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
 
     col += ambient * 0.25;
     col += NdotL_wrap * (spec * (1.2 + rings)) + vec3(ringHi);
@@ -293,7 +301,7 @@ void main() {
   } else if (isFabric) {
     float weaveX = sin(v_worldPos.x * 70.0);
     float weaveZ = sin(v_worldPos.z * 70.0);
-    float weave  = weaveX * weaveZ * 0.04;
+    float weave = weaveX * weaveZ * 0.04;
     float embroidery = fbm(uv * 6.0) * 0.08;
 
     float h = fbm(uv * 22.0) * 0.7 + weave * 0.6;
@@ -306,17 +314,18 @@ void main() {
     vec3 F = fresnelSchlick(VdotH, F0);
     float D = D_GGX(saturate(dot(N, H)), roughness);
     float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
-    vec3  spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
 
     float sheen = pow(1.0 - NdotV, 6.0) * 0.10;
     albedo *= 1.0 + fbm(uv * 5.0) * 0.10 - 0.05;
 
     col += ambient * albedo;
-    col += NdotL_wrap * (albedo * (1.0 - F) + spec * 0.3) + vec3(weave + embroidery + sheen);
+    col += NdotL_wrap * (albedo * (1.0 - F) + spec * 0.3) +
+           vec3(weave + embroidery + sheen);
 
   } else { // leather
     float grain = fbm(uv * 10.0) * 0.15;
-    float wear  = fbm(uv * 3.0) * 0.12;
+    float wear = fbm(uv * 3.0) * 0.12;
 
     float h = fbm(uv * 18.0);
     N = perturbNormalWS(N, v_worldPos, h, 0.28);
@@ -328,7 +337,7 @@ void main() {
     vec3 F = fresnelSchlick(VdotH, F0);
     float D = D_GGX(saturate(dot(N, H)), roughness);
     float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
-    vec3  spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
 
     float sheen = pow(1.0 - NdotV, 4.0) * 0.06;
 

+ 377 - 310
render/entity/horse_renderer.cpp

@@ -1,15 +1,15 @@
 #include "horse_renderer.h"
 
-#include "../humanoid_base.h"
-#include "../palette.h"
 #include "../geom/math_utils.h"
 #include "../geom/transforms.h"
 #include "../gl/mesh.h"
 #include "../gl/primitives.h"
+#include "../humanoid_base.h"
+#include "../palette.h"
 
 #include <QMatrix4x4>
-#include <QVector3D>
 #include <QRandomGenerator>
+#include <QVector3D>
 
 #include <algorithm>
 #include <cmath>
@@ -17,25 +17,21 @@
 
 namespace Render::GL {
 
+using Render::Geom::clamp01;
 using Render::Geom::coneFromTo;
 using Render::Geom::cylinderBetween;
-using Render::Geom::clamp01;
 
-// ------------------------------
-// helpers (private to this file)
-// ------------------------------
 namespace {
 
 constexpr float kPi = 3.14159265358979323846f;
 
-// simple uint->float hash in [0,1)
 inline float hash01(uint32_t x) {
   x ^= x >> 16;
   x *= 0x7feb352dU;
   x ^= x >> 15;
   x *= 0x846ca68bU;
   x ^= x >> 16;
-  return (x & 0xFFFFFF) / float(0x1000000); // 24-bit precision is plenty
+  return (x & 0xFFFFFF) / float(0x1000000);
 }
 
 inline float randBetween(uint32_t seed, uint32_t salt, float minV, float maxV) {
@@ -43,7 +39,7 @@ inline float randBetween(uint32_t seed, uint32_t salt, float minV, float maxV) {
   return minV + (maxV - minV) * t;
 }
 
-inline QVector3D lerpVec(const QVector3D& a, const QVector3D& b, float t) {
+inline QVector3D lerpVec(const QVector3D &a, const QVector3D &b, float t) {
   return a * (1.0f - t) + b * t;
 }
 
@@ -54,180 +50,155 @@ inline float smoothstep(float e0, float e1, float x) {
   return t * t * (3.0f - 2.0f * t);
 }
 
-// rotate v around the given axis by angle (radians)
-inline QVector3D rotateAroundY(const QVector3D& v, float angle) {
+inline QVector3D rotateAroundY(const QVector3D &v, float angle) {
   float s = std::sin(angle), c = std::cos(angle);
-  return QVector3D(v.x()*c + v.z()*s, v.y(), -v.x()*s + v.z()*c);
+  return QVector3D(v.x() * c + v.z() * s, v.y(), -v.x() * s + v.z() * c);
 }
-inline QVector3D rotateAroundZ(const QVector3D& v, float angle) {
+inline QVector3D rotateAroundZ(const QVector3D &v, float angle) {
   float s = std::sin(angle), c = std::cos(angle);
-  return QVector3D(v.x()*c - v.y()*s, v.x()*s + v.y()*c, v.z());
+  return QVector3D(v.x() * c - v.y() * s, v.x() * s + v.y() * c, v.z());
 }
 
-inline QVector3D darken(const QVector3D& c, float k) { return c * k; }
-inline QVector3D lighten(const QVector3D& c, float k) { return QVector3D(
-  saturate(c.x()*k), saturate(c.y()*k), saturate(c.z()*k)); }
+inline QVector3D darken(const QVector3D &c, float k) { return c * k; }
+inline QVector3D lighten(const QVector3D &c, float k) {
+  return QVector3D(saturate(c.x() * k), saturate(c.y() * k),
+                   saturate(c.z() * k));
+}
 
-// deterministic pseudo-random from a color (used to vary markings without changing interfaces)
-inline uint32_t colorHash(const QVector3D& c) {
+inline uint32_t colorHash(const QVector3D &c) {
   uint32_t r = uint32_t(saturate(c.x()) * 255.0f);
   uint32_t g = uint32_t(saturate(c.y()) * 255.0f);
   uint32_t b = uint32_t(saturate(c.z()) * 255.0f);
   uint32_t v = (r << 16) | (g << 8) | b;
-  // avalanche
-  v ^= v >> 16; v *= 0x7feb352dU;
-  v ^= v >> 15; v *= 0x846ca68bU;
+
+  v ^= v >> 16;
+  v *= 0x7feb352dU;
+  v ^= v >> 15;
+  v *= 0x846ca68bU;
   v ^= v >> 16;
   return v;
 }
 
-} // anonymous namespace
-
-// -----------------------------------
-// profile & variant construction
-// -----------------------------------
+} // namespace
 
 HorseDimensions makeHorseDimensions(uint32_t seed) {
   HorseDimensions d;
 
-  // barrel/body - raised for longer legs
-  d.bodyLength      = randBetween(seed, 0x12u,   0.74f, 0.84f);
-  d.bodyWidth       = randBetween(seed, 0x34u,   0.17f, 0.20f);
-  d.bodyHeight      = randBetween(seed, 0x56u,   0.33f, 0.37f);
-  d.barrelCenterY   = randBetween(seed, 0x78u,   0.02f, 0.08f);  // Slightly raised
+  d.bodyLength = randBetween(seed, 0x12u, 0.74f, 0.84f);
+  d.bodyWidth = randBetween(seed, 0x34u, 0.17f, 0.20f);
+  d.bodyHeight = randBetween(seed, 0x56u, 0.33f, 0.37f);
+  d.barrelCenterY = randBetween(seed, 0x78u, 0.02f, 0.08f);
 
-  // neck / head
-  d.neckLength      = randBetween(seed, 0x9Au,   0.32f, 0.37f);
-  d.neckRise        = randBetween(seed, 0xBCu,   0.20f, 0.26f);
-  d.headLength      = randBetween(seed, 0xDEu,   0.24f, 0.28f);
-  d.headWidth       = randBetween(seed, 0xF1u,   0.13f, 0.16f);
-  d.headHeight      = randBetween(seed, 0x1357u, 0.16f, 0.19f);
-  d.muzzleLength    = randBetween(seed, 0x2468u, 0.11f, 0.14f);
+  d.neckLength = randBetween(seed, 0x9Au, 0.32f, 0.37f);
+  d.neckRise = randBetween(seed, 0xBCu, 0.20f, 0.26f);
+  d.headLength = randBetween(seed, 0xDEu, 0.24f, 0.28f);
+  d.headWidth = randBetween(seed, 0xF1u, 0.13f, 0.16f);
+  d.headHeight = randBetween(seed, 0x1357u, 0.16f, 0.19f);
+  d.muzzleLength = randBetween(seed, 0x2468u, 0.11f, 0.14f);
 
-  // legs / hooves - longer for realistic proportions (1.5x original)
-  d.legLength       = randBetween(seed, 0x369Cu, 0.87f, 0.99f);
-  d.hoofHeight      = randBetween(seed, 0x48AEu, 0.070f, 0.080f);
+  d.legLength = randBetween(seed, 0x369Cu, 0.87f, 0.99f);
+  d.hoofHeight = randBetween(seed, 0x48AEu, 0.070f, 0.080f);
 
-  // tail
-  d.tailLength      = randBetween(seed, 0x5ABCu, 0.30f, 0.36f);
+  d.tailLength = randBetween(seed, 0x5ABCu, 0.30f, 0.36f);
 
-  // tack / saddle
   d.saddleThickness = randBetween(seed, 0x6CDEu, 0.040f, 0.052f);
   d.seatForwardOffset = randBetween(seed, 0x7531u, 0.020f, 0.050f);
-  d.stirrupOut      = d.bodyWidth * randBetween(seed, 0x8642u, 0.88f, 0.98f);
-  d.stirrupDrop     = randBetween(seed, 0x9753u, 0.23f, 0.27f);
+  d.stirrupOut = d.bodyWidth * randBetween(seed, 0x8642u, 0.88f, 0.98f);
+  d.stirrupDrop = randBetween(seed, 0x9753u, 0.23f, 0.27f);
 
-  // motion
   d.idleBobAmplitude = randBetween(seed, 0xA864u, 0.005f, 0.008f);
   d.moveBobAmplitude = randBetween(seed, 0xB975u, 0.020f, 0.028f);
 
-  // derived
   d.saddleHeight = d.barrelCenterY + d.bodyHeight * 0.55f + d.saddleThickness;
 
   return d;
 }
 
-HorseVariant makeHorseVariant(uint32_t seed,
-                              const QVector3D& leatherBase,
-                              const QVector3D& clothBase) {
+HorseVariant makeHorseVariant(uint32_t seed, const QVector3D &leatherBase,
+                              const QVector3D &clothBase) {
   HorseVariant v;
 
-  // pick a believable coat given a pseudo hue bucket
   float coatHue = hash01(seed ^ 0x23456u);
   if (coatHue < 0.18f) {
-    v.coatColor = QVector3D(0.70f, 0.68f, 0.63f);    // grey
+    v.coatColor = QVector3D(0.70f, 0.68f, 0.63f);
   } else if (coatHue < 0.38f) {
-    v.coatColor = QVector3D(0.40f, 0.30f, 0.22f);    // bay
+    v.coatColor = QVector3D(0.40f, 0.30f, 0.22f);
   } else if (coatHue < 0.65f) {
-    v.coatColor = QVector3D(0.28f, 0.22f, 0.19f);    // dark bay/brown
+    v.coatColor = QVector3D(0.28f, 0.22f, 0.19f);
   } else if (coatHue < 0.85f) {
-    v.coatColor = QVector3D(0.18f, 0.15f, 0.13f);    // near-black
+    v.coatColor = QVector3D(0.18f, 0.15f, 0.13f);
   } else {
-    v.coatColor = QVector3D(0.48f, 0.42f, 0.39f);    // light brown
+    v.coatColor = QVector3D(0.48f, 0.42f, 0.39f);
   }
 
-  // facial blaze chance nudges overall tone slightly lighter
   float blazeChance = hash01(seed ^ 0x1122u);
   if (blazeChance > 0.82f) {
     v.coatColor = lerpVec(v.coatColor, QVector3D(0.92f, 0.92f, 0.90f), 0.25f);
   }
 
-  // mane/tail typically darker than coat
   v.maneColor = lerpVec(v.coatColor, QVector3D(0.10f, 0.09f, 0.08f),
                         randBetween(seed, 0x3344u, 0.55f, 0.85f));
   v.tailColor = lerpVec(v.maneColor, v.coatColor, 0.35f);
 
-  // muzzle & hoof tone
   v.muzzleColor = lerpVec(v.coatColor, QVector3D(0.18f, 0.14f, 0.12f), 0.65f);
-  v.hoofColor   = lerpVec(QVector3D(0.16f, 0.14f, 0.12f),
-                          QVector3D(0.40f, 0.35f, 0.32f),
-                          randBetween(seed, 0x5566u, 0.15f, 0.65f));
+  v.hoofColor =
+      lerpVec(QVector3D(0.16f, 0.14f, 0.12f), QVector3D(0.40f, 0.35f, 0.32f),
+              randBetween(seed, 0x5566u, 0.15f, 0.65f));
 
-  // leather & tack tints
   float leatherTone = randBetween(seed, 0x7788u, 0.78f, 0.96f);
-  float tackTone    = randBetween(seed, 0x88AAu, 0.58f, 0.78f);
+  float tackTone = randBetween(seed, 0x88AAu, 0.58f, 0.78f);
   QVector3D leatherTint = leatherBase * leatherTone;
-  QVector3D tackTint    = leatherBase * tackTone;
+  QVector3D tackTint = leatherBase * tackTone;
   if (blazeChance > 0.90f) {
-    // chance of darker/burnished tack
+
     tackTint = lerpVec(tackTint, QVector3D(0.18f, 0.19f, 0.22f), 0.25f);
   }
   v.saddleColor = leatherTint;
-  v.tackColor   = tackTint;
+  v.tackColor = tackTint;
 
-  // blanket tone from a cloth base
   v.blanketColor = clothBase * randBetween(seed, 0x99B0u, 0.92f, 1.05f);
 
   return v;
 }
 
-HorseProfile makeHorseProfile(uint32_t seed,
-                              const QVector3D& leatherBase,
-                              const QVector3D& clothBase) {
+HorseProfile makeHorseProfile(uint32_t seed, const QVector3D &leatherBase,
+                              const QVector3D &clothBase) {
   HorseProfile profile;
-  profile.dims    = makeHorseDimensions(seed);
+  profile.dims = makeHorseDimensions(seed);
   profile.variant = makeHorseVariant(seed, leatherBase, clothBase);
 
-  // gait
-  profile.gait.cycleTime    = randBetween(seed, 0xAA12u, 0.50f, 0.58f);
-  profile.gait.frontLegPhase= randBetween(seed, 0xBB34u, 0.10f, 0.18f);
-  float diagonalLead        = randBetween(seed, 0xCC56u, 0.48f, 0.56f);
-  profile.gait.rearLegPhase = std::fmod(profile.gait.frontLegPhase + diagonalLead, 1.0f);
-  profile.gait.strideSwing  = randBetween(seed, 0xDD78u, 0.18f, 0.24f);
-  profile.gait.strideLift   = randBetween(seed, 0xEE9Au, 0.11f, 0.15f);
+  profile.gait.cycleTime = randBetween(seed, 0xAA12u, 0.50f, 0.58f);
+  profile.gait.frontLegPhase = randBetween(seed, 0xBB34u, 0.10f, 0.18f);
+  float diagonalLead = randBetween(seed, 0xCC56u, 0.48f, 0.56f);
+  profile.gait.rearLegPhase =
+      std::fmod(profile.gait.frontLegPhase + diagonalLead, 1.0f);
+  profile.gait.strideSwing = randBetween(seed, 0xDD78u, 0.18f, 0.24f);
+  profile.gait.strideLift = randBetween(seed, 0xEE9Au, 0.11f, 0.15f);
 
   return profile;
 }
 
-// -----------------------------------
-// rendering
-// -----------------------------------
-
-void HorseRenderer::render(const DrawContext& ctx,
-                           const AnimationInputs& anim,
-                           const HorseProfile& profile,
-                           ISubmitter& out) const {
-  const HorseDimensions& d = profile.dims;
-  const HorseVariant&    v = profile.variant;
-  const HorseGait&       g = profile.gait;
+void HorseRenderer::render(const DrawContext &ctx, const AnimationInputs &anim,
+                           const HorseProfile &profile, ISubmitter &out) const {
+  const HorseDimensions &d = profile.dims;
+  const HorseVariant &v = profile.variant;
+  const HorseGait &g = profile.gait;
 
   float phase = 0.0f;
-  float bob   = 0.0f;
+  float bob = 0.0f;
 
   if (anim.isMoving) {
     float cycle = std::max(0.20f, g.cycleTime);
     phase = std::fmod(anim.time / cycle, 1.0f);
-    bob   = std::sin(phase * 2.0f * kPi) * d.moveBobAmplitude;
+    bob = std::sin(phase * 2.0f * kPi) * d.moveBobAmplitude;
   } else {
     phase = std::fmod(anim.time * 0.25f, 1.0f);
-    bob   = std::sin(phase * 2.0f * kPi) * d.idleBobAmplitude;
+    bob = std::sin(phase * 2.0f * kPi) * d.idleBobAmplitude;
   }
 
-  // subtle head nod synced with gait
   float headNod = anim.isMoving ? std::sin((phase + 0.25f) * 2.0f * kPi) * 0.04f
                                 : std::sin(anim.time * 1.5f) * 0.01f;
 
-  // derive pseudo-random per-variant toggles (socks, star/blaze)
   uint32_t vhash = colorHash(v.coatColor);
   float sockChanceFL = hash01(vhash ^ 0x101u);
   float sockChanceFR = hash01(vhash ^ 0x202u);
@@ -236,48 +207,48 @@ void HorseRenderer::render(const DrawContext& ctx,
   bool hasBlaze = hash01(vhash ^ 0x505u) > 0.82f;
 
   QVector3D barrelCenter(0.0f, d.barrelCenterY + bob, 0.0f);
-  QVector3D chestCenter = barrelCenter + QVector3D(0.0f, d.bodyHeight * 0.12f,  d.bodyLength * 0.34f);
-  QVector3D rumpCenter  = barrelCenter + QVector3D(0.0f, d.bodyHeight * 0.08f, -d.bodyLength * 0.36f);
-  QVector3D bellyCenter = barrelCenter + QVector3D(0.0f,-d.bodyHeight * 0.35f, -d.bodyLength * 0.05f);
+  QVector3D chestCenter = barrelCenter + QVector3D(0.0f, d.bodyHeight * 0.12f,
+                                                   d.bodyLength * 0.34f);
+  QVector3D rumpCenter = barrelCenter + QVector3D(0.0f, d.bodyHeight * 0.08f,
+                                                  -d.bodyLength * 0.36f);
+  QVector3D bellyCenter = barrelCenter + QVector3D(0.0f, -d.bodyHeight * 0.35f,
+                                                   -d.bodyLength * 0.05f);
 
-  // ----------------
-  // BODY MASS (torso)
-  // ----------------
-
-  // chest (pectorals / withers influence slightly darker top)
   {
     QMatrix4x4 chest = ctx.model;
     chest.translate(chestCenter);
-    chest.scale(d.bodyWidth * 1.12f, d.bodyHeight * 0.95f, d.bodyLength * 0.36f);
+    chest.scale(d.bodyWidth * 1.12f, d.bodyHeight * 0.95f,
+                d.bodyLength * 0.36f);
     out.mesh(getUnitSphere(), chest, v.coatColor * 1.03f, nullptr, 1.0f);
   }
 
-  // withers bulge
   {
     QMatrix4x4 withers = ctx.model;
-    withers.translate(chestCenter + QVector3D(0.0f, d.bodyHeight * 0.55f, -d.bodyLength * 0.03f));
-    withers.scale(d.bodyWidth * 0.75f, d.bodyHeight * 0.35f, d.bodyLength * 0.18f);
+    withers.translate(chestCenter + QVector3D(0.0f, d.bodyHeight * 0.55f,
+                                              -d.bodyLength * 0.03f));
+    withers.scale(d.bodyWidth * 0.75f, d.bodyHeight * 0.35f,
+                  d.bodyLength * 0.18f);
     out.mesh(getUnitSphere(), withers, v.coatColor * 0.96f, nullptr, 1.0f);
   }
 
-  // belly (lighter underside / countershading)
   {
     QMatrix4x4 belly = ctx.model;
     belly.translate(bellyCenter);
-    belly.scale(d.bodyWidth * 0.98f, d.bodyHeight * 0.64f, d.bodyLength * 0.40f);
+    belly.scale(d.bodyWidth * 0.98f, d.bodyHeight * 0.64f,
+                d.bodyLength * 0.40f);
     out.mesh(getUnitSphere(), belly, v.coatColor * 1.06f, nullptr, 1.0f);
   }
 
-  // ribcage lateral rounding
   for (int i = 0; i < 2; ++i) {
-    float side = (i==0) ? 1.0f : -1.0f;
+    float side = (i == 0) ? 1.0f : -1.0f;
     QMatrix4x4 ribs = ctx.model;
-    ribs.translate(barrelCenter + QVector3D(side * d.bodyWidth * 0.90f, -d.bodyHeight * 0.10f, -d.bodyLength * 0.05f));
+    ribs.translate(barrelCenter + QVector3D(side * d.bodyWidth * 0.90f,
+                                            -d.bodyHeight * 0.10f,
+                                            -d.bodyLength * 0.05f));
     ribs.scale(d.bodyWidth * 0.38f, d.bodyHeight * 0.42f, d.bodyLength * 0.30f);
     out.mesh(getUnitSphere(), ribs, v.coatColor * 1.00f, nullptr, 1.0f);
   }
 
-  // rump / croup
   {
     QMatrix4x4 rump = ctx.model;
     rump.translate(rumpCenter);
@@ -285,84 +256,122 @@ void HorseRenderer::render(const DrawContext& ctx,
     out.mesh(getUnitSphere(), rump, v.coatColor * 0.98f, nullptr, 1.0f);
   }
 
-  // hip bulges
   for (int i = 0; i < 2; ++i) {
-    float side = (i==0) ? 1.0f : -1.0f;
+    float side = (i == 0) ? 1.0f : -1.0f;
     QMatrix4x4 hip = ctx.model;
-    hip.translate(rumpCenter + QVector3D(side * d.bodyWidth * 0.95f, -d.bodyHeight * 0.10f, -d.bodyLength * 0.08f));
+    hip.translate(rumpCenter + QVector3D(side * d.bodyWidth * 0.95f,
+                                         -d.bodyHeight * 0.10f,
+                                         -d.bodyLength * 0.08f));
     hip.scale(d.bodyWidth * 0.45f, d.bodyHeight * 0.42f, d.bodyLength * 0.26f);
     out.mesh(getUnitSphere(), hip, v.coatColor * 0.99f, nullptr, 1.0f);
   }
 
-  // ----------------
-  // NECK + HEAD
-  // ----------------
+  QVector3D neckBase =
+      chestCenter + QVector3D(0.0f, d.bodyHeight * 0.38f, d.bodyLength * 0.06f);
+  QVector3D neckTop = neckBase + QVector3D(0.0f, d.neckRise, d.neckLength);
+  float neckRadius = d.bodyWidth * 0.42f;
 
-  QVector3D neckBase = chestCenter + QVector3D(0.0f, d.bodyHeight * 0.38f, d.bodyLength * 0.06f);
-  QVector3D neckTop  = neckBase + QVector3D(0.0f, d.neckRise, d.neckLength);
-  float neckRadius   = d.bodyWidth * 0.42f;
-
-  // neck S-curve: two blended cylinders to avoid a straight "pipe"
-  QVector3D neckMid = lerpVec(neckBase, neckTop, 0.55f) + QVector3D(0.0f, d.bodyHeight * 0.02f, d.bodyLength * 0.02f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, neckBase, neckMid, neckRadius * 1.00f),
+  QVector3D neckMid =
+      lerpVec(neckBase, neckTop, 0.55f) +
+      QVector3D(0.0f, d.bodyHeight * 0.02f, d.bodyLength * 0.02f);
+  out.mesh(getUnitCylinder(),
+           cylinderBetween(ctx.model, neckBase, neckMid, neckRadius * 1.00f),
            v.coatColor * 1.03f, nullptr, 1.0f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, neckMid, neckTop, neckRadius * 0.86f),
+  out.mesh(getUnitCylinder(),
+           cylinderBetween(ctx.model, neckMid, neckTop, neckRadius * 0.86f),
            v.coatColor * 1.04f, nullptr, 1.0f);
 
-  QVector3D headCenter = neckTop + QVector3D(0.0f, d.headHeight * (0.10f - headNod * 0.15f), d.headLength * 0.40f);
-  // skull mass (forehead)
+  QVector3D headCenter =
+      neckTop + QVector3D(0.0f, d.headHeight * (0.10f - headNod * 0.15f),
+                          d.headLength * 0.40f);
+
   {
     QMatrix4x4 skull = ctx.model;
-    skull.translate(headCenter + QVector3D(0.0f, d.headHeight * 0.10f, -d.headLength * 0.10f));
-    skull.scale(d.headWidth * 0.95f, d.headHeight * 0.90f, d.headLength * 0.80f);
+    skull.translate(headCenter + QVector3D(0.0f, d.headHeight * 0.10f,
+                                           -d.headLength * 0.10f));
+    skull.scale(d.headWidth * 0.95f, d.headHeight * 0.90f,
+                d.headLength * 0.80f);
     out.mesh(getUnitSphere(), skull, v.coatColor * 1.05f, nullptr, 1.0f);
   }
-  // cheek / jaw mass
-  for (int i=0;i<2;++i){
-    float side = (i==0)?1.0f:-1.0f;
+
+  for (int i = 0; i < 2; ++i) {
+    float side = (i == 0) ? 1.0f : -1.0f;
     QMatrix4x4 cheek = ctx.model;
-    cheek.translate(headCenter + QVector3D(side * d.headWidth * 0.55f, -d.headHeight * 0.15f, 0.0f));
-    cheek.scale(d.headWidth * 0.45f, d.headHeight * 0.50f, d.headLength * 0.60f);
+    cheek.translate(headCenter + QVector3D(side * d.headWidth * 0.55f,
+                                           -d.headHeight * 0.15f, 0.0f));
+    cheek.scale(d.headWidth * 0.45f, d.headHeight * 0.50f,
+                d.headLength * 0.60f);
     out.mesh(getUnitSphere(), cheek, v.coatColor * 1.02f, nullptr, 1.0f);
   }
 
-  // muzzle (tapered and slightly down-tilted)
-  QVector3D muzzleCenter = headCenter + QVector3D(0.0f, -d.headHeight * 0.18f, d.headLength * 0.58f);
+  QVector3D muzzleCenter =
+      headCenter + QVector3D(0.0f, -d.headHeight * 0.18f, d.headLength * 0.58f);
   {
     QMatrix4x4 muzzle = ctx.model;
-    muzzle.translate(muzzleCenter + QVector3D(0.0f, -d.headHeight * 0.05f, 0.0f));
-    muzzle.scale(d.headWidth * 0.68f, d.headHeight * 0.60f, d.muzzleLength * 1.05f);
+    muzzle.translate(muzzleCenter +
+                     QVector3D(0.0f, -d.headHeight * 0.05f, 0.0f));
+    muzzle.scale(d.headWidth * 0.68f, d.headHeight * 0.60f,
+                 d.muzzleLength * 1.05f);
     out.mesh(getUnitSphere(), muzzle, v.muzzleColor, nullptr, 1.0f);
   }
 
-  // nostrils (dark inward cones)
   {
-    QVector3D nostrilBase = muzzleCenter + QVector3D(0.0f, -d.headHeight * 0.02f, d.muzzleLength * 0.60f);
-    QVector3D leftBase  = nostrilBase + QVector3D( d.headWidth * 0.26f, 0.0f, 0.0f);
-    QVector3D rightBase = nostrilBase + QVector3D(-d.headWidth * 0.26f, 0.0f, 0.0f);
-    QVector3D inward    = QVector3D(0.0f, -d.headHeight * 0.02f, d.muzzleLength * -0.30f);
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, leftBase  + inward, leftBase,  d.headWidth * 0.11f), darken(v.muzzleColor, 0.6f), nullptr, 1.0f);
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, rightBase + inward, rightBase, d.headWidth * 0.11f), darken(v.muzzleColor, 0.6f), nullptr, 1.0f);
+    QVector3D nostrilBase =
+        muzzleCenter +
+        QVector3D(0.0f, -d.headHeight * 0.02f, d.muzzleLength * 0.60f);
+    QVector3D leftBase =
+        nostrilBase + QVector3D(d.headWidth * 0.26f, 0.0f, 0.0f);
+    QVector3D rightBase =
+        nostrilBase + QVector3D(-d.headWidth * 0.26f, 0.0f, 0.0f);
+    QVector3D inward =
+        QVector3D(0.0f, -d.headHeight * 0.02f, d.muzzleLength * -0.30f);
+    out.mesh(
+        getUnitCone(),
+        coneFromTo(ctx.model, leftBase + inward, leftBase, d.headWidth * 0.11f),
+        darken(v.muzzleColor, 0.6f), nullptr, 1.0f);
+    out.mesh(getUnitCone(),
+             coneFromTo(ctx.model, rightBase + inward, rightBase,
+                        d.headWidth * 0.11f),
+             darken(v.muzzleColor, 0.6f), nullptr, 1.0f);
   }
 
-  // ears with slight flick (independent phase)
   float earFlickL = std::sin(anim.time * 1.7f + 1.3f) * 0.15f;
   float earFlickR = std::sin(anim.time * 1.9f + 2.1f) * -0.12f;
 
-  QVector3D earBaseLeft  = headCenter + QVector3D( d.headWidth * 0.45f, d.headHeight * 0.42f, -d.headLength * 0.20f);
-  QVector3D earTipLeft   = earBaseLeft + rotateAroundY(QVector3D(d.headWidth * 0.08f, d.headHeight * 0.42f, -d.headLength * 0.10f), earFlickL);
-  QVector3D earBaseRight = headCenter + QVector3D(-d.headWidth * 0.45f, d.headHeight * 0.42f, -d.headLength * 0.20f);
-  QVector3D earTipRight  = earBaseRight + rotateAroundY(QVector3D(-d.headWidth * 0.08f, d.headHeight * 0.42f, -d.headLength * 0.10f), earFlickR);
-
-  out.mesh(getUnitCone(), coneFromTo(ctx.model, earTipLeft,  earBaseLeft,  d.headWidth * 0.11f), v.maneColor, nullptr, 1.0f);
-  out.mesh(getUnitCone(), coneFromTo(ctx.model, earTipRight, earBaseRight, d.headWidth * 0.11f), v.maneColor, nullptr, 1.0f);
-
-  // eyes: dark globe + pupil + tiny highlight
-  QVector3D eyeLeft  = headCenter + QVector3D( d.headWidth * 0.48f, d.headHeight * 0.10f, d.headLength * 0.05f);
-  QVector3D eyeRight = headCenter + QVector3D(-d.headWidth * 0.48f, d.headHeight * 0.10f, d.headLength * 0.05f);
+  QVector3D earBaseLeft =
+      headCenter + QVector3D(d.headWidth * 0.45f, d.headHeight * 0.42f,
+                             -d.headLength * 0.20f);
+  QVector3D earTipLeft =
+      earBaseLeft +
+      rotateAroundY(QVector3D(d.headWidth * 0.08f, d.headHeight * 0.42f,
+                              -d.headLength * 0.10f),
+                    earFlickL);
+  QVector3D earBaseRight =
+      headCenter + QVector3D(-d.headWidth * 0.45f, d.headHeight * 0.42f,
+                             -d.headLength * 0.20f);
+  QVector3D earTipRight =
+      earBaseRight +
+      rotateAroundY(QVector3D(-d.headWidth * 0.08f, d.headHeight * 0.42f,
+                              -d.headLength * 0.10f),
+                    earFlickR);
+
+  out.mesh(getUnitCone(),
+           coneFromTo(ctx.model, earTipLeft, earBaseLeft, d.headWidth * 0.11f),
+           v.maneColor, nullptr, 1.0f);
+  out.mesh(
+      getUnitCone(),
+      coneFromTo(ctx.model, earTipRight, earBaseRight, d.headWidth * 0.11f),
+      v.maneColor, nullptr, 1.0f);
+
+  QVector3D eyeLeft =
+      headCenter + QVector3D(d.headWidth * 0.48f, d.headHeight * 0.10f,
+                             d.headLength * 0.05f);
+  QVector3D eyeRight =
+      headCenter + QVector3D(-d.headWidth * 0.48f, d.headHeight * 0.10f,
+                             d.headLength * 0.05f);
   QVector3D eyeBaseColor(0.10f, 0.10f, 0.10f);
 
-  auto drawEye = [&](const QVector3D& pos) {
+  auto drawEye = [&](const QVector3D &pos) {
     {
       QMatrix4x4 eye = ctx.model;
       eye.translate(pos);
@@ -370,121 +379,128 @@ void HorseRenderer::render(const DrawContext& ctx,
       out.mesh(getUnitSphere(), eye, eyeBaseColor, nullptr, 1.0f);
     }
     {
-      // pupil
+
       QMatrix4x4 pupil = ctx.model;
       pupil.translate(pos + QVector3D(0.0f, 0.0f, d.headWidth * 0.04f));
       pupil.scale(d.headWidth * 0.05f);
-      out.mesh(getUnitSphere(), pupil, QVector3D(0.03f, 0.03f, 0.03f), nullptr, 1.0f);
+      out.mesh(getUnitSphere(), pupil, QVector3D(0.03f, 0.03f, 0.03f), nullptr,
+               1.0f);
     }
     {
-      // specular highlight
+
       QMatrix4x4 spec = ctx.model;
-      spec.translate(pos + QVector3D(d.headWidth * 0.03f, d.headWidth * 0.03f, d.headWidth * 0.03f));
+      spec.translate(pos + QVector3D(d.headWidth * 0.03f, d.headWidth * 0.03f,
+                                     d.headWidth * 0.03f));
       spec.scale(d.headWidth * 0.02f);
-      out.mesh(getUnitSphere(), spec, QVector3D(0.95f, 0.95f, 0.95f), nullptr, 1.0f);
+      out.mesh(getUnitSphere(), spec, QVector3D(0.95f, 0.95f, 0.95f), nullptr,
+               1.0f);
     }
   };
   drawEye(eyeLeft);
   drawEye(eyeRight);
 
-  // subtle facial blaze/star (without new interfaces)
   if (hasBlaze) {
     QMatrix4x4 blaze = ctx.model;
-    blaze.translate(headCenter + QVector3D(0.0f, d.headHeight * 0.15f, d.headLength * 0.10f));
-    blaze.scale(d.headWidth * 0.22f, d.headHeight * 0.32f, d.headLength * 0.10f);
-    out.mesh(getUnitSphere(), blaze, QVector3D(0.92f, 0.92f, 0.90f), nullptr, 1.0f);
+    blaze.translate(headCenter + QVector3D(0.0f, d.headHeight * 0.15f,
+                                           d.headLength * 0.10f));
+    blaze.scale(d.headWidth * 0.22f, d.headHeight * 0.32f,
+                d.headLength * 0.10f);
+    out.mesh(getUnitSphere(), blaze, QVector3D(0.92f, 0.92f, 0.90f), nullptr,
+             1.0f);
   }
 
-  // mane: more segments, gentle gravity + gait sway
-  QVector3D maneRoot = neckTop + QVector3D(0.0f, d.headHeight * 0.20f, -d.headLength * 0.20f);
+  QVector3D maneRoot =
+      neckTop + QVector3D(0.0f, d.headHeight * 0.20f, -d.headLength * 0.20f);
   for (int i = 0; i < 12; ++i) {
     float t = i / 11.0f;
     QVector3D segStart = lerpVec(maneRoot, neckBase, t);
     segStart.setY(segStart.y() + (0.07f - t * 0.05f));
-    float sway = (anim.isMoving ? std::sin((phase + t * 0.15f) * 2.0f * kPi) * 0.04f
-                                : std::sin((anim.time * 0.8f + t * 2.3f)) * 0.02f);
-    QVector3D segEnd = segStart + QVector3D(sway, 0.07f - t * 0.05f, -0.05f - t * 0.03f);
+    float sway =
+        (anim.isMoving ? std::sin((phase + t * 0.15f) * 2.0f * kPi) * 0.04f
+                       : std::sin((anim.time * 0.8f + t * 2.3f)) * 0.02f);
+    QVector3D segEnd =
+        segStart + QVector3D(sway, 0.07f - t * 0.05f, -0.05f - t * 0.03f);
     out.mesh(getUnitCylinder(),
-             cylinderBetween(ctx.model, segStart, segEnd, d.headWidth * (0.10f * (1.0f - t * 0.4f))),
+             cylinderBetween(ctx.model, segStart, segEnd,
+                             d.headWidth * (0.10f * (1.0f - t * 0.4f))),
              v.maneColor * (0.98f + t * 0.05f), nullptr, 1.0f);
   }
 
-  // ----------------
-  // TAIL
-  // ----------------
-  QVector3D tailBase = rumpCenter + QVector3D(0.0f, d.bodyHeight * 0.36f, -d.bodyLength * 0.48f);
+  QVector3D tailBase =
+      rumpCenter + QVector3D(0.0f, d.bodyHeight * 0.36f, -d.bodyLength * 0.48f);
   for (int i = 0; i < 8; ++i) {
     float t = i / 8.0f;
-    float swing = (anim.isMoving
-                   ? std::sin((phase + t * 0.10f) * 2.0f * kPi) * (0.05f + 0.02f * (1.0f - t))
-                   : std::sin((phase * 0.7f + t * 0.20f) * 2.0f * kPi) * 0.04f);
-    QVector3D segStart = tailBase + QVector3D(swing, -i * 0.06f, -t * d.tailLength * 0.65f);
-    QVector3D segEnd   = segStart + QVector3D(swing * 0.6f, -0.08f - t * 0.05f, -d.tailLength * 0.16f);
+    float swing =
+        (anim.isMoving
+             ? std::sin((phase + t * 0.10f) * 2.0f * kPi) *
+                   (0.05f + 0.02f * (1.0f - t))
+             : std::sin((phase * 0.7f + t * 0.20f) * 2.0f * kPi) * 0.04f);
+    QVector3D segStart =
+        tailBase + QVector3D(swing, -i * 0.06f, -t * d.tailLength * 0.65f);
+    QVector3D segEnd = segStart + QVector3D(swing * 0.6f, -0.08f - t * 0.05f,
+                                            -d.tailLength * 0.16f);
     float radius = d.bodyWidth * (0.22f - 0.025f * i);
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, segStart, segEnd, radius),
+    out.mesh(getUnitCylinder(),
+             cylinderBetween(ctx.model, segStart, segEnd, radius),
              v.tailColor * (0.95f - 0.05f * i), nullptr, 1.0f);
   }
 
-  // ----------------
-  // LEGS
-  // ----------------
-  auto drawLeg = [&](const QVector3D& anchor, float lateralSign,
-                     float forwardBias, float phaseOffset,
-                     float sockChance) {
+  auto drawLeg = [&](const QVector3D &anchor, float lateralSign,
+                     float forwardBias, float phaseOffset, float sockChance) {
     float legPhase = std::fmod(phase + phaseOffset, 1.0f);
-    float stride   = 0.0f;
-    float lift     = 0.0f;
+    float stride = 0.0f;
+    float lift = 0.0f;
 
     if (anim.isMoving) {
       float angle = legPhase * 2.0f * kPi;
       stride = std::sin(angle) * g.strideSwing + forwardBias;
       float liftRaw = std::sin(angle);
-      lift = liftRaw > 0.0f ? liftRaw * g.strideLift : liftRaw * g.strideLift * 0.22f;
+      lift = liftRaw > 0.0f ? liftRaw * g.strideLift
+                            : liftRaw * g.strideLift * 0.22f;
     } else {
       float idle = std::sin(legPhase * 2.0f * kPi);
       stride = idle * g.strideSwing * 0.06f + forwardBias;
-      lift   = idle * d.idleBobAmplitude * 2.0f;
+      lift = idle * d.idleBobAmplitude * 2.0f;
     }
 
-  // joint chain with more anatomical placement
-  bool tightenLegs = anim.isMoving;
-  float shoulderOut = d.bodyWidth * (tightenLegs ? 0.48f : 0.58f);
-  QVector3D shoulder = anchor + QVector3D(lateralSign * shoulderOut,
-                      0.05f + lift * 0.05f, stride);
-  bool isRear = (forwardBias < 0.0f);
-
-  // Full gallop cycle parameters
-  float gallopAngle = legPhase * 2.0f * kPi;
-  float hipSwing = anim.isMoving ? std::sin(gallopAngle) : 0.0f;
-  float liftFactor = anim.isMoving ? std::max(0.0f, std::sin(gallopAngle + (isRear ? 0.35f : -0.25f))) : 0.0f;
-
-  // Move whole hip forward/back slightly for stride reach
-  shoulder.setZ(shoulder.z() + hipSwing * (isRear ? -0.12f : 0.10f));
-  if (tightenLegs)
-    shoulder.setX(shoulder.x() - lateralSign * liftFactor * 0.05f);
-
-  // Thigh: majority of leg length; hips pitch heavily during gallop
-  float thighLength = d.legLength * 0.55f;
-  float hipPitch = hipSwing * (isRear ? 0.70f : 0.55f);
-  float inwardLean = tightenLegs ? (-0.06f - liftFactor * 0.04f) : -0.015f;
-  QVector3D thighDir(lateralSign * inwardLean,
-                       -std::cos(hipPitch) * 0.90f,
+    bool tightenLegs = anim.isMoving;
+    float shoulderOut = d.bodyWidth * (tightenLegs ? 0.48f : 0.58f);
+    QVector3D shoulder = anchor + QVector3D(lateralSign * shoulderOut,
+                                            0.05f + lift * 0.05f, stride);
+    bool isRear = (forwardBias < 0.0f);
+
+    float gallopAngle = legPhase * 2.0f * kPi;
+    float hipSwing = anim.isMoving ? std::sin(gallopAngle) : 0.0f;
+    float liftFactor =
+        anim.isMoving
+            ? std::max(0.0f, std::sin(gallopAngle + (isRear ? 0.35f : -0.25f)))
+            : 0.0f;
+
+    shoulder.setZ(shoulder.z() + hipSwing * (isRear ? -0.12f : 0.10f));
+    if (tightenLegs)
+      shoulder.setX(shoulder.x() - lateralSign * liftFactor * 0.05f);
+
+    float thighLength = d.legLength * 0.55f;
+    float hipPitch = hipSwing * (isRear ? 0.70f : 0.55f);
+    float inwardLean = tightenLegs ? (-0.06f - liftFactor * 0.04f) : -0.015f;
+    QVector3D thighDir(lateralSign * inwardLean, -std::cos(hipPitch) * 0.90f,
                        (isRear ? -1.0f : 1.0f) * std::sin(hipPitch) * 0.65f);
     if (thighDir.lengthSquared() > 1e-6f)
       thighDir.normalize();
 
-  QVector3D knee = shoulder + thighDir * thighLength;
-  knee.setY(knee.y() + liftFactor * thighLength * 0.28f);
+    QVector3D knee = shoulder + thighDir * thighLength;
+    knee.setY(knee.y() + liftFactor * thighLength * 0.28f);
 
-  // Knee flexion controls cannon orientation
-  float kneeFlex = anim.isMoving ? clamp01(std::sin(gallopAngle + (isRear ? 0.65f : -0.45f)) * 0.7f + 0.45f)
-                   : 0.35f;
+    float kneeFlex =
+        anim.isMoving
+            ? clamp01(std::sin(gallopAngle + (isRear ? 0.65f : -0.45f)) * 0.7f +
+                      0.45f)
+            : 0.35f;
 
     float forearmLength = d.legLength * 0.30f;
     float bendCos = std::cos(kneeFlex * kPi * 0.5f);
     float bendSin = std::sin(kneeFlex * kPi * 0.5f);
-    QVector3D forearmDir(0.0f,
-                         -bendCos,
+    QVector3D forearmDir(0.0f, -bendCos,
                          (isRear ? -1.0f : 1.0f) * bendSin * 0.85f);
     if (forearmDir.lengthSquared() < 1e-6f)
       forearmDir = QVector3D(0.0f, -1.0f, 0.0f);
@@ -492,94 +508,103 @@ void HorseRenderer::render(const DrawContext& ctx,
       forearmDir.normalize();
     QVector3D cannon = knee + forearmDir * forearmLength;
 
-  float pasternLength = d.legLength * 0.12f;
-  QVector3D fetlock = cannon + QVector3D(0.0f, -pasternLength, 0.0f);
+    float pasternLength = d.legLength * 0.12f;
+    QVector3D fetlock = cannon + QVector3D(0.0f, -pasternLength, 0.0f);
 
-    // hoof angle (toes down slightly during swing)
-    float hoofPitch = anim.isMoving ? (-0.20f + std::sin(legPhase * 2.0f * kPi + (isRear?0.2f:-0.1f)) * 0.10f)
-                                    : 0.0f;
+    float hoofPitch = anim.isMoving
+                          ? (-0.20f + std::sin(legPhase * 2.0f * kPi +
+                                               (isRear ? 0.2f : -0.1f)) *
+                                          0.10f)
+                          : 0.0f;
     QVector3D hoofDir = rotateAroundZ(QVector3D(0.0f, -1.0f, 0.0f), hoofPitch);
     QVector3D hoofTop = fetlock;
     QVector3D hoofBottom = hoofTop + hoofDir * d.hoofHeight;
 
-    // MASSIVE muscular thigh with convex profile
     float shoulderR = d.bodyWidth * (isRear ? 0.48f : 0.42f);
     float thighBellyR = d.bodyWidth * (isRear ? 0.60f : 0.54f);
     float kneeR = d.bodyWidth * 0.26f;
     float cannonR = d.bodyWidth * 0.19f;
     float pasternR = d.bodyWidth * 0.14f;
 
-  QVector3D thighBelly = shoulder + (knee - shoulder) * 0.62f;
+    QVector3D thighBelly = shoulder + (knee - shoulder) * 0.62f;
 
-    // Upper thigh - small at hip growing to belly
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, thighBelly, shoulder, thighBellyR),
+    out.mesh(getUnitCone(),
+             coneFromTo(ctx.model, thighBelly, shoulder, thighBellyR),
              v.coatColor * 1.01f, nullptr, 1.0f);
 
     {
       QMatrix4x4 muscle = ctx.model;
-      muscle.translate(thighBelly + QVector3D(0.0f, 0.0f, isRear ? -0.015f : 0.020f));
+      muscle.translate(thighBelly +
+                       QVector3D(0.0f, 0.0f, isRear ? -0.015f : 0.020f));
       muscle.scale(thighBellyR * QVector3D(1.05f, 0.85f, 0.92f));
       out.mesh(getUnitSphere(), muscle, v.coatColor * 1.04f, nullptr, 1.0f);
     }
 
-    // Lower thigh - belly down to knee, tapering
     out.mesh(getUnitCone(), coneFromTo(ctx.model, knee, thighBelly, kneeR),
              v.coatColor * 0.98f, nullptr, 1.0f);
 
     {
-      // Prominent knee joint with patella hint
+
       QMatrix4x4 joint = ctx.model;
       joint.translate(knee + QVector3D(0.0f, 0.0f, isRear ? -0.025f : 0.030f));
       joint.scale(QVector3D(kneeR * 1.22f, kneeR * 1.08f, kneeR * 1.40f));
       out.mesh(getUnitSphere(), joint, v.coatColor * 0.95f, nullptr, 1.0f);
     }
 
-    // Forearm/cannon - power stroke
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, knee, cannon, cannonR),
+    out.mesh(getUnitCylinder(),
+             cylinderBetween(ctx.model, knee, cannon, cannonR),
              v.coatColor * 0.94f, nullptr, 1.0f);
-    
+
     {
-      // Fetlock joint
+
       QMatrix4x4 joint = ctx.model;
       joint.translate(fetlock);
       joint.scale(pasternR * 1.18f);
       out.mesh(getUnitSphere(), joint, v.coatColor * 0.95f, nullptr, 1.0f);
     }
 
-    // distal coloring (socks): lighten towards hoof if selected
-    float sock = sockChance > 0.78f ? 1.0f : (sockChance > 0.58f ? 0.55f : 0.0f);
-    QVector3D distalColor = (sock > 0.0f) ? lighten(v.coatColor, 1.18f) : v.coatColor * 0.92f;
+    float sock =
+        sockChance > 0.78f ? 1.0f : (sockChance > 0.58f ? 0.55f : 0.0f);
+    QVector3D distalColor =
+        (sock > 0.0f) ? lighten(v.coatColor, 1.18f) : v.coatColor * 0.92f;
     float tSock = smoothstep(0.0f, 1.0f, sock);
 
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, cannon, fetlock, pasternR * 1.05f),
-             lerpVec(v.coatColor*0.94f, distalColor, tSock * 0.8f), nullptr, 1.0f);
+    out.mesh(getUnitCylinder(),
+             cylinderBetween(ctx.model, cannon, fetlock, pasternR * 1.05f),
+             lerpVec(v.coatColor * 0.94f, distalColor, tSock * 0.8f), nullptr,
+             1.0f);
 
-    // hoof: short cone + flat bottom via tiny sphere suggestion
     QVector3D hoofColor = v.hoofColor;
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, hoofTop, hoofBottom, pasternR * 0.95f),
+    out.mesh(getUnitCylinder(),
+             cylinderBetween(ctx.model, hoofTop, hoofBottom, pasternR * 0.95f),
              hoofColor, nullptr, 1.0f);
 
     {
-      // toe bevel (cone)
+
       QVector3D toe = hoofBottom + QVector3D(0.0f, -d.hoofHeight * 0.15f, 0.0f);
-      out.mesh(getUnitCone(), coneFromTo(ctx.model, toe, hoofBottom, pasternR * 0.88f), hoofColor * 0.96f, nullptr, 1.0f);
+      out.mesh(getUnitCone(),
+               coneFromTo(ctx.model, toe, hoofBottom, pasternR * 0.88f),
+               hoofColor * 0.96f, nullptr, 1.0f);
     }
   };
 
-  QVector3D frontAnchor = barrelCenter + QVector3D(0.0f, d.bodyHeight * 0.05f,  d.bodyLength * 0.28f);
-  QVector3D rearAnchor  = barrelCenter + QVector3D(0.0f, d.bodyHeight * 0.02f, -d.bodyLength * 0.32f);
+  QVector3D frontAnchor = barrelCenter + QVector3D(0.0f, d.bodyHeight * 0.05f,
+                                                   d.bodyLength * 0.28f);
+  QVector3D rearAnchor = barrelCenter + QVector3D(0.0f, d.bodyHeight * 0.02f,
+                                                  -d.bodyLength * 0.32f);
 
-  // FL, FR, RL, RR with per-variant sock chances
-  drawLeg(frontAnchor,  1.0f,  d.bodyLength * 0.30f, g.frontLegPhase,             sockChanceFL);
-  drawLeg(frontAnchor, -1.0f,  d.bodyLength * 0.30f, g.frontLegPhase + 0.5f,      sockChanceFR);
-  drawLeg(rearAnchor,   1.0f, -d.bodyLength * 0.28f, g.rearLegPhase,               sockChanceRL);
-  drawLeg(rearAnchor,  -1.0f, -d.bodyLength * 0.28f, g.rearLegPhase + 0.5f,        sockChanceRR);
+  drawLeg(frontAnchor, 1.0f, d.bodyLength * 0.30f, g.frontLegPhase,
+          sockChanceFL);
+  drawLeg(frontAnchor, -1.0f, d.bodyLength * 0.30f, g.frontLegPhase + 0.5f,
+          sockChanceFR);
+  drawLeg(rearAnchor, 1.0f, -d.bodyLength * 0.28f, g.rearLegPhase,
+          sockChanceRL);
+  drawLeg(rearAnchor, -1.0f, -d.bodyLength * 0.28f, g.rearLegPhase + 0.5f,
+          sockChanceRR);
 
-  // ----------------
-  // SADDLE + TACK
-  // ----------------
   float saddleTop = d.saddleHeight;
-  QVector3D saddleCenter(0.0f, saddleTop - d.saddleThickness * 0.3f, d.seatForwardOffset * 0.4f);
+  QVector3D saddleCenter(0.0f, saddleTop - d.saddleThickness * 0.3f,
+                         d.seatForwardOffset * 0.4f);
   {
     QMatrix4x4 saddle = ctx.model;
     saddle.translate(saddleCenter);
@@ -587,79 +612,121 @@ void HorseRenderer::render(const DrawContext& ctx,
     out.mesh(getUnitSphere(), saddle, v.saddleColor, nullptr, 1.0f);
   }
 
-  // blanket (slightly softer/lighter)
-  QVector3D blanketCenter = saddleCenter + QVector3D(0.0f, -d.saddleThickness, 0.0f);
+  QVector3D blanketCenter =
+      saddleCenter + QVector3D(0.0f, -d.saddleThickness, 0.0f);
   {
     QMatrix4x4 blanket = ctx.model;
     blanket.translate(blanketCenter);
-    blanket.scale(d.bodyWidth * 1.24f, d.saddleThickness * 0.35f, d.bodyLength * 0.40f);
+    blanket.scale(d.bodyWidth * 1.24f, d.saddleThickness * 0.35f,
+                  d.bodyLength * 0.40f);
     out.mesh(getUnitSphere(), blanket, v.blanketColor * 1.02f, nullptr, 1.0f);
   }
 
-  // girth straps
   for (int i = 0; i < 2; ++i) {
     float side = (i == 0) ? 1.0f : -1.0f;
-    QVector3D strapTop    = saddleCenter + QVector3D(side * d.bodyWidth * 0.92f, d.saddleThickness * 0.35f, 0.0f);
-    QVector3D strapBottom = strapTop + QVector3D(0.0f, -d.bodyHeight * 0.95f, 0.02f);
-    out.mesh(getUnitCylinder(),
-             cylinderBetween(ctx.model, strapTop, strapBottom, d.bodyWidth * 0.07f),
-             v.tackColor * 0.94f, nullptr, 1.0f);
+    QVector3D strapTop =
+        saddleCenter +
+        QVector3D(side * d.bodyWidth * 0.92f, d.saddleThickness * 0.35f, 0.0f);
+    QVector3D strapBottom =
+        strapTop + QVector3D(0.0f, -d.bodyHeight * 0.95f, 0.02f);
+    out.mesh(
+        getUnitCylinder(),
+        cylinderBetween(ctx.model, strapTop, strapBottom, d.bodyWidth * 0.07f),
+        v.tackColor * 0.94f, nullptr, 1.0f);
   }
 
-  // stirrups
   auto drawStirrup = [&](float sideSign) {
-    QVector3D stirrupAttach = saddleCenter + QVector3D(sideSign * d.bodyWidth * 0.92f, -d.saddleThickness * 0.1f, d.seatForwardOffset * 0.3f);
-    QVector3D stirrupBottom = stirrupAttach + QVector3D(0.0f, -d.stirrupDrop, 0.0f);
+    QVector3D stirrupAttach =
+        saddleCenter + QVector3D(sideSign * d.bodyWidth * 0.92f,
+                                 -d.saddleThickness * 0.1f,
+                                 d.seatForwardOffset * 0.3f);
+    QVector3D stirrupBottom =
+        stirrupAttach + QVector3D(0.0f, -d.stirrupDrop, 0.0f);
 
     out.mesh(getUnitCylinder(),
-             cylinderBetween(ctx.model, stirrupAttach, stirrupBottom, d.bodyWidth * 0.05f),
+             cylinderBetween(ctx.model, stirrupAttach, stirrupBottom,
+                             d.bodyWidth * 0.05f),
              v.tackColor, nullptr, 1.0f);
 
     QMatrix4x4 stirrup = ctx.model;
-    stirrup.translate(stirrupBottom + QVector3D(0.0f, -d.bodyWidth * 0.06f, 0.0f));
-    stirrup.scale(d.bodyWidth * 0.20f, d.bodyWidth * 0.07f, d.bodyWidth * 0.16f);
-    out.mesh(getUnitSphere(), stirrup, QVector3D(0.66f, 0.65f, 0.62f), nullptr, 1.0f);
+    stirrup.translate(stirrupBottom +
+                      QVector3D(0.0f, -d.bodyWidth * 0.06f, 0.0f));
+    stirrup.scale(d.bodyWidth * 0.20f, d.bodyWidth * 0.07f,
+                  d.bodyWidth * 0.16f);
+    out.mesh(getUnitSphere(), stirrup, QVector3D(0.66f, 0.65f, 0.62f), nullptr,
+             1.0f);
   };
 
-  drawStirrup( 1.0f);
+  drawStirrup(1.0f);
   drawStirrup(-1.0f);
 
-  // bridle (cheek straps + noseband + browband), add bit ring & short reins hint
-  QVector3D cheekLeftTop     = headCenter + QVector3D( d.headWidth * 0.60f, -d.headHeight * 0.10f, d.headLength * 0.25f);
-  QVector3D cheekLeftBottom  = cheekLeftTop + QVector3D(0.0f, -d.headHeight, -d.headLength * 0.12f);
-  QVector3D cheekRightTop    = headCenter + QVector3D(-d.headWidth * 0.60f, -d.headHeight * 0.10f, d.headLength * 0.25f);
-  QVector3D cheekRightBottom = cheekRightTop + QVector3D(0.0f, -d.headHeight, -d.headLength * 0.12f);
-
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, cheekLeftTop,  cheekLeftBottom,  d.headWidth * 0.08f), v.tackColor, nullptr, 1.0f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, cheekRightTop, cheekRightBottom, d.headWidth * 0.08f), v.tackColor, nullptr, 1.0f);
+  QVector3D cheekLeftTop =
+      headCenter + QVector3D(d.headWidth * 0.60f, -d.headHeight * 0.10f,
+                             d.headLength * 0.25f);
+  QVector3D cheekLeftBottom =
+      cheekLeftTop + QVector3D(0.0f, -d.headHeight, -d.headLength * 0.12f);
+  QVector3D cheekRightTop =
+      headCenter + QVector3D(-d.headWidth * 0.60f, -d.headHeight * 0.10f,
+                             d.headLength * 0.25f);
+  QVector3D cheekRightBottom =
+      cheekRightTop + QVector3D(0.0f, -d.headHeight, -d.headLength * 0.12f);
+
+  out.mesh(getUnitCylinder(),
+           cylinderBetween(ctx.model, cheekLeftTop, cheekLeftBottom,
+                           d.headWidth * 0.08f),
+           v.tackColor, nullptr, 1.0f);
+  out.mesh(getUnitCylinder(),
+           cylinderBetween(ctx.model, cheekRightTop, cheekRightBottom,
+                           d.headWidth * 0.08f),
+           v.tackColor, nullptr, 1.0f);
 
-  QVector3D noseBandFront = muzzleCenter + QVector3D(0.0f, d.headHeight * 0.02f, d.muzzleLength * 0.35f);
-  QVector3D noseBandLeft  = noseBandFront + QVector3D( d.headWidth * 0.55f, 0.0f, 0.0f);
-  QVector3D noseBandRight = noseBandFront + QVector3D(-d.headWidth * 0.55f, 0.0f, 0.0f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, noseBandLeft, noseBandRight, d.headWidth * 0.08f),
+  QVector3D noseBandFront = muzzleCenter + QVector3D(0.0f, d.headHeight * 0.02f,
+                                                     d.muzzleLength * 0.35f);
+  QVector3D noseBandLeft =
+      noseBandFront + QVector3D(d.headWidth * 0.55f, 0.0f, 0.0f);
+  QVector3D noseBandRight =
+      noseBandFront + QVector3D(-d.headWidth * 0.55f, 0.0f, 0.0f);
+  out.mesh(getUnitCylinder(),
+           cylinderBetween(ctx.model, noseBandLeft, noseBandRight,
+                           d.headWidth * 0.08f),
            v.tackColor * 0.92f, nullptr, 1.0f);
 
-  QVector3D browBandFront = headCenter + QVector3D(0.0f, d.headHeight * 0.28f, d.headLength * 0.15f);
-  QVector3D browBandLeft  = browBandFront + QVector3D( d.headWidth * 0.58f, 0.0f, 0.0f);
-  QVector3D browBandRight = browBandFront + QVector3D(-d.headWidth * 0.58f, 0.0f, 0.0f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, browBandLeft, browBandRight, d.headWidth * 0.07f),
+  QVector3D browBandFront =
+      headCenter + QVector3D(0.0f, d.headHeight * 0.28f, d.headLength * 0.15f);
+  QVector3D browBandLeft =
+      browBandFront + QVector3D(d.headWidth * 0.58f, 0.0f, 0.0f);
+  QVector3D browBandRight =
+      browBandFront + QVector3D(-d.headWidth * 0.58f, 0.0f, 0.0f);
+  out.mesh(getUnitCylinder(),
+           cylinderBetween(ctx.model, browBandLeft, browBandRight,
+                           d.headWidth * 0.07f),
            v.tackColor, nullptr, 1.0f);
 
-  // bit & short reins indication
-  QVector3D bitLeft  = muzzleCenter + QVector3D( d.headWidth * 0.55f, -d.headHeight * 0.08f, d.muzzleLength * 0.10f);
-  QVector3D bitRight = muzzleCenter + QVector3D(-d.headWidth * 0.55f, -d.headHeight * 0.08f, d.muzzleLength * 0.10f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, bitLeft, bitRight, d.headWidth * 0.05f),
+  QVector3D bitLeft =
+      muzzleCenter + QVector3D(d.headWidth * 0.55f, -d.headHeight * 0.08f,
+                               d.muzzleLength * 0.10f);
+  QVector3D bitRight =
+      muzzleCenter + QVector3D(-d.headWidth * 0.55f, -d.headHeight * 0.08f,
+                               d.muzzleLength * 0.10f);
+  out.mesh(getUnitCylinder(),
+           cylinderBetween(ctx.model, bitLeft, bitRight, d.headWidth * 0.05f),
            QVector3D(0.55f, 0.55f, 0.55f), nullptr, 1.0f);
 
-  // short reins to saddle area (loose)
-  for (int i=0;i<2;++i){
-    float side = (i==0)?1.0f:-1.0f;
-    QVector3D reinStart = (i==0)? bitLeft : bitRight;
-    QVector3D reinEnd   = saddleCenter + QVector3D(side * d.bodyWidth * 0.65f, -d.saddleThickness * 0.3f, d.seatForwardOffset * 0.15f);
-    // add a small sag in the middle
-    QVector3D mid = lerpVec(reinStart, reinEnd, 0.5f) + QVector3D(0.0f, -d.bodyHeight * 0.10f, 0.0f);
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, reinStart, mid, d.bodyWidth * 0.02f), v.tackColor * 0.95f, nullptr, 1.0f);
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, mid, reinEnd,   d.bodyWidth * 0.02f), v.tackColor * 0.95f, nullptr, 1.0f);
+  for (int i = 0; i < 2; ++i) {
+    float side = (i == 0) ? 1.0f : -1.0f;
+    QVector3D reinStart = (i == 0) ? bitLeft : bitRight;
+    QVector3D reinEnd = saddleCenter + QVector3D(side * d.bodyWidth * 0.65f,
+                                                 -d.saddleThickness * 0.3f,
+                                                 d.seatForwardOffset * 0.15f);
+
+    QVector3D mid = lerpVec(reinStart, reinEnd, 0.5f) +
+                    QVector3D(0.0f, -d.bodyHeight * 0.10f, 0.0f);
+    out.mesh(getUnitCylinder(),
+             cylinderBetween(ctx.model, reinStart, mid, d.bodyWidth * 0.02f),
+             v.tackColor * 0.95f, nullptr, 1.0f);
+    out.mesh(getUnitCylinder(),
+             cylinderBetween(ctx.model, mid, reinEnd, d.bodyWidth * 0.02f),
+             v.tackColor * 0.95f, nullptr, 1.0f);
   }
 }
 

+ 6 - 12
render/entity/horse_renderer.h

@@ -5,7 +5,6 @@
 
 namespace Render::GL {
 
-// Forward declarations (defined elsewhere, e.g. in humanoid_base.h)
 struct DrawContext;
 struct AnimationInputs;
 class ISubmitter;
@@ -65,21 +64,16 @@ struct HorseProfile {
   HorseGait gait;
 };
 
-// Seeded generators
 HorseDimensions makeHorseDimensions(uint32_t seed);
-HorseVariant    makeHorseVariant(uint32_t seed,
-                                 const QVector3D& leatherBase,
-                                 const QVector3D& clothBase);
-HorseProfile    makeHorseProfile(uint32_t seed,
-                                 const QVector3D& leatherBase,
-                                 const QVector3D& clothBase);
+HorseVariant makeHorseVariant(uint32_t seed, const QVector3D &leatherBase,
+                              const QVector3D &clothBase);
+HorseProfile makeHorseProfile(uint32_t seed, const QVector3D &leatherBase,
+                              const QVector3D &clothBase);
 
 class HorseRenderer {
 public:
-  void render(const DrawContext& ctx,
-              const AnimationInputs& anim,
-              const HorseProfile& profile,
-              ISubmitter& out) const;
+  void render(const DrawContext &ctx, const AnimationInputs &anim,
+              const HorseProfile &profile, ISubmitter &out) const;
 };
 
 } // namespace Render::GL

+ 212 - 219
render/entity/mounted_knight_renderer.cpp

@@ -1,5 +1,4 @@
 #include "mounted_knight_renderer.h"
-#include "horse_renderer.h"
 #include "../../game/core/component.h"
 #include "../../game/core/entity.h"
 #include "../../game/core/world.h"
@@ -17,6 +16,7 @@
 #include "../palette.h"
 #include "../scene_renderer.h"
 #include "../submitter.h"
+#include "horse_renderer.h"
 #include "registry.h"
 #include <unordered_map>
 
@@ -94,29 +94,25 @@ public:
 
     uint32_t horseSeed = seed;
     if (ctx.entity) {
-      horseSeed = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ctx.entity) &
-                                        0xFFFFFFFFu);
+      horseSeed = static_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(ctx.entity) & 0xFFFFFFFFu);
     }
 
     const HorseDimensions dims = makeHorseDimensions(horseSeed);
 
-    // Calculate how much to lift the entire rider to sit on saddle
     const float saddleHeight = dims.saddleHeight;
     const float offsetY = saddleHeight - pose.pelvisPos.y();
 
-    // Lift the ENTIRE body uniformly to maintain proportions
     pose.pelvisPos.setY(pose.pelvisPos.y() + offsetY);
     pose.headPos.setY(pose.headPos.y() + offsetY);
     pose.neckBase.setY(pose.neckBase.y() + offsetY);
     pose.shoulderL.setY(pose.shoulderL.y() + offsetY);
     pose.shoulderR.setY(pose.shoulderR.y() + offsetY);
-    
-    // Adjust shoulder forward position for riding lean
+
     const float leanForward = dims.seatForwardOffset * 0.08f;
     pose.shoulderL.setZ(pose.shoulderL.z() + leanForward);
     pose.shoulderR.setZ(pose.shoulderR.z() + leanForward);
 
-    // Feet in stirrups
     const float stirrupForward = dims.seatForwardOffset - 0.035f;
     const float stirrupHeight = saddleHeight - dims.stirrupDrop;
 
@@ -124,20 +120,18 @@ public:
     pose.footL = QVector3D(-dims.stirrupOut, stirrupHeight, stirrupForward);
     pose.footR = QVector3D(dims.stirrupOut, stirrupHeight, stirrupForward);
 
-    // Knees bent to reach stirrups
     const float kneeY = stirrupHeight + (saddleHeight - stirrupHeight) * 0.62f;
     const float kneeZ = stirrupForward * 0.60f + 0.06f;
 
     pose.kneeL = QVector3D(-dims.stirrupOut * 0.92f, kneeY, kneeZ);
     pose.kneeR = QVector3D(dims.stirrupOut * 0.92f, kneeY, kneeZ);
 
-    // Hand positions for holding reins - lower and forward
     const float reinForward = dims.seatForwardOffset + 0.22f;
     const float shoulderHeight = pose.shoulderL.y();
     const float reinSpread = HP::SHOULDER_WIDTH * 0.36f;
 
-    // Hands should be below shoulder level for holding reins naturally
-    QVector3D restHandR(reinSpread, shoulderHeight - 0.05f + armHeightJitter, reinForward);
+    QVector3D restHandR(reinSpread, shoulderHeight - 0.05f + armHeightJitter,
+                        reinForward);
     QVector3D restHandL(-reinSpread * 0.85f,
                         shoulderHeight - 0.08f - armHeightJitter * 0.4f,
                         reinForward - 0.05f);
@@ -145,88 +139,71 @@ public:
     restHandR.setX(restHandR.x() + armAsymmetry * 0.45f);
     restHandL.setX(restHandL.x() - armAsymmetry * 0.55f);
 
-    // Set elbow positions to match the lowered hands
-    pose.elbowL = QVector3D(
-        pose.shoulderL.x() * 0.4f + restHandL.x() * 0.6f,
-        (pose.shoulderL.y() + restHandL.y()) * 0.5f - 0.08f,
-        (pose.shoulderL.z() + restHandL.z()) * 0.5f
-    );
-    pose.elbowR = QVector3D(
-        pose.shoulderR.x() * 0.4f + restHandR.x() * 0.6f,
-        (pose.shoulderR.y() + restHandR.y()) * 0.5f - 0.08f,
-        (pose.shoulderR.z() + restHandR.z()) * 0.5f
-    );
+    pose.elbowL = QVector3D(pose.shoulderL.x() * 0.4f + restHandL.x() * 0.6f,
+                            (pose.shoulderL.y() + restHandL.y()) * 0.5f - 0.08f,
+                            (pose.shoulderL.z() + restHandL.z()) * 0.5f);
+    pose.elbowR = QVector3D(pose.shoulderR.x() * 0.4f + restHandR.x() * 0.6f,
+                            (pose.shoulderR.y() + restHandR.y()) * 0.5f - 0.08f,
+                            (pose.shoulderR.z() + restHandR.z()) * 0.5f);
 
     if (anim.isAttacking && anim.isMelee) {
       const float attackCycleTime = 0.70f;
       float attackPhase = std::fmod(anim.time / attackCycleTime, 1.0f);
 
-      // Cavalry sword slash animation - side sweep from shoulder height
       QVector3D restPos = restHandR;
-      QVector3D windupPos = QVector3D(restHandR.x() + 0.32f, 
-                                       shoulderHeight + 0.15f, 
-                                       reinForward - 0.35f);
-      QVector3D raisedPos = QVector3D(reinSpread + 0.38f,
-                                       shoulderHeight + 0.28f,
-                                       reinForward - 0.25f);
-      QVector3D slashPos = QVector3D(-reinSpread * 0.65f,
-                                      shoulderHeight - 0.08f,
-                                      reinForward + 0.85f);
-      QVector3D followThrough = QVector3D(-reinSpread * 0.85f,
-                                           shoulderHeight - 0.15f,
-                                           reinForward + 0.60f);
-      QVector3D recoverPos = QVector3D(reinSpread * 0.45f,
-                                        shoulderHeight - 0.05f,
-                                        reinForward + 0.25f);
+      QVector3D windupPos = QVector3D(
+          restHandR.x() + 0.32f, shoulderHeight + 0.15f, reinForward - 0.35f);
+      QVector3D raisedPos = QVector3D(
+          reinSpread + 0.38f, shoulderHeight + 0.28f, reinForward - 0.25f);
+      QVector3D slashPos = QVector3D(
+          -reinSpread * 0.65f, shoulderHeight - 0.08f, reinForward + 0.85f);
+      QVector3D followThrough = QVector3D(
+          -reinSpread * 0.85f, shoulderHeight - 0.15f, reinForward + 0.60f);
+      QVector3D recoverPos = QVector3D(
+          reinSpread * 0.45f, shoulderHeight - 0.05f, reinForward + 0.25f);
 
       if (attackPhase < 0.18f) {
-        // Wind up
+
         float t = easeInOutCubic(attackPhase / 0.18f);
         pose.handR = restPos * (1.0f - t) + windupPos * t;
       } else if (attackPhase < 0.30f) {
-        // Raise to shoulder
+
         float t = easeInOutCubic((attackPhase - 0.18f) / 0.12f);
         pose.handR = windupPos * (1.0f - t) + raisedPos * t;
       } else if (attackPhase < 0.48f) {
-        // Powerful slash across
+
         float t = (attackPhase - 0.30f) / 0.18f;
-        t = t * t * t; // Fast acceleration
+        t = t * t * t;
         pose.handR = raisedPos * (1.0f - t) + slashPos * t;
       } else if (attackPhase < 0.62f) {
-        // Follow through
+
         float t = easeInOutCubic((attackPhase - 0.48f) / 0.14f);
         pose.handR = slashPos * (1.0f - t) + followThrough * t;
       } else if (attackPhase < 0.80f) {
-        // Recover position
+
         float t = easeInOutCubic((attackPhase - 0.62f) / 0.18f);
         pose.handR = followThrough * (1.0f - t) + recoverPos * t;
       } else {
-        // Return to rest
+
         float t = smoothstep(0.80f, 1.0f, attackPhase);
         pose.handR = recoverPos * (1.0f - t) + restPos * t;
       }
 
       float reinTension = clamp01((attackPhase - 0.10f) * 2.2f);
       pose.handL = restHandL +
-                   QVector3D(0.0f, -0.015f * reinTension,
-                             0.10f * reinTension);
-      
-      // Update elbows for attack animation
-      pose.elbowR = QVector3D(
-          pose.shoulderR.x() * 0.3f + pose.handR.x() * 0.7f,
-          (pose.shoulderR.y() + pose.handR.y()) * 0.5f - 0.12f,
-          (pose.shoulderR.z() + pose.handR.z()) * 0.5f
-      );
-      pose.elbowL = QVector3D(
-          pose.shoulderL.x() * 0.4f + pose.handL.x() * 0.6f,
-          (pose.shoulderL.y() + pose.handL.y()) * 0.5f - 0.08f,
-          (pose.shoulderL.z() + pose.handL.z()) * 0.5f
-      );
+                   QVector3D(0.0f, -0.015f * reinTension, 0.10f * reinTension);
+
+      pose.elbowR =
+          QVector3D(pose.shoulderR.x() * 0.3f + pose.handR.x() * 0.7f,
+                    (pose.shoulderR.y() + pose.handR.y()) * 0.5f - 0.12f,
+                    (pose.shoulderR.z() + pose.handR.z()) * 0.5f);
+      pose.elbowL =
+          QVector3D(pose.shoulderL.x() * 0.4f + pose.handL.x() * 0.6f,
+                    (pose.shoulderL.y() + pose.handL.y()) * 0.5f - 0.08f,
+                    (pose.shoulderL.z() + pose.handL.z()) * 0.5f);
     } else {
       pose.handR = restHandR;
       pose.handL = restHandL;
-      
-      // Elbows already set above for rest position
     }
   }
 
@@ -235,8 +212,8 @@ public:
                       ISubmitter &out) const override {
     uint32_t horseSeed = 0u;
     if (ctx.entity) {
-      horseSeed = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ctx.entity) &
-                                        0xFFFFFFFFu);
+      horseSeed = static_cast<uint32_t>(
+          reinterpret_cast<uintptr_t>(ctx.entity) & 0xFFFFFFFFu);
     }
 
     MountedKnightExtras extras;
@@ -528,170 +505,186 @@ private:
     return e;
   }
 
+  static void drawSword(const DrawContext &ctx, const HumanoidPose &pose,
+                        const HumanoidVariant &v,
+                        const MountedKnightExtras &extras, bool isAttacking,
+                        float attackPhase, ISubmitter &out) {
+
+    const QVector3D gripPos = pose.handR;
+
+    QVector3D swordDir(0.0f, 0.15f, 1.0f);
+    swordDir.normalize();
+
+    QVector3D worldUp(0.0f, 1.0f, 0.0f);
+    QVector3D rightAxis = QVector3D::crossProduct(worldUp, swordDir);
+    if (rightAxis.lengthSquared() < 1e-6f)
+      rightAxis = QVector3D(1.0f, 0.0f, 0.0f);
+    rightAxis.normalize();
+    QVector3D upAxis = QVector3D::crossProduct(swordDir, rightAxis);
+    upAxis.normalize();
+
+    const QVector3D steel = extras.metalColor;
+    const QVector3D steelHi = steel * 1.18f;
+    const QVector3D steelLo = steel * 0.92f;
+    const QVector3D leather = v.palette.leather;
+    const QVector3D pommelCol =
+        v.palette.metal * QVector3D(1.25f, 1.10f, 0.75f);
+
+    const float pommelOffset = 0.10f;
+    const float gripLen = 0.16f;
+    const float gripRad = 0.017f;
+    const float guardHalf = 0.11f;
+    const float guardRad = 0.012f;
+    const float guardCurve = 0.03f;
+
+    const QVector3D pommelPos = gripPos - swordDir * pommelOffset;
+    out.mesh(getUnitSphere(), sphereAt(ctx.model, pommelPos, 0.028f), pommelCol,
+             nullptr, 1.0f);
 
+    {
+      QVector3D neckA = pommelPos + swordDir * 0.010f;
+      QVector3D neckB = gripPos - swordDir * 0.005f;
+      out.mesh(getUnitCylinder(),
+               cylinderBetween(ctx.model, neckA, neckB, 0.0125f), steelLo,
+               nullptr, 1.0f);
 
-  static void drawSword(const DrawContext &ctx, const HumanoidPose &pose,
-                      const HumanoidVariant &v,
-                      const MountedKnightExtras &extras, bool isAttacking,
-                      float attackPhase, ISubmitter &out) {
-  // Anchor in the right hand
-  const QVector3D gripPos = pose.handR;
-
-  // Sword forward axis (kept compatible with old behavior)
-  QVector3D swordDir(0.0f, 0.15f, 1.0f);
-  swordDir.normalize();
-
-  // Build a local orthonormal frame for shaping (right/flat axis & up/edge axis)
-  QVector3D worldUp(0.0f, 1.0f, 0.0f);
-  QVector3D rightAxis = QVector3D::crossProduct(worldUp, swordDir);
-  if (rightAxis.lengthSquared() < 1e-6f) rightAxis = QVector3D(1.0f, 0.0f, 0.0f);
-  rightAxis.normalize();
-  QVector3D upAxis = QVector3D::crossProduct(swordDir, rightAxis);
-  upAxis.normalize();
-
-  // Colors
-  const QVector3D steel     = extras.metalColor;
-  const QVector3D steelHi   = steel * 1.18f;
-  const QVector3D steelLo   = steel * 0.92f;
-  const QVector3D leather   = v.palette.leather;
-  const QVector3D pommelCol = v.palette.metal * QVector3D(1.25f, 1.10f, 0.75f);
-
-  // Hilt proportions (more massive, two-hand friendly) — thin blade later
-  const float pommelOffset  = 0.10f;
-  const float gripLen       = 0.16f;
-  const float gripRad       = 0.017f;
-  const float guardHalf     = 0.11f;   // each quillon half-length
-  const float guardRad      = 0.012f;
-  const float guardCurve    = 0.03f;   // slight downturn of quillons
-
-  // Pommel (wheel-ish: sphere + short neck + peen)
-  const QVector3D pommelPos = gripPos - swordDir * pommelOffset;
-  out.mesh(getUnitSphere(), sphereAt(ctx.model, pommelPos, 0.028f), pommelCol, nullptr, 1.0f);
-
-  // short neck between pommel and grip core (tang hint)
-  {
-    QVector3D neckA = pommelPos + swordDir * 0.010f;
-    QVector3D neckB = gripPos   - swordDir * 0.005f;
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, neckA, neckB, 0.0125f), steelLo, nullptr, 1.0f);
-
-    // peen cap
-    QVector3D peen = pommelPos - swordDir * 0.012f;
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, peen, pommelPos, 0.010f), steel, nullptr, 1.0f);
-  }
+      QVector3D peen = pommelPos - swordDir * 0.012f;
+      out.mesh(getUnitCone(), coneFromTo(ctx.model, peen, pommelPos, 0.010f),
+               steel, nullptr, 1.0f);
+    }
 
-  // Grip: extended and slightly waisted with leather rings
-  const QVector3D gripA = gripPos - swordDir * 0.005f;
-  const QVector3D gripB = gripPos + swordDir * (gripLen - 0.005f);
-  const int wrapRings = 5;
-  for (int i = 0; i < wrapRings; ++i) {
-    float t0 = (float)i / wrapRings;
-    float t1 = (float)(i + 1) / wrapRings;
-    QVector3D a = gripA + swordDir * (gripLen * t0);
-    QVector3D b = gripA + swordDir * (gripLen * t1);
-    // subtle waist
-    float rMid = gripRad * (0.96f + 0.08f * std::sin((t0 + t1) * 3.14159f));
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, a, b, rMid), leather * 0.98f, nullptr, 1.0f);
-  }
+    const QVector3D gripA = gripPos - swordDir * 0.005f;
+    const QVector3D gripB = gripPos + swordDir * (gripLen - 0.005f);
+    const int wrapRings = 5;
+    for (int i = 0; i < wrapRings; ++i) {
+      float t0 = (float)i / wrapRings;
+      float t1 = (float)(i + 1) / wrapRings;
+      QVector3D a = gripA + swordDir * (gripLen * t0);
+      QVector3D b = gripA + swordDir * (gripLen * t1);
+
+      float rMid = gripRad * (0.96f + 0.08f * std::sin((t0 + t1) * 3.14159f));
+      out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, a, b, rMid),
+               leather * 0.98f, nullptr, 1.0f);
+    }
 
-  // Cross-guard: curved quillons (segmented arc) + flared tips
-  const QVector3D guardCenter = gripB + swordDir * 0.010f;
-  {
-    const int segs = 4;
-    QVector3D prev = guardCenter - rightAxis * guardHalf + (-upAxis * guardCurve);
-    for (int s = 1; s <= segs; ++s) {
-      float u = -1.0f + 2.0f * (float)s / segs; // -1..+1
-      QVector3D p = guardCenter + rightAxis * (guardHalf * u) + (-upAxis * guardCurve * (1.0f - u*u));
-      out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, prev, p, guardRad), steelHi, nullptr, 1.0f);
-      prev = p;
+    const QVector3D guardCenter = gripB + swordDir * 0.010f;
+    {
+      const int segs = 4;
+      QVector3D prev =
+          guardCenter - rightAxis * guardHalf + (-upAxis * guardCurve);
+      for (int s = 1; s <= segs; ++s) {
+        float u = -1.0f + 2.0f * (float)s / segs;
+        QVector3D p = guardCenter + rightAxis * (guardHalf * u) +
+                      (-upAxis * guardCurve * (1.0f - u * u));
+        out.mesh(getUnitCylinder(),
+                 cylinderBetween(ctx.model, prev, p, guardRad), steelHi,
+                 nullptr, 1.0f);
+        prev = p;
+      }
+
+      QVector3D Lend =
+          guardCenter - rightAxis * guardHalf + (-upAxis * guardCurve);
+      QVector3D Rend =
+          guardCenter + rightAxis * guardHalf + (-upAxis * guardCurve);
+      out.mesh(getUnitCone(),
+               coneFromTo(ctx.model, Lend - rightAxis * 0.030f, Lend,
+                          guardRad * 1.12f),
+               steelHi, nullptr, 1.0f);
+      out.mesh(getUnitCone(),
+               coneFromTo(ctx.model, Rend + rightAxis * 0.030f, Rend,
+                          guardRad * 1.12f),
+               steelHi, nullptr, 1.0f);
+
+      out.mesh(getUnitSphere(),
+               sphereAt(ctx.model, guardCenter, guardRad * 0.9f), steel,
+               nullptr, 1.0f);
     }
-    // end caps slightly flared
-    QVector3D Lend = guardCenter - rightAxis * guardHalf + (-upAxis * guardCurve);
-    QVector3D Rend = guardCenter + rightAxis * guardHalf + (-upAxis * guardCurve);
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, Lend - rightAxis * 0.030f, Lend, guardRad * 1.12f), steelHi, nullptr, 1.0f);
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, Rend + rightAxis * 0.030f, Rend, guardRad * 1.12f), steelHi, nullptr, 1.0f);
-
-    // central block (ecusson)
-    out.mesh(getUnitSphere(), sphereAt(ctx.model, guardCenter, guardRad * 0.9f), steel, nullptr, 1.0f);
-  }
 
-  // -------------------------
-  // BLADE (thin, realistic)
-  // -------------------------
-  // Keep overall length budget but make the blade slender with fuller & ricasso
-  const float bladeLen   = std::max(0.0f, extras.swordLength - 0.14f);
-  const QVector3D bladeRoot = guardCenter + swordDir * 0.020f;
-  const QVector3D bladeTip  = bladeRoot + swordDir * bladeLen;
-
-  // Ricasso (short, thicker base just above guard)
-  const QVector3D ricassoEnd = bladeRoot + swordDir * (bladeLen * 0.08f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, bladeRoot, ricassoEnd, extras.swordWidth * 0.32f),
-           steelHi, nullptr, 1.0f);
-
-  // Fuller (blood groove) — runs ~10%..80% of the blade
-  const QVector3D fullerA = bladeRoot + swordDir * (bladeLen * 0.10f);
-  const QVector3D fullerB = bladeRoot + swordDir * (bladeLen * 0.80f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, fullerA, fullerB, extras.swordWidth * 0.10f),
-           steelLo, nullptr, 1.0f);
-
-  // Main blade body: multi-segment taper to mimic a thin diamond/flat grind
-  const float baseR = extras.swordWidth * 0.26f; // THIN blade
-  const float midR  = extras.swordWidth * 0.16f;
-  const float preTipR = extras.swordWidth * 0.09f;
-
-  QVector3D s0 = ricassoEnd;
-  QVector3D s1 = bladeRoot + swordDir * (bladeLen * 0.55f);
-  QVector3D s2 = bladeRoot + swordDir * (bladeLen * 0.88f);
-
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, s0, s1, baseR), steelHi, nullptr, 1.0f);
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, s1, s2, midR),  steelHi, nullptr, 1.0f);
-
-  // Edge hint lines (extremely thin “bevels” along flats)
-  {
-    float edgeR = extras.swordWidth * 0.03f;
-    QVector3D eA = bladeRoot + swordDir * (bladeLen * 0.10f);
-    QVector3D eB = bladeTip  - swordDir * (bladeLen * 0.06f);
-    QVector3D leftEdgeA  = eA + rightAxis * (baseR * 0.95f);
-    QVector3D leftEdgeB  = eB + rightAxis * (preTipR * 0.95f);
-    QVector3D rightEdgeA = eA - rightAxis * (baseR * 0.95f);
-    QVector3D rightEdgeB = eB - rightAxis * (preTipR * 0.95f);
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, leftEdgeA,  leftEdgeB,  edgeR), steel * 1.08f, nullptr, 1.0f);
-    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, rightEdgeA, rightEdgeB, edgeR), steel * 1.08f, nullptr, 1.0f);
-  }
+    const float bladeLen = std::max(0.0f, extras.swordLength - 0.14f);
+    const QVector3D bladeRoot = guardCenter + swordDir * 0.020f;
+    const QVector3D bladeTip = bladeRoot + swordDir * bladeLen;
 
-  // Pre-tip transition and acute tip
-  out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, s2, bladeTip - swordDir * 0.020f, preTipR),
-           steelHi, nullptr, 1.0f);
-  out.mesh(getUnitCone(), coneFromTo(ctx.model, bladeTip, bladeTip - swordDir * 0.060f, preTipR * 0.95f),
-           steelHi * 1.04f, nullptr, 1.0f);
-
-  // Small ricasso shoulders (subtle flare into guard)
-  {
-    QVector3D shoulderL0 = bladeRoot + rightAxis * (baseR * 1.05f);
-    QVector3D shoulderL1 = shoulderL0 - rightAxis * (baseR * 0.45f);
-    QVector3D shoulderR0 = bladeRoot - rightAxis * (baseR * 1.05f);
-    QVector3D shoulderR1 = shoulderR0 + rightAxis * (baseR * 0.45f);
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, shoulderL1, shoulderL0, baseR * 0.22f), steel, nullptr, 1.0f);
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, shoulderR1, shoulderR0, baseR * 0.22f), steel, nullptr, 1.0f);
-  }
+    const QVector3D ricassoEnd = bladeRoot + swordDir * (bladeLen * 0.08f);
+    out.mesh(getUnitCylinder(),
+             cylinderBetween(ctx.model, bladeRoot, ricassoEnd,
+                             extras.swordWidth * 0.32f),
+             steelHi, nullptr, 1.0f);
+
+    const QVector3D fullerA = bladeRoot + swordDir * (bladeLen * 0.10f);
+    const QVector3D fullerB = bladeRoot + swordDir * (bladeLen * 0.80f);
+    out.mesh(
+        getUnitCylinder(),
+        cylinderBetween(ctx.model, fullerA, fullerB, extras.swordWidth * 0.10f),
+        steelLo, nullptr, 1.0f);
+
+    const float baseR = extras.swordWidth * 0.26f;
+    const float midR = extras.swordWidth * 0.16f;
+    const float preTipR = extras.swordWidth * 0.09f;
+
+    QVector3D s0 = ricassoEnd;
+    QVector3D s1 = bladeRoot + swordDir * (bladeLen * 0.55f);
+    QVector3D s2 = bladeRoot + swordDir * (bladeLen * 0.88f);
+
+    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, s0, s1, baseR),
+             steelHi, nullptr, 1.0f);
+    out.mesh(getUnitCylinder(), cylinderBetween(ctx.model, s1, s2, midR),
+             steelHi, nullptr, 1.0f);
 
-  // -------------------------
-  // Motion trail during slash
-  // -------------------------
-  if (isAttacking && attackPhase >= 0.28f && attackPhase < 0.58f) {
-    float t = (attackPhase - 0.28f) / 0.30f;
-    float alpha = clamp01(0.40f * (1.0f - t * t));
-    QVector3D sweep = (-rightAxis * 0.18f - swordDir * 0.10f) * t;
-
-    QVector3D trailTip  = bladeTip   + sweep;
-    QVector3D trailRoot = bladeRoot  + sweep * 0.6f;
-
-    // two layered cones to look more “sheet-like”
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, trailRoot, trailTip, baseR * 1.10f),
-             steel * 0.90f, nullptr, alpha);
-    out.mesh(getUnitCone(), coneFromTo(ctx.model, trailRoot + upAxis * 0.01f, trailTip, baseR * 0.75f),
-             steel * 0.80f, nullptr, alpha * 0.7f);
+    {
+      float edgeR = extras.swordWidth * 0.03f;
+      QVector3D eA = bladeRoot + swordDir * (bladeLen * 0.10f);
+      QVector3D eB = bladeTip - swordDir * (bladeLen * 0.06f);
+      QVector3D leftEdgeA = eA + rightAxis * (baseR * 0.95f);
+      QVector3D leftEdgeB = eB + rightAxis * (preTipR * 0.95f);
+      QVector3D rightEdgeA = eA - rightAxis * (baseR * 0.95f);
+      QVector3D rightEdgeB = eB - rightAxis * (preTipR * 0.95f);
+      out.mesh(getUnitCylinder(),
+               cylinderBetween(ctx.model, leftEdgeA, leftEdgeB, edgeR),
+               steel * 1.08f, nullptr, 1.0f);
+      out.mesh(getUnitCylinder(),
+               cylinderBetween(ctx.model, rightEdgeA, rightEdgeB, edgeR),
+               steel * 1.08f, nullptr, 1.0f);
+    }
+
+    out.mesh(
+        getUnitCylinder(),
+        cylinderBetween(ctx.model, s2, bladeTip - swordDir * 0.020f, preTipR),
+        steelHi, nullptr, 1.0f);
+    out.mesh(getUnitCone(),
+             coneFromTo(ctx.model, bladeTip, bladeTip - swordDir * 0.060f,
+                        preTipR * 0.95f),
+             steelHi * 1.04f, nullptr, 1.0f);
+
+    {
+      QVector3D shoulderL0 = bladeRoot + rightAxis * (baseR * 1.05f);
+      QVector3D shoulderL1 = shoulderL0 - rightAxis * (baseR * 0.45f);
+      QVector3D shoulderR0 = bladeRoot - rightAxis * (baseR * 1.05f);
+      QVector3D shoulderR1 = shoulderR0 + rightAxis * (baseR * 0.45f);
+      out.mesh(getUnitCone(),
+               coneFromTo(ctx.model, shoulderL1, shoulderL0, baseR * 0.22f),
+               steel, nullptr, 1.0f);
+      out.mesh(getUnitCone(),
+               coneFromTo(ctx.model, shoulderR1, shoulderR0, baseR * 0.22f),
+               steel, nullptr, 1.0f);
+    }
+
+    if (isAttacking && attackPhase >= 0.28f && attackPhase < 0.58f) {
+      float t = (attackPhase - 0.28f) / 0.30f;
+      float alpha = clamp01(0.40f * (1.0f - t * t));
+      QVector3D sweep = (-rightAxis * 0.18f - swordDir * 0.10f) * t;
+
+      QVector3D trailTip = bladeTip + sweep;
+      QVector3D trailRoot = bladeRoot + sweep * 0.6f;
+
+      out.mesh(getUnitCone(),
+               coneFromTo(ctx.model, trailRoot, trailTip, baseR * 1.10f),
+               steel * 0.90f, nullptr, alpha);
+      out.mesh(getUnitCone(),
+               coneFromTo(ctx.model, trailRoot + upAxis * 0.01f, trailTip,
+                          baseR * 0.75f),
+               steel * 0.80f, nullptr, alpha * 0.7f);
+    }
   }
-}
 
   static void drawCavalryShield(const DrawContext &ctx,
                                 const HumanoidPose &pose,

+ 1 - 1
render/humanoid_base.cpp

@@ -74,7 +74,7 @@ FormationParams HumanoidRendererBase::resolveFormation(const DrawContext &ctx) {
       params.maxPerRow = Game::Units::TroopConfig::instance().getMaxUnitsPerRow(
           unit->unitType);
       if (unit->unitType == "mounted_knight") {
-        params.spacing = 1.35f; // wider footprint so horses do not overlap
+        params.spacing = 1.35f;
       }
     }
   }