mirror of
https://github.com/neocturne/MinedMap.git
synced 2025-07-04 06:39:07 +02:00
world: implement top_layer function
Implement one of the core functions of MinedMap: finding the topmost visible block at each coordinate.
This commit is contained in:
parent
2530a557a9
commit
f48aa877d2
3 changed files with 148 additions and 2 deletions
130
src/world/layer.rs
Normal file
130
src/world/layer.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
use anyhow::{Context, Result};
|
||||
use itertools::iproduct;
|
||||
|
||||
use super::chunk::Chunk;
|
||||
use crate::{
|
||||
resource::{BlockFlag, BlockType, BlockTypeMap},
|
||||
types::*,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct BlockHeight(i32);
|
||||
|
||||
impl BlockHeight {
|
||||
/// Constructs a new [BlockHeight] from section and block Y indices
|
||||
///
|
||||
/// Returns an error if the resulting coordindate does not fit into
|
||||
/// an [i32].
|
||||
pub fn new(section: SectionY, block: BlockY) -> Result<Self> {
|
||||
let height = section
|
||||
.0
|
||||
.checked_mul(BLOCKS_PER_CHUNK as i32)
|
||||
.and_then(|y| y.checked_add_unsigned(block.0.into()))
|
||||
.context("Block height out of bounds")?;
|
||||
Ok(BlockHeight(height))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BlockInfo {
|
||||
block_type: BlockType,
|
||||
y: BlockHeight,
|
||||
depth: Option<BlockHeight>,
|
||||
}
|
||||
|
||||
/// Helper methods for [BlockInfo]
|
||||
trait OptionBlockInfoExt {
|
||||
/// Checks if a [BlockInfo] has been filled in completely
|
||||
///
|
||||
/// Helper used by [top_layer]
|
||||
fn done(&self) -> bool;
|
||||
|
||||
/// Fills in a [BlockInfo] based on a [BlockType]
|
||||
///
|
||||
/// Only fills in data if the block is part of the visible top layer
|
||||
/// of the rendered map.
|
||||
///
|
||||
/// Must be called on an incomplete [BlockInfo] entry. Returns `true`
|
||||
/// if the entry has been filled in completely.
|
||||
fn fill(&mut self, y: BlockHeight, block_type: BlockType) -> bool;
|
||||
}
|
||||
|
||||
impl OptionBlockInfoExt for Option<BlockInfo> {
|
||||
fn done(&self) -> bool {
|
||||
let Some(info) = self else {
|
||||
return false;
|
||||
};
|
||||
|
||||
info.depth.is_some()
|
||||
}
|
||||
|
||||
fn fill(&mut self, y: BlockHeight, block_type: BlockType) -> bool {
|
||||
if !block_type.is(BlockFlag::Opaque) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.is_none() {
|
||||
*self = Some(BlockInfo {
|
||||
block_type,
|
||||
y,
|
||||
depth: None,
|
||||
});
|
||||
}
|
||||
|
||||
if block_type.is(BlockFlag::Water) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let info = self.as_mut().unwrap();
|
||||
info.depth = Some(y);
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub type BlockInfoArray = LayerBlockArray<Option<BlockInfo>>;
|
||||
|
||||
/// Fills in a [BlockInfoArray] with the information of the chunk's top
|
||||
/// block layer
|
||||
///
|
||||
/// For each (X, Z) coordinate pair, the topmost opaque block is
|
||||
/// determined as the block that should be visible on the rendered
|
||||
/// map. For water blocks, the height of the first non-water block
|
||||
/// is additionally filled in as the water depth.
|
||||
pub fn top_layer(chunk: &Chunk, block_types: &BlockTypeMap) -> Result<BlockInfoArray> {
|
||||
use BLOCKS_PER_CHUNK as N;
|
||||
|
||||
let mut done = 0;
|
||||
let mut ret = BlockInfoArray::default();
|
||||
|
||||
for ((section_y, section), y, xz) in iproduct!(
|
||||
chunk.sections().rev(),
|
||||
BlockY::iter().rev(),
|
||||
BlockInfoArray::keys()
|
||||
) {
|
||||
let entry = &mut ret[xz];
|
||||
if entry.done() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let coords = SectionBlockCoords { xz, y };
|
||||
let block_id = section.block_id_at(coords)?;
|
||||
let Some(&block_type) = block_types.get(block_id) else {
|
||||
eprintln!("Unknown block type: {}", block_id);
|
||||
continue;
|
||||
};
|
||||
let height = BlockHeight::new(section_y, y)?;
|
||||
if !entry.fill(height, block_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert!(entry.done());
|
||||
|
||||
done += 1;
|
||||
if done == N * N {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod chunk;
|
||||
pub mod de;
|
||||
pub mod layer;
|
||||
pub mod section;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue