mirror of
https://github.com/neocturne/MinedMap.git
synced 2025-03-05 17:44:52 +01:00
minedmap: add mipmapping
This commit is contained in:
parent
86382772c3
commit
216aa6ceec
2 changed files with 128 additions and 0 deletions
|
@ -1,5 +1,6 @@
|
||||||
mod common;
|
mod common;
|
||||||
mod region_processor;
|
mod region_processor;
|
||||||
|
mod tile_mipmapper;
|
||||||
mod tile_renderer;
|
mod tile_renderer;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -9,6 +10,7 @@ use clap::Parser;
|
||||||
|
|
||||||
use common::Config;
|
use common::Config;
|
||||||
use region_processor::RegionProcessor;
|
use region_processor::RegionProcessor;
|
||||||
|
use tile_mipmapper::TileMipmapper;
|
||||||
use tile_renderer::TileRenderer;
|
use tile_renderer::TileRenderer;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
@ -25,6 +27,7 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
let regions = RegionProcessor::new(&config).run()?;
|
let regions = RegionProcessor::new(&config).run()?;
|
||||||
TileRenderer::new(&config).run(regions.iter().copied())?;
|
TileRenderer::new(&config).run(regions.iter().copied())?;
|
||||||
|
TileMipmapper::new(&config).run(regions)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
125
src/bin/minedmap/tile_mipmapper.rs
Normal file
125
src/bin/minedmap/tile_mipmapper.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
|
use minedmap::{io::fs, types::*};
|
||||||
|
|
||||||
|
use super::common::*;
|
||||||
|
|
||||||
|
pub struct TileMipmapper<'a> {
|
||||||
|
config: &'a Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TileMipmapper<'a> {
|
||||||
|
pub fn new(config: &'a Config) -> Self {
|
||||||
|
TileMipmapper { config }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn done(tiles: &BTreeSet<TileCoords>) -> bool {
|
||||||
|
tiles
|
||||||
|
.into_iter()
|
||||||
|
.all(|TileCoords { x, z }| (-1..=0).contains(x) && (-1..=0).contains(z))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_coords(tiles: &BTreeSet<TileCoords>) -> BTreeSet<TileCoords> {
|
||||||
|
let mut ret = BTreeSet::new();
|
||||||
|
|
||||||
|
for coords in tiles {
|
||||||
|
ret.insert(TileCoords {
|
||||||
|
x: coords.x >> 1,
|
||||||
|
z: coords.z >> 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_mipmap<P: image::PixelWithColorType>(
|
||||||
|
&self,
|
||||||
|
kind: TileKind,
|
||||||
|
level: usize,
|
||||||
|
coords: TileCoords,
|
||||||
|
prev: &BTreeSet<TileCoords>,
|
||||||
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[P::Subpixel]: image::EncodableLayout,
|
||||||
|
image::ImageBuffer<P, Vec<P::Subpixel>>: Into<image::DynamicImage>,
|
||||||
|
{
|
||||||
|
const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32;
|
||||||
|
|
||||||
|
let output_path = self.config.tile_path(kind, level, coords);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Rendering mipmap tile {}",
|
||||||
|
output_path
|
||||||
|
.strip_prefix(&self.config.output_dir)
|
||||||
|
.expect("tile path must be in output directory")
|
||||||
|
.display(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut image: image::DynamicImage =
|
||||||
|
image::ImageBuffer::<P, Vec<P::Subpixel>>::new(N, N).into();
|
||||||
|
|
||||||
|
for (dx, dz) in [(0, 0), (0, 1), (1, 0), (1, 1)] {
|
||||||
|
let source_coords = TileCoords {
|
||||||
|
x: 2 * coords.x + dx,
|
||||||
|
z: 2 * coords.z + dz,
|
||||||
|
};
|
||||||
|
if !prev.contains(&source_coords) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let source_path = self.config.tile_path(kind, level - 1, source_coords);
|
||||||
|
let source = match image::open(&source_path) {
|
||||||
|
Ok(source) => source,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"Failed to read source image {}: {}",
|
||||||
|
source_path.display(),
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let resized = source.resize(N / 2, N / 2, image::imageops::FilterType::Triangle);
|
||||||
|
image::imageops::overlay(
|
||||||
|
&mut image,
|
||||||
|
&resized,
|
||||||
|
dx as i64 * (N / 2) as i64,
|
||||||
|
dz as i64 * (N / 2) as i64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::create_with_tmpfile(&output_path, |file| {
|
||||||
|
image
|
||||||
|
.write_to(file, image::ImageFormat::Png)
|
||||||
|
.context("Failed to save image")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(self, tiles: BTreeSet<TileCoords>) -> Result<Vec<BTreeSet<TileCoords>>> {
|
||||||
|
let mut tile_stack = vec![tiles];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let level = tile_stack.len();
|
||||||
|
let prev = &tile_stack[level - 1];
|
||||||
|
if Self::done(prev) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::create_dir_all(&self.config.tile_dir(TileKind::Map, level))?;
|
||||||
|
fs::create_dir_all(&self.config.tile_dir(TileKind::Lightmap, level))?;
|
||||||
|
|
||||||
|
let next = Self::map_coords(prev);
|
||||||
|
|
||||||
|
for &coords in &next {
|
||||||
|
self.render_mipmap::<image::Rgba<u8>>(TileKind::Map, level, coords, prev)?;
|
||||||
|
self.render_mipmap::<image::LumaA<u8>>(TileKind::Lightmap, level, coords, prev)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
tile_stack.push(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tile_stack)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue