djeada 4 dienas atpakaļ
vecāks
revīzija
333f7cad92
67 mainītis faili ar 1246 papildinājumiem un 3091 dzēšanām
  1. 3 3
      Makefile
  2. 19 47
      assets/shaders/archer.frag
  3. 97 155
      assets/shaders/archer_carthage.frag
  4. 10 38
      assets/shaders/archer_carthage.vert
  5. 45 123
      assets/shaders/archer_roman_republic.frag
  6. 6 15
      assets/shaders/archer_roman_republic.vert
  7. 3 3
      assets/shaders/basic.frag
  8. 22 55
      assets/shaders/bridge.frag
  9. 1 2
      assets/shaders/bridge.vert
  10. 5 18
      assets/shaders/catapult.frag
  11. 0 6
      assets/shaders/catapult.vert
  12. 3 4
      assets/shaders/firecamp.vert
  13. 1 5
      assets/shaders/grid.frag
  14. 7 14
      assets/shaders/ground_plane.frag
  15. 1 6
      assets/shaders/ground_plane.vert
  16. 1 2
      assets/shaders/healer.frag
  17. 3 3
      assets/shaders/healer_carthage.frag
  18. 0 11
      assets/shaders/healer_carthage.vert
  19. 9 45
      assets/shaders/healer_roman_republic.frag
  20. 1 6
      assets/shaders/healer_roman_republic.vert
  21. 129 184
      assets/shaders/horse_archer_carthage.frag
  22. 2 6
      assets/shaders/horse_archer_carthage.vert
  23. 95 346
      assets/shaders/horse_archer_roman_republic.frag
  24. 10 18
      assets/shaders/horse_archer_roman_republic.vert
  25. 111 159
      assets/shaders/horse_spearman_carthage.frag
  26. 1 3
      assets/shaders/horse_spearman_carthage.vert
  27. 94 349
      assets/shaders/horse_spearman_roman_republic.frag
  28. 9 15
      assets/shaders/horse_spearman_roman_republic.vert
  29. 11 47
      assets/shaders/horse_swordsman.frag
  30. 38 51
      assets/shaders/horse_swordsman_carthage.frag
  31. 1 3
      assets/shaders/horse_swordsman_carthage.vert
  32. 106 361
      assets/shaders/horse_swordsman_roman_republic.frag
  33. 9 15
      assets/shaders/horse_swordsman_roman_republic.vert
  34. 6 38
      assets/shaders/olive_instanced.frag
  35. 4 26
      assets/shaders/olive_instanced.vert
  36. 4 13
      assets/shaders/pine_instanced.frag
  37. 4 27
      assets/shaders/pine_instanced.vert
  38. 8 42
      assets/shaders/plant_instanced.frag
  39. 7 19
      assets/shaders/plant_instanced.vert
  40. 0 2
      assets/shaders/primitive_instanced.frag
  41. 6 12
      assets/shaders/primitive_instanced.vert
  42. 9 19
      assets/shaders/river.frag
  43. 1 5
      assets/shaders/river.vert
  44. 6 27
      assets/shaders/riverbank.frag
  45. 0 1
      assets/shaders/riverbank.vert
  46. 6 35
      assets/shaders/road.frag
  47. 1 2
      assets/shaders/road.vert
  48. 16 50
      assets/shaders/spearman.frag
  49. 63 109
      assets/shaders/spearman_carthage.frag
  50. 5 13
      assets/shaders/spearman_carthage.vert
  51. 45 108
      assets/shaders/spearman_roman_republic.frag
  52. 6 15
      assets/shaders/spearman_roman_republic.vert
  53. 0 2
      assets/shaders/stone_instanced.frag
  54. 2 4
      assets/shaders/stone_instanced.vert
  55. 16 37
      assets/shaders/swordsman.frag
  56. 73 95
      assets/shaders/swordsman_carthage.frag
  57. 1 6
      assets/shaders/swordsman_carthage.vert
  58. 40 120
      assets/shaders/swordsman_roman_republic.frag
  59. 8 20
      assets/shaders/swordsman_roman_republic.vert
  60. 30 65
      assets/shaders/terrain_chunk.frag
  61. 1 12
      assets/shaders/terrain_chunk.vert
  62. 7 18
      assets/shaders/troop_shadow.frag
  63. 14 19
      render/equipment/armor/roman_greaves.cpp
  64. 1 2
      render/equipment/weapons/bow_renderer.cpp
  65. 1 2
      render/gl/humanoid/humanoid_types.h
  66. 0 1
      render/humanoid/pose_controller.cpp
  67. 2 7
      render/humanoid/rig.cpp

+ 3 - 3
Makefile

@@ -203,11 +203,11 @@ EXCLUDE_DIRS := ./$(BUILD_DIR) ./$(BUILD_TIDY_DIR) ./third_party
 EXCLUDE_FIND := $(foreach d,$(EXCLUDE_DIRS),-not -path "$(d)/*")
 
 format:
-	@echo "$(BOLD)$(BLUE)Stripping comments in app/... game/... render/... tools/... ui/...$(RESET)"
+	@echo "$(BOLD)$(BLUE)Stripping comments in app/... game/... render/... tools/... ui/... assets/shaders/...$(RESET)"
 	@if [ -x scripts/remove-comments.sh ]; then \
-		./scripts/remove-comments.sh app/ game/ render/ tools/ ui/; \
+		./scripts/remove-comments.sh app/ game/ render/ tools/ ui/ assets/shaders/; \
 	elif [ -f scripts/remove-comments.sh ]; then \
-		bash scripts/remove-comments.sh app/ game/ render/ tools/ ui/; \
+		bash scripts/remove-comments.sh app/ game/ render/ tools/ ui/ assets/shaders/; \
 	else \
 		echo "$(RED)scripts/remove-comments.sh not found$(RESET)"; exit 1; \
 	fi

+ 19 - 47
assets/shaders/archer.frag

@@ -3,7 +3,7 @@
 in vec3 v_normal;
 in vec2 v_texCoord;
 in vec3 v_worldPos;
-in float v_armorLayer; // NEW: Armor layer from vertex shader
+in float v_armorLayer;
 
 uniform sampler2D u_texture;
 uniform vec3 u_color;
@@ -29,14 +29,12 @@ float noise(vec2 p) {
   return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
 }
 
-// Roman chainmail (lorica hamata) ring pattern
 float chainmailRings(vec2 p) {
   vec2 grid = fract(p * 32.0) - 0.5;
   float ring = length(grid);
   float ringPattern =
       smoothstep(0.38, 0.32, ring) - smoothstep(0.28, 0.22, ring);
 
-  // Offset rows for interlocking
   vec2 offsetGrid = fract(p * 32.0 + vec2(0.5, 0.0)) - 0.5;
   float offsetRing = length(offsetGrid);
   float offsetPattern =
@@ -45,37 +43,30 @@ float chainmailRings(vec2 p) {
   return (ringPattern + offsetPattern) * 0.14;
 }
 
-// Leather pteruges strips (hanging skirt/shoulder guards)
 float pterugesStrips(vec2 p, float y) {
-  // Vertical leather strips
+
   float stripX = fract(p.x * 9.0);
   float strip = smoothstep(0.15, 0.20, stripX) - smoothstep(0.80, 0.85, stripX);
 
-  // Add leather texture to strips
   float leatherTex = noise(p * 18.0) * 0.35;
 
-  // Strips hang and curve
   float hang = smoothstep(0.65, 0.45, y);
 
   return strip * leatherTex * hang;
 }
 
-// Scale armor (lorica squamata) - overlapping metal scales
 float scaleArmor(vec2 p, float y) {
-  // Offset scale rows for overlap effect
+
   vec2 scaleGrid = p * 18.0;
-  scaleGrid.y += sin(scaleGrid.x * 2.0) * 0.15; // Wave pattern
+  scaleGrid.y += sin(scaleGrid.x * 2.0) * 0.15;
 
   vec2 scaleFract = fract(scaleGrid) - 0.5;
 
-  // Individual scale shape (teardrop/fish scale)
   float scaleDist = length(scaleFract * vec2(1.0, 1.4));
   float scaleShape = smoothstep(0.48, 0.38, scaleDist);
 
-  // Overlap shadow
   float overlap = sin(scaleGrid.y * 3.14159) * 0.08;
 
-  // Edge highlight on scales
   float edge =
       smoothstep(0.42, 0.38, scaleDist) - smoothstep(0.38, 0.34, scaleDist);
 
@@ -92,7 +83,6 @@ void main() {
   vec2 uv = v_worldPos.xz * 4.5;
   float avgColor = (color.r + color.g + color.b) / 3.0;
 
-  // Detect materials by color and armor layer
   bool isBronze =
       (color.r > color.g * 1.08 && color.r > color.b * 1.15 && avgColor > 0.50);
   bool isRedCape = (color.r > color.g * 1.3 && color.r > color.b * 1.4);
@@ -102,101 +92,86 @@ void main() {
   bool isScaleArmor =
       (v_armorLayer == 1.0 && avgColor > 0.50 && avgColor <= 0.70);
 
-  // === ROMAN AUXILIARY ARCHER (SAGITTARII) MATERIALS ===
-
-  // BRONZE AUXILIARY HELMET (simpler than legionary)
   if (isBronze && isHelmet) {
-    // Ancient bronze patina and wear
+
     float bronzePatina = noise(uv * 8.0) * 0.12;
-    float verdigris = noise(uv * 15.0) * 0.08; // Green oxidation
+    float verdigris = noise(uv * 15.0) * 0.08;
 
-    // Bronze is less reflective than polished steel
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float bronzeSheen = pow(viewAngle, 7.0) * 0.25;
     float bronzeFresnel = pow(1.0 - viewAngle, 2.2) * 0.18;
 
-    // Hammer marks from forging
     float hammerMarks = noise(uv * 25.0) * 0.035;
 
     color += vec3(bronzeSheen + bronzeFresnel);
     color -= vec3(bronzePatina * 0.4 + verdigris * 0.3);
     color += vec3(hammerMarks * 0.5);
   }
-  // LORICA SQUAMATA (SCALE ARMOR) - Auxiliary preference over chainmail
+
   else if (isScaleArmor && !isRedCape) {
-    // Overlapping bronze/iron scales
+
     float scales = scaleArmor(v_worldPos.xz, v_worldPos.y);
 
-    // Scale armor has moderate shine
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float scaleSheen = pow(viewAngle, 6.0) * 0.20;
 
-    // Rust and wear between scales
     float wearBetweenScales = noise(uv * 12.0) * 0.09;
 
     color += vec3(scales + scaleSheen);
     color -= vec3(wearBetweenScales * 0.35);
-    color *= 1.0 - noise(uv * 20.0) * 0.05; // Shadow between scales
+    color *= 1.0 - noise(uv * 20.0) * 0.05;
   }
-  // LORICA HAMATA (CHAINMAIL) - Some auxiliaries wore this
+
   else if (isChainmail && !isRedCape) {
-    // Interlocked iron rings
+
     float rings = chainmailRings(v_worldPos.xz);
 
-    // Chainmail has dull metallic sheen
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float chainSheen = pow(viewAngle, 5.0) * 0.16;
 
-    // Iron rust spots
     float rust = noise(uv * 10.0) * 0.08;
 
     color += vec3(rings + chainSheen);
-    color -= vec3(rust * 0.4);              // Darken with age
-    color *= 1.0 - noise(uv * 18.0) * 0.06; // Shadow between rings
+    color -= vec3(rust * 0.4);
+    color *= 1.0 - noise(uv * 18.0) * 0.06;
   }
-  // RED SAGUM CAPE (bright red woolen cloak)
+
   else if (isRedCape) {
-    // Thick woolen weave
+
     float weaveX = sin(v_worldPos.x * 55.0);
     float weaveZ = sin(v_worldPos.z * 55.0);
     float weave = weaveX * weaveZ * 0.045;
 
-    // Wool texture (fuzzy)
     float woolFuzz = noise(uv * 20.0) * 0.10;
 
-    // Fabric folds and draping
     float folds = noise(uv * 6.0) * 0.12 - 0.06;
 
-    // Soft fabric sheen
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float capeSheen = pow(1.0 - viewAngle, 8.0) * 0.08;
 
     color *= 1.0 + woolFuzz - 0.05 + folds;
     color += vec3(weave + capeSheen);
   }
-  // LEATHER PTERUGES & ARMOR STRIPS (tan/brown leather strips)
+
   else if (avgColor > 0.35) {
-    // Thick leather with visible grain
+
     float leatherGrain = noise(uv * 10.0) * 0.16;
     float leatherPores = noise(uv * 22.0) * 0.08;
 
-    // Pteruges strip pattern
     float strips = pterugesStrips(v_worldPos.xz, v_worldPos.y);
 
-    // Worn leather edges
     float wear = noise(uv * 4.0) * 0.10 - 0.05;
 
-    // Leather has subtle sheen
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float leatherSheen = pow(1.0 - viewAngle, 4.5) * 0.10;
 
     color *= 1.0 + leatherGrain + leatherPores - 0.08 + wear;
     color += vec3(strips * 0.15 + leatherSheen);
   }
-  // DARK ELEMENTS (cingulum belt, straps, manicae)
+
   else {
     float leatherDetail = noise(uv * 8.0) * 0.14;
-    float tooling = noise(uv * 16.0) * 0.06; // Decorative tooling
+    float tooling = noise(uv * 16.0) * 0.06;
     float darkening = noise(uv * 2.5) * 0.08;
 
     color *= 1.0 + leatherDetail - 0.07 + tooling - darkening;
@@ -204,15 +179,12 @@ void main() {
 
   color = clamp(color, 0.0, 1.0);
 
-  // Lighting model - soft wrap for leather/fabric, harder for metal
   vec3 lightDir = normalize(vec3(1.0, 1.15, 1.0));
   float nDotL = dot(normal, lightDir);
 
-  // Metal = harder shadows, Fabric/leather = soft wrap
   float wrapAmount = isBronze ? 0.15 : 0.38;
   float diff = max(nDotL * (1.0 - wrapAmount) + wrapAmount, 0.22);
 
-  // Enhance contrast for bronze
   if (isBronze) {
     diff = pow(diff, 0.90);
   }

+ 97 - 155
assets/shaders/archer_carthage.frag

@@ -1,10 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CARTHAGINIAN ARCHER - Rich Brown Leather & Team-Colored Cloak
-// Unique earth-tone palette with vibrant team colors
-// ============================================================================
-
 in vec3 v_worldNormal;
 in vec3 v_worldPos;
 in vec2 v_texCoord;
@@ -17,13 +12,9 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// CONSTANTS & HELPERS
-// ============================================================================
-
 const float PI = 3.14159265359;
 const vec3 ARCHER_SKIN_BASE = vec3(0.08, 0.07, 0.065);
-// Reuse the spearman helmet brown so the hat matches the infantry tone
+
 const vec3 SPEARMAN_HELMET_BROWN = vec3(0.36, 0.22, 0.10);
 
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
@@ -63,10 +54,6 @@ float fbm(vec2 p) {
   return v;
 }
 
-// ============================================================================
-// PBR FUNCTIONS
-// ============================================================================
-
 float D_GGX(float NdotH, float roughness) {
   float a = roughness * roughness;
   float a2 = a * a;
@@ -90,18 +77,13 @@ vec3 F_Schlick(float cosTheta, vec3 F0) {
   return F0 + (1.0 - F0) * t5;
 }
 
-// ============================================================================
-// LEATHER TEXTURE - RICH BROWN PALETTE
-// ============================================================================
-
-// Multiple brown leather tones
 vec3 brownLeatherPalette(float variation) {
-  // Range from warm chocolate to reddish-brown to tan
-  vec3 darkBrown = vec3(0.28, 0.18, 0.10);    // Deep chocolate
-  vec3 redBrown = vec3(0.45, 0.25, 0.15);     // Ox-blood/cordovan
-  vec3 warmBrown = vec3(0.55, 0.38, 0.22);    // Saddle brown
-  vec3 lightTan = vec3(0.68, 0.52, 0.35);     // Natural tan
-  
+
+  vec3 darkBrown = vec3(0.28, 0.18, 0.10);
+  vec3 redBrown = vec3(0.45, 0.25, 0.15);
+  vec3 warmBrown = vec3(0.55, 0.38, 0.22);
+  vec3 lightTan = vec3(0.68, 0.52, 0.35);
+
   if (variation < 0.33) {
     return mix(darkBrown, redBrown, variation * 3.0);
   } else if (variation < 0.66) {
@@ -122,7 +104,7 @@ float leatherGrain(vec2 uv, float scale) {
 float stitchPattern(vec2 uv, float spacing) {
   float stitch = fract(uv.y * spacing);
   stitch = smoothstep(0.4, 0.5, stitch) * smoothstep(0.6, 0.5, stitch);
-  float seamLine = smoothstep(0.48, 0.50, fract(uv.x * 4.0)) * 
+  float seamLine = smoothstep(0.48, 0.50, fract(uv.x * 4.0)) *
                    smoothstep(0.52, 0.50, fract(uv.x * 4.0));
   return stitch * seamLine;
 }
@@ -141,15 +123,11 @@ float oilSheen(vec3 pos, vec3 N, vec3 V) {
   return facing * facing * variation;
 }
 
-// ============================================================================
-// CLOAK FABRIC
-// ============================================================================
-
 float cloakFolds(vec3 pos) {
-  // Large flowing folds
+
   float folds = sin(pos.x * 8.0 + pos.y * 3.0) * 0.5 + 0.5;
   folds += sin(pos.z * 6.0 - pos.y * 2.0) * 0.3;
-  // Add wind-swept variation
+
   float wind = fbm(pos.xz * 4.0 + pos.y * 2.0);
   return folds * 0.6 + wind * 0.4;
 }
@@ -160,48 +138,40 @@ float fabricWeave(vec2 uv) {
   return warpX * weftY;
 }
 
-// ============================================================================
-// BOW WOOD PATTERNS
-// ============================================================================
-
 float woodGrainBow(vec3 pos) {
-  // Long flowing grain along the length of the bow
+
   float grain = sin(pos.y * 40.0 + fbm(pos.xy * 8.0) * 3.0);
   grain = grain * 0.5 + 0.5;
   float rings = fbm(vec2(pos.x * 20.0, pos.z * 20.0));
   return grain * 0.7 + rings * 0.3;
 }
 
-// ============================================================================
-// MAIN
-// ============================================================================
-
 void main() {
   vec3 baseColor = clamp(u_color, 0.0, 1.0);
   if (u_useTexture) {
     baseColor *= texture(u_texture, v_texCoord).rgb;
   }
-  
-  // Material IDs: 0=skin, 1=armor, 2=helmet, 3=weapon, 4=shield, 5=cloak
+  vec3 cloakTeamBase = clamp(
+      boostSaturation(baseColor * vec3(1.05, 0.95, 0.95), 0.55), 0.0, 1.0);
+
   bool is_skin = (u_materialId == 0);
   bool is_armor = (u_materialId == 1);
   bool is_helmet = (u_materialId == 2);
   bool is_weapon = (u_materialId == 3);
   bool is_shield = (u_materialId == 4);
-  bool is_cloak = (u_materialId == 5 || u_materialId >= 12);
-  
+  bool is_cloak =
+      (u_materialId == 5 || u_materialId == 6 || u_materialId >= 12);
+
   vec3 N = normalize(v_worldNormal);
   vec3 V = normalize(vec3(0.0, 0.0, 1.0));
   vec3 L = normalize(vec3(0.5, 1.0, 0.4));
   vec3 H = normalize(L + V);
-  
+
   vec3 albedo = baseColor;
   float metallic = 0.0;
   float roughness = 0.5;
   float sheen = 0.0;
-  
-  // ========== MATERIAL SETUP ==========
-  
+
   if (is_skin) {
     vec3 skinBase = ARCHER_SKIN_BASE;
     vec3 teamTint = mix(skinBase, baseColor, 0.2);
@@ -209,243 +179,215 @@ void main() {
     albedo = clamp(teamTint + vec3(toneNoise) * 0.04, 0.0, 1.0);
     metallic = 0.0;
     roughness = 0.58;
-    
-    // Leather arm guards and leg wrappings
-    float armGuard = smoothstep(0.55, 0.65, v_worldPos.y) * 
+
+    float armGuard = smoothstep(0.55, 0.65, v_worldPos.y) *
                      smoothstep(0.75, 0.65, v_worldPos.y);
     float legWrap = 1.0 - smoothstep(0.25, 0.45, v_worldPos.y);
     float leatherMask = max(armGuard, legWrap);
-    
-    // Brown leather with grain variation
+
     float leatherVar = fbm(v_worldPos.xy * 5.0);
     vec3 leatherColor = brownLeatherPalette(leatherVar);
     float grain = leatherGrain(v_worldPos.xy, 16.0);
     leatherColor *= 0.9 + grain * 0.2;
-    
-    // Braided pattern on wrappings
+
     float braid = sin(v_worldPos.y * 30.0) * 0.5 + 0.5;
     braid *= smoothstep(0.3, 0.5, fract(v_worldPos.x * 8.0));
     leatherColor *= 0.95 + braid * 0.1;
-    
+
     albedo = mix(albedo, leatherColor, leatherMask);
     roughness = mix(roughness, 0.45, leatherMask);
     sheen = leatherMask * 0.3;
-    
+
   } else if (is_armor) {
-    // ====== RICH BROWN LEATHER ARMOR ======
+
     float leatherVar = fbm(v_worldPos.xy * 4.0 + v_worldPos.z * 2.0);
     vec3 leatherBase = brownLeatherPalette(leatherVar);
-    
+
     float grain = leatherGrain(v_worldPos.xy, 14.0);
     float wear = battleWear(v_worldPos);
     float oil = oilSheen(v_worldPos, N, V);
     float stitches = stitchPattern(v_worldPos.xy, 15.0);
-    
-    // Rich leather from palette, enhanced with base color tint
+
     albedo = mix(leatherBase, baseColor * 0.8, 0.25);
     albedo = boostSaturation(albedo, 0.25);
     albedo *= 0.88 + grain * 0.24;
-    
-    // Layered leather effect - darker in creases
+
     float depth = fbm(v_worldPos.xy * 6.0);
     albedo = mix(albedo * 0.72, albedo * 1.12, depth);
-    
-    // Worn edges lighter
+
     vec3 wornColor = albedo * 1.3 + vec3(0.1, 0.08, 0.05);
     albedo = mix(albedo, wornColor, wear * 0.45);
-    
-    // Visible stitching
+
     albedo = mix(albedo, albedo * 0.55, stitches * 0.8);
-    
-    // Leather straps with buckles
+
     float straps = smoothstep(0.45, 0.48, fract(v_worldPos.x * 6.0)) *
                    smoothstep(0.55, 0.52, fract(v_worldPos.x * 6.0));
-    vec3 strapColor = brownLeatherPalette(0.15); // Darker straps
+    vec3 strapColor = brownLeatherPalette(0.15);
     albedo = mix(albedo, strapColor, straps * 0.6);
-    
+
     metallic = 0.0;
     roughness = 0.42 - oil * 0.14 + grain * 0.1;
     roughness = clamp(roughness, 0.28, 0.55);
     sheen = oil * 0.55;
-    
+
   } else if (is_helmet) {
-    // Leather cap with bronze trim (dark brown base)
+
     float grain = leatherGrain(v_worldPos.xz, 12.0);
     vec3 leatherColor = SPEARMAN_HELMET_BROWN;
     leatherColor = mix(leatherColor, baseColor * 0.4, 0.1);
     leatherColor *= 0.9 + grain * 0.25;
 
-    // Bronze cheek guards and trim
     float bronzeTrim = smoothstep(0.7, 0.75, abs(v_worldPos.x) * 2.0);
     bronzeTrim += smoothstep(0.85, 0.9, v_worldPos.y);
     vec3 bronzeColor = vec3(0.64, 0.42, 0.24);
     bronzeColor = boostSaturation(bronzeColor, 0.3);
-    
+
     albedo = mix(leatherColor, bronzeColor, bronzeTrim);
     metallic = mix(0.0, 0.88, bronzeTrim);
     roughness = mix(0.45, 0.25, bronzeTrim);
     sheen = (1.0 - bronzeTrim) * 0.4;
-    
+
   } else if (is_weapon) {
-    // ====== BOW - Beautiful wood with leather grip ======
+
     float h = v_worldPos.y;
-    
-    // Grip area in the middle
+
     float grip = smoothstep(0.38, 0.45, h) * smoothstep(0.62, 0.55, h);
-    // Limbs are the curved parts
+
     float limbs = 1.0 - grip;
-    // String at extremes
+
     float stringArea = smoothstep(0.92, 1.0, h) + smoothstep(0.08, 0.0, h);
-    
-    // Beautiful yew/ash wood for bow limbs
+
     float woodGrain = woodGrainBow(v_worldPos);
-    vec3 woodLight = vec3(0.72, 0.55, 0.35);  // Heartwood
-    vec3 woodDark = vec3(0.48, 0.32, 0.18);   // Sapwood contrast
-    vec3 woodColor = mix(woodDark, woodLight, woodGrain);
-    woodColor = boostSaturation(woodColor, 0.2);
-    
-    // Polish sheen on wood
+    vec3 woodColor = vec3(0.45, 0.28, 0.15);
+    woodColor += vec3(0.02) * woodGrain;
+    woodColor = boostSaturation(woodColor, 0.12);
+
     float woodPolish = pow(max(dot(reflect(-V, N), L), 0.0), 24.0);
-    
-    // Leather grip wrap
+
     float gripGrain = leatherGrain(v_worldPos.xy, 25.0);
-    vec3 gripLeather = brownLeatherPalette(0.25); // Dark leather
+    vec3 gripLeather = brownLeatherPalette(0.25);
     gripLeather *= 0.9 + gripGrain * 0.2;
-    
-    // Criss-cross wrap pattern
+
     float wrapPattern = sin(v_worldPos.y * 50.0 + v_worldPos.x * 20.0);
     wrapPattern = smoothstep(-0.2, 0.2, wrapPattern);
     gripLeather *= 0.9 + wrapPattern * 0.15;
-    
-    // String - natural gut/linen color
+
     vec3 stringColor = vec3(0.85, 0.80, 0.72);
     float stringShine = pow(max(dot(reflect(-V, N), L), 0.0), 48.0);
-    
+
     albedo = woodColor;
     albedo = mix(albedo, gripLeather, grip);
     albedo = mix(albedo, stringColor, stringArea * 0.8);
-    
+
     metallic = 0.0;
     roughness = mix(0.38, 0.48, grip);
     roughness = mix(roughness, 0.32, stringArea);
-    sheen = woodPolish * limbs * 0.4 + grip * 0.3 + stringShine * stringArea * 0.3;
-    
+    sheen =
+        woodPolish * limbs * 0.4 + grip * 0.3 + stringShine * stringArea * 0.3;
+
   } else if (is_shield) {
-    // Leather-covered wooden shield
+
     float leatherVar = fbm(v_worldPos.xz * 5.0);
     vec3 leatherColor = brownLeatherPalette(leatherVar * 0.7 + 0.15);
-    
+
     float grain = leatherGrain(v_worldPos.xz, 10.0);
     leatherColor *= 0.88 + grain * 0.24;
-    
-    // Central boss and edge trim in bronze
+
     float dist = length(v_worldPos.xz);
     float boss = smoothstep(0.15, 0.0, dist);
     float edgeRim = smoothstep(0.85, 0.95, dist);
-    
+
     vec3 bronzeColor = vec3(0.85, 0.65, 0.35);
     bronzeColor = boostSaturation(bronzeColor, 0.25);
-    
+
     albedo = leatherColor;
     albedo = mix(albedo, bronzeColor, boss + edgeRim * 0.7);
-    
+
     metallic = mix(0.0, 0.9, boss + edgeRim * 0.6);
     roughness = mix(0.48, 0.22, boss);
     sheen = (1.0 - boss) * (1.0 - edgeRim) * 0.35;
-    
+
   } else if (is_cloak) {
-    // ====== TEAM-COLORED CLOAK ======
-    // Use u_color for team color - make it vibrant!
-    vec3 teamColor = boostSaturation(baseColor, 0.5);
-    teamColor *= 1.2; // Brighten
-    
+
     float folds = cloakFolds(v_worldPos);
     float weave = fabricWeave(v_worldPos.xy);
-    
-    // Rich fabric with fold shadows and highlights
-    albedo = teamColor;
-    albedo *= 0.75 + folds * 0.5; // Folds create depth
-    albedo *= 0.95 + weave * 0.08;
-    
-    // Slight color shift in shadows (cooler)
-    vec3 shadowTint = mix(teamColor, teamColor * vec3(0.85, 0.9, 1.0), 0.3);
-    albedo = mix(shadowTint, albedo, folds);
-    
-    // Fabric edge fray
+    vec3 teamColor = clamp(boostSaturation(cloakTeamBase, 0.3), 0.0, 1.0);
+    vec3 highlightColor =
+        mix(teamColor * 0.88, teamColor * 1.28, smoothstep(0.45, 0.7, folds));
+    vec3 shadowTint = mix(teamColor * vec3(0.7, 0.8, 0.92), teamColor, 0.35);
+
+    float edgeGlow = smoothstep(0.2, 0.45, abs(v_worldPos.z));
+    vec3 baseShade =
+        mix(shadowTint, highlightColor, folds * 0.85) * (0.82 + folds * 0.36);
+    albedo = mix(baseShade, highlightColor, edgeGlow * 0.35);
+    albedo *= 1.0 + weave * 0.06;
+
     float edgeFray = noise(v_worldPos.xy * 40.0);
-    albedo *= 0.97 + edgeFray * 0.06;
-    
+    albedo *= 0.94 + edgeFray * 0.08;
+
     metallic = 0.0;
-    roughness = 0.72;
-    // Velvet-like sheen on fabric
-    sheen = pow(1.0 - max(dot(N, V), 0.0), 4.0) * 0.25;
-    
+    roughness = 0.66 + folds * 0.05;
+
+    sheen = pow(1.0 - max(dot(N, V), 0.0), 4.0) * 0.28;
+
   } else {
-    // Default - leather-tinted
+
     float leatherVar = fbm(v_worldPos.xy * 6.0);
     albedo = mix(brownLeatherPalette(leatherVar), baseColor, 0.4);
     albedo = boostSaturation(albedo, 0.15);
     metallic = 0.0;
     roughness = 0.55;
   }
-  
-  // ========== PBR LIGHTING ==========
-  
+
   float NdotL = max(dot(N, L), 0.0);
   float NdotV = max(dot(N, V), 0.001);
   float NdotH = max(dot(N, H), 0.0);
   float VdotH = max(dot(V, H), 0.0);
-  
+
   vec3 F0 = mix(vec3(0.04), albedo, metallic);
-  
+
   float D = D_GGX(NdotH, max(roughness, 0.01));
   float G = G_Smith(NdotV, NdotL, roughness);
   vec3 F = F_Schlick(VdotH, F0);
   vec3 specular = (D * G * F) / (4.0 * NdotV * NdotL + 0.001);
-  
+
   vec3 kD = (vec3(1.0) - F) * (1.0 - metallic);
   vec3 diffuse = kD * albedo / PI;
-  
+
   vec3 color = (diffuse + specular * 1.6) * NdotL * 2.0;
-  
-  // ====== SHEEN EFFECTS ======
+
   if (sheen > 0.01) {
     vec3 R = reflect(-V, N);
     float sheenSpec = pow(max(dot(R, L), 0.0), 14.0);
     color += albedo * sheenSpec * sheen * 1.4;
-    
+
     float edgeSheen = pow(1.0 - NdotV, 3.0);
     color += vec3(0.92, 0.88, 0.78) * edgeSheen * sheen * 0.35;
   }
-  
-  // ====== METALLIC SHINE (bronze) ======
+
   if (metallic > 0.5) {
     vec3 R = reflect(-V, N);
     float specPower = 128.0 / (roughness * roughness + 0.001);
     specPower = min(specPower, 512.0);
     float mirrorSpec = pow(max(dot(R, L), 0.0), specPower);
     color += albedo * mirrorSpec * 1.1;
-    
+
     float hotspot = pow(NdotH, 256.0);
     color += vec3(1.0) * hotspot * (1.0 - roughness);
   }
-  
-  // ====== WARM AMBIENT ======
+
   vec3 ambient = albedo * 0.36;
-  
-  // Subsurface hint for leather
+
   float sss = pow(saturate(dot(-N, L)), 2.5) * 0.12;
   ambient += albedo * vec3(1.1, 0.92, 0.75) * sss;
-  
-  // Rim light
+
   float rim = pow(1.0 - NdotV, 3.5);
   ambient += vec3(0.32, 0.30, 0.26) * rim * 0.22;
-  
+
   color += ambient;
-  
-  // Tone mapping
+
   color = color / (color + vec3(0.55));
   color = pow(color, vec3(0.94));
-  
+
   FragColor = vec4(saturate3(color), u_alpha);
 }

+ 10 - 38
assets/shaders/archer_carthage.vert

@@ -30,73 +30,51 @@ vec3 fallbackUp(vec3 normal) {
 void main() {
   vec3 pos = a_position;
 
-  // ==========================================================
-  // CLOAK BACK DRAPE (Material ID 5) - Vertical, hangs down back
-  // pos.x = left/right, pos.z = length (top to bottom in world)
-  // ==========================================================
   if (u_materialId == 5) {
-    float v_raw = a_texCoord.y; // mesh uses 0 at top, 1 at bottom
-    float v = 1.0 - v_raw;      // normalize so 1 = top, 0 = bottom
-    float u = a_texCoord.x;     // 0 left, 1 right
+    float v_raw = a_texCoord.y;
+    float v = 1.0 - v_raw;
+    float u = a_texCoord.x;
 
-    // Pin top portion
-    // Keep only the very top firmly attached
     float pin = smoothstep(0.85, 1.0, v);
     float move = 1.0 - pin;
 
-    float x_norm = (u - 0.5) * 2.0; // -1 to +1 left/right
+    float x_norm = (u - 0.5) * 2.0;
 
-    // Top folds onto the shoulders: only affect the upper band so the body of
-    // the cloak hangs straight
     float top_blend = smoothstep(0.92, 1.0, v);
     float edge_emphasis = abs(x_norm);
     float shoulder_wrap = top_blend * (0.45 + edge_emphasis * 0.55);
-    // Pull toward the body and fold the top edge downward (increase z)
+
     pos.y -= shoulder_wrap * 0.08;
     pos.z += shoulder_wrap * 0.08;
 
-    // Bottom flares outward (positive Y = away from body)
     float v_bottom = 1.0 - v;
     float bottom_blend = smoothstep(0.0, 0.35, v_bottom);
     pos.y += bottom_blend * 0.08;
 
-    // Bottom is slightly wider - expand X at bottom
     pos.x += sign(pos.x) * bottom_blend * 0.10;
 
-    // Gentle wind animation
     float t = u_time * 1.8;
     float wave = sin(pos.z * 5.0 - t + x_norm * 0.5) * 0.02;
     pos.y += wave * move;
   }
 
-  // ==========================================================
-  // CLOAK SHOULDER CAPE (Material ID 6) - Drapes over shoulders
-  // Plane mesh is in XZ (flat), Y is up. pos.x = side, pos.z = front-back
-  // ==========================================================
   if (u_materialId == 6) {
-    float u = a_texCoord.x; // 0 to 1 left-right
-    float v = a_texCoord.y; // 0 front, 1 back
+    float u = a_texCoord.x;
+    float v = a_texCoord.y;
 
-    // Normalized coordinates
-    float x_norm = (u - 0.5) * 2.0; // -1 left, +1 right
+    float x_norm = (u - 0.5) * 2.0;
     float x_abs = abs(x_norm);
-    float z_norm = (v - 0.5) * 2.0; // -1 front, +1 back
+    float z_norm = (v - 0.5) * 2.0;
 
-    // === SHOULDER DROOP ===
-    // Edges droop down gently to follow shoulder slope
     float shoulder_droop = x_abs * x_abs * 0.10;
     pos.y -= shoulder_droop;
 
-    // === FRONT-BACK CURVE ===
-    // Back droops slightly to connect with back drape
     float back_droop = max(0.0, z_norm) * max(0.0, z_norm) * 0.06;
     pos.y -= back_droop;
 
-    // Front edge curves down slightly
     float front_droop = max(0.0, -z_norm) * max(0.0, -z_norm) * 0.03;
     pos.y -= front_droop;
 
-    // Subtle animated flutter
     float t = u_time * 2.0;
     float flutter = sin(t + x_norm * 3.0 + z_norm * 2.0) * 0.005;
     pos.y += flutter;
@@ -105,18 +83,16 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * a_normal);
 
-  // Build a stable TBN without needing extra attributes
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
-  // Gram–Schmidt to ensure orthonormality
+
   t = normalize(t - worldNormal * dot(worldNormal, t));
   vec3 b = normalize(cross(worldNormal, t));
 
   vec4 modelPos = u_model * vec4(pos, 1.0);
   vec3 worldPos = modelPos.xyz;
 
-  // Small normal push to avoid self-shadow acne in forward pipelines
   vec3 offsetPos = worldPos + worldNormal * 0.008;
 
   gl_Position = u_mvp * vec4(pos, 1.0);
@@ -128,7 +104,6 @@ void main() {
   v_tangent = t;
   v_bitangent = b;
 
-  // Layer bands from mesh-space height so placement does not change masks.
   vec3 axisY = vec3(u_model[1].xyz);
   float axisLen = max(length(axisY), 1e-4);
   vec3 axisDir = axisY / axisLen;
@@ -136,17 +111,14 @@ void main() {
   float height01 =
       clamp(dot(worldPos - modelOrigin, axisDir) / axisLen + 0.5, 0.0, 1.0);
 
-  // Use a single armor band for the whole piece to avoid partial masks.
   float layer_mask = (u_materialId == 1) ? 1.0 : 0.0;
   v_armorLayer = layer_mask;
 
-  // Leather tension: variation + curvature bias + height influence
   float tensionSeed = hash13(offsetPos * 0.35 + worldNormal);
   float heightFactor = height01;
   float curvatureFactor = length(vec2(worldNormal.x, worldNormal.z));
   v_leatherTension = mix(tensionSeed, 1.0 - tensionSeed, layer_mask * 0.33) *
                      (0.7 + curvatureFactor * 0.3) * (0.8 + heightFactor * 0.2);
 
-  // Normalized torso height for gradient effects
   v_bodyHeight = height01;
 }

+ 45 - 123
assets/shaders/archer_roman_republic.frag

@@ -1,9 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// INPUTS & OUTPUTS
-// ============================================================================
-
 in vec3 v_normal;
 in vec3 v_worldNormal;
 in vec3 v_tangent;
@@ -25,10 +21,6 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// UTILITY FUNCTIONS
-// ============================================================================
-
 float hash(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * 0.1031);
   p3 += dot(p3, p3.yzx + 33.33);
@@ -46,18 +38,12 @@ float noise(vec2 p) {
   return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
 }
 
-// ============================================================================
-// MATERIAL PATTERN FUNCTIONS (Roman Archer - Sagittarius)
-// ============================================================================
-
-// Roman chainmail (lorica hamata) ring pattern
 float chainmail_rings(vec2 p) {
   vec2 grid = fract(p * 32.0) - 0.5;
   float ring = length(grid);
   float ring_pattern =
       smoothstep(0.38, 0.32, ring) - smoothstep(0.28, 0.22, ring);
 
-  // Offset rows for interlocking
   vec2 offset_grid = fract(p * 32.0 + vec2(0.5, 0.0)) - 0.5;
   float offset_ring = length(offset_grid);
   float offset_pattern =
@@ -66,26 +52,19 @@ float chainmail_rings(vec2 p) {
   return (ring_pattern + offset_pattern) * 0.14;
 }
 
-// Leather pteruges strips (hanging skirt/shoulder guards)
 float pteruges_strips(vec2 p, float y) {
-  // Vertical leather strips
+
   float strip_x = fract(p.x * 9.0);
   float strip =
       smoothstep(0.15, 0.20, strip_x) - smoothstep(0.80, 0.85, strip_x);
 
-  // Add leather texture to strips
   float leather_tex = noise(p * 18.0) * 0.35;
 
-  // Strips hang and curve
   float hang = smoothstep(0.65, 0.45, y);
 
   return strip * leather_tex * hang;
 }
 
-// ============================================================================
-// MAIN FRAGMENT SHADER
-// ============================================================================
-
 void main() {
   vec3 color = u_color;
   if (u_useTexture) {
@@ -95,7 +74,6 @@ void main() {
   vec3 normal = normalize(v_normal);
   vec2 uv = v_worldPos.xz * 4.5;
 
-  // Material ID: 0=body/skin, 1=armor, 2=helmet, 3=weapon, 4=shield, 5=cloak/greaves, 6=cloak_back
   bool is_skin = (u_materialId == 0);
   bool is_armor = (u_materialId == 1);
   bool is_helmet = (u_materialId == 2);
@@ -104,9 +82,6 @@ void main() {
   bool is_cloak = (u_materialId == 5 || u_materialId == 6);
   bool is_greaves = (u_materialId == 5);
 
-  // === ROMAN ARCHER (SAGITTARIUS) MATERIALS ===
-
-  // LIGHT BRONZE HELMET (warm golden auxiliary helmet)
   if (is_skin) {
     vec3 N = normalize(v_worldNormal);
     vec3 V = normalize(vec3(0.0, 1.0, 0.35));
@@ -117,20 +92,16 @@ void main() {
     float rim = pow(1.0 - max(dot(N, V), 0.0), 4.0) * 0.04;
     color += vec3(rim);
   } else if (is_helmet) {
-    // Use vertex-computed helmet detail
+
     float bands = v_helmetDetail * 0.15;
 
-    // Warm bronze patina and wear
     float bronze_patina = noise(uv * 8.0) * 0.12;
     float verdigris = noise(uv * 15.0) * 0.08;
 
-    // Hammer marks from forging (using vertex curvature)
     float hammer_marks = noise(uv * 25.0) * 0.035 * (1.0 - v_curvature * 0.3);
 
-    // Conical shape highlight
     float apex = smoothstep(0.85, 1.0, v_bodyHeight) * 0.12;
 
-    // ENHANCED: Cheek guards (hinged bronze plates)
     float cheek_guard_height = smoothstep(0.72, 0.82, v_bodyHeight) *
                                smoothstep(0.88, 0.82, v_bodyHeight);
     float cheek_x = abs(v_worldPos.x);
@@ -138,17 +109,14 @@ void main() {
         cheek_guard_height * smoothstep(0.10, 0.08, cheek_x) * 0.35;
     float guard_edge = cheek_guard * step(0.32, noise(uv * 18.0)) * 0.18;
 
-    // ENHANCED: Bronze composition variation (copper/tin ratio affects color)
     float bronze_variation = noise(uv * 5.0) * 0.10;
-    vec3 rich_bronze = vec3(0.82, 0.68, 0.42); // Higher copper
-    vec3 pale_bronze = vec3(0.75, 0.72, 0.55); // Higher tin
+    vec3 rich_bronze = vec3(0.82, 0.68, 0.42);
+    vec3 pale_bronze = vec3(0.75, 0.72, 0.55);
 
-    // ENHANCED: Plume socket (top center mounting point)
     float plume_socket_height = smoothstep(0.92, 0.96, v_bodyHeight);
     float plume_socket =
         plume_socket_height * smoothstep(0.04, 0.02, abs(v_worldPos.x)) * 0.25;
 
-    // Bronze sheen using tangent space
     vec3 N = normalize(v_worldNormal);
     vec3 V = normalize(vec3(0.0, 1.0, 0.5));
     float view_angle = max(dot(N, V), 0.0);
@@ -162,84 +130,62 @@ void main() {
     color -= vec3(bronze_patina * 0.4 + verdigris * 0.3);
     color += vec3(hammer_marks * 0.5);
   }
-  // LIGHT CHAINMAIL ARMOR (lorica hamata - historically accurate 4-in-1
-  // pattern)
+
   else if (is_armor) {
-    // DRAMATICALLY VISIBLE chainmail - base color much darker than body
-    // Start with strong grey base that's clearly NOT skin
-    color = color * vec3(0.45, 0.48, 0.52); // Force grey metal base
 
-    // Roman chainmail: butted iron rings, 8-10mm diameter, 1.2mm wire
-    vec2 chain_uv = v_worldPos.xz * 22.0; // Larger, more visible rings
+    color = color * vec3(0.45, 0.48, 0.52);
 
-    // MUCH STRONGER ring pattern - these need to be OBVIOUS
-    float rings = chainmail_rings(chain_uv) * 2.5; // 3x stronger
+    vec2 chain_uv = v_worldPos.xz * 22.0;
+
+    float rings = chainmail_rings(chain_uv) * 2.5;
 
-    // Deep shadows in ring gaps - CRITICAL for visibility
     float ring_gaps = (1.0 - chainmail_rings(chain_uv)) * 0.45;
 
-    // Ring structure with STRONG contrast
     float ring_highlight = rings * 0.85;
 
-    // ENHANCED: Ring quality variation (handmade vs forge-produced)
-    float ring_quality =
-        noise(chain_uv * 3.5) * 0.18; // Size/shape inconsistency
-    float wire_thickness =
-        noise(chain_uv * 28.0) * 0.12; // Wire gauge variation
+    float ring_quality = noise(chain_uv * 3.5) * 0.18;
+    float wire_thickness = noise(chain_uv * 28.0) * 0.12;
 
-    // ENHANCED: Micro-gaps between interlocked rings (4-in-1 pattern realism)
     float ring_joints = step(0.88, fract(chain_uv.x * 0.5)) *
                         step(0.88, fract(chain_uv.y * 0.5)) * 0.35;
     float gap_shadow = ring_joints * smoothstep(0.4, 0.6, rings);
 
-    // ENHANCED: Oxidation gradients (rust spreads from contact points)
     float rust_seed = noise(chain_uv * 4.2);
     float oxidation =
         smoothstep(0.45, 0.75, rust_seed) * v_chainmailPhase * 0.32;
     vec3 rust_color = vec3(0.42, 0.32, 0.26);
     vec3 dark_rust = vec3(0.28, 0.20, 0.16);
 
-    // ENHANCED: Stress points at shoulder/elbow joints
-    float joint_stress = smoothstep(1.25, 1.35, v_bodyHeight) * // Shoulders
-                         smoothstep(1.05, 1.15, v_bodyHeight) * // Elbows
-                         0.25;
+    float joint_stress = smoothstep(1.25, 1.35, v_bodyHeight) *
+                         smoothstep(1.05, 1.15, v_bodyHeight) * 0.25;
     float stress_wear = joint_stress * noise(chain_uv * 8.0) * 0.15;
 
-    // Iron oxidation (non-galvanized iron ages naturally)
     float iron_tarnish = noise(chain_uv * 12.0) * 0.20 * v_chainmailPhase;
 
-    // Battle damage - visible broken rings
     float damage = step(0.88, noise(chain_uv * 0.8)) * 0.35;
 
-    // STRONG specular highlights on ring surfaces
     vec3 N = normalize(v_worldNormal);
     vec3 V = normalize(vec3(0.0, 1.0, 0.5));
     float view_angle = max(dot(N, V), 0.0);
-    float chain_sheen = pow(view_angle, 4.0) * 0.65; // Much stronger
+    float chain_sheen = pow(view_angle, 4.0) * 0.65;
 
-    // Metallic shimmer as rings catch light
     float shimmer = abs(sin(chain_uv.x * 32.0) * sin(chain_uv.y * 32.0)) * 0.25;
 
-    // Apply all chainmail effects with STRONG visibility
     color += vec3(ring_highlight + chain_sheen + shimmer);
     color -= vec3(ring_gaps + damage + gap_shadow + stress_wear);
     color *= 1.0 + ring_quality - 0.05 + wire_thickness;
     color += vec3(iron_tarnish * 0.4);
 
-    // ENHANCED: Multi-stage oxidation (gradient from bright iron to dark rust)
     color = mix(color, rust_color, oxidation * 0.35);
-    color = mix(color, dark_rust,
-                oxidation * oxidation * 0.15); // Deep rust in crevices
+    color = mix(color, dark_rust, oxidation * oxidation * 0.15);
 
-    // Ensure chainmail is CLEARLY visible - never blend into skin
     color = clamp(color, vec3(0.35), vec3(0.85));
   }
-  // LEATHER PTERUGES & BELT (tan/brown leather strips)
+
   else if (is_cloak) {
     vec3 N = normalize(v_worldNormal);
     vec3 V = normalize(vec3(0.0, 1.0, 0.35));
 
-    // Team-tinted cloak: blend input color with team color (u_color).
     vec3 team_tint = clamp(u_color, 0.0, 1.0);
     color = mix(color, team_tint, 0.75);
 
@@ -251,32 +197,27 @@ void main() {
     color *= shading + weave * 0.2;
     color += vec3(wrinkle * 0.12 + fresnel);
   }
-  // SCUTUM SHIELD (curved laminated wood with metal boss)
+
   else if (is_shield) {
-    // Shield boss (metal center dome for deflection)
+
     float boss_dist = length(v_worldPos.xy);
     float boss = smoothstep(0.12, 0.08, boss_dist) * 0.6;
     float boss_rim = smoothstep(0.14, 0.12, boss_dist) *
                      smoothstep(0.10, 0.12, boss_dist) * 0.3;
 
-    // Laminated wood layers (3-ply construction visible at edges)
     float edge_dist = max(abs(v_worldPos.x), abs(v_worldPos.y));
     float edge_wear = smoothstep(0.42, 0.48, edge_dist);
     float wood_layers = edge_wear * noise(uv * 40.0) * 0.25;
 
-    // Leather/linen facing (painted surface)
     float fabric_grain = noise(uv * 25.0) * 0.08;
     float canvas_weave = sin(uv.x * 60.0) * sin(uv.y * 58.0) * 0.04;
 
-    // Metal edging (bronze trim around perimeter)
     float metal_edge = smoothstep(0.46, 0.48, edge_dist) * 0.45;
     vec3 bronze_edge = vec3(0.75, 0.55, 0.30);
 
-    // Battle damage (dents, cuts, scratches)
     float dents = noise(uv * 8.0) * 0.15;
     float cuts = step(0.92, noise(uv * 12.0)) * 0.25;
 
-    // Boss metallic sheen (polished bronze)
     vec3 V = normalize(vec3(0.0, 1.0, 0.5));
     float view_angle = max(dot(normalize(v_worldNormal), V), 0.0);
     float boss_sheen = pow(view_angle, 10.0) * boss * 0.55;
@@ -287,15 +228,15 @@ void main() {
     color = mix(color, bronze_edge, metal_edge);
     color -= vec3(dents * 0.08 + cuts * 0.15);
   }
-  // BOW & ARROWS (composite wood, sinew, horn)
+
   else if (is_weapon) {
-    // Composite bow construction (wood core, horn belly, sinew back)
+
     bool is_bow_limb = (v_bodyHeight > 0.3 && v_bodyHeight < 0.9);
     bool is_grip = (v_bodyHeight >= 0.4 && v_bodyHeight <= 0.6);
     bool is_string = (v_bodyHeight < 0.05 || v_bodyHeight > 0.95);
 
     if (is_bow_limb) {
-      // Wood grain with horn lamination
+
       float wood_grain = noise(uv * 35.0) * 0.18;
       float horn_banding = abs(sin(v_bodyHeight * 25.0)) * 0.12;
 
@@ -306,99 +247,80 @@ void main() {
       color *= 1.0 + wood_grain + horn_banding;
       color += vec3(horn_sheen);
     } else if (is_grip) {
-      // Leather wrap with cord binding
+
       float leather_wrap = noise(uv * 20.0) * 0.14;
       float cord_binding = step(0.92, fract(v_worldPos.y * 30.0)) * 0.15;
-      color *= vec3(0.55, 0.42, 0.32); // Dark leather
+      color *= vec3(0.55, 0.42, 0.32);
       color *= 1.0 + leather_wrap;
       color += vec3(cord_binding);
     } else if (is_string) {
-      // Sinew bowstring (natural fiber)
-      color = vec3(0.72, 0.68, 0.60); // Natural sinew color
+
+      color = vec3(0.72, 0.68, 0.60);
       float fiber_twist = noise(uv * 80.0) * 0.10;
       color *= 1.0 + fiber_twist;
     }
   }
-  // BRONZE GREAVES (shin guards - polished bronze with high shine)
+
   else if (is_greaves) {
-    // Rich polished bronze base (warm golden metallic)
+
     vec3 bronze_base = vec3(0.88, 0.72, 0.45);
     vec3 bronze_highlight = vec3(1.0, 0.92, 0.75);
     vec3 bronze_shadow = vec3(0.55, 0.42, 0.25);
-    
-    // Apply bronze base
+
     color = mix(color, bronze_base, 0.90);
-    
-    // Micro brushed metal texture (fine polish lines)
+
     float brushed = abs(sin(v_worldPos.y * 120.0)) * 0.015;
-    
-    // View direction for specular
+
     vec3 V = normalize(vec3(0.15, 0.85, 0.5));
     vec3 N = normalize(v_worldNormal);
     float NdotV = max(dot(N, V), 0.0);
-    
-    // Primary specular (sharp highlight - polished metal)
+
     float spec_primary = pow(NdotV, 32.0) * 1.2;
-    
-    // Secondary specular (broader shine)
+
     float spec_secondary = pow(NdotV, 8.0) * 0.45;
-    
-    // Metallic fresnel (bright edges)
+
     float fresnel = pow(1.0 - NdotV, 3.0) * 0.65;
-    
-    // Anisotropic highlight (stretched along greave height)
+
     float aniso = pow(abs(sin(v_worldPos.y * 40.0 + NdotV * 3.14)), 4.0) * 0.25;
-    
-    // Environment reflection (sky above, ground below)
+
     float env_up = max(N.y, 0.0) * 0.35;
     float env_side = (1.0 - abs(N.y)) * 0.20;
-    
-    // Combine all shine effects
+
     vec3 shine = bronze_highlight * (spec_primary + spec_secondary + aniso);
     shine += vec3(fresnel * 0.8, fresnel * 0.7, fresnel * 0.5);
     shine += bronze_base * (env_up + env_side);
-    
+
     color += shine;
     color += vec3(brushed);
-    
-    // Ensure bright metallic finish
+
     color = clamp(color, bronze_shadow, vec3(1.0));
   }
 
   color = clamp(color, 0.0, 1.0);
 
-  // === PHASE 4: ADVANCED FEATURES ===
-  // Environmental interactions and battle wear (procedural, no new uniforms
-  // needed)
-
-  // Battle-hardened appearance (more wear on lower body parts)
   float campaign_wear = (1.0 - v_bodyHeight * 0.6) * 0.15;
   float dust_accumulation = noise(v_worldPos.xz * 8.0) * campaign_wear * 0.12;
 
-  // Rain streaks (vertical weathering patterns)
   float rain_streaks =
       smoothstep(0.85, 0.92,
                  noise(v_worldPos.xz * 2.5 + vec2(0.0, v_worldPos.y * 8.0))) *
-      v_bodyHeight * 0.08; // More visible on upper body
+      v_bodyHeight * 0.08;
 
-  // Mud splatter (lower body, procedural based on position)
-  float mud_height =
-      smoothstep(0.5, 0.2, v_bodyHeight); // Concentrated at feet/legs
+  float mud_height = smoothstep(0.5, 0.2, v_bodyHeight);
   float mud_pattern =
       step(0.75, noise(v_worldPos.xz * 12.0 + v_worldPos.y * 3.0));
   float mud_splatter = mud_height * mud_pattern * 0.18;
   vec3 mud_color = vec3(0.22, 0.18, 0.14);
 
-  // Apply environmental effects
-  color -= vec3(dust_accumulation);            // Dust darkens surfaces
-  color -= vec3(rain_streaks * 0.5);           // Rain creates dark streaks
-  color = mix(color, mud_color, mud_splatter); // Mud obscures material
+  color -= vec3(dust_accumulation);
+  color -= vec3(rain_streaks * 0.5);
+  color = mix(color, mud_color, mud_splatter);
 
-  // Lighting model per material
   vec3 light_dir = normalize(vec3(1.0, 1.15, 1.0));
   float n_dot_l = dot(normal, light_dir);
 
-  float wrap_amount = is_helmet ? 0.15 : (is_armor ? 0.22 : (is_greaves ? 0.15 : 0.38));
+  float wrap_amount =
+      is_helmet ? 0.15 : (is_armor ? 0.22 : (is_greaves ? 0.15 : 0.38));
   float diff = max(n_dot_l * (1.0 - wrap_amount) + wrap_amount, 0.22);
 
   if (is_helmet || is_greaves) {

+ 6 - 15
assets/shaders/archer_roman_republic.vert

@@ -34,9 +34,8 @@ void main() {
   vec3 position = a_position;
   vec3 normal = a_normal;
 
-  // Cloak back drape (Material ID 5)
   if (u_materialId == 5) {
-    float v = 1.0 - a_texCoord.y; // 1 = top, 0 = bottom
+    float v = 1.0 - a_texCoord.y;
     float u = a_texCoord.x;
     float x_norm = (u - 0.5) * 2.0;
 
@@ -57,7 +56,6 @@ void main() {
     position.y += wave * move;
   }
 
-  // Cloak shoulder cape (Material ID 6)
   if (u_materialId == 6) {
     float u = a_texCoord.x;
     float v = a_texCoord.y;
@@ -78,7 +76,6 @@ void main() {
     position.y += flutter;
   }
 
-  // Shield curving: bend flat rectangle into scutum curve (materialId=4)
   if (u_materialId == 4) {
     float curveRadius = 0.55;
     float curveAmount = 0.45;
@@ -95,7 +92,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -105,15 +101,15 @@ void main() {
   vec4 modelPos = u_model * vec4(position, 1.0);
   vec3 worldPos = modelPos.xyz;
 
-  // Only add battle-wear deformation to armored pieces (not skin or cloth)
-  bool deformArmor = (u_materialId == 1 || u_materialId == 2 ||
-                      u_materialId == 4 || u_materialId == 3 || u_materialId == 5);
+  bool deformArmor =
+      (u_materialId == 1 || u_materialId == 2 || u_materialId == 4 ||
+       u_materialId == 3 || u_materialId == 5);
 
   vec3 batteredPos = worldPos;
   vec3 offsetPos = worldPos;
 
   if (deformArmor) {
-    // Procedural denting and battle damage for armor/helmet/shield/weapons
+
     float dentSeed = hash13(worldPos * 0.75 + worldNormal * 0.22);
     float hammerNoise = sin(worldPos.y * 14.3 + dentSeed * 12.56);
     vec3 dentOffset = worldNormal * ((dentSeed - 0.5) * 0.0095);
@@ -136,28 +132,23 @@ void main() {
   v_bitangent = b;
 
   float height = offsetPos.y;
-  // Keep armor/material selection stable: 1.0 only for armor material.
+
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
-  // Body height normalization
   float torsoMin = 0.55;
   float torsoMax = 1.65;
   v_bodyHeight =
       clamp((offsetPos.y - torsoMin) / (torsoMax - torsoMin), 0.0, 1.0);
 
-  // Light helmet detail attributes
   float conicalHeight = smoothstep(1.55, 1.75, height);
   float browBandRegion =
       smoothstep(1.48, 1.52, height) * smoothstep(1.56, 1.52, height);
   v_helmetDetail = conicalHeight * 0.6 + browBandRegion * 0.4;
 
-  // Chainmail ring phase
   v_chainmailPhase = fract(offsetPos.x * 32.0 + offsetPos.z * 32.0);
 
-  // Leather wear and tension
   float tensionSeed = hash13(offsetPos * 0.42 + worldNormal * 1.5);
   v_leatherWear = tensionSeed * (0.7 + v_bodyHeight * 0.3);
 
-  // Surface curvature indicator
   v_curvature = length(vec2(worldNormal.x, worldNormal.z));
 }

+ 3 - 3
assets/shaders/basic.frag

@@ -36,13 +36,13 @@ vec3 proceduralMaterialVariation(vec3 baseColor, vec3 worldPos, vec3 normal) {
   vec3 variation = baseColor;
 
   if (avgColor < 0.40) {
-    // Metal/dark materials
+
     float metalNoise = noise(uv * 9.0) * 0.018;
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float fresnel = pow(1.0 - viewAngle, 2.0) * 0.10;
     variation = baseColor + vec3(metalNoise + fresnel);
   } else if (avgColor > 0.65) {
-    // Cloth/bright materials
+
     float weaveX = sin(worldPos.x * 55.0);
     float weaveZ = sin(worldPos.z * 55.0);
     float weavePattern = weaveX * weaveZ * 0.025;
@@ -53,7 +53,7 @@ vec3 proceduralMaterialVariation(vec3 baseColor, vec3 worldPos, vec3 normal) {
 
     variation = baseColor * (1.0 + clothNoise + weavePattern) + vec3(sheen);
   } else {
-    // Leather/medium materials
+
     float leatherNoise = noise(uv * 5.5);
     float blotches = noise(uv * 1.8) * 0.12 - 0.06;
     variation = baseColor * (1.0 + leatherNoise * 0.14 - 0.07 + blotches);

+ 22 - 55
assets/shaders/bridge.frag

@@ -1,20 +1,15 @@
 #version 330 core
 
-in vec3 v_normal;   // world-space normal
-in vec2 v_texCoord; // mesh UVs (used for optional fog/dirt overlay)
-in vec3 v_worldPos; // world-space position
+in vec3 v_normal;
+in vec2 v_texCoord;
+in vec3 v_worldPos;
 
-uniform vec3 u_color; // base stone tint
-uniform vec3
-    u_lightDirection; // world-space light dir (doesn't need to be normalized)
-uniform sampler2D
-    u_fogTexture; // optional fog/dirt mask (0..1), tiled in UV space
+uniform vec3 u_color;
+uniform vec3 u_lightDirection;
+uniform sampler2D u_fogTexture;
 
 out vec4 FragColor;
 
-// ------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------
 const float PI = 3.14159265359;
 
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
@@ -24,7 +19,6 @@ mat2 rot(float a) {
   return mat2(c, -s, s, c);
 }
 
-// Hash / Noise
 float hash(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * 0.1031);
   p3 += dot(p3, p3.yzx + 33.33);
@@ -50,13 +44,11 @@ float fbm(vec2 p) {
   return v;
 }
 
-// 2D random vector for Worley
 vec2 hash2(vec2 p) {
   float n = sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123;
   return fract(vec2(n, n * 1.2154));
 }
 
-// Worley (Voronoi) distances: returns F1 and F2 (nearest & second-nearest)
 vec2 worleyF(vec2 p) {
   vec2 n = floor(p);
   vec2 f = fract(p);
@@ -66,9 +58,7 @@ vec2 worleyF(vec2 p) {
     for (int i = -1; i <= 1; ++i) {
       vec2 g = vec2(float(i), float(j));
       vec2 o = hash2(n + g);
-      vec2 r =
-          (g + o) -
-          f; // distance from current frac coord to neighbor's jittered center
+      vec2 r = (g + o) - f;
       float d = dot(r, r);
       if (d < F1) {
         F2 = F1;
@@ -81,12 +71,10 @@ vec2 worleyF(vec2 p) {
   return vec2(sqrt(F1), sqrt(F2));
 }
 
-// Fresnel (Schlick)
 float fresnelSchlick(float cosTheta, float F0) {
   return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
 }
 
-// Minimal GGX specular (Smith-Schlick)
 float ggxSpecular(vec3 N, vec3 V, vec3 L, float rough, float F0) {
   vec3 H = normalize(V + L);
   float NdotV = max(dot(N, V), 0.0);
@@ -100,7 +88,7 @@ float ggxSpecular(vec3 N, vec3 V, vec3 L, float rough, float F0) {
   float D = a2 / max(PI * denom * denom, 1e-4);
 
   float k = (a + 1.0);
-  k = (k * k) * 0.125; // (a+1)^2 / 8
+  k = (k * k) * 0.125;
   float Gv = NdotV / (NdotV * (1.0 - k) + k);
   float Gl = NdotL / (NdotL * (1.0 - k) + k);
   float G = Gv * Gl;
@@ -109,95 +97,74 @@ float ggxSpecular(vec3 N, vec3 V, vec3 L, float rough, float F0) {
   return (D * G * F) / max(4.0 * NdotV * NdotL, 1e-4);
 }
 
-// ------------------------------------------------------------
-// Main
-// ------------------------------------------------------------
 void main() {
-  // World-anchored mapping to avoid UV tiling
+
   vec2 uv = v_worldPos.xz * 0.6;
 
-  // Irregular stone layout via Worley
   vec2 F = worleyF(uv * 1.2);
-  float edgeMetric = F.y - F.x; // small near borders (mortar)
-  float stoneMask =
-      smoothstep(0.05, 0.30, edgeMetric); // 1 inside stones, 0 mortar
+  float edgeMetric = F.y - F.x;
+  float stoneMask = smoothstep(0.05, 0.30, edgeMetric);
   float mortarMask = 1.0 - stoneMask;
 
-  // Per-cell jitter to vary stone shapes
   vec2 cell = floor(uv * 1.2);
   float cellRnd = hash(cell);
-  vec2 local = fract(uv); // local coords inside cell (0..1)
-  vec2 uvVar = (rot(cellRnd * 6.2831853) * (local - 0.5) + 0.5) +
-               floor(uv); // shape drift
+  vec2 local = fract(uv);
+  vec2 uvVar = (rot(cellRnd * 6.2831853) * (local - 0.5) + 0.5) + floor(uv);
 
-  // Albedo variation
   float varLow = (fbm(uv * 0.5) - 0.5) * 0.20;
   float varMid = (fbm(uvVar * 3.0) - 0.5) * 0.15;
   float grain = (noise(uvVar * 18.0) - 0.5) * 0.08;
 
   vec3 stoneColor = u_color * (1.0 + varLow + varMid + grain);
-  vec3 mortarColor = u_color * 0.55; // keep mortar darker & flatter
+  vec3 mortarColor = u_color * 0.55;
 
-  // Micro cracks inside stones
   float crack = smoothstep(0.02, 0.0, abs(noise(uv * 10.0) - 0.5)) * 0.25;
   stoneColor *= (1.0 - crack * stoneMask);
 
-  // Ambient occlusion / cavity
-  float cavity = smoothstep(0.0, 0.18, edgeMetric); // 0 at border, 1 inside
+  float cavity = smoothstep(0.0, 0.18, edgeMetric);
   float ao = mix(0.55, 1.0, cavity) * (0.92 + 0.08 * fbm(uv * 2.5));
 
-  // Height map: stones bulge slightly; mortar recessed
   float microBump = (fbm(uvVar * 4.0) - 0.5) * 0.06 * stoneMask;
   float macroWarp = (fbm(uv * 1.2) - 0.5) * 0.03 * stoneMask;
   float mortarDip = -0.06 * mortarMask;
   float h = microBump + macroWarp + mortarDip;
 
-  // Normal perturbation from screen-space height derivatives
   float sx = dFdx(h);
   float sy = dFdy(h);
-  float bumpStrength = 14.0; // increase for sharper normals
+  float bumpStrength = 14.0;
   vec3 nBump = normalize(vec3(-sx * bumpStrength, 1.0, -sy * bumpStrength));
 
-  // Blend with geometric normal to keep stability
   vec3 Ng = normalize(v_normal);
   vec3 N = normalize(mix(Ng, nBump, 0.65));
 
-  // Lighting vectors
   vec3 L = normalize(u_lightDirection);
-  vec3 V = normalize(vec3(0.0, 0.9, 0.4)); // plausible fixed view dir
+  vec3 V = normalize(vec3(0.0, 0.9, 0.4));
 
-  // Diffuse (Lambert) + AO
   float NdotL = max(dot(N, L), 0.0);
   float diffuse = NdotL;
 
-  // Roughness varies: smoother on worn tops, rougher near edges
   float steep = saturate(length(vec2(sx, sy)) * bumpStrength);
   float roughness = clamp(mix(0.65, 0.95, steep), 0.02, 1.0);
-  float F0 = 0.035; // dielectric stone
+  float F0 = 0.035;
 
   float spec = ggxSpecular(N, V, L, roughness, F0);
 
-  // Base color: stone vs mortar
   vec3 baseColor = mix(mortarColor, stoneColor, stoneMask);
 
-  // Simple hemispheric fill
   vec3 hemiSky = vec3(0.18, 0.24, 0.30);
   vec3 hemiGround = vec3(0.12, 0.10, 0.09);
   float hemi = N.y * 0.5 + 0.5;
 
   vec3 litColor = baseColor * (0.35 + 0.70 * diffuse) * ao;
-  litColor += mix(hemiGround, hemiSky, hemi) * 0.15; // skylight
-  litColor += vec3(1.0) * spec * 0.25;               // subtle spec
+  litColor += mix(hemiGround, hemiSky, hemi) * 0.15;
+  litColor += vec3(1.0) * spec * 0.25;
 
-  // Dirt/grime accumulation in cavities (slight desaturation & darken)
   float grime = (1.0 - cavity) * 0.25 * (0.8 + 0.2 * noise(uv * 7.0));
   float gray = dot(litColor, vec3(0.299, 0.587, 0.114));
   litColor = mix(litColor, vec3(gray * 0.9), grime);
 
-  // Optional fog/dirt overlay from texture (soft multiply; leave at 1 if
-  // unused)
-  float fogMask = texture(u_fogTexture, v_texCoord).r; // 0..1
-  float fogAmt = 1.0 - fogMask; // treat dark as “more fog/dirt”
+  float fogMask = texture(u_fogTexture, v_texCoord).r;
+  float fogAmt = 1.0 - fogMask;
   litColor *= mix(1.0, 0.85, fogAmt * 0.5);
 
   FragColor = vec4(litColor, 1.0);

+ 1 - 2
assets/shaders/bridge.vert

@@ -12,11 +12,10 @@ out vec2 v_texCoord;
 out vec3 v_worldPos;
 
 void main() {
-  // Transform normal to world space
+
   v_normal = normalize(mat3(transpose(inverse(u_model))) * a_normal);
   v_texCoord = a_texCoord;
 
-  // World position for lighting and fog
   v_worldPos = vec3(u_model * vec4(a_position, 1.0));
 
   gl_Position = u_mvp * vec4(a_position, 1.0);

+ 5 - 18
assets/shaders/catapult.frag

@@ -1,10 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CATAPULT FRAGMENT SHADER
-// Wood and metal materials for siege equipment
-// ============================================================================
-
 in vec3 v_normal;
 in vec2 v_texCoord;
 in vec3 v_worldPos;
@@ -35,14 +30,12 @@ float noise(vec2 p) {
   return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
 }
 
-// Wood grain pattern
 float wood_grain(vec2 p) {
   float grain = sin(p.y * 30.0 + noise(p * 5.0) * 3.0) * 0.5 + 0.5;
   float fine_grain = noise(p * 50.0) * 0.2;
   return grain * 0.15 + fine_grain;
 }
 
-// Metal surface pattern
 float metal_surface(vec2 p) {
   float scratches = noise(p * 80.0) * 0.1;
   float polish = noise(p * 20.0) * 0.05;
@@ -59,7 +52,6 @@ void main() {
   vec2 uv = v_worldPos.xz * 4.0;
   float avg_color = (color.r + color.g + color.b) / 3.0;
 
-  // Detect material type from color
   bool is_wood =
       (color.r > color.b * 1.2 && avg_color > 0.25 && avg_color < 0.60);
   bool is_metal =
@@ -69,41 +61,37 @@ void main() {
   bool is_leather =
       (avg_color > 0.20 && avg_color < 0.45 && color.r > color.b * 1.3);
 
-  // === WOOD MATERIALS ===
   if (is_wood) {
     float grain = wood_grain(v_worldPos.xz);
     float knots = step(0.92, noise(uv * 3.0)) * 0.15;
 
-    // Wood has subtle sheen along grain
     float view_angle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.3))));
     float wood_sheen = pow(1.0 - view_angle, 6.0) * 0.08;
 
     color *= 1.0 + grain - knots;
     color += vec3(wood_sheen);
   }
-  // === METAL MATERIALS ===
+
   else if (is_metal) {
     float surface = metal_surface(v_worldPos.xz);
 
-    // Metal has stronger specular
     float view_angle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.4))));
     float metal_sheen = pow(1.0 - view_angle, 4.0) * 0.18;
 
-    // Slight rust/patina on exposed metal
     float patina = noise(uv * 8.0) * 0.08;
 
     color *= 1.0 + surface - patina;
     color += vec3(metal_sheen);
   }
-  // === ROPE MATERIALS ===
+
   else if (is_rope) {
-    // Twisted fiber pattern
+
     float twist = sin(v_worldPos.y * 40.0 + v_worldPos.x * 10.0) * 0.08;
     float fiber = noise(uv * 60.0) * 0.12;
 
     color *= 1.0 + twist + fiber - 0.05;
   }
-  // === LEATHER MATERIALS ===
+
   else if (is_leather) {
     float grain = noise(uv * 20.0) * 0.15;
     float crease = noise(uv * 8.0) * 0.10;
@@ -114,7 +102,7 @@ void main() {
     color *= 1.0 + grain - crease;
     color += vec3(leather_sheen);
   }
-  // === DEFAULT ===
+
   else {
     float detail = noise(uv * 15.0) * 0.08;
     color *= 1.0 + detail - 0.04;
@@ -122,7 +110,6 @@ void main() {
 
   color = clamp(color, 0.0, 1.0);
 
-  // Lighting
   vec3 light_dir = normalize(vec3(1.0, 1.2, 0.8));
   float n_dot_l = dot(normal, light_dir);
   float wrap = 0.35;

+ 0 - 6
assets/shaders/catapult.vert

@@ -1,10 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CATAPULT VERTEX SHADER
-// Simple vertex transformation for siege equipment
-// ============================================================================
-
 layout(location = 0) in vec3 a_position;
 layout(location = 1) in vec3 a_normal;
 layout(location = 2) in vec2 a_texCoord;
@@ -22,7 +17,6 @@ void main() {
   v_texCoord = a_texCoord;
   v_worldPos = vec3(u_model * vec4(a_position, 1.0));
 
-  // Material region based on height (0=base/wheels, 1=frame, 2=arm/mechanism)
   if (v_worldPos.y < 0.25) {
     v_materialRegion = 0.0;
   } else if (v_worldPos.y < 0.55) {

+ 3 - 4
assets/shaders/firecamp.vert

@@ -2,9 +2,8 @@
 layout(location = 0) in vec3 aPos;
 layout(location = 1) in vec2 aTexCoord;
 
-// Instance attributes
-layout(location = 3) in vec4 i_posIntensity; // x, y, z, intensity
-layout(location = 4) in vec4 i_radiusPhase;  // radius, phase, duration, unused
+layout(location = 3) in vec4 i_posIntensity;
+layout(location = 4) in vec4 i_radiusPhase;
 
 uniform mat4 u_viewProj;
 uniform float u_time;
@@ -33,7 +32,7 @@ void main() {
   vec3 upVec = vec3(0.0, 1.0, 0.0);
 
   float planeId = floor(aPos.z + 0.5);
-  float angle = planeId * 2.0943951; // 120 degrees
+  float angle = planeId * 2.0943951;
   float c = cos(angle);
   float s = sin(angle);
   vec3 horizontalAxis = normalize(rightVec * c + forwardVec * s);

+ 1 - 5
assets/shaders/grid.frag

@@ -7,11 +7,10 @@ in vec3 v_worldPos;
 uniform vec3 u_gridColor;
 uniform vec3 u_lineColor;
 uniform float u_cellSize;
-uniform float u_thickness; // fraction of cell (0..0.5)
+uniform float u_thickness;
 
 out vec4 FragColor;
 
-// Hash for subtle per-cell variation
 float hash12(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * 0.1031);
   p3 += dot(p3, p3.yzx + 33.33);
@@ -23,7 +22,6 @@ void main() {
   vec2 f = fract(coord) - 0.5;
   vec2 af = abs(f);
 
-  // Anti-aliased lines using fwidth
   float fw = fwidth(af.x);
   float lineX =
       1.0 - smoothstep(0.5 - u_thickness - fw, 0.5 - u_thickness + fw, af.x);
@@ -32,13 +30,11 @@ void main() {
       1.0 - smoothstep(0.5 - u_thickness - fw, 0.5 - u_thickness + fw, af.y);
   float lineMask = max(lineX, lineY);
 
-  // Emphasize major lines every 5 cells
   vec2 cell = floor(coord);
   float major =
       (abs(mod(cell.x, 5.0)) < 0.5 || abs(mod(cell.y, 5.0)) < 0.5) ? 1.0 : 0.0;
   vec3 lineCol = mix(u_lineColor, u_lineColor * 1.2, major);
 
-  // Subtle per-cell brightness jitter
   float jitter = (hash12(cell) - 0.5) * 0.06;
   vec3 base = u_gridColor * (1.0 + jitter);
 

+ 7 - 14
assets/shaders/ground_plane.frag

@@ -18,13 +18,12 @@ uniform float u_soilBlendSharpness;
 uniform float u_ambientBoost;
 uniform vec3 u_lightDir;
 
-// Ground-type-specific uniforms
-uniform float u_snowCoverage;    // 0-1: snow accumulation
-uniform float u_moistureLevel;   // 0-1: wetness/dryness
-uniform float u_crackIntensity;  // 0-1: ground cracking
-uniform float u_grassSaturation; // 0-1.5: grass color intensity
-uniform float u_soilRoughness;   // 0-1: soil texture roughness
-uniform vec3 u_snowColor;        // Snow tint color
+uniform float u_snowCoverage;
+uniform float u_moistureLevel;
+uniform float u_crackIntensity;
+uniform float u_grassSaturation;
+uniform float u_soilRoughness;
+uniform vec3 u_snowColor;
 
 float hash21(vec2 p) {
   return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
@@ -71,7 +70,6 @@ void main() {
   soilMix = max(soilMix, mudPatch * 0.85);
   vec3 baseCol = mix(grassCol, u_soilColor, soilMix);
 
-  // Ground cracking effect (for dry Mediterranean terrain)
   if (u_crackIntensity > 0.01) {
     float crackNoise1 = noise21(wuv * 8.0);
     float crackNoise2 = noise21(wuv * 16.0 + vec2(42.0, 17.0));
@@ -81,7 +79,6 @@ void main() {
     baseCol *= crackDarkening;
   }
 
-  // Snow coverage effect (for alpine terrain)
   if (u_snowCoverage > 0.01) {
     float snowNoise = fbm(wuv * 0.5 + vec2(123.0, 456.0));
     float snowAccumulation = smoothstep(0.3, 0.7, snowNoise);
@@ -92,11 +89,9 @@ void main() {
     baseCol = mix(baseCol, snowTinted, snowMask * 0.85);
   }
 
-  // Apply grass saturation modifier
   vec3 grayLevel = vec3(dot(baseCol, vec3(0.299, 0.587, 0.114)));
   baseCol = mix(grayLevel, baseCol, u_grassSaturation);
 
-  // Moisture effect
   float wetDarkening = 1.0 - u_moistureLevel * 0.15;
   baseCol *= wetDarkening;
 
@@ -119,7 +114,7 @@ void main() {
   float ndl = max(dot(nMicro, L), 0.0);
   float ambient = 0.40;
   float fres = pow(1.0 - max(dot(nMicro, vec3(0, 1, 0)), 0.0), 2.0);
-  // Surface roughness affects specular - wet surfaces are shinier
+
   float surfaceRoughness = mix(0.65, 0.95, u_soilRoughness);
   surfaceRoughness = mix(surfaceRoughness, 0.45, u_moistureLevel * 0.5);
   float specContrib = fres * 0.08 * (1.0 - surfaceRoughness);
@@ -127,8 +122,6 @@ void main() {
   float shade = ambient + ndl * 0.65 + specContrib;
   vec3 lit = col * shade * (u_ambientBoost + heightTint);
 
-  // Debug tint to make displacement obvious: blue for negative, red for
-  // positive
   vec3 dispTint = mix(vec3(0.3, 0.5, 1.0), vec3(1.0, 0.4, 0.3),
                       smoothstep(-1.0, 1.0, v_disp));
   lit = mix(lit, lit * dispTint, 0.25);

+ 1 - 6
assets/shaders/ground_plane.vert

@@ -44,31 +44,26 @@ void main() {
       6.2831853;
   vec2 uv = rot2(ang) * (wp.xz + u_noiseOffset);
 
-  // Gentle domain warp to break straight lines and angular patterns
   float warpBase = fbm2(uv * 0.35);
   float warpOff = fbm2((uv + vec2(11.3, -7.7)) * 0.35);
   vec2 uvWarp = uv + (vec2(warpBase, warpOff) - 0.5) * 0.7;
 
-  // Tighten irregularities so bumps are closer together
   float freq = max(u_heightNoiseFrequency * 3.0, 1.1);
   float base = fbm2(uvWarp * freq * 0.65);
   float detail = fbm2(uvWarp * freq * 1.6);
   float fine = noise21(uvWarp * freq * 3.2);
   float h = (base * 0.50 + detail * 0.35 + fine * 0.15) * 2.0 - 1.0;
-  // Soften extremes to avoid blocky plateaus
+
   h = h - 0.2 * h * h * h;
 
-  // Ensure a clearly visible base amount of warping but keep it grounded
   float strength = max(u_heightNoiseStrength, 0.35);
   float amp = clamp(strength * 1.8, 0.10, 0.65);
   float disp = h * amp;
 
-  // Subtle sine warp just to guarantee non-flatness, but keep it small
   disp += sin(uvWarp.x * 1.8) * 0.05 + sin(uvWarp.y * 2.1) * 0.05;
 
   wp.y += disp;
 
-  // Estimate slope to bend normals with the displaced surface
   float gradStep = max(0.15, 0.35 / max(freq * 1.1, 0.05));
   float h_base_x = fbm2((uvWarp + vec2(gradStep, 0.0)) * freq * 0.65);
   float h_det_x = fbm2((uvWarp + vec2(gradStep, 0.0)) * freq * 1.6);

+ 1 - 2
assets/shaders/healer.frag

@@ -44,7 +44,6 @@ void main() {
   vec2 uv = v_worldPos.xz * 4.5;
   float avgColor = (color.r + color.g + color.b) / 3.0;
 
-  // Light cloth material (healer robes)
   if (avgColor > 0.65) {
     float weave = clothWeave(v_worldPos.xz);
     float folds = noise(uv * 8.0) * 0.13;
@@ -55,7 +54,7 @@ void main() {
     color *= 1.0 + weave + folds - 0.03;
     color += vec3(clothSheen);
   }
-  // Leather/darker elements
+
   else if (avgColor > 0.30) {
     float leatherGrain = noise(uv * 14.0) * 0.14;
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.4))));

+ 3 - 3
assets/shaders/healer_carthage.frag

@@ -191,7 +191,7 @@ void main() {
     base_color *= texture(u_texture, v_texCoord).rgb;
   }
 
-  vec3 teamDefault = vec3(0.0); // remove purple bias to keep true team hue
+  vec3 teamDefault = vec3(0.0);
   vec3 teamColor = clamp(mix(teamDefault, u_color, 0.75), 0.0, 1.0);
 
   bool is_body = (u_materialId == 0);
@@ -218,10 +218,10 @@ void main() {
   float ao = ao_folds;
 
   if (is_body) {
-    vec3 skin_base = vec3(0.08, 0.07, 0.065); // deep brown/black skin tone
+    vec3 skin_base = vec3(0.08, 0.07, 0.065);
     float legs = smoothstep(0.05, 0.50, v_bodyHeight) *
                  (1.0 - smoothstep(0.52, 0.70, v_bodyHeight));
-    float limb_team = clamp(legs, 0.0, 1.0); // only legs get team tint
+    float limb_team = clamp(legs, 0.0, 1.0);
     skin_base = mix(skin_base, mix(skin_base, teamColor, 0.92), limb_team);
     float tone_noise = fbm(v_worldPos.xz * 3.1) - 0.5;
     albedo = clamp(skin_base + vec3(tone_noise) * 0.04, 0.0, 1.0);

+ 0 - 11
assets/shaders/healer_carthage.vert

@@ -1,10 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CARTHAGINIAN HEALER VERTEX SHADER
-// Flowing Mediterranean robes with natural draping
-// ============================================================================
-
 layout(location = 0) in vec3 a_position;
 layout(location = 1) in vec3 a_normal;
 layout(location = 2) in vec2 a_texCoord;
@@ -39,7 +34,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space for detailed shading
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -53,11 +47,8 @@ void main() {
   v_texCoord = a_texCoord;
   v_worldPos = vec3(u_model * vec4(position, 1.0));
 
-  // Body height for cloth flow (0.0 = feet, 1.0 = head)
   v_bodyHeight = clamp((v_worldPos.y + 0.2) / 1.8, 0.0, 1.0);
 
-  // Phoenician robes drape differently - looser, more flowing folds
-  // Emphasis on chest/waist gather and lower hem flow
   float chestGather = smoothstep(1.05, 1.20, v_worldPos.y) *
                       smoothstep(1.35, 1.20, v_worldPos.y);
   float waistSash = smoothstep(0.80, 0.92, v_worldPos.y) *
@@ -70,13 +61,11 @@ void main() {
                            fold_wave * 0.35,
                        0.0, 1.2);
 
-  // Fabric wear pattern - more at edges and stress points
   float shoulderStress = smoothstep(1.05, 1.45, v_worldPos.y) * 0.35;
   float hemWear = smoothstep(0.55, 0.15, v_worldPos.y) * 0.45;
   v_fabricWear =
       hash13(v_worldPos * 0.4) * 0.22 + 0.14 + shoulderStress + hemWear;
 
-  // Keep material selection stable; armor layer only toggles for armor.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   gl_Position = u_mvp * vec4(position, 1.0);

+ 9 - 45
assets/shaders/healer_roman_republic.frag

@@ -1,15 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// ROMAN MEDICUS (HEALER) SHADER
-// Clean, practical Roman medical professional appearance with crisp textiles,
-// maintained leather, polished bronze tools, and soft wrap lighting.
-// ============================================================================
-
-// ============================================================================
-// INPUTS & OUTPUTS
-// ============================================================================
-
 in vec3 v_normal;
 in vec3 v_worldNormal;
 in vec3 v_tangent;
@@ -29,10 +19,6 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// UTILITY FUNCTIONS
-// ============================================================================
-
 float hash(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * 0.1031);
   p3 += dot(p3, p3.yzx + 33.33);
@@ -72,19 +58,15 @@ float triplanar_noise(vec3 pos, vec3 normal, float scale) {
   return xy * w.z + yz * w.x + zx * w.y;
 }
 
-// ============================================================================
-// ROMAN TEXTILE PATTERNS
-// ============================================================================
-
 float cloth_weave(vec2 p) {
-  // Tight Roman linen weave
+
   float warp_thread = sin(p.x * 72.0);
   float weft_thread = sin(p.y * 70.0);
   return warp_thread * weft_thread * 0.055;
 }
 
 float roman_linen(vec2 p) {
-  // Fine bleached Roman linen - crisp and clean
+
   float weave = cloth_weave(p);
   float fine_thread = noise(p * 95.0) * 0.06;
   float slub = fbm(p * 7.5) * 0.05;
@@ -92,17 +74,13 @@ float roman_linen(vec2 p) {
 }
 
 float roman_wool(vec2 p) {
-  // Coarser wool for cape/sash - more texture
+
   float coarse_weave = sin(p.x * 55.0) * sin(p.y * 52.0) * 0.08;
   float fiber_variation = noise(p * 65.0) * 0.09;
   float nap = fbm(p * 9.0) * 0.05;
   return coarse_weave + fiber_variation + nap;
 }
 
-// ============================================================================
-// PERTURBED NORMALS
-// ============================================================================
-
 vec3 perturb_linen_normal(vec3 N, vec3 T, vec3 B, vec2 uv) {
   float warp = sin(uv.x * 142.0) * 0.05;
   float weft = sin(uv.y * 138.0) * 0.05;
@@ -129,10 +107,6 @@ vec3 perturb_bronze_normal(vec3 N, vec3 T, vec3 B, vec2 uv) {
   return normalize(N + T * hammer + B * (hammer * 0.4 + ripple));
 }
 
-// ============================================================================
-// LIGHTING HELPERS
-// ============================================================================
-
 float D_GGX(float NdotH, float a) {
   float a2 = a * a;
   float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
@@ -188,9 +162,6 @@ vec3 apply_lighting(vec3 albedo, vec3 N, vec3 V, vec3 L, float roughness,
   return ambient * (0.56 + 0.44 * ao_strength) + light * ao_strength;
 }
 
-// ============================================================================
-// BEARD/FACIAL HAIR RENDERING (parity with Carthage healer)
-// ============================================================================
 float beard_density(vec2 uv, vec3 worldPos) {
   float strand_base = fbm(uv * 24.0) * 0.6;
   float curl_pattern = sin(uv.x * 80.0 + noise(uv * 40.0) * 3.0) * 0.2;
@@ -217,10 +188,6 @@ vec3 apply_beard_shading(vec3 base_skin, vec2 uv, vec3 normal, vec3 worldPos,
   return mix(base_skin, beard_color, density * beard_mask * 0.85);
 }
 
-// ============================================================================
-// MAIN FRAGMENT SHADER
-// ============================================================================
-
 void main() {
   vec3 base_color = u_color;
   if (u_useTexture) {
@@ -233,8 +200,6 @@ void main() {
   vec2 uv = v_worldPos.xz * 4.5;
   float avg_color = (base_color.r + base_color.g + base_color.b) / 3.0;
 
-  // Material ID: 0=body/skin, 1=tunica, 2=leather, 3=medical tools, 4=red
-  // trim/sash
   bool is_body = (u_materialId == 0);
   bool is_tunica = (u_materialId == 1);
   bool is_leather = (u_materialId == 2);
@@ -256,7 +221,6 @@ void main() {
       clamp(1.0 - (v_clothFolds * 0.52 + curvature * 0.78), 0.28, 1.0);
   float ao = ao_folds;
 
-  // BODY / SKIN
   if (is_body) {
     vec3 skin = base_color;
     float skin_detail = noise(uv * 24.0) * 0.06;
@@ -277,7 +241,7 @@ void main() {
     sheen = 0.06 + subdermal * 0.2;
     wrap = 0.46;
   }
-  // WHITE/CREAM LINEN TUNICA (main garment - bleached Roman style)
+
   else if (is_tunica) {
     vec3 tunic_base = vec3(0.95, 0.93, 0.90);
     albedo = tunic_base;
@@ -310,7 +274,7 @@ void main() {
     wrap = 0.54;
     ao *= 1.0 - dust * 0.35;
   }
-  // RED WOOL SASH/TRIM (military medicus identification)
+
   else if (is_red_trim) {
     float weave = roman_wool(v_worldPos.xz);
     float wool_tex = noise(uv * 58.0) * 0.10;
@@ -335,7 +299,7 @@ void main() {
     sheen = 0.10;
     wrap = 0.48;
   }
-  // LEATHER EQUIPMENT (medical bag, belt, sandals, straps)
+
   else if (is_leather) {
     float leather_grain = noise(uv * 16.0) * 0.16 * (1.0 + v_fabricWear * 0.25);
     float pores = noise(uv * 38.0) * 0.06;
@@ -362,7 +326,7 @@ void main() {
     sheen = 0.08;
     wrap = 0.46;
   }
-  // BRONZE MEDICAL IMPLEMENTS
+
   else if (is_medical_tools) {
     vec3 bronze_base = vec3(0.76, 0.56, 0.32);
 
@@ -385,7 +349,7 @@ void main() {
     sheen = 0.12;
     wrap = 0.42;
   }
-  // BODY/SKIN (Roman - clean-shaven face, hands, arms)
+
   else if (is_body) {
     float skin_detail = noise(uv * 28.0) * 0.07;
     float skin_subsurface = noise(uv * 8.0) * 0.04;
@@ -403,7 +367,7 @@ void main() {
     sheen = 0.05;
     wrap = 0.46;
   }
-  // DEFAULT (catch-all)
+
   else {
     float detail = noise(uv * 11.0) * 0.09;
     albedo *= 1.0 + detail - 0.05;

+ 1 - 6
assets/shaders/healer_roman_republic.vert

@@ -34,7 +34,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -48,10 +47,8 @@ void main() {
   v_texCoord = a_texCoord;
   v_worldPos = vec3(u_model * vec4(position, 1.0));
 
-  // Body height for cloth flow and detail placement (0.0 = feet, 1.0 = head)
   v_bodyHeight = clamp((v_worldPos.y + 0.2) / 1.8, 0.0, 1.0);
 
-  // Cloth fold intensity based on joint areas (elbows, waist, knees)
   float elbowFolds = smoothstep(1.15, 1.25, v_worldPos.y) *
                      smoothstep(1.35, 1.25, v_worldPos.y);
   float waistFolds = smoothstep(0.85, 0.95, v_worldPos.y) *
@@ -60,10 +57,8 @@ void main() {
                     smoothstep(0.65, 0.55, v_worldPos.y);
   v_clothFolds = (elbowFolds + waistFolds + kneeFolds) * 0.5;
 
-  // Fabric wear pattern (procedural based on world position)
-  v_fabricWear = hash13(v_worldPos * 0.5) * 0.3 + 0.2; // 0.2-0.5 range
+  v_fabricWear = hash13(v_worldPos * 0.5) * 0.3 + 0.2;
 
-  // Armor selection based solely on material ID.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   gl_Position = u_mvp * vec4(position, 1.0);

+ 129 - 184
assets/shaders/horse_archer_carthage.frag

@@ -1,10 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CARTHAGINIAN HORSE ARCHER - Rich Brown Leather & Team-Colored Cloak
-// Unique earth-tone palette with vibrant team colors
-// ============================================================================
-
 in vec3 v_normal;
 in vec2 v_texCoord;
 in vec3 v_worldPos;
@@ -18,16 +13,11 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// CONSTANTS & HELPERS
-// ============================================================================
-
 const float PI = 3.14159265359;
 
-// *** Core bases
 const vec3 LEATHER_BASE_BROWN = vec3(0.36, 0.22, 0.10);
-const vec3 BRONZE_BASE_COLOR  = vec3(0.86, 0.66, 0.36);
-const vec3 ARCHER_SKIN_BASE    = vec3(0.08, 0.07, 0.065);
+const vec3 BRONZE_BASE_COLOR = vec3(0.86, 0.66, 0.36);
+const vec3 ARCHER_SKIN_BASE = vec3(0.08, 0.07, 0.065);
 
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
 vec3 saturate3(vec3 x) { return clamp(x, 0.0, 1.0); }
@@ -66,10 +56,6 @@ float fbm(vec2 p) {
   return v;
 }
 
-// ============================================================================
-// PBR FUNCTIONS
-// ============================================================================
-
 float D_GGX(float NdotH, float roughness) {
   float a = roughness * roughness;
   float a2 = a * a;
@@ -93,16 +79,12 @@ vec3 F_Schlick(float cosTheta, vec3 F0) {
   return F0 + (1.0 - F0) * t5;
 }
 
-// ============================================================================
-// LEATHER TEXTURE - RICH BROWN PALETTE
-// ============================================================================
-
 vec3 brownLeatherPalette(float variation) {
   vec3 darkBrown = vec3(0.28, 0.18, 0.10);
-  vec3 redBrown  = vec3(0.45, 0.25, 0.15);
+  vec3 redBrown = vec3(0.45, 0.25, 0.15);
   vec3 warmBrown = vec3(0.55, 0.38, 0.22);
-  vec3 lightTan  = vec3(0.68, 0.52, 0.35);
-  
+  vec3 lightTan = vec3(0.68, 0.52, 0.35);
+
   if (variation < 0.33) {
     return mix(darkBrown, redBrown, variation * 3.0);
   } else if (variation < 0.66) {
@@ -123,7 +105,7 @@ float leatherGrain(vec2 uv, float scale) {
 float stitchPattern(vec2 uv, float spacing) {
   float stitch = fract(uv.y * spacing);
   stitch = smoothstep(0.4, 0.5, stitch) * smoothstep(0.6, 0.5, stitch);
-  float seamLine = smoothstep(0.48, 0.50, fract(uv.x * 4.0)) * 
+  float seamLine = smoothstep(0.48, 0.50, fract(uv.x * 4.0)) *
                    smoothstep(0.52, 0.50, fract(uv.x * 4.0));
   return stitch * seamLine;
 }
@@ -142,10 +124,6 @@ float oilSheen(vec3 pos, vec3 N, vec3 V) {
   return facing * facing * variation;
 }
 
-// ============================================================================
-// CLOAK & FABRIC
-// ============================================================================
-
 float cloakFolds(vec3 pos) {
   float folds = sin(pos.x * 8.0 + pos.y * 3.0) * 0.5 + 0.5;
   folds += sin(pos.z * 6.0 - pos.y * 2.0) * 0.3;
@@ -159,10 +137,6 @@ float fabricWeave(vec2 uv) {
   return warpX * weftY;
 }
 
-// ============================================================================
-// HORSE PATTERNS
-// ============================================================================
-
 float horseCoatPattern(vec2 uv) {
   float coarse = fbm(uv * 3.0) * 0.15;
   float fine = noise(uv * 25.0) * 0.08;
@@ -177,10 +151,6 @@ float furStrand(vec2 uv, float density) {
   return strand * 0.3 + variation * 0.2;
 }
 
-// ============================================================================
-// BOW WOOD
-// ============================================================================
-
 float woodGrainBow(vec3 pos) {
   float grain = sin(pos.y * 40.0 + fbm(pos.xy * 8.0) * 3.0);
   grain = grain * 0.5 + 0.5;
@@ -188,43 +158,36 @@ float woodGrainBow(vec3 pos) {
   return grain * 0.7 + rings * 0.3;
 }
 
-// ============================================================================
-// MAIN
-// ============================================================================
-
 void main() {
   vec3 baseColor = clamp(u_color, 0.0, 1.0);
   if (u_useTexture) {
     baseColor *= texture(u_texture, v_texCoord).rgb;
   }
-  
-  // Material IDs
-  bool is_rider_skin      = (u_materialId == 0);
-  bool is_armor           = (u_materialId == 1);
-  bool is_helmet          = (u_materialId == 2);
-  bool is_weapon          = (u_materialId == 3);
-  bool is_shield          = (u_materialId == 4);
-  bool is_rider_clothing  = (u_materialId == 5);
-  bool is_horse_hide      = (u_materialId == 6);
-  bool is_horse_mane      = (u_materialId == 7);
-  bool is_horse_hoof      = (u_materialId == 8);
-  bool is_saddle_leather  = (u_materialId == 9);
-  bool is_bridle          = (u_materialId == 10);
-  bool is_saddle_blanket  = (u_materialId == 11);
-  bool is_cloak           = (u_materialId == 12 || u_materialId == 13);
-  
+
+  bool is_rider_skin = (u_materialId == 0);
+  bool is_armor = (u_materialId == 1);
+  bool is_helmet = (u_materialId == 2);
+  bool is_weapon = (u_materialId == 3);
+  bool is_shield = (u_materialId == 4);
+  bool is_rider_clothing = (u_materialId == 5);
+  bool is_horse_hide = (u_materialId == 6);
+  bool is_horse_mane = (u_materialId == 7);
+  bool is_horse_hoof = (u_materialId == 8);
+  bool is_saddle_leather = (u_materialId == 9);
+  bool is_bridle = (u_materialId == 10);
+  bool is_saddle_blanket = (u_materialId == 11);
+  bool is_cloak = (u_materialId == 12 || u_materialId == 13);
+
   vec3 N = normalize(v_normal);
   vec3 V = normalize(vec3(0.0, 0.5, 1.0));
   vec3 L = normalize(vec3(0.5, 1.0, 0.4));
   vec3 H = normalize(L + V);
-  
+
   vec3 albedo = baseColor;
   float metallic = 0.0;
   float roughness = 0.5;
   float sheen = 0.0;
-  
-  // ========== MATERIAL SETUP ==========
-  
+
   if (is_rider_skin) {
     vec3 skinBase = ARCHER_SKIN_BASE;
     vec3 teamTint = mix(skinBase, baseColor, 0.2);
@@ -232,247 +195,235 @@ void main() {
     albedo = clamp(teamTint + vec3(toneNoise) * 0.04, 0.0, 1.0);
     metallic = 0.0;
     roughness = 0.58;
-    
+
   } else if (is_armor) {
-    // ====== RICH BROWN LEATHER ARMOR (UV-based, clearly rough leather) ======
-    vec2 leatherUV = v_texCoord * 6.0;                         // *** UV-driven
+
+    vec2 leatherUV = v_texCoord * 6.0;
     float leatherVar = fbm(leatherUV * 2.0);
     vec3 leatherPalette = brownLeatherPalette(leatherVar);
-    
-    // *** Core brown leather base, lightly tinted by team color
+
     vec3 tint = mix(vec3(1.0), baseColor, 0.25);
     vec3 leatherBase = mix(LEATHER_BASE_BROWN, leatherPalette, 0.5);
     albedo = leatherBase * tint;
     albedo = boostSaturation(albedo, 0.25);
-    
-    float grain    = leatherGrain(leatherUV, 8.0);              // *** stronger grain
-    float wear     = battleWear(v_worldPos);
-    float oil      = oilSheen(v_worldPos, N, V);
-    float stitches = stitchPattern(leatherUV, 18.0);            // *** visible seams
-    
-    // Strong grain contrast
+
+    float grain = leatherGrain(leatherUV, 8.0);
+    float wear = battleWear(v_worldPos);
+    float oil = oilSheen(v_worldPos, N, V);
+    float stitches = stitchPattern(leatherUV, 18.0);
+
     float grainStrength = 0.55;
     albedo = mix(albedo * 0.7, albedo * 1.3, grain * grainStrength);
-    
-    // Darker creases / lighter raised areas
+
     float depth = fbm(leatherUV * 3.0);
     albedo = mix(albedo * 0.7, albedo * 1.15, depth);
-    
-    // Worn edges lighter & slightly desaturated
+
     vec3 wornColor = albedo * 1.3 + vec3(0.1, 0.08, 0.05);
-    wornColor = mix(wornColor, vec3(dot(wornColor, vec3(0.3, 0.59, 0.11))), 0.15);
+    wornColor =
+        mix(wornColor, vec3(dot(wornColor, vec3(0.3, 0.59, 0.11))), 0.15);
     albedo = mix(albedo, wornColor, wear * 0.6);
-    
-    // Stitch darkening
+
     albedo = mix(albedo, albedo * 0.55, stitches * 0.9);
-    
-    // Leather straps (keep existing pattern)
+
     float straps = smoothstep(0.45, 0.48, fract(v_worldPos.x * 6.0)) *
                    smoothstep(0.55, 0.52, fract(v_worldPos.x * 6.0));
     vec3 strapColor = brownLeatherPalette(0.15);
     albedo = mix(albedo, strapColor, straps * 0.6);
-    
-    // *** Fully leather-like roughness
-    metallic  = 0.0;
+
+    metallic = 0.0;
     float baseRough = 0.85;
     roughness = baseRough - oil * 0.25 + grain * 0.05;
     roughness = clamp(roughness, 0.65, 0.9);
-    sheen     = oil * 0.6;
-    
+    sheen = oil * 0.6;
+
   } else if (is_helmet) {
-    // ====== BRONZE-HEAVY HELMET WITH SMALL LEATHER UNDER-CAP ======
-    
-    // Leather under-cap near bottom/inside
+
     float leatherZone = smoothstep(0.2, 0.0, v_worldPos.y);
     leatherZone *= smoothstep(0.25, 0.15, abs(v_worldPos.x));
-    
+
     float helmLeatherVar = fbm(v_worldPos.xz * 6.0);
     vec3 helmLeather = brownLeatherPalette(helmLeatherVar * 0.6 + 0.2);
     float helmGrain = leatherGrain(v_worldPos.xz, 12.0);
     helmLeather *= 0.9 + helmGrain * 0.2;
-    
-    // *** Hammered bronze shell
-    vec2 bronzeUV   = v_texCoord * 10.0;
+
+    vec2 bronzeUV = v_texCoord * 10.0;
     float bronzeNoise = fbm(bronzeUV * 2.5);
-    float hammer      = noise(bronzeUV * 20.0);
-    
+    float hammer = noise(bronzeUV * 20.0);
+
     vec3 bronzeColor = BRONZE_BASE_COLOR;
-    vec3 patina      = vec3(0.78, 0.82, 0.70);
+    vec3 patina = vec3(0.78, 0.82, 0.70);
     bronzeColor = mix(bronzeColor, patina, bronzeNoise * 0.25);
     bronzeColor *= 0.9 + hammer * 0.25;
     bronzeColor = boostSaturation(bronzeColor, 0.2);
-    
-    // Extra bright bronze trim on cheek guards and ridge
+
     float trimMask = 0.0;
-    trimMask += smoothstep(0.65, 0.8, abs(v_worldPos.x) * 1.8); // sides
-    trimMask += smoothstep(0.8, 0.9, v_worldPos.y);             // top
+    trimMask += smoothstep(0.65, 0.8, abs(v_worldPos.x) * 1.8);
+    trimMask += smoothstep(0.8, 0.9, v_worldPos.y);
     trimMask = saturate(trimMask);
-    
+
     vec3 trimBronze = bronzeColor * 1.25 + vec3(0.06, 0.04, 0.02);
-    
-    // Combine bronze + trim, with small leather zone
+
     vec3 helmBase = mix(bronzeColor, trimBronze, trimMask * 0.7);
     albedo = mix(helmLeather, helmBase, 1.0 - leatherZone * 0.7);
-    
-    // Strongly metallic bronze, non-metallic leather
-    float bronzeMetal  = 0.95;
+
+    float bronzeMetal = 0.95;
     float leatherMetal = 0.0;
     metallic = mix(bronzeMetal, leatherMetal, leatherZone);
-    
-    // Bronze relatively smooth, leather rough
+
     float bronzeRough = 0.32 + hammer * 0.05;
     float leatherRough = 0.7;
     roughness = mix(bronzeRough, leatherRough, leatherZone);
-    
+
     sheen = (1.0 - leatherZone) * 0.35;
-    
+
   } else if (is_weapon) {
-    // ====== BOW ======
+
     float h = v_worldPos.y;
     float grip = smoothstep(0.38, 0.45, h) * smoothstep(0.62, 0.55, h);
     float limbs = 1.0 - grip;
     float stringArea = smoothstep(0.92, 1.0, h) + smoothstep(0.08, 0.0, h);
-    
+
     float woodGrain = woodGrainBow(v_worldPos);
     vec3 woodLight = vec3(0.72, 0.55, 0.35);
     vec3 woodDark = vec3(0.48, 0.32, 0.18);
     vec3 woodColor = mix(woodDark, woodLight, woodGrain);
     woodColor = boostSaturation(woodColor, 0.2);
-    
+
     float woodPolish = pow(max(dot(reflect(-V, N), L), 0.0), 24.0);
-    
+
     float gripGrain = leatherGrain(v_worldPos.xy, 25.0);
     vec3 gripLeather = brownLeatherPalette(0.25);
     gripLeather *= 0.9 + gripGrain * 0.2;
-    
+
     float wrapPattern = sin(v_worldPos.y * 50.0 + v_worldPos.x * 20.0);
     wrapPattern = smoothstep(-0.2, 0.2, wrapPattern);
     gripLeather *= 0.9 + wrapPattern * 0.15;
-    
+
     vec3 stringColor = vec3(0.85, 0.80, 0.72);
     float stringShine = pow(max(dot(reflect(-V, N), L), 0.0), 48.0);
-    
+
     albedo = woodColor;
     albedo = mix(albedo, gripLeather, grip);
     albedo = mix(albedo, stringColor, stringArea * 0.8);
-    
+
     metallic = 0.0;
     roughness = mix(0.38, 0.48, grip);
     roughness = mix(roughness, 0.32, stringArea);
-    sheen = woodPolish * limbs * 0.4 + grip * 0.3 + stringShine * stringArea * 0.3;
-    
+    sheen =
+        woodPolish * limbs * 0.4 + grip * 0.3 + stringShine * stringArea * 0.3;
+
   } else if (is_shield) {
     float leatherVar = fbm(v_worldPos.xz * 5.0);
     vec3 leatherColor = brownLeatherPalette(leatherVar * 0.7 + 0.15);
-    
+
     float grain = leatherGrain(v_worldPos.xz, 10.0);
     leatherColor *= 0.88 + grain * 0.24;
-    
+
     float dist = length(v_worldPos.xz);
     float boss = smoothstep(0.15, 0.0, dist);
     float edgeRim = smoothstep(0.85, 0.95, dist);
-    
+
     vec3 bronzeColor = vec3(0.85, 0.65, 0.35);
     bronzeColor = boostSaturation(bronzeColor, 0.25);
-    
+
     albedo = leatherColor;
     albedo = mix(albedo, bronzeColor, boss + edgeRim * 0.7);
-    
+
     metallic = mix(0.0, 0.9, boss + edgeRim * 0.6);
     roughness = mix(0.48, 0.22, boss);
     sheen = (1.0 - boss) * (1.0 - edgeRim) * 0.35;
-    
+
   } else if (is_rider_clothing) {
-    // Linen tunic
+
     float weave = fabricWeave(v_worldPos.xy);
     float threadVar = noise(v_worldPos.xz * 40.0);
-    
+
     albedo = boostSaturation(baseColor, 0.2);
     albedo *= 0.92 + weave * 0.08 + threadVar * 0.06;
-    
+
     float folds = fbm(v_worldPos.xy * 6.0);
     albedo *= 0.88 + folds * 0.18;
-    
+
     metallic = 0.0;
     roughness = 0.72;
     sheen = pow(1.0 - max(dot(N, V), 0.0), 4.0) * 0.12;
-    
+
   } else if (is_cloak) {
-    // ====== TEAM-COLORED CLOAK ======
+
     vec3 teamColor = boostSaturation(baseColor, 0.5);
     teamColor *= 1.2;
-    
+
     float folds = cloakFolds(v_worldPos);
     float weave = fabricWeave(v_worldPos.xy);
-    
+
     albedo = teamColor;
     albedo *= 0.75 + folds * 0.5;
     albedo *= 0.95 + weave * 0.08;
-    
+
     vec3 shadowTint = mix(teamColor, teamColor * vec3(0.85, 0.9, 1.0), 0.3);
     albedo = mix(shadowTint, albedo, folds);
-    
+
     float edgeFray = noise(v_worldPos.xy * 40.0);
     albedo *= 0.97 + edgeFray * 0.06;
-    
+
     metallic = 0.0;
     roughness = 0.72;
     sheen = pow(1.0 - max(dot(N, V), 0.0), 4.0) * 0.25;
-    
+
   } else if (is_horse_hide) {
-    // ====== HORSE COAT ======
+
     float coat = horseCoatPattern(v_worldPos.xz);
     float fur = furStrand(v_worldPos.xz, 60.0);
-    
+
     albedo = boostSaturation(baseColor, 0.18);
     albedo *= 0.9 + coat * 0.15 + fur * 0.1;
-    
+
     float muscle = fbm(v_worldPos.xy * 4.0);
     albedo *= 0.95 + muscle * 0.1;
-    
+
     metallic = 0.0;
     roughness = 0.65;
     sheen = 0.22;
-    
+
   } else if (is_horse_mane) {
     float strand = furStrand(v_worldPos.xy, 120.0);
     float clump = fbm(v_worldPos.xy * 8.0);
-    
+
     albedo = baseColor * 0.35;
     albedo = boostSaturation(albedo, 0.15);
     albedo *= 0.85 + strand * 0.2 + clump * 0.1;
-    
+
     metallic = 0.0;
     roughness = 0.75;
     sheen = 0.18;
-    
+
   } else if (is_horse_hoof) {
     float grain = fbm(v_worldPos.xy * 20.0);
-    
+
     albedo = vec3(0.18, 0.15, 0.12);
     albedo = boostSaturation(albedo, 0.1);
     albedo *= 0.9 + grain * 0.15;
-    
+
     metallic = 0.0;
     roughness = 0.45;
     sheen = 0.28;
-    
+
   } else if (is_saddle_leather || is_bridle) {
-    // ====== TACK - Rich brown leather ======
+
     float leatherVar = fbm(v_worldPos.xy * 5.0);
     vec3 tackLeather = brownLeatherPalette(leatherVar * 0.5 + 0.3);
-    
+
     float grain = leatherGrain(v_worldPos.xy, 18.0);
     float wear = battleWear(v_worldPos) * 0.5;
     float oil = oilSheen(v_worldPos, N, V);
-    
+
     albedo = tackLeather;
     albedo = boostSaturation(albedo, 0.28);
     albedo *= 0.88 + grain * 0.22;
-    
+
     vec3 wornColor = albedo * 1.2 + vec3(0.06, 0.04, 0.02);
     albedo = mix(albedo, wornColor, wear * 0.4);
-    
+
     if (is_bridle) {
       float buckle = smoothstep(0.78, 0.82, noise(v_worldPos.xz * 15.0));
       vec3 brassColor = vec3(0.82, 0.62, 0.32);
@@ -483,94 +434,88 @@ void main() {
       metallic = 0.0;
       roughness = 0.42 - oil * 0.12 + grain * 0.08;
     }
-    
+
     sheen = oil * 0.45;
-    
+
   } else if (is_saddle_blanket) {
-    // Woven wool - can have team color accent
+
     float weaveX = sin(v_worldPos.x * 50.0);
     float weaveZ = sin(v_worldPos.z * 50.0);
     float weave = weaveX * weaveZ * 0.5 + 0.5;
     float fuzz = noise(v_worldPos.xz * 80.0);
-    
+
     vec3 woolBase = vec3(0.45, 0.35, 0.25);
     vec3 teamStripe = boostSaturation(baseColor, 0.4);
-    
+
     float stripe = smoothstep(0.35, 0.45, fract(v_worldPos.x * 3.0));
     stripe *= smoothstep(0.65, 0.55, fract(v_worldPos.x * 3.0));
-    
+
     albedo = mix(woolBase, teamStripe, stripe * 0.7);
     albedo = boostSaturation(albedo, 0.2);
     albedo *= 0.88 + weave * 0.12 + fuzz * 0.06;
-    
+
     metallic = 0.0;
     roughness = 0.82;
     sheen = 0.08;
-    
+
   } else {
-    // Default - leather-tinted
+
     float leatherVar = fbm(v_worldPos.xy * 6.0);
     albedo = mix(brownLeatherPalette(leatherVar), baseColor, 0.4);
     albedo = boostSaturation(albedo, 0.15);
     metallic = 0.0;
     roughness = 0.55;
   }
-  
-  // ========== PBR LIGHTING ==========
-  
+
   float NdotL = max(dot(N, L), 0.0);
   float NdotV = max(dot(N, V), 0.001);
   float NdotH = max(dot(N, H), 0.0);
   float VdotH = max(dot(V, H), 0.0);
-  
+
   vec3 F0 = mix(vec3(0.04), albedo, metallic);
-  
+
   float D = D_GGX(NdotH, max(roughness, 0.01));
   float G = G_Smith(NdotV, NdotL, roughness);
   vec3 F = F_Schlick(VdotH, F0);
   vec3 specular = (D * G * F) / (4.0 * NdotV * NdotL + 0.001);
-  
+
   vec3 kD = (vec3(1.0) - F) * (1.0 - metallic);
   vec3 diffuse = kD * albedo / PI;
-  
+
   vec3 color = (diffuse + specular * 1.6) * NdotL * 2.0;
-  
-  // ====== SHEEN EFFECTS ======
+
   if (sheen > 0.01) {
     vec3 R = reflect(-V, N);
     float sheenSpec = pow(max(dot(R, L), 0.0), 14.0);
     color += albedo * sheenSpec * sheen * 1.4;
-    
+
     float edgeSheen = pow(1.0 - NdotV, 3.0);
     color += vec3(0.92, 0.88, 0.78) * edgeSheen * sheen * 0.35;
   }
-  
-  // ====== METALLIC SHINE (bronze) ======
+
   if (metallic > 0.5) {
     vec3 R = reflect(-V, N);
     float specPower = 128.0 / (roughness * roughness + 0.001);
     specPower = min(specPower, 512.0);
     float mirrorSpec = pow(max(dot(R, L), 0.0), specPower);
     color += albedo * mirrorSpec * 1.1;
-    
+
     float hotspot = pow(NdotH, 256.0);
     color += vec3(1.0) * hotspot * (1.0 - roughness);
   }
-  
-  // ====== WARM AMBIENT ======
+
   vec3 ambient = albedo * 0.36;
-  
+
   float sss = pow(saturate(dot(-N, L)), 2.5) * 0.12;
   ambient += albedo * vec3(1.1, 0.92, 0.75) * sss;
-  
+
   float rim = pow(1.0 - NdotV, 3.5);
   ambient += vec3(0.32, 0.30, 0.26) * rim * 0.22;
-  
+
   color += ambient;
-  
-  // Tone mapping
+
   color = color / (color + vec3(0.55));
   color = pow(color, vec3(0.94));
-  
+
   FragColor = vec4(saturate3(color), u_alpha);
 }

+ 2 - 6
assets/shaders/horse_archer_carthage.vert

@@ -12,15 +12,13 @@ uniform float u_time;
 out vec3 v_normal;
 out vec2 v_texCoord;
 out vec3 v_worldPos;
-out float
-    v_armorLayer; // Distinguish armor pieces for Carthaginian Numidian cavalry
+out float v_armorLayer;
 
 void main() {
   vec3 pos = a_position;
 
-  // Cloak back drape (Material ID 12)
   if (u_materialId == 12) {
-    float v = 1.0 - a_texCoord.y; // 1 = top, 0 = bottom
+    float v = 1.0 - a_texCoord.y;
     float u = a_texCoord.x;
     float x_norm = (u - 0.5) * 2.0;
 
@@ -41,7 +39,6 @@ void main() {
     pos.y += wave * move;
   }
 
-  // Cloak shoulder cape (Material ID 13)
   if (u_materialId == 13) {
     float u = a_texCoord.x;
     float v = a_texCoord.y;
@@ -67,7 +64,6 @@ void main() {
   vec4 model_pos = u_model * vec4(pos, 1.0);
   v_worldPos = model_pos.xyz;
 
-  // Keep armor material consistent: 1.0 means armor in the fragment shader.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   gl_Position = u_mvp * vec4(pos, 1.0);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 95 - 346
assets/shaders/horse_archer_roman_republic.frag


+ 10 - 18
assets/shaders/horse_archer_roman_republic.vert

@@ -15,13 +15,13 @@ out vec3 v_tangent;
 out vec3 v_bitangent;
 out vec2 v_texCoord;
 out vec3 v_worldPos;
-out float v_armorLayer;  // Distinguish armor pieces for Roman equites cavalry
-out float v_bodyHeight;  // 0.0-1.0 for both rider and horse
-out float v_armorSheen;  // Rider armor polish level
-out float v_leatherWear; // Tack aging
-out float v_horseMusculature; // Horse body definition
-out float v_hairFlow;         // Mane/tail direction
-out float v_hoofWear;         // Hoof chipping
+out float v_armorLayer;
+out float v_bodyHeight;
+out float v_armorSheen;
+out float v_leatherWear;
+out float v_horseMusculature;
+out float v_hairFlow;
+out float v_hoofWear;
 
 float hash13(vec3 p) {
   return fract(sin(dot(p, vec3(12.9898, 78.233, 37.719))) * 43758.5453);
@@ -35,9 +35,8 @@ void main() {
   vec3 position = a_position;
   vec3 normal = a_normal;
 
-  // Cloak back drape (Material ID 12)
   if (u_materialId == 12) {
-    float v = 1.0 - a_texCoord.y; // 1 = top, 0 = bottom
+    float v = 1.0 - a_texCoord.y;
     float u = a_texCoord.x;
     float x_norm = (u - 0.5) * 2.0;
 
@@ -58,7 +57,6 @@ void main() {
     position.y += wave * move;
   }
 
-  // Cloak shoulder cape (Material ID 13)
   if (u_materialId == 13) {
     float u = a_texCoord.x;
     float v = a_texCoord.y;
@@ -82,7 +80,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space for normal mapping
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -96,23 +93,18 @@ void main() {
   v_texCoord = a_texCoord;
   v_worldPos = vec3(u_model * vec4(position, 1.0));
 
-  // Body height for both rider and horse (0.0-1.0)
   v_bodyHeight = clamp((v_worldPos.y + 0.2) / 2.0, 0.0, 1.0);
 
-  // Procedural detail based on world position
   float hashVal = hash13(v_worldPos * 0.5);
 
-  // Rider equipment details
-  v_armorSheen = 0.6 + hashVal * 0.3;  // Polish variation
-  v_leatherWear = hashVal * 0.4 + 0.1; // Tack wear (0.1-0.5)
+  v_armorSheen = 0.6 + hashVal * 0.3;
+  v_leatherWear = hashVal * 0.4 + 0.1;
 
-  // Horse details
   v_horseMusculature =
       smoothstep(0.3, 0.6, v_bodyHeight) * smoothstep(1.0, 0.7, v_bodyHeight);
   v_hairFlow = hashVal * 0.5 + 0.5;
   v_hoofWear = hashVal * 0.3;
 
-  // Armor selection based solely on material ID.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   gl_Position = u_mvp * vec4(position, 1.0);

+ 111 - 159
assets/shaders/horse_spearman_carthage.frag

@@ -1,9 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CARTHAGINIAN HORSE SPEARMAN - Rich Leather Armor with Battle-Worn Character
-// ============================================================================
-
 in vec3 v_normal;
 in vec2 v_texCoord;
 in vec3 v_worldPos;
@@ -17,15 +13,10 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// CONSTANTS & HELPERS
-// ============================================================================
-
 const float PI = 3.14159265359;
 
-// *** Core base colors
 const vec3 LEATHER_BASE_BROWN = vec3(0.36, 0.22, 0.10);
-const vec3 BRONZE_BASE_COLOR  = vec3(0.86, 0.66, 0.36);
+const vec3 BRONZE_BASE_COLOR = vec3(0.86, 0.66, 0.36);
 
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
 vec3 saturate3(vec3 x) { return clamp(x, 0.0, 1.0); }
@@ -64,10 +55,6 @@ float fbm(vec2 p) {
   return v;
 }
 
-// ============================================================================
-// PBR FUNCTIONS
-// ============================================================================
-
 float D_GGX(float NdotH, float roughness) {
   float a = roughness * roughness;
   float a2 = a * a;
@@ -91,17 +78,12 @@ vec3 F_Schlick(float cosTheta, vec3 F0) {
   return F0 + (1.0 - F0) * t5;
 }
 
-// ============================================================================
-// LEATHER TEXTURE PATTERNS
-// ============================================================================
-
-// *** Multi-tone brown leather palette
 vec3 brownLeatherPalette(float variation) {
   vec3 darkBrown = vec3(0.28, 0.18, 0.10);
-  vec3 redBrown  = vec3(0.45, 0.25, 0.15);
+  vec3 redBrown = vec3(0.45, 0.25, 0.15);
   vec3 warmBrown = vec3(0.55, 0.38, 0.22);
-  vec3 lightTan  = vec3(0.68, 0.52, 0.35);
-  
+  vec3 lightTan = vec3(0.68, 0.52, 0.35);
+
   if (variation < 0.33) {
     return mix(darkBrown, redBrown, variation * 3.0);
   } else if (variation < 0.66) {
@@ -122,7 +104,7 @@ float leatherGrain(vec2 uv, float scale) {
 float stitchPattern(vec2 uv, float spacing) {
   float stitch = fract(uv.y * spacing);
   stitch = smoothstep(0.4, 0.5, stitch) * smoothstep(0.6, 0.5, stitch);
-  float seamLine = smoothstep(0.48, 0.50, fract(uv.x * 3.0)) * 
+  float seamLine = smoothstep(0.48, 0.50, fract(uv.x * 3.0)) *
                    smoothstep(0.52, 0.50, fract(uv.x * 3.0));
   return stitch * seamLine;
 }
@@ -142,10 +124,6 @@ float oilSheen(vec3 pos, vec3 N, vec3 V) {
   return facing * facing * variation;
 }
 
-// ============================================================================
-// HORSE PATTERNS
-// ============================================================================
-
 float horseCoatPattern(vec2 uv) {
   float coarse = fbm(uv * 3.0) * 0.15;
   float fine = noise(uv * 25.0) * 0.08;
@@ -160,259 +138,239 @@ float furStrand(vec2 uv, float density) {
   return strand * 0.3 + variation * 0.2;
 }
 
-// ============================================================================
-// MAIN
-// ============================================================================
-
 void main() {
   vec3 baseColor = clamp(u_color, 0.0, 1.0);
   if (u_useTexture) {
     baseColor *= texture(u_texture, v_texCoord).rgb;
   }
   baseColor = boostSaturation(baseColor, 0.25);
-  
-  // Material IDs
-  bool is_rider_skin      = (u_materialId == 0);
-  bool is_armor           = (u_materialId == 1);
-  bool is_helmet          = (u_materialId == 2);
-  bool is_weapon          = (u_materialId == 3);
-  bool is_shield          = (u_materialId == 4);
-  bool is_rider_clothing  = (u_materialId == 5);
-  bool is_horse_hide      = (u_materialId == 6);
-  bool is_horse_mane      = (u_materialId == 7);
-  bool is_horse_hoof      = (u_materialId == 8);
-  bool is_saddle_leather  = (u_materialId == 9);
-  bool is_bridle          = (u_materialId == 10);
-  bool is_saddle_blanket  = (u_materialId == 11);
-  
+
+  bool is_rider_skin = (u_materialId == 0);
+  bool is_armor = (u_materialId == 1);
+  bool is_helmet = (u_materialId == 2);
+  bool is_weapon = (u_materialId == 3);
+  bool is_shield = (u_materialId == 4);
+  bool is_rider_clothing = (u_materialId == 5);
+  bool is_horse_hide = (u_materialId == 6);
+  bool is_horse_mane = (u_materialId == 7);
+  bool is_horse_hoof = (u_materialId == 8);
+  bool is_saddle_leather = (u_materialId == 9);
+  bool is_bridle = (u_materialId == 10);
+  bool is_saddle_blanket = (u_materialId == 11);
+
   vec3 N = normalize(v_normal);
   vec3 V = normalize(vec3(0.0, 0.5, 1.0));
   vec3 L = normalize(vec3(0.5, 1.0, 0.4));
   vec3 H = normalize(L + V);
-  
+
   vec3 albedo = baseColor;
   float metallic = 0.0;
   float roughness = 0.5;
   float sheen = 0.0;
-  
-  // ========== MATERIAL SETUP ==========
-  
+
   if (is_rider_skin) {
-    // Warm Mediterranean skin
+
     albedo = mix(baseColor, vec3(0.75, 0.58, 0.45), 0.3);
     albedo = boostSaturation(albedo, 0.15);
     metallic = 0.0;
     roughness = 0.55;
-    
+
   } else if (is_armor) {
-    // ====== RICH BROWN LEATHER ARMOR (UV-based, clearly rough leather) ======
-    vec2 leatherUV = v_texCoord * 6.0;                        // *** UV-driven pattern
+
+    vec2 leatherUV = v_texCoord * 6.0;
     float leatherVar = fbm(leatherUV * 2.0);
     vec3 leatherPalette = brownLeatherPalette(leatherVar);
-    
-    // *** Core leather base, lightly tinted by team color
+
     vec3 tint = mix(vec3(1.0), baseColor, 0.25);
     vec3 leatherBase = mix(LEATHER_BASE_BROWN, leatherPalette, 0.5);
     albedo = leatherBase * tint;
     albedo = boostSaturation(albedo, 0.25);
-    
-    float grain    = leatherGrain(leatherUV, 8.0);             // *** sharper grain
-    float wear     = battleWear(v_worldPos);
-    float oil      = oilSheen(v_worldPos, N, V);
-    float stitches = stitchPattern(leatherUV, 18.0);           // *** clear seams
-    
-    // Strong grain contrast
+
+    float grain = leatherGrain(leatherUV, 8.0);
+    float wear = battleWear(v_worldPos);
+    float oil = oilSheen(v_worldPos, N, V);
+    float stitches = stitchPattern(leatherUV, 18.0);
+
     float grainStrength = 0.55;
     albedo = mix(albedo * 0.7, albedo * 1.3, grain * grainStrength);
-    
-    // Darker in creases, lighter on raised areas
+
     float depth = fbm(leatherUV * 3.0);
     albedo = mix(albedo * 0.7, albedo * 1.15, depth);
-    
-    // Worn edges lighter & slightly desaturated
+
     vec3 wornColor = albedo * 1.3 + vec3(0.1, 0.08, 0.05);
-    wornColor = mix(wornColor, vec3(dot(wornColor, vec3(0.3, 0.59, 0.11))), 0.15);
+    wornColor =
+        mix(wornColor, vec3(dot(wornColor, vec3(0.3, 0.59, 0.11))), 0.15);
     albedo = mix(albedo, wornColor, wear * 0.6);
-    
-    // Stitch darkening
+
     albedo = mix(albedo, albedo * 0.55, stitches * 0.9);
-    
-    // Leather straps using world-space bands (keep existing idea)
+
     float straps = smoothstep(0.45, 0.48, fract(v_worldPos.x * 6.0)) *
                    smoothstep(0.55, 0.52, fract(v_worldPos.x * 6.0));
     vec3 strapColor = brownLeatherPalette(0.15);
     albedo = mix(albedo, strapColor, straps * 0.6);
-    
-    // *** Strongly rough leather, only mildly smoothed by oil
-    metallic  = 0.0;
+
+    metallic = 0.0;
     float baseRough = 0.85;
     roughness = baseRough - oil * 0.25 + grain * 0.05;
     roughness = clamp(roughness, 0.65, 0.9);
-    sheen     = oil * 0.6;
-    
+    sheen = oil * 0.6;
+
   } else if (is_helmet) {
-    // ====== BRONZE-HEAVY HELMET WITH SMALL LEATHER UNDER-CAP ======
-    
-    // Leather under-cap near bottom / inside
+
     float leatherZone = smoothstep(0.2, 0.0, v_worldPos.y);
     leatherZone *= smoothstep(0.25, 0.15, abs(v_worldPos.x));
-    
+
     float helmLeatherVar = fbm(v_worldPos.xz * 6.0);
     vec3 helmLeather = brownLeatherPalette(helmLeatherVar * 0.6 + 0.2);
     float helmGrain = leatherGrain(v_worldPos.xz, 12.0);
     helmLeather *= 0.9 + helmGrain * 0.2;
-    
-    // Hammered bronze shell
+
     vec2 bronzeUV = v_texCoord * 10.0;
     float bronzeNoise = fbm(bronzeUV * 2.5);
     float hammer = noise(bronzeUV * 20.0);
-    
+
     vec3 bronzeColor = BRONZE_BASE_COLOR;
     vec3 patina = vec3(0.78, 0.82, 0.70);
     bronzeColor = mix(bronzeColor, patina, bronzeNoise * 0.25);
     bronzeColor *= 0.9 + hammer * 0.25;
     bronzeColor = boostSaturation(bronzeColor, 0.2);
-    
-    // Extra bright bronze trim on cheek guards and crest
+
     float trimMask = 0.0;
-    trimMask += smoothstep(0.65, 0.8, abs(v_worldPos.x) * 1.8); // side plates
-    trimMask += smoothstep(0.8, 0.9, v_worldPos.y);             // top ridge
+    trimMask += smoothstep(0.65, 0.8, abs(v_worldPos.x) * 1.8);
+    trimMask += smoothstep(0.8, 0.9, v_worldPos.y);
     trimMask = saturate(trimMask);
-    
+
     vec3 trimBronze = bronzeColor * 1.25 + vec3(0.06, 0.04, 0.02);
-    
-    // Combine bronze + trim, with small leather zone
+
     vec3 helmBase = mix(bronzeColor, trimBronze, trimMask * 0.7);
     albedo = mix(helmLeather, helmBase, 1.0 - leatherZone * 0.7);
-    
-    // Strongly metallic bronze, non-metallic leather
-    float bronzeMetal  = 0.95;
+
+    float bronzeMetal = 0.95;
     float leatherMetal = 0.0;
     metallic = mix(bronzeMetal, leatherMetal, leatherZone);
-    
-    // Bronze relatively smooth, leather rough
-    float bronzeRough  = 0.32 + hammer * 0.05;
+
+    float bronzeRough = 0.32 + hammer * 0.05;
     float leatherRough = 0.7;
     roughness = mix(bronzeRough, leatherRough, leatherZone);
-    
+
     sheen = (1.0 - leatherZone) * 0.35;
-    
+
   } else if (is_weapon) {
-    // Spear
+
     float h = v_worldPos.y;
     float tip = smoothstep(0.40, 0.55, h);
     float binding = smoothstep(0.35, 0.42, h) * (1.0 - tip);
-    
+
     vec3 woodColor = boostSaturation(baseColor * 0.85, 0.3);
     float woodGrain = fbm(vec2(v_worldPos.x * 8.0, v_worldPos.y * 35.0));
     woodColor *= 0.85 + woodGrain * 0.3;
     float woodSheen = pow(max(dot(reflect(-V, N), L), 0.0), 16.0);
-    
+
     vec3 bindColor = baseColor * 0.6;
     bindColor = boostSaturation(bindColor, 0.2);
     float bindGrain = leatherGrain(v_worldPos.xy, 25.0);
     bindColor *= 0.9 + bindGrain * 0.2;
-    
+
     vec3 ironColor = vec3(0.55, 0.55, 0.58);
     float ironBrush = fbm(v_worldPos.xy * 40.0);
     ironColor += vec3(0.08) * ironBrush;
     ironColor = mix(ironColor, baseColor * 0.3 + vec3(0.4), 0.15);
-    
+
     albedo = woodColor;
     albedo = mix(albedo, bindColor, binding);
     albedo = mix(albedo, ironColor, tip);
-    
+
     metallic = mix(0.0, 0.85, tip);
     roughness = mix(0.38, 0.28, tip);
     roughness = mix(roughness, 0.5, binding);
     sheen = woodSheen * (1.0 - tip) * (1.0 - binding) * 0.3;
-    
+
   } else if (is_shield) {
     float dist = length(v_worldPos.xz);
     float boss = smoothstep(0.18, 0.0, dist);
     float bossRim = smoothstep(0.22, 0.18, dist) * (1.0 - boss);
-    
+
     float shieldGrain = leatherGrain(v_worldPos.xz, 10.0);
     float shieldWear = battleWear(v_worldPos);
-    
+
     albedo = boostSaturation(baseColor, 0.3);
     albedo *= 0.9 + shieldGrain * 0.2;
     albedo = mix(albedo, albedo * 1.2, shieldWear * 0.3);
-    
+
     vec3 bronzeColor = vec3(0.88, 0.68, 0.38);
     bronzeColor = boostSaturation(bronzeColor, 0.25);
     albedo = mix(albedo, bronzeColor, boss + bossRim * 0.8);
-    
+
     metallic = mix(0.0, 0.9, boss + bossRim * 0.7);
     roughness = mix(0.45, 0.22, boss);
-    
+
   } else if (is_rider_clothing) {
-    // Linen tunic with texture
+
     float weave = sin(v_worldPos.x * 80.0) * sin(v_worldPos.z * 80.0);
     weave = weave * 0.5 + 0.5;
     float threadVar = noise(v_worldPos.xz * 40.0);
-    
+
     albedo = boostSaturation(baseColor, 0.25);
     albedo *= 0.92 + weave * 0.08 + threadVar * 0.06;
-    
+
     float folds = fbm(v_worldPos.xy * 6.0);
     albedo *= 0.9 + folds * 0.15;
-    
+
     metallic = 0.0;
     roughness = 0.72;
     sheen = pow(1.0 - max(dot(N, V), 0.0), 4.0) * 0.15;
-    
+
   } else if (is_horse_hide) {
-    // ====== HORSE COAT ======
+
     float coat = horseCoatPattern(v_worldPos.xz);
     float fur = furStrand(v_worldPos.xz, 60.0);
-    
+
     albedo = boostSaturation(baseColor, 0.2);
     albedo *= 0.9 + coat * 0.15 + fur * 0.1;
-    
+
     float muscle = fbm(v_worldPos.xy * 4.0);
     albedo *= 0.95 + muscle * 0.1;
-    
+
     metallic = 0.0;
     roughness = 0.65;
     sheen = 0.25;
-    
+
   } else if (is_horse_mane) {
     float strand = furStrand(v_worldPos.xy, 120.0);
     float clump = fbm(v_worldPos.xy * 8.0);
-    
+
     albedo = baseColor * 0.35;
     albedo = boostSaturation(albedo, 0.15);
     albedo *= 0.85 + strand * 0.2 + clump * 0.1;
-    
+
     metallic = 0.0;
     roughness = 0.75;
     sheen = 0.2;
-    
+
   } else if (is_horse_hoof) {
     float grain = fbm(v_worldPos.xy * 20.0);
-    
+
     albedo = vec3(0.18, 0.15, 0.12);
     albedo = boostSaturation(albedo, 0.1);
     albedo *= 0.9 + grain * 0.15;
-    
+
     metallic = 0.0;
     roughness = 0.45;
     sheen = 0.3;
-    
+
   } else if (is_saddle_leather || is_bridle) {
-    // ====== TACK LEATHER ======
+
     float grain = leatherGrain(v_worldPos.xy, 18.0);
     float wear = battleWear(v_worldPos) * 0.6;
     float oil = oilSheen(v_worldPos, N, V);
-    
+
     albedo = baseColor * 1.1;
     albedo = boostSaturation(albedo, 0.3);
     albedo *= 0.88 + grain * 0.22;
-    
+
     vec3 wornColor = albedo * 1.2 + vec3(0.06, 0.04, 0.02);
     albedo = mix(albedo, wornColor, wear * 0.4);
-    
+
     if (is_bridle) {
       float buckle = smoothstep(0.78, 0.82, noise(v_worldPos.xz * 15.0));
       vec3 brassColor = vec3(0.82, 0.62, 0.32);
@@ -423,88 +381,82 @@ void main() {
       metallic = 0.0;
       roughness = 0.42 - oil * 0.12 + grain * 0.08;
     }
-    
+
     sheen = oil * 0.5;
-    
+
   } else if (is_saddle_blanket) {
-    // Woven wool blanket
+
     float weaveX = sin(v_worldPos.x * 50.0);
     float weaveZ = sin(v_worldPos.z * 50.0);
     float weave = weaveX * weaveZ * 0.5 + 0.5;
     float fuzz = noise(v_worldPos.xz * 80.0);
-    
+
     albedo = boostSaturation(baseColor, 0.35);
     albedo *= 0.88 + weave * 0.12 + fuzz * 0.06;
-    
+
     float stripe = smoothstep(0.4, 0.5, fract(v_worldPos.x * 4.0));
     stripe *= smoothstep(0.6, 0.5, fract(v_worldPos.x * 4.0));
     albedo = mix(albedo, albedo * 0.7, stripe * 0.3);
-    
+
     metallic = 0.0;
     roughness = 0.82;
     sheen = 0.1;
-    
+
   } else {
     albedo = boostSaturation(baseColor, 0.2);
     metallic = 0.0;
     roughness = 0.55;
   }
-  
-  // ========== PBR LIGHTING ==========
-  
+
   float NdotL = max(dot(N, L), 0.0);
   float NdotV = max(dot(N, V), 0.001);
   float NdotH = max(dot(N, H), 0.0);
   float VdotH = max(dot(V, H), 0.0);
-  
+
   vec3 F0 = mix(vec3(0.04), albedo, metallic);
-  
+
   float D = D_GGX(NdotH, max(roughness, 0.01));
   float G = G_Smith(NdotV, NdotL, roughness);
   vec3 F = F_Schlick(VdotH, F0);
   vec3 specular = (D * G * F) / (4.0 * NdotV * NdotL + 0.001);
-  
+
   vec3 kD = (vec3(1.0) - F) * (1.0 - metallic);
   vec3 diffuse = kD * albedo / PI;
-  
+
   vec3 color = (diffuse + specular * 1.8) * NdotL * 2.0;
-  
-  // ====== LEATHER/COAT SHEEN EFFECT ======
+
   if (sheen > 0.01) {
     vec3 R = reflect(-V, N);
     float sheenSpec = pow(max(dot(R, L), 0.0), 12.0);
     color += albedo * sheenSpec * sheen * 1.5;
-    
+
     float edgeSheen = pow(1.0 - NdotV, 3.0);
     color += vec3(0.95, 0.90, 0.80) * edgeSheen * sheen * 0.4;
   }
-  
-  // ====== METALLIC SHINE (bronze parts) ======
+
   if (metallic > 0.5) {
     vec3 R = reflect(-V, N);
     float specPower = 128.0 / (roughness * roughness + 0.001);
     specPower = min(specPower, 512.0);
     float mirrorSpec = pow(max(dot(R, L), 0.0), specPower);
     color += albedo * mirrorSpec * 1.2;
-    
+
     float hotspot = pow(NdotH, 256.0);
     color += vec3(1.0) * hotspot * (1.0 - roughness);
   }
-  
-  // ====== WARM AMBIENT ======
+
   vec3 ambient = albedo * 0.38;
-  
+
   float sss = pow(saturate(dot(-N, L)), 2.0) * 0.12;
   ambient += albedo * vec3(1.1, 0.9, 0.7) * sss;
-  
+
   float rim = pow(1.0 - NdotV, 3.5);
   ambient += vec3(0.35, 0.32, 0.28) * rim * 0.25;
-  
+
   color += ambient;
-  
-  // Tone mapping
+
   color = color / (color + vec3(0.55));
   color = pow(color, vec3(0.94));
-  
+
   FragColor = vec4(saturate3(color), u_alpha);
 }

+ 1 - 3
assets/shaders/horse_spearman_carthage.vert

@@ -11,15 +11,13 @@ uniform int u_materialId;
 out vec3 v_normal;
 out vec2 v_texCoord;
 out vec3 v_worldPos;
-out float
-    v_armorLayer; // Distinguish armor pieces for Carthaginian Numidian cavalry
+out float v_armorLayer;
 
 void main() {
   v_normal = mat3(transpose(inverse(u_model))) * a_normal;
   v_texCoord = a_texCoord;
   v_worldPos = vec3(u_model * vec4(a_position, 1.0));
 
-  // Keep armor material consistent: 1.0 means armor in the fragment shader.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   gl_Position = u_mvp * vec4(a_position, 1.0);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 94 - 349
assets/shaders/horse_spearman_roman_republic.frag


+ 9 - 15
assets/shaders/horse_spearman_roman_republic.vert

@@ -14,13 +14,13 @@ out vec3 v_tangent;
 out vec3 v_bitangent;
 out vec2 v_texCoord;
 out vec3 v_worldPos;
-out float v_armorLayer;  // Distinguish armor pieces for Roman equites cavalry
-out float v_bodyHeight;  // 0.0-1.0 for both rider and horse
-out float v_armorSheen;  // Rider armor polish level
-out float v_leatherWear; // Tack aging
-out float v_horseMusculature; // Horse body definition
-out float v_hairFlow;         // Mane/tail direction
-out float v_hoofWear;         // Hoof chipping
+out float v_armorLayer;
+out float v_bodyHeight;
+out float v_armorSheen;
+out float v_leatherWear;
+out float v_horseMusculature;
+out float v_hairFlow;
+out float v_hoofWear;
 
 float hash13(vec3 p) {
   return fract(sin(dot(p, vec3(12.9898, 78.233, 37.719))) * 43758.5453);
@@ -37,7 +37,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space for normal mapping
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -51,23 +50,18 @@ void main() {
   v_texCoord = a_texCoord;
   v_worldPos = vec3(u_model * vec4(position, 1.0));
 
-  // Body height for both rider and horse (0.0-1.0)
   v_bodyHeight = clamp((v_worldPos.y + 0.2) / 2.0, 0.0, 1.0);
 
-  // Procedural detail based on world position
   float hashVal = hash13(v_worldPos * 0.5);
 
-  // Rider equipment details
-  v_armorSheen = 0.7 + hashVal * 0.3;  // Higher polish for heavy cavalry
-  v_leatherWear = hashVal * 0.4 + 0.1; // Tack wear (0.1-0.5)
+  v_armorSheen = 0.7 + hashVal * 0.3;
+  v_leatherWear = hashVal * 0.4 + 0.1;
 
-  // Horse details
   v_horseMusculature =
       smoothstep(0.3, 0.6, v_bodyHeight) * smoothstep(1.0, 0.7, v_bodyHeight);
   v_hairFlow = hashVal * 0.5 + 0.5;
   v_hoofWear = hashVal * 0.3;
 
-  // Armor selection based solely on material ID.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   gl_Position = u_mvp * vec4(position, 1.0);

+ 11 - 47
assets/shaders/horse_swordsman.frag

@@ -11,9 +11,6 @@ uniform float u_alpha;
 
 out vec4 FragColor;
 
-// ---------------------
-// utilities & noise
-// ---------------------
 const float PI = 3.14159265359;
 
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
@@ -47,31 +44,23 @@ float fbm(vec2 p) {
   return f;
 }
 
-// anti-aliased step
 float aaStep(float edge, float x) {
   float w = fwidth(x);
   return smoothstep(edge - w, edge + w, x);
 }
 
-// ---------------------
-// patterns
-// ---------------------
-
-// plate seams + rivets (AA)
 float armorPlates(vec2 p, float y) {
   float plateY = fract(y * 6.5);
   float line = smoothstep(0.92, 0.98, plateY) - smoothstep(0.98, 1.0, plateY);
-  // anti-aliased line thickness
+
   line = smoothstep(0.0, fwidth(plateY) * 2.0, line) * 0.12;
 
-  // rivets on top seams
   float rivetX = fract(p.x * 18.0);
   float rivet = smoothstep(0.48, 0.50, rivetX) * smoothstep(0.52, 0.50, rivetX);
   rivet *= step(0.92, plateY);
   return line + rivet * 0.25;
 }
 
-// linked ring suggestion (AA)
 float chainmailRings(vec2 p) {
   vec2 uv = p * 35.0;
 
@@ -97,9 +86,6 @@ float horseHidePattern(vec2 p) {
   return grain + ripple + mottling;
 }
 
-// ---------------------
-// microfacet shading
-// ---------------------
 vec3 fresnelSchlick(float cosTheta, vec3 F0) {
   return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
 }
@@ -119,7 +105,6 @@ float G_Smith(float NdotV, float NdotL, float rough) {
   return gV * gL;
 }
 
-// screen-space bump from a height field h(uv) in world XZ
 vec3 perturbNormalWS(vec3 N, vec3 worldPos, float h, float scale) {
   vec3 dpdx = dFdx(worldPos);
   vec3 dpdy = dFdy(worldPos);
@@ -131,7 +116,6 @@ vec3 perturbNormalWS(vec3 N, vec3 worldPos, float h, float scale) {
   return Np;
 }
 
-// hemisphere ambient (sky/ground)
 vec3 hemilight(vec3 N) {
   vec3 sky = vec3(0.55, 0.64, 0.80);
   vec3 ground = vec3(0.23, 0.20, 0.17);
@@ -139,9 +123,6 @@ vec3 hemilight(vec3 N) {
   return mix(ground, sky, t) * 0.28;
 }
 
-// ---------------------
-// main
-// ---------------------
 void main() {
   vec3 baseColor = u_color;
   if (u_useTexture)
@@ -162,10 +143,8 @@ void main() {
   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));
   vec3 H = normalize(L + V);
 
   float NdotL = saturate(dot(N, L));
@@ -173,31 +152,25 @@ void main() {
   float NdotH = saturate(dot(N, H));
   float VdotH = saturate(dot(V, H));
 
-  // wrap diffuse like original (softens lambert)
   float wrapAmount = (avg > 0.50) ? 0.08 : 0.30;
   float NdotL_wrap = max(NdotL * (1.0 - wrapAmount) + wrapAmount, 0.12);
 
-  // base material params
   float roughness = 0.5;
-  vec3 F0 = vec3(0.04); // dielectric default
+  vec3 F0 = vec3(0.04);
   float metalness = 0.0;
   vec3 albedo = baseColor;
 
-  // micro details / masks (re-used)
   float nSmall = fbm(uv * 6.0);
   float nLarge = fbm(uv * 2.0);
   float cavity = 1.0 - (nLarge * 0.25 + nSmall * 0.15);
 
-  // ---------------------
-  // MATERIAL BRANCHES
-  // ---------------------
   vec3 col = vec3(0.0);
   vec3 ambient = hemilight(N) * (0.85 + 0.15 * cavity);
 
   if (isHorseHide) {
-    // subtle anisotropic sheen along body flow
+
     vec3 up = vec3(0.0, 1.0, 0.0);
-    vec3 T = normalize(cross(up, N) + 1e-4); // hair tangent guess
+    vec3 T = normalize(cross(up, N) + 1e-4);
     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);
@@ -209,14 +182,12 @@ void main() {
     F0 = vec3(0.035);
     metalness = 0.0;
 
-    // slight bump from hair grain
     float h = fbm(v_worldPos.xz * 35.0);
     N = perturbNormalWS(N, v_worldPos, h, 0.35);
 
-    // composition
     albedo = albedo * (1.0 + hideTex * 0.20) * (0.98 + 0.02 * nSmall);
     col += ambient * albedo;
-    // 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);
@@ -230,35 +201,30 @@ void main() {
     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));
     N = perturbNormalWS(N, v_worldPos, h, 0.35);
 
-    // steel-like params
     metalness = 1.0;
     F0 = vec3(0.92);
     roughness = 0.28 + brushed * 2.0 + dents * 0.6;
     roughness = clamp(roughness, 0.15, 0.55);
 
-    // base tint & sky reflection lift
     albedo = mix(vec3(0.60), baseColor, 0.25);
     float skyRefl = (N.y * 0.5 + 0.5) * 0.10;
 
-    // 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 F = fresnelSchlick(VdotH, F0 * albedo);
     vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
 
-    col += ambient * 0.3; // metals rely more on spec
+    col += ambient * 0.3;
     col += NdotL_wrap * spec * 1.5;
     col += vec3(plates) + vec3(skyRefl) - vec3(dents * 0.25) + vec3(brushed);
 
   } else if (isBrass) {
     float brassNoise = noise(uv * 22.0) * 0.02;
-    float patina = fbm(uv * 4.0) * 0.12; // larger-scale patina
+    float patina = fbm(uv * 4.0) * 0.12;
 
-    // bump from subtle hammering
     float h = fbm(uv * 18.0);
     N = perturbNormalWS(N, v_worldPos, h, 0.30);
 
@@ -280,7 +246,6 @@ void main() {
     float rings = chainmailRings(v_worldPos.xz);
     float ringHi = noise(uv * 30.0) * 0.10;
 
-    // small pitted bump
     float h = fbm(uv * 35.0);
     N = perturbNormalWS(N, v_worldPos, h, 0.25);
 
@@ -295,7 +260,7 @@ void main() {
 
     col += ambient * 0.25;
     col += NdotL_wrap * (spec * (1.2 + rings)) + vec3(ringHi);
-    // slight diffuse damping to keep chainmail darker in cavities
+
     col *= (0.95 - 0.10 * (1.0 - cavity));
 
   } else if (isFabric) {
@@ -323,7 +288,7 @@ void main() {
     col += NdotL_wrap * (albedo * (1.0 - F) + spec * 0.3) +
            vec3(weave + embroidery + sheen);
 
-  } else { // leather
+  } else {
     float grain = fbm(uv * 10.0) * 0.15;
     float wear = fbm(uv * 3.0) * 0.12;
 
@@ -346,7 +311,6 @@ void main() {
     col += NdotL_wrap * (albedo * (1.0 - F)) + spec * 0.4 + vec3(sheen);
   }
 
-  // final clamp and alpha
   col = saturate(col);
   FragColor = vec4(col, u_alpha);
 }

+ 38 - 51
assets/shaders/horse_swordsman_carthage.frag

@@ -1,9 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CARTHAGINIAN HORSE SWORDSMAN - Armor & Helmet changed to dark metallic
-// ============================================================================
-
 in vec3 v_normal;
 in vec2 v_texCoord;
 in vec3 v_worldPos;
@@ -17,18 +13,12 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// CONSTANTS & HELPERS
-// ============================================================================
-
 const float PI = 3.14159265359;
 
-// ORIGINAL bronze kept for weapon/shield/etc
 const vec3 BRONZE_BASE_COLOR = vec3(0.86, 0.66, 0.36);
 
-// NEW dark metal for armor + helmet only
 const vec3 DARK_METAL_COLOR = vec3(0.14, 0.14, 0.16);
-// Dark brown base for the Carthaginian rider shield
+
 const vec3 SHIELD_BROWN_COLOR = vec3(0.18, 0.09, 0.035);
 
 float saturatef(float x) { return clamp(x, 0.0, 1.0); }
@@ -68,10 +58,6 @@ float fbm(vec2 p) {
   return v;
 }
 
-// ============================================================================
-// PBR
-// ============================================================================
-
 float D_GGX(float NdotH, float roughness) {
   float a = roughness * roughness;
   float a2 = a * a;
@@ -94,10 +80,6 @@ vec3 F_Schlick(float cosTheta, vec3 F0) {
   return F0 + (1.0 - F0) * (t * t * t * t * t);
 }
 
-// ============================================================================
-// PATTERNS
-// ============================================================================
-
 float hammerPattern(vec3 pos) {
   float coarse = fbm(pos.xz * 14.0);
   float fine = fbm(pos.xy * 30.0 + 5.3);
@@ -140,54 +122,56 @@ float leatherGrain(vec2 p) {
   return coarse + fine;
 }
 
-// ============================================================================
-// MAIN
-// ============================================================================
-
 void main() {
   vec3 baseColor = clamp(u_color, 0.0, 1.0);
-  if (u_useTexture) baseColor *= texture(u_texture, v_texCoord).rgb;
+  if (u_useTexture)
+    baseColor *= texture(u_texture, v_texCoord).rgb;
   baseColor = boostSaturation(baseColor, 0.25);
+  vec3 vibrantTeamColor =
+      clamp(boostSaturation(baseColor * vec3(1.08, 0.92, 0.94), 0.6), 0.0, 1.0);
 
   vec3 N = normalize(v_normal);
   vec3 V = normalize(vec3(0.0, 0.5, 1.0));
   vec3 L = normalize(vec3(0.5, 1.0, 0.4));
   vec3 H = normalize(L + V);
 
-  bool is_rider_skin     = (u_materialId == 0);
-  bool is_body_armor     = (u_materialId == 1);
-  bool is_helmet         = (u_materialId == 2);
-  bool is_weapon         = (u_materialId == 3);
-  bool is_shield         = (u_materialId == 4);
+  bool is_rider_skin = (u_materialId == 0);
+  bool is_body_armor = (u_materialId == 1);
+  bool is_helmet = (u_materialId == 2);
+  bool is_weapon = (u_materialId == 3);
+  bool is_shield = (u_materialId == 4);
   bool is_rider_clothing = (u_materialId == 5);
-  bool is_horse_hide     = (u_materialId == 6);
-  bool is_horse_mane     = (u_materialId == 7);
-  bool is_horse_hoof     = (u_materialId == 8);
+  bool is_horse_hide = (u_materialId == 6);
+  bool is_horse_mane = (u_materialId == 7);
+  bool is_horse_hoof = (u_materialId == 8);
   bool is_saddle_leather = (u_materialId == 9);
-  bool is_bridle         = (u_materialId == 10);
+  bool is_bridle = (u_materialId == 10);
   bool is_saddle_blanket = (u_materialId == 11);
 
   vec3 albedo = baseColor;
   float metallic = 0.0;
   float roughness = 0.5;
 
-  // =======================================================================
-  // MATERIALS
-  // =======================================================================
-
   if (is_rider_skin) {
     albedo = mix(baseColor, vec3(0.92, 0.76, 0.62), 0.25);
     albedo = boostSaturation(albedo, 0.15);
+    float armMask = smoothstep(0.3, 0.55, v_worldPos.y) *
+                    smoothstep(0.25, 0.45, abs(v_worldPos.x));
+    float legMask = smoothstep(0.0, 0.35, v_worldPos.y) *
+                    smoothstep(0.20, 0.48, abs(v_worldPos.x));
+    vec3 limbTint = mix(albedo * 0.9, vibrantTeamColor, 0.6);
+    float limbBlend = clamp(max(armMask, legMask), 0.0, 1.0);
+    albedo = mix(albedo, limbTint, limbBlend * 0.9);
     metallic = 0.0;
     roughness = 0.55;
   }
 
   else if (is_body_armor) {
-    // ========== DARK METAL BODY ARMOR ==========
+
     vec2 metalUV = v_texCoord * 6.0;
     float hammer = hammerPattern(vec3(metalUV, v_worldPos.y));
     float scales = scaleArmor(metalUV);
-    float chain  = chainmailRings(v_worldPos.xz);
+    float chain = chainmailRings(v_worldPos.xz);
 
     vec3 metal = DARK_METAL_COLOR;
     float patinaNoise = fbm(metalUV * 2.5);
@@ -206,7 +190,7 @@ void main() {
   }
 
   else if (is_helmet) {
-    // ========== DARK METAL HELMET ==========
+
     vec2 metalUV = v_texCoord * 8.0;
     float hammer = hammerPattern(vec3(metalUV, v_worldPos.y));
     float scales = scaleArmor(metalUV * 1.1);
@@ -272,12 +256,20 @@ void main() {
   else if (is_rider_clothing || is_saddle_blanket) {
     float weave = sin(v_worldPos.x * 55.0) * sin(v_worldPos.z * 55.0) * 0.04;
     float texture_var = fbm(v_worldPos.xz * 8.0);
+    float rimGlow = smoothstep(0.2, 0.65, abs(v_worldPos.z));
+    float foldDepth = smoothstep(0.15, 0.5, abs(v_worldPos.y));
 
-    albedo = boostSaturation(baseColor, 0.4);
-    albedo *= 1.0 + weave + texture_var * 0.12;
+    vec3 clothBase = mix(vibrantTeamColor, baseColor * 0.82, 0.12);
+    vec3 highlight = mix(clothBase * 0.9, clothBase * 1.28,
+                         clamp(weave + texture_var * 0.3, 0.0, 1.0));
+    vec3 shadow = mix(clothBase * vec3(0.8, 0.9, 0.95), clothBase, 0.35);
+    vec3 clothLayer = mix(shadow, highlight, foldDepth * 1.1);
+
+    albedo = clothLayer * (1.0 + weave * 0.38 + texture_var * 0.08);
+    albedo = mix(albedo, highlight, rimGlow * 0.45);
 
     metallic = 0.0;
-    roughness = 0.7;
+    roughness = 0.62 + texture_var * 0.06 + foldDepth * 0.03;
   }
 
   else if (is_horse_hide) {
@@ -334,10 +326,6 @@ void main() {
     roughness = 0.6;
   }
 
-  // =======================================================================
-  // PBR LIGHTING
-  // =======================================================================
-
   float NdotL = max(dot(N, L), 0.0);
   float NdotV = max(dot(N, V), 0.001);
   float NdotH = max(dot(N, H), 0.0);
@@ -355,7 +343,6 @@ void main() {
 
   vec3 color = (diffuse + specular * 2.2) * NdotL * 2.1;
 
-  // Enhanced metallic shine
   if (metallic > 0.5) {
     vec3 R = reflect(-V, N);
 
@@ -370,16 +357,16 @@ void main() {
     float softSpec = pow(max(dot(R, L), 0.0), 28.0);
     color += albedo * softSpec * 0.6;
 
-    vec3 skyCol    = vec3(0.55, 0.65, 0.85);
+    vec3 skyCol = vec3(0.55, 0.65, 0.85);
     vec3 groundCol = vec3(0.38, 0.32, 0.25);
-    float upFace   = R.y * 0.5 + 0.5;
+    float upFace = R.y * 0.5 + 0.5;
     vec3 envReflect = mix(groundCol, skyCol, upFace);
     color += envReflect * (1.0 - roughness) * 0.5;
   }
 
   vec3 ambient = albedo * 0.42;
 
-  vec3 skyAmbient    = vec3(0.45, 0.55, 0.70);
+  vec3 skyAmbient = vec3(0.45, 0.55, 0.70);
   vec3 groundAmbient = vec3(0.30, 0.25, 0.20);
   float hemi = N.y * 0.5 + 0.5;
 

+ 1 - 3
assets/shaders/horse_swordsman_carthage.vert

@@ -11,15 +11,13 @@ uniform int u_materialId;
 out vec3 v_normal;
 out vec2 v_texCoord;
 out vec3 v_worldPos;
-out float
-    v_armorLayer; // Distinguish armor pieces for Carthaginian Numidian cavalry
+out float v_armorLayer;
 
 void main() {
   v_normal = mat3(transpose(inverse(u_model))) * a_normal;
   v_texCoord = a_texCoord;
   v_worldPos = vec3(u_model * vec4(a_position, 1.0));
 
-  // Keep armor material consistent: 1.0 means armor in the fragment shader.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   gl_Position = u_mvp * vec4(a_position, 1.0);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 106 - 361
assets/shaders/horse_swordsman_roman_republic.frag


+ 9 - 15
assets/shaders/horse_swordsman_roman_republic.vert

@@ -14,13 +14,13 @@ out vec3 v_tangent;
 out vec3 v_bitangent;
 out vec2 v_texCoord;
 out vec3 v_worldPos;
-out float v_armorLayer;  // Distinguish armor pieces for Roman equites cavalry
-out float v_bodyHeight;  // 0.0-1.0 for both rider and horse
-out float v_armorSheen;  // Rider armor polish level
-out float v_leatherWear; // Tack aging
-out float v_horseMusculature; // Horse body definition
-out float v_hairFlow;         // Mane/tail direction
-out float v_hoofWear;         // Hoof chipping
+out float v_armorLayer;
+out float v_bodyHeight;
+out float v_armorSheen;
+out float v_leatherWear;
+out float v_horseMusculature;
+out float v_hairFlow;
+out float v_hoofWear;
 
 float hash13(vec3 p) {
   return fract(sin(dot(p, vec3(12.9898, 78.233, 37.719))) * 43758.5453);
@@ -37,7 +37,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space for normal mapping
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -51,23 +50,18 @@ void main() {
   v_texCoord = a_texCoord;
   v_worldPos = vec3(u_model * vec4(position, 1.0));
 
-  // Body height for both rider and horse (0.0-1.0)
   v_bodyHeight = clamp((v_worldPos.y + 0.2) / 2.0, 0.0, 1.0);
 
-  // Procedural detail based on world position
   float hashVal = hash13(v_worldPos * 0.5);
 
-  // Rider equipment details (elite cavalry)
-  v_armorSheen = 0.8 + hashVal * 0.2;  // Highest polish for elite
-  v_leatherWear = hashVal * 0.3 + 0.1; // Less wear on elite equipment
+  v_armorSheen = 0.8 + hashVal * 0.2;
+  v_leatherWear = hashVal * 0.3 + 0.1;
 
-  // Horse details
   v_horseMusculature =
       smoothstep(0.3, 0.6, v_bodyHeight) * smoothstep(1.0, 0.7, v_bodyHeight);
   v_hairFlow = hashVal * 0.5 + 0.5;
   v_hoofWear = hashVal * 0.3;
 
-  // Armor selection based solely on material ID.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   gl_Position = u_mvp * vec4(position, 1.0);

+ 6 - 38
assets/shaders/olive_instanced.frag

@@ -17,7 +17,6 @@ out vec4 FragColor;
 const float PI = 3.14159265359;
 const float TWO_PI = 6.28318530718;
 
-// Pseudo-random hash functions
 float hash(vec2 p) {
   return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
 }
@@ -26,7 +25,6 @@ float hash3(vec3 p) {
   return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453);
 }
 
-// Simple 2D noise
 float noise2D(vec2 p) {
   vec2 i = floor(p);
   vec2 f = fract(p);
@@ -39,71 +37,53 @@ float noise2D(vec2 p) {
 }
 
 void main() {
-  // --- Lighting ---
+
   vec3 n = normalize(vNormal);
   vec3 l = normalize(uLightDirection);
   float diffuse = max(dot(n, l), 0.0);
   float ambient = 0.45;
   float lighting = ambient + diffuse * 0.60;
 
-  // === MANY SMALL LEAVES ===
-  // The key is high-frequency patterns that look like individual small leaves
-
-  // Screen-space position for stable leaf patterns
   vec2 leafPos = vLocalPosXZ * 120.0 + vec2(vLeafSeed * 17.3, vBarkSeed * 23.1);
 
-  // Individual leaf shapes - small ellipses scattered across surface
-  // Use multiple layers for depth
   float leafLayer1 = hash(floor(leafPos));
   float leafLayer2 = hash(floor(leafPos * 1.7 + vec2(5.3, 8.7)));
   float leafLayer3 = hash(floor(leafPos * 0.6 + vec2(13.1, 3.9)));
 
-  // Combine layers - more leaves where all layers align
   float leafDensity = (leafLayer1 + leafLayer2 + leafLayer3) / 3.0;
 
-  // High frequency variation for leaf edges
   float leafEdge = noise2D(leafPos * 2.5);
   float leafFine = hash(leafPos * 0.37 + vec2(vBranchId * 7.0));
 
-  // === LEAF COLORS - Olive tree specific ===
-  // Real olive leaves: dark green on top, silvery-gray underneath
-  vec3 leafDarkGreen = vec3(0.22, 0.32, 0.20);  // Dark olive green
-  vec3 leafMidGreen = vec3(0.32, 0.42, 0.28);   // Medium green
-  vec3 leafLightGreen = vec3(0.42, 0.50, 0.38); // Light green
-  vec3 leafSilver = vec3(0.52, 0.56, 0.50);     // Silvery underside
+  vec3 leafDarkGreen = vec3(0.22, 0.32, 0.20);
+  vec3 leafMidGreen = vec3(0.32, 0.42, 0.28);
+  vec3 leafLightGreen = vec3(0.42, 0.50, 0.38);
+  vec3 leafSilver = vec3(0.52, 0.56, 0.50);
 
-  // Base leaf color with per-leaf variation
   float colorChoice = leafFine;
   vec3 leafColor = mix(leafDarkGreen, leafMidGreen, colorChoice);
 
-  // Some leaves show silvery underside (facing away from light)
   float backfacing = 1.0 - max(dot(n, l), 0.0);
   float silverShow = smoothstep(0.4, 0.8, backfacing) * leafLayer2;
   leafColor = mix(leafColor, leafSilver, silverShow * 0.5);
 
-  // Highlight some leaves
   float highlight = smoothstep(0.7, 0.9, leafLayer1 * leafEdge);
   leafColor = mix(leafColor, leafLightGreen, highlight * 0.4);
 
-  // Instance tint variation
   leafColor = mix(leafColor, vColor, 0.15);
 
-  // Interior shadow - darker toward center of canopy
   float canopyDepth = 1.0 - smoothstep(0.0, 0.35, length(vLocalPosXZ));
   leafColor *= mix(0.75, 1.0, canopyDepth);
 
-  // === BARK - Gnarled olive trunk ===
   float barkU = vTexCoord.x * TWO_PI;
   float barkV = vTexCoord.y;
 
-  // Deep vertical furrows characteristic of old olive trees
   float furrows = pow(abs(sin(barkU * 5.0 + vBarkSeed * TWO_PI)), 0.4);
   float verticalGrain =
       noise2D(vec2(barkU * 3.0, barkV * 25.0 + vBarkSeed * 7.0));
   float barkNoise = noise2D(vec2(barkU * 8.0, barkV * 15.0)) * 0.3;
   float barkTexture = furrows * 0.5 + verticalGrain * 0.35 + barkNoise;
 
-  // Olive bark colors - gray-brown
   vec3 barkDark = vec3(0.18, 0.16, 0.13);
   vec3 barkMid = vec3(0.30, 0.27, 0.23);
   vec3 barkLight = vec3(0.42, 0.38, 0.33);
@@ -113,51 +93,39 @@ void main() {
       smoothstep(0.75, 0.95, hash(vec2(barkV * 15.0, barkU * 3.0)));
   barkColor = mix(barkColor, barkLight, barkHighlight * 0.35);
 
-  // === FINAL COLOR ===
   vec3 baseColor = mix(barkColor, leafColor, vFoliageMask);
   vec3 color = baseColor * lighting;
 
-  // === ALPHA - Create many small leaf silhouettes ===
   float alpha = 1.0;
 
   if (vFoliageMask > 0.1) {
-    // Base: lots of small gaps between leaves
-    // Higher frequency = smaller individual leaves
+
     float leafMask = leafDensity;
 
-    // Add leaf-shaped holes using noise threshold
     float holePattern = noise2D(leafPos * 0.8);
     float holes = smoothstep(0.20, 0.35, holePattern);
 
-    // Gaps between leaf clusters (medium scale)
     float clusterGaps = noise2D(vLocalPosXZ * 15.0 + vec2(vLeafSeed * 3.0));
     float gaps = smoothstep(0.15, 0.40, clusterGaps);
 
-    // Edge fade - more gaps at canopy edge
     float edgeDist = length(vLocalPosXZ);
     float edgeFade = 1.0 - smoothstep(0.25, 0.50, edgeDist) * 0.5;
 
-    // Top fade
     float topFade = 1.0 - smoothstep(0.85, 1.0, vTexCoord.y) * 0.4;
 
-    // Combine all alpha factors
     alpha = leafMask * holes * gaps * edgeFade * topFade;
     alpha = mix(1.0, alpha, vFoliageMask);
 
-    // Random small holes for airiness
     if (leafFine > 0.82) {
       alpha *= 0.2;
     }
   }
 
-  // Ground fade
   alpha *= smoothstep(0.0, 0.06, vTexCoord.y);
 
-  // Alpha test - discard fully transparent pixels
   if (alpha < 0.15)
     discard;
 
-  // Normalize alpha
   alpha = clamp(alpha * 1.3, 0.0, 1.0);
 
   FragColor = vec4(color, alpha);

+ 4 - 26
assets/shaders/olive_instanced.vert

@@ -1,27 +1,17 @@
 #version 330 core
 
-// ─────────────────────────────────────────────────────────────
-// Vertex Attributes
-// ─────────────────────────────────────────────────────────────
 layout(location = 0) in vec3 aPos;
 layout(location = 1) in vec2 aTexCoord;
 layout(location = 2) in vec3 aNormal;
-layout(location = 3) in vec4 aPosScale;  // instance: xyz = world pos, w = scale
-layout(location = 4) in vec4 aColorSway; // instance: rgb = tint, a = sway phase
-layout(location =
-           5) in vec4 aRotation; // instance: x = Y-axis rotation, yzw = seeds
-
-// ─────────────────────────────────────────────────────────────
-// Uniforms
-// ─────────────────────────────────────────────────────────────
+layout(location = 3) in vec4 aPosScale;
+layout(location = 4) in vec4 aColorSway;
+layout(location = 5) in vec4 aRotation;
+
 uniform mat4 uViewProj;
 uniform float uTime;
 uniform float uWindStrength;
 uniform float uWindSpeed;
 
-// ─────────────────────────────────────────────────────────────
-// Varyings
-// ─────────────────────────────────────────────────────────────
 out vec3 vWorldPos;
 out vec3 vNormal;
 out vec3 vColor;
@@ -32,13 +22,9 @@ out float vBarkSeed;
 out float vBranchId;
 out vec2 vLocalPosXZ;
 
-// ─────────────────────────────────────────────────────────────
-// Main Shader Logic
-// ─────────────────────────────────────────────────────────────
 void main() {
   const float TWO_PI = 6.2831853;
 
-  // Instance data unpacking
   float scale = aPosScale.w;
   vec3 worldPos = aPosScale.xyz;
   float swayPhase = aColorSway.a;
@@ -49,26 +35,20 @@ void main() {
 
   vec3 modelPos = aPos;
 
-  // ── Region masks based on texture V coordinate ──
-  // Trunk: v = 0.00-0.20, Branches: 0.18-0.50, Leaves: 0.50-1.00
   float trunkMask = 1.0 - smoothstep(0.12, 0.20, aTexCoord.y);
   float foliageMask = smoothstep(0.45, 0.55, aTexCoord.y);
 
-  // Branch ID from horizontal angle for variation
   float angle = aTexCoord.x * TWO_PI;
   float branchId = floor(angle / TWO_PI * 4.0 + silhouetteSeed * 4.0);
 
-  // ── Trunk gnarling ──
   if (trunkMask > 0.0) {
     float twist = sin(aTexCoord.y * 20.0 + barkSeed * TWO_PI) * 0.02;
     modelPos.x += twist * trunkMask;
     modelPos.z += twist * 0.7 * trunkMask;
   }
 
-  // Scale
   vec3 localPos = modelPos * scale;
 
-  // ── Wind sway ──
   float heightFactor = clamp(aPos.y * 2.0, 0.0, 1.0);
   float windTime = uTime * uWindSpeed * 0.4;
   float sway = sin(windTime + swayPhase) * uWindStrength * 0.3;
@@ -78,7 +58,6 @@ void main() {
   localPos.x += sway * swayAmount;
   localPos.z += sway2 * swayAmount * 0.6;
 
-  // ── Instance rotation ──
   float cosR = cos(rotation);
   float sinR = sin(rotation);
   mat2 rot = mat2(cosR, -sinR, sinR, cosR);
@@ -90,7 +69,6 @@ void main() {
   vec3 finalNormal =
       normalize(vec3(rotatedNormalXZ.x, aNormal.y, rotatedNormalXZ.y));
 
-  // ── Outputs ──
   vWorldPos = localPos + worldPos;
   vNormal = finalNormal;
   vColor = aColorSway.rgb;

+ 4 - 13
assets/shaders/pine_instanced.frag

@@ -4,9 +4,9 @@ in vec3 vWorldPos;
 in vec3 vNormal;
 in vec3 vColor;
 in vec2 vTexCoord;
-in float vFoliageMask; // 0 = bark, 1 = foliage
-in float vNeedleSeed;  // per-instance variation
-in float vBarkSeed;    // per-instance variation
+in float vFoliageMask;
+in float vNeedleSeed;
+in float vBarkSeed;
 
 uniform vec3 uLightDirection;
 
@@ -15,36 +15,31 @@ out vec4 FragColor;
 const float PI = 3.14159265359;
 const float TWO_PI = 6.28318530718;
 
-// Hash-based noise (cheap, repeatable)
 float hash(vec2 p) {
   return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
 }
 
 void main() {
-  // --- Lighting ---
+
   vec3 n = normalize(vNormal);
   vec3 l = normalize(uLightDirection);
   float diffuse = max(dot(n, l), 0.0);
   float ambient = 0.4;
   float lighting = ambient + diffuse * 0.7;
 
-  // --- Foliage (needles) variation ---
   float needleNoise = hash(vec2(vTexCoord.x * 28.0 + vNeedleSeed * 7.1,
                                 vTexCoord.y * 24.0 + vNeedleSeed * 5.3));
 
-  // vertical streaking; quantize Y to bands
   float needleStreak = hash(vec2(vTexCoord.x * 12.0 + vNeedleSeed * 3.7,
                                  floor(vTexCoord.y * 6.0 + vNeedleSeed * 2.0)));
 
   vec3 needleColor = vColor * (0.78 + needleNoise * 0.28);
   needleColor += vec3(0.02, 0.05, 0.02) * needleStreak;
 
-  // Subtle brightening toward tips (top of UVs)
   float tipBlend = smoothstep(0.82, 1.02, vTexCoord.y);
   needleColor =
       mix(needleColor, needleColor * vec3(1.08, 1.04, 1.10), tipBlend);
 
-  // --- Bark variation: stripes + noise ---
   float barkStripe = sin(vTexCoord.y * 45.0 + vBarkSeed * TWO_PI) * 0.1 + 0.9;
   float barkNoise = hash(vec2(vTexCoord.x * 18.0 + vBarkSeed * 4.3,
                               vTexCoord.y * 10.0 + vBarkSeed * 7.7));
@@ -52,21 +47,17 @@ void main() {
   vec3 trunkBase = vec3(0.32, 0.24, 0.16) * barkStripe;
   vec3 trunkColor = trunkBase * (0.85 + barkNoise * 0.35);
 
-  // --- Final color ---
   vec3 baseColor = mix(trunkColor, needleColor, vFoliageMask);
   vec3 color = baseColor * lighting;
 
-  // --- Alpha shaping (silhouette for foliage + bottom fade) ---
   float silhouetteNoise = hash(vec2(vTexCoord.x * 30.0 + vNeedleSeed * 9.0,
                                     vTexCoord.y * 40.0 + vNeedleSeed * 5.5));
 
   float alphaFoliage = 0.70 + silhouetteNoise * 0.25;
   float alpha = mix(1.0, alphaFoliage, vFoliageMask);
 
-  // fade near bottom (ground contact)
   alpha *= smoothstep(0.00, 0.05, vTexCoord.y);
 
-  // clamp and alpha test
   alpha = clamp(alpha, 0.0, 1.0);
   if (alpha < 0.06)
     discard;

+ 4 - 27
assets/shaders/pine_instanced.vert

@@ -1,27 +1,17 @@
 #version 330 core
 
-// ─────────────────────────────────────────────────────────────
-// Vertex Attributes
-// ─────────────────────────────────────────────────────────────
 layout(location = 0) in vec3 aPos;
 layout(location = 1) in vec2 aTexCoord;
 layout(location = 2) in vec3 aNormal;
-layout(location = 3) in vec4 aPosScale;  // instance: xyz = world pos, w = scale
-layout(location = 4) in vec4 aColorSway; // instance: rgb = tint, a = sway phase
-layout(location =
-           5) in vec4 aRotation; // instance: x = Y-axis rotation, yzw = seeds
-
-// ─────────────────────────────────────────────────────────────
-// Uniforms
-// ─────────────────────────────────────────────────────────────
+layout(location = 3) in vec4 aPosScale;
+layout(location = 4) in vec4 aColorSway;
+layout(location = 5) in vec4 aRotation;
+
 uniform mat4 uViewProj;
 uniform float uTime;
 uniform float uWindStrength;
 uniform float uWindSpeed;
 
-// ─────────────────────────────────────────────────────────────
-// Varyings
-// ─────────────────────────────────────────────────────────────
 out vec3 vWorldPos;
 out vec3 vNormal;
 out vec3 vColor;
@@ -30,13 +20,9 @@ out float vFoliageMask;
 out float vNeedleSeed;
 out float vBarkSeed;
 
-// ─────────────────────────────────────────────────────────────
-// Main Shader Logic
-// ─────────────────────────────────────────────────────────────
 void main() {
   const float TWO_PI = 6.2831853;
 
-  // Instance data unpacking
   float scale = aPosScale.w;
   vec3 worldPos = aPosScale.xyz;
   float swayPhase = aColorSway.a;
@@ -47,12 +33,10 @@ void main() {
 
   vec3 modelPos = aPos;
 
-  // ── Foliage and tip region masks ──────────────────────────
   float foliageMask = smoothstep(0.34, 0.42, aTexCoord.y);
   float tipMask = smoothstep(0.88, 1.02, aTexCoord.y);
   float angle = aTexCoord.x * TWO_PI;
 
-  // ── Irregular silhouette shaping ──────────────────────────
   float irregularBase = sin(angle * 3.0 + silhouetteSeed * TWO_PI);
   float irregularFine = sin(angle * 5.0 + silhouetteSeed * TWO_PI * 2.0);
   float irregular = (irregularBase * 0.11 + irregularFine * 0.05) *
@@ -60,25 +44,20 @@ void main() {
 
   modelPos.xz *= (1.0 + irregular);
 
-  // Slight droop on foliage
   float droop = foliageMask * (1.0 - tipMask) * 0.08;
   modelPos.y -= droop;
 
-  // Height-based influence
   float heightFactor = clamp(modelPos.y, 0.0, 1.1);
   vec3 localPos = modelPos * scale;
 
-  // ── Wind sway (stronger at upper regions) ─────────────────
   float sway = sin(uTime * uWindSpeed * 0.5 + swayPhase) * uWindStrength * 0.8 *
                heightFactor * heightFactor;
 
   float swayInfluence = mix(0.04, 0.12, foliageMask);
   localPos.x += sway * swayInfluence;
 
-  // Slight forward bend for branches
   localPos.y -= sway * 0.02 * foliageMask;
 
-  // ── Adjust normals for irregular foliage ──────────────────
   vec3 localNormal = aNormal;
   if (foliageMask > 0.0) {
     float normalScale = 1.0 + irregular;
@@ -87,7 +66,6 @@ void main() {
                                  localNormal.z * normalScale));
   }
 
-  // ── Instance rotation about Y-axis ────────────────────────
   float cosR = cos(rotation);
   float sinR = sin(rotation);
   mat2 rot = mat2(cosR, -sinR, sinR, cosR);
@@ -99,7 +77,6 @@ void main() {
   vec3 finalNormal =
       normalize(vec3(rotatedNormalXZ.x, localNormal.y, rotatedNormalXZ.y));
 
-  // ── Outputs ───────────────────────────────────────────────
   vWorldPos = localPos + worldPos;
   vNormal = finalNormal;
   vColor = aColorSway.rgb;

+ 8 - 42
assets/shaders/plant_instanced.frag

@@ -4,7 +4,7 @@ in vec3 vWorldPos;
 in vec3 vNormal;
 in vec3 vColor;
 in vec2 vTexCoord;
-in float vAlpha; // (unused)
+in float vAlpha;
 in float vHeight;
 in float vSeed;
 in float vType;
@@ -15,46 +15,28 @@ uniform vec3 uLightDirection;
 
 out vec4 FragColor;
 
-// ─────────────────────────────────────────────────────────────
-// Toggles
-// ─────────────────────────────────────────────────────────────
-// Use when distant shimmer persists and you want depth-stable masking.
-// #define USE_HASHED_ALPHA 1
-// If your renderer uses TAA, screen-anchor the dither; otherwise leave off for
-// world-anchored.
-// #define DITHER_SCREEN_ANCHORED 1
-
-// ─────────────────────────────────────────────────────────────
-// Helpers
-// ─────────────────────────────────────────────────────────────
 float h11(float n) { return fract(sin(n) * 43758.5453123); }
 
-// Stable, screen-space UV footprint with power-of-two quantization.
 float aawidthUV(vec2 uv) {
   vec2 dx = dFdx(uv), dy = dFdy(uv);
   float w = 0.5 * (length(dx) + length(dy));
-  float q = exp2(floor(log2(max(w, 1e-6)) + 0.5)); // quantize to PoT buckets
+  float q = exp2(floor(log2(max(w, 1e-6)) + 0.5));
   return clamp(q, 0.0015, 0.0060);
 }
 
-// Conservative, quantized alpha from SDF + UV footprint.
 float sdf_to_alpha_uv_stable(float sdf, vec2 uv) {
   float w = aawidthUV(uv);
 
-  // Slight inward bias stabilizes coverage at threshold.
   sdf -= 0.25 * w;
 
-  // Analytic coverage.
   float a = 1.0 - smoothstep(-w, w, sdf);
 
-  // Mild alpha quantization (finer as alpha grows) to reduce sub-8bit flicker.
   float steps = mix(24.0, 64.0, a);
   a = floor(a * steps + 0.5) / steps;
 
   return a;
 }
 
-// Deterministic noise; anchor chosen below.
 float interleavedGradientNoise(vec2 p) {
   float f = dot(p, vec2(0.06711056, 0.00583715));
   return fract(52.9829189 * fract(f));
@@ -68,12 +50,8 @@ float stableDither(float seed) {
 #endif
 }
 
-// Quantized step for stable finite differences.
 float quantStep(float w) { return exp2(floor(log2(max(w, 1e-6)) + 0.5)); }
 
-// ─────────────────────────────────────────────────────────────
-// SDF Silhouettes (non-destructive)
-// ─────────────────────────────────────────────────────────────
 float bushSDF(vec2 uv, float seed) {
   vec2 p = (uv - 0.5) * vec2(1.08, 0.96);
   float sdf = 1e9;
@@ -99,7 +77,7 @@ float rosetteSDF(vec2 uv, float seed) {
 
 float cactusSDF(vec2 uv, float seed) {
   vec2 p = (uv - 0.5) * vec2(0.92, 1.08);
-  float sdf = length(p) - 0.48; // body
+  float sdf = length(p) - 0.48;
   for (int i = 0; i < 3; i++) {
     float fi = float(i);
     float ang = mix(-1.6, 1.6, h11(seed * 3.3 + fi));
@@ -119,8 +97,6 @@ float plantSDF(vec2 uv, float typeVal, float seed) {
   return cactusSDF(uv, seed);
 }
 
-// Finite-difference SDF gradient in UV (step tied & quantized to pixel
-// footprint).
 vec2 sdfGrad(vec2 uv, float typeVal, float seed, float stepUV) {
   stepUV = quantStep(stepUV);
   vec2 e = vec2(stepUV, 0.0);
@@ -131,11 +107,8 @@ vec2 sdfGrad(vec2 uv, float typeVal, float seed, float stepUV) {
   return vec2(sx1 - sx2, sy1 - sy2) * (0.5 / stepUV);
 }
 
-// ─────────────────────────────────────────────────────────────
-// Shading
-// ─────────────────────────────────────────────────────────────
 void main() {
-  // Base palette
+
   float dryness = mix(0.35, 0.92, h11(vSeed * 2.7 + vType * 0.73));
   vec3 lush = vec3(0.17, 0.32, 0.19);
   vec3 dry = vec3(0.46, 0.44, 0.28);
@@ -143,7 +116,6 @@ void main() {
   base = mix(base, vColor, 0.40);
   base *= 0.88;
 
-  // Bulged normals for shrub fullness
   vec2 uv2 = (vTexCoord - 0.5) * 2.0;
   float r2 = clamp(dot(uv2, uv2), 0.0, 1.0);
   float z = sqrt(max(1.0 - r2, 0.0));
@@ -151,7 +123,6 @@ void main() {
       normalize(vTangent * uv2.x + vBitangent * uv2.y + vNormal * (z * 1.8));
   vec3 N = normalize(mix(vNormal, Nbulge, 0.85));
 
-  // SDF & stable alpha
   float typeVal = fract(vType);
   float sdf = plantSDF(vTexCoord, typeVal, vSeed);
   float alpha = sdf_to_alpha_uv_stable(sdf, vTexCoord);
@@ -159,7 +130,6 @@ void main() {
   if (alpha <= 0.002)
     discard;
 
-    // Optional hashed alpha for ultra-thin coverage → stable depth
 #ifdef USE_HASHED_ALPHA
   {
     float w = aawidthUV(vTexCoord);
@@ -167,26 +137,23 @@ void main() {
     if (thin < 0.98) {
       if (thin < stableDither(vSeed))
         discard;
-      alpha = 1.0; // write solid depth for kept pixels
+      alpha = 1.0;
     }
   }
 #endif
 
-  // Shape-space normal near silhouettes (with stabilized FD step)
   float stepUV = aawidthUV(vTexCoord);
   vec2 g = sdfGrad(vTexCoord, typeVal, vSeed, stepUV);
   vec3 Nshape =
       normalize(vTangent * (-g.x) + vBitangent * (-g.y) + vNormal * 3.0);
-  float edgeMix = smoothstep(0.30, 0.0, sdf); // only inside & near edge
+  float edgeMix = smoothstep(0.30, 0.0, sdf);
   vec3 Ntemp = normalize(mix(N, Nshape, 0.6 * edgeMix));
 
-  // Edge attenuation to avoid sparkling highlights & reduce normal swapping
   float wAA = aawidthUV(vTexCoord);
-  float edge1 = 1.0 - smoothstep(-wAA, wAA, sdf); // 1 at edge, 0 inside
+  float edge1 = 1.0 - smoothstep(-wAA, wAA, sdf);
   N = normalize(mix(Ntemp, vNormal, edge1 * 0.5));
-  float edgeAtten = mix(0.6, 1.0, pow(1.0 - edge1, 1.5)); // 0.6 at very edge
+  float edgeAtten = mix(0.6, 1.0, pow(1.0 - edge1, 1.5));
 
-  // Lighting
   vec3 L = normalize(uLightDirection);
   float nl = max(dot(N, L), 0.0);
   float halfLambert = nl * 0.5 + 0.5;
@@ -197,7 +164,6 @@ void main() {
 
   float aoStem = mix(0.50, 1.0, smoothstep(0.0, 0.55, vHeight));
 
-  // Subtle tip brightening & inner-edge occlusion (view-stable)
   float tip = smoothstep(0.25, 1.0, r2);
   float inner = smoothstep(-2.0 * wAA, -0.2 * wAA, sdf);
   vec3 albedo = base;

+ 7 - 19
assets/shaders/plant_instanced.vert

@@ -1,16 +1,13 @@
-// ──────────────────────────────────────────────────────────────────────────────
-// VERTEX — larger shrubs, irregular lean, varied wind, tangents for bulge
-// ──────────────────────────────────────────────────────────────────────────────
+
 
 #version 330 core
 
 layout(location = 0) in vec3 aPos;
 layout(location = 1) in vec2 aTexCoord;
 layout(location = 2) in vec3 aNormal;
-layout(location = 3) in vec4 aPosScale;  // xyz = world pos, w = scale
-layout(location = 4) in vec4 aColorSway; // rgb = tint, a = sway phase
-layout(location = 5) in vec4
-    aTypeParams; // x = type, y = rotation, z = sway strength, w = sway speed
+layout(location = 3) in vec4 aPosScale;
+layout(location = 4) in vec4 aColorSway;
+layout(location = 5) in vec4 aTypeParams;
 
 uniform mat4 uViewProj;
 uniform float uTime;
@@ -42,24 +39,20 @@ void main() {
   float swaySpeed = aTypeParams.w;
   float swayPhase = aColorSway.a;
 
-  // stable per-instance seed
   float seed = h31(worldOrigin * 0.173 + vec3(0.27, 0.49, 0.19));
 
-  // bigger plants + size jitter
-  const float SIZE_MULT = 1.85; // ← bump this if still small
+  const float SIZE_MULT = 1.85;
   float sizeJitter = mix(0.90, 1.45, h11(seed * 3.9));
   float finalScale = scale * SIZE_MULT * sizeJitter;
 
   vec3 localPos = aPos * finalScale;
   float h = clamp(aPos.y, 0.0, 1.0);
 
-  // irregular lean (looks like full shrubs, not thin leaves)
-  float leanAngle = (h11(seed * 2.1) - 0.5) * 0.18; // ±10°
+  float leanAngle = (h11(seed * 2.1) - 0.5) * 0.18;
   float leanYaw = h11(seed * 3.7) * 6.28318;
   vec2 leanDir = vec2(cos(leanYaw), sin(leanYaw));
   localPos.xz += leanDir * (h * h) * tan(leanAngle) * finalScale;
 
-  // wind: gentle gusts with per-instance direction
   float gust = sin(uTime * 0.35 + seed * 6.0) * 0.5 + 0.5;
   float sway = sin(uTime * swaySpeed * uWindSpeed + swayPhase + seed * 4.0);
   sway *= (0.22 + 0.55 * gust) * swayStrength * uWindStrength * pow(h, 1.25);
@@ -68,27 +61,23 @@ void main() {
   vec2 windDir = normalize(vec2(cos(windYaw), sin(windYaw)) + vec2(0.6, 0.8));
   localPos.xz += windDir * (0.10 * sway);
 
-  // mild twist toward the tip for volume
-  float twist = (h11(seed * 5.5) - 0.5) * 0.30; // ±17°
+  float twist = (h11(seed * 5.5) - 0.5) * 0.30;
   float twistAngle = twist * h;
   mat2 tw =
       mat2(cos(twistAngle), -sin(twistAngle), sin(twistAngle), cos(twistAngle));
   localPos.xz = tw * localPos.xz;
 
-  // instance rotation about Y
   float cs = cos(rotation), sn = sin(rotation);
   mat2 rot = mat2(cs, -sn, sn, cs);
   localPos.xz = rot * localPos.xz;
 
   vWorldPos = localPos + worldOrigin;
 
-  // rotate normal the same way
   vec3 n = aNormal;
   n.xz = tw * n.xz;
   n.xz = rot * n.xz;
   vNormal = normalize(n);
 
-  // world-space tangents (card X/Z) for bulge normal in FS
   vec3 t = vec3(1.0, 0.0, 0.0);
   vec3 b = vec3(0.0, 0.0, 1.0);
   t.xz = tw * t.xz;
@@ -105,7 +94,6 @@ void main() {
   vColor = aColorSway.rgb;
   vTexCoord = aTexCoord;
 
-  // fuller base alpha (final silhouette handled in FS)
   vAlpha = 1.0 - smoothstep(0.49, 0.56, abs(aTexCoord.x - 0.5));
 
   gl_Position = uViewProj * vec4(vWorldPos, 1.0);

+ 0 - 2
assets/shaders/primitive_instanced.frag

@@ -14,10 +14,8 @@ void main() {
   vec3 normal = normalize(v_normal);
   vec3 lightDir = normalize(u_lightDir);
 
-  // Diffuse lighting
   float diff = max(dot(normal, lightDir), 0.0);
 
-  // Combine ambient and diffuse
   float lighting = u_ambientStrength + (1.0 - u_ambientStrength) * diff;
 
   vec3 color = v_color * lighting;

+ 6 - 12
assets/shaders/primitive_instanced.vert

@@ -1,15 +1,13 @@
 #version 330 core
 
-// Mesh vertex attributes
 layout(location = 0) in vec3 a_position;
 layout(location = 1) in vec3 a_normal;
 layout(location = 2) in vec2 a_texCoord;
 
-// Per-instance attributes (model matrix as 3 columns + color/alpha)
-layout(location = 3) in vec4 i_modelCol0;  // First column of model matrix
-layout(location = 4) in vec4 i_modelCol1;  // Second column of model matrix
-layout(location = 5) in vec4 i_modelCol2;  // Third column of model matrix
-layout(location = 6) in vec4 i_colorAlpha; // RGB color + alpha
+layout(location = 3) in vec4 i_modelCol0;
+layout(location = 4) in vec4 i_modelCol1;
+layout(location = 5) in vec4 i_modelCol2;
+layout(location = 6) in vec4 i_colorAlpha;
 
 uniform mat4 u_viewProj;
 uniform vec3 u_lightDir;
@@ -20,23 +18,19 @@ out vec3 v_color;
 out float v_alpha;
 
 void main() {
-  // Reconstruct model matrix from columns
-  // The 4th column (translation) is in the w components
+
   mat4 modelMatrix =
       mat4(vec4(i_modelCol0.xyz, 0.0), vec4(i_modelCol1.xyz, 0.0),
            vec4(i_modelCol2.xyz, 0.0),
            vec4(i_modelCol0.w, i_modelCol1.w, i_modelCol2.w, 1.0));
 
-  // Transform position
   vec4 worldPos4 = modelMatrix * vec4(a_position, 1.0);
   v_worldPos = worldPos4.xyz;
 
-  // Transform normal (using transpose of inverse for non-uniform scaling)
   mat3 normalMatrix = mat3(modelMatrix);
-  // For uniform scaling, we can simplify
+
   v_normal = normalize(normalMatrix * a_normal);
 
-  // Pass through color and alpha
   v_color = i_colorAlpha.rgb;
   v_alpha = i_colorAlpha.a;
 

+ 9 - 19
assets/shaders/river.frag

@@ -5,7 +5,6 @@ in vec3 WorldPos;
 
 uniform float time;
 
-// ------------------------------------------------------------
 const float PI = 3.14159265359;
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
 vec3 saturate(vec3 v) { return clamp(v, vec3(0.0), vec3(1.0)); }
@@ -62,7 +61,6 @@ float ggxSpec(vec3 N, vec3 V, vec3 L, float rough, float F0) {
   return (D * Gv * Gl * F) / max(4.0 * NdotV * NdotL, 0.001);
 }
 
-// ---------------- water field (isotropic, world-space) ---------------
 float waterHeight(vec2 uv) {
   vec2 p = uv;
   vec2 w1 = vec2(fbm(p * 0.6 + time * 0.05), fbm(p * 0.6 - time * 0.04));
@@ -106,9 +104,8 @@ vec3 waterNormal(vec2 uv, vec2 grad) {
       mix(N, normalize(N + 0.22 * (microNormal(uv) - vec3(0, 1, 0))), 0.35));
 }
 
-// ---------------- main ----------------
 void main() {
-  // world-space UV so no seams
+
   vec2 uv = rot(0.35) * (WorldPos.xz * 0.38);
 
   float h, lap;
@@ -122,47 +119,40 @@ void main() {
   float NdotV = max(dot(N, V), 0.0);
   float F0 = 0.02;
 
-  // ---------- transmission (make it BLUER) ----------
-  vec3 deepWater = vec3(0.008, 0.035, 0.080); // deeper, bluer
+  vec3 deepWater = vec3(0.008, 0.035, 0.080);
   vec3 shallowWater = vec3(0.060, 0.180, 0.300);
 
   float calm = smoothstep(0.0, 0.45, abs(h));
   float shallow = saturate(0.35 + 0.35 * (fbm(uv * 0.6) * (1.0 - calm)));
 
-  // stronger red/green absorption so blue survives
   vec3 absorb = vec3(0.90, 0.45, 0.12);
   float thickness =
       mix(0.6, 3.5, 1.0 - shallow) * (0.35 + pow(1.0 - NdotV, 0.7));
   vec3 transBase = mix(deepWater, shallowWater, shallow);
   vec3 transmission = transBase * exp(-absorb * thickness);
 
-  // ---------- reflection (dim + blue-tinted) ----------
   vec3 R = reflect(-V, N);
   vec3 reflection = skyColor(R, sunDir);
-  reflection *= 0.70;                         // dim
-  reflection *= vec3(0.60, 0.75, 1.00);       // blue tint
-  float F = fresnelSchlick(NdotV, F0) * 0.40; // less mirror
+  reflection *= 0.70;
+  reflection *= vec3(0.60, 0.75, 1.00);
+  float F = fresnelSchlick(NdotV, F0) * 0.40;
 
-  // ---------- lighting (tamed) ----------
   float NdotL = max(dot(N, sunDir), 0.0);
   float rough = mix(0.12, 0.26, smoothstep(0.0, 0.6, length(grad)));
-  float spec = ggxSpec(N, V, sunDir, rough, F0) * 0.50; // half energy
-  vec3 specCol = vec3(0.75, 0.85, 1.10) * spec;         // watery tint
+  float spec = ggxSpec(N, V, sunDir, rough, F0) * 0.50;
+  vec3 specCol = vec3(0.75, 0.85, 1.10) * spec;
   vec3 sunDiffuse = transmission * NdotL * 0.20;
 
-  // shoreline foam only (softer & less white)
   float shore = 1.0 - (smoothstep(0.07, 0.28, TexCoord.y) *
                        smoothstep(0.07, 0.28, 1.0 - TexCoord.y));
   float foam = shore * (0.45 + 0.55 * fbm(uv * 3.0 + time * 0.6));
   vec3 foamCol = vec3(0.92, 0.96, 1.0);
-  foam = clamp(foam * 0.35, 0.0, 1.0); // much less foam
+  foam = clamp(foam * 0.35, 0.0, 1.0);
 
-  // ---------- combine (energy aware) ----------
-  vec3 color = transmission * (1.0 - F) + reflection * F; // avoid whitening
+  vec3 color = transmission * (1.0 - F) + reflection * F;
   color += specCol + sunDiffuse;
   color = mix(color, foamCol * mix(0.82, 1.0, NdotL), foam);
 
-  // gentle blue rim
   color += vec3(0.03, 0.06, 0.12) * pow(1.0 - NdotV, 3.0);
 
   FragColor = vec4(saturate(color), 0.85);

+ 1 - 5
assets/shaders/river.vert

@@ -11,22 +11,18 @@ out vec2 TexCoord;
 out vec3 WorldPos;
 
 void main() {
-  // Multi-directional wave simulation
+
   vec3 pos = aPos;
 
-  // Primary waves along river flow
   float wave1 = sin(aPos.z * 0.5 + time * 2.0) * 0.04;
   float wave2 = sin(aPos.x * 0.8 + time * 1.5) * 0.03;
   float wave3 = sin((aPos.x + aPos.z) * 0.3 + time * 2.5) * 0.02;
 
-  // Cross-ripples for realism
   float ripple1 = sin(aPos.x * 2.0 + aPos.z * 1.5 + time * 3.0) * 0.015;
   float ripple2 = cos(aPos.z * 2.5 - aPos.x * 1.2 + time * 2.2) * 0.012;
 
-  // Combine all wave motions
   pos.y += wave1 + wave2 + wave3 + ripple1 + ripple2;
 
-  // Output world position for fragment shader
   WorldPos = (model * vec4(pos, 1.0)).xyz;
 
   gl_Position = projection * view * model * vec4(pos, 1.0);

+ 6 - 27
assets/shaders/riverbank.frag

@@ -1,13 +1,12 @@
 #version 330 core
 out vec4 FragColor;
 
-in vec2 TexCoord; // x: 0 = water edge -> 1 = land side, y: along the river
+in vec2 TexCoord;
 in vec3 WorldPos;
 in vec3 Normal;
 
 uniform float time;
 
-// ------------------------- utils -------------------------
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
 vec2 saturate(vec2 v) { return clamp(v, vec2(0.0), vec2(1.0)); }
 vec3 saturate(vec3 v) { return clamp(v, vec3(0.0), vec3(1.0)); }
@@ -39,87 +38,67 @@ vec2 warp(vec2 uv) {
   return uv + 0.22 * w1 + 0.12 * w2;
 }
 
-// ------------------------- main -------------------------
 void main() {
-  // World-anchored UVs to avoid tiling per mesh piece
+
   vec2 uv = warp(WorldPos.xz * 0.45);
 
-  // --- Palettes (slightly lighter and more natural) ---
   vec3 wetSoil = vec3(0.20, 0.17, 0.14);
   vec3 dampSoil = vec3(0.32, 0.27, 0.22);
   vec3 drySoil = vec3(0.46, 0.40, 0.33);
   vec3 grassTint = vec3(0.30, 0.50, 0.25);
 
-  // --- Wetness computation ---
-  // Base width from cross-river coord (narrower; was too thick)
-  float baseWet =
-      smoothstep(0.30, 0.04, TexCoord.x); // 1 near water, 0 towards land
+  float baseWet = smoothstep(0.30, 0.04, TexCoord.x);
 
-  // Irregular shoreline: jitter width with world-space FBM and along-flow noise
   float edgeJitter = (fbm(uv * 2.5) - 0.5) * 0.12 +
                      (fbm(vec2(TexCoord.y * 3.0, 0.0)) - 0.5) * 0.10;
   baseWet = saturate(baseWet + edgeJitter);
 
-  // Slope-aware: steeper banks drain faster (less wet)
   float slope = 1.0 - saturate(normalize(Normal).y);
   float wetness = saturate(baseWet - slope * 0.45);
 
-  // A super-narrow “water contact” band to soften the hard polygon edge
-  // visually
   float contact =
       smoothstep(0.10, 0.95, wetness) * smoothstep(0.04, 0.00, TexCoord.x);
-  contact *= 0.6 + 0.4 * fbm(uv * 4.0 + time * 0.2); // noisy, thin
+  contact *= 0.6 + 0.4 * fbm(uv * 4.0 + time * 0.2);
 
-  // --- Macro variation & flow-aligned streaks (erosion/silt) ---
   float macro = fbm(uv * 0.8);
   float streaks =
       fbm(vec2(TexCoord.y * 6.0 + macro * 0.7, TexCoord.x * 0.6 - time * 0.03));
-  streaks = pow(saturate(streaks), 3.0); // thin dark streak lines
+  streaks = pow(saturate(streaks), 3.0);
 
-  // Small grit & sparse pebbles
   float grit = noise(uv * 18.0);
   float pebbles = smoothstep(0.82, 0.97, noise(uv * 9.0 + 17.3));
 
-  // --- Albedo synthesis ---
-  // Wet->dry soil ramp with macro tinting
   vec3 soilWet = mix(wetSoil, dampSoil, saturate(macro * 0.5 + 0.25));
   vec3 soilDry = mix(dampSoil, drySoil, saturate(macro * 0.5 + 0.25));
   vec3 base = mix(soilWet, soilDry, 1.0 - wetness);
 
-  // Grass appears as it gets drier, patchy by macro noise
   float grassMask = smoothstep(0.50, 0.92, 1.0 - wetness) *
                     smoothstep(0.25, 0.75, fbm(uv * 1.2 + 5.1));
   base = mix(base, mix(base, grassTint, 0.6), grassMask);
 
-  // Soft cool tint right at the contact band (reduces “brown outline”)
   base = mix(base, base * vec3(0.92, 0.96, 1.02), contact * 0.35);
 
-  // Grit/pebble breakup (subtle, only really reads when dry)
   base *= (0.92 + 0.16 * grit);
   base = mix(base, base * 0.82 + vec3(0.05),
              pebbles * (0.10 + 0.35 * (1.0 - wetness)));
 
-  // --- Lighting ---
   vec3 L = normalize(vec3(0.3, 0.8, 0.4));
   vec3 V = normalize(vec3(0.0, 1.0, 0.5));
   vec3 N = normalize(Normal);
 
   float NdotL = max(dot(N, L), 0.0);
 
-  // Moisture darkening (lighter than before to avoid a black ring)
   float darken = mix(1.0, 0.94, wetness);
 
   vec3 ambient = vec3(0.42, 0.44, 0.46);
   vec3 color = base * darken * (ambient + NdotL * vec3(0.66, 0.64, 0.60));
 
-  // Wet specular: only very near the water edge; cool tint
   vec3 H = normalize(L + V);
   float shininess = mix(10.0, 38.0, saturate(wetness * 1.2));
   float spec = pow(max(dot(N, H), 0.0), shininess);
-  spec *= (0.10 + 0.50 * contact); // confined to the thin contact zone
+  spec *= (0.10 + 0.50 * contact);
   color += spec * vec3(0.30, 0.33, 0.36);
 
-  // Flow-aligned silt darkening (very gentle)
   color *= (1.0 - streaks * 0.07 * (0.3 + 0.7 * wetness));
 
   FragColor = vec4(saturate(color), 1.0);

+ 0 - 1
assets/shaders/riverbank.vert

@@ -15,7 +15,6 @@ out vec3 Normal;
 void main() {
   vec3 pos = aPos;
 
-  // Subtle movement for organic feel
   float sway = sin(aPos.x * 0.3 + time * 0.5) * 0.005;
   pos.y += sway;
 

+ 6 - 35
assets/shaders/road.frag

@@ -4,15 +4,12 @@ in vec3 v_normal;
 in vec2 v_tex_coord;
 in vec3 v_world_pos;
 
-uniform vec3 u_color;           // base road color
-uniform vec3 u_light_direction; // world-space light direction
-uniform float u_alpha;          // transparency (for fog-of-war)
+uniform vec3 u_color;
+uniform vec3 u_light_direction;
+uniform float u_alpha;
 
 out vec4 frag_color;
 
-// ------------------------------------------------------------
-// Helpers
-// ------------------------------------------------------------
 const float PI = 3.14159265359;
 
 float saturate_val(float x) { return clamp(x, 0.0, 1.0); }
@@ -22,7 +19,6 @@ mat2 rotate_2d(float a) {
   return mat2(c, -s, s, c);
 }
 
-// Hash / Noise functions
 float hash_2d(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * 0.1031);
   p3 += dot(p3, p3.yzx + 33.33);
@@ -50,13 +46,11 @@ float fbm_2d(vec2 p) {
   return v;
 }
 
-// 2D random vector for Worley
 vec2 hash_2d_vec(vec2 p) {
   float n = sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123;
   return fract(vec2(n, n * 1.2154));
 }
 
-// Worley (Voronoi) for stone pattern: returns F1 and F2
 vec2 worley_f(vec2 p) {
   vec2 n = floor(p);
   vec2 f = fract(p);
@@ -79,12 +73,10 @@ vec2 worley_f(vec2 p) {
   return vec2(sqrt(f1), sqrt(f2));
 }
 
-// Fresnel (Schlick)
 float fresnel_schlick(float cos_theta, float f0) {
   return f0 + (1.0 - f0) * pow(1.0 - cos_theta, 5.0);
 }
 
-// GGX specular
 float ggx_specular(vec3 n_vec, vec3 v_vec, vec3 l_vec, float rough, float f0) {
   vec3 h_vec = normalize(v_vec + l_vec);
   float n_dot_v = max(dot(n_vec, v_vec), 0.0);
@@ -107,86 +99,67 @@ float ggx_specular(vec3 n_vec, vec3 v_vec, vec3 l_vec, float rough, float f0) {
   return (d_val * g_val * f_val) / max(4.0 * n_dot_v * n_dot_l, 1e-4);
 }
 
-// ------------------------------------------------------------
-// Main
-// ------------------------------------------------------------
 void main() {
-  // World-anchored UV mapping for consistent stone pattern
+
   vec2 uv = v_world_pos.xz * 0.8;
 
-  // Irregular cobblestone layout via Worley noise
   vec2 worley_result = worley_f(uv * 1.5);
   float edge_metric = worley_result.y - worley_result.x;
   float stone_mask = smoothstep(0.04, 0.25, edge_metric);
   float mortar_mask = 1.0 - stone_mask;
 
-  // Per-cell variation for stone shapes
   vec2 cell = floor(uv * 1.5);
   float cell_rnd = hash_2d(cell);
   vec2 local = fract(uv);
   vec2 uv_var =
       (rotate_2d(cell_rnd * 6.2831853) * (local - 0.5) + 0.5) + floor(uv);
 
-  // Albedo variation - Roman roads have weathered stone look
   float var_low = (fbm_2d(uv * 0.4) - 0.5) * 0.18;
   float var_mid = (fbm_2d(uv_var * 2.5) - 0.5) * 0.12;
   float grain = (noise_2d(uv_var * 15.0) - 0.5) * 0.06;
 
-  // Base stone color with Roman road characteristics (brownish-gray)
   vec3 stone_base = u_color;
   vec3 stone_color = stone_base * (1.0 + var_low + var_mid + grain);
 
-  // Mortar/gravel between stones - darker and rougher
   vec3 mortar_color = stone_base * 0.60;
 
-  // Wear patterns - roads show wear in the center from traffic
   float center_wear = 1.0 - abs(v_tex_coord.x - 0.5) * 1.8;
   center_wear = smoothstep(0.0, 0.5, center_wear) * 0.08;
   stone_color *= (1.0 + center_wear);
 
-  // Micro cracks in stones
   float crack = smoothstep(0.02, 0.0, abs(noise_2d(uv * 12.0) - 0.5)) * 0.20;
   stone_color *= (1.0 - crack * stone_mask);
 
-  // Ambient occlusion in mortar grooves
   float cavity = smoothstep(0.0, 0.20, edge_metric);
   float ao = mix(0.50, 1.0, cavity) * (0.90 + 0.10 * fbm_2d(uv * 2.0));
 
-  // Height variation for normal perturbation
   float micro_bump = (fbm_2d(uv_var * 3.5) - 0.5) * 0.05 * stone_mask;
   float macro_warp = (fbm_2d(uv * 1.0) - 0.5) * 0.025 * stone_mask;
   float mortar_dip = -0.05 * mortar_mask;
   float h = micro_bump + macro_warp + mortar_dip;
 
-  // Screen-space normal perturbation
   float sx = dFdx(h);
   float sy = dFdy(h);
   float bump_strength = 12.0;
   vec3 n_bump = normalize(vec3(-sx * bump_strength, 1.0, -sy * bump_strength));
 
-  // Blend with geometric normal
   vec3 n_geom = normalize(v_normal);
   vec3 n_final = normalize(mix(n_geom, n_bump, 0.60));
 
-  // Lighting
   vec3 light_dir = normalize(u_light_direction);
-  vec3 view_dir = normalize(vec3(0.0, 0.9, 0.4)); // Fixed view direction
+  vec3 view_dir = normalize(vec3(0.0, 0.9, 0.4));
 
-  // Diffuse (Lambert) with AO
   float n_dot_l = max(dot(n_final, light_dir), 0.0);
   float diffuse = n_dot_l;
 
-  // Roughness - worn stones are smoother, edges rougher
   float steep = saturate_val(length(vec2(sx, sy)) * bump_strength);
   float roughness = clamp(mix(0.70, 0.95, steep), 0.02, 1.0);
-  float f0 = 0.03; // Dielectric stone
+  float f0 = 0.03;
 
   float spec = ggx_specular(n_final, view_dir, light_dir, roughness, f0);
 
-  // Final base color: blend stone and mortar
   vec3 base_color = mix(mortar_color, stone_color, stone_mask);
 
-  // Hemispheric ambient lighting
   vec3 hemi_sky = vec3(0.20, 0.25, 0.30);
   vec3 hemi_ground = vec3(0.10, 0.09, 0.08);
   float hemi = n_final.y * 0.5 + 0.5;
@@ -195,12 +168,10 @@ void main() {
   lit_color += mix(hemi_ground, hemi_sky, hemi) * 0.12;
   lit_color += vec3(1.0) * spec * 0.20;
 
-  // Dirt accumulation in grooves
   float grime = (1.0 - cavity) * 0.20 * (0.8 + 0.2 * noise_2d(uv * 6.0));
   float gray = dot(lit_color, vec3(0.299, 0.587, 0.114));
   lit_color = mix(lit_color, vec3(gray * 0.85), grime);
 
-  // Edge darkening for road borders
   float edge_fade = smoothstep(0.0, 0.08, v_tex_coord.x) *
                     smoothstep(0.0, 0.08, 1.0 - v_tex_coord.x);
   lit_color *= mix(0.75, 1.0, edge_fade);

+ 1 - 2
assets/shaders/road.vert

@@ -12,11 +12,10 @@ out vec2 v_tex_coord;
 out vec3 v_world_pos;
 
 void main() {
-  // Transform normal to world space
+
   v_normal = normalize(mat3(transpose(inverse(u_model))) * a_normal);
   v_tex_coord = a_tex_coord;
 
-  // World position for lighting and procedural texturing
   v_world_pos = vec3(u_model * vec4(a_position, 1.0));
 
   gl_Position = u_mvp * vec4(a_position, 1.0);

+ 16 - 50
assets/shaders/spearman.frag

@@ -1,6 +1,5 @@
 #version 330 core
 
-// === Inputs preserved (do not change) ===
 in vec3 v_normal;
 in vec2 v_texCoord;
 in vec3 v_worldPos;
@@ -12,7 +11,6 @@ uniform float u_alpha;
 
 out vec4 FragColor;
 
-// === Utility ===
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
 vec3 saturate(vec3 v) { return clamp(v, 0.0, 1.0); }
 
@@ -39,14 +37,12 @@ float leatherGrain(vec2 p) {
   return grain + pores;
 }
 
-// Fixed bug: use 2D input (was referencing p.z).
 float fabricWeave(vec2 p) {
   float weaveU = sin(p.x * 60.0);
   float weaveV = sin(p.y * 60.0);
   return weaveU * weaveV * 0.05;
 }
 
-// Hemispheric ambient (simple IBL feel without extra uniforms)
 vec3 hemiAmbient(vec3 n) {
   float up = saturate(n.y * 0.5 + 0.5);
   vec3 sky = vec3(0.60, 0.70, 0.80) * 0.35;
@@ -54,31 +50,27 @@ vec3 hemiAmbient(vec3 n) {
   return mix(ground, sky, up);
 }
 
-// Schlick Fresnel
 vec3 fresnelSchlick(float cosTheta, vec3 F0) {
   return F0 + (1.0 - F0) * pow(1.0 - saturate(cosTheta), 5.0);
 }
 
-// GGX / Trowbridge-Reitz
 float distributionGGX(float NdotH, float a) {
   float a2 = a * a;
   float d = (NdotH * NdotH) * (a2 - 1.0) + 1.0;
   return a2 / max(3.14159265 * d * d, 1e-6);
 }
 
-// Smith's Schlick-G for GGX
 float geometrySchlickGGX(float NdotX, float k) {
   return NdotX / max(NdotX * (1.0 - k) + k, 1e-6);
 }
 float geometrySmith(float NdotV, float NdotL, float roughness) {
   float r = roughness + 1.0;
-  float k = (r * r) / 8.0; // Schlick approximation
+  float k = (r * r) / 8.0;
   float ggx1 = geometrySchlickGGX(NdotV, k);
   float ggx2 = geometrySchlickGGX(NdotL, k);
   return ggx1 * ggx2;
 }
 
-// Screen-space curvature (edge detector) from normal derivatives
 float edgeWearMask(vec3 n) {
   vec3 nx = dFdx(n);
   vec3 ny = dFdy(n);
@@ -86,7 +78,6 @@ float edgeWearMask(vec3 n) {
   return saturate(smoothstep(0.10, 0.70, curvature));
 }
 
-// Build an approximate TBN from derivatives (no new inputs needed)
 void buildTBN(out vec3 T, out vec3 B, out vec3 N, vec3 n, vec3 pos, vec2 uv) {
   vec3 dp1 = dFdx(pos);
   vec3 dp2 = dFdy(pos);
@@ -100,13 +91,11 @@ void buildTBN(out vec3 T, out vec3 B, out vec3 N, vec3 n, vec3 pos, vec2 uv) {
   N = normalize(n);
 }
 
-// Cheap bump from a procedural height map in UV space
 vec3 perturbNormalFromHeight(vec3 n, vec3 pos, vec2 uv, float height,
                              float scale, float strength) {
   vec3 T, B, N;
   buildTBN(T, B, N, n, pos, uv);
 
-  // Finite-difference heights in UV for gradient
   float h0 = height;
   float hx = noise((uv + vec2(0.002, 0.0)) * scale) - h0;
   float hy = noise((uv + vec2(0.0, 0.002)) * scale) - h0;
@@ -116,13 +105,12 @@ vec3 perturbNormalFromHeight(vec3 n, vec3 pos, vec2 uv, float height,
 }
 
 void main() {
-  // Base color
+
   vec3 color = u_color;
   if (u_useTexture) {
     color *= texture(u_texture, v_texCoord).rgb;
   }
 
-  // Inputs & coordinate prep
   vec3 N = normalize(v_normal);
   vec2 uvW = v_worldPos.xz * 4.5;
   vec2 uv = v_texCoord * 4.5;
@@ -131,15 +119,12 @@ void main() {
   float colorHue =
       max(max(color.r, color.g), color.b) - min(min(color.r, color.g), color.b);
 
-  // Material classification preserved
   bool isMetal = (avgColor > 0.45 && avgColor <= 0.65 && colorHue < 0.15);
   bool isLeather = (avgColor > 0.30 && avgColor <= 0.50 && colorHue < 0.20);
   bool isFabric = (avgColor > 0.25 && !isMetal && !isLeather);
 
-  // Lighting basis (kept compatible with prior shader)
   vec3 L = normalize(vec3(1.0, 1.15, 1.0));
-  // Approximate view vector from world origin; nudged to avoid degenerate
-  // normalization
+
   vec3 V = normalize(-v_worldPos + N * 0.001);
   vec3 H = normalize(L + V);
 
@@ -148,69 +133,57 @@ void main() {
   float NdotH = saturate(dot(N, H));
   float VdotH = saturate(dot(V, H));
 
-  // Ambient
   vec3 ambient = hemiAmbient(N);
 
-  // Shared wrap diffuse (preserved behavior, slight tweak via saturate)
   float wrapAmount = isMetal ? 0.12 : (isLeather ? 0.25 : 0.35);
   float diffWrap = max(NdotL * (1.0 - wrapAmount) + wrapAmount, 0.20);
   if (isMetal)
     diffWrap = pow(diffWrap, 0.88);
 
-  // Edge & cavity masks (for wear/rust/shine)
-  float edgeMask = edgeWearMask(N);  // bright edges
-  float cavityMask = 1.0 - edgeMask; // crevices
-  // Gravity bias: downward-facing areas collect more dirt/rust
+  float edgeMask = edgeWearMask(N);
+  float cavityMask = 1.0 - edgeMask;
+
   float downBias = saturate((-N.y) * 0.6 + 0.4);
   cavityMask *= downBias;
 
-  // === Material models ===
-  vec3 F0 = vec3(0.04);  // default dielectric reflectance
-  float roughness = 0.6; // default roughness
-  float cavityAO = 1.0;  // occlusion multiplier
-  vec3 albedo = color;   // base diffuse/albedo
+  vec3 F0 = vec3(0.04);
+  float roughness = 0.6;
+  float cavityAO = 1.0;
+  vec3 albedo = color;
   vec3 specular = vec3(0.0);
 
   if (isMetal) {
-    // Use texture UVs for stability (as in original)
+
     vec2 metalUV = v_texCoord * 4.5;
 
-    // Brushed/anisotropic micro-lines & microdents
     float brushed = abs(sin(v_texCoord.y * 85.0)) * 0.022;
     float dents = noise(metalUV * 6.0) * 0.035;
     float rustTex = noise(metalUV * 8.0) * 0.10;
 
-    // Small directional scratches
     float scratchLines = smoothstep(
         0.97, 1.0, abs(sin(metalUV.x * 160.0 + noise(metalUV * 3.0) * 2.0)));
     scratchLines *= 0.08;
 
-    // Procedural height for bumping (kept subtle to avoid shimmer)
     float height = noise(metalUV * 12.0) * 0.5 + brushed * 2.0 + scratchLines;
     vec3 Np =
         perturbNormalFromHeight(N, v_worldPos, v_texCoord, height, 12.0, 0.55);
-    N = mix(N, Np, 0.65); // blend to keep stable
+    N = mix(N, Np, 0.65);
 
-    // Physically-based specular with GGX
     roughness = clamp(0.18 + brushed * 0.35 + dents * 0.25 + rustTex * 0.30 -
                           edgeMask * 0.12,
                       0.05, 0.9);
     float a = max(0.001, roughness * roughness);
 
-    // Metals take F0 from their base color
     F0 = saturate(color);
 
-    // Rust/dirt reduce albedo and boost roughness in cavities
     float rustMask = smoothstep(0.55, 0.85, noise(metalUV * 5.5)) * cavityMask;
-    vec3 rustTint = vec3(0.35, 0.18, 0.08); // warm iron-oxide tint
+    vec3 rustTint = vec3(0.35, 0.18, 0.08);
     albedo = mix(albedo, albedo * 0.55 + rustTint * 0.35, rustMask);
     roughness = clamp(mix(roughness, 0.85, rustMask), 0.05, 0.95);
 
-    // Edge wear: brighten edges with lower roughness (polished)
     albedo = mix(albedo, albedo * 1.12 + vec3(0.05), edgeMask * 0.7);
     roughness = clamp(mix(roughness, 0.10, edgeMask * 0.5), 0.05, 0.95);
 
-    // Recompute lighting terms with updated normal
     H = normalize(L + V);
     NdotL = saturate(dot(N, L));
     NdotV = saturate(dot(N, V));
@@ -223,31 +196,26 @@ void main() {
 
     specular = (D * G * F) / max(4.0 * NdotL * NdotV, 1e-4);
 
-    // Clearcoat sparkle (very subtle tight lobe)
-    float aCoat = 0.04; // ~roughness 0.2
+    float aCoat = 0.04;
     float Dcoat = distributionGGX(NdotH, aCoat);
     float Gcoat = geometrySmith(NdotV, NdotL, sqrt(aCoat));
     vec3 Fcoat = fresnelSchlick(VdotH, vec3(0.04));
     specular += 0.06 * (Dcoat * Gcoat * Fcoat) / max(4.0 * NdotL * NdotV, 1e-4);
 
-    // Metals have almost no diffuse term
     float kD = 0.0;
     vec3 diffuse = vec3(kD);
 
-    // AO from cavities
     cavityAO = 1.0 - rustMask * 0.6;
 
-    // Final combine (ambient + wrapped diffuse + specular)
     vec3 lit = ambient * albedo * cavityAO + diffWrap * albedo * diffuse +
                specular * NdotL;
 
-    // Small addition of brushed sheen from the original
     lit += vec3(brushed) * 0.8;
 
     color = lit;
 
   } else if (isLeather) {
-    // Leather microstructure & wear
+
     float leather = leatherGrain(uvW);
     float wear = noise(uvW * 4.0) * 0.12 - 0.06;
 
@@ -257,7 +225,6 @@ void main() {
     albedo *= 1.0 + leather - 0.08 + wear;
     albedo += vec3(leatherSheen);
 
-    // Leather: dielectric
     roughness = clamp(0.55 + leather * 0.25, 0.2, 0.95);
     float a = roughness * roughness;
 
@@ -300,7 +267,7 @@ void main() {
     color = ambient * albedo * cavityAO + diffWrap * diffuse + specular * NdotL;
 
   } else {
-    // Generic matte
+
     float detail = noise(uvW * 8.0) * 0.14;
     albedo *= 1.0 + detail - 0.07;
 
@@ -318,7 +285,6 @@ void main() {
     color = ambient * albedo + diffWrap * diffuse + specular * NdotL;
   }
 
-  // Final color clamp and alpha preserved
   color = saturate(color);
   FragColor = vec4(color, u_alpha);
 }

+ 63 - 109
assets/shaders/spearman_carthage.frag

@@ -1,9 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CARTHAGINIAN SPEARMAN - Rich Leather Armor with Battle-Worn Character
-// ============================================================================
-
 in vec3 v_worldNormal;
 in vec3 v_worldPos;
 in vec2 v_texCoord;
@@ -16,17 +12,12 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// CONSTANTS & HELPERS
-// ============================================================================
-
 const float PI = 3.14159265359;
-const vec3 LEATHER_BROWN = vec3(0.36, 0.22, 0.10); // *** fixed realistic brown
+const vec3 LEATHER_BROWN = vec3(0.36, 0.22, 0.10);
 
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
 vec3 saturate3(vec3 x) { return clamp(x, 0.0, 1.0); }
 
-// Boost saturation
 vec3 boostSaturation(vec3 color, float amount) {
   float grey = dot(color, vec3(0.299, 0.587, 0.114));
   return mix(vec3(grey), color, 1.0 + amount);
@@ -61,10 +52,6 @@ float fbm(vec2 p) {
   return v;
 }
 
-// ============================================================================
-// PBR FUNCTIONS
-// ============================================================================
-
 float D_GGX(float NdotH, float roughness) {
   float a = roughness * roughness;
   float a2 = a * a;
@@ -88,34 +75,25 @@ vec3 F_Schlick(float cosTheta, vec3 F0) {
   return F0 + (1.0 - F0) * t5;
 }
 
-// ============================================================================
-// LEATHER TEXTURE PATTERNS
-// ============================================================================
-
-// Natural leather grain - irregular pores and creases
 float leatherGrain(vec2 uv, float scale) {
   float coarse = fbm(uv * scale);
   float medium = fbm(uv * scale * 2.5 + 7.3);
   float fine = noise(uv * scale * 6.0);
-  
-  // Create pore-like depressions
+
   float pores = smoothstep(0.55, 0.65, noise(uv * scale * 4.0));
-  
+
   return coarse * 0.4 + medium * 0.35 + fine * 0.15 + pores * 0.1;
 }
 
-// Stitching pattern for leather seams
 float stitchPattern(vec2 uv, float spacing) {
   float stitch = fract(uv.y * spacing);
   stitch = smoothstep(0.4, 0.5, stitch) * smoothstep(0.6, 0.5, stitch);
-  
-  // Only show stitches along certain lines
-  float seamLine = smoothstep(0.48, 0.50, fract(uv.x * 3.0)) * 
+
+  float seamLine = smoothstep(0.48, 0.50, fract(uv.x * 3.0)) *
                    smoothstep(0.52, 0.50, fract(uv.x * 3.0));
   return stitch * seamLine;
 }
 
-// Battle wear - scratches, scuffs, worn edges
 float battleWear(vec3 pos) {
   float scratch1 = smoothstep(0.7, 0.75, noise(pos.xy * 25.0 + pos.z * 5.0));
   float scratch2 = smoothstep(0.72, 0.77, noise(pos.zy * 20.0 - 3.7));
@@ -125,108 +103,90 @@ float battleWear(vec3 pos) {
   return (scratch1 + scratch2) * 0.3 + scuff * 0.4 + edgeWear * 0.3;
 }
 
-// Oiled leather sheen pattern
 float oilSheen(vec3 pos, vec3 N, vec3 V) {
   float facing = 1.0 - abs(dot(N, V));
   float variation = fbm(pos.xz * 15.0) * 0.5 + 0.5;
   return facing * facing * variation;
 }
 
-// ============================================================================
-// MAIN
-// ============================================================================
-
 void main() {
-  // Get and enhance base color
+
   vec3 baseColor = clamp(u_color, 0.0, 1.0);
   if (u_useTexture) {
     baseColor *= texture(u_texture, v_texCoord).rgb;
   }
   baseColor = boostSaturation(baseColor, 0.25);
-  
-  bool is_skin   = (u_materialId == 0);
-  bool is_armor  = (u_materialId == 1);
+
+  bool is_skin = (u_materialId == 0);
+  bool is_armor = (u_materialId == 1);
   bool is_helmet = (u_materialId == 2);
   bool is_weapon = (u_materialId == 3);
   bool is_shield = (u_materialId == 4);
-  
+
   vec3 N = normalize(v_worldNormal);
   vec3 V = normalize(vec3(0.0, 0.0, 1.0));
   vec3 L = normalize(vec3(0.5, 1.0, 0.4));
   vec3 H = normalize(L + V);
-  
+
   vec3 albedo = baseColor;
   float metallic = 0.0;
   float roughness = 0.5;
   float sheen = 0.0;
-  
-  // ========== MATERIAL SETUP ==========
-  
+
   if (is_skin) {
-    // Warm Mediterranean skin
+
     albedo = mix(baseColor, vec3(0.88, 0.72, 0.58), 0.25);
     albedo = boostSaturation(albedo, 0.15);
     metallic = 0.0;
     roughness = 0.55;
-    
-    // Leather leg wrappings
+
     float legWrap = 1.0 - smoothstep(0.30, 0.55, v_worldPos.y);
     float wrapGrain = leatherGrain(v_worldPos.xz, 18.0);
     vec3 wrapColor = baseColor * 0.7;
     wrapColor = boostSaturation(wrapColor, 0.2);
     wrapColor -= vec3(0.06) * wrapGrain;
-    
+
     float bands = sin(v_worldPos.y * 25.0) * 0.5 + 0.5;
     bands = smoothstep(0.3, 0.7, bands);
     wrapColor *= 0.9 + bands * 0.15;
-    
+
     albedo = mix(albedo, wrapColor, legWrap);
     roughness = mix(roughness, 0.48, legWrap);
-    
+
   } else if (is_armor || is_helmet) {
-    // ====== RICH LEATHER ARMOR ======
-    
-    // *** Use UVs so grain is visible and tiles nicely
-    vec2 leatherUV = v_texCoord * 6.0; 
-    
-    float grain     = leatherGrain(leatherUV, 8.0);          // *** UV-based grain
-    float stitches  = stitchPattern(leatherUV, 18.0);        // *** UV-based seams
-    float wear      = battleWear(v_worldPos);
-    float oil       = oilSheen(v_worldPos, N, V);
-    
-    // *** Force a realistic brown leather base, only slightly tinted by u_color
+
+    vec2 leatherUV = v_texCoord * 6.0;
+
+    float grain = leatherGrain(leatherUV, 8.0);
+    float stitches = stitchPattern(leatherUV, 18.0);
+    float wear = battleWear(v_worldPos);
+    float oil = oilSheen(v_worldPos, N, V);
+
     vec3 tint = mix(vec3(1.0), baseColor, 0.25);
-    albedo = LEATHER_BROWN * tint;                           // *** core leather color
+    albedo = LEATHER_BROWN * tint;
     albedo = boostSaturation(albedo, 0.25);
-    
-    // *** Stronger grain contrast
+
     float grainStrength = 0.55;
     albedo = mix(albedo * 0.7, albedo * 1.3, grain * grainStrength);
-    
-    // Darker in creases, lighter on raised areas
-    float depth = fbm(leatherUV * 3.0);                      // *** use UV for consistency
+
+    float depth = fbm(leatherUV * 3.0);
     albedo = mix(albedo * 0.7, albedo * 1.15, depth);
-    
-    // Worn areas are lighter/more faded + slightly desaturated
+
     vec3 wornColor = albedo * 1.25 + vec3(0.07, 0.05, 0.03);
-    wornColor = mix(wornColor, vec3(dot(wornColor, vec3(0.3,0.59,0.11))), 0.15);
+    wornColor =
+        mix(wornColor, vec3(dot(wornColor, vec3(0.3, 0.59, 0.11))), 0.15);
     albedo = mix(albedo, wornColor, wear * 0.65);
-    
-    // Stitching detail (darker seams)
+
     albedo = mix(albedo, albedo * 0.55, stitches * 0.9);
-    
-    // Leather is not metallic but quite rough
+
     metallic = 0.0;
-    
-    // *** Full leather roughness, only slightly reduced where oiled
+
     float baseRoughness = 0.85;
     roughness = baseRoughness - oil * 0.25 + grain * 0.05;
-    roughness = clamp(roughness, 0.65, 0.9);                 // *** always quite rough
-    
-    // Sheen mainly from oil
+    roughness = clamp(roughness, 0.65, 0.9);
+
     sheen = oil * 0.6;
-    
-    // Bronze studs/reinforcements on helmet
+
     if (is_helmet) {
       float studs = smoothstep(0.75, 0.8, noise(v_worldPos.xz * 12.0));
       vec3 bronzeColor = vec3(0.85, 0.65, 0.35);
@@ -234,83 +194,80 @@ void main() {
       metallic = mix(metallic, 0.9, studs);
       roughness = mix(roughness, 0.25, studs);
     }
-    
+
   } else if (is_weapon) {
-    // Spear with wooden shaft and iron tip
+
     float h = v_worldPos.y;
     float tip = smoothstep(0.40, 0.55, h);
     float binding = smoothstep(0.35, 0.42, h) * (1.0 - tip);
-    
+
     vec3 woodColor = boostSaturation(baseColor * 0.85, 0.3);
     float woodGrain = fbm(vec2(v_worldPos.x * 8.0, v_worldPos.y * 35.0));
     woodColor *= 0.85 + woodGrain * 0.3;
     float woodSheen = pow(max(dot(reflect(-V, N), L), 0.0), 16.0);
-    
+
     vec3 bindColor = baseColor * 0.6;
     bindColor = boostSaturation(bindColor, 0.2);
     float bindGrain = leatherGrain(v_worldPos.xy, 25.0);
     bindColor *= 0.9 + bindGrain * 0.2;
-    
+
     vec3 ironColor = vec3(0.55, 0.55, 0.58);
     float ironBrush = fbm(v_worldPos.xy * 40.0);
     ironColor += vec3(0.08) * ironBrush;
     ironColor = mix(ironColor, baseColor * 0.3 + vec3(0.4), 0.15);
-    
+
     albedo = woodColor;
     albedo = mix(albedo, bindColor, binding);
     albedo = mix(albedo, ironColor, tip);
-    
+
     metallic = mix(0.0, 0.85, tip);
     roughness = mix(0.38, 0.28, tip);
     roughness = mix(roughness, 0.5, binding);
     sheen = woodSheen * (1.0 - tip) * (1.0 - binding) * 0.3;
-    
+
   } else if (is_shield) {
-    // Leather-covered wooden shield with bronze boss
+
     float dist = length(v_worldPos.xz);
     float boss = smoothstep(0.18, 0.0, dist);
     float bossRim = smoothstep(0.22, 0.18, dist) * (1.0 - boss);
-    
+
     float shieldGrain = leatherGrain(v_worldPos.xz, 10.0);
     float shieldWear = battleWear(v_worldPos);
-    
+
     albedo = boostSaturation(baseColor, 0.3);
     albedo *= 0.9 + shieldGrain * 0.2;
     albedo = mix(albedo, albedo * 1.2, shieldWear * 0.3);
-    
+
     vec3 bronzeColor = vec3(0.88, 0.68, 0.38);
     bronzeColor = boostSaturation(bronzeColor, 0.25);
     albedo = mix(albedo, bronzeColor, boss + bossRim * 0.8);
-    
+
     metallic = mix(0.0, 0.9, boss + bossRim * 0.7);
     roughness = mix(0.45, 0.22, boss);
-    
+
   } else {
     albedo = boostSaturation(baseColor, 0.2);
     metallic = 0.0;
     roughness = 0.55;
   }
-  
-  // ========== PBR LIGHTING ==========
-  
+
   float NdotL = max(dot(N, L), 0.0);
   float NdotV = max(dot(N, V), 0.001);
   float NdotH = max(dot(N, H), 0.0);
   float VdotH = max(dot(V, H), 0.0);
-  
+
   vec3 F0 = mix(vec3(0.04), albedo, metallic);
-  
+
   float D = D_GGX(NdotH, max(roughness, 0.01));
   float G = G_Smith(NdotV, NdotL, roughness);
   vec3 F = F_Schlick(VdotH, F0);
   vec3 specular = (D * G * F) / (4.0 * NdotV * NdotL + 0.001);
-  
+
   vec3 kD = (vec3(1.0) - F) * (1.0 - metallic);
   vec3 diffuse = kD * albedo / PI;
-  
+
   vec3 color = (diffuse + specular * 1.8) * NdotL * 2.0;
-  
-  // ====== LEATHER SHEEN EFFECT ======
+
   if (sheen > 0.01) {
     vec3 R = reflect(-V, N);
     float sheenSpec = pow(max(dot(R, L), 0.0), 12.0);
@@ -318,8 +275,7 @@ void main() {
     float edgeSheen = pow(1.0 - NdotV, 3.0);
     color += vec3(0.95, 0.90, 0.80) * edgeSheen * sheen * 0.4;
   }
-  
-  // ====== METALLIC SHINE (for bronze parts) ======
+
   if (metallic > 0.5) {
     vec3 R = reflect(-V, N);
     float specPower = 128.0 / (roughness * roughness + 0.001);
@@ -329,19 +285,17 @@ void main() {
     float hotspot = pow(NdotH, 256.0);
     color += vec3(1.0) * hotspot * (1.0 - roughness);
   }
-  
-  // ====== WARM AMBIENT ======
+
   vec3 ambient = albedo * 0.38;
   float sss = pow(saturate(dot(-N, L)), 2.0) * 0.15;
   ambient += albedo * vec3(1.1, 0.9, 0.7) * sss;
   float rim = pow(1.0 - NdotV, 3.5);
   ambient += vec3(0.35, 0.32, 0.28) * rim * 0.25;
-  
+
   color += ambient;
-  
-  // Tone mapping
+
   color = color / (color + vec3(0.55));
   color = pow(color, vec3(0.94));
-  
+
   FragColor = vec4(saturate3(color), u_alpha);
 }

+ 5 - 13
assets/shaders/spearman_carthage.vert

@@ -34,7 +34,6 @@ void main() {
   vec3 position = a_position;
   vec3 normal = a_normal;
 
-  // Slight curvature for large shields (materialId = 4)
   if (u_materialId == 4) {
     float curveRadius = 0.52;
     float curveAmount = 0.46;
@@ -51,7 +50,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -67,14 +65,11 @@ void main() {
   float height01 =
       clamp(dot(worldPos - modelOrigin, axisDir) / axisLen + 0.5, 0.0, 1.0);
 
-  // Only deform armored pieces (helmet/armor/shield/weapons) and only within
-  // their height bands to avoid touching skin/face
   bool torsoZone = (height01 > 0.18 && height01 <= 0.98);
-  bool helmetZone = (height01 > 0.70); // allow small overlap for helmet rim
+  bool helmetZone = (height01 > 0.70);
   bool deformArmor =
-      (u_materialId == 4 || u_materialId == 3 || // shields, weapons
-       (u_materialId == 1 && torsoZone) ||       // chainmail
-       (u_materialId == 2 && helmetZone));       // helmet
+      (u_materialId == 4 || u_materialId == 3 ||
+       (u_materialId == 1 && torsoZone) || (u_materialId == 2 && helmetZone));
 
   float dentSeed = 0.0;
   float hammerImpact = 0.0;
@@ -82,7 +77,7 @@ void main() {
   vec3 offsetPos = worldPos;
 
   if (deformArmor) {
-    // Subtle battered offset to avoid self-shadowing
+
     dentSeed = hash13(worldPos * 0.82 + worldNormal * 0.28);
     hammerImpact = sin(worldPos.y * 15.0 + dentSeed * 18.84);
     vec3 dentOffset = worldNormal * ((dentSeed - 0.5) * 0.0095);
@@ -106,12 +101,10 @@ void main() {
 
   float height = height01;
 
-  // Keep armor material active across the whole piece; avoid partial bands.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   v_bodyHeight = clamp((height - 0.05) / 0.90, 0.0, 1.0);
 
-  // Helmet detail bands and rivets
   float reinforcementBands = fract(height * 12.0);
   float browBandRegion =
       smoothstep(0.78, 0.82, height) * smoothstep(0.90, 0.86, height);
@@ -120,8 +113,7 @@ void main() {
   v_helmetDetail =
       reinforcementBands * 0.4 + browBandRegion * 0.4 + cheekGuardArea * 0.2;
 
-  // Wear masks
-  v_steelWear = dentSeed * (1.0 - v_bodyHeight * 0.3); // More wear lower down
+  v_steelWear = dentSeed * (1.0 - v_bodyHeight * 0.3);
   v_chainmailPhase =
       fract(offsetPos.x * 28.0 + offsetPos.z * 28.0 + offsetPos.y * 0.45);
   v_rivetPattern = step(0.96, fract(offsetPos.x * 22.0)) *

+ 45 - 108
assets/shaders/spearman_roman_republic.frag

@@ -1,9 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// INPUTS & OUTPUTS
-// ============================================================================
-
 in vec3 v_normal;
 in vec3 v_worldNormal;
 in vec3 v_tangent;
@@ -26,10 +22,6 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// UTILITY FUNCTIONS
-// ============================================================================
-
 float hash(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * 0.1031);
   p3 += dot(p3, p3.yzx + 33.33);
@@ -47,10 +39,6 @@ float noise(vec2 p) {
   return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
 }
 
-// ============================================================================
-// MATERIAL PATTERN FUNCTIONS (Roman Spearman - Hastatus)
-// ============================================================================
-
 float chainmail_rings(vec2 p) {
   vec2 grid = fract(p * 32.0) - 0.5;
   float ring = length(grid);
@@ -72,10 +60,6 @@ float pteruges_strips(vec2 p, float y) {
   return strip * leather_tex * hang;
 }
 
-// ============================================================================
-// MAIN FRAGMENT SHADER
-// ============================================================================
-
 void main() {
   vec3 color = u_color;
   if (u_useTexture) {
@@ -85,7 +69,6 @@ void main() {
   vec3 normal = normalize(v_normal);
   vec2 uv = v_worldPos.xz * 4.5;
 
-  // Material ID: 0=body/skin, 1=armor, 2=helmet, 3=weapon, 4=shield, 5=greaves
   bool is_skin = (u_materialId == 0);
   bool is_armor = (u_materialId == 1);
   bool is_helmet = (u_materialId == 2);
@@ -93,9 +76,6 @@ void main() {
   bool is_shield = (u_materialId == 4);
   bool is_greaves = (u_materialId == 5);
 
-  // === ROMAN SPEARMAN (HASTATUS) MATERIALS ===
-
-  // HEAVY STEEL HELMET (cool blue-grey steel)
   if (is_skin) {
     float skin_detail = noise(uv * 18.0) * 0.06;
     float subdermal = noise(uv * 6.0) * 0.05;
@@ -106,17 +86,14 @@ void main() {
     color += vec3(0.025, 0.015, 0.010) * subdermal;
     color += vec3(rim);
   } else if (is_helmet) {
-    // Steel wear patterns from vertex shader
+
     float brushed = abs(sin(v_worldPos.y * 95.0)) * 0.020;
     float dents = noise(uv * 6.5) * 0.032 * v_steelWear;
     float rust_tex = noise(uv * 9.0) * 0.11 * v_steelWear;
 
-    // Use vertex-computed helmet detail (reinforcement bands, brow band, cheek
-    // guards)
     float bands = v_helmetDetail * 0.12;
     float rivets = v_rivetPattern * 0.10;
 
-    // ENHANCED: Cheek guards (hinged steel plates with hinge pins)
     float cheek_guard_height = smoothstep(0.70, 0.80, v_bodyHeight) *
                                smoothstep(0.90, 0.80, v_bodyHeight);
     float cheek_x = abs(v_worldPos.x);
@@ -124,16 +101,13 @@ void main() {
         cheek_guard_height * smoothstep(0.12, 0.09, cheek_x) * 0.40;
     float hinge_pins =
         cheek_guard * step(0.94, fract(v_bodyHeight * 28.0)) * 0.20;
-    float guard_repousse =
-        cheek_guard * noise(uv * 12.0) * 0.08; // Decorative embossing
+    float guard_repousse = cheek_guard * noise(uv * 12.0) * 0.08;
 
-    // ENHANCED: Brow reinforcement (frontal impact protection)
     float brow_height = smoothstep(0.84, 0.88, v_bodyHeight) *
                         smoothstep(0.92, 0.88, v_bodyHeight);
     float front_facing = step(0.3, v_worldNormal.z);
     float brow_reinforce = brow_height * front_facing * 0.22;
 
-    // ENHANCED: Plume socket (central crest mount)
     float plume_socket_height = smoothstep(0.90, 0.94, v_bodyHeight);
     float plume_socket =
         plume_socket_height * smoothstep(0.05, 0.03, abs(v_worldPos.x)) * 0.28;
@@ -149,60 +123,51 @@ void main() {
     color -= vec3(rust_tex * 0.3);
     color += vec3(brushed * 0.6);
   }
-  // LIGHT CHAINMAIL ARMOR (lorica hamata - pectorale reinforcement optional)
+
   else if (is_armor) {
-    // FORCE grey metal base - chainmail is CLEARLY not skin
-    color = color * vec3(0.42, 0.46, 0.50); // Darker grey base
 
-    vec2 chain_uv = v_worldPos.xz * 22.0; // Larger rings
+    color = color * vec3(0.42, 0.46, 0.50);
+
+    vec2 chain_uv = v_worldPos.xz * 22.0;
 
-    // PECTORALE CHEST PLATE - highly visible steel plate overlay
-    float chest_plate =
-        smoothstep(1.15, 1.25, v_bodyHeight) *
-        smoothstep(1.55, 1.45, v_bodyHeight) *
-        smoothstep(0.25, 0.15, abs(v_worldPos.x)); // Center chest
+    float chest_plate = smoothstep(1.15, 1.25, v_bodyHeight) *
+                        smoothstep(1.55, 1.45, v_bodyHeight) *
+                        smoothstep(0.25, 0.15, abs(v_worldPos.x));
 
-    // STRONG distinction between plate and chainmail
     if (chest_plate > 0.3) {
-      // PECTORALE - polished steel plate
-      color = vec3(0.72, 0.76, 0.82); // Bright steel
 
-      // Plate edges and rivets
+      color = vec3(0.72, 0.76, 0.82);
+
       float plate_edge = smoothstep(0.88, 0.92, chest_plate) * 0.25;
       float rivets = step(0.92, fract(v_worldPos.x * 18.0)) *
                      step(0.92, fract(v_worldPos.y * 12.0)) * 0.30;
 
       vec3 V = normalize(vec3(0.0, 1.0, 0.5));
       float view_angle = max(dot(normalize(v_worldNormal), V), 0.0);
-      float plate_sheen = pow(view_angle, 8.0) * 0.75; // Strong reflection
+      float plate_sheen = pow(view_angle, 8.0) * 0.75;
 
       color += vec3(plate_sheen + rivets);
       color -= vec3(plate_edge);
     } else {
-      // CHAINMAIL - much more visible rings with enhanced realism
+
       float rings = chainmail_rings(chain_uv) * 2.2;
       float ring_gaps = (1.0 - chainmail_rings(chain_uv)) * 0.50;
 
-      // ENHANCED: Ring quality variation and micro-gaps
       float ring_quality = noise(chain_uv * 3.5) * 0.18;
       float wire_thickness = noise(chain_uv * 28.0) * 0.12;
       float ring_joints = step(0.88, fract(chain_uv.x * 0.5)) *
                           step(0.88, fract(chain_uv.y * 0.5)) * 0.35;
       float gap_shadow = ring_joints * smoothstep(0.4, 0.6, rings);
 
-      // ENHANCED: Oxidation gradients spreading from contact points
       float rust_seed = noise(chain_uv * 4.2);
       float oxidation = smoothstep(0.45, 0.75, rust_seed) * v_steelWear * 0.28;
       vec3 rust_color = vec3(0.38, 0.28, 0.22);
       vec3 dark_rust = vec3(0.28, 0.20, 0.16);
 
-      // ENHANCED: Stress points at armor articulation zones
-      float joint_stress = smoothstep(1.20, 1.30, v_bodyHeight) * // Shoulders
-                           smoothstep(1.00, 1.10, v_bodyHeight) * // Elbows
-                           0.25;
+      float joint_stress = smoothstep(1.20, 1.30, v_bodyHeight) *
+                           smoothstep(1.00, 1.10, v_bodyHeight) * 0.25;
       float stress_wear = joint_stress * noise(chain_uv * 8.0) * 0.15;
 
-      // Battle damage
       float damage = step(0.86, noise(chain_uv * 0.8)) * 0.40;
 
       vec3 V = normalize(vec3(0.0, 1.0, 0.5));
@@ -216,40 +181,33 @@ void main() {
       color -= vec3(ring_gaps + damage + gap_shadow + stress_wear);
       color *= 1.0 + ring_quality - 0.05 + wire_thickness;
 
-      // Multi-stage oxidation with rust progression
       color = mix(color, rust_color, oxidation * 0.40);
       color = mix(color, dark_rust, oxidation * oxidation * 0.15);
     }
 
-    // Ensure armor is ALWAYS clearly visible
     color = clamp(color, vec3(0.32), vec3(0.88));
   }
-  // SCUTUM SHIELD (curved laminated wood with metal boss)
+
   else if (is_shield) {
-    // Shield boss (domed metal center)
+
     float boss_dist = length(v_worldPos.xy);
     float boss = smoothstep(0.12, 0.08, boss_dist) * 0.6;
     float boss_rim = smoothstep(0.14, 0.12, boss_dist) *
                      smoothstep(0.10, 0.12, boss_dist) * 0.3;
 
-    // Wood construction layers
     float edge_dist = max(abs(v_worldPos.x), abs(v_worldPos.y));
     float edge_wear = smoothstep(0.42, 0.48, edge_dist);
     float wood_layers = edge_wear * noise(uv * 40.0) * 0.25;
 
-    // Canvas facing
     float fabric_grain = noise(uv * 25.0) * 0.08;
     float canvas_weave = sin(uv.x * 60.0) * sin(uv.y * 58.0) * 0.04;
 
-    // Bronze edging
     float metal_edge = smoothstep(0.46, 0.48, edge_dist) * 0.45;
     vec3 bronze_edge = vec3(0.75, 0.55, 0.30);
 
-    // Battle wear
     float dents = noise(uv * 8.0) * v_steelWear * 0.18;
     float cuts = step(0.90, noise(uv * 12.0)) * 0.28;
 
-    // Boss reflection
     vec3 V = normalize(vec3(0.0, 1.0, 0.5));
     float view_angle = max(dot(normalize(v_worldNormal), V), 0.0);
     float boss_sheen = pow(view_angle, 10.0) * boss * 0.55;
@@ -260,25 +218,22 @@ void main() {
     color = mix(color, bronze_edge, metal_edge);
     color -= vec3(dents * 0.08 + cuts * 0.12);
   }
-  // PILUM & GLADIUS (iron spear and steel sword)
+
   else if (is_weapon) {
-    // Determine weapon part by height
-    bool is_blade = (v_bodyHeight > 0.5); // Upper half is blade/spearhead
-    bool is_shaft = (v_bodyHeight > 0.2 && v_bodyHeight <= 0.5); // Mid section
-    bool is_grip = (v_bodyHeight <= 0.2); // Lower section
+
+    bool is_blade = (v_bodyHeight > 0.5);
+    bool is_shaft = (v_bodyHeight > 0.2 && v_bodyHeight <= 0.5);
+    bool is_grip = (v_bodyHeight <= 0.2);
 
     if (is_blade) {
-      // Iron spearhead or steel blade
+
       vec3 steel_color = vec3(0.72, 0.75, 0.80);
 
-      // Fuller groove (blood channel)
       float fuller_x = abs(v_worldPos.x);
       float fuller = smoothstep(0.03, 0.01, fuller_x) * 0.20;
 
-      // Edge sharpness (bright along cutting edge)
       float edge = smoothstep(0.09, 0.10, fuller_x) * 0.45;
 
-      // Forging marks and tempering
       float forge_marks = noise(uv * 45.0) * 0.10;
       float tempering = abs(sin(v_bodyHeight * 35.0)) * 0.08;
 
@@ -291,106 +246,88 @@ void main() {
       color += vec3(edge + metal_sheen);
       color += vec3(forge_marks * 0.5 + tempering * 0.3);
     } else if (is_shaft) {
-      // Ash wood shaft
+
       float wood_grain = noise(uv * 32.0) * 0.16;
       float rings = abs(sin(v_worldPos.y * 40.0)) * 0.08;
-      color *= vec3(0.68, 0.58, 0.45); // Ash wood color
+      color *= vec3(0.68, 0.58, 0.45);
       color *= 1.0 + wood_grain + rings;
     } else if (is_grip) {
-      // Leather-wrapped grip
+
       float leather_wrap = noise(uv * 22.0) * 0.14;
       float binding = step(0.90, fract(v_worldPos.y * 25.0)) * 0.18;
-      color *= vec3(0.48, 0.38, 0.28); // Dark leather
+      color *= vec3(0.48, 0.38, 0.28);
       color *= 1.0 + leather_wrap;
       color += vec3(binding);
     }
   }
-  // BRONZE GREAVES (shin guards - polished bronze with high shine)
+
   else if (is_greaves) {
-    // Rich polished bronze base (warm golden metallic)
+
     vec3 bronze_base = vec3(0.88, 0.72, 0.45);
     vec3 bronze_highlight = vec3(1.0, 0.92, 0.75);
     vec3 bronze_shadow = vec3(0.55, 0.42, 0.25);
-    
-    // Apply bronze base
+
     color = mix(color, bronze_base, 0.90);
-    
-    // Micro brushed metal texture (fine polish lines)
+
     float brushed = abs(sin(v_worldPos.y * 120.0)) * 0.015;
-    
-    // View direction for specular
+
     vec3 V = normalize(vec3(0.15, 0.85, 0.5));
     vec3 N = normalize(v_worldNormal);
     float NdotV = max(dot(N, V), 0.0);
-    
-    // Primary specular (sharp highlight - polished metal)
+
     float spec_primary = pow(NdotV, 32.0) * 1.2;
-    
-    // Secondary specular (broader shine)
+
     float spec_secondary = pow(NdotV, 8.0) * 0.45;
-    
-    // Metallic fresnel (bright edges)
+
     float fresnel = pow(1.0 - NdotV, 3.0) * 0.65;
-    
-    // Anisotropic highlight (stretched along greave height)
+
     float aniso = pow(abs(sin(v_worldPos.y * 40.0 + NdotV * 3.14)), 4.0) * 0.25;
-    
-    // Environment reflection (sky above, ground below)
+
     float env_up = max(N.y, 0.0) * 0.35;
     float env_side = (1.0 - abs(N.y)) * 0.20;
-    
-    // Combine all shine effects
+
     vec3 shine = bronze_highlight * (spec_primary + spec_secondary + aniso);
     shine += vec3(fresnel * 0.8, fresnel * 0.7, fresnel * 0.5);
     shine += bronze_base * (env_up + env_side);
-    
+
     color += shine;
     color += vec3(brushed);
-    
-    // Ensure bright metallic finish
+
     color = clamp(color, bronze_shadow, vec3(1.0));
   }
 
   color = clamp(color, 0.0, 1.0);
 
-  // === PHASE 4: ADVANCED FEATURES ===
-  // Environmental interactions and enhanced battle wear
-
-  // Campaign wear (medium infantry shows moderate weathering)
   float campaign_wear = (1.0 - v_bodyHeight * 0.5) * 0.18;
   float dust_accumulation = noise(v_worldPos.xz * 8.0) * campaign_wear * 0.14;
 
-  // Rain streaks (vertical weathering on steel armor)
   float rain_streaks =
       smoothstep(0.85, 0.92,
                  noise(v_worldPos.xz * 2.5 + vec2(0.0, v_worldPos.y * 8.0))) *
-      v_bodyHeight * 0.10; // Visible on pectorale
+      v_bodyHeight * 0.10;
 
-  // Mud splatter (frontline combat conditions)
   float mud_height = smoothstep(0.55, 0.15, v_bodyHeight);
   float mud_pattern =
       step(0.72, noise(v_worldPos.xz * 12.0 + v_worldPos.y * 3.0));
   float mud_splatter = mud_height * mud_pattern * 0.22;
   vec3 mud_color = vec3(0.22, 0.18, 0.14);
 
-  // Blood stains (combat evidence on armor/shield)
-  float blood_height = smoothstep(0.85, 0.60, v_bodyHeight); // Torso/arms
+  float blood_height = smoothstep(0.85, 0.60, v_bodyHeight);
   float blood_pattern =
       step(0.88, noise(v_worldPos.xz * 15.0 + v_bodyHeight * 5.0));
   float blood_stain = blood_height * blood_pattern * v_steelWear * 0.12;
-  vec3 blood_color = vec3(0.18, 0.08, 0.06); // Dried blood (dark brown)
+  vec3 blood_color = vec3(0.18, 0.08, 0.06);
 
-  // Apply environmental effects
   color -= vec3(dust_accumulation);
   color -= vec3(rain_streaks * 0.6);
   color = mix(color, mud_color, mud_splatter);
   color = mix(color, blood_color, blood_stain);
 
-  // Lighting per material
   vec3 light_dir = normalize(vec3(1.0, 1.15, 1.0));
   float n_dot_l = dot(normalize(v_worldNormal), light_dir);
 
-  float wrap_amount = is_helmet ? 0.12 : (is_armor ? 0.22 : (is_greaves ? 0.12 : 0.35));
+  float wrap_amount =
+      is_helmet ? 0.12 : (is_armor ? 0.22 : (is_greaves ? 0.12 : 0.35));
   float diff = max(n_dot_l * (1.0 - wrap_amount) + wrap_amount, 0.20);
 
   if (is_helmet || is_greaves) {

+ 6 - 15
assets/shaders/spearman_roman_republic.vert

@@ -34,7 +34,6 @@ void main() {
   vec3 position = a_position;
   vec3 normal = a_normal;
 
-  // Shield curving: bend flat rectangle into scutum curve (materialId=4)
   if (u_materialId == 4) {
     float curveRadius = 0.55;
     float curveAmount = 0.45;
@@ -51,7 +50,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space for normal mapping
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -61,9 +59,9 @@ void main() {
   vec4 modelPos = u_model * vec4(position, 1.0);
   vec3 worldPos = modelPos.xyz;
 
-  // Restrict deformation to hard surfaces (armor, helmet, shield, weapons, greaves)
-  bool deformArmor = (u_materialId == 1 || u_materialId == 2 ||
-                      u_materialId == 4 || u_materialId == 3 || u_materialId == 5);
+  bool deformArmor =
+      (u_materialId == 1 || u_materialId == 2 || u_materialId == 4 ||
+       u_materialId == 3 || u_materialId == 5);
 
   float dentSeed = 0.0;
   float hammerImpact = 0.0;
@@ -71,7 +69,7 @@ void main() {
   vec3 offsetPos = worldPos;
 
   if (deformArmor) {
-    // Heavy steel battle damage simulation (skip skin/cloth)
+
     dentSeed = hash13(worldPos * 0.82 + worldNormal * 0.28);
     hammerImpact = sin(worldPos.y * 16.5 + dentSeed * 18.84);
     vec3 dentOffset = worldNormal * ((dentSeed - 0.5) * 0.0115);
@@ -94,16 +92,14 @@ void main() {
   v_bitangent = b;
 
   float height = offsetPos.y;
-  // Keep armor/material selection stable: 1.0 only for armor material.
+
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
-  // Body height normalization
   float torsoMin = 0.55;
   float torsoMax = 1.68;
   v_bodyHeight =
       clamp((offsetPos.y - torsoMin) / (torsoMax - torsoMin), 0.0, 1.0);
 
-  // Heavy helmet detail attributes
   float reinforcementBands = fract(height * 14.0);
   float browBandRegion =
       smoothstep(1.48, 1.52, height) * smoothstep(1.56, 1.52, height);
@@ -112,19 +108,14 @@ void main() {
   v_helmetDetail =
       reinforcementBands * 0.4 + browBandRegion * 0.4 + cheekGuardArea * 0.2;
 
-  // Steel wear patterns (rust, scratches)
-  v_steelWear =
-      dentSeed * (1.0 - v_bodyHeight * 0.3); // More wear on lower parts
+  v_steelWear = dentSeed * (1.0 - v_bodyHeight * 0.3);
 
-  // Chainmail ring phase for light armor
   v_chainmailPhase =
       fract(offsetPos.x * 32.0 + offsetPos.z * 32.0 + offsetPos.y * 0.5);
 
-  // Rivet pattern for helmet
   v_rivetPattern = step(0.96, fract(offsetPos.x * 22.0)) *
                    step(0.94, fract(offsetPos.z * 18.0));
 
-  // Leather wear
   v_leatherWear =
       hash13(offsetPos * 0.45 + worldNormal * 1.8) * (0.6 + v_bodyHeight * 0.4);
 }

+ 0 - 2
assets/shaders/stone_instanced.frag

@@ -12,10 +12,8 @@ void main() {
   vec3 normal = normalize(vNormal);
   vec3 lightDir = normalize(uLightDirection);
 
-  // Diffuse lighting
   float diffuse = max(dot(normal, lightDir), 0.0);
 
-  // Ambient + diffuse
   float ambient = 0.4;
   float lighting = ambient + diffuse * 0.6;
 

+ 2 - 4
assets/shaders/stone_instanced.vert

@@ -2,8 +2,8 @@
 
 layout(location = 0) in vec3 aPos;
 layout(location = 1) in vec3 aNormal;
-layout(location = 2) in vec4 aPosScale; // instance: xyz=world pos, w=scale
-layout(location = 3) in vec4 aColorRot; // instance: rgb=color, a=rotation
+layout(location = 2) in vec4 aPosScale;
+layout(location = 3) in vec4 aColorRot;
 
 uniform mat4 uViewProj;
 
@@ -16,7 +16,6 @@ void main() {
   vec3 worldPos = aPosScale.xyz;
   float rotation = aColorRot.a;
 
-  // Rotate vertex around Y-axis
   float cosR = cos(rotation);
   float sinR = sin(rotation);
   mat2 rot = mat2(cosR, -sinR, sinR, cosR);
@@ -27,7 +26,6 @@ void main() {
 
   vWorldPos = localPos + worldPos;
 
-  // Rotate normal
   vec2 rotatedNormalXZ = rot * aNormal.xz;
   vNormal = normalize(vec3(rotatedNormalXZ.x, aNormal.y, rotatedNormalXZ.y));
 

+ 16 - 37
assets/shaders/swordsman.frag

@@ -28,28 +28,24 @@ float noise(vec2 p) {
   return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
 }
 
-// Medieval plate armor articulation lines
 float armorPlates(vec2 p, float y) {
-  // Horizontal articulation lines (overlapping plates)
+
   float plateY = fract(y * 6.5);
   float plateLine = smoothstep(0.90, 0.98, plateY) * 0.12;
 
-  // Brass rivet decorations
   float rivetX = fract(p.x * 18.0);
   float rivet = smoothstep(0.48, 0.50, rivetX) * smoothstep(0.52, 0.50, rivetX);
-  float rivetPattern = rivet * step(0.92, plateY) * 0.25; // Brass is brighter
+  float rivetPattern = rivet * step(0.92, plateY) * 0.25;
 
   return plateLine + rivetPattern;
 }
 
-// Chainmail texture pattern
 float chainmailRings(vec2 p) {
   vec2 grid = fract(p * 35.0) - 0.5;
   float ring = length(grid);
   float ringPattern =
       smoothstep(0.35, 0.30, ring) - smoothstep(0.25, 0.20, ring);
 
-  // Offset every other row for interlinked appearance
   vec2 offsetGrid = fract(p * 35.0 + vec2(0.5, 0.0)) - 0.5;
   float offsetRing = length(offsetGrid);
   float offsetPattern =
@@ -68,33 +64,24 @@ void main() {
   vec2 uv = v_worldPos.xz * 5.0;
   float avgColor = (color.r + color.g + color.b) / 3.0;
 
-  // Detect material type by color tone
   float colorHue =
       max(max(color.r, color.g), color.b) - min(min(color.r, color.g), color.b);
   bool isBrass =
       (color.r > color.g * 1.15 && color.r > color.b * 1.2 && avgColor > 0.55);
 
-  // === MEDIEVAL KNIGHT MATERIALS ===
-
-  // POLISHED STEEL PLATE (Great Helm, cuirass, pauldrons, rerebraces) - bright
-  // silvery
   if (avgColor > 0.60 && !isBrass) {
-    // Mirror-polished steel finish
+
     float brushedMetal = abs(sin(v_worldPos.y * 95.0)) * 0.02;
 
-    // Battle wear: scratches and dents
     float scratches = noise(uv * 35.0) * 0.018;
     float dents = noise(uv * 8.0) * 0.025;
 
-    // Plate articulation lines and rivets
     float plates = armorPlates(v_worldPos.xz, v_worldPos.y);
 
-    // Strong specular reflections (polished metal)
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
-    float fresnel = pow(1.0 - viewAngle, 1.8) * 0.35; // Bright rim lighting
-    float specular = pow(viewAngle, 12.0) * 0.55;     // Sharp mirror highlights
+    float fresnel = pow(1.0 - viewAngle, 1.8) * 0.35;
+    float specular = pow(viewAngle, 12.0) * 0.55;
 
-    // Environmental reflections (sky dome)
     float skyReflection = (normal.y * 0.5 + 0.5) * 0.12;
 
     color += vec3(fresnel + skyReflection + specular * 1.8);
@@ -102,11 +89,11 @@ void main() {
     color += vec3(brushedMetal);
     color -= vec3(scratches + dents * 0.4);
   }
-  // BRASS ACCENTS (rivets, buckles, crosses, decorations) - golden
+
   else if (isBrass) {
-    // Warm metallic brass
+
     float brassNoise = noise(uv * 22.0) * 0.025;
-    float patina = noise(uv * 6.0) * 0.08; // Age darkening
+    float patina = noise(uv * 6.0) * 0.08;
 
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float brassSheen = pow(viewAngle, 8.0) * 0.35;
@@ -114,42 +101,37 @@ void main() {
 
     color += vec3(brassSheen + brassFresnel);
     color += vec3(brassNoise);
-    color -= vec3(patina * 0.5); // Darker in recesses
+    color -= vec3(patina * 0.5);
   }
-  // CHAINMAIL AVENTAIL (hanging neck protection) - grey steel rings
+
   else if (avgColor > 0.40 && avgColor <= 0.60) {
-    // Interlocked ring texture
+
     float rings = chainmailRings(v_worldPos.xz);
 
-    // Chainmail has less shine than plate
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float chainSheen = pow(viewAngle, 6.0) * 0.18;
 
-    // Individual ring highlights
     float ringHighlights = noise(uv * 30.0) * 0.12;
 
     color += vec3(rings + chainSheen + ringHighlights);
-    color *= 1.0 - noise(uv * 12.0) * 0.08; // Slight darkening between rings
+    color *= 1.0 - noise(uv * 12.0) * 0.08;
   }
-  // HERALDIC SURCOAT (team-colored tabard over armor) - bright cloth
+
   else if (avgColor > 0.25) {
-    // Rich fabric weave texture
+
     float weaveX = sin(v_worldPos.x * 70.0);
     float weaveZ = sin(v_worldPos.z * 70.0);
     float weave = weaveX * weaveZ * 0.04;
 
-    // Embroidered cross emblem texture
     float embroidery = noise(uv * 12.0) * 0.06;
 
-    // Fabric has soft sheen
     float viewAngle = abs(dot(normal, normalize(vec3(0.0, 1.0, 0.5))));
     float fabricSheen = pow(1.0 - viewAngle, 6.0) * 0.08;
 
-    // Heraldic colors are vibrant
     color *= 1.0 + noise(uv * 5.0) * 0.10 - 0.05;
     color += vec3(weave + embroidery + fabricSheen);
   }
-  // LEATHER/DARK ELEMENTS (straps, gloves, scabbard) - dark brown
+
   else {
     float leatherGrain = noise(uv * 10.0) * 0.15;
     float wearMarks = noise(uv * 3.0) * 0.10;
@@ -159,17 +141,14 @@ void main() {
 
   color = clamp(color, 0.0, 1.0);
 
-  // Lighting model - hard shadows for metal, soft for fabric
   vec3 lightDir = normalize(vec3(1.0, 1.2, 1.0));
   float nDotL = dot(normal, lightDir);
 
-  // Metal = hard shadows, Fabric = soft wrap
   float wrapAmount = (avgColor > 0.50) ? 0.08 : 0.30;
   float diff = max(nDotL * (1.0 - wrapAmount) + wrapAmount, 0.18);
 
-  // Extra contrast for polished steel
   if (avgColor > 0.60 && !isBrass) {
-    diff = pow(diff, 0.85); // Sharper lighting falloff
+    diff = pow(diff, 0.85);
   }
 
   color *= diff;

+ 73 - 95
assets/shaders/swordsman_carthage.frag

@@ -1,9 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// CARTHAGINIAN SWORDSMAN - Modified: Armor & Helmet now dark metallic
-// ============================================================================
-
 in vec3 v_worldNormal;
 in vec3 v_worldPos;
 in vec2 v_texCoord;
@@ -16,18 +12,12 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// CONSTANTS & HELPERS
-// ============================================================================
-
 const float PI = 3.14159265359;
 
-// ORIGINAL bronze kept for shield/weapon/etc
 const vec3 BRONZE_BASE_COLOR = vec3(0.86, 0.66, 0.36);
 
-// NEW — dark metal for armor + helmet
 const vec3 DARK_METAL_COLOR = vec3(0.14, 0.14, 0.16);
-// Dark brown for the Carthaginian shield
+
 const vec3 SHIELD_BROWN_COLOR = vec3(0.18, 0.09, 0.035);
 
 float saturate(float x) { return clamp(x, 0.0, 1.0); }
@@ -67,10 +57,6 @@ float fbm(vec2 p) {
   return v;
 }
 
-// ============================================================================
-// PBR FUNCTIONS
-// ============================================================================
-
 float D_GGX(float NdotH, float roughness) {
   float a = roughness * roughness;
   float a2 = a * a;
@@ -94,14 +80,10 @@ vec3 F_Schlick(float cosTheta, vec3 F0) {
   return F0 + (1.0 - F0) * t5;
 }
 
-// ============================================================================
-// METAL / ARMOR PATTERNS
-// ============================================================================
-
 float hammerPattern(vec3 pos) {
   float coarse = fbm(pos.xz * 12.0);
-  float fine   = fbm(pos.xy * 28.0 + 7.3);
-  float micro  = noise(pos.yz * 45.0);
+  float fine = fbm(pos.xy * 28.0 + 7.3);
+  float micro = noise(pos.yz * 45.0);
   return coarse * 0.5 + fine * 0.35 + micro * 0.15;
 }
 
@@ -116,42 +98,36 @@ float scaleArmor(vec2 uv) {
   return edge + highlight * 0.4;
 }
 
-// ============================================================================
-// MAIN
-// ============================================================================
-
 void main() {
   vec3 baseColor = clamp(u_color, 0.0, 1.0);
   if (u_useTexture) {
     baseColor *= texture(u_texture, v_texCoord).rgb;
   }
   baseColor = boostSaturation(baseColor, 0.3);
-  
-  bool is_skin   = (u_materialId == 0);
-  bool is_armor  = (u_materialId == 1);
+  vec3 vibrantTeamColor =
+      boostSaturation(baseColor * vec3(1.05, 0.95, 0.95), 0.55);
+
+  bool is_skin = (u_materialId == 0);
+  bool is_armor = (u_materialId == 1);
   bool is_helmet = (u_materialId == 2);
   bool is_weapon = (u_materialId == 3);
   bool is_shield = (u_materialId == 4);
-  
+
   vec3 N = normalize(v_worldNormal);
   vec3 V = normalize(vec3(0.0, 0.0, 1.0));
   vec3 L = normalize(vec3(0.5, 1.0, 0.4));
   vec3 H = normalize(L + V);
-  
+
   vec3 albedo = baseColor;
   float metallic = 0.0;
   float roughness = 0.5;
-  
-  // ========================================================================
-  // MATERIALS
-  // ========================================================================
-  
+
   if (is_skin) {
     albedo = mix(baseColor, vec3(0.95, 0.78, 0.65), 0.2);
     albedo = boostSaturation(albedo, 0.15);
     metallic = 0.0;
     roughness = 0.55;
-    
+
     float pants = 1.0 - smoothstep(0.35, 0.60, v_worldPos.y);
     float grain = fbm(v_worldPos.xz * 18.0 + v_worldPos.y * 4.0);
     vec3 leather = baseColor * 0.75;
@@ -159,157 +135,159 @@ void main() {
     leather -= vec3(0.08) * grain;
     albedo = mix(albedo, leather, pants);
     roughness = mix(roughness, 0.5, pants);
-    
-  }
-  else if (is_armor) {
-    // ==================================================================
-    // DARK METAL ARMOR (replacing bronze)
-    // ==================================================================
+
+    float clothMask =
+        clamp(max(pants, 1.0 - smoothstep(0.6, 0.85, v_worldPos.y)), 0.0, 1.0);
+    float clothNoise = fbm(v_worldPos.xz * 22.0);
+    vec3 clothTone = mix(vibrantTeamColor * 0.88, vibrantTeamColor * 1.12,
+                         clothNoise * 0.5 + 0.25);
+    albedo = mix(albedo, clothTone, clothMask * 0.75);
+
+    float armBand = smoothstep(0.3, 0.55, v_worldPos.y) *
+                    smoothstep(0.25, 0.45, abs(v_worldPos.x)) *
+                    smoothstep(0.85, 0.45, abs(v_worldPos.z));
+    float armAccent = clamp(armBand, 0.0, 1.0);
+    vec3 limbTeamColor = mix(baseColor * 0.82, vibrantTeamColor, 0.72);
+    albedo = mix(albedo, limbTeamColor, armAccent * 0.9);
+
+  } else if (is_armor) {
+
     vec2 metalUV = v_texCoord * 6.0;
     float hammer = hammerPattern(vec3(metalUV, v_worldPos.y));
     float scales = scaleArmor(metalUV);
-    
+
     vec3 metal = DARK_METAL_COLOR;
     float patinaNoise = fbm(metalUV * 2.5);
     vec3 patinaTint = DARK_METAL_COLOR * 1.15;
     metal = mix(metal, patinaTint, patinaNoise * 0.2);
-    
+
     vec3 teamTint = mix(vec3(1.0), baseColor, 0.25);
     albedo = metal * teamTint;
-    
+
     albedo *= 0.9 + hammer * 0.25;
     albedo = mix(albedo, albedo * 1.2, scales * 0.5);
-    
+
     metallic = 1.0;
     roughness = 0.22 + hammer * 0.08 - scales * 0.08;
     roughness = clamp(roughness, 0.10, 0.35);
-  }
-  else if (is_helmet) {
-    // ==================================================================
-    // DARK METAL HELMET (replacing bronze)
-    // ==================================================================
+  } else if (is_helmet) {
+
     vec2 metalUV = v_texCoord * 8.0;
     float hammer = hammerPattern(vec3(metalUV, v_worldPos.y));
     float scales = scaleArmor(metalUV * 1.2);
-    
+
     vec3 metal = DARK_METAL_COLOR;
     float patinaNoise = fbm(metalUV * 3.0);
     vec3 patinaTint = DARK_METAL_COLOR * 1.12;
     metal = mix(metal, patinaTint, patinaNoise * 0.18);
-    
+
     float crest = smoothstep(0.8, 0.9, v_worldPos.y);
     crest *= smoothstep(0.4, 0.3, abs(v_worldPos.x));
-    
+
     vec3 crestColor = boostSaturation(baseColor, 0.5) * 1.3;
-    
+
     albedo = mix(metal, crestColor, crest * 0.5);
-    
+
     albedo *= 0.9 + hammer * 0.25;
     albedo = mix(albedo, albedo * 1.18, scales * 0.5);
-    
+
     metallic = 1.0;
     roughness = 0.18 + hammer * 0.08 - scales * 0.08;
     roughness = clamp(roughness, 0.08, 0.30);
-  }
-  else if (is_weapon) {
+  } else if (is_weapon) {
     float h = v_worldPos.y;
     float blade = smoothstep(0.28, 0.50, h);
     float guard = smoothstep(0.18, 0.28, h) * (1.0 - blade);
-    
+
     float polish = fbm(v_worldPos.xy * 35.0);
-    
+
     vec3 handle = boostSaturation(baseColor * 0.9, 0.25);
     handle += vec3(0.06) * polish;
-    
+
     vec3 guardCol = mix(BRONZE_BASE_COLOR, baseColor * 1.1, 0.3);
     guardCol = boostSaturation(guardCol, 0.3);
-    
+
     vec3 steel = vec3(0.85, 0.87, 0.92);
     steel += vec3(0.08) * polish;
     steel = mix(steel, baseColor * 0.4 + vec3(0.55), 0.15);
-    
+
     albedo = mix(handle, guardCol, guard);
     albedo = mix(albedo, steel, blade);
-    
+
     metallic = mix(0.15, 1.0, blade + guard * 0.9);
     roughness = mix(0.45, 0.05, blade);
-  }
-  else if (is_shield) {
+  } else if (is_shield) {
     float dist = length(v_worldPos.xz);
     float boss = smoothstep(0.25, 0.0, dist);
     float rings = sin(dist * 20.0) * 0.5 + 0.5;
     rings = smoothstep(0.3, 0.7, rings) * (1.0 - boss);
-    
-    vec3 shieldFace = boostSaturation(mix(SHIELD_BROWN_COLOR, baseColor, 0.12), 0.35);
+
+    vec3 shieldFace =
+        boostSaturation(mix(SHIELD_BROWN_COLOR, baseColor, 0.12), 0.35);
     vec3 bronze = BRONZE_BASE_COLOR;
     vec3 shieldMetal = mix(shieldFace, bronze, boss + rings * 0.6);
 
     albedo = mix(shieldFace, shieldMetal, boss + rings * 0.5);
-    
-    metallic  = mix(0.2, 1.0, boss + rings * 0.7);
+
+    metallic = mix(0.2, 1.0, boss + rings * 0.7);
     roughness = mix(0.45, 0.12, boss);
-  }
-  else {
+  } else {
     albedo = boostSaturation(baseColor, 0.2);
     metallic = 0.0;
     roughness = 0.6;
   }
-  
-  // ========================================================================
-  // PBR LIGHTING
-  // ========================================================================
-  
+
   float NdotL = max(dot(N, L), 0.0);
   float NdotV = max(dot(N, V), 0.001);
   float NdotH = max(dot(N, H), 0.0);
   float VdotH = max(dot(V, H), 0.0);
-  
+
   vec3 F0 = mix(vec3(0.04), albedo, metallic);
-  
+
   float D = D_GGX(NdotH, max(roughness, 0.01));
   float G = G_Smith(NdotV, NdotL, roughness);
   vec3 F = F_Schlick(VdotH, F0);
   vec3 specular = (D * G * F) / (4.0 * NdotV * NdotL + 0.001);
-  
+
   vec3 kD = (vec3(1.0) - F) * (1.0 - metallic);
   vec3 diffuse = kD * albedo / PI;
-  
+
   vec3 color = (diffuse + specular * 2.0) * NdotL * 2.0;
-  
+
   if (metallic > 0.5) {
     vec3 R = reflect(-V, N);
-    
+
     float specPower = 196.0 / (roughness * roughness + 0.001);
     specPower = min(specPower, 1536.0);
     float mirrorSpec = pow(max(dot(R, L), 0.0), specPower);
     color += albedo * mirrorSpec * 1.6;
-    
+
     float hotspot = pow(NdotH, 400.0);
     color += vec3(1.1) * hotspot * (1.0 - roughness * 1.6);
-    
+
     float softSpec = pow(max(dot(R, L), 0.0), 32.0);
     color += albedo * softSpec * 0.5;
-    
-    vec3 skyCol    = vec3(0.6, 0.7, 0.9);
+
+    vec3 skyCol = vec3(0.6, 0.7, 0.9);
     vec3 groundCol = vec3(0.4, 0.35, 0.28);
-    float upFace   = R.y * 0.5 + 0.5;
+    float upFace = R.y * 0.5 + 0.5;
     vec3 envReflect = mix(groundCol, skyCol, upFace);
     color += envReflect * (1.0 - roughness) * 0.4;
   }
-  
+
   vec3 ambient = albedo * 0.42;
-  
+
   float rim = pow(1.0 - NdotV, 3.5);
   ambient += vec3(0.4, 0.45, 0.55) * rim * 0.28;
-  
+
   if (metallic > 0.5) {
     ambient += albedo * 0.12 * (1.0 - roughness);
   }
-  
+
   color += ambient;
-  
+
   color = color / (color + vec3(0.6));
   color = pow(color, vec3(0.92));
-  
+
   FragColor = vec4(saturate3(color), u_alpha);
 }

+ 1 - 6
assets/shaders/swordsman_carthage.vert

@@ -36,7 +36,6 @@ void main() {
   vec3 position = a_position;
   vec3 normal = a_normal;
 
-  // Only annotate helmet flag; shield bending removed to keep mesh intact.
   bool is_shield = (u_materialId == 4);
   bool is_helmet = (u_materialId == 2);
 
@@ -52,8 +51,6 @@ void main() {
   vec4 modelPos = u_model * vec4(position, 1.0);
   vec3 worldPos = modelPos.xyz;
 
-  // Normalize height along the model's local Y so lighting gradients stay
-  // stable regardless of placement or per-piece transforms.
   vec3 axisY = vec3(u_model[1].xyz);
   float axisLen = max(length(axisY), 1e-4);
   vec3 axisDir = axisY / axisLen;
@@ -62,9 +59,8 @@ void main() {
       clamp(dot(worldPos - modelOrigin, axisDir) / axisLen + 0.5, 0.0, 1.0);
 
   float dentSeed = hash13(worldPos * 0.8 + worldNormal * 0.3);
-  float torsion = 0.0; // removed bulk shear to avoid squashing
+  float torsion = 0.0;
 
-  // Minimal acne offset; no sculpting except shield bend above.
   vec3 offsetPos = worldPos + worldNormal * 0.003;
 
   mat4 invModel = inverse(u_model);
@@ -92,7 +88,6 @@ void main() {
 
   v_frontMask = clamp(smoothstep(-0.18, 0.18, -localPos.z), 0.0, 1.0);
 
-  // Force the full armor piece to use the armor material; avoid partial masks.
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
   v_bodyHeight = clamp((height01 - 0.05) / 0.90, 0.0, 1.0);

+ 40 - 120
assets/shaders/swordsman_roman_republic.frag

@@ -1,9 +1,5 @@
 #version 330 core
 
-// ============================================================================
-// INPUTS & OUTPUTS
-// ============================================================================
-
 in vec3 v_normal;
 in vec3 v_worldNormal;
 in vec3 v_tangent;
@@ -26,10 +22,6 @@ uniform int u_materialId;
 
 out vec4 FragColor;
 
-// ============================================================================
-// UTILITY FUNCTIONS
-// ============================================================================
-
 float hash(vec2 p) {
   vec3 p3 = fract(vec3(p.xyx) * 0.1031);
   p3 += dot(p3, p3.yzx + 33.33);
@@ -47,11 +39,6 @@ float noise(vec2 p) {
   return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
 }
 
-// ============================================================================
-// MATERIAL PATTERN FUNCTIONS (Roman Swordsman - Legionary)
-// ============================================================================
-
-// Segmented armor plates (lorica segmentata)
 float armor_plates(vec2 p, float y) {
   float plate_y = fract(y * 6.5);
   float plate_line = smoothstep(0.90, 0.98, plate_y) * 0.12;
@@ -71,10 +58,6 @@ float pteruges_strips(vec2 p, float y) {
   return strip * leather_tex * hang;
 }
 
-// ============================================================================
-// MAIN FRAGMENT SHADER
-// ============================================================================
-
 void main() {
   vec3 color = u_color;
   if (u_useTexture) {
@@ -84,7 +67,6 @@ void main() {
   vec3 normal = normalize(v_normal);
   vec2 uv = v_worldPos.xz * 5.0;
 
-  // Material ID: 0=body/skin, 1=armor, 2=helmet, 3=weapon, 4=shield, 5=greaves
   bool is_skin = (u_materialId == 0);
   bool is_armor = (u_materialId == 1);
   bool is_helmet = (u_materialId == 2);
@@ -92,9 +74,6 @@ void main() {
   bool is_shield = (u_materialId == 4);
   bool is_greaves = (u_materialId == 5);
 
-  // === ROMAN SWORDSMAN (LEGIONARY) MATERIALS ===
-
-  // HEAVY STEEL HELMET (galea - cool blue-grey steel)
   if (is_skin) {
     vec3 V = normalize(vec3(0.0, 1.0, 0.35));
     float skin_detail = noise(uv * 18.0) * 0.06;
@@ -105,16 +84,14 @@ void main() {
     color += vec3(0.025, 0.015, 0.010) * subdermal;
     color += vec3(rim);
   } else if (is_helmet) {
-    // Polished steel finish with vertex polish level
+
     float brushed_metal = abs(sin(v_worldPos.y * 95.0)) * 0.02;
     float scratches = noise(uv * 35.0) * 0.018 * (1.0 - v_polishLevel * 0.5);
     float dents = noise(uv * 8.0) * 0.025;
 
-    // Use vertex-computed helmet detail
     float bands = v_helmetDetail * 0.12;
     float rivets = v_rivetPattern * 0.12;
 
-    // ENHANCED: Elaborate cheek guards (legionary standard)
     float cheek_guard_height = smoothstep(0.68, 0.78, v_bodyHeight) *
                                smoothstep(0.92, 0.82, v_bodyHeight);
     float cheek_x = abs(v_worldPos.x);
@@ -125,7 +102,6 @@ void main() {
     float hinge_detail =
         cheek_guard * step(0.92, fract(v_bodyHeight * 32.0)) * 0.25;
 
-    // ENHANCED: Extended neck guard (deep rear protection)
     float neck_guard_height = smoothstep(0.64, 0.70, v_bodyHeight) *
                               smoothstep(0.84, 0.74, v_bodyHeight);
     float behind_head = step(v_worldNormal.z, -0.20);
@@ -135,14 +111,12 @@ void main() {
     float neck_rivets =
         step(0.90, fract(v_worldPos.x * 22.0)) * neck_guard * 0.12;
 
-    // ENHANCED: Reinforced brow band (legionary frontal ridge)
     float brow_height = smoothstep(0.82, 0.86, v_bodyHeight) *
                         smoothstep(0.94, 0.88, v_bodyHeight);
     float front_facing = step(0.4, v_worldNormal.z);
     float brow_reinforce = brow_height * front_facing * 0.28;
     float brow_decoration = brow_reinforce * noise(uv * 20.0) * 0.10;
 
-    // ENHANCED: Plume socket with mounting bracket (officer insignia)
     float plume_socket_height = smoothstep(0.88, 0.92, v_bodyHeight);
     float plume_socket =
         plume_socket_height * smoothstep(0.06, 0.04, abs(v_worldPos.x)) * 0.32;
@@ -162,123 +136,93 @@ void main() {
     color += vec3(brushed_metal);
     color -= vec3(scratches + dents * 0.4);
   }
-  // HEAVY SEGMENTED ARMOR (lorica segmentata - iconic Roman plate armor)
+
   else if (is_armor) {
-    // FORCE polished steel base - segmentata is BRIGHT, REFLECTIVE armor
-    color = vec3(0.72, 0.78, 0.88); // Bright steel base - NOT skin tone!
+
+    color = vec3(0.72, 0.78, 0.88);
 
     vec2 armor_uv = v_worldPos.xz * 5.5;
 
-    // === HORIZONTAL PLATE BANDS - MUST BE OBVIOUS ===
-    // 6-7 clearly visible bands wrapping torso
     float band_pattern = fract(v_platePhase);
 
-    // STRONG band edges (plate separations)
     float band_edge = step(0.92, band_pattern) + step(band_pattern, 0.08);
-    float plate_line = band_edge * 0.55; // Much stronger
+    float plate_line = band_edge * 0.55;
 
-    // DEEP shadows between overlapping plates
     float overlap_shadow = smoothstep(0.90, 0.98, band_pattern) * 0.65;
 
-    // Alternating plate brightness (polishing variation)
     float plate_brightness = step(0.5, fract(v_platePhase * 0.5)) * 0.15;
 
-    // === RIVETS - CLEARLY VISIBLE ===
-    // Large brass rivets along each band edge
     float rivet_x = fract(v_worldPos.x * 16.0);
-    float rivet_y = fract(v_platePhase * 6.5); // Align with bands
+    float rivet_y = fract(v_platePhase * 6.5);
     float rivet = smoothstep(0.45, 0.50, rivet_x) *
                   smoothstep(0.55, 0.50, rivet_x) *
                   (step(0.92, rivet_y) + step(rivet_y, 0.08));
-    float brass_rivets = rivet * 0.45;         // Much more visible
-    vec3 brass_color = vec3(0.95, 0.82, 0.45); // Bright brass
+    float brass_rivets = rivet * 0.45;
+    vec3 brass_color = vec3(0.95, 0.82, 0.45);
 
-    // === METALLIC FINISH ===
-    // Polished steel with strong reflections
     float brushed_metal = abs(sin(v_worldPos.y * 75.0)) * 0.12;
 
     vec3 V = normalize(vec3(0.0, 1.0, 0.5));
     float view_angle = max(dot(normalize(v_worldNormal), V), 0.0);
 
-    // VERY STRONG specular - legionary armor was highly polished
     float plate_specular = pow(view_angle, 9.0) * 0.85 * v_polishLevel;
 
-    // Metallic fresnel
     float plate_fresnel = pow(1.0 - view_angle, 2.2) * 0.45;
 
-    // Sky reflection
     float sky_reflect = (v_worldNormal.y * 0.5 + 0.5) * 0.35 * v_polishLevel;
 
-    // === WEAR & DAMAGE ===
-    // Battle scratches
     float scratches =
         noise(armor_uv * 42.0) * 0.08 * (1.0 - v_polishLevel * 0.7);
 
-    // Impact dents (front armor takes hits)
     float front_facing = smoothstep(-0.2, 0.7, v_worldNormal.z);
     float dents = noise(armor_uv * 6.0) * 0.12 * front_facing;
 
-    // ENHANCED: Joint wear between plates with articulation stress
     float joint_wear = v_segmentStress * 0.25;
 
-    // ENHANCED: Plate edge wear (horizontal band edges show most damage)
     float edge_wear = smoothstep(0.88, 0.96, band_pattern) *
                       smoothstep(0.12, 0.04, band_pattern) * 0.18;
     float edge_darkening = edge_wear * noise(armor_uv * 15.0) * 0.22;
 
-    // ENHANCED: Rivet stress patterns (rivets loosen/oxidize at stress points)
     float rivet_stress = rivet * v_segmentStress * 0.15;
     vec3 rivet_oxidation = vec3(0.35, 0.28, 0.20) * rivet_stress;
 
-    // ENHANCED: Plate segment definition (each band slightly different
-    // polish/color)
     float segment_variation = noise(vec2(v_platePhase, 0.5) * 2.0) * 0.08;
 
-    // ENHANCED: Overlapping plate shadows with depth (more realistic
-    // articulation)
     float plate_depth = smoothstep(0.94, 0.98, band_pattern) * 0.40;
-    float gap_shadow = plate_depth * (1.0 - view_angle * 0.5); // Deeper in gaps
+    float gap_shadow = plate_depth * (1.0 - view_angle * 0.5);
 
-    // Apply all plate effects - STRONG VISIBILITY
     color += vec3(plate_brightness + plate_specular + plate_fresnel +
                   sky_reflect + brushed_metal + segment_variation);
     color -= vec3(plate_line * 0.4 + overlap_shadow + scratches + dents * 0.5 +
                   joint_wear + edge_darkening + gap_shadow);
 
-    // Add brass rivets with color and oxidation
     color = mix(color, brass_color, brass_rivets);
     color -= rivet_oxidation;
 
-    // Ensure segmentata is ALWAYS bright and visible
     color = clamp(color, vec3(0.45), vec3(0.95));
   }
-  // SCUTUM SHIELD (curved laminated wood with metal boss)
+
   else if (is_shield) {
-    // Shield boss (raised metal dome)
+
     float boss_dist = length(v_worldPos.xy);
     float boss = smoothstep(0.12, 0.08, boss_dist) * 0.6;
     float boss_rim = smoothstep(0.14, 0.12, boss_dist) *
                      smoothstep(0.10, 0.12, boss_dist) * 0.3;
 
-    // Laminated wood construction
     float edge_dist = max(abs(v_worldPos.x), abs(v_worldPos.y));
     float edge_wear = smoothstep(0.42, 0.48, edge_dist);
     float wood_layers = edge_wear * noise(uv * 40.0) * 0.25;
 
-    // Painted canvas facing
     float fabric_grain = noise(uv * 25.0) * 0.08;
     float canvas_weave = sin(uv.x * 60.0) * sin(uv.y * 58.0) * 0.04;
 
-    // Bronze edging (perimeter reinforcement)
     float metal_edge = smoothstep(0.46, 0.48, edge_dist) * 0.45;
     vec3 bronze_edge = vec3(0.75, 0.55, 0.30);
 
-    // Combat damage (more on legionary shields)
     float dents = noise(uv * 8.0) * v_polishLevel * 0.20;
     float cuts = step(0.88, noise(uv * 12.0)) * 0.32;
     float scratches = noise(uv * 50.0) * 0.15;
 
-    // Boss polish and reflection
     vec3 V = normalize(vec3(0.0, 1.0, 0.5));
     float view_angle = max(dot(normalize(v_worldNormal), V), 0.0);
     float boss_sheen = pow(view_angle, 12.0) * boss * v_polishLevel * 0.60;
@@ -289,28 +233,24 @@ void main() {
     color = mix(color, bronze_edge, metal_edge);
     color -= vec3(dents * 0.08 + cuts * 0.12 + scratches * 0.05);
   }
-  // GLADIUS (Roman short sword - steel blade)
+
   else if (is_weapon) {
-    // Weapon parts by body height
-    bool is_blade = (v_bodyHeight > 0.35); // Blade section
-    bool is_grip = (v_bodyHeight > 0.15 && v_bodyHeight <= 0.35); // Handle
-    bool is_pommel = (v_bodyHeight <= 0.15); // Pommel/guard
+
+    bool is_blade = (v_bodyHeight > 0.35);
+    bool is_grip = (v_bodyHeight > 0.15 && v_bodyHeight <= 0.35);
+    bool is_pommel = (v_bodyHeight <= 0.15);
 
     if (is_blade) {
-      // Polished steel blade
+
       vec3 steel_color = vec3(0.78, 0.81, 0.85);
 
-      // Fuller groove (weight reduction channel)
       float fuller_x = abs(v_worldPos.x);
       float fuller = smoothstep(0.025, 0.015, fuller_x) * 0.25;
 
-      // Razor-sharp edge
       float edge = smoothstep(0.08, 0.10, fuller_x) * 0.50;
 
-      // High polish finish (legionary standard)
       float polish_marks = noise(uv * 60.0) * 0.08 * v_polishLevel;
 
-      // Edge tempering line (heat treatment)
       float temper_line = smoothstep(0.075, 0.085, fuller_x) * 0.12;
 
       vec3 V = normalize(vec3(0.0, 1.0, 0.5));
@@ -323,16 +263,16 @@ void main() {
       color += vec3(edge + metal_sheen + reflection);
       color += vec3(polish_marks * 0.6 + temper_line * 0.4);
     } else if (is_grip) {
-      // Wood grip with leather wrap
+
       float wood_base = noise(uv * 28.0) * 0.14;
       float leather_wrap = noise(uv * 35.0) * 0.12;
       float wire_binding = step(0.88, fract(v_worldPos.y * 30.0)) * 0.20;
 
-      color *= vec3(0.52, 0.42, 0.32); // Leather-wrapped wood
+      color *= vec3(0.52, 0.42, 0.32);
       color *= 1.0 + wood_base + leather_wrap;
-      color += vec3(wire_binding * 0.5); // Bronze wire
+      color += vec3(wire_binding * 0.5);
     } else if (is_pommel) {
-      // Brass pommel (counterweight and decoration)
+
       vec3 brass_color = vec3(0.82, 0.72, 0.42);
 
       float decoration = noise(uv * 20.0) * 0.10;
@@ -345,102 +285,82 @@ void main() {
       color += vec3(brass_sheen + decoration * 0.4);
     }
   }
-  // BRONZE GREAVES (shin guards - highly polished mirror-like bronze)
+
   else if (is_greaves) {
-    // Rich polished bronze base (warm golden metallic)
+
     vec3 bronze_base = vec3(0.88, 0.72, 0.45);
     vec3 bronze_highlight = vec3(1.0, 0.92, 0.75);
     vec3 bronze_shadow = vec3(0.55, 0.42, 0.25);
-    
-    // Apply bronze base
+
     color = mix(color, bronze_base, 0.90);
-    
-    // Micro brushed metal texture (fine polish lines)
+
     float brushed = abs(sin(v_worldPos.y * 120.0)) * 0.015;
-    
-    // View direction for specular
+
     vec3 V = normalize(vec3(0.15, 0.85, 0.5));
     vec3 N = normalize(v_worldNormal);
     float NdotV = max(dot(N, V), 0.0);
-    
-    // Primary specular (sharp highlight - polished metal)
+
     float spec_primary = pow(NdotV, 32.0) * 1.2;
-    
-    // Secondary specular (broader shine)
+
     float spec_secondary = pow(NdotV, 8.0) * 0.45;
-    
-    // Metallic fresnel (bright edges)
+
     float fresnel = pow(1.0 - NdotV, 3.0) * 0.65;
-    
-    // Anisotropic highlight (stretched along greave height)
+
     float aniso = pow(abs(sin(v_worldPos.y * 40.0 + NdotV * 3.14)), 4.0) * 0.25;
-    
-    // Environment reflection (sky above, ground below)
+
     float env_up = max(N.y, 0.0) * 0.35;
     float env_side = (1.0 - abs(N.y)) * 0.20;
-    
-    // Combine all shine effects
+
     vec3 shine = bronze_highlight * (spec_primary + spec_secondary + aniso);
     shine += vec3(fresnel * 0.8, fresnel * 0.7, fresnel * 0.5);
     shine += bronze_base * (env_up + env_side);
-    
+
     color += shine;
     color += vec3(brushed);
-    
-    // Ensure bright metallic finish
+
     color = clamp(color, bronze_shadow, vec3(1.0));
   }
 
   color = clamp(color, 0.0, 1.0);
 
-  // === PHASE 4: ADVANCED FEATURES ===
-  // Environmental interactions (elite legionary maintains higher standards)
-
-  // Limited campaign wear (legionaries keep equipment polished)
   float campaign_wear =
       (1.0 - v_bodyHeight * 0.7) * (1.0 - v_polishLevel) * 0.12;
   float dust_accumulation = noise(v_worldPos.xz * 8.0) * campaign_wear * 0.08;
 
-  // Rain streaks (even polished armor shows weather)
   float rain_streaks =
       smoothstep(0.87, 0.93,
                  noise(v_worldPos.xz * 2.5 + vec2(0.0, v_worldPos.y * 8.0))) *
-      v_bodyHeight * 0.08; // Subtle on segmentata
+      v_bodyHeight * 0.08;
 
-  // Minimal mud (legionaries in formation, not skirmishing)
-  float mud_height = smoothstep(0.45, 0.10, v_bodyHeight); // Only ankles/feet
+  float mud_height = smoothstep(0.45, 0.10, v_bodyHeight);
   float mud_pattern =
       step(0.80, noise(v_worldPos.xz * 12.0 + v_worldPos.y * 3.0));
   float mud_splatter = mud_height * mud_pattern * 0.15;
   vec3 mud_color = vec3(0.22, 0.18, 0.14);
 
-  // Blood evidence (frontline combat)
-  float blood_height = smoothstep(0.88, 0.65, v_bodyHeight); // Upper torso/arms
+  float blood_height = smoothstep(0.88, 0.65, v_bodyHeight);
   float blood_pattern =
       step(0.90, noise(v_worldPos.xz * 15.0 + v_bodyHeight * 5.0));
   float blood_stain =
       blood_height * blood_pattern * (1.0 - v_polishLevel * 0.5) * 0.10;
   vec3 blood_color = vec3(0.18, 0.08, 0.06);
 
-  // Apply environmental effects (moderated by polish level)
   color -= vec3(dust_accumulation);
   color -= vec3(rain_streaks * 0.5);
   color = mix(color, mud_color, mud_splatter);
   color = mix(color, blood_color, blood_stain);
 
-  // Subtle ambient occlusion to ground the metal and leather
   float ao = 0.90 - noise(v_worldPos.xz * 4.0) * 0.10;
   color *= ao;
-  color = mix(color, vec3(dot(color, vec3(0.333))), 0.08); // mild desaturation
+  color = mix(color, vec3(dot(color, vec3(0.333))), 0.08);
 
-  // Lighting per material
   vec3 light_dir = normalize(vec3(1.0, 1.2, 1.0));
   float n_dot_l = dot(normalize(v_worldNormal), light_dir);
 
-  float wrap_amount = is_helmet ? 0.08 : (is_armor ? 0.08 : (is_greaves ? 0.08 : 0.30));
+  float wrap_amount =
+      is_helmet ? 0.08 : (is_armor ? 0.08 : (is_greaves ? 0.08 : 0.30));
   float diff = max(n_dot_l * (1.0 - wrap_amount) + wrap_amount, 0.16);
 
-  // Extra contrast for polished steel and bronze
   if (is_helmet || is_armor || is_greaves) {
     diff = pow(diff, 0.85);
   }

+ 8 - 20
assets/shaders/swordsman_roman_republic.vert

@@ -35,18 +35,15 @@ void main() {
   vec3 position = a_position;
   vec3 normal = a_normal;
 
-  // Shield curving: bend flat rectangle into scutum curve (materialId=4)
   if (u_materialId == 4) {
-    float curveRadius = 0.55; // Curve radius relative to shield width
-    float curveAmount = 0.45; // How much to curve (±25 degrees)
-    float angle = position.x * curveAmount; // X position drives curve angle
+    float curveRadius = 0.55;
+    float curveAmount = 0.45;
+    float angle = position.x * curveAmount;
 
-    // Bend position around Y axis (vertical shield)
     float curved_x = sin(angle) * curveRadius;
     float curved_z = position.z + (1.0 - cos(angle)) * curveRadius;
     position = vec3(curved_x, position.y, curved_z);
 
-    // Rotate normal to follow curved surface
     normal = vec3(sin(angle) * normal.z + cos(angle) * normal.x, normal.y,
                   cos(angle) * normal.z - sin(angle) * normal.x);
   }
@@ -54,7 +51,6 @@ void main() {
   mat3 normalMatrix = mat3(transpose(inverse(u_model)));
   vec3 worldNormal = normalize(normalMatrix * normal);
 
-  // Build tangent space
   vec3 t = normalize(cross(fallbackUp(worldNormal), worldNormal));
   if (length(t) < 1e-4)
     t = vec3(1.0, 0.0, 0.0);
@@ -64,9 +60,9 @@ void main() {
   vec4 modelPos = u_model * vec4(position, 1.0);
   vec3 worldPos = modelPos.xyz;
 
-  // Deform only armored pieces (not face/skin/clothing)
-  bool deformArmor = (u_materialId == 1 || u_materialId == 2 ||
-                      u_materialId == 4 || u_materialId == 3 || u_materialId == 5);
+  bool deformArmor =
+      (u_materialId == 1 || u_materialId == 2 || u_materialId == 4 ||
+       u_materialId == 3 || u_materialId == 5);
 
   float dentSeed = 0.0;
   float combatStress = 0.0;
@@ -74,7 +70,7 @@ void main() {
   vec3 offsetPos = worldPos;
 
   if (deformArmor) {
-    // Heavy battle damage for elite legionary equipment
+
     dentSeed = hash13(worldPos * 0.88 + worldNormal * 0.32);
     combatStress = sin(worldPos.y * 18.2 + dentSeed * 22.61);
     vec3 dentOffset = worldNormal * ((dentSeed - 0.5) * 0.0105);
@@ -96,17 +92,14 @@ void main() {
   v_tangent = t;
   v_bitangent = b;
 
-  // Keep armor/material selection stable: 1.0 only for armor material.
   float height = offsetPos.y;
   v_armorLayer = (u_materialId == 1) ? 1.0 : 0.0;
 
-  // Body height normalization
   float torsoMin = 0.55;
   float torsoMax = 1.70;
   v_bodyHeight =
       clamp((offsetPos.y - torsoMin) / (torsoMax - torsoMin), 0.0, 1.0);
 
-  // Heavy helmet attributes (galea)
   float reinforcementBands = fract(height * 14.0);
   float browBandRegion =
       smoothstep(1.48, 1.52, height) * smoothstep(1.56, 1.52, height);
@@ -115,21 +108,16 @@ void main() {
   v_helmetDetail =
       reinforcementBands * 0.35 + browBandRegion * 0.45 + cheekGuardArea * 0.2;
 
-  // Segmented armor plate phase (lorica segmentata)
-  v_platePhase = fract(height * 6.5); // Horizontal bands
+  v_platePhase = fract(height * 6.5);
 
-  // Segment articulation stress
   float localX = (invModel * vec4(offsetPos, 1.0)).x;
   v_segmentStress = combatStress * (0.6 + 0.4 * sin(localX * 12.0));
 
-  // Rivet pattern (visible on both helmet and armor)
   v_rivetPattern = step(0.96, fract(offsetPos.x * 18.0)) *
                    step(0.94, fract(v_platePhase * 1.0));
 
-  // Leather wear for pteruges
   v_leatherWear = hash13(offsetPos * 0.48 + worldNormal * 2.1) *
                   (0.55 + v_bodyHeight * 0.45);
 
-  // Polish level (higher on helmet and exposed plates)
   v_polishLevel = 1.0 - dentSeed * 0.4 - v_bodyHeight * 0.2;
 }

+ 30 - 65
assets/shaders/terrain_chunk.frag

@@ -16,32 +16,27 @@ uniform float u_soilBlendHeight, u_soilBlendSharpness;
 uniform float u_heightNoiseStrength, u_heightNoiseFrequency;
 uniform float u_ambientBoost, u_rockDetailStrength;
 
-// Ground-type-specific uniforms
-uniform float u_snowCoverage;    // 0-1: snow accumulation (alpine_mix)
-uniform float u_moistureLevel;   // 0-1: wetness/dryness
-uniform float u_crackIntensity;  // 0-1: ground cracking (grass_dry)
-uniform float u_rockExposure;    // 0-1: how much rock shows through
-uniform float u_grassSaturation; // 0-1.5: grass color intensity
-uniform float u_soilRoughness;   // 0-1: soil texture roughness
-uniform vec3 u_snowColor;        // Snow tint color
-
-// lets soil “climb” up steep toes (world units)
-uniform float u_soilFootHeight; // try 0.6–1.2
-
-// -------- OPTIONAL (leave at defaults if you don’t have a heightmap) --------
-uniform int u_hasHeightTex; // 0 = off (default), 1 = on
+uniform float u_snowCoverage;
+uniform float u_moistureLevel;
+uniform float u_crackIntensity;
+uniform float u_rockExposure;
+uniform float u_grassSaturation;
+uniform float u_soilRoughness;
+uniform vec3 u_snowColor;
+
+uniform float u_soilFootHeight;
+
+uniform int u_hasHeightTex;
 uniform sampler2D u_heightTex;
 uniform vec2 u_heightTexelSize;
 uniform vec2 u_heightUVScale, u_heightUVOffset;
-uniform float u_heightTexToWorld; // height normalization -> world units
-uniform int u_toeTexRadius;       // 3–6
-uniform float u_toeHeightDelta;   // ~0.5–2.0 world units
-uniform float u_toeStrength;      // 0..1
-// ----------------------------------------------------------------------------
+uniform float u_heightTexToWorld;
+uniform int u_toeTexRadius;
+uniform float u_toeHeightDelta;
+uniform float u_toeStrength;
 
-// NEW: view-consistent, data-free toe smoothing (works even without heightmap)
-uniform float u_screenToeMul;   // try 12.0–30.0
-uniform float u_screenToeClamp; // max extra width in world units (try 0.8)
+uniform float u_screenToeMul;
+uniform float u_screenToeClamp;
 
 float hash21(vec2 p) {
   return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
@@ -94,31 +89,20 @@ vec3 geomNormal() {
   return (dot(n, v_normal) < 0.0) ? -n : n;
 }
 
-// ---- Optional heightmap helpers --------------------------------------------
 float sampleHeight(vec2 uv) {
   return texture(u_heightTex, uv).r * u_heightTexToWorld;
 }
 
-// (kept for reference; no longer used by the fix)
-// float maxRiseNearby(vec2 uv, int r){ ... }  // removed to avoid confusion
-
-// --- world <-> UV helpers for height texture sampling (FIX ADD) ---
-vec2 uvToWorld(vec2 duv) {
-  // uv = world_xz * u_heightUVScale + u_heightUVOffset
-  // => world_xz delta per uv delta = duv / u_heightUVScale (component-wise)
-  return duv / max(abs(u_heightUVScale), vec2(1e-6));
-}
+vec2 uvToWorld(vec2 duv) { return duv / max(abs(u_heightUVScale), vec2(1e-6)); }
 
 float avgWorldPerTexel() {
   vec2 wpt = abs(uvToWorld(u_heightTexelSize));
   return 0.5 * (wpt.x + wpt.y);
 }
 
-// Radial min-distance (in WORLD units) to a higher “cliff” neighborhood (FIX
-// ADD)
 float minCliffDistanceRadial(vec2 uv, int r, float riseDelta) {
-  const int MAX_R = 12;   // max steps per ray (in texels)
-  const int NUM_DIR = 12; // number of ray directions
+  const int MAX_R = 12;
+  const int NUM_DIR = 12;
   r = clamp(r, 1, MAX_R);
 
   float h0 = sampleHeight(uv);
@@ -145,9 +129,8 @@ float minCliffDistanceRadial(vec2 uv, int r, float riseDelta) {
       }
     }
   }
-  return best; // 1e9 = not found
+  return best;
 }
-// ----------------------------------------------------------------------------
 
 void main() {
   vec3 normal = geomNormal();
@@ -172,28 +155,21 @@ void main() {
   dryness += moistureVar * 0.15;
   vec3 grassColor = mix(lushGrass, u_grassDry, dryness);
 
-  // ----- Soil band (height + toe expansion) -----
   float soilWidth = max(0.01, 1.0 / max(u_soilBlendSharpness, 0.001));
 
-  // gentle noise to avoid tiled edges
   float heightNoise =
       (triplanarNoise(v_worldPos, max(0.0001, u_heightNoiseFrequency)) - 0.5) *
       u_heightNoiseStrength;
 
-  // local toe from slope (small on flats)
   float toeLocal = smoothstep(0.25, 0.9, slope);
 
-  // world-consistent “screen-space” dilation (FIX REPLACE)
   vec2 dxxz = dFdx(v_worldPos.xz);
   vec2 dyxz = dFdy(v_worldPos.xz);
-  float pxWorld =
-      max(length(dxxz), length(dyxz)); // world meters per pixel (approx)
-  float dh = max(abs(dFdx(v_worldPos.y)),
-                 abs(dFdy(v_worldPos.y))); // height change per pixel
+  float pxWorld = max(length(dxxz), length(dyxz));
+  float dh = max(abs(dFdx(v_worldPos.y)), abs(dFdy(v_worldPos.y)));
   float toeSS =
       clamp((dh / max(pxWorld, 1e-6)) * u_screenToeMul, 0.0, u_screenToeClamp);
 
-  // heightmap-based toe: isotropic distance-to-cliff (FIX REPLACE)
   float toeHM = 0.0;
   if (u_hasHeightTex == 1) {
     vec2 huv = v_worldPos.xz * u_heightUVScale + u_heightUVOffset;
@@ -208,7 +184,6 @@ void main() {
   float toeProximity =
       max(toeLocal, max(toeHM, toeSS / max(1e-6, u_soilFootHeight)));
 
-  // concave bias
   float concavityLift =
       smoothstep(0.0, 0.02, -curvature) * (0.25 * u_soilFootHeight);
 
@@ -225,7 +200,6 @@ void main() {
 
   vec3 soilBlend = mix(grassColor, u_soilColor, soilMix);
 
-  // ----- Rocks -----
   float slopeCut =
       smoothstep(u_slopeRockThreshold, u_slopeRockThreshold + 0.02, slope);
   float rockMask = clamp(pow(slopeCut, max(1.0, u_slopeRockSharpness)) +
@@ -238,7 +212,6 @@ void main() {
   rockColor = mix(rockColor, rockColor * 1.15,
                   clamp(u_rockDetailStrength * 1.4, 0.0, 1.0));
 
-  // micro normal
   vec3 microNormal = normal;
   float microDetailScale = u_detailNoiseScale * 8.0 / tileScale;
   vec2 microOffset = vec2(0.01, 0.0);
@@ -252,7 +225,6 @@ void main() {
   float microAmp = 0.15 * u_rockDetailStrength * (0.2 + 0.8 * slope);
   microNormal = normalize(normal + microGrad * microAmp);
 
-  // feature signals
   float isFlat = 1.0 - smoothstep(0.10, 0.25, slope);
   float isHigh = smoothstep(u_soilBlendHeight + 0.5, u_soilBlendHeight + 1.5,
                             v_worldPos.y);
@@ -267,67 +239,60 @@ void main() {
       clamp(rockMask + rimFactor * 0.10 - plateauFactor * 0.06 - isGully * 0.08,
             0.0, 1.0);
 
-  // Apply rock exposure modifier from ground type
   rockMask = clamp(rockMask + (u_rockExposure - 0.3) * 0.4, 0.0, 1.0);
 
   vec3 terrainColor = mix(soilBlend, rockColor, rockMask);
 
-  // Ground cracking effect (for dry Mediterranean terrain)
   if (u_crackIntensity > 0.01) {
     float crackNoise1 = noise21(world_coord * 8.0);
     float crackNoise2 = noise21(world_coord * 16.0 + vec2(42.0, 17.0));
     float crackPattern = smoothstep(0.45, 0.50, crackNoise1) *
                          smoothstep(0.40, 0.55, crackNoise2);
-    crackPattern *= (1.0 - slope * 0.8); // Less cracking on slopes
+    crackPattern *= (1.0 - slope * 0.8);
     float crackDarkening = 1.0 - crackPattern * u_crackIntensity * 0.35;
     terrainColor *= crackDarkening;
   }
 
-  // Snow coverage effect (for alpine terrain)
   if (u_snowCoverage > 0.01) {
     float snowNoise = fbm(world_coord * 0.5 + vec2(123.0, 456.0));
     float snowAccumulation = smoothstep(0.3, 0.7, snowNoise);
-    // Snow accumulates more on flat areas and less on steep slopes
+
     float slopeSnowReduction = 1.0 - smoothstep(0.15, 0.45, slope);
-    // Higher areas get more snow
+
     float heightSnowBonus = smoothstep(-0.5, 1.5, v_worldPos.y) * 0.3;
     float snowMask = clamp(snowAccumulation * slopeSnowReduction *
                                (u_snowCoverage + heightSnowBonus),
                            0.0, 1.0);
-    // Blend in snow color with brightness boost
+
     vec3 snowTinted = u_snowColor * (1.0 + detailNoise * 0.1);
     terrainColor = mix(terrainColor, snowTinted, snowMask * 0.85);
   }
 
-  // Apply grass saturation modifier
   vec3 grayLevel = vec3(dot(terrainColor, vec3(0.299, 0.587, 0.114)));
   terrainColor = mix(grayLevel, terrainColor, u_grassSaturation);
 
-  // Moisture effect on surface appearance
   float moistureEffect = u_moistureLevel;
-  // Wet surfaces are darker and more saturated
+
   float wetDarkening = 1.0 - moistureEffect * 0.15 * (1.0 - rockMask);
   terrainColor *= wetDarkening;
 
-  // albedo jitter - modulated by soil roughness
   float jitterAmp = 0.06 * (0.5 + u_soilRoughness * 0.5);
   float jitter =
       (hash21(world_coord * 0.27 + vec2(17.0, 9.0)) - 0.5) * jitterAmp;
   float brightnessVar = (moistureVar - 0.5) * 0.08 * (1.0 - rockMask);
   terrainColor *= (1.0 + jitter + brightnessVar) * u_tint;
 
-  // lighting
   vec3 L = normalize(u_lightDir);
   float ndl = max(dot(microNormal, L), 0.0);
   float ambient = 0.35;
   float fresnel =
       pow(1.0 - max(dot(microNormal, vec3(0.0, 1.0, 0.0)), 0.0), 2.0);
-  // Surface roughness affects specular - wet surfaces are shinier
+
   float surfaceRoughness = mix(0.65, 0.95, u_soilRoughness);
   surfaceRoughness = mix(surfaceRoughness, 0.45, u_moistureLevel * 0.5);
   float specContrib =
       fresnel * 0.12 * (1.0 - surfaceRoughness) * (1.0 - rockMask);
-  // Add subtle moisture-based specular for wet surfaces
+
   specContrib += u_moistureLevel * 0.08 * fresnel * (1.0 - rockMask);
   float shade = ambient + ndl * 0.75 + specContrib;
 

+ 1 - 12
assets/shaders/terrain_chunk.vert

@@ -15,12 +15,10 @@ out vec3 v_normal;
 out vec2 v_uv;
 out float v_vertexDisplacement;
 
-// Simple hash function for noise
 float hash21(vec2 p) {
   return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
 }
 
-// Value noise
 float noise21(vec2 p) {
   vec2 i = floor(p);
   vec2 f = fract(p);
@@ -34,7 +32,6 @@ float noise21(vec2 p) {
   return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
 }
 
-// Low-octave fBM (2 octaves for smooth, broad displacement)
 float fbm2(vec2 p) {
   float value = 0.0;
   float amplitude = 0.5;
@@ -46,7 +43,6 @@ float fbm2(vec2 p) {
   return value;
 }
 
-// 2D rotation matrix
 mat2 rot2(float angle) {
   float c = cos(angle);
   float s = sin(angle);
@@ -54,31 +50,24 @@ mat2 rot2(float angle) {
 }
 
 void main() {
-  // Transform to world space
+
   vec3 wp = (u_model * vec4(a_position, 1.0)).xyz;
   vec3 worldNormal = normalize(mat3(u_model) * a_normal);
 
-  // Generate stable rotation angle from noise offset
   float angle =
       fract(sin(dot(u_noiseOffset, vec2(12.9898, 78.233))) * 43758.5453) *
       6.2831853;
 
-  // Rotate noise coordinates for variation
   vec2 uv = rot2(angle) * (wp.xz + u_noiseOffset);
 
-  // Sample low-octave fBM for displacement (2 octaves)
   float h = fbm2(uv * u_heightNoiseFrequency) * 2.0 - 1.0;
 
-  // Flatness factor: high on plateaus/flat areas, low on steep faces
   float flatness = clamp(worldNormal.y, 0.0, 1.0);
 
-  // More displacement on flat areas (plateaus), less on steep slopes
   float displacementFactor = mix(0.4, 1.0, flatness);
 
-  // Clamp height noise strength to reasonable range (0.10-0.20 world units)
   float heightAmp = clamp(u_heightNoiseStrength, 0.0, 0.20);
 
-  // Apply displacement
   float displacement = h * heightAmp * displacementFactor;
   wp.y += displacement;
 

+ 7 - 18
assets/shaders/troop_shadow.frag

@@ -7,15 +7,14 @@ uniform float u_alpha;
 uniform vec3 u_color;
 uniform bool u_useTexture;
 uniform sampler2D u_texture;
-uniform vec2 u_lightDir; // normalized XZ direction of light
+uniform vec2 u_lightDir;
 
 out vec4 FragColor;
 
 void main() {
-  // v_texCoord is [0,1]. Map to [-1,1] so the quad border is around |uv| = 1.
+
   vec2 uv = v_texCoord * 2.0 - 1.0;
 
-  // Slight stretch in light direction so it’s not a perfect circle.
   vec2 dir = u_lightDir;
   if (length(dir) < 1e-4)
     dir = vec2(0.0, 1.0);
@@ -25,31 +24,25 @@ void main() {
   float along = dot(uv, dir);
   float across = dot(uv, tangent);
 
-  // Ellipse shape: close to circular with a tiny light-lean.
-  float alongScale = 1.15;  // bigger = longer soft shadow
-  float acrossScale = 0.95; // smaller = thinner shadow
+  float alongScale = 1.15;
+  float acrossScale = 0.95;
 
   float ax = along / alongScale;
   float ay = across / acrossScale;
 
-  // 0 at center, grows toward ellipse boundary (>1 outside).
   float r = length(vec2(ax, ay));
 
-  // Tiny wobble so the outline isn’t too clean.
   float wobble = 0.04 * sin(uv.x * 5.3) * sin(uv.y * 4.7);
   r = max(0.0, r + wobble);
 
-  // Blend a wide gaussian with a gentle linear falloff to get a fuzzy blob.
-  float gaussian = exp(-r * r * 2.2);       // wide, very soft bell curve
-  float feather = clamp(1.0 - r, 0.0, 1.0); // keeps a hint of shape
+  float gaussian = exp(-r * r * 2.2);
+  float feather = clamp(1.0 - r, 0.0, 1.0);
   float shadowIntensity = mix(feather, gaussian, 0.7);
-  shadowIntensity = pow(shadowIntensity, 1.35); // keep soft but less vanishing
+  shadowIntensity = pow(shadowIntensity, 1.35);
 
-  // Fade slightly with height so the model uniform stays in the program.
   float heightFade = clamp(1.0 - max(v_worldPos.y, 0.0) * 0.08, 0.6, 1.0);
   shadowIntensity *= heightFade;
 
-  // Texture tint / mask if provided.
   vec3 texColor = vec3(1.0);
   float texAlpha = 1.0;
   if (u_useTexture) {
@@ -60,14 +53,10 @@ void main() {
 
   shadowIntensity *= texAlpha;
 
-  // Base shadow color (dark but not black), intentionally very faint.
   vec3 shadowColor = vec3(0.013) * u_color * texColor;
 
-  // Keep the shadow extremely transparent.
   float finalAlpha = shadowIntensity * u_alpha * 0.95;
 
-  // Also modulate color by intensity so the blob looks soft even if blending is
-  // odd.
   vec3 finalColor = shadowColor * shadowIntensity;
 
   FragColor = vec4(finalColor, finalAlpha);

+ 14 - 19
render/equipment/armor/roman_greaves.cpp

@@ -15,41 +15,36 @@ using Render::Geom::sphereAt;
 using Render::GL::Humanoid::saturate_color;
 
 void RomanGreavesRenderer::render(const DrawContext &ctx,
-                                   const BodyFrames &frames,
-                                   const HumanoidPalette &palette,
-                                   const HumanoidAnimationContext &anim,
-                                   ISubmitter &submitter) {
+                                  const BodyFrames &frames,
+                                  const HumanoidPalette &palette,
+                                  const HumanoidAnimationContext &anim,
+                                  ISubmitter &submitter) {
   (void)anim;
 
   using HP = HumanProportions;
 
-  // Polished bronze color for greaves
   QVector3D const greaves_color =
       saturate_color(palette.metal * QVector3D(0.95F, 0.88F, 0.68F));
 
-  // Simple bent sheet greave using shin frame
   auto render_greave = [&](const AttachmentFrame &shin) {
     float const shin_r = shin.radius;
     float const shin_length = HP::LOWER_LEG_LEN;
 
-    // Greave coverage
     float const greave_start = shin_length * 0.10F;
     float const greave_end = shin_length * 0.92F;
     float const greave_len = greave_end - greave_start;
 
-    // Greave positions along shin
     QVector3D greave_top = shin.origin + shin.up * (shin_length - greave_start);
-    QVector3D greave_bottom = shin.origin + shin.up * (shin_length - greave_end);
+    QVector3D greave_bottom =
+        shin.origin + shin.up * (shin_length - greave_end);
 
-    // Use shin frame vectors for orientation
     QVector3D const &plate_up = shin.up;
     QVector3D const &plate_forward = shin.forward;
     QVector3D const &plate_right = shin.right;
 
-    // Simple 3-segment bent sheet (center + two angled sides)
     constexpr int NUM_SEGMENTS = 3;
-    float const angles[NUM_SEGMENTS] = {-0.8F, 0.0F, 0.8F};  // ~45° bend on each side
-    
+    float const angles[NUM_SEGMENTS] = {-0.8F, 0.0F, 0.8F};
+
     float const greave_offset = shin_r * 1.08F;
     float const greave_thickness = 0.006F;
     float const segment_width = shin_r * 0.55F;
@@ -59,7 +54,6 @@ void RomanGreavesRenderer::render(const DrawContext &ctx,
       float cos_a = std::cos(angle);
       float sin_a = std::sin(angle);
 
-      // Segment position curved around shin front
       QVector3D segment_offset = plate_forward * (greave_offset * cos_a) +
                                  plate_right * (greave_offset * sin_a);
 
@@ -67,11 +61,11 @@ void RomanGreavesRenderer::render(const DrawContext &ctx,
       QVector3D segment_bottom = greave_bottom + segment_offset;
       QVector3D segment_center = (segment_top + segment_bottom) * 0.5F;
 
-      // Normal pointing outward
-      QVector3D segment_normal = (plate_forward * cos_a + plate_right * sin_a).normalized();
-      QVector3D seg_tangent = QVector3D::crossProduct(plate_up, segment_normal).normalized();
+      QVector3D segment_normal =
+          (plate_forward * cos_a + plate_right * sin_a).normalized();
+      QVector3D seg_tangent =
+          QVector3D::crossProduct(plate_up, segment_normal).normalized();
 
-      // Build transform
       QMatrix4x4 seg_transform = ctx.model;
       seg_transform.translate(segment_center);
 
@@ -84,7 +78,8 @@ void RomanGreavesRenderer::render(const DrawContext &ctx,
 
       seg_transform.scale(segment_width, greave_len * 0.5F, greave_thickness);
 
-      submitter.mesh(getUnitCube(), seg_transform, greaves_color, nullptr, 1.0F, 5);
+      submitter.mesh(getUnitCube(), seg_transform, greaves_color, nullptr, 1.0F,
+                     5);
     }
   };
 

+ 1 - 2
render/equipment/weapons/bow_renderer.cpp

@@ -34,7 +34,6 @@ void BowRenderer::render(const DrawContext &ctx, const BodyFrames &frames,
   const QVector3D up(0.0F, 1.0F, 0.0F);
   const QVector3D forward(0.0F, 0.0F, 1.0F);
 
-  // Right hand now holds the bow grip; use it as anchor for the bow plane.
   QVector3D const grip = frames.hand_r.origin;
 
   float const bow_half_height = (m_config.bow_top_y - m_config.bow_bot_y) *
@@ -53,7 +52,7 @@ void BowRenderer::render(const DrawContext &ctx, const BodyFrames &frames,
   } else {
     outward.normalize();
   }
-  // Keep the bow plane close to the grip so the hand actually touches it.
+
   QVector3D const side = outward * 0.02F;
 
   float const bow_plane_x = grip.x() + m_config.bow_x + side.x();

+ 1 - 2
render/gl/humanoid/humanoid_types.h

@@ -44,8 +44,7 @@ struct BodyFrames {
   AttachmentFrame hand_r{};
   AttachmentFrame foot_l{};
   AttachmentFrame foot_r{};
-  // Shin frames for leg equipment (greaves, etc.)
-  // Origin at ankle, up points toward knee (shin direction)
+
   AttachmentFrame shin_l{};
   AttachmentFrame shin_r{};
 };

+ 0 - 1
render/humanoid/pose_controller.cpp

@@ -197,7 +197,6 @@ void HumanoidPoseController::aimBow(float draw_phase) {
 
   draw_phase = std::clamp(draw_phase, 0.0F, 1.0F);
 
-  // Keep string hand closer to bow plane so it actually reaches the chord.
   QVector3D const aim_pos(-0.02F, HP::SHOULDER_Y + 0.18F, 0.42F);
   QVector3D const draw_pos(-0.05F, HP::SHOULDER_Y + 0.12F, 0.22F);
   QVector3D const release_pos(-0.02F, HP::SHOULDER_Y + 0.20F, 0.34F);

+ 2 - 7
render/humanoid/rig.cpp

@@ -659,14 +659,11 @@ void HumanoidRendererBase::drawCommonBody(const DrawContext &ctx,
   pose.body_frames.foot_r.forward = foot_forward_r;
   pose.body_frames.foot_r.radius = foot_radius;
 
-  // Shin frames for leg equipment (greaves, etc.)
-  // Origin at ankle, up points toward knee (actual shin direction)
   auto computeShinFrame = [&](const QVector3D &ankle, const QVector3D &knee,
                               float right_sign) -> AttachmentFrame {
     AttachmentFrame shin{};
     shin.origin = ankle;
 
-    // Up vector points from ankle toward knee (shin direction)
     QVector3D shin_dir = knee - ankle;
     float shin_len = shin_dir.length();
     if (shin_len > 1e-6F) {
@@ -675,10 +672,9 @@ void HumanoidRendererBase::drawCommonBody(const DrawContext &ctx,
       shin.up = up_axis;
     }
 
-    // Forward is perpendicular to shin and points toward front of leg
-    // Use torso forward as reference, then orthogonalize against shin up
     QVector3D shin_forward = forward_axis;
-    shin_forward = shin_forward - shin.up * QVector3D::dotProduct(shin_forward, shin.up);
+    shin_forward =
+        shin_forward - shin.up * QVector3D::dotProduct(shin_forward, shin.up);
     if (shin_forward.lengthSquared() > 1e-6F) {
       shin_forward.normalize();
     } else {
@@ -686,7 +682,6 @@ void HumanoidRendererBase::drawCommonBody(const DrawContext &ctx,
     }
     shin.forward = shin_forward;
 
-    // Right is cross product of up and forward
     shin.right = QVector3D::crossProduct(shin.up, shin.forward) * right_sign;
     shin.radius = HP::LOWER_LEG_R;
 

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels