feat(render): diagnose captured camera clipping
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -40,9 +40,9 @@ mod swapchain_resources;
|
||||
mod validation;
|
||||
|
||||
pub use self::asset_mesh::{
|
||||
project_land_msh_to_static_mesh, project_land_msh_to_static_mesh_in_world_space,
|
||||
project_land_msh_to_static_mesh_in_xy_frame, project_msh_to_static_mesh,
|
||||
project_msh_to_static_mesh_in_world_space,
|
||||
project_land_msh_to_static_mesh, project_land_msh_to_static_mesh_in_legacy_world_space,
|
||||
project_land_msh_to_static_mesh_in_world_space, project_land_msh_to_static_mesh_in_xy_frame,
|
||||
project_msh_to_static_mesh, project_msh_to_static_mesh_in_world_space,
|
||||
project_msh_to_static_mesh_in_world_space_with_transform,
|
||||
project_msh_to_static_mesh_in_xy_frame, VulkanAssetMeshError, VulkanStaticXyFrame,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user