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:
1 parent
a701212299
commit
1094c7c5f2
2 files changed
+63
-29
No files matched your search
+19
-11
@@ -622,19 +622,27 @@ fn gen_atlas() -> Image {
|
|||||||
_ => shade((24, 50, 88), d),
|
_ => 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| {
|
cv.cell(T_LAVA + v, |x, y| {
|
||||||
let crust = rnd((x / 4 + (y / 4) % 2) % 4, y / 4, v + 130);
|
let tau = std::f32::consts::TAU;
|
||||||
let edge = rnd(x, y, v + 131) * 0.25;
|
let (fx, fy) = (x as f32 / 16.0, y as f32 / 16.0);
|
||||||
if crust + edge > 0.85 {
|
let o = v as f32 * 1.3;
|
||||||
Some(shade((60, 22, 14), (rnd(x, y, v + 132) * 14.0) as i32))
|
let heat = (tau * (fx + fy * 2.0) + o).sin() * 0.45
|
||||||
} else if crust + edge > 0.7 {
|
+ (tau * (fx * 3.0 - fy) + (tau * fy * 2.0 + o).sin() * 1.2).sin() * 0.35
|
||||||
Some(c(200, 60, 20))
|
+ (tau * (fy * 3.0 + fx * 2.0) + o * 2.0).sin() * 0.2;
|
||||||
} else if hash(x, y, v + 133) % 11 == 0 {
|
let d = (rnd(x, y, v + 134) * 16.0) as i32 - 8;
|
||||||
Some(c(255, 230, 120))
|
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 {
|
} 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
|
// knight fortress walls: pale stone blocks with blue-grey mortar
|
||||||
cv.cell(T_FORTWALL + v, |x, y| {
|
cv.cell(T_FORTWALL + v, |x, y| {
|
||||||
|
|||||||
+44
-18
@@ -219,9 +219,9 @@ void main() {
|
|||||||
float a = color0.a;
|
float a = color0.a;
|
||||||
if (a > 96.0 && a < 192.0) {
|
if (a > 96.0 && a < 192.0) {
|
||||||
bool lava = a < 128.0;
|
bool lava = a < 128.0;
|
||||||
float sp = lava ? 0.55 : 1.6;
|
float sp = lava ? 0.8 : 1.6;
|
||||||
float amp = lava ? 0.018 : 0.028;
|
float amp = lava ? 0.04 : 0.05;
|
||||||
w.y += amp * (sin(w.x * 1.7 + Time * sp) * 0.6 + sin(w.z * 2.3 - Time * sp * 1.3) * 0.4);
|
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;
|
wpos = w.xyz;
|
||||||
wnorm = normal.xyz;
|
wnorm = normal.xyz;
|
||||||
@@ -250,20 +250,27 @@ void main() {
|
|||||||
float wave = 0.0;
|
float wave = 0.0;
|
||||||
bool liquid = color.a > 0.38 && color.a < 0.75;
|
bool liquid = color.a > 0.38 && color.a < 0.75;
|
||||||
if (liquid) {
|
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;
|
bool lava = color.a < 0.5;
|
||||||
vec2 flow = lava ? vec2(0.035, 0.015) : vec2(0.11, 0.045);
|
vec2 dir = wnorm.xz / max(length(wnorm.xz), 0.001);
|
||||||
vec2 p = wpos.xz + flow * Time;
|
float speed = lava ? 0.16 : 0.32;
|
||||||
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)));
|
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;
|
vec2 cell = floor(uv * AtlasSize / 16.0) * 16.0;
|
||||||
tuv = (cell + fract(p) * 16.0) / AtlasSize;
|
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);
|
vec4 tex = texture2D(Texture, tuv);
|
||||||
if (tex.a < 0.5) discard;
|
if (tex.a < 0.5) discard;
|
||||||
// see-through units (invisibility): every other pixel left out
|
// 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;
|
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;
|
vec3 light = color.rgb;
|
||||||
for (int i = 0; i < 24; i++) {
|
for (int i = 0; i < 24; i++) {
|
||||||
if (float(i) >= LightCount) break;
|
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;
|
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 += LightCol[i].rgb * LightCol[i].a * att * ndl;
|
||||||
}
|
}
|
||||||
// light rippling over the surface: glints on water, a slow pulse of the glow on lava
|
// light rippling over the surface: glints on water; lava glows by itself and pulses
|
||||||
if (liquid) light *= 1.0 + wave * (color.a < 0.5 ? 0.12 : 0.1);
|
if (liquid) {
|
||||||
gl_FragColor = vec4(tex.rgb * min(light, vec3(1.7)), 1.0);
|
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
|
// ---- lava light: an orange glow up to LAVA_REACH tiles around lava, fading with the distance
|
||||||
const LAVA_REACH: f32 = 10.0;
|
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
|
// 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).
|
// (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
|
0.0
|
||||||
} else {
|
} else {
|
||||||
let k = (1.0 - d as f32 / max as f32).clamp(0.0, 1.0);
|
let k = (1.0 - d as f32 / max as f32).clamp(0.0, 1.0);
|
||||||
k * k
|
k.powf(1.6)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
@@ -528,6 +541,19 @@ fn lava_at(f: &[f32], x: f32, z: f32) -> f32 {
|
|||||||
a * (1.0 - fz) + b * fz
|
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
|
// a surface colour with the lava light added
|
||||||
fn lava_lit(base: Vec3, glow: f32) -> Vec3 {
|
fn lava_lit(base: Vec3, glow: f32) -> Vec3 {
|
||||||
base + LAVA_LIGHT * glow
|
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)
|
// vertex alpha that marks river surfaces for the lit shader (waves and flowing texture)
|
||||||
const LIQ_WATER_A: f32 = 0.6;
|
const LIQ_WATER_A: f32 = 0.6;
|
||||||
const LIQ_LAVA_A: f32 = 0.42;
|
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 DECK_Y: f32 = 0.03; // height of bridge decks
|
||||||
const OUTLINE_C: [u8; 4] = [8, 5, 6, 150];
|
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)
|
// (its centre, robust even with low precision uvs)
|
||||||
[(la + lb) * 0.5; 4],
|
[(la + lb) * 0.5; 4],
|
||||||
[lc; 4],
|
[lc; 4],
|
||||||
[Vec3::Y; 4],
|
[liquid_flow(map, x, z); 4],
|
||||||
);
|
);
|
||||||
let (fa, fb) = cell_uv(T_FACE_EARTH);
|
let (fa, fb) = cell_uv(T_FACE_EARTH);
|
||||||
for (d, &(dx, dz)) in DIR4.iter().enumerate() {
|
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) {
|
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 {
|
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();
|
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 life = (p.t / p.max).clamp(0.0, 1.0);
|
||||||
let k = 1.0 - life * life;
|
let k = 1.0 - life * life;
|
||||||
let bob = (p.t * 3.0 + p.x * 2.0).sin() * 0.01;
|
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 dir = vec3(p.vx, 0.0, p.vy).normalize_or_zero();
|
||||||
let half = dir * (0.17 * k);
|
let half = dir * (0.17 * k);
|
||||||
let wood = vec3(0.45, 0.31, 0.18);
|
let wood = vec3(0.45, 0.31, 0.18);
|
||||||
|
|||||||
Reference in new issue
Block a user