diff options
Diffstat (limited to 'apps/fparkan-vulkan-smoke/src')
| -rw-r--r-- | apps/fparkan-vulkan-smoke/src/main.rs | 224 |
1 files changed, 171 insertions, 53 deletions
diff --git a/apps/fparkan-vulkan-smoke/src/main.rs b/apps/fparkan-vulkan-smoke/src/main.rs index fc725d8..48bd766 100644 --- a/apps/fparkan-vulkan-smoke/src/main.rs +++ b/apps/fparkan-vulkan-smoke/src/main.rs @@ -16,7 +16,8 @@ use fparkan_platform_winit::{window_native_handles, WinitWindowPlan}; use fparkan_render_vulkan::{ project_msh_to_static_mesh, VulkanSmokeBootstrapProgress, VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanSmokeRendererReport, - VulkanSmokeShutdownReport, VulkanStaticMesh, VulkanStaticTexture, VulkanValidationReport, + VulkanSmokeShutdownReport, VulkanStaticMaterial, VulkanStaticMesh, VulkanStaticTexture, + VulkanValidationReport, }; use serde::Serialize; use std::path::PathBuf; @@ -56,7 +57,7 @@ fn main() { fn run(args: &[String]) -> Result<String, String> { let options = SmokeOptions::parse(args)?; let mesh = options.load_mesh()?; - let texture = options.load_texture()?; + let materials = options.load_materials(&mesh)?; remove_stale_output(&options)?; let event_loop = EventLoop::new().map_err(|err| format!("winit event loop: {err}"))?; event_loop.set_control_flow(ControlFlow::Poll); @@ -67,7 +68,7 @@ fn run(args: &[String]) -> Result<String, String> { Arc::clone(&completed), Arc::clone(&progress), ); - let mut app = SmokeApp::new(options, mesh, texture, completed, progress); + let mut app = SmokeApp::new(options, mesh, materials, completed, progress); if let Err(err) = event_loop.run_app(&mut app) { app.error = Some(format!("winit event loop: {err}")); } @@ -132,9 +133,17 @@ struct WearMaterialInput { } #[derive(Clone, Debug, Eq, PartialEq)] +struct WearInput { + root: PathBuf, + archive: String, + name: String, +} + +#[derive(Clone, Debug, Eq, PartialEq)] enum TextureInput { Direct(ResourceInput), WearMaterial(WearMaterialInput), + WearBatchMaterials(WearInput), } impl TextureInput { @@ -142,13 +151,14 @@ impl TextureInput { match self { Self::Direct(_) => "original-texm", Self::WearMaterial(_) => "original-wear-mat0-texm", + Self::WearBatchMaterials(_) => "original-wear-mat0-texm-batch", } } fn archive(&self) -> &str { match self { Self::Direct(input) => &input.archive, - Self::WearMaterial(_) => "Textures.lib", + Self::WearMaterial(_) | Self::WearBatchMaterials(_) => "Textures.lib", } } @@ -156,7 +166,7 @@ impl TextureInput { match self { Self::Direct(input) => &input.name, // The selected name comes from MAT0 at load time and is not a CLI request. - Self::WearMaterial(_) => "", + Self::WearMaterial(_) | Self::WearBatchMaterials(_) => "", } } } @@ -363,9 +373,16 @@ impl SmokeOptions { material_index, })) } + (Some(root), Some(archive), Some(name), None) => { + Some(TextureInput::WearBatchMaterials(WearInput { + root, + archive, + name, + })) + } _ => { return Err( - "--wear-root, --wear-archive, --wear-name and --material-index must be supplied together" + "--wear-root, --wear-archive and --wear-name must be supplied together; --material-index is optional" .to_string(), ); } @@ -400,42 +417,93 @@ impl SmokeOptions { } } - fn load_texture(&self) -> Result<Option<LoadedTexture>, String> { - self.texture_input.as_ref().map_or(Ok(None), |input| { - let (image, selected_name) = match input { - TextureInput::Direct(input) => ( - fparkan_inspection::load_texture_mip0_rgba8_from_root( + fn load_materials(&self, mesh: &VulkanStaticMesh) -> Result<Vec<LoadedTexture>, String> { + let Some(input) = self.texture_input.as_ref() else { + return Ok(Vec::new()); + }; + match input { + TextureInput::Direct(input) => Ok(vec![Self::load_material( + 0, + { + let image = fparkan_inspection::load_texture_mip0_rgba8_from_root( &input.root, &input.archive, &input.name, - )?, - input.name.clone(), - ), - TextureInput::WearMaterial(input) => { - let selected = - fparkan_inspection::load_wear_material_texture_mip0_rgba8_from_root( - &input.root, - &input.archive, - &input.name, - input.material_index, - )?; - (selected.image, selected.texture_name) - } - }; - Ok(Some(LoadedTexture { - texture: VulkanStaticTexture { - width: image.width, - height: image.height, - rgba8: image.rgba8, + )?; + (image.width, image.height, image.rgba8) }, - selected_name, - })) - }) + input.name.clone(), + )]), + TextureInput::WearMaterial(input) => { + let selected = fparkan_inspection::load_wear_material_texture_mip0_rgba8_from_root( + &input.root, + &input.archive, + &input.name, + input.material_index, + )?; + Ok(vec![Self::load_material( + input.material_index, + ( + selected.image.width, + selected.image.height, + selected.image.rgba8, + ), + selected.texture_name, + )]) + } + TextureInput::WearBatchMaterials(input) => { + let mut selectors = mesh + .draw_ranges + .iter() + .map(|range| range.material_index) + .collect::<Vec<_>>(); + selectors.sort_unstable(); + selectors.dedup(); + selectors + .into_iter() + .map(|material_index| { + let selected = + fparkan_inspection::load_wear_material_texture_mip0_rgba8_from_root( + &input.root, + &input.archive, + &input.name, + material_index, + )?; + Ok(Self::load_material( + material_index, + ( + selected.image.width, + selected.image.height, + selected.image.rgba8, + ), + selected.texture_name, + )) + }) + .collect() + } + } + } + + fn load_material( + material_index: u16, + image: (u32, u32, Vec<u8>), + selected_name: String, + ) -> LoadedTexture { + LoadedTexture { + material_index, + texture: VulkanStaticTexture { + width: image.0, + height: image.1, + rgba8: image.2, + }, + selected_name, + } } } #[derive(Clone, Debug)] struct LoadedTexture { + material_index: u16, texture: VulkanStaticTexture, selected_name: String, } @@ -443,7 +511,7 @@ struct LoadedTexture { struct SmokeApp { options: SmokeOptions, mesh: VulkanStaticMesh, - texture: Option<LoadedTexture>, + materials: Vec<LoadedTexture>, completed: Arc<AtomicBool>, progress: Arc<SharedSmokeProgress>, window_id: Option<WindowId>, @@ -488,14 +556,14 @@ impl SmokeApp { fn new( options: SmokeOptions, mesh: VulkanStaticMesh, - texture: Option<LoadedTexture>, + materials: Vec<LoadedTexture>, completed: Arc<AtomicBool>, progress: Arc<SharedSmokeProgress>, ) -> Self { Self { options, mesh, - texture, + materials, completed, progress, window_id: None, @@ -603,23 +671,35 @@ impl SmokeApp { .texture_input .as_ref() .map_or("", TextureInput::archive), - texture_name: self.texture.as_ref().map_or_else( - || { - self.options - .texture_input - .as_ref() - .map_or("", TextureInput::requested_name) - }, - |texture| texture.selected_name.as_str(), - ), + texture_name: match self.materials.as_slice() { + [] => self + .options + .texture_input + .as_ref() + .map_or("", TextureInput::requested_name), + [material] => material.selected_name.as_str(), + _ => "multiple", + }, texture_width: self - .texture - .as_ref() - .map_or(0, |texture| texture.texture.width), + .materials + .first() + .map_or(0, |material| material.texture.width), texture_height: self - .texture - .as_ref() - .map_or(0, |texture| texture.texture.height), + .materials + .first() + .map_or(0, |material| material.texture.height), + material_binding_count: self.materials.len(), + mesh_material_indices: self + .mesh + .draw_ranges + .iter() + .map(|range| range.material_index) + .collect(), + material_texture_names: self + .materials + .iter() + .map(|material| material.selected_name.clone()) + .collect(), shader_manifest_hash: renderer .as_ref() .map_or("", |snapshot| snapshot.report.shader_manifest_hash.as_str()), @@ -839,6 +919,9 @@ fn render_timeout_failure_report( .map_or("", TextureInput::requested_name), texture_width: 0, texture_height: 0, + material_binding_count: 0, + mesh_material_indices: Vec::new(), + material_texture_names: Vec::new(), shader_manifest_hash: "", vulkan_loader_status: if bootstrap.loader_available { "available" @@ -934,7 +1017,14 @@ impl ApplicationHandler for SmokeApp { render_request: RenderRequest::conservative(), enable_validation: true, mesh: self.mesh.clone(), - texture: self.texture.as_ref().map(|texture| texture.texture.clone()), + materials: self + .materials + .iter() + .map(|material| VulkanStaticMaterial { + material_index: material.material_index, + texture: material.texture.clone(), + }) + .collect(), bootstrap_progress: Some(Arc::clone(&self.progress.bootstrap)), }) { Ok(renderer) => renderer, @@ -1073,6 +1163,9 @@ struct SmokeReport<'a> { texture_name: &'a str, texture_width: u32, texture_height: u32, + material_binding_count: usize, + mesh_material_indices: Vec<u16>, + material_texture_names: Vec<String>, shader_manifest_hash: &'a str, vulkan_loader_status: &'a str, vulkan_instance_status: &'a str, @@ -1464,6 +1557,28 @@ mod tests { } #[test] + fn parses_original_wear_batch_material_input_without_override() { + let parsed = SmokeOptions::parse(&[ + "--out".to_string(), + "target/report.json".to_string(), + "--wear-root".to_string(), + "C:/GOG".to_string(), + "--wear-archive".to_string(), + "system.rlb".to_string(), + "--wear-name".to_string(), + "MODEL.WEA".to_string(), + ]); + + assert!(matches!( + parsed, + Ok(SmokeOptions { + texture_input: Some(TextureInput::WearBatchMaterials(WearInput { archive, name, .. })), + .. + }) if archive == "system.rlb" && name == "MODEL.WEA" + )); + } + + #[test] fn rejects_deprecated_self_assertion_flags() { for flag in [ "--status", @@ -1533,6 +1648,9 @@ mod tests { texture_name: "DEFAULT.0", texture_width: 16, texture_height: 16, + material_binding_count: 1, + mesh_material_indices: vec![0], + material_texture_names: vec!["DEFAULT.0".to_string()], shader_manifest_hash: "deadbeef", vulkan_loader_status: "available", vulkan_instance_status: "created", @@ -1585,7 +1703,7 @@ mod tests { texture_input: None, }, mesh: VulkanStaticMesh::smoke_triangle(), - texture: None, + materials: Vec::new(), completed: Arc::new(AtomicBool::new(false)), progress: Arc::new(SharedSmokeProgress::default()), window_id: None, |
