pub mod render; use crate::render::{ composite::image_buffer_blit, 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, info}; 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/"); let solid = |x: isize, y: isize, z: isize| { dim.block(x, y, z) .map(|b| match b.name() { "minecraft:air" | "minecraft:cave_air" => (false, false), "minecraft:water" => (false, true), _ => (true, true), }) .unwrap_or((true, true)) }; let mut textures = HashMap::::new(); let mut view: Texture = ImageBuffer::new(16 * (SEG_SIZE + 1) as u32, 16 * (SEG_SIZE + 1) as u32); let mut visible = Vec::<((isize, isize, isize), (isize, isize, isize))>::new(); for ix in 0..SEG_SIZE { for iy in 0..(SEG_SIZE * 2) { for off in 0..=1 { let mut y = 100; let mut x = -ix + iy + off; let mut z = ix + iy; loop { let (solid, has_texture) = solid(x, y, z); if has_texture { visible.push(((x, y, z), (ix, iy, off))); } if solid { break; } y -= 1; x -= 1; z -= 1; } } } } info!("{} visible blocks", visible.len()); visible.sort_by_cached_key(|((x, y, z), _)| x + y + z); info!("compositing textures"); for ((x, y, z), (ix, iy, off)) in visible { let name = match dim.block(x, y, z) { Some(block) => block.name().to_owned(), None => "minecraft:debug".to_owned(), }; let name = &name["minecraft:".len()..]; let texture = textures .entry(name.to_owned()) .or_insert_with(|| processed_block_texture(name)); let ix = ix * 16 + off * 8; let iy = iy * 8 + off * 4; image_buffer_blit(&mut view, texture, (ix as u32, iy as u32)); } info!("saving png"); 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 { info!("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(); debug!("loading chunk {:?}", (cx, cz)); let chunk: Option = reg .read_chunk(cx.rem_euclid(32) as usize, cz.rem_euclid(32) as usize) .ok() .flatten() .map(|chunk| fastnbt::from_bytes(&chunk).ok()) .flatten(); 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()) } }