From efab61a45c8837d3c2aaec464d8f6243fecb7a38 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Thu, 19 Feb 2026 09:46:23 +0000 Subject: feat(render-core): add default UV scale and refactor UV mapping logic - Introduced a constant `DEFAULT_UV_SCALE` for UV scaling. - Refactored UV mapping in `build_render_mesh` to use the new constant. - Simplified `compute_bounds` functions by extracting common logic into `compute_bounds_impl`. test(render-core): add tests for rendering with empty and multi-node models - Added tests to verify behavior when building render meshes from models with no slots and multiple nodes. - Ensured UV scaling is correctly applied in tests. feat(render-demo): add FOV argument and improve error handling - Added a `--fov` command-line argument to set the field of view. - Enhanced error messages for texture resolution failures. - Updated MVP computation to use the new FOV parameter. fix(rsli): improve error handling in LZH decompression - Added checks to prevent out-of-bounds access in LZH decoding logic. refactor(texm): streamline texture parsing and decoding tests - Created a helper function `build_texm_payload` for constructing test payloads. - Added tests for various texture formats including RGB565, RGB556, ARGB4444, and Luminance Alpha. - Improved error handling for invalid TEXM headers and mip bounds. --- crates/render-core/src/lib.rs | 51 ++++++++++---------- crates/render-core/src/tests.rs | 102 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 27 deletions(-) (limited to 'crates/render-core/src') diff --git a/crates/render-core/src/lib.rs b/crates/render-core/src/lib.rs index ddb93fb..d06761a 100644 --- a/crates/render-core/src/lib.rs +++ b/crates/render-core/src/lib.rs @@ -1,5 +1,7 @@ use msh_core::Model; +pub const DEFAULT_UV_SCALE: f32 = 1024.0; + #[derive(Clone, Debug)] pub struct RenderVertex { pub position: [f32; 3], @@ -58,7 +60,12 @@ pub fn build_render_mesh(model: &Model, lod: usize, group: usize) -> RenderMesh let uv = uv0 .and_then(|uvs| uvs.get(final_idx)) .copied() - .map(|packed| [packed[0] as f32 / 1024.0, packed[1] as f32 / 1024.0]) + .map(|packed| { + [ + packed[0] as f32 / DEFAULT_UV_SCALE, + packed[1] as f32 / DEFAULT_UV_SCALE, + ] + }) .unwrap_or([0.0, 0.0]); vertices.push(RenderVertex { position: *pos, @@ -76,38 +83,28 @@ pub fn build_render_mesh(model: &Model, lod: usize, group: usize) -> RenderMesh } pub fn compute_bounds(vertices: &[[f32; 3]]) -> Option<([f32; 3], [f32; 3])> { - let mut iter = vertices.iter(); - let first = iter.next()?; - let mut min_v = *first; - let mut max_v = *first; - - for v in iter { - for i in 0..3 { - if v[i] < min_v[i] { - min_v[i] = v[i]; - } - if v[i] > max_v[i] { - max_v[i] = v[i]; - } - } - } - - Some((min_v, max_v)) + compute_bounds_impl(vertices.iter().copied()) } pub fn compute_bounds_for_mesh(vertices: &[RenderVertex]) -> Option<([f32; 3], [f32; 3])> { - let mut iter = vertices.iter(); - let first = iter.next()?; - let mut min_v = first.position; - let mut max_v = first.position; + compute_bounds_impl(vertices.iter().map(|v| v.position)) +} + +fn compute_bounds_impl(mut positions: I) -> Option<([f32; 3], [f32; 3])> +where + I: Iterator, +{ + let first = positions.next()?; + let mut min_v = first; + let mut max_v = first; - for v in iter { + for pos in positions { for i in 0..3 { - if v.position[i] < min_v[i] { - min_v[i] = v.position[i]; + if pos[i] < min_v[i] { + min_v[i] = pos[i]; } - if v.position[i] > max_v[i] { - max_v[i] = v.position[i]; + if pos[i] > max_v[i] { + max_v[i] = pos[i]; } } } diff --git a/crates/render-core/src/tests.rs b/crates/render-core/src/tests.rs index 22103c6..c9b55a0 100644 --- a/crates/render-core/src/tests.rs +++ b/crates/render-core/src/tests.rs @@ -129,3 +129,105 @@ fn compute_bounds_for_mesh_handles_empty_and_non_empty() { assert_eq!(bounds.0, [-2.0, -1.0, 0.5]); assert_eq!(bounds.1, [1.0, 5.0, 9.0]); } + +fn nodes_with_slot_refs(slot_ids: &[Option]) -> Vec { + let mut out = vec![0u8; slot_ids.len().saturating_mul(38)]; + for (node_index, slot_id) in slot_ids.iter().copied().enumerate() { + let node_off = node_index * 38; + for i in 0..15 { + let off = node_off + 8 + i * 2; + out[off..off + 2].copy_from_slice(&u16::MAX.to_le_bytes()); + } + if let Some(slot_id) = slot_id { + out[node_off + 8..node_off + 10].copy_from_slice(&slot_id.to_le_bytes()); + } + } + out +} + +fn slot(batch_start: u16, batch_count: u16) -> msh_core::Slot { + msh_core::Slot { + tri_start: 0, + tri_count: 0, + batch_start, + batch_count, + aabb_min: [0.0; 3], + aabb_max: [0.0; 3], + sphere_center: [0.0; 3], + sphere_radius: 0.0, + opaque: [0; 5], + } +} + +fn batch(index_start: u32, index_count: u16, base_vertex: u32) -> msh_core::Batch { + msh_core::Batch { + batch_flags: 0, + material_index: 0, + opaque4: 0, + opaque6: 0, + index_count, + index_start, + opaque14: 0, + base_vertex, + } +} + +#[test] +fn build_render_mesh_handles_empty_slot_model() { + let model = msh_core::Model { + node_stride: 38, + node_count: 1, + nodes_raw: nodes_with_slot_refs(&[None]), + slots: Vec::new(), + positions: vec![[0.0, 0.0, 0.0]], + normals: None, + uv0: None, + indices: Vec::new(), + batches: Vec::new(), + node_names: None, + }; + + let mesh = build_render_mesh(&model, 0, 0); + assert!(mesh.vertices.is_empty()); + assert_eq!(mesh.batch_count, 0); + assert_eq!(mesh.triangle_count(), 0); +} + +#[test] +fn build_render_mesh_supports_multi_node_and_uv_scaling() { + let model = msh_core::Model { + node_stride: 38, + node_count: 2, + nodes_raw: nodes_with_slot_refs(&[Some(0), Some(1)]), + slots: vec![slot(0, 1), slot(1, 1)], + positions: vec![ + [0.0, 0.0, 0.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [2.0, 0.0, 0.0], + [3.0, 0.0, 0.0], + [2.0, 1.0, 0.0], + ], + normals: None, + uv0: Some(vec![ + [1024, -1024], + [512, 256], + [0, 0], + [1024, 1024], + [2048, 1024], + [1024, 0], + ]), + indices: vec![0, 1, 2, 0, 1, 2], + batches: vec![batch(0, 3, 0), batch(3, 3, 3)], + node_names: None, + }; + + let mesh = build_render_mesh(&model, 0, 0); + assert_eq!(mesh.batch_count, 2); + assert_eq!(mesh.vertices.len(), 6); + assert_eq!(mesh.triangle_count(), 2); + assert_eq!(mesh.vertices[0].uv0, [1.0, -1.0]); + assert_eq!(mesh.vertices[1].uv0, [0.5, 0.25]); + assert_eq!(mesh.vertices[2].uv0, [0.0, 0.0]); + assert_eq!(mesh.vertices[3].uv0, [1.0, 1.0]); +} -- cgit v1.2.3