aboutsummaryrefslogtreecommitdiff
path: root/adapters/fparkan-render-vulkan/src/asset_mesh.rs
diff options
context:
space:
mode:
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/asset_mesh.rs')
-rw-r--r--adapters/fparkan-render-vulkan/src/asset_mesh.rs58
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),