From 4d024223bb00586b05e8fb5fd1d2469ce7bbb91d Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Sat, 18 Jul 2026 18:19:55 +0400 Subject: feat(render): apply placement in xy preview --- adapters/fparkan-render-vulkan/src/asset_mesh.rs | 58 ++++++++++++++++++++++++ adapters/fparkan-render-vulkan/src/ffi.rs | 4 +- 2 files changed, 61 insertions(+), 1 deletion(-) (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 57ab249..ba67437 100644 --- a/adapters/fparkan-render-vulkan/src/asset_mesh.rs +++ b/adapters/fparkan-render-vulkan/src/asset_mesh.rs @@ -174,6 +174,38 @@ pub fn project_msh_to_static_mesh_in_xy_frame( }) } +/// Projects a static MSH preview through the recovered placement transform and +/// then into a shared diagnostic XY frame. +/// +/// This is the diagnostic-camera counterpart of +/// [`project_msh_to_static_mesh_in_world_space_with_node_fallback_poses`]. It +/// deliberately keeps the diagnostic camera itself separate from the original +/// D3D7 camera, while ensuring that mission placement uses the same proven +/// `Rz(z) * Ry(y) * Rx(x)` convention in both preview paths. +/// +/// # Errors +/// +/// Returns [`VulkanAssetMeshError`] when the model cannot be represented by +/// the static bridge or a transformed coordinate is non-finite. +pub fn project_msh_to_static_mesh_in_xy_frame_with_node_fallback_poses( + model: &ModelAsset, + frame: VulkanStaticXyFrame, + transform: LegacyIron3dEulerTransform, + scale: [f32; 3], +) -> Result { + let mut mesh = project_msh_to_static_mesh_in_world_space_with_node_fallback_poses( + model, transform, scale, + )?; + for vertex in &mut mesh.vertices { + if !vertex.position.iter().all(|value| value.is_finite()) { + return Err(VulkanAssetMeshError::NonFinitePosition); + } + let projected = frame.project(vertex.position); + vertex.position = [projected[0], projected[1], 0.0]; + } + Ok(mesh) +} + /// Projects MSH geometry into source world coordinates with known translation and scale. /// /// Unlike [`project_msh_to_static_mesh_in_xy_frame`], this does not normalize @@ -906,6 +938,32 @@ mod tests { assert_eq!(mesh.vertices[2].position, [10.0, 22.0, 30.0]); } + #[test] + fn xy_preview_applies_recovered_iron3d_orientation_before_framing() { + let source = model( + vec![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]], + vec![0, 1, 2], + vec![batch(0, 3, 0)], + ); + let frame = + VulkanStaticXyFrame::from_bounds(0.0, 4.0, 0.0, 4.0).expect("valid diagnostic frame"); + + let mesh = project_msh_to_static_mesh_in_xy_frame_with_node_fallback_poses( + &source, + frame, + LegacyIron3dEulerTransform { + translation: [2.0, 1.0, 0.0], + orientation_radians: [0.0, 0.0, std::f32::consts::FRAC_PI_2], + }, + [1.0; 3], + ) + .expect("diagnostic static mesh"); + + assert!(mesh.vertices[0].position[0].abs() < 0.0001); + assert!(mesh.vertices[0].position[1].abs() < 0.0001); + assert_eq!(mesh.vertices[0].position[2], 0.0); + } + #[test] fn world_space_msh_applies_each_node_fallback_pose_before_root_transform() { let mut nodes = vec![0_u8; 76]; diff --git a/adapters/fparkan-render-vulkan/src/ffi.rs b/adapters/fparkan-render-vulkan/src/ffi.rs index f5428e8..86d8b45 100644 --- a/adapters/fparkan-render-vulkan/src/ffi.rs +++ b/adapters/fparkan-render-vulkan/src/ffi.rs @@ -45,7 +45,9 @@ pub use self::asset_mesh::{ project_msh_to_static_mesh, project_msh_to_static_mesh_in_world_space, project_msh_to_static_mesh_in_world_space_with_node_fallback_poses, project_msh_to_static_mesh_in_world_space_with_transform, - project_msh_to_static_mesh_in_xy_frame, VulkanAssetMeshError, VulkanStaticXyFrame, + project_msh_to_static_mesh_in_xy_frame, + project_msh_to_static_mesh_in_xy_frame_with_node_fallback_poses, VulkanAssetMeshError, + VulkanStaticXyFrame, }; pub use self::capabilities::{ probe_vulkan_runtime_capabilities, probe_vulkan_runtime_capabilities_for_request, -- cgit v1.2.3