mirror of
https://github.com/neocturne/MinedMap.git
synced 2025-03-04 17:23:33 +01:00
Store per-region biome list in IndexSet
Index into the biome list instead of duplicating the biome data for each coordinate. This reduces the size of the processed data and speeds up encoding/decoding.
This commit is contained in:
parent
c38f00e411
commit
fb712cd2f5
8 changed files with 70 additions and 18 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -220,6 +220,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -279,6 +285,12 @@ version = "0.24.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42218cb640844e3872cc3c153dc975229e080a6c4733b34709ef445610550226"
|
checksum = "42218cb640844e3872cc3c153dc975229e080a6c4733b34709ef445610550226"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -305,6 +317,17 @@ dependencies = [
|
||||||
"png",
|
"png",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -375,6 +398,7 @@ dependencies = [
|
||||||
"flate2",
|
"flate2",
|
||||||
"glam",
|
"glam",
|
||||||
"image",
|
"image",
|
||||||
|
"indexmap",
|
||||||
"itertools",
|
"itertools",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -17,6 +17,7 @@ fastnbt = "2.3.2"
|
||||||
flate2 = "1.0.25"
|
flate2 = "1.0.25"
|
||||||
glam = "0.24.0"
|
glam = "0.24.0"
|
||||||
image = { version = "0.24.5", default-features = false, features = ["png"] }
|
image = { version = "0.24.5", default-features = false, features = ["png"] }
|
||||||
|
indexmap = { version = "2.0.0", features = ["serde"] }
|
||||||
itertools = "0.11.0"
|
itertools = "0.11.0"
|
||||||
num-integer = "0.1.45"
|
num-integer = "0.1.45"
|
||||||
serde = "1.0.152"
|
serde = "1.0.152"
|
||||||
|
|
|
@ -4,9 +4,10 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use indexmap::IndexSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use minedmap::{io::fs::FileMetaVersion, types::*, world::layer};
|
use minedmap::{io::fs::FileMetaVersion, resource::Biome, types::*, world::layer};
|
||||||
|
|
||||||
// Increase to force regeneration of all output files
|
// Increase to force regeneration of all output files
|
||||||
pub const FILE_META_VERSION: FileMetaVersion = FileMetaVersion(0);
|
pub const FILE_META_VERSION: FileMetaVersion = FileMetaVersion(0);
|
||||||
|
@ -43,7 +44,12 @@ pub struct ProcessedChunk {
|
||||||
pub biomes: Box<layer::BiomeArray>,
|
pub biomes: Box<layer::BiomeArray>,
|
||||||
pub depths: Box<layer::DepthArray>,
|
pub depths: Box<layer::DepthArray>,
|
||||||
}
|
}
|
||||||
pub type ProcessedRegion = ChunkArray<Option<ProcessedChunk>>;
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct ProcessedRegion {
|
||||||
|
pub biome_list: IndexSet<Biome>,
|
||||||
|
pub chunks: ChunkArray<Option<ProcessedChunk>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub region_dir: PathBuf,
|
pub region_dir: PathBuf,
|
||||||
|
|
|
@ -2,9 +2,10 @@ use std::{path::Path, time::SystemTime};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
|
use indexmap::IndexSet;
|
||||||
use minedmap::{
|
use minedmap::{
|
||||||
io::{fs, storage},
|
io::{fs, storage},
|
||||||
resource,
|
resource::{self, Biome},
|
||||||
types::*,
|
types::*,
|
||||||
world::{
|
world::{
|
||||||
self,
|
self,
|
||||||
|
@ -45,9 +46,13 @@ impl<'a> RegionProcessor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes a single chunk
|
/// Processes a single chunk
|
||||||
fn process_chunk(&self, data: world::de::Chunk) -> Result<Option<LayerData>> {
|
fn process_chunk(
|
||||||
|
&self,
|
||||||
|
biome_list: &mut IndexSet<Biome>,
|
||||||
|
data: world::de::Chunk,
|
||||||
|
) -> Result<Option<LayerData>> {
|
||||||
let chunk = world::chunk::Chunk::new(&data, &self.block_types, &self.biome_types)?;
|
let chunk = world::chunk::Chunk::new(&data, &self.block_types, &self.biome_types)?;
|
||||||
world::layer::top_layer(&chunk)
|
world::layer::top_layer(biome_list, &chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk_lightmap(
|
fn render_chunk_lightmap(
|
||||||
|
@ -110,12 +115,12 @@ impl<'a> RegionProcessor<'a> {
|
||||||
minedmap::io::region::from_file(path)?.foreach_chunk(
|
minedmap::io::region::from_file(path)?.foreach_chunk(
|
||||||
|chunk_coords, data: world::de::Chunk| {
|
|chunk_coords, data: world::de::Chunk| {
|
||||||
let Some(layer::LayerData{ blocks, biomes, block_light, depths }) = self
|
let Some(layer::LayerData{ blocks, biomes, block_light, depths }) = self
|
||||||
.process_chunk(data)
|
.process_chunk(&mut processed_region.biome_list, data)
|
||||||
.with_context(|| format!("Failed to process chunk {:?}", chunk_coords))?
|
.with_context(|| format!("Failed to process chunk {:?}", chunk_coords))?
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
processed_region[chunk_coords] = Some(ProcessedChunk {
|
processed_region.chunks[chunk_coords] = Some(ProcessedChunk {
|
||||||
blocks,
|
blocks,
|
||||||
biomes,
|
biomes,
|
||||||
depths,
|
depths,
|
||||||
|
|
|
@ -56,7 +56,8 @@ fn biome_at(
|
||||||
z: block_z,
|
z: block_z,
|
||||||
};
|
};
|
||||||
let region = region_group.get(region_x, region_z)?;
|
let region = region_group.get(region_x, region_z)?;
|
||||||
region[chunk].as_ref()?.biomes[block].as_ref()
|
let index = region.chunks[chunk].as_ref()?.biomes[block]?.get() - 1;
|
||||||
|
region.biome_list.get_index(index.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TileRenderer<'a> {
|
pub struct TileRenderer<'a> {
|
||||||
|
@ -144,7 +145,7 @@ impl<'a> TileRenderer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_region(image: &mut image::RgbaImage, region_group: &RegionGroup<ProcessedRegion>) {
|
fn render_region(image: &mut image::RgbaImage, region_group: &RegionGroup<ProcessedRegion>) {
|
||||||
for (coords, chunk) in region_group.center().iter() {
|
for (coords, chunk) in region_group.center().chunks.iter() {
|
||||||
let Some(chunk) = chunk else {
|
let Some(chunk) = chunk else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,13 +2,13 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::Color;
|
use super::Color;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum BiomeGrassColorModifier {
|
pub enum BiomeGrassColorModifier {
|
||||||
DarkForest,
|
DarkForest,
|
||||||
Swamp,
|
Swamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct Biome {
|
pub struct Biome {
|
||||||
pub temp: i8,
|
pub temp: i8,
|
||||||
pub downfall: i8,
|
pub downfall: i8,
|
||||||
|
|
|
@ -35,7 +35,7 @@ where
|
||||||
BitFlags::<BlockFlag>::from_bits(bits).map_err(de::Error::custom)
|
BitFlags::<BlockFlag>::from_bits(bits).map_err(de::Error::custom)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct Color(pub [u8; 3]);
|
pub struct Color(pub [u8; 3]);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
use std::num::NonZeroU16;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use indexmap::IndexSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::chunk::{Chunk, SectionIterItem};
|
use super::chunk::{Chunk, SectionIterItem};
|
||||||
|
@ -26,13 +29,13 @@ impl BlockHeight {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BlockArray = LayerBlockArray<Option<BlockType>>;
|
pub type BlockArray = LayerBlockArray<Option<BlockType>>;
|
||||||
pub type BiomeArray = LayerBlockArray<Option<Biome>>;
|
pub type BiomeArray = LayerBlockArray<Option<NonZeroU16>>;
|
||||||
pub type BlockLightArray = LayerBlockArray<u8>;
|
pub type BlockLightArray = LayerBlockArray<u8>;
|
||||||
pub type DepthArray = LayerBlockArray<Option<BlockHeight>>;
|
pub type DepthArray = LayerBlockArray<Option<BlockHeight>>;
|
||||||
|
|
||||||
struct LayerEntry<'a> {
|
struct LayerEntry<'a> {
|
||||||
block: &'a mut Option<BlockType>,
|
block: &'a mut Option<BlockType>,
|
||||||
biome: &'a mut Option<Biome>,
|
biome: &'a mut Option<NonZeroU16>,
|
||||||
block_light: &'a mut u8,
|
block_light: &'a mut u8,
|
||||||
depth: &'a mut Option<BlockHeight>,
|
depth: &'a mut Option<BlockHeight>,
|
||||||
}
|
}
|
||||||
|
@ -46,7 +49,12 @@ impl<'a> LayerEntry<'a> {
|
||||||
self.depth.is_some()
|
self.depth.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill(&mut self, section: SectionIterItem, coords: SectionBlockCoords) -> Result<bool> {
|
fn fill(
|
||||||
|
&mut self,
|
||||||
|
biome_list: &mut IndexSet<Biome>,
|
||||||
|
section: SectionIterItem,
|
||||||
|
coords: SectionBlockCoords,
|
||||||
|
) -> Result<bool> {
|
||||||
let Some(block_type) = section.section.block_at(coords)?
|
let Some(block_type) = section.section.block_at(coords)?
|
||||||
.filter(|block_type| block_type.is(BlockFlag::Opaque))
|
.filter(|block_type| block_type.is(BlockFlag::Opaque))
|
||||||
else {
|
else {
|
||||||
|
@ -59,7 +67,14 @@ impl<'a> LayerEntry<'a> {
|
||||||
|
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
*self.block = Some(block_type);
|
*self.block = Some(block_type);
|
||||||
*self.biome = section.biomes.biome_at(section.y, coords)?.copied();
|
if let Some(biome) = section.biomes.biome_at(section.y, coords)? {
|
||||||
|
let (biome_index, _) = biome_list.insert_full(*biome);
|
||||||
|
*self.biome = NonZeroU16::new(
|
||||||
|
(biome_index + 1)
|
||||||
|
.try_into()
|
||||||
|
.expect("biome index not in range"),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if block_type.is(BlockFlag::Water) {
|
if block_type.is(BlockFlag::Water) {
|
||||||
|
@ -99,7 +114,7 @@ impl LayerData {
|
||||||
/// determined as the block that should be visible on the rendered
|
/// determined as the block that should be visible on the rendered
|
||||||
/// map. For water blocks, the height of the first non-water block
|
/// map. For water blocks, the height of the first non-water block
|
||||||
/// is additionally filled in as the water depth.
|
/// is additionally filled in as the water depth.
|
||||||
pub fn top_layer(chunk: &Chunk) -> Result<Option<LayerData>> {
|
pub fn top_layer(biome_list: &mut IndexSet<Biome>, chunk: &Chunk) -> Result<Option<LayerData>> {
|
||||||
use BLOCKS_PER_CHUNK as N;
|
use BLOCKS_PER_CHUNK as N;
|
||||||
|
|
||||||
if chunk.is_empty() {
|
if chunk.is_empty() {
|
||||||
|
@ -121,7 +136,7 @@ pub fn top_layer(chunk: &Chunk) -> Result<Option<LayerData>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let coords = SectionBlockCoords { xz, y };
|
let coords = SectionBlockCoords { xz, y };
|
||||||
if !entry.fill(section, coords)? {
|
if !entry.fill(biome_list, section, coords)? {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue