mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
fix(lod): light saved far columns' open water as open sky
A saved chunk's LOD columns kept each water block's own skylight, which fades a level per block of water, while the generator's far columns light open water as open sky. Where the two met - the stride limit in `SavedLodSampler::grid` - the difference drew a dark band through the sea. `Column::light_open_water` already existed for this and the live path (`Column::from_chunk`) applies it; `decode_lod_surfaces` never did. It has no `BlockFacts` and so cannot reach `state_kinds`, so the rule moves into `Column::light_open_water_where`, taking the caller's own test for water; the decoder names water from the block registry, which leaves lava out as the emissive test did. Only where there is a sky: the decoder's `sky_light` gates it, so the Nether's water gains no skylight it never had. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
44ca91ef8f
commit
b4629e064f
2 files changed
+40
-3
No files matched your search
@@ -278,10 +278,18 @@ impl Column {
|
||||
/// The water is one span again.
|
||||
fn light_open_water(&mut self, kinds: &[u8]) {
|
||||
// Water; lava glows.
|
||||
let water = |span: &Span| {
|
||||
let kind = kinds.get(span.state.0 as usize).copied().unwrap_or(0);
|
||||
self.light_open_water_where(|state| {
|
||||
let kind = kinds.get(state.0 as usize).copied().unwrap_or(0);
|
||||
kind & FLUID != 0 && kind >> 4 == 0
|
||||
};
|
||||
});
|
||||
}
|
||||
/// [`Column::light_open_water`] for a caller that knows which states are
|
||||
/// water without `BlockFacts`: the save decoder, whose palette already
|
||||
/// names water apart from lava. Saved columns skipping this lit their
|
||||
/// water a level darker per block, which drew a band through the sea
|
||||
/// where they met the generator's columns.
|
||||
pub fn light_open_water_where(&mut self, is_water: impl Fn(BlockStateId) -> bool) {
|
||||
let water = |span: &Span| is_water(span.state);
|
||||
if !self.spans.last().is_some_and(water) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4846,8 +4846,23 @@ fn decode_lod_surfaces(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Spans are pushed top down, so the reverse puts the topmost last, as
|
||||
// `Column::from_chunk` leaves them. Open water is then lit as open sky,
|
||||
// exactly as the live path does: without it a saved column kept each
|
||||
// water block's own faded skylight and drew a dark band through the sea
|
||||
// where saved columns met the generator's (`grid`'s stride limit).
|
||||
// Only where there is a sky to be lit by: the Nether's water must not
|
||||
// gain skylight it never had.
|
||||
let is_water = |state| {
|
||||
sky_light
|
||||
&& assets
|
||||
.registry
|
||||
.state(state)
|
||||
.is_some_and(|block| block.name == "minecraft:water")
|
||||
};
|
||||
for column in &mut *columns {
|
||||
column.spans.reverse();
|
||||
column.light_open_water_where(is_water);
|
||||
}
|
||||
Some(Arc::from(columns))
|
||||
}
|
||||
@@ -5639,6 +5654,20 @@ mod tests {
|
||||
let underwater = surfaces[4 * 16 + 4].surface.unwrap();
|
||||
assert_eq!((underwater.height, underwater.state), (55, sand));
|
||||
assert_eq!(underwater.water, Some((64, water)));
|
||||
// Open water reads as open sky, as the generator's far columns do.
|
||||
// Saved columns that kept each block's own faded skylight drew a dark
|
||||
// band through the sea where the two met.
|
||||
let sea = &surfaces[4 * 16 + 4].spans;
|
||||
let lit: Vec<_> = sea
|
||||
.iter()
|
||||
.filter(|span| span.state == water)
|
||||
.map(|span| span.light & 0xf0)
|
||||
.collect();
|
||||
assert!(!lit.is_empty(), "the column keeps its water spans");
|
||||
assert!(
|
||||
lit.iter().all(|sky| *sky == 0xf0),
|
||||
"saved open water is lit as open sky, got {lit:?}"
|
||||
);
|
||||
let chorus_column = &surfaces[2 * 16 + 12];
|
||||
assert_eq!(
|
||||
chorus_column
|
||||
|
||||
Reference in new issue
Block a user