aboutsummaryrefslogtreecommitdiff
path: root/adapters/fparkan-render-vulkan
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-07-18 15:41:53 +0300
committerValentin Popov <valentin@popov.link>2026-07-18 15:41:53 +0300
commit4df46865091fe12a3e04606d4710521516f35d85 (patch)
treed79eb47661b0922a5a4d040eb0d8022f221b66d5 /adapters/fparkan-render-vulkan
parente0f0e4a18ef9f2b6ca34917d4436e42eeb7f1eb1 (diff)
downloadfparkan-4df46865091fe12a3e04606d4710521516f35d85.tar.xz
fparkan-4df46865091fe12a3e04606d4710521516f35d85.zip
feat(terrain): bind map base textures
Diffstat (limited to 'adapters/fparkan-render-vulkan')
-rw-r--r--adapters/fparkan-render-vulkan/src/asset_mesh.rs85
1 files changed, 72 insertions, 13 deletions
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<Vec<u32>, Vulkan
fn static_terrain_draw_ranges(
terrain: &LandMeshDocument,
) -> Result<Vec<VulkanStaticDrawRange>, 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);
+ }
}