aboutsummaryrefslogtreecommitdiff
path: root/crates/fparkan-runtime
diff options
context:
space:
mode:
Diffstat (limited to 'crates/fparkan-runtime')
-rw-r--r--crates/fparkan-runtime/Cargo.toml5
-rw-r--r--crates/fparkan-runtime/src/lib.rs160
2 files changed, 82 insertions, 83 deletions
diff --git a/crates/fparkan-runtime/Cargo.toml b/crates/fparkan-runtime/Cargo.toml
index 17d95c1..347c713 100644
--- a/crates/fparkan-runtime/Cargo.toml
+++ b/crates/fparkan-runtime/Cargo.toml
@@ -6,15 +6,12 @@ license.workspace = true
repository.workspace = true
[dependencies]
-fparkan-mission-format = { path = "../fparkan-mission-format" }
-fparkan-nres = { path = "../fparkan-nres" }
+fparkan-assets = { path = "../fparkan-assets" }
fparkan-path = { path = "../fparkan-path" }
fparkan-platform = { path = "../fparkan-platform" }
fparkan-prototype = { path = "../fparkan-prototype" }
fparkan-render = { path = "../fparkan-render" }
fparkan-resource = { path = "../fparkan-resource" }
-fparkan-terrain = { path = "../fparkan-terrain" }
-fparkan-terrain-format = { path = "../fparkan-terrain-format" }
fparkan-vfs = { path = "../fparkan-vfs" }
fparkan-world = { path = "../fparkan-world" }
diff --git a/crates/fparkan-runtime/src/lib.rs b/crates/fparkan-runtime/src/lib.rs
index 1fc0137..053d7bd 100644
--- a/crates/fparkan-runtime/src/lib.rs
+++ b/crates/fparkan-runtime/src/lib.rs
@@ -1,19 +1,20 @@
#![forbid(unsafe_code)]
//! Runtime orchestration for headless and rendered modes.
-use fparkan_mission_format::{
- decode_tma, decode_tma_land_path, LpString, MissionDocument, MissionError, TmaProfile,
+use fparkan_assets::{
+ AssetError as AssetPreparationError, AssetManager, MissionAssetPlan,
+ decode_mission_land_path, decode_nres_payload, decode_mission_payload, prepare_terrain_world,
+ derive_mission_land_paths, BuildCategory, MissionDocument, MissionError, MissionTerrainPaths,
+ TerrainFormatError, TerrainPreparationError, TmaProfile, TerrainWorld,
+ NresError,
+ extend_graph_report_with_visual_dependencies,
};
use fparkan_path::{normalize_relative, NormalizedPath, PathError, PathPolicy};
use fparkan_prototype::{
- build_prototype_graph_report, extend_graph_report_with_visual_dependencies, EffectivePrototype,
+ build_prototype_graph_report,
PrototypeGraph, PrototypeGraphFailure, PrototypeGraphReport,
};
use fparkan_resource::{resource_name, CachedResourceRepository};
-use fparkan_terrain::TerrainWorld;
-use fparkan_terrain_format::{
- decode_build_dat, decode_land_map, decode_land_msh, BuildCategory, TerrainFormatError,
-};
use fparkan_vfs::{Vfs, VfsError};
use fparkan_world::{
construct_object, new as new_world, register_object, step, InputSnapshot, ObjectDraft,
@@ -21,6 +22,8 @@ use fparkan_world::{
};
use std::sync::Arc;
+pub use fparkan_assets::MissionAssets;
+
/// Engine mode.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EngineMode {
@@ -167,6 +170,8 @@ pub struct LoadedMission {
pub graph_unit_component_count: usize,
/// Mission prototype graph root count.
pub graph_root_count: usize,
+ /// Mission asset plan visual count after dependency preparation.
+ pub asset_visual_count: usize,
/// Expanded prototype requests resolved to effective prototypes.
pub graph_resolved_count: usize,
/// Reached mesh dependency count.
@@ -189,6 +194,14 @@ pub struct LoadedMission {
pub graph_lightmap_request_count: usize,
/// Lightmap Texm entries decoded.
pub graph_lightmap_resolved_count: usize,
+ /// Mission asset plan mesh-backed count after dependency preparation.
+ pub asset_model_count: usize,
+ /// Mission asset plan material count after dependency preparation.
+ pub asset_material_count: usize,
+ /// Mission asset plan texture count after dependency preparation.
+ pub asset_texture_count: usize,
+ /// Mission asset plan lightmap count after dependency preparation.
+ pub asset_lightmap_count: usize,
}
/// Frame result.
@@ -222,7 +235,8 @@ struct LoadedMissionState {
build_categories: Vec<BuildCategory>,
prototype_graph: PrototypeGraph,
prototype_report: PrototypeGraphReport,
- resolved_prototypes: Vec<EffectivePrototype>,
+ mission_assets: MissionAssets,
+ asset_plan: MissionAssetPlan,
}
/// Engine error.
@@ -251,7 +265,7 @@ pub enum EngineError {
/// Resource path.
path: String,
/// Source error.
- source: fparkan_nres::NresError,
+ source: NresError,
},
/// Mission decode error.
Mission {
@@ -268,12 +282,19 @@ pub enum EngineError {
source: TerrainFormatError,
},
/// Terrain runtime build error.
- Terrain(fparkan_terrain::TerrainError),
+ Terrain(fparkan_assets::TerrainError),
/// Prototype graph errors.
PrototypeGraph {
/// Root failures.
failures: Vec<PrototypeGraphFailure>,
},
+ /// Asset preparation errors.
+ AssetPreparation {
+ /// Mission key.
+ mission: String,
+ /// Source error.
+ source: AssetPreparationError,
+ },
/// World error.
World(fparkan_world::WorldError),
/// Scheduler phase order was violated.
@@ -319,6 +340,9 @@ impl std::fmt::Display for EngineError {
Self::PrototypeGraph { failures } => {
write!(f, "mission prototype graph has {} failures", failures.len())
}
+ Self::AssetPreparation { mission, source } => {
+ write!(f, "{mission}: asset preparation failed: {source}")
+ }
Self::World(source) => write!(f, "{source}"),
Self::SchedulerPhaseOrder { previous, current } => write!(
f,
@@ -346,6 +370,7 @@ impl std::error::Error for EngineError {
Self::TerrainFormat { source, .. } => Some(source),
Self::Terrain(source) => Some(source),
Self::World(source) => Some(source),
+ Self::AssetPreparation { source, .. } => Some(source),
Self::MissingVfs
| Self::PrototypeGraph { .. }
| Self::SchedulerPhaseOrder { .. }
@@ -410,44 +435,44 @@ fn load_mission_with_options(
let mission_bytes = read_vfs(&vfs, &mission_path)?;
trace.phases.push(MissionLoadPhase::Map);
- let land_path = decode_tma_land_path(&mission_bytes, TmaProfile::Strict).map_err(|source| {
+ let land_path = decode_mission_land_path(&mission_bytes, TmaProfile::Strict).map_err(|source| {
EngineError::Mission {
path: mission_path.as_str().to_string(),
source,
}
})?;
- let (land_msh_path, land_map_path) = terrain_paths_from_land_path(&land_path)?;
- let land_msh_nres = decode_nres(&vfs, &land_msh_path)?;
- let land_map_nres = decode_nres(&vfs, &land_map_path)?;
- let land_msh =
- decode_land_msh(&land_msh_nres).map_err(|source| EngineError::TerrainFormat {
+ let MissionTerrainPaths { land_msh: land_msh_path, land_map: land_map_path } =
+ derive_mission_land_paths(&land_path).map_err(|source| EngineError::Path {
+ role: "mission land",
+ value: mission_path.as_str().to_string(),
+ source,
+ })?;
+ let land_msh_nres = decode_nres_payload(read_vfs(&vfs, &land_msh_path)?)
+ .map_err(|source| EngineError::Nres {
path: land_msh_path.as_str().to_string(),
source,
})?;
- let land_map =
- decode_land_map(&land_map_nres).map_err(|source| EngineError::TerrainFormat {
+ let land_map_nres = decode_nres_payload(read_vfs(&vfs, &land_map_path)?)
+ .map_err(|source| EngineError::Nres {
path: land_map_path.as_str().to_string(),
source,
})?;
- let terrain =
- TerrainWorld::from_land_assets(&land_msh, &land_map).map_err(EngineError::Terrain)?;
-
let build_dat_path = normalize_engine_path("BuildDat", "BuildDat.lst")?;
let build_dat = read_vfs(&vfs, &build_dat_path)?;
- let build_categories =
- decode_build_dat(&build_dat).map_err(|source| EngineError::TerrainFormat {
- path: build_dat_path.as_str().to_string(),
- source,
+ let (terrain, build_categories) = prepare_terrain_world(&land_msh_nres, &land_map_nres, &build_dat)
+ .map_err(|source| match source {
+ TerrainPreparationError::Decode(source) => EngineError::TerrainFormat {
+ path: build_dat_path.as_str().to_string(),
+ source,
+ },
+ TerrainPreparationError::Runtime(source) => EngineError::Terrain(source),
})?;
trace.phases.push(MissionLoadPhase::Tma);
let mission =
- decode_tma(mission_bytes, TmaProfile::Strict).map_err(|source| EngineError::Mission {
+ decode_mission_payload(mission_bytes, TmaProfile::Strict).map_err(|source| EngineError::Mission {
path: mission_path.as_str().to_string(),
source,
})?;
- let verified_terrain_paths = terrain_paths(&mission)?;
- debug_assert_eq!(verified_terrain_paths.0.as_str(), land_msh_path.as_str());
- debug_assert_eq!(verified_terrain_paths.1.as_str(), land_map_path.as_str());
trace.transforms = mission
.objects
.iter()
@@ -471,6 +496,7 @@ fn load_mission_with_options(
extend_graph_report_with_visual_dependencies(
&repository,
&mut prototype_report,
+ &prototype_graph,
&resolved_prototypes,
);
if !prototype_report.is_success() {
@@ -478,6 +504,16 @@ fn load_mission_with_options(
failures: prototype_report.failures.clone(),
});
}
+ let mission_assets = AssetManager::new(repository)
+ .prepare_mission_assets(
+ &prototype_graph.root_prototype_request_spans,
+ &resolved_prototypes,
+ )
+ .map_err(|source| EngineError::AssetPreparation {
+ mission: request.key.clone(),
+ source,
+ })?;
+ let mission_asset_plan = mission_assets.to_plan();
trace.phases.push(MissionLoadPhase::Assets);
let mut new_runtime_world = new_world(WorldConfig);
@@ -519,6 +555,7 @@ fn load_mission_with_options(
graph_direct_reference_count: prototype_report.direct_reference_count,
graph_unit_component_count: prototype_report.unit_component_count,
graph_root_count: prototype_report.root_count,
+ asset_visual_count: mission_asset_plan.visual_count,
graph_resolved_count: prototype_report.resolved_count,
graph_mesh_dependency_count: prototype_report.mesh_dependency_count,
graph_failure_count: prototype_report.failures.len(),
@@ -530,6 +567,10 @@ fn load_mission_with_options(
graph_texture_resolved_count: prototype_report.texture_resolved_count,
graph_lightmap_request_count: prototype_report.lightmap_request_count,
graph_lightmap_resolved_count: prototype_report.lightmap_resolved_count,
+ asset_model_count: mission_asset_plan.model_count,
+ asset_material_count: mission_asset_plan.material_count,
+ asset_texture_count: mission_asset_plan.texture_count,
+ asset_lightmap_count: mission_asset_plan.lightmap_count,
};
engine.world = new_runtime_world;
@@ -540,7 +581,8 @@ fn load_mission_with_options(
build_categories,
prototype_graph,
prototype_report,
- resolved_prototypes,
+ mission_assets,
+ asset_plan: mission_asset_plan,
});
Ok((summary, trace))
}
@@ -618,13 +660,16 @@ pub fn loaded_prototype_graph_report(engine: &Engine) -> Option<&PrototypeGraphR
engine.loaded.as_ref().map(|state| &state.prototype_report)
}
-/// Returns resolved effective prototypes for the loaded mission.
+/// Returns the prepared mission asset plan for the loaded mission.
#[must_use]
-pub fn loaded_resolved_prototypes(engine: &Engine) -> Option<&[EffectivePrototype]> {
- engine
- .loaded
- .as_ref()
- .map(|state| state.resolved_prototypes.as_slice())
+pub fn loaded_mission_asset_plan(engine: &Engine) -> Option<&MissionAssetPlan> {
+ engine.loaded.as_ref().map(|state| &state.asset_plan)
+}
+
+/// Returns prepared mission assets for the loaded mission.
+#[must_use]
+pub fn loaded_mission_assets(engine: &Engine) -> Option<&MissionAssets> {
+ engine.loaded.as_ref().map(|state| &state.mission_assets)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -716,49 +761,6 @@ fn read_vfs(vfs: &Arc<dyn Vfs>, path: &NormalizedPath) -> Result<Arc<[u8]>, Engi
})
}
-fn decode_nres(
- vfs: &Arc<dyn Vfs>,
- path: &NormalizedPath,
-) -> Result<fparkan_nres::NresDocument, EngineError> {
- let bytes = read_vfs(vfs, path)?;
- fparkan_nres::decode(bytes, fparkan_nres::ReadProfile::Compatible).map_err(|source| {
- EngineError::Nres {
- path: path.as_str().to_string(),
- source,
- }
- })
-}
-
-fn terrain_paths(
- mission: &MissionDocument,
-) -> Result<(NormalizedPath, NormalizedPath), EngineError> {
- terrain_paths_from_land_path(&mission.land_path)
-}
-
-fn terrain_paths_from_land_path(
- land_path: &LpString,
-) -> Result<(NormalizedPath, NormalizedPath), EngineError> {
- let land_path_raw = String::from_utf8_lossy(&land_path.raw).to_string();
- let normalized =
- normalize_relative(&land_path.raw, PathPolicy::StrictLegacy).map_err(|source| {
- EngineError::Path {
- role: "mission land",
- value: land_path_raw.clone(),
- source,
- }
- })?;
- let Some((parent, _stem)) = normalized.as_str().rsplit_once('/') else {
- return Err(EngineError::Path {
- role: "mission land",
- value: normalized.as_str().to_string(),
- source: PathError::Empty,
- });
- };
- let mesh = normalize_engine_path("Land.msh", &format!("{parent}/Land.msh"))?;
- let map = normalize_engine_path("Land.map", &format!("{parent}/Land.map"))?;
- Ok((mesh, map))
-}
-
#[cfg(test)]
mod tests {
use super::*;