瀏覽代碼

improved armors carthage and rome

djeada 1 周之前
父節點
當前提交
5099d7e38d

+ 1 - 1
assets/maps/map_rivers.json

@@ -91,7 +91,7 @@
       "playerId": 1
     },
     {
-      "type": "archer",
+      "type": "swordsman",
       "x": 32,
       "z": 28,
       "playerId": 1

+ 3 - 2
assets/shaders/archer_carthage.frag

@@ -602,8 +602,9 @@ void main() {
     material = make_cloth_sample(base_color, Nw, Tw, Bw, v_worldPos, wet_mask,
                                  curvature);
   } else if (prefer_leather) {
+    vec3 leather_base = mix(base_color, vec3(0.44, 0.30, 0.19), 0.75);
     material = make_leather_sample(
-        base_color, Nw, Tw, Bw, v_worldPos, clamp(v_leatherTension, 0.0, 1.0),
+        leather_base, Nw, Tw, Bw, v_worldPos, clamp(v_leatherTension, 0.0, 1.0),
         clamp(v_bodyHeight, 0.0, 1.0), v_armorLayer, wet_mask, curvature);
   } else if (likely_linen) {
     material =
@@ -635,7 +636,7 @@ void main() {
   }
 
   vec3 ambient =
-      compute_ambient(material.normal) * material.albedo * material.ao * 0.35;
+      compute_ambient(material.normal) * material.albedo * material.ao * 0.42;
   vec3 bounce = vec3(0.45, 0.34, 0.25) *
                 (0.15 + 0.45 * clamp(-material.normal.y, 0.0, 1.0));
   vec3 color =

+ 45 - 3
assets/shaders/horse_archer_carthage.frag

@@ -177,8 +177,8 @@ void main() {
 
   // Material-based detection only (no fallbacks)
   bool is_brass = is_helmet;
-  bool is_steel = is_armor;
-  bool is_chain = is_armor;
+  bool is_steel = false;
+  bool is_chain = false;
   bool is_fabric = is_rider_clothing || is_saddle_blanket || is_cloak;
   bool is_leather = is_saddle_leather || is_bridle;
 
@@ -214,7 +214,49 @@ void main() {
   vec3 col = vec3(0.0);
   vec3 ambient = hemilight(N) * (0.85 + 0.15 * cavity);
 
-  if (is_horse_hide) {
+  if (is_armor) {
+    // Leather-first mix, consistent with infantry light armor
+    float leather_grain = fbm(uv * 12.0) * 0.12;
+    float linen_weave = fbm(uv * 6.0) * 0.08;
+    float scale_hint = armor_plates(v_worldPos.xz, v_worldPos.y) * 0.35;
+
+    float h = fbm(vec2(v_worldPos.y * 22.0, v_worldPos.z * 7.0));
+    N = perturb_normal_ws(N, v_worldPos, h, 0.30);
+
+    vec3 leather_tint = vec3(0.44, 0.30, 0.19);
+    vec3 linen_tint = vec3(0.86, 0.80, 0.72);
+    vec3 bronze_tint = vec3(0.62, 0.46, 0.20);
+    vec3 chain_tint = vec3(0.78, 0.80, 0.82);
+
+    float torsoBand = 1.0 - step(1.5, v_armorLayer);
+    float skirtBand = step(1.0, v_armorLayer);
+    float linenBlend = skirtBand * 0.40;
+    float bronzeBlend = torsoBand * 0.45;
+    float chainBlend = torsoBand * 0.20;
+    float leatherOverlay = skirtBand * 0.90 + torsoBand * 0.30;
+    float edge = 1.0 - clamp(dot(N, vec3(0.0, 1.0, 0.0)), 0.0, 1.0);
+    vec3 highlight = vec3(0.10, 0.08, 0.05) * smoothstep(0.3, 0.9, edge);
+
+    albedo = leather_tint;
+    albedo = mix(albedo, linen_tint, linenBlend);
+    albedo = mix(albedo, bronze_tint, bronzeBlend);
+    albedo = mix(albedo, chain_tint, chainBlend);
+    albedo = mix(albedo, leather_tint + highlight, leatherOverlay);
+
+    roughness = 0.42 - leather_grain * 0.12 + linen_weave * 0.08;
+    roughness = clamp(roughness, 0.26, 0.62);
+    F0 = mix(vec3(0.06), bronze_tint, 0.22);
+
+    float D = D_GGX(saturate(dot(N, H)), roughness);
+    float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
+    vec3 F = fresnel_schlick(VdotH, F0);
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+
+    col += ambient * albedo * 0.70;
+    col += NdotL_wrap * (albedo * (1.0 - F) + spec * 0.65);
+    col += vec3(scale_hint * 0.4 + leather_grain * 0.2 + linen_weave * 0.15);
+
+  } else if (is_horse_hide) {
     // 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

+ 50 - 3
assets/shaders/horse_spearman_carthage.frag

@@ -175,8 +175,8 @@ void main() {
 
   // Material-based detection only (no fallbacks)
   bool is_brass = is_helmet;
-  bool is_steel = is_armor;
-  bool is_chain = is_armor;
+  bool is_chain = false;
+  bool is_steel = false;
   bool is_fabric = is_rider_clothing || is_saddle_blanket;
   bool is_leather = is_saddle_leather || is_bridle;
 
@@ -212,7 +212,54 @@ void main() {
   vec3 col = vec3(0.0);
   vec3 ambient = hemilight(N) * (0.85 + 0.15 * cavity);
 
-  if (is_horse_hide) {
+  if (is_armor) {
+    // Leather-first mix with subtle bronze and linen to match infantry
+    float leather_grain = fbm(uv * 12.0) * 0.12;
+    float linen_weave = fbm(uv * 6.0) * 0.08;
+    float scale_hint = armor_plates(v_worldPos.xz, v_worldPos.y) * 0.35;
+
+    float h = fbm(vec2(v_worldPos.y * 22.0, v_worldPos.z * 7.0));
+    N = perturb_normal_ws(N, v_worldPos, h, 0.30);
+
+    vec3 leather_tint = vec3(0.44, 0.30, 0.19);
+    vec3 linen_tint = vec3(0.86, 0.80, 0.72);
+    vec3 bronze_tint = vec3(0.62, 0.46, 0.20);
+    vec3 chain_tint = vec3(0.78, 0.80, 0.82);
+
+    float torsoBand = 1.0 - step(1.5, v_armorLayer);
+    float skirtBand = step(1.0, v_armorLayer);
+    float linenBlend = skirtBand * 0.40;
+    float bronzeBlend = torsoBand * 0.45;
+    float chainBlend = torsoBand * 0.20;
+    float leatherOverlay = skirtBand * 0.90 + torsoBand * 0.30;
+    float edge = 1.0 - clamp(dot(N, vec3(0.0, 1.0, 0.0)), 0.0, 1.0);
+    vec3 highlight = vec3(0.10, 0.08, 0.05) * smoothstep(0.3, 0.9, edge);
+
+    albedo = leather_tint;
+    albedo = mix(albedo, linen_tint, linenBlend);
+    albedo = mix(albedo, bronze_tint, bronzeBlend);
+    albedo = mix(albedo, chain_tint, chainBlend);
+    albedo = mix(albedo, leather_tint + highlight, leatherOverlay);
+
+    float leather_depth = clamp(
+        leatherOverlay * 0.8 + linenBlend * 0.2 + bronzeBlend * 0.15, 0.0, 1.0);
+    albedo = mix(albedo, albedo * 0.88 + vec3(0.04, 0.03, 0.02),
+                 leather_depth * 0.35);
+
+    roughness = 0.42 - leather_grain * 0.12 + linen_weave * 0.08;
+    roughness = clamp(roughness, 0.26, 0.62);
+    F0 = mix(vec3(0.06), bronze_tint, 0.22);
+
+    float D = D_GGX(saturate(dot(N, H)), roughness);
+    float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
+    vec3 F = fresnel_schlick(VdotH, F0);
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+
+    col += ambient * albedo * 0.70;
+    col += NdotL_wrap * (albedo * (1.0 - F) + spec * 0.65);
+    col += vec3(scale_hint * 0.4 + leather_grain * 0.2 + linen_weave * 0.15);
+
+  } else if (is_horse_hide) {
     // 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

+ 57 - 4
assets/shaders/horse_swordsman_carthage.frag

@@ -161,7 +161,7 @@ void main() {
   // 5=rider clothing, 6=horse hide, 7=horse mane, 8=horse hoof,
   // 9=saddle leather, 10=bridle, 11=saddle blanket
   bool is_rider_skin = (u_materialId == 0);
-  bool is_armor = (u_materialId == 1);
+  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);
@@ -175,8 +175,8 @@ void main() {
 
   // Material-based detection only (no fallbacks)
   bool is_brass = is_helmet;
-  bool is_steel = is_armor;
-  bool is_chain = is_armor;
+  bool is_steel = false;
+  bool is_chain = false;
   bool is_fabric = is_rider_clothing || is_saddle_blanket;
   bool is_leather = is_saddle_leather || is_bridle;
 
@@ -212,7 +212,60 @@ void main() {
   vec3 col = vec3(0.0);
   vec3 ambient = hemilight(N) * (0.85 + 0.15 * cavity);
 
-  if (is_horse_hide) {
+  if (is_body_armor) {
+    // Bronze + chain + linen mix to match infantry look.
+    float brushed =
+        abs(sin(v_worldPos.y * 55.0)) * 0.02 + noise(uv * 28.0) * 0.015;
+    float plates = armor_plates(v_worldPos.xz, v_worldPos.y);
+    float rings = chainmail_rings(v_worldPos.xz);
+    float linen = fbm(uv * 5.0);
+
+    // bump from light hammering
+    float h = fbm(vec2(v_worldPos.y * 18.0, v_worldPos.z * 6.0));
+    N = perturb_normal_ws(N, v_worldPos, h, 0.32);
+
+    vec3 bronze_tint = vec3(0.62, 0.46, 0.20);
+    vec3 steel_tint = vec3(0.68, 0.70, 0.74);
+    vec3 linen_tint = vec3(0.86, 0.80, 0.72);
+    vec3 leather_tint = vec3(0.38, 0.25, 0.15);
+
+    float torsoBand = 1.0 - step(1.5, v_armorLayer);
+    float skirtBand = step(1.0, v_armorLayer);
+    float mailBlend =
+        clamp(smoothstep(0.15, 0.85, rings + cavity * 0.25), 0.15, 1.0) *
+        torsoBand;
+    float cuirassBlend = torsoBand;
+    float leatherBlend = skirtBand * 0.65;
+    float linenBlend = skirtBand * 0.45;
+
+    vec3 bronze = mix(bronze_tint, base_color, 0.40);
+    vec3 chain_col = mix(steel_tint, base_color, 0.25);
+    vec3 linen_col = mix(linen_tint, base_color, 0.20);
+    vec3 leather_col = mix(leather_tint, base_color, 0.30);
+
+    albedo = bronze;
+    albedo = mix(albedo, chain_col, mailBlend);
+    albedo = mix(albedo, linen_col, linenBlend);
+    albedo = mix(albedo, leather_col, leatherBlend);
+
+    // bias toward brighter metal luma
+    float armor_luma = dot(albedo, vec3(0.299, 0.587, 0.114));
+    albedo = mix(albedo, albedo * 1.20, smoothstep(0.30, 0.65, armor_luma));
+
+    roughness = 0.32 + brushed * 1.2;
+    roughness = clamp(roughness, 0.18, 0.55);
+    F0 = mix(vec3(0.74), albedo, 0.25);
+
+    float D = D_GGX(saturate(dot(N, H)), roughness);
+    float G = G_Smith(saturate(dot(N, V)), saturate(dot(N, L)), roughness);
+    vec3 F = fresnel_schlick(VdotH, F0);
+    vec3 spec = (D * G) * F / max(1e-5, 4.0 * NdotV * NdotL);
+
+    col += ambient * mix(vec3(1.0), albedo, 0.25);
+    col += NdotL_wrap * (spec * 1.35);
+    col += vec3(plates) * 0.35 + vec3(rings * 0.25) + vec3(linen * linenBlend);
+
+  } else if (is_horse_hide) {
     // 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

+ 35 - 18
assets/shaders/spearman_carthage.frag

@@ -324,44 +324,61 @@ void main() {
   if (is_helmet) {
     mat = sample_bronze_helmet(base_color, v_worldPos, Nw, Tw, Bw);
   } else if (is_armor) {
+    vec3 leather_base = vec3(0.44, 0.30, 0.19);
+    vec3 linen_base = vec3(0.86, 0.80, 0.72);
+    vec3 bronze_base = vec3(0.62, 0.46, 0.20);
+    vec3 chain_base = vec3(0.78, 0.80, 0.82);
+
+    MaterialSample leather =
+        sample_leather(leather_base, v_worldPos * 0.85, Nw, Tw, Bw);
     MaterialSample linen =
-        sample_linen(base_color, v_worldPos * 1.1, Nw, Tw, Bw);
+        sample_linen(linen_base, v_worldPos * 1.0, Nw, Tw, Bw);
     MaterialSample scales =
-        sample_bronze_scales(base_color, v_worldPos * 1.0, Nw, Tw, Bw);
+        sample_bronze_scales(bronze_base, v_worldPos * 0.95, Nw, Tw, Bw);
     MaterialSample mail =
-        sample_chainmail(base_color, v_worldPos * 0.9, Nw, Tw, Bw);
-    MaterialSample leather =
-        sample_leather(base_color, v_worldPos * 0.8, Nw, Tw, Bw);
+        sample_chainmail(chain_base, v_worldPos * 0.9, Nw, Tw, Bw);
 
     float torsoBand = 1.0 - step(1.5, v_armorLayer);
     float skirtBand = step(1.0, v_armorLayer);
     float mailBlend =
         clamp(smoothstep(0.25, 0.85, v_chainmailPhase + v_steelWear * 0.35),
               0.0, 1.0) *
-        torsoBand;
-    float scaleBlend = clamp(0.35 + v_steelWear * 0.6, 0.0, 1.0) * torsoBand;
-    float leatherBlend = skirtBand * 0.75;
-
-    // Blend linen base with bronze scales and chainmail overlays
-    mat.color = linen.color;
+        torsoBand * 0.30;
+    float scaleBlend =
+        clamp(0.28 + v_steelWear * 0.55, 0.0, 1.0) * torsoBand * 0.55;
+    float linenBlend = skirtBand * 0.40;
+    float leatherOverlay = skirtBand * 0.90 + torsoBand * 0.30;
+
+    // subtle edge tint to lift highlights
+    float edge = 1.0 - clamp(dot(Nw, vec3(0.0, 1.0, 0.0)), 0.0, 1.0);
+    vec3 highlight = vec3(0.10, 0.08, 0.05) * smoothstep(0.3, 0.9, edge);
+
+    // Leather-first blend with lighter linen skirt and subtle bronze/chain
+    mat.color = leather.color;
+    mat.color = mix(mat.color, linen.color, linenBlend);
     mat.color = mix(mat.color, scales.color, scaleBlend);
     mat.color = mix(mat.color, mail.color, mailBlend);
-    mat.color = mix(mat.color, leather.color, leatherBlend);
+    mat.color = mix(mat.color, leather.color + highlight, leatherOverlay);
+
+    float leather_depth = clamp(
+        leatherOverlay * 0.8 + linenBlend * 0.2 + scaleBlend * 0.15, 0.0, 1.0);
+    mat.color = mix(mat.color, mat.color * 0.88 + vec3(0.04, 0.03, 0.02),
+                    leather_depth * 0.35);
 
-    mat.normal = linen.normal;
+    mat.normal = leather.normal;
+    mat.normal = normalize(mix(mat.normal, linen.normal, linenBlend));
     mat.normal = normalize(mix(mat.normal, scales.normal, scaleBlend));
     mat.normal = normalize(mix(mat.normal, mail.normal, mailBlend));
-    mat.normal = normalize(mix(mat.normal, leather.normal, leatherBlend));
 
-    mat.roughness = linen.roughness;
+    mat.roughness = leather.roughness;
+    mat.roughness = mix(mat.roughness, linen.roughness, linenBlend);
     mat.roughness = mix(mat.roughness, scales.roughness, scaleBlend);
     mat.roughness = mix(mat.roughness, mail.roughness, mailBlend);
-    mat.roughness = mix(mat.roughness, leather.roughness, leatherBlend);
 
-    mat.F0 = linen.F0;
+    mat.F0 = leather.F0;
+    mat.F0 = mix(mat.F0, linen.F0, linenBlend);
     mat.F0 = mix(mat.F0, scales.F0, scaleBlend);
     mat.F0 = mix(mat.F0, mail.F0, mailBlend);
-    mat.F0 = mix(mat.F0, leather.F0, leatherBlend);
   } else if (is_weapon) {
     if (v_bodyHeight > 0.55) {
       mat = sample_steel(base_color, v_worldPos * 1.4, Nw, Tw, Bw);

+ 7 - 2
assets/shaders/swordsman_roman_republic.frag

@@ -388,12 +388,17 @@ void main() {
   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
+
   // 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.10 : 0.30);
-  float diff = max(n_dot_l * (1.0 - wrap_amount) + wrap_amount, 0.18);
+  float wrap_amount = is_helmet ? 0.08 : (is_armor ? 0.08 : 0.30);
+  float diff = max(n_dot_l * (1.0 - wrap_amount) + wrap_amount, 0.16);
 
   // Extra contrast for polished steel
   if (is_helmet || is_armor) {

+ 2 - 2
render/entity/horse_spearman_renderer_base.cpp

@@ -49,7 +49,7 @@ HorseSpearmanRendererBase::HorseSpearmanRendererBase(
 
 auto HorseSpearmanRendererBase::get_proportion_scaling() const -> QVector3D {
 
-  return QVector3D{0.88F, 0.86F, 0.90F};
+  return QVector3D{0.84F, 0.84F, 0.86F};
 }
 
 auto HorseSpearmanRendererBase::get_mount_scale() const -> float {
@@ -59,7 +59,7 @@ auto HorseSpearmanRendererBase::get_mount_scale() const -> float {
 void HorseSpearmanRendererBase::adjust_variation(
     const DrawContext &, uint32_t, VariationParams &variation) const {
   variation.height_scale = 0.90F;
-  variation.bulk_scale = 0.78F;
+  variation.bulk_scale = 0.74F;
   variation.stance_width = 0.60F;
   variation.arm_swing_amp = 0.40F;
   variation.walk_speed_mult = 1.0F;

+ 1 - 1
render/entity/nations/carthage/horse_spearman_renderer.cpp

@@ -18,7 +18,7 @@ auto make_horse_spearman_config() -> HorseSpearmanRendererConfig {
   config.spear_equipment_id = "spear";
   config.helmet_equipment_id = "carthage_heavy";
   config.armor_equipment_id = "armor_heavy_carthage";
-  config.shoulder_equipment_id = "carthage_shoulder_cover";
+  config.shoulder_equipment_id = "carthage_shoulder_cover_cavalry";
   config.has_shoulder = true;
   config.helmet_offset_moving = 0.04F;
   config.horse_attachments.emplace_back(

+ 51 - 2
render/entity/nations/carthage/horse_swordsman_renderer.cpp

@@ -4,22 +4,71 @@
 #include "../../../equipment/horse/tack/reins_renderer.h"
 #include "../../../gl/backend.h"
 #include "../../../gl/shader.h"
+#include "../../../humanoid/style_palette.h"
 #include "../../../scene_renderer.h"
 #include "../../../submitter.h"
 #include "../../mounted_knight_renderer_base.h"
+#include "swordsman_style.h"
 
 #include <memory>
+#include <optional>
 
 namespace Render::GL::Carthage {
 namespace {
 
+constexpr float k_team_mix_weight = 0.6F;
+constexpr float k_style_mix_weight = 0.4F;
+
+auto carthage_style() -> KnightStyleConfig {
+  KnightStyleConfig style;
+  style.cloth_color = QVector3D(0.15F, 0.36F, 0.55F);
+  style.leather_color = QVector3D(0.32F, 0.22F, 0.12F);
+  style.leather_dark_color = QVector3D(0.20F, 0.14F, 0.09F);
+  style.metal_color = QVector3D(0.70F, 0.68F, 0.52F);
+  style.shader_id = "horse_swordsman_carthage";
+  return style;
+}
+
+class CarthageMountedKnightRenderer : public MountedKnightRendererBase {
+public:
+  using MountedKnightRendererBase::MountedKnightRendererBase;
+
+  void get_variant(const DrawContext &ctx, uint32_t seed,
+                   HumanoidVariant &v) const override {
+    MountedKnightRendererBase::get_variant(ctx, seed, v);
+    const KnightStyleConfig style = carthage_style();
+    QVector3D const team_tint = resolveTeamTint(ctx);
+
+    auto apply_color = [&](const std::optional<QVector3D> &override_color,
+                           QVector3D &target) {
+      target = Render::GL::Humanoid::mix_palette_color(
+          target, override_color, team_tint, k_team_mix_weight,
+          k_style_mix_weight);
+    };
+
+    apply_color(style.cloth_color, v.palette.cloth);
+    apply_color(style.leather_color, v.palette.leather);
+    apply_color(style.leather_dark_color, v.palette.leatherDark);
+    apply_color(style.metal_color, v.palette.metal);
+  }
+
+  auto resolve_shader_key(const DrawContext &ctx) const -> QString {
+    const KnightStyleConfig style = carthage_style();
+    if (!style.shader_id.empty()) {
+      return QString::fromStdString(style.shader_id);
+    }
+    return MountedKnightRendererBase::resolve_shader_key(ctx);
+  }
+};
+
 auto makeMountedKnightConfig() -> MountedKnightRendererConfig {
   MountedKnightRendererConfig config;
   config.sword_equipment_id = "sword_carthage";
   config.shield_equipment_id = "shield_carthage_cavalry";
   config.helmet_equipment_id = "carthage_heavy";
   config.armor_equipment_id = "armor_heavy_carthage";
-  config.shoulder_equipment_id = "carthage_shoulder_cover";
+  config.shoulder_equipment_id = "carthage_shoulder_cover_cavalry";
+  config.metal_color = QVector3D(0.70F, 0.68F, 0.52F);
   config.has_shoulder = true;
   config.helmet_offset_moving = 0.03F;
   config.horse_attachments.emplace_back(
@@ -34,7 +83,7 @@ void registerMountedKnightRenderer(EntityRendererRegistry &registry) {
   registry.register_renderer(
       "troops/carthage/horse_swordsman",
       [](const DrawContext &ctx, ISubmitter &out) {
-        static MountedKnightRendererBase const static_renderer(
+        static CarthageMountedKnightRenderer const static_renderer(
             makeMountedKnightConfig());
         Shader *horse_swordsman_shader = nullptr;
         if (ctx.backend != nullptr) {

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

@@ -114,7 +114,7 @@ class SpearmanRenderer : public HumanoidRendererBase {
 public:
   auto get_proportion_scaling() const -> QVector3D override {
 
-    return {0.94F, 1.04F, 0.92F};
+    return {0.82F, 0.94F, 0.84F};
   }
 
   void adjust_variation(const DrawContext &, uint32_t,

+ 1 - 1
render/entity/nations/carthage/swordsman_renderer.cpp

@@ -99,7 +99,7 @@ class KnightRenderer : public HumanoidRendererBase {
 public:
   auto get_proportion_scaling() const -> QVector3D override {
 
-    return {0.750F, 1.05F, 0.50F};
+    return {0.70F, 1.03F, 0.46F};
   }
 
 private:

+ 1 - 1
render/entity/nations/roman/horse_spearman_renderer.cpp

@@ -18,7 +18,7 @@ auto make_horse_spearman_config() -> HorseSpearmanRendererConfig {
   config.spear_equipment_id = "spear";
   config.helmet_equipment_id = "roman_heavy";
   config.armor_equipment_id = "roman_heavy_armor";
-  config.shoulder_equipment_id = "roman_shoulder_cover";
+  config.shoulder_equipment_id = "roman_shoulder_cover_cavalry";
   config.has_shoulder = true;
   config.helmet_offset_moving = 0.06F;
   config.horse_attachments.emplace_back(

+ 1 - 1
render/entity/nations/roman/horse_swordsman_renderer.cpp

@@ -19,7 +19,7 @@ auto makeMountedKnightConfig() -> MountedKnightRendererConfig {
   config.shield_equipment_id = "shield_roman";
   config.helmet_equipment_id = "roman_heavy";
   config.armor_equipment_id = "roman_heavy_armor";
-  config.shoulder_equipment_id = "roman_shoulder_cover";
+  config.shoulder_equipment_id = "roman_shoulder_cover_cavalry";
   config.has_shoulder = true;
   config.helmet_offset_moving = 0.035F;
   config.horse_attachments.emplace_back(

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

@@ -112,7 +112,7 @@ struct SpearmanExtras {
 class SpearmanRenderer : public HumanoidRendererBase {
 public:
   auto get_proportion_scaling() const -> QVector3D override {
-    return {1.02F, 0.96F, 0.92F};
+    return {0.70F, 0.80F, 0.76F};
   }
 
 private:

+ 3 - 3
render/entity/nations/roman/swordsman_renderer.cpp

@@ -97,9 +97,9 @@ struct KnightExtras {
 
 class KnightRenderer : public HumanoidRendererBase {
 public:
-  static constexpr float kShoulderWidth = 1.02F;
-  static constexpr float kTorsoScale = 0.94F;
-  static constexpr float kArmScale = 0.88F;
+  static constexpr float kShoulderWidth = 0.70F;
+  static constexpr float kTorsoScale = 0.78F;
+  static constexpr float kArmScale = 0.76F;
 
   auto get_proportion_scaling() const -> QVector3D override {
 

+ 10 - 10
render/equipment/armor/armor_heavy_carthage.cpp

@@ -50,16 +50,16 @@ void ArmorHeavyCarthageRenderer::render(const DrawContext &ctx,
       waist.radius > 0.0F ? waist.radius : torso.radius * 0.90F;
   float const head_r = head.radius > 0.0F ? head.radius : torso.radius * 0.60F;
 
-  QVector3D top = torso.origin + up * (torso_r * 0.60F);
+  QVector3D top = torso.origin + up * (torso_r * 0.64F);
   QVector3D head_guard = head.origin - head_up * (head_r * 1.35F);
   if (QVector3D::dotProduct(top - head_guard, up) > 0.0F) {
     top = head_guard - up * (torso_r * 0.06F);
   }
 
-  QVector3D bottom = waist.origin - waist_up * (waist_r * 0.20F) -
-                     forward * (torso_r * 0.015F);
-  QVector3D chainmail_bottom = waist.origin - waist_up * (waist_r * 0.18F) -
-                               forward * (torso_r * 0.020F);
+  QVector3D bottom = waist.origin - waist_up * (waist_r * 0.32F) -
+                     forward * (torso_r * 0.018F);
+  QVector3D chainmail_bottom = waist.origin - waist_up * (waist_r * 0.28F) -
+                               forward * (torso_r * 0.024F);
 
   QVector3D bronze_color = QVector3D(0.72F, 0.53F, 0.28F);
   QVector3D bronze_core = bronze_color * 0.92F;
@@ -75,14 +75,14 @@ void ArmorHeavyCarthageRenderer::render(const DrawContext &ctx,
                    color, nullptr, 1.0F, material_id);
   };
 
-  draw_torso(top, chainmail_bottom, torso_r * 1.06F, chainmail_color, 1.05F,
-             1.02F, 1);
+  draw_torso(top, chainmail_bottom, torso_r * 1.10F, chainmail_color, 1.07F,
+             1.04F, 1);
 
   draw_torso(top + forward * (torso_r * 0.02F),
-             bottom + forward * (torso_r * 0.02F), torso_r * 1.10F,
-             bronze_color, 1.08F, 1.00F, 1);
+             bottom + forward * (torso_r * 0.02F), torso_r * 1.16F,
+             bronze_color, 1.10F, 1.04F, 1);
 
-  draw_torso(top, bottom, torso_r * 1.04F, bronze_core, 1.03F, 0.95F, 1);
+  draw_torso(top, bottom, torso_r * 1.10F, bronze_core, 1.05F, 1.00F, 1);
 }
 
 } // namespace Render::GL

+ 9 - 9
render/equipment/armor/armor_light_carthage.cpp

@@ -51,7 +51,7 @@ void ArmorLightCarthageRenderer::render(const DrawContext &ctx,
   QVector3D waist_up =
       (waist.up.lengthSquared() > 1e-6F) ? waist.up.normalized() : up;
 
-  QVector3D top = torso.origin + up * (torso_r * 0.35F);
+  QVector3D top = torso.origin + up * (torso_r * 0.50F);
   QVector3D head_guard =
       head.origin -
       head_up * ((head_r > 0.0F ? head_r : torso_r * 0.6F) * 1.45F);
@@ -59,11 +59,11 @@ void ArmorLightCarthageRenderer::render(const DrawContext &ctx,
     top = head_guard - up * (torso_r * 0.05F);
   }
 
-  QVector3D bottom =
-      waist.origin + waist_up * (waist_r * 0.03F) - forward * (torso_r * 0.01F);
+  QVector3D bottom = waist.origin - waist_up * (waist_r * 0.24F) -
+                     forward * (torso_r * 0.016F);
 
-  float main_radius = torso_r * 0.96F;
-  float const main_depth = torso_depth * 0.92F;
+  float main_radius = torso_r * 1.26F;
+  float const main_depth = torso_depth * 1.24F;
 
   QMatrix4x4 cuirass = cylinderBetween(ctx.model, top, bottom, main_radius);
   cuirass.scale(1.0F, 1.0F, std::max(0.15F, main_depth / main_radius));
@@ -90,8 +90,8 @@ void ArmorLightCarthageRenderer::render(const DrawContext &ctx,
       bottom + forward * (torso_depth * 0.38F) + up * (torso_r * 0.03F);
   QMatrix4x4 front_panel = cylinderBetween(ctx.model, front_panel_top,
                                            front_panel_bottom, torso_r * 0.48F);
-  front_panel.scale(0.95F, 1.0F,
-                    std::max(0.12F, (torso_depth * 0.5F) / (torso_r * 0.48F)));
+  front_panel.scale(1.18F, 1.0F,
+                    std::max(0.22F, (torso_depth * 0.76F) / (torso_r * 0.76F)));
   submitter.mesh(torso_mesh != nullptr ? torso_mesh : getUnitTorso(),
                  front_panel, leather_highlight, nullptr, 1.0F, 1);
 
@@ -101,8 +101,8 @@ void ArmorLightCarthageRenderer::render(const DrawContext &ctx,
       bottom - forward * (torso_depth * 0.34F) + up * (torso_r * 0.02F);
   QMatrix4x4 back_panel = cylinderBetween(ctx.model, back_panel_top,
                                           back_panel_bottom, torso_r * 0.50F);
-  back_panel.scale(0.96F, 1.0F,
-                   std::max(0.12F, (torso_depth * 0.45F) / (torso_r * 0.50F)));
+  back_panel.scale(1.18F, 1.0F,
+                   std::max(0.22F, (torso_depth * 0.74F) / (torso_r * 0.80F)));
   submitter.mesh(torso_mesh != nullptr ? torso_mesh : getUnitTorso(),
                  back_panel, leather_shadow, nullptr, 1.0F, 1);
 }

+ 6 - 6
render/equipment/armor/carthage_shoulder_cover.cpp

@@ -29,24 +29,24 @@ void CarthageShoulderCoverRenderer::render(const DrawContext &ctx,
                                  const QVector3D &outward) {
     float const upper_arm_r = HP::UPPER_ARM_R;
 
-    QVector3D upper_pos =
-        shoulder_pos + outward * 0.012F + QVector3D(0.0F, 0.108F, 0.0F);
+    QVector3D upper_pos = shoulder_pos + outward * (0.012F * m_outward_scale) +
+                          QVector3D(0.0F, 0.108F, 0.0F);
     QMatrix4x4 upper = ctx.model;
     upper.translate(upper_pos);
     upper.scale(upper_arm_r * 1.75F, upper_arm_r * 0.38F, upper_arm_r * 1.55F);
     submitter.mesh(getUnitSphere(), upper, leather_color * 1.05F, nullptr, 1.0F,
                    1);
 
-    QVector3D lower_pos =
-        upper_pos - QVector3D(0.0F, 0.045F, 0.0F) + outward * 0.010F;
+    QVector3D lower_pos = upper_pos - QVector3D(0.0F, 0.045F, 0.0F) +
+                          outward * (0.010F * m_outward_scale);
     QMatrix4x4 lower = ctx.model;
     lower.translate(lower_pos);
     lower.scale(upper_arm_r * 1.58F, upper_arm_r * 0.34F, upper_arm_r * 1.40F);
     submitter.mesh(getUnitSphere(), lower, leather_color * 0.96F, nullptr, 1.0F,
                    1);
 
-    QVector3D trim_pos =
-        lower_pos - QVector3D(0.0F, 0.025F, 0.0F) + outward * 0.006F;
+    QVector3D trim_pos = lower_pos - QVector3D(0.0F, 0.025F, 0.0F) +
+                         outward * (0.006F * m_outward_scale);
     QMatrix4x4 trim = ctx.model;
     trim.translate(trim_pos);
     trim.scale(upper_arm_r * 1.42F, upper_arm_r * 0.18F, upper_arm_r * 1.25F);

+ 5 - 1
render/equipment/armor/carthage_shoulder_cover.h

@@ -8,12 +8,16 @@ namespace Render::GL {
 
 class CarthageShoulderCoverRenderer : public IEquipmentRenderer {
 public:
-  CarthageShoulderCoverRenderer() = default;
+  explicit CarthageShoulderCoverRenderer(float outward_scale = 1.0F)
+      : m_outward_scale(outward_scale) {}
 
   void render(const DrawContext &ctx, const BodyFrames &frames,
               const HumanoidPalette &palette,
               const HumanoidAnimationContext &anim,
               ISubmitter &submitter) override;
+
+private:
+  float m_outward_scale = 1.0F;
 };
 
 } // namespace Render::GL

+ 12 - 12
render/equipment/armor/roman_armor.cpp

@@ -63,17 +63,17 @@ void RomanHeavyArmorRenderer::render(const DrawContext &ctx,
       waist.radius > 0.0F ? waist.radius : torso.radius * 0.88F;
   float const head_r = head.radius > 0.0F ? head.radius : torso.radius * 0.58F;
 
-  QVector3D top = torso.origin + up * (torso_r * 0.48F);
+  QVector3D top = torso.origin + up * (torso_r * 0.60F);
   QVector3D head_guard = head.origin - head_up * (head_r * 1.30F);
   if (QVector3D::dotProduct(top - head_guard, up) > 0.0F) {
     top = head_guard - up * (torso_r * 0.05F);
   }
 
-  QVector3D bottom =
-      waist.origin + waist_up * (waist_r * 0.08F) - forward * (torso_r * 0.01F);
+  QVector3D bottom = waist.origin - waist_up * (waist_r * 0.45F) -
+                     forward * (torso_r * 0.016F);
 
-  QMatrix4x4 plates = cylinderBetween(ctx.model, top, bottom, torso_r * 1.02F);
-  plates.scale(1.05F, 1.0F, depth_scale_for(0.86F));
+  QMatrix4x4 plates = cylinderBetween(ctx.model, top, bottom, torso_r * 1.24F);
+  plates.scale(1.18F, 1.0F, depth_scale_for(1.10F));
   Mesh *torso_mesh = torso_mesh_without_bottom_cap();
   submitter.mesh(torso_mesh != nullptr ? torso_mesh : getUnitTorso(), plates,
                  steel_color, nullptr, 1.0F, 1);
@@ -151,26 +151,26 @@ void RomanLightArmorRenderer::render(const DrawContext &ctx,
       waist.radius > 0.0F ? waist.radius : torso.radius * 0.86F;
   float const head_r = head.radius > 0.0F ? head.radius : torso.radius * 0.58F;
 
-  QVector3D top = torso.origin + up * (torso_r * 0.42F);
+  QVector3D top = torso.origin + up * (torso_r * 0.54F);
   QVector3D head_guard = head.origin - head_up * (head_r * 1.35F);
   if (QVector3D::dotProduct(top - head_guard, up) > 0.0F) {
     top = head_guard - up * (torso_r * 0.06F);
   }
 
-  QVector3D bottom = waist.origin - waist_up * (waist_r * 0.15F);
+  QVector3D bottom = waist.origin - waist_up * (waist_r * 0.40F);
 
   QMatrix4x4 chainmail =
-      cylinderBetween(ctx.model, top, bottom, torso_r * 0.98F);
-  chainmail.scale(1.02F, 1.0F, depth_scale_for(0.82F));
+      cylinderBetween(ctx.model, top, bottom, torso_r * 1.16F);
+  chainmail.scale(1.12F, 1.0F, depth_scale_for(1.02F));
   Mesh *torso_mesh = torso_mesh_without_bottom_cap();
   submitter.mesh(torso_mesh != nullptr ? torso_mesh : getUnitTorso(), chainmail,
                  chainmail_color, nullptr, 1.0F, 1);
 
   QVector3D chest_center =
       torso.origin + up * (torso_r * 0.12F) + forward * (torso_depth * 0.48F);
-  float const plate_width = torso_r * 0.85F;
-  float const plate_height = torso_r * 0.65F;
-  float const plate_depth = torso_r * 0.18F;
+  float const plate_width = torso_r * 1.10F;
+  float const plate_height = torso_r * 0.96F;
+  float const plate_depth = torso_r * 0.30F;
 
   QMatrix4x4 pectorale = ctx.model;
   pectorale.translate(chest_center);

+ 6 - 6
render/equipment/armor/roman_shoulder_cover.cpp

@@ -30,22 +30,22 @@ void RomanShoulderCoverRenderer::render(const DrawContext &ctx,
                                  const QVector3D &outward) {
     float const upper_arm_r = HP::UPPER_ARM_R;
 
-    QVector3D top_pos =
-        shoulder_pos + outward * 0.032F + QVector3D(0.0F, 0.110F, 0.0F);
+    QVector3D top_pos = shoulder_pos + outward * (0.032F * m_outward_scale) +
+                        QVector3D(0.0F, 0.110F, 0.0F);
     QMatrix4x4 top = ctx.model;
     top.translate(top_pos);
     top.scale(upper_arm_r * 1.70F, upper_arm_r * 0.42F, upper_arm_r * 1.50F);
     submitter.mesh(getUnitSphere(), top, metal_base, nullptr, 1.0F, 1);
 
-    QVector3D under_pos =
-        top_pos - QVector3D(0.0F, 0.040F, 0.0F) + outward * 0.020F;
+    QVector3D under_pos = top_pos - QVector3D(0.0F, 0.040F, 0.0F) +
+                          outward * (0.020F * m_outward_scale);
     QMatrix4x4 under = ctx.model;
     under.translate(under_pos);
     under.scale(upper_arm_r * 1.55F, upper_arm_r * 0.30F, upper_arm_r * 1.32F);
     submitter.mesh(getUnitSphere(), under, metal_dark, nullptr, 1.0F, 1);
 
-    QVector3D rim_pos =
-        under_pos - QVector3D(0.0F, 0.025F, 0.0F) + outward * 0.014F;
+    QVector3D rim_pos = under_pos - QVector3D(0.0F, 0.025F, 0.0F) +
+                        outward * (0.014F * m_outward_scale);
     QMatrix4x4 rim = ctx.model;
     rim.translate(rim_pos);
     rim.scale(upper_arm_r * 1.40F, upper_arm_r * 0.14F, upper_arm_r * 1.18F);

+ 5 - 1
render/equipment/armor/roman_shoulder_cover.h

@@ -8,11 +8,15 @@ namespace Render::GL {
 
 class RomanShoulderCoverRenderer : public IEquipmentRenderer {
 public:
-  RomanShoulderCoverRenderer() = default;
+  explicit RomanShoulderCoverRenderer(float outward_scale = 1.0F)
+      : m_outward_scale(outward_scale) {}
   void render(const DrawContext &ctx, const BodyFrames &frames,
               const HumanoidPalette &palette,
               const HumanoidAnimationContext &anim,
               ISubmitter &submitter) override;
+
+private:
+  float m_outward_scale = 1.0F;
 };
 
 } // namespace Render::GL

+ 10 - 0
render/equipment/register_equipment.cpp

@@ -120,12 +120,22 @@ void registerBuiltInEquipment() {
   auto roman_shoulder_cover = std::make_shared<RomanShoulderCoverRenderer>();
   registry.registerEquipment(EquipmentCategory::Armor, "roman_shoulder_cover",
                              roman_shoulder_cover);
+  auto roman_shoulder_cover_cavalry =
+      std::make_shared<RomanShoulderCoverRenderer>(1.8F);
+  registry.registerEquipment(EquipmentCategory::Armor,
+                             "roman_shoulder_cover_cavalry",
+                             roman_shoulder_cover_cavalry);
 
   auto carthage_shoulder_cover =
       std::make_shared<CarthageShoulderCoverRenderer>();
   registry.registerEquipment(EquipmentCategory::Armor,
                              "carthage_shoulder_cover",
                              carthage_shoulder_cover);
+  auto carthage_shoulder_cover_cavalry =
+      std::make_shared<CarthageShoulderCoverRenderer>(1.8F);
+  registry.registerEquipment(EquipmentCategory::Armor,
+                             "carthage_shoulder_cover_cavalry",
+                             carthage_shoulder_cover_cavalry);
   CloakConfig carthage_cloak_config;
   carthage_cloak_config.primary_color = QVector3D(0.14F, 0.38F, 0.54F);
   carthage_cloak_config.trim_color = QVector3D(0.75F, 0.66F, 0.42F);