Refactor structure, implement layers, add Erode/Fill/layer panel

- Restructure folder hierarchy: max 4 .rs per dir using subfolders
  (ops/operations -> brush/ + shaping/, model/ -> world/, io/* -> subtypes, etc.)
- Fix golden_test.rs comment, extract BrushApply helper, add World::with_heightmap
- Implement 6 layer data generators (Caves, River, Frost, Trees, Biome, Resources)
  with noise-based (OpenSimplex) and terrain-derived algorithms
- Fix clippy warnings, dead code cleanup, import path fixes
- GUI: add Erode&Fill tool modes with radio buttons/sliders/apply_tool
- GUI: add Layers panel with 6 checkboxes + CPU viewport overlay rendering
This commit is contained in:
loki5512344 2026-06-24 12:27:55 +02:00
parent 3d3798136d
commit bdd4564240
104 changed files with 5184 additions and 3912 deletions

View file

@ -0,0 +1,4 @@
mod palette;
mod palette_io;
pub use palette::*;

View file

@ -0,0 +1,82 @@
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockState {
pub name: String,
pub properties: HashMap<String, String>,
}
impl Hash for BlockState {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
let mut keys: Vec<&String> = self.properties.keys().collect();
keys.sort();
for k in keys {
k.hash(state);
self.properties[k].hash(state);
}
}
}
impl BlockState {
pub fn new(name: &str) -> Self {
BlockState {
name: name.to_string(),
properties: HashMap::new(),
}
}
pub fn with_property(mut self, key: &str, val: &str) -> Self {
self.properties.insert(key.to_string(), val.to_string());
self
}
}
/// A palette mapping unique `BlockState`s to compact indices.
pub struct BlockPalette {
pub entries: Vec<BlockState>,
index_map: HashMap<BlockState, u32>,
}
impl Default for BlockPalette {
fn default() -> Self {
Self::new()
}
}
impl BlockPalette {
pub fn new() -> Self {
BlockPalette {
entries: Vec::new(),
index_map: HashMap::new(),
}
}
/// Add a block state, or return its existing index.
pub fn add_or_get(&mut self, state: BlockState) -> u32 {
if let Some(&idx) = self.index_map.get(&state) {
return idx;
}
let idx = self.entries.len() as u32;
self.index_map.insert(state.clone(), idx);
self.entries.push(state);
idx
}
pub fn get(&self, index: u32) -> Option<&BlockState> {
self.entries.get(index as usize)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
/// Minimum bits per entry needed for this palette.
pub fn bits_per_entry(&self) -> u8 {
crate::bits::BitArray::bits_needed(self.entries.len())
}
}

View file

@ -1,39 +1,9 @@
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use terrafier_nbt::Tag;
/// A Minecraft block state — name plus key-value properties.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockState {
pub name: String,
pub properties: HashMap<String, String>,
}
impl Hash for BlockState {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
let mut keys: Vec<&String> = self.properties.keys().collect();
keys.sort();
for k in keys {
k.hash(state);
self.properties[k].hash(state);
}
}
}
use super::palette::{BlockPalette, BlockState};
impl BlockState {
pub fn new(name: &str) -> Self {
BlockState {
name: name.to_string(),
properties: HashMap::new(),
}
}
pub fn with_property(mut self, key: &str, val: &str) -> Self {
self.properties.insert(key.to_string(), val.to_string());
self
}
/// Parse from `TAG_Compound` with `"Name"` and optional `"Properties"`.
pub fn from_nbt(tag: &Tag) -> Option<Self> {
let map = match tag {
@ -78,53 +48,7 @@ impl BlockState {
}
}
/// A palette mapping unique `BlockState`s to compact indices.
pub struct BlockPalette {
pub entries: Vec<BlockState>,
index_map: HashMap<BlockState, u32>,
}
impl Default for BlockPalette {
fn default() -> Self {
Self::new()
}
}
impl BlockPalette {
pub fn new() -> Self {
BlockPalette {
entries: Vec::new(),
index_map: HashMap::new(),
}
}
/// Add a block state, or return its existing index.
pub fn add_or_get(&mut self, state: BlockState) -> u32 {
if let Some(&idx) = self.index_map.get(&state) {
return idx;
}
let idx = self.entries.len() as u32;
self.index_map.insert(state.clone(), idx);
self.entries.push(state);
idx
}
pub fn get(&self, index: u32) -> Option<&BlockState> {
self.entries.get(index as usize)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
/// Minimum bits per entry needed for this palette.
pub fn bits_per_entry(&self) -> u8 {
crate::bits::BitArray::bits_needed(self.entries.len())
}
/// Import from NBT palette list + block data `LongArray`.
pub fn from_nbt(palette_list: &[Tag], data: &[i64]) -> (Self, crate::bits::BitArray) {
let mut palette = BlockPalette::new();
@ -149,7 +73,8 @@ impl BlockPalette {
#[cfg(test)]
mod tests {
use super::*;
use super::super::palette::{BlockPalette, BlockState};
use terrafier_nbt::Tag;
#[test]
fn test_add_get() {