From 4df46865091fe12a3e04606d4710521516f35d85 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Sat, 18 Jul 2026 16:41:53 +0400 Subject: feat(terrain): bind map base textures --- adapters/fparkan-render-vulkan/src/asset_mesh.rs | 85 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 13 deletions(-) (limited to 'adapters/fparkan-render-vulkan/src') diff --git a/adapters/fparkan-render-vulkan/src/asset_mesh.rs b/adapters/fparkan-render-vulkan/src/asset_mesh.rs index 959b51a..57ab249 100644 --- a/adapters/fparkan-render-vulkan/src/asset_mesh.rs +++ b/adapters/fparkan-render-vulkan/src/asset_mesh.rs @@ -420,8 +420,10 @@ fn rotate_by_quaternion(position: [f32; 3], rotation: [f32; 4]) -> [f32; 3] { /// Projects validated `Land.msh` terrain geometry into the static Vulkan path. /// /// This bridge preserves the source triangle order from `TerrainFace28` and -/// consumes its validated position and UV0 streams. It is deliberately a -/// geometry-only terrain slice: source slot/material selection, camera +/// consumes its validated position and UV0 streams. Contiguous source faces are +/// retained as separate draw ranges keyed by their exact packed +/// `TerrainFace28.material_tag`, so a caller can resolve the map-local +/// material contract without changing topology. Slot selection, camera /// transforms, fog and surface-mask shading need their own evidence before /// they can be modeled as renderer state. /// @@ -592,16 +594,32 @@ fn static_terrain_indices(terrain: &LandMeshDocument) -> Result, Vulkan fn static_terrain_draw_ranges( terrain: &LandMeshDocument, ) -> Result, VulkanAssetMeshError> { - Ok(vec![VulkanStaticDrawRange { - first_index: 0, - index_count: u32::try_from(terrain.faces.len()) - .map_err(|_| VulkanAssetMeshError::IndexOutOfRange)? - .checked_mul(3) - .ok_or(VulkanAssetMeshError::IndexOutOfRange)?, - material_index: 0, - pipeline_state: LegacyPipelineState::default(), - alpha_test_reference: 0, - }]) + let mut ranges = Vec::new(); + let mut start_face = 0usize; + while start_face < terrain.faces.len() { + let material_index = terrain.faces[start_face].material_tag; + let mut end_face = start_face + 1; + while end_face < terrain.faces.len() + && terrain.faces[end_face].material_tag == material_index + { + end_face += 1; + } + ranges.push(VulkanStaticDrawRange { + first_index: u32::try_from(start_face) + .map_err(|_| VulkanAssetMeshError::IndexOutOfRange)? + .checked_mul(3) + .ok_or(VulkanAssetMeshError::IndexOutOfRange)?, + index_count: u32::try_from(end_face - start_face) + .map_err(|_| VulkanAssetMeshError::IndexOutOfRange)? + .checked_mul(3) + .ok_or(VulkanAssetMeshError::IndexOutOfRange)?, + material_index, + pipeline_state: LegacyPipelineState::default(), + alpha_test_reference: 0, + }); + start_face = end_face; + } + Ok(ranges) } fn static_model_indices_and_ranges( @@ -1109,9 +1127,13 @@ mod tests { } fn terrain_face(vertices: [u16; 3]) -> TerrainFace28 { + terrain_face_with_tag(0, vertices) + } + + fn terrain_face_with_tag(material_tag: u16, vertices: [u16; 3]) -> TerrainFace28 { TerrainFace28 { flags: FullSurfaceMask(0), - material_tag: 0, + material_tag, aux_tag: 0, vertices, neighbors: [None; 3], @@ -1119,4 +1141,41 @@ mod tests { raw: [0; 28], } } + + #[test] + fn terrain_ranges_preserve_face_order_and_packed_material_tags() { + let terrain = LandMeshDocument { + streams: Vec::new(), + nodes_raw: Vec::new(), + slots: TerrainSlotTable { + header_raw: Vec::new(), + slots_raw: Vec::new(), + }, + positions: vec![[0.0, 0.0, 0.0]; 3], + normals: Vec::new(), + uv0: vec![[0, 0]; 3], + accelerator: Vec::new(), + aux14: Vec::new(), + aux18: Vec::new(), + faces: vec![ + terrain_face_with_tag(0xff01, [0, 1, 2]), + terrain_face_with_tag(0xff01, [0, 1, 2]), + terrain_face_with_tag(0x0102, [0, 1, 2]), + terrain_face_with_tag(0xff01, [0, 1, 2]), + ], + }; + + let ranges = static_terrain_draw_ranges(&terrain).expect("representable terrain"); + + assert_eq!(ranges.len(), 3); + assert_eq!(ranges[0].first_index, 0); + assert_eq!(ranges[0].index_count, 6); + assert_eq!(ranges[0].material_index, 0xff01); + assert_eq!(ranges[1].first_index, 6); + assert_eq!(ranges[1].index_count, 3); + assert_eq!(ranges[1].material_index, 0x0102); + assert_eq!(ranges[2].first_index, 9); + assert_eq!(ranges[2].index_count, 3); + assert_eq!(ranges[2].material_index, 0xff01); + } } -- cgit v1.2.3