mirror of
https://github.com/neocturne/MinedMap.git
synced 2025-03-05 17:44:52 +01: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
19
src/main.rs
19
src/main.rs
|
@ -3,6 +3,8 @@ use std::path::PathBuf;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
|
use minedmap::{resource, world};
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Filename to dump
|
/// Filename to dump
|
||||||
|
@ -12,9 +14,22 @@ struct Args {
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
|
let block_types = resource::block_types();
|
||||||
|
|
||||||
minedmap::io::region::from_file(args.file.as_path())?.foreach_chunk(
|
minedmap::io::region::from_file(args.file.as_path())?.foreach_chunk(
|
||||||
|coords, value: minedmap::world::de::Chunk| {
|
|coords, data: world::de::Chunk| {
|
||||||
println!("Chunk {:?}: {:#?}", coords, value);
|
let chunk = match world::chunk::Chunk::new(&data) {
|
||||||
|
Ok(chunk) => chunk,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Chunk {:?}: {}", coords, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match world::layer::top_layer(&chunk, &block_types) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => println!("{:?}", err),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
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 chunk;
|
||||||
pub mod de;
|
pub mod de;
|
||||||
|
pub mod layer;
|
||||||
pub mod section;
|
pub mod section;
|
||||||
|
|
Loading…
Add table
Reference in a new issue