fix: cross-fade tiles to sky tile using alpha instead of color tint

Replace df_fade_color (multiplicative tint that preserves texture hue)
with tile_for_depth which cross-fades between real tile and sky tile:

- z_diff <= 0: real tile at full opacity (no fade)
- z_diff in (0, threshold): real tile fades out via alpha
- z_diff past threshold: sky tile (index 0) fades in
- invisible tiles: solid sky colour

Also fix tile_id_to_tileset_index to include sky (id=0) as index 0,
so sky tiles render correctly as the fade target.
This commit is contained in:
2026-03-20 14:27:24 +00:00
parent 6fbfb10e48
commit fc47e719d7
+47 -25
View File
@@ -69,7 +69,7 @@ pub const FIXTURE_ROW_OFFSET: u16 = 6;
fn tile_id_to_tileset_index(tile_id: u32) -> Option<u16> { fn tile_id_to_tileset_index(tile_id: u32) -> Option<u16> {
if tile_id == 0 { if tile_id == 0 {
return None; return Some(0);
} }
if tile_id <= 5 { if tile_id <= 5 {
return Some(tile_id as u16); return Some(tile_id as u16);
@@ -82,22 +82,44 @@ fn tile_id_to_tileset_index(tile_id: u32) -> Option<u16> {
} }
} }
fn df_fade_color(z_diff: i32) -> Color { fn tile_for_depth(real_index: u16, z_diff: i32) -> TileData {
const SKY_R: f32 = 133_f32 / 255_f32;
const SKY_G: f32 = 167_f32 / 255_f32;
const SKY_B: f32 = 178_f32 / 255_f32;
const LAYER_ALPHA: f32 = 70_f32 / 255_f32;
if z_diff <= 0 { if z_diff <= 0 {
return Color::WHITE; return TileData {
tileset_index: real_index,
color: Color::WHITE,
visible: true,
};
} }
let n = (z_diff as f32).min(8_f32); let n = (z_diff as f32).min(8_f32);
let overlay_r = 133_f32 / 255_f32; let t = 1_f32 - (1_f32 - LAYER_ALPHA).powf(n);
let overlay_g = 167_f32 / 255_f32;
let overlay_b = 178_f32 / 255_f32; if t >= 0.98 {
let layer_alpha = 41_f32 / 255_f32; return TileData {
let acc_alpha = 1_f32 - (1_f32 - layer_alpha).powf(n); tileset_index: 0,
let inv = 1_f32 - acc_alpha; color: Color::srgb(SKY_R, SKY_G, SKY_B),
Color::srgb( visible: true,
overlay_r * acc_alpha + inv, };
overlay_g * acc_alpha + inv, }
overlay_b * acc_alpha + inv,
) if t < 0.5 {
TileData {
tileset_index: real_index,
color: Color::srgba(1_f32, 1_f32, 1_f32, 1_f32 - t * 2_f32),
visible: true,
}
} else {
TileData {
tileset_index: 0,
color: Color::srgba(SKY_R, SKY_G, SKY_B, (t - 0.5_f32) * 2_f32),
visible: true,
}
}
} }
fn is_tile_visible_at_z(tile_data: &FloorTileData, z_index: usize) -> bool { fn is_tile_visible_at_z(tile_data: &FloorTileData, z_index: usize) -> bool {
@@ -134,19 +156,19 @@ fn populate_chunk_tiles(
); );
let tile = tilemap.get_floor(&world_pos); let tile = tilemap.get_floor(&world_pos);
match tile { match tile {
Some(t) if tile_id_to_tileset_index(t.id as u32).is_some() => { Some(t) => {
let visible = is_tile_visible_at_z(t, z_index); let idx = tile_id_to_tileset_index(t.id as u32);
let color = if visible { if let Some(tileset_idx) = idx {
df_fade_color(z_diff) let is_visible = is_tile_visible_at_z(t, z_index);
let tile_data = if is_visible {
tile_for_depth(tileset_idx, z_diff)
} else { } else {
Color::BLACK tile_for_depth(0, 999)
}; };
let tileset_idx = tile_id_to_tileset_index(t.id as u32).unwrap(); tiles.push(Some(tile_data));
tiles.push(Some(TileData { } else {
tileset_index: tileset_idx, tiles.push(None);
color, }
visible,
}));
} }
_ => tiles.push(None), _ => tiles.push(None),
} }