diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs index d75a8a2..3549b11 100644 --- a/apps/fparkan-game/src/main.rs +++ b/apps/fparkan-game/src/main.rs @@ -32,7 +32,7 @@ use fparkan_render::{ use fparkan_render_vulkan::VulkanPlanningBackend; use fparkan_runtime::{ create, frame, load_mission, loaded_mission_assets, EngineConfig, EngineMode, EngineServices, - MissionAssets, MissionRequest, + MissionAssets, MissionObjectDraft, MissionRequest, }; use fparkan_vfs::DirectoryVfs; use fparkan_world::WorldSnapshot; @@ -85,8 +85,10 @@ fn run(args: &[String]) -> Result { last_tick = result.snapshot.tick.0; last_hash = result.snapshot.hash.0; let mission_assets = loaded_mission_assets(&engine); - let commands = render_snapshot_commands_with_assets(&result.snapshot, mission_assets) - .map_err(|err| format!("render snapshot: {err}"))?; + let mission_drafts = fparkan_runtime::loaded_mission_object_drafts(&engine); + let commands = + render_snapshot_commands_with_assets(&result.snapshot, mission_assets, mission_drafts) + .map_err(|err| format!("render snapshot: {err}"))?; last_draw_count = commands .commands .iter() @@ -114,20 +116,22 @@ fn run(args: &[String]) -> Result { #[cfg(test)] fn render_snapshot_commands(snapshot: &WorldSnapshot) -> Result { - render_snapshot_commands_with_assets(snapshot, None) + render_snapshot_commands_with_assets(snapshot, None, None) } fn render_snapshot_commands_with_assets( snapshot: &WorldSnapshot, mission_assets: Option<&MissionAssets>, + mission_drafts: Option<&[MissionObjectDraft]>, ) -> Result { - let render_snapshot = render_snapshot_with_assets(snapshot, mission_assets); + let render_snapshot = render_snapshot_with_assets(snapshot, mission_assets, mission_drafts); build_commands(&render_snapshot, RenderProfile::default()).map_err(|err| err.to_string()) } fn render_snapshot_with_assets( snapshot: &WorldSnapshot, mission_assets: Option<&MissionAssets>, + mission_drafts: Option<&[MissionObjectDraft]>, ) -> RenderSnapshot { let mut draws = Vec::with_capacity(snapshot.objects.len()); for (index, handle) in snapshot.objects.iter().enumerate() { @@ -168,7 +172,12 @@ fn render_snapshot_with_assets( material_slots: vec![material], material_index: 0, pipeline_state: fparkan_render::LegacyPipelineState::default(), - transform: identity_transform(index_to_f32(index)), + transform: mission_drafts + .and_then(|drafts| drafts.get(index)) + .map_or_else( + || identity_transform(index_to_f32(index)), + mission_position_scale_transform, + ), range: IndexRange { start: 0, count: 3 }, stable_order, }); @@ -180,6 +189,27 @@ fn render_snapshot_with_assets( } } +fn mission_position_scale_transform(draft: &MissionObjectDraft) -> [f32; 16] { + [ + draft.scale[0], + 0.0, + 0.0, + draft.position[0], + 0.0, + draft.scale[1], + 0.0, + draft.position[1], + 0.0, + 0.0, + draft.scale[2], + draft.position[2], + 0.0, + 0.0, + 0.0, + 1.0, + ] +} + fn identity_transform(x: f32) -> [f32; 16] { [ 1.0, 0.0, 0.0, x, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, @@ -362,7 +392,7 @@ mod tests { object_visuals: vec![vec![AssetId::new(101), AssetId::new(102)]], ..MissionAssets::default() }; - let commands = render_snapshot_commands_with_assets(&snapshot, Some(&assets))?; + let commands = render_snapshot_commands_with_assets(&snapshot, Some(&assets), None)?; let draws: Vec<_> = commands .commands .iter() @@ -380,6 +410,24 @@ mod tests { Ok(()) } + #[test] + fn render_snapshot_uses_mission_position_and_scale_without_interpreting_orientation() { + let draft = MissionObjectDraft { + original_id: None, + resource_name_raw: Vec::new(), + identity_or_clan_raw: 0, + position: [10.0, 20.0, 30.0], + orientation_raw: [1.0, 2.0, 3.0], + scale: [2.0, 3.0, 4.0], + visual_ids: Vec::new(), + properties: Vec::new(), + }; + assert_eq!( + mission_position_scale_transform(&draft), + [2.0, 0.0, 0.0, 10.0, 0.0, 3.0, 0.0, 20.0, 0.0, 0.0, 4.0, 30.0, 0.0, 0.0, 0.0, 1.0,] + ); + } + fn prepared_visual(id: u64, material: u64) -> PreparedVisual { PreparedVisual { id: AssetId::new(id), diff --git a/docs/tomes/05-render.md b/docs/tomes/05-render.md index 4a4db7b..85fc231 100644 --- a/docs/tomes/05-render.md +++ b/docs/tomes/05-render.md @@ -916,6 +916,15 @@ prepared visual сохраняет один compatibility fallback draw. Это multi-component prototype graph для будущего real Vulkan renderer, однако текущий game app всё ещё использует planning backend и triangle ranges. +### Mission position/scale render bridge + +`fparkan-game` now maps preserved TMA position and non-uniform scale into each +backend-neutral draw matrix instead of diagnostic index placement. Raw +orientation deliberately remains uninterpreted: the original Euler order and +matrix convention are not yet proven. This preserves authored values without +inventing transform math; the app still uses planning backend and triangle +ranges, so it is not a full renderer claim. + `Land.msh` использует отдельный geometry-only bridge: validated `TerrainFace28` сохраняет source triangle order, а его positions и packed UV0 попадают в тот же static vertex/index upload path. Для текущего диагностического viewer XZ bounds