pub mod render; use crate::render::{ composite::{image_buffer_blit, isometric_coord_mapping, CHUNK_HEIGHT}, models::processed_block_texture, processing::Texture, }; use chashmap::{CHashMap, ReadGuard, WriteGuard}; use fastanvil::{Block, Chunk, CurrentJavaChunk, Region, RegionFileLoader, RegionLoader}; use image::ImageBuffer; use log::debug; use std::{collections::HashMap, fs::File, path::Path}; const SEG_SIZE: isize = 128; fn main() { env_logger::builder() .filter_level(log::LevelFilter::Debug) .parse_env("LOG") .init(); let dim = Dimension::new("/home/muffin/containers/games/home/user/server/world/region/"); dim.block(123, 123, 123); let mut visible_blocks = Vec::new(); for x in 0..SEG_SIZE { for y in -64..256 { for z in 0..SEG_SIZE { let solid = |x: isize, y: isize, z: isize| { dim.block(x, y, z) .map(|b| { !matches!( b.name(), "minecraft:air" | "minecraft:water" | "minecraft:cave_air" ) }) .unwrap_or(false) }; let visible = |b: &Block| !matches!(b.name(), "minecraft:air" | "minecraft:cave_air"); let ray_visible = || { for i in 1..100 { if solid(x - i, y + i, z - i) { return false; } } true }; if visible(&dim.block(x, y, z).unwrap()) && !(solid(x + 1, y, z) && solid(x - 1, y, z) && solid(x, y + 1, z) && solid(x, y - 1, z) && solid(x, y, z + 1) && solid(x, y, z - 1)) && ray_visible() { visible_blocks.push((x, y, z)) } } } } debug!("{} potentially visible blocks", visible_blocks.len()); debug!("sorting by z-order"); visible_blocks.sort_by_cached_key(|(x, y, z)| x + y + z); debug!("done"); let mut view: Texture = ImageBuffer::new( 16 * SEG_SIZE as u32, 16 * (SEG_SIZE as u32 + CHUNK_HEIGHT as u32) / 2, ); let mut textures = HashMap::::new(); for (x, y, z) in visible_blocks { let coords = isometric_coord_mapping(x as i32, y as i32, z as i32); let block = dim.block(x, y, z).unwrap(); let name = &block.name()["minecraft:".len()..]; let texture = textures .entry(name.to_owned()) .or_insert_with(|| processed_block_texture(name)); image_buffer_blit(&mut view, texture, coords); } view.save(format!("/tmp/a.png")).unwrap(); } struct Dimension { loader: RegionFileLoader, regions: CHashMap<(isize, isize), Option>>, chunks: CHashMap<(isize, isize), Option>, } impl Dimension { pub fn new(path: &str) -> Self { let loader = fastanvil::RegionFileLoader::new(Path::new(path).to_path_buf()); Self { loader, regions: Default::default(), chunks: Default::default(), } } pub fn region( &self, rx: isize, rz: isize, ) -> WriteGuard<'_, (isize, isize), Option>> { if self.regions.contains_key(&(rx, rz)) { self.regions.get_mut(&(rx, rz)).unwrap() } else { debug!("loading region {:?}", (rx, rz)); self.regions.insert( (rx, rz), self.loader .region(fastanvil::RCoord(rx), fastanvil::RCoord(rz)), ); self.region(rx, rz) } } pub fn chunk( &self, cx: isize, cz: isize, ) -> ReadGuard<'_, (isize, isize), Option> { if self.chunks.contains_key(&(cx, cz)) { self.chunks.get(&(cx, cz)).unwrap() } else { let mut guard = self.region(cx / 32, cz / 32); let reg = guard.as_mut().unwrap(); let chunk = reg .read_chunk(cx.rem_euclid(32) as usize, cz.rem_euclid(32) as usize) .ok() .unwrap() .unwrap(); debug!("loading chunk {:?}", (cx, cz)); let chunk: Option = fastnbt::from_bytes(&chunk).ok(); self.chunks.insert((cx, cz), chunk); self.chunk(cx, cz) } } pub fn block(&self, x: isize, y: isize, z: isize) -> Option { self.chunk(x / 16, z / 16) .as_ref()? .block(x.rem_euclid(16) as usize, y, z.rem_euclid(16) as usize) .map(|e| e.to_owned()) } }