From 13f563bb79228fdb075698c4ed79deea3cc65301 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Sun, 18 Oct 2026 14:09:06 +0400 Subject: Update controlled component geometry and mounts --- apps/fparkan-game/src/main.rs | 748 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 740 insertions(+), 8 deletions(-) (limited to 'apps') diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs index 7068882..0c4c7eb 100644 --- a/apps/fparkan-game/src/main.rs +++ b/apps/fparkan-game/src/main.rs @@ -32,7 +32,7 @@ use fparkan_msh::{ }; use fparkan_path::ResourceName; use fparkan_platform_winit::{window_native_handles, WinitWindow, WinitWindowPlan}; -use fparkan_prototype::{unit_component_tree, PROTOTYPE_TYPE_EXTO}; +use fparkan_prototype::{unit_component_tree, UnitComponentTreeNode, PROTOTYPE_TYPE_EXTO}; use fparkan_render::{ LegacyBlendMode, LegacyCullMode, LegacyD3d7Projection, LegacyDepthMode, LegacyIron3dEulerTransform, LegacyPipelineState, RawCameraTransform, @@ -1588,6 +1588,49 @@ fn static_mesh_shadow_triangles(mesh: &VulkanStaticMesh) -> Vec .collect() } +fn expand_shadow_sphere_to_triangles( + sphere: &mut ShadowSphere, + triangles: &[ShadowTriangle], +) -> Result { + if !sphere.radius.is_finite() + || sphere.radius < 0.0 + || !sphere.center.iter().all(|value| value.is_finite()) + { + return Err("animated shadow caster has an invalid bounding sphere".to_string()); + } + let mut changed = false; + for point in triangles.iter().flat_map(|triangle| triangle.positions) { + if !point.iter().all(|value| value.is_finite()) { + return Err("animated shadow triangle has a non-finite position".to_string()); + } + let offset = sub3(point, sphere.center); + let distance = length3(offset) + .ok_or_else(|| "animated shadow point distance is non-finite".to_string())?; + if distance <= sphere.radius { + continue; + } + let next_radius = 0.5 * (sphere.radius + distance); + let center_shift = if distance > f32::EPSILON { + scale3(offset, (next_radius - sphere.radius) / distance) + } else { + [0.0; 3] + }; + sphere.center = add3(sphere.center, center_shift); + sphere.radius = next_radius; + changed = true; + } + Ok(changed) +} + +fn shadow_sphere_enclosing_triangles( + baseline: ShadowSphere, + triangles: &[ShadowTriangle], +) -> Result { + let mut sphere = baseline; + expand_shadow_sphere_to_triangles(&mut sphere, triangles)?; + Ok(sphere) +} + #[derive(Clone, Copy, Debug)] struct ShadowComponentSphere { center: [f32; 3], @@ -3781,9 +3824,16 @@ struct StaticPreviewScene { material_animations: Vec, sun_occlusion_range_indices: Vec, shadow_casters: Vec, + shadow_sphere_baselines: Vec, shadow_receivers: Vec, shadow_material_index: usize, terrain: Arc, + component_pose_buffers: Vec>>, + component_mount_poses: Vec>>, + control_animation_instances: Vec, + dynamic_component_geometry: Vec, + dynamic_component_mounts: Vec, + shadow_caster_indices_by_object: Vec>, camera: VulkanStaticCamera, view_camera: PreviewCameraFrame, captured_viewport: Option, @@ -3793,6 +3843,35 @@ struct StaticPreviewScene { terrain_components: usize, } +#[derive(Clone, Debug)] +struct ControlAnimationInstance { + object_index: usize, + component_index: usize, + model: Arc, + control: PreparedComponentControl, + state: ControlInstanceState, +} + +#[derive(Clone, Debug)] +struct DynamicComponentGeometry { + object_index: usize, + component_index: usize, + model: Arc, + transform: LegacyIron3dEulerTransform, + scale: [f32; 3], + vertex_start: usize, + vertex_count: usize, + shadow_triangle_range: Option>, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct DynamicComponentMount { + object_index: usize, + component_index: usize, + parent_index: usize, + socket: usize, +} + #[derive(Clone, Copy, Debug, PartialEq)] struct CapturedViewport { /// Original D3D7 viewport rectangle `(left, top, right, bottom)`. The @@ -3998,6 +4077,90 @@ fn initial_control_gate_values(control: &PreparedComponentControl) -> Vec