From 5f8f41f60ed6142781bc3883f26e03cdfe2a85e3 Mon Sep 17 00:00:00 2001 From: LachyFS <100457804+LachyFS@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:23:56 +1100 Subject: [PATCH] fix: return Uint16Array from block_ids and add batch fill_column - Change block_ids()/transparent_block_ids() to return Uint16Array directly, eliminating per-call Vec allocation - Add Chunk::fill_column() for efficient batch column filling - Add non_air_blocks() accessor and tests --- crates/urath-core/src/chunk.rs | 54 ++++++++++++++++++++++++++++++++++ crates/urath-wasm/src/lib.rs | 24 +++++---------- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/crates/urath-core/src/chunk.rs b/crates/urath-core/src/chunk.rs index ebe190a..943493c 100644 --- a/crates/urath-core/src/chunk.rs +++ b/crates/urath-core/src/chunk.rs @@ -196,6 +196,30 @@ impl Chunk { count } + /// Fill a column at (x, z) from y=0 to y=height-1 with a block ID. + /// + /// More efficient than calling `set()` in a loop because it computes + /// the base index once and increments by stride, and batches the + /// `non_air_count` update. + pub fn fill_column(&mut self, x: usize, z: usize, height: usize, block_id: u16) { + if x >= self.size || z >= self.size { + return; + } + let max = height.min(self.size); + let base = x + z * self.size * self.size; + let stride = self.size; // y stride in the x + y*size + z*size*size layout + for y in 0..max { + let idx = base + y * stride; + let old = self.blocks[idx]; + if old == 0 && block_id != 0 { + self.non_air_count += 1; + } else if old != 0 && block_id == 0 { + self.non_air_count -= 1; + } + self.blocks[idx] = block_id; + } + } + /// Check if (x, y, z) is air (block ID 0). #[inline] pub fn is_air(&self, x: usize, y: usize, z: usize) -> bool { @@ -208,6 +232,12 @@ impl Chunk { self.non_air_count == 0 } + /// Number of non-air blocks in the chunk. O(1). + #[inline] + pub fn non_air_blocks(&self) -> u32 { + self.non_air_count + } + /// True if every block is non-air. O(1). #[inline] pub fn is_solid(&self) -> bool { @@ -459,4 +489,28 @@ mod tests { let data = vec![0u16; 10]; // wrong size assert!(chunk.replace_blocks(&data).is_err()); } + + #[test] + fn fill_column_basic() { + let mut chunk = Chunk::new(4).unwrap(); + chunk.fill_column(1, 2, 3, 5); + assert_eq!(chunk.get(1, 0, 2), 5); + assert_eq!(chunk.get(1, 1, 2), 5); + assert_eq!(chunk.get(1, 2, 2), 5); + assert_eq!(chunk.get(1, 3, 2), 0); // height=3, so y=3 is air + assert!(!chunk.is_empty()); + } + + #[test] + fn fill_column_overwrite() { + let mut chunk = Chunk::new(4).unwrap(); + chunk.fill_column(0, 0, 4, 1); + assert_eq!(chunk.non_air_blocks(), 4); + chunk.fill_column(0, 0, 2, 0); // clear first 2 + assert_eq!(chunk.non_air_blocks(), 2); + assert_eq!(chunk.get(0, 0, 0), 0); + assert_eq!(chunk.get(0, 1, 0), 0); + assert_eq!(chunk.get(0, 2, 0), 1); + assert_eq!(chunk.get(0, 3, 0), 1); + } } diff --git a/crates/urath-wasm/src/lib.rs b/crates/urath-wasm/src/lib.rs index 9dcc0f4..10aa28f 100644 --- a/crates/urath-wasm/src/lib.rs +++ b/crates/urath-wasm/src/lib.rs @@ -53,10 +53,7 @@ impl WasmChunk { /// Fill a column from y=0 to y=height-1 with a block ID. pub fn fill_column(&mut self, x: usize, z: usize, height: usize, block_id: u16) { - let max = height.min(self.inner.size()); - for y in 0..max { - self.inner.set(x, y, z, block_id); - } + self.inner.fill_column(x, z, height, block_id); } /// Copy all block data into a new Uint16Array. @@ -195,10 +192,9 @@ impl WasmMeshResult { js_sys::Float32Array::from(&self.opaque.ao[..]) } - /// Opaque block IDs (Float32Array, cast from u16). - pub fn block_ids(&self) -> js_sys::Float32Array { - let f32_vec: Vec = self.opaque.block_ids.iter().map(|&id| id as f32).collect(); - js_sys::Float32Array::from(&f32_vec[..]) + /// Opaque block IDs (Uint16Array). + pub fn block_ids(&self) -> js_sys::Uint16Array { + js_sys::Uint16Array::from(&self.opaque.block_ids[..]) } /// Opaque UV coordinates (Float32Array, 2 floats per vertex). @@ -243,15 +239,9 @@ impl WasmMeshResult { js_sys::Float32Array::from(&self.transparent.ao[..]) } - /// Transparent block IDs (Float32Array, cast from u16). - pub fn transparent_block_ids(&self) -> js_sys::Float32Array { - let f32_vec: Vec = self - .transparent - .block_ids - .iter() - .map(|&id| id as f32) - .collect(); - js_sys::Float32Array::from(&f32_vec[..]) + /// Transparent block IDs (Uint16Array). + pub fn transparent_block_ids(&self) -> js_sys::Uint16Array { + js_sys::Uint16Array::from(&self.transparent.block_ids[..]) } /// Transparent UV coordinates (Float32Array, 2 floats per vertex).