feat(render): apply placement in xy preview

This commit is contained in:
2026-07-18 18:19:55 +04:00
parent cc7a1a0faa
commit 4d024223bb
5 changed files with 102 additions and 32 deletions
@@ -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<VulkanStaticMesh, VulkanAssetMeshError> {
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];
+3 -1
View File
@@ -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,