Fix critical structural issues: undo/redo, surface import, brush strength, i18n macro, level.dat export, GPU fallback, Nether/End support, dead code removal, dim serde, CPU perf, wasm-host, GPU constants, clippy warnings

- Replace OnceLock with Mutex<Option> for snapshot system in all 9 operations (undo/redo now works after redo)
- Fix import surface detection: scan actual packed block data for highest solid block instead of always using +15
- Apply brush strength threshold (< 0.3) in PaintOperation for natural edge falloff
- Remove broken t!() macro variant with variable substitution
- Complete level.dat with RandomSeed, GameType, Difficulty, Spawn, Time, DataPacks, etc.
- GPU viewport falls back to CPU renderer for multi-tile worlds
- Export supports Nether (DIM-1/region) and End (DIM1/region) dimension subdirectories
- Remove dead FloodOperation (duplicate of PaintOperation)
- Add log::warn for malformed dimension tile keys in serde
- Reduce CPU viewport base_size from 64 to 32 (4x fewer pixels)
- Simplify wasm-host host_get_seed to direct cast
- Add named constants for GPU buffer sizes
- Fix unused import warning in cli/main.rs
- cargo fmt
This commit is contained in:
loki5512344 2026-07-04 11:44:34 +02:00
parent 18613260a8
commit 2419b603c1
24 changed files with 339 additions and 271 deletions

View file

@ -71,8 +71,16 @@ impl eframe::App for TerrafierApp {
&& let Some(tile) = world.dimensions[0].tiles.values().next()
{
renderer.render(
&tile.heightmap.as_slice().try_into().unwrap_or_else(|_| [0; 16384]),
&tile.terrain.as_slice().try_into().unwrap_or_else(|_| [0; 16384]),
&tile
.heightmap
.as_slice()
.try_into()
.unwrap_or_else(|_| [0; 16384]),
&tile
.terrain
.as_slice()
.try_into()
.unwrap_or_else(|_| [0; 16384]),
self.show_heightmap,
);

View file

@ -1,5 +1,10 @@
use std::sync::Arc;
const TILE_SIZE: u32 = 128;
const TILE_CELLS: u32 = TILE_SIZE * TILE_SIZE;
const HEIGHTMAP_PACKED_SIZE: u64 = (TILE_CELLS / 2) as u64 * 4;
const TERRAIN_PACKED_SIZE: u64 = (TILE_CELLS / 4) as u64 * 4;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Uniforms {
@ -111,13 +116,13 @@ impl GpuRenderer {
});
let heightmap_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("heightmap_buffer"),
size: 8192 * 4,
size: HEIGHTMAP_PACKED_SIZE,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let terrain_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("terrain_buffer"),
size: 4096 * 4,
size: TERRAIN_PACKED_SIZE,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});

View file

@ -105,7 +105,7 @@ pub fn render(ui: &mut egui::Ui, app: &mut crate::app::TerrafierApp) {
max_tz = max_tz.max(tz);
}
let base_size = 64u32;
let base_size = 32u32;
let zoom = app.view_zoom;
let grid_w = ((max_tx - min_tx + 1) * base_size as i32) as usize;
@ -190,10 +190,7 @@ pub fn render(ui: &mut egui::Ui, app: &mut crate::app::TerrafierApp) {
// Viewport: allocate a large area for the world with scroll
let viewport_size = Vec2::new(grid_w as f32 * zoom, grid_h as f32 * zoom);
let (response, painter) = ui.allocate_painter(
viewport_size,
egui::Sense::click_and_drag(),
);
let (response, painter) = ui.allocate_painter(viewport_size, egui::Sense::click_and_drag());
// Handle scroll-wheel zoom
let scroll_delta = ui.input(|i| i.raw_scroll_delta);
@ -215,7 +212,10 @@ pub fn render(ui: &mut egui::Ui, app: &mut crate::app::TerrafierApp) {
texture_id,
egui::Rect::from_min_max(
egui::pos2(rect.min.x + offset.x, rect.min.y + offset.y),
egui::pos2(rect.min.x + offset.x + grid_w as f32 * app.view_zoom, rect.min.y + offset.y + grid_h as f32 * app.view_zoom),
egui::pos2(
rect.min.x + offset.x + grid_w as f32 * app.view_zoom,
rect.min.y + offset.y + grid_h as f32 * app.view_zoom,
),
),
egui::Rect::from_min_max(egui::pos2(0.0, 0.0), egui::pos2(1.0, 1.0)),
Color32::WHITE,
@ -229,8 +229,7 @@ pub fn render(ui: &mut egui::Ui, app: &mut crate::app::TerrafierApp) {
{
let bx = rect.min.x + offset.x + (sel_tx - min_tx) as f32 * scaled_size;
let bz = rect.min.y + offset.y + (sel_tz - min_tz) as f32 * scaled_size;
let border_rect =
egui::Rect::from_min_size(egui::pos2(bx, bz), Vec2::splat(scaled_size));
let border_rect = egui::Rect::from_min_size(egui::pos2(bx, bz), Vec2::splat(scaled_size));
painter.rect_stroke(
border_rect,
0.0,
@ -267,10 +266,12 @@ pub fn render(ui: &mut egui::Ui, app: &mut crate::app::TerrafierApp) {
let tz = (lz / scaled_size).floor() as i32 + min_tz;
if dim.tiles.contains_key(&(tx, tz)) {
app.selected_tile = Some((tx, tz));
let local_x =
((lx as u32 % scaled_size as u32) * TILE_SIZE as u32 / scaled_size as u32).min(127);
let local_z =
((lz as u32 % scaled_size as u32) * TILE_SIZE as u32 / scaled_size as u32).min(127);
let local_x = ((lx as u32 % scaled_size as u32) * TILE_SIZE as u32
/ scaled_size as u32)
.min(127);
let local_z = ((lz as u32 % scaled_size as u32) * TILE_SIZE as u32
/ scaled_size as u32)
.min(127);
app.brush_local_x = Some(local_x);
app.brush_local_z = Some(local_z);
app.status_message = format!(

View file

@ -5,16 +5,20 @@ pub fn try_render(ui: &mut egui::Ui, app: &mut crate::app::TerrafierApp) -> bool
let Some(ref world) = app.world else {
return false;
};
let dim = &world.dimensions[0];
let Some(dim) = world.dimensions.first() else {
return false;
};
// If world has more than one tile, fall back to CPU renderer
if dim.tiles.len() > 1 {
return false;
}
if let Some(ref renderer) = app.renderer
&& let Some(texture_id) = renderer.texture_id
{
let desired_size = Vec2::new(512.0, 512.0);
let (response, painter) = ui.allocate_painter(
desired_size,
egui::Sense::click_and_drag(),
);
let (response, painter) = ui.allocate_painter(desired_size, egui::Sense::click_and_drag());
let rect = response.rect;
// Handle scroll-wheel zoom
@ -50,8 +54,10 @@ pub fn try_render(ui: &mut egui::Ui, app: &mut crate::app::TerrafierApp) -> bool
// GPU only renders the first tile - position it correctly
// Use a default position for the rendered tile
let first_tile_pos = dim.tiles.keys().next().copied().unwrap_or((0, 0));
let tex_origin_x = rect.min.x + offset.x + (first_tile_pos.0 - min_tx) as f32 * display_size;
let tex_origin_y = rect.min.y + offset.y + (first_tile_pos.1 - min_tz) as f32 * display_size;
let tex_origin_x =
rect.min.x + offset.x + (first_tile_pos.0 - min_tx) as f32 * display_size;
let tex_origin_y =
rect.min.y + offset.y + (first_tile_pos.1 - min_tz) as f32 * display_size;
let tex_rect = egui::Rect::from_min_size(
egui::pos2(tex_origin_x, tex_origin_y),
@ -82,10 +88,14 @@ pub fn try_render(ui: &mut egui::Ui, app: &mut crate::app::TerrafierApp) -> bool
{
let tile_x_in_pixels = (sel_tx - min_tx) as f32 * display_size;
let tile_z_in_pixels = (sel_tz - min_tz) as f32 * display_size;
let brush_x =
rect.min.x + offset.x + tile_x_in_pixels + (bx as f32 * display_size / TILE_SIZE as f32);
let brush_z =
rect.min.y + offset.y + tile_z_in_pixels + (bz as f32 * display_size / TILE_SIZE as f32);
let brush_x = rect.min.x
+ offset.x
+ tile_x_in_pixels
+ (bx as f32 * display_size / TILE_SIZE as f32);
let brush_z = rect.min.y
+ offset.y
+ tile_z_in_pixels
+ (bz as f32 * display_size / TILE_SIZE as f32);
let brush_radius_px = app.brush_radius as f32 * display_size / TILE_SIZE as f32;
painter.circle_stroke(
egui::pos2(brush_x, brush_z),