diff options
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/fparkan-cli/Cargo.toml | 3 | ||||
| -rw-r--r-- | apps/fparkan-cli/src/main.rs | 78 | ||||
| -rw-r--r-- | apps/fparkan-game/Cargo.toml | 2 | ||||
| -rw-r--r-- | apps/fparkan-game/src/main.rs | 49 | ||||
| -rw-r--r-- | apps/fparkan-viewer/Cargo.toml | 8 | ||||
| -rw-r--r-- | apps/fparkan-viewer/src/main.rs | 169 |
6 files changed, 154 insertions, 155 deletions
diff --git a/apps/fparkan-cli/Cargo.toml b/apps/fparkan-cli/Cargo.toml index 22952e6..90b26da 100644 --- a/apps/fparkan-cli/Cargo.toml +++ b/apps/fparkan-cli/Cargo.toml @@ -7,10 +7,9 @@ repository.workspace = true [dependencies] fparkan-corpus = { path = "../../crates/fparkan-corpus" } -fparkan-nres = { path = "../../crates/fparkan-nres" } fparkan-prototype = { path = "../../crates/fparkan-prototype" } +fparkan-inspection = { path = "../../crates/fparkan-inspection" } fparkan-resource = { path = "../../crates/fparkan-resource" } -fparkan-rsli = { path = "../../crates/fparkan-rsli" } fparkan-runtime = { path = "../../crates/fparkan-runtime" } fparkan-vfs = { path = "../../crates/fparkan-vfs" } diff --git a/apps/fparkan-cli/src/main.rs b/apps/fparkan-cli/src/main.rs index ee1f928..043a21c 100644 --- a/apps/fparkan-cli/src/main.rs +++ b/apps/fparkan-cli/src/main.rs @@ -3,9 +3,10 @@ //! `FParkan` command-line tools. use fparkan_corpus::{discover, render_report_json, report, DiscoverOptions}; -use fparkan_prototype::{ - build_prototype_graph_report, extend_graph_report_with_visual_dependencies, -}; +use fparkan_inspection::inspect_archive_file; +use fparkan_inspection::ArchiveInspection; +use fparkan_assets::extend_graph_report_with_visual_dependencies; +use fparkan_prototype::build_prototype_graph_report; use fparkan_resource::{resource_name, CachedResourceRepository}; use fparkan_runtime::{ create, load_mission, EngineConfig, EngineMode, EngineServices, MissionRequest, @@ -134,7 +135,12 @@ fn inspect_prototype(args: &[String]) -> Result<(), String> { let roots = [resource_name(key.as_bytes())]; let (graph, resolved, mut report) = build_prototype_graph_report(&repository, vfs.as_ref(), &roots); - extend_graph_report_with_visual_dependencies(&repository, &mut report, &resolved); + extend_graph_report_with_visual_dependencies( + &repository, + &mut report, + &graph, + &resolved, + ); println!("{}", prototype_inspect_json(&key, &graph, &report)); Ok(()) } @@ -202,42 +208,34 @@ fn graph_mission(args: &[String]) -> Result<(), String> { fn inspect_archive(args: &[String]) -> Result<(), String> { let path = parse_archive_path(args)?; - let bytes = std::fs::read(&path).map_err(|err| format!("{}: {err}", path.display()))?; - if bytes.starts_with(b"NRes") { - let document = fparkan_nres::decode( - Arc::from(bytes.into_boxed_slice()), - fparkan_nres::ReadProfile::Compatible, - ) - .map_err(|err| err.to_string())?; - println!( - "{}", - archive_inspect_json( - &path.display().to_string(), - "NRes", - document.entries().len(), - Some(document.lookup_order_valid()), - ) - ); - return Ok(()); - } - if bytes.get(0..4) == Some(b"NL\0\x01") { - let document = fparkan_rsli::decode( - Arc::from(bytes.into_boxed_slice()), - fparkan_rsli::ReadProfile::Compatible, - ) - .map_err(|err| err.to_string())?; - println!( - "{}", - archive_inspect_json( - &path.display().to_string(), - "RsLi", - document.entries().len(), - None - ) - ); - return Ok(()); + let inspection = inspect_archive_file(&path, 0).map_err(|err| err.to_string())?; + + match inspection { + ArchiveInspection::Nres { + entries, + lookup_order_valid, + .. + } => { + println!( + "{}", + archive_inspect_json( + &path.display().to_string(), + "NRes", + entries, + Some(lookup_order_valid), + ) + ); + Ok(()) + } + ArchiveInspection::Rsli { entries } => { + println!( + "{}", + archive_inspect_json(&path.display().to_string(), "RsLi", entries, None) + ); + Ok(()) + } + ArchiveInspection::Unsupported => Err(format!("{}: unsupported archive magic", path.display())), } - Err(format!("{}: unsupported archive magic", path.display())) } fn archive_inspect_json( @@ -278,7 +276,7 @@ fn json_string(value: &str) -> String { '\r' => out.push_str("\\r"), '\t' => out.push_str("\\t"), c if c.is_control() => { - let _ = write!(out, "\\u{:04x}", u32::from(c)); + let _ = write!(out, "\\u{:04x}", c as u32); } c => out.push(c), } diff --git a/apps/fparkan-game/Cargo.toml b/apps/fparkan-game/Cargo.toml index eef4d81..bac3397 100644 --- a/apps/fparkan-game/Cargo.toml +++ b/apps/fparkan-game/Cargo.toml @@ -7,6 +7,8 @@ repository.workspace = true [dependencies] fparkan-render = { path = "../../crates/fparkan-render" } +fparkan-platform-winit = { path = "../../adapters/fparkan-platform-winit" } +fparkan-render-vulkan = { path = "../../adapters/fparkan-render-vulkan" } fparkan-runtime = { path = "../../crates/fparkan-runtime" } fparkan-vfs = { path = "../../crates/fparkan-vfs" } fparkan-world = { path = "../../crates/fparkan-world" } diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs index 05a1e0a..7ea7d0e 100644 --- a/apps/fparkan-game/src/main.rs +++ b/apps/fparkan-game/src/main.rs @@ -3,11 +3,14 @@ //! `FParkan` rendered game composition root. use fparkan_render::{ - DrawCommand, DrawId, GpuMaterialId, GpuMeshId, IndexRange, RecordingBackend, RenderBackend, + DrawCommand, DrawId, GpuMaterialId, GpuMeshId, IndexRange, RenderBackend, RenderCommand, RenderCommandList, RenderPhase, }; +use fparkan_platform_winit::WinitWindow; +use fparkan_render_vulkan::VulkanBackend; use fparkan_runtime::{ create, frame, load_mission, EngineConfig, EngineMode, EngineServices, MissionRequest, + MissionAssets, loaded_mission_assets, }; use fparkan_vfs::DirectoryVfs; use fparkan_world::WorldSnapshot; @@ -47,7 +50,11 @@ fn run(args: &[String]) -> Result<String, String> { ) .map_err(|err| err.to_string())?; - let mut backend = RecordingBackend::default(); + let mut backend = VulkanBackend::new(); + let _request = WinitWindow::default_render_request(); + let window = WinitWindow::synthetic(1280, 720); + let _ = window.drawable_size(); + let _ = window.handle(); let mut last_draw_count = 0usize; let mut last_tick = 0u64; let mut last_hash = [0u8; 32]; @@ -55,7 +62,8 @@ fn run(args: &[String]) -> Result<String, String> { let result = frame(&mut engine).map_err(|err| err.to_string())?; last_tick = result.snapshot.tick.0; last_hash = result.snapshot.hash.0; - let commands = render_snapshot_commands(&result.snapshot); + let mission_assets = loaded_mission_assets(&engine); + let commands = render_snapshot_commands_with_assets(&result.snapshot, mission_assets); last_draw_count = commands .commands .iter() @@ -66,6 +74,8 @@ fn run(args: &[String]) -> Result<String, String> { .map_err(|err| format!("render backend: {err}"))?; } + let capture_report = backend.report(); + Ok(format!( "{{\"mission\":{},\"objects\":{},\"frames\":{},\"tick\":{},\"draws\":{},\"captures\":{},\"last_capture_bytes\":{},\"hash\":{}}}", json_string(&args.mission), @@ -73,17 +83,40 @@ fn run(args: &[String]) -> Result<String, String> { args.frames, last_tick, last_draw_count, - backend.captures().len(), - backend.last_capture().map_or(0, <[u8]>::len), + capture_report.submissions, + capture_report.last_capture_size, json_hash(&last_hash) )) } fn render_snapshot_commands(snapshot: &WorldSnapshot) -> RenderCommandList { + render_snapshot_commands_with_assets(snapshot, None) +} + +fn render_snapshot_commands_with_assets( + snapshot: &WorldSnapshot, + mission_assets: Option<&MissionAssets>, +) -> RenderCommandList { let mut commands = Vec::with_capacity(snapshot.objects.len() + 2); commands.push(RenderCommand::BeginFrame); for (index, handle) in snapshot.objects.iter().enumerate() { let stable_order = u64::from(handle.slot); + let prepared = mission_assets.and_then(|assets| { + assets + .visual_for_object(index) + .and_then(|visual_id| assets.visual_by_id(visual_id)) + }); + let mesh = if let Some(visual) = prepared { + visual.mesh.as_ref().map_or_else( + || GpuMeshId(u64::from(handle.slot) + 1), + |_| GpuMeshId(visual.id.raw()), + ) + } else { + GpuMeshId(u64::from(handle.slot) + 1) + }; + let material = prepared + .and_then(|visual| visual.primary_material_id()) + .map_or(GpuMaterialId(1), |material_id| GpuMaterialId(material_id.raw())); let draw_id = snapshot .tick .0 @@ -93,8 +126,8 @@ fn render_snapshot_commands(snapshot: &WorldSnapshot) -> RenderCommandList { id: DrawId(draw_id), phase: RenderPhase::Opaque, object_id: None, - mesh: GpuMeshId(u64::from(handle.slot) + 1), - material: GpuMaterialId(1), + mesh, + material, transform: identity_transform(index_to_f32(index)), range: IndexRange { start: 0, count: 3 }, stable_order, @@ -178,7 +211,7 @@ fn json_string(value: &str) -> String { '\t' => out.push_str("\\t"), c if c.is_control() => { use std::fmt::Write as _; - let _ = write!(out, "\\u{:04x}", u32::from(c)); + let _ = write!(out, "\\u{:04x}", c as u32); } c => out.push(c), } diff --git a/apps/fparkan-viewer/Cargo.toml b/apps/fparkan-viewer/Cargo.toml index 4219e8a..270334c 100644 --- a/apps/fparkan-viewer/Cargo.toml +++ b/apps/fparkan-viewer/Cargo.toml @@ -6,14 +6,8 @@ license.workspace = true repository.workspace = true [dependencies] -fparkan-msh = { path = "../../crates/fparkan-msh" } -fparkan-nres = { path = "../../crates/fparkan-nres" } -fparkan-resource = { path = "../../crates/fparkan-resource" } +fparkan-inspection = { path = "../../crates/fparkan-inspection" } fparkan-render = { path = "../../crates/fparkan-render" } -fparkan-rsli = { path = "../../crates/fparkan-rsli" } -fparkan-terrain-format = { path = "../../crates/fparkan-terrain-format" } -fparkan-texm = { path = "../../crates/fparkan-texm" } -fparkan-vfs = { path = "../../crates/fparkan-vfs" } [lints] workspace = true diff --git a/apps/fparkan-viewer/src/main.rs b/apps/fparkan-viewer/src/main.rs index 1720cd7..ee96ab5 100644 --- a/apps/fparkan-viewer/src/main.rs +++ b/apps/fparkan-viewer/src/main.rs @@ -2,19 +2,16 @@ #![allow(clippy::print_stderr, clippy::print_stdout)] //! `FParkan` asset viewer composition root. -use fparkan_msh::{decode_msh, validate_msh}; -use fparkan_nres::{decode as decode_nres, ReadProfile as NresReadProfile}; +use fparkan_inspection::{ + inspect_land_file, inspect_model_from_root, inspect_texture_from_root, ArchiveInspection, LandFileKind, + MapInspection, NresEntrySummary, +}; use fparkan_render::{ build_commands, CameraSnapshot, DrawId, GpuMaterialId, GpuMeshId, IndexRange, RenderPhase, RenderProfile, RenderSnapshot, RenderSnapshotDraw, }; -use fparkan_resource::{archive_path, resource_name, CachedResourceRepository, ResourceRepository}; -use fparkan_terrain_format::{decode_land_map, decode_land_msh}; -use fparkan_texm::decode_texm; -use fparkan_vfs::DirectoryVfs; use std::fmt::Write; use std::path::PathBuf; -use std::sync::Arc; fn main() { let args = std::env::args().skip(1).collect::<Vec<_>>(); @@ -44,35 +41,27 @@ fn run(args: &[String]) -> Result<String, String> { fn inspect_archive(args: &[String]) -> Result<String, String> { let file = parse_file(args)?; let limit = parse_limit(args)?; - let bytes = std::fs::read(&file).map_err(|err| format!("{}: {err}", file.display()))?; - if bytes.starts_with(b"NRes") { - let document = decode_nres( - Arc::from(bytes.into_boxed_slice()), - NresReadProfile::Compatible, - ) - .map_err(|err| err.to_string())?; - let sample = render_nres_entries(&document, limit); - return Ok(format!( + let inspection = fparkan_inspection::inspect_archive_file(&file, limit)?; + + match inspection { + ArchiveInspection::Nres { + entries, + lookup_order_valid, + sample, + } => Ok(format!( "{{\"kind\":\"NRes\",\"path\":{},\"entries\":{},\"lookup_order_valid\":{},\"sample\":[{}]}}", json_string(&file.display().to_string()), - document.entries().len(), - document.lookup_order_valid(), - sample - )); - } - if bytes.get(0..4) == Some(b"NL\0\x01") { - let document = fparkan_rsli::decode( - Arc::from(bytes.into_boxed_slice()), - fparkan_rsli::ReadProfile::Compatible, - ) - .map_err(|err| err.to_string())?; - return Ok(format!( + entries, + lookup_order_valid, + render_nres_entries(&sample) + )), + ArchiveInspection::Rsli { entries } => Ok(format!( "{{\"kind\":\"RsLi\",\"path\":{},\"entries\":{}}}", json_string(&file.display().to_string()), - document.entries().len() - )); + entries + )), + ArchiveInspection::Unsupported => Err(format!("{}: unsupported archive magic", file.display())), } - Err(format!("{}: unsupported archive magic", file.display())) } fn inspect_model(args: &[String]) -> Result<String, String> { @@ -81,21 +70,18 @@ fn inspect_model(args: &[String]) -> Result<String, String> { } let query = parse_resource_query(args)?; - let bytes = read_resource(&query)?; - let nested = decode_nres(bytes, NresReadProfile::Compatible).map_err(|err| err.to_string())?; - let document = decode_msh(&nested).map_err(|err| err.to_string())?; - let model = validate_msh(&document).map_err(|err| err.to_string())?; + let inspection = inspect_model_from_root(&query.root, &query.archive, &query.name)?; Ok(format!( "{{\"kind\":\"model\",\"archive\":{},\"name\":{},\"streams\":{},\"nodes\":{},\"slots\":{},\"positions\":{},\"indices\":{},\"batches\":{}}}", json_string(&query.archive), json_string(&query.name), - document.streams().len(), - model.node_count, - model.slots.len(), - model.positions.len(), - model.indices.len(), - model.batches.len() + inspection.streams, + inspection.nodes, + inspection.slots, + inspection.positions, + inspection.indices, + inspection.batches )) } @@ -139,54 +125,54 @@ impl ViewerModelService { fn inspect_texture(args: &[String]) -> Result<String, String> { let query = parse_resource_query(args)?; - let document = decode_texm(read_resource(&query)?).map_err(|err| err.to_string())?; + let inspection = inspect_texture_from_root(&query.root, &query.archive, &query.name)?; Ok(format!( "{{\"kind\":\"texture\",\"archive\":{},\"name\":{},\"width\":{},\"height\":{},\"format\":{},\"mips\":{},\"pages\":{}}}", json_string(&query.archive), json_string(&query.name), - document.width(), - document.height(), - json_string(&format!("{:?}", document.format())), - document.mip_count(), - document.page_rects().len() + inspection.width, + inspection.height, + json_string(&inspection.format), + inspection.mips, + inspection.pages )) } fn inspect_map(args: &[String]) -> Result<String, String> { let file = parse_file(args)?; let kind = parse_option(args, &["--kind"]).ok_or_else(|| "missing --kind".to_string())?; - let bytes = std::fs::read(&file).map_err(|err| format!("{}: {err}", file.display()))?; - let nres = decode_nres( - Arc::from(bytes.into_boxed_slice()), - NresReadProfile::Compatible, - ) - .map_err(|err| err.to_string())?; - - match kind.as_str() { - "land-msh" => { - let land = decode_land_msh(&nres).map_err(|err| err.to_string())?; - Ok(format!( - "{{\"kind\":\"land-msh\",\"path\":{},\"streams\":{},\"positions\":{},\"faces\":{},\"slots\":{}}}", - json_string(&file.display().to_string()), - land.streams.len(), - land.positions.len(), - land.faces.len(), - land.slots.slots_raw.len() - )) - } - "land-map" => { - let land = decode_land_map(&nres).map_err(|err| err.to_string())?; - Ok(format!( - "{{\"kind\":\"land-map\",\"path\":{},\"areals\":{},\"declared_areals\":{},\"grid_width\":{},\"grid_height\":{}}}", - json_string(&file.display().to_string()), - land.areals.len(), - land.areal_count, - land.grid.cells_x, - land.grid.cells_y - )) - } - _ => Err(format!("unknown map kind: {kind}")), + let inspection = inspect_land_file( + &file, + match kind.as_str() { + "land-msh" => LandFileKind::LandMsh, + "land-map" => LandFileKind::LandMap, + _ => return Err(format!("unknown map kind: {kind}")), + }, + )?; + + Ok(render_map_inspection_json(&file.display().to_string(), &kind, &inspection)) +} + +fn render_map_inspection_json(path: &str, kind: &str, inspection: &MapInspection) -> String { + match kind { + "land-msh" => format!( + "{{\"kind\":\"land-msh\",\"path\":{},\"streams\":{},\"positions\":{},\"faces\":{},\"slots\":{}}}", + json_string(path), + inspection.streams, + inspection.positions, + inspection.faces, + inspection.slots + ), + "land-map" => format!( + "{{\"kind\":\"land-map\",\"path\":{},\"areals\":{},\"declared_areals\":{},\"grid_width\":{},\"grid_height\":{}}}", + json_string(path), + inspection.areals, + inspection.declared_areals, + inspection.grid_width, + inspection.grid_height + ), + _ => unreachable!("invalid land kind: {kind}"), } } @@ -205,19 +191,6 @@ fn parse_resource_query(args: &[String]) -> Result<ResourceQuery, String> { }) } -fn read_resource(query: &ResourceQuery) -> Result<Arc<[u8]>, String> { - let repository = CachedResourceRepository::new(Arc::new(DirectoryVfs::new(&query.root))); - let archive = repository - .open_archive(&archive_path(query.archive.as_bytes()).map_err(|err| err.to_string())?) - .map_err(|err| err.to_string())?; - let entry = repository - .find(archive, &resource_name(query.name.as_bytes())) - .map_err(|err| err.to_string())? - .ok_or_else(|| format!("resource not found: {}/{}", query.archive, query.name))?; - let bytes = repository.read(entry).map_err(|err| err.to_string())?; - Ok(Arc::from(bytes.into_owned())) -} - fn parse_file(args: &[String]) -> Result<PathBuf, String> { parse_path_option(args, &["--file"], "--file") } @@ -233,19 +206,19 @@ fn parse_limit(args: &[String]) -> Result<usize, String> { .map(|value| value.unwrap_or(0)) } -fn render_nres_entries(document: &fparkan_nres::NresDocument, limit: usize) -> String { +fn render_nres_entries(entries: &[NresEntrySummary]) -> String { let mut out = String::new(); - for (index, entry) in document.entries().iter().take(limit).enumerate() { + for (index, entry) in entries.iter().enumerate() { if index > 0 { out.push(','); } - let name = String::from_utf8_lossy(entry.name_bytes()); + let name = &entry.name; let _ = write!( out, "{{\"name\":{},\"type\":{},\"size\":{}}}", - json_string(&name), - entry.meta().type_id, - entry.meta().data_size + json_string(name), + entry.type_id, + entry.data_size ); } out @@ -278,7 +251,7 @@ fn json_string(value: &str) -> String { '\r' => out.push_str("\\r"), '\t' => out.push_str("\\t"), c if c.is_control() => { - let _ = write!(out, "\\u{:04x}", u32::from(c)); + let _ = write!(out, "\\u{:04x}", c as u32); } c => out.push(c), } |
