diff options
| author | Valentin Popov <valentin@popov.link> | 2026-07-18 14:50:52 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-07-18 14:50:52 +0300 |
| commit | fea984ddf51ba1e56ce800b9f370ac4208dcfcab (patch) | |
| tree | 96345c489f8c8d80ab0cbeb9d5dcf054217d6859 /adapters/fparkan-render-vulkan/src/asset_mesh.rs | |
| parent | 2a793f1854231be6da930a3692dce691a6426211 (diff) | |
| download | fparkan-fea984ddf51ba1e56ce800b9f370ac4208dcfcab.tar.xz fparkan-fea984ddf51ba1e56ce800b9f370ac4208dcfcab.zip | |
feat(render): diagnose captured camera clipping
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/asset_mesh.rs')
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/asset_mesh.rs | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/adapters/fparkan-render-vulkan/src/asset_mesh.rs b/adapters/fparkan-render-vulkan/src/asset_mesh.rs index d92f76d..413eba6 100644 --- a/adapters/fparkan-render-vulkan/src/asset_mesh.rs +++ b/adapters/fparkan-render-vulkan/src/asset_mesh.rs @@ -5,6 +5,9 @@ use fparkan_msh::ModelAsset; use fparkan_render::{LegacyIron3dEulerTransform, LegacyPipelineState}; use fparkan_terrain_format::LandMeshDocument; +/// Legacy `Land.msh` stored-height to mission-world-height scale. +const LEGACY_LAND_HEIGHT_SCALE: f32 = 1.0 / 32.0; + /// Error returned when a validated MSH cannot enter the current static GPU path. #[derive(Clone, Debug, Eq, PartialEq)] pub enum VulkanAssetMeshError { @@ -383,6 +386,31 @@ pub fn project_land_msh_to_static_mesh_in_world_space( }) } +/// Projects terrain into the mission space consumed by the legacy D3D7 camera. +/// +/// `Land.msh` stores horizontal coordinates directly, while its height stream +/// uses 1/32 units. The scale is evidenced by the GOG `AutoDemo` map: raw terrain +/// heights `95.29412..288.23532` become `2.978..9.007`, matching its placed +/// object heights and the live Ngi32 camera's world-space Z coordinate. +/// +/// This conversion is intentionally confined to the captured legacy-camera +/// path. [`project_land_msh_to_static_mesh_in_world_space`] remains the raw +/// source-coordinate bridge for format inspection. +/// +/// # Errors +/// +/// Returns [`VulkanAssetMeshError`] when the terrain cannot enter the static +/// Vulkan input contract. +pub fn project_land_msh_to_static_mesh_in_legacy_world_space( + terrain: &LandMeshDocument, +) -> Result<VulkanStaticMesh, VulkanAssetMeshError> { + let mut mesh = project_land_msh_to_static_mesh_in_world_space(terrain)?; + for vertex in &mut mesh.vertices { + vertex.position[2] *= LEGACY_LAND_HEIGHT_SCALE; + } + Ok(mesh) +} + fn static_terrain_indices(terrain: &LandMeshDocument) -> Result<Vec<u32>, VulkanAssetMeshError> { let mut indices = Vec::with_capacity( terrain @@ -699,6 +727,36 @@ mod tests { assert_eq!(mesh.vertices[0].uv, [1.0, -0.5]); } + #[test] + fn legacy_world_space_terrain_scales_only_stored_height() { + let terrain = LandMeshDocument { + streams: Vec::new(), + nodes_raw: Vec::new(), + slots: TerrainSlotTable { + header_raw: Vec::new(), + slots_raw: Vec::new(), + }, + positions: vec![ + [100.0, 200.0, 96.0], + [300.0, 400.0, 288.0], + [500.0, 600.0, 192.0], + ], + normals: Vec::new(), + uv0: vec![[0, 0]; 3], + accelerator: Vec::new(), + aux14: Vec::new(), + aux18: Vec::new(), + faces: vec![terrain_face([0, 1, 2])], + }; + + let mesh = project_land_msh_to_static_mesh_in_legacy_world_space(&terrain) + .expect("representable terrain"); + + assert_eq!(mesh.vertices[0].position, [100.0, 200.0, 3.0]); + assert_eq!(mesh.vertices[1].position, [300.0, 400.0, 9.0]); + assert_eq!(mesh.vertices[2].position, [500.0, 600.0, 6.0]); + } + fn terrain_face(vertices: [u16; 3]) -> TerrainFace28 { TerrainFace28 { flags: FullSurfaceMask(0), |
