diff options
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/fparkan-game/Cargo.toml | 1 | ||||
| -rw-r--r-- | apps/fparkan-game/src/main.rs | 139 |
2 files changed, 125 insertions, 15 deletions
diff --git a/apps/fparkan-game/Cargo.toml b/apps/fparkan-game/Cargo.toml index 4d01c94..d285a20 100644 --- a/apps/fparkan-game/Cargo.toml +++ b/apps/fparkan-game/Cargo.toml @@ -12,6 +12,7 @@ fparkan-render = { path = "../../crates/fparkan-render", version = "0.1.0" } fparkan-platform-winit = { path = "../../adapters/fparkan-platform-winit", version = "0.1.0" } fparkan-render-vulkan = { path = "../../adapters/fparkan-render-vulkan", version = "0.1.0" } fparkan-runtime = { path = "../../crates/fparkan-runtime", version = "0.1.0" } +fparkan-terrain = { path = "../../crates/fparkan-terrain", version = "0.1.0" } fparkan-vfs = { path = "../../crates/fparkan-vfs", version = "0.1.0" } fparkan-world = { path = "../../crates/fparkan-world", version = "0.1.0" } winit = { version = "0.30", default-features = false, features = ["rwh_06"] } diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs index 286a23e..2bc7386 100644 --- a/apps/fparkan-game/src/main.rs +++ b/apps/fparkan-game/src/main.rs @@ -30,16 +30,18 @@ use fparkan_render::{ RenderSnapshotDraw, }; use fparkan_render_vulkan::{ - project_msh_to_static_mesh, VulkanPlanningBackend, VulkanSmokeFrameOutcome, - VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanStaticMaterial, VulkanStaticMesh, - VulkanStaticTexture, + project_land_msh_to_static_mesh_in_xz_frame, project_msh_to_static_mesh_in_xz_frame, + VulkanPlanningBackend, VulkanSmokeFrameOutcome, VulkanSmokeRenderer, + VulkanSmokeRendererCreateInfo, VulkanStaticMaterial, VulkanStaticMesh, VulkanStaticTexture, + VulkanStaticXzFrame, }; use fparkan_runtime::{ create, frame, load_mission, load_mission_static_preview, load_mission_static_preview_with_progress, load_mission_with_progress, loaded_mission_assets, - EngineConfig, EngineMode, EngineServices, MissionAssets, MissionLoadPhase, MissionObjectDraft, - MissionRequest, + loaded_mission_object_drafts, loaded_terrain, EngineConfig, EngineMode, EngineServices, + MissionAssets, MissionLoadPhase, MissionObjectDraft, MissionRequest, }; +use fparkan_terrain::TerrainWorld; use fparkan_vfs::DirectoryVfs; #[cfg(test)] use fparkan_world::OriginalObjectId; @@ -82,11 +84,17 @@ fn run(args: &[String]) -> Result<String, String> { if args.backend == RenderBackendMode::StaticVulkan { let mission_assets = loaded_mission_assets(&engine) .ok_or_else(|| "mission assets are unavailable after loading".to_string())?; - let preview = static_preview_mesh_and_materials(mission_assets)?; + let terrain = loaded_terrain(&engine) + .ok_or_else(|| "mission terrain is unavailable after loading".to_string())?; + let root = loaded_mission_object_drafts(&engine) + .and_then(|drafts| drafts.first()) + .ok_or_else(|| "first mission object draft is unavailable after loading".to_string())?; + let preview = static_preview_mesh_and_materials(mission_assets, terrain, root)?; return run_static_vulkan_mode( preview.mesh, preview.materials, preview.mesh_components, + preview.terrain_components, args.frames, &args.mission, loaded.object_count, @@ -183,25 +191,45 @@ struct StaticPreviewScene { mesh: VulkanStaticMesh, materials: Vec<VulkanStaticMaterial>, mesh_components: usize, + terrain_components: usize, } -/// Projects every MSH component of the explicitly bounded static-preview root -/// into one mesh and selector-keyed diffuse material descriptors. +/// Projects the mission terrain plus every MSH component of the explicitly +/// bounded static-preview root into one diagnostic XZ frame. /// /// This intentionally takes only the first MAT0 texture request for each MSH -/// batch selector. Later material phases, animation, lightmaps, transforms, -/// and gameplay visibility remain outside this preview bridge. -fn static_preview_mesh_and_materials(assets: &MissionAssets) -> Result<StaticPreviewScene, String> { +/// batch selector. The terrain uses an explicit white diagnostic texture; +/// terrain slot/material selection, orientation, camera, later material phases, +/// animation, lightmaps and gameplay visibility remain outside this bridge. +fn static_preview_mesh_and_materials( + assets: &MissionAssets, + terrain: &TerrainWorld, + root: &MissionObjectDraft, +) -> Result<StaticPreviewScene, String> { let visual_ids = assets.visuals_for_object(0); if visual_ids.is_empty() { return Err("first static preview root has no prepared visuals".to_string()); } + let terrain_mesh = terrain + .source_mesh() + .ok_or_else(|| "runtime terrain does not retain its validated source mesh".to_string())?; + let frame = static_preview_xz_frame(assets, visual_ids, terrain, root)?; let mut mesh = VulkanStaticMesh { vertices: Vec::new(), indices: Vec::new(), draw_ranges: Vec::new(), }; - let mut materials = Vec::new(); + let mut materials = vec![VulkanStaticMaterial { + material_index: 0, + texture: VulkanStaticTexture { + width: 1, + height: 1, + rgba8: vec![255, 255, 255, 255], + }, + }]; + let terrain_component = project_land_msh_to_static_mesh_in_xz_frame(terrain_mesh, frame) + .map_err(|err| format!("project mission terrain for Vulkan: {err}"))?; + append_static_preview_component(&mut mesh, terrain_component, &[(0, 0)])?; let mut mesh_components = 0; for visual_id in visual_ids { let visual = assets.visual_by_id(*visual_id).ok_or_else(|| { @@ -213,8 +241,13 @@ fn static_preview_mesh_and_materials(assets: &MissionAssets) -> Result<StaticPre let model = assets.model_by_id(model_id).ok_or_else(|| { format!("static preview visual {visual_id:?} references unknown model {model_id:?}") })?; - let component = project_msh_to_static_mesh(&model.validated) - .map_err(|err| format!("project mission MSH for Vulkan: {err}"))?; + let component = project_msh_to_static_mesh_in_xz_frame( + &model.validated, + frame, + root.position, + root.scale, + ) + .map_err(|err| format!("project mission MSH for Vulkan: {err}"))?; let selector_remap = static_preview_component_materials(assets, visual, &component, &mut materials)?; append_static_preview_component(&mut mesh, component, &selector_remap)?; @@ -227,9 +260,79 @@ fn static_preview_mesh_and_materials(assets: &MissionAssets) -> Result<StaticPre mesh, materials, mesh_components, + terrain_components: 1, }) } +fn static_preview_xz_frame( + assets: &MissionAssets, + visual_ids: &[fparkan_assets::AssetId<PreparedVisual>], + terrain: &TerrainWorld, + root: &MissionObjectDraft, +) -> Result<VulkanStaticXzFrame, String> { + if !root + .position + .iter() + .chain(root.scale.iter()) + .all(|value| value.is_finite()) + { + return Err("first static preview root has a non-finite position or scale".to_string()); + } + let mut min_x = f32::INFINITY; + let mut max_x = f32::NEG_INFINITY; + let mut min_z = f32::INFINITY; + let mut max_z = f32::NEG_INFINITY; + let terrain_positions = terrain + .source_positions() + .ok_or_else(|| "runtime terrain does not retain source positions".to_string())?; + for position in terrain_positions { + extend_static_preview_xz_bounds(*position, &mut min_x, &mut max_x, &mut min_z, &mut max_z)?; + } + for visual_id in visual_ids { + let visual = assets.visual_by_id(*visual_id).ok_or_else(|| { + format!("first static preview root references unknown visual {visual_id:?}") + })?; + let Some(model_id) = visual.model_id else { + continue; + }; + let model = assets.model_by_id(model_id).ok_or_else(|| { + format!("static preview visual {visual_id:?} references unknown model {model_id:?}") + })?; + for position in &model.validated.positions { + extend_static_preview_xz_bounds( + [ + position[0] * root.scale[0] + root.position[0], + position[1] * root.scale[1] + root.position[1], + position[2] * root.scale[2] + root.position[2], + ], + &mut min_x, + &mut max_x, + &mut min_z, + &mut max_z, + )?; + } + } + VulkanStaticXzFrame::from_bounds(min_x, max_x, min_z, max_z) + .map_err(|err| format!("build static preview XZ frame: {err}")) +} + +fn extend_static_preview_xz_bounds( + position: [f32; 3], + min_x: &mut f32, + max_x: &mut f32, + min_z: &mut f32, + max_z: &mut f32, +) -> Result<(), String> { + if !position.iter().all(|value| value.is_finite()) { + return Err("static preview contains a non-finite XZ position".to_string()); + } + *min_x = min_x.min(position[0]); + *max_x = max_x.max(position[0]); + *min_z = min_z.min(position[2]); + *max_z = max_z.max(position[2]); + Ok(()) +} + fn static_preview_component_materials( assets: &MissionAssets, visual: &PreparedVisual, @@ -361,6 +464,7 @@ fn run_static_vulkan_mode( mesh: VulkanStaticMesh, materials: Vec<VulkanStaticMaterial>, mesh_components: usize, + terrain_components: usize, target_frames: u64, mission: &str, object_count: usize, @@ -371,6 +475,7 @@ fn run_static_vulkan_mode( mesh, materials, mesh_components, + terrain_components, target_frames, mission, object_count, @@ -385,6 +490,7 @@ struct StaticVulkanApp { mesh: VulkanStaticMesh, materials: Vec<VulkanStaticMaterial>, mesh_components: usize, + terrain_components: usize, target_frames: u64, mission: String, object_count: usize, @@ -401,6 +507,7 @@ impl StaticVulkanApp { mesh: VulkanStaticMesh, materials: Vec<VulkanStaticMaterial>, mesh_components: usize, + terrain_components: usize, target_frames: u64, mission: &str, object_count: usize, @@ -409,6 +516,7 @@ impl StaticVulkanApp { mesh, materials, mesh_components, + terrain_components, target_frames, mission: mission.to_string(), object_count, @@ -451,11 +559,12 @@ impl StaticVulkanApp { return; } self.output = Some(format!( - "{{\"report_kind\":\"rendered-static-mission\",\"backend\":\"vulkan-static\",\"window\":\"native\",\"mission\":{},\"objects\":{},\"frames\":{},\"mesh_components\":{},\"material_descriptors\":{},\"swapchain\":[{},{}],\"swapchain_images\":{},\"validation_warnings\":{},\"validation_errors\":{},\"readback_bytes\":{},\"readback_hash\":{}}}", + "{{\"report_kind\":\"rendered-static-mission\",\"backend\":\"vulkan-static\",\"window\":\"native\",\"mission\":{},\"objects\":{},\"frames\":{},\"mesh_components\":{},\"terrain_components\":{},\"material_descriptors\":{},\"swapchain\":[{},{}],\"swapchain_images\":{},\"validation_warnings\":{},\"validation_errors\":{},\"readback_bytes\":{},\"readback_hash\":{}}}", json_string(&self.mission), self.object_count, self.frames_presented, self.mesh_components, + self.terrain_components, self.materials.len(), report.renderer_report.swapchain_extent.0, report.renderer_report.swapchain_extent.1, |
