aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/fparkan-cli/Cargo.toml3
-rw-r--r--apps/fparkan-cli/src/main.rs78
-rw-r--r--apps/fparkan-game/Cargo.toml2
-rw-r--r--apps/fparkan-game/src/main.rs49
-rw-r--r--apps/fparkan-viewer/Cargo.toml8
-rw-r--r--apps/fparkan-viewer/src/main.rs169
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),
}