Stronger liquid swell, flow along rivers, brighter lava and new lava texture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-05 15:08:51 +01:00
1 parent a701212299
commit 1094c7c5f2
2 files changed
+63 -29

No files matched your search

+19 -11
View File
@@ -622,19 +622,27 @@ fn gen_atlas() -> Image {
_ => shade((24, 50, 88), d),
})
});
// lava: glowing orange with dark crust plates (4x4 plates, seamless at the cell border)
// lava: molten orange with hot yellow streaks and darker red troughs, no black crust.
// Seamless (period 16) so it can flow across the tiles
cv.cell(T_LAVA + v, |x, y| {
let crust = rnd((x / 4 + (y / 4) % 2) % 4, y / 4, v + 130);
let edge = rnd(x, y, v + 131) * 0.25;
if crust + edge > 0.85 {
Some(shade((60, 22, 14), (rnd(x, y, v + 132) * 14.0) as i32))
} else if crust + edge > 0.7 {
Some(c(200, 60, 20))
} else if hash(x, y, v + 133) % 11 == 0 {
Some(c(255, 230, 120))
let tau = std::f32::consts::TAU;
let (fx, fy) = (x as f32 / 16.0, y as f32 / 16.0);
let o = v as f32 * 1.3;
let heat = (tau * (fx + fy * 2.0) + o).sin() * 0.45
+ (tau * (fx * 3.0 - fy) + (tau * fy * 2.0 + o).sin() * 1.2).sin() * 0.35
+ (tau * (fy * 3.0 + fx * 2.0) + o * 2.0).sin() * 0.2;
let d = (rnd(x, y, v + 134) * 16.0) as i32 - 8;
Some(if heat > 0.62 {
shade((255, 228, 120), d / 2)
} else if heat > 0.3 {
shade((255, 170, 55), d)
} else if heat > -0.35 {
shade((240, 110, 25), d)
} else if heat > -0.7 {
shade((200, 64, 18), d)
} else {
Some(shade((250, 130, 30), (rnd(x, y, v + 134) * 30.0) as i32 - 15))
}
shade((150, 40, 14), d)
})
});
// knight fortress walls: pale stone blocks with blue-grey mortar
cv.cell(T_FORTWALL + v, |x, y| {
+44 -18
View File
@@ -219,9 +219,9 @@ void main() {
float a = color0.a;
if (a > 96.0 && a < 192.0) {
bool lava = a < 128.0;
float sp = lava ? 0.55 : 1.6;
float amp = lava ? 0.018 : 0.028;
w.y += amp * (sin(w.x * 1.7 + Time * sp) * 0.6 + sin(w.z * 2.3 - Time * sp * 1.3) * 0.4);
float sp = lava ? 0.8 : 1.6;
float amp = lava ? 0.04 : 0.05;
w.y += amp * (sin(w.x * 1.3 + w.z * 0.4 + Time * sp) * 0.6 + sin(w.z * 1.9 - w.x * 0.5 - Time * sp * 1.3) * 0.4);
}
wpos = w.xyz;
wnorm = normal.xyz;
@@ -250,20 +250,27 @@ void main() {
float wave = 0.0;
bool liquid = color.a > 0.38 && color.a < 0.75;
if (liquid) {
// flowing texture: sampled in world space inside the tile's (seamless) atlas cell
// flowing texture: sampled in world space inside the tile's (seamless) atlas cell, moving
// along the river (its direction comes in the normal's x/z, see liquid_flow)
bool lava = color.a < 0.5;
vec2 flow = lava ? vec2(0.035, 0.015) : vec2(0.11, 0.045);
vec2 p = wpos.xz + flow * Time;
p += (lava ? 0.03 : 0.05) * vec2(sin(wpos.z * 3.0 + Time * (lava ? 0.4 : 1.1)), sin(wpos.x * 3.0 + Time * (lava ? 0.3 : 0.9)));
vec2 dir = wnorm.xz / max(length(wnorm.xz), 0.001);
float speed = lava ? 0.16 : 0.32;
float ts = lava ? 0.8 : 1.6; // wave tempo: lava rolls slower
// swell: broad moving bands; their slope shades the surface and bends the texture
float ph1 = wpos.x * 1.3 + wpos.z * 0.4 + Time * ts;
float ph2 = wpos.z * 1.9 - wpos.x * 0.5 - Time * ts * 1.3;
vec2 slope = vec2(cos(ph1) * 1.3 * 0.6 - cos(ph2) * 0.5 * 0.4, cos(ph1) * 0.4 * 0.6 + cos(ph2) * 1.9 * 0.4);
vec2 p = wpos.xz - dir * speed * Time + slope * (lava ? 0.05 : 0.07);
vec2 cell = floor(uv * AtlasSize / 16.0) * 16.0;
tuv = (cell + fract(p) * 16.0) / AtlasSize;
wave = sin(wpos.x * 5.0 + wpos.z * 3.0 + Time * (lava ? 0.7 : 2.2)) * sin(wpos.z * 4.0 - wpos.x * 2.0 + Time * (lava ? 0.5 : 1.7));
float ripple = sin(wpos.x * 5.0 + wpos.z * 3.0 + Time * ts * 1.4) * sin(wpos.z * 4.0 - wpos.x * 2.0 + Time * ts);
wave = dot(slope, vec2(0.45, 0.35)) * 0.6 + ripple * 0.4;
}
vec4 tex = texture2D(Texture, tuv);
if (tex.a < 0.5) discard;
// see-through units (invisibility): every other pixel left out
if (color.a < 0.37 && mod(floor(gl_FragCoord.x) + floor(gl_FragCoord.y), 2.0) < 1.0) discard;
vec3 n = normalize(wnorm);
vec3 n = liquid ? vec3(0.0, 1.0, 0.0) : normalize(wnorm);
vec3 light = color.rgb;
for (int i = 0; i < 24; i++) {
if (float(i) >= LightCount) break;
@@ -274,9 +281,15 @@ void main() {
float ndl = max(dot(n, d / max(dist, 0.001)), 0.0) * 0.8 + 0.2;
light += LightCol[i].rgb * LightCol[i].a * att * ndl;
}
// light rippling over the surface: glints on water, a slow pulse of the glow on lava
if (liquid) light *= 1.0 + wave * (color.a < 0.5 ? 0.12 : 0.1);
gl_FragColor = vec4(tex.rgb * min(light, vec3(1.7)), 1.0);
// light rippling over the surface: glints on water; lava glows by itself and pulses
if (liquid) {
if (color.a < 0.5) {
light = max(light, vec3(1.45, 1.3, 1.2)) * (1.0 + wave * 0.18);
} else {
light *= 1.0 + wave * 0.28;
}
}
gl_FragColor = vec4(tex.rgb * min(light, vec3(1.9)), 1.0);
}
"#;
@@ -466,7 +479,7 @@ fn amb_at(f: &[f32], x: f32, z: f32) -> f32 {
// ---- lava light: an orange glow up to LAVA_REACH tiles around lava, fading with the distance
const LAVA_REACH: f32 = 10.0;
const LAVA_LIGHT: Vec3 = Vec3::new(0.62, 0.27, 0.05); // added light at full strength (next to the lava)
const LAVA_LIGHT: Vec3 = Vec3::new(1.05, 0.45, 0.08); // added light at full strength (next to the lava)
// glow per tile: 1 on lava, falling to 0 at LAVA_REACH tiles. The light spreads over open ground
// (walls are lit on their faces but do not let it through).
@@ -507,7 +520,7 @@ fn lava_field(map: &Map) -> Vec<f32> {
0.0
} else {
let k = (1.0 - d as f32 / max as f32).clamp(0.0, 1.0);
k * k
k.powf(1.6)
}
})
.collect()
@@ -528,6 +541,19 @@ fn lava_at(f: &[f32], x: f32, z: f32) -> f32 {
a * (1.0 - fz) + b * fz
}
// the direction a river flows at a tile, carried to the shader in the surface normal's x/z
// (y stays 1 so the normal still points up): along the longer run of river tiles around it
fn liquid_flow(map: &Map, x: i32, z: i32) -> Vec3 {
let river = |dx: i32, dz: i32| map.get(x + dx, z + dz).map_or(false, |t| t.river().is_some()) as i32;
let along_x: i32 = (-3..=3).map(|d| river(d, 0)).sum();
let along_z: i32 = (-3..=3).map(|d| river(0, d)).sum();
if along_x > along_z {
vec3(0.5, 1.0, 0.0)
} else {
vec3(0.0, 1.0, 0.5)
}
}
// a surface colour with the lava light added
fn lava_lit(base: Vec3, glow: f32) -> Vec3 {
base + LAVA_LIGHT * glow
@@ -550,7 +576,7 @@ const LIQ_D: f32 = 0.3; // depth of the river surface below the floor
// vertex alpha that marks river surfaces for the lit shader (waves and flowing texture)
const LIQ_WATER_A: f32 = 0.6;
const LIQ_LAVA_A: f32 = 0.42;
const LIQ_SKIRT: f32 = 0.06; // river banks reach a bit below the surface, so waves leave no gap
const LIQ_SKIRT: f32 = 0.1; // river banks reach a bit below the surface, so waves leave no gap
const DECK_Y: f32 = 0.03; // height of bridge decks
const OUTLINE_C: [u8; 4] = [8, 5, 6, 150];
@@ -673,7 +699,7 @@ fn build_chunk(map: &Map, amb: &[f32], glow: &[f32], cx: i32, cz: i32, atlas: &T
// (its centre, robust even with low precision uvs)
[(la + lb) * 0.5; 4],
[lc; 4],
[Vec3::Y; 4],
[liquid_flow(map, x, z); 4],
);
let (fa, fb) = cell_uv(T_FACE_EARTH);
for (d, &(dx, dz)) in DIR4.iter().enumerate() {
@@ -1108,7 +1134,7 @@ impl R3d {
for x in (ccx - r).max(0)..(ccx + r).min(mw() as i32) {
if x % 3 == 0 && g.map.t[idx(x, z)].kind == Kind::Lava {
let fl = 0.8 + 0.2 * (t * 2.0 + (x * 7 + z * 3) as f32).sin();
push(vec3(x as f32 + 0.5, 0.3, z as f32 + 0.5), 4.0, vec3(1.0, 0.45, 0.12), 0.9 * fl, 0.0);
push(vec3(x as f32 + 0.5, 0.3, z as f32 + 0.5), 5.0, vec3(1.0, 0.45, 0.12), 1.3 * fl, 0.0);
}
}
}
@@ -2645,7 +2671,7 @@ fn draw_parts_solid(g: &Game) {
let life = (p.t / p.max).clamp(0.0, 1.0);
let k = 1.0 - life * life;
let bob = (p.t * 3.0 + p.x * 2.0).sin() * 0.01;
let mid = vec3(p.x, -LIQ_D + 0.04 + bob, p.y); // above the swell of the waves
let mid = vec3(p.x, -LIQ_D + 0.07 + bob, p.y); // above the swell of the waves
let dir = vec3(p.vx, 0.0, p.vy).normalize_or_zero();
let half = dir * (0.17 * k);
let wood = vec3(0.45, 0.31, 0.18);