Skip to main content

myth_scene/
scene.rs

1use std::borrow::Cow;
2use std::sync::atomic::{AtomicU32, Ordering};
3
4use myth_animation::{AnimationMixer, AnimationTarget};
5use myth_core::{NodeHandle, SkeletonKey, Transform};
6use myth_resources::Input;
7use myth_resources::bloom::BloomSettings;
8use myth_resources::buffer::CpuBuffer;
9use myth_resources::mesh::Mesh;
10use myth_resources::screen_space::ScreenSpaceSettings;
11use myth_resources::shader_defines::ShaderDefines;
12use myth_resources::ssao::SsaoSettings;
13use myth_resources::tone_mapping::ToneMappingSettings;
14use myth_resources::uniforms::{EnvironmentUniforms, GpuLightStorage};
15
16use crate::background::{BackgroundMode, BackgroundSettings};
17use crate::camera::Camera;
18use crate::environment::Environment;
19use crate::light::Light;
20use crate::light::LightKind;
21use crate::node::Node;
22use crate::skeleton::{BindMode, Skeleton, SkinBinding};
23use crate::transform_system;
24use crate::wrapper::SceneNode;
25use glam::{Affine3A, Quat, Vec3};
26use slotmap::{SecondaryMap, SlotMap, SparseSecondaryMap};
27
28static NEXT_SCENE_ID: AtomicU32 = AtomicU32::new(1);
29
30/// Trait for scene update logic.
31///
32/// Allows users to define custom behavior scripts that update
33/// along with the scene lifecycle each frame.
34///
35/// # Example
36///
37/// ```rust,ignore
38/// struct RotateScript {
39///     target: NodeHandle,
40///     speed: f32,
41/// }
42///
43/// impl SceneLogic for RotateScript {
44///     fn update(&mut self, scene: &mut Scene, input: &Input, dt: f32) {
45///         if let Some(node) = scene.get_node_mut(self.target) {
46///             node.transform.rotation *= Quat::from_rotation_y(self.speed * dt);
47///         }
48///     }
49/// }
50/// ```
51pub trait SceneLogic: Send + Sync + 'static {
52    /// Called each frame to update scene state.
53    fn update(&mut self, scene: &mut Scene, input: &Input, dt: f32);
54}
55
56/// Syntactic sugar: allows using closures directly as scene logic.
57pub struct CallbackLogic<F>(pub F);
58impl<F> SceneLogic for CallbackLogic<F>
59where
60    F: FnMut(&mut Scene, &Input, f32) + Send + Sync + 'static,
61{
62    fn update(&mut self, scene: &mut Scene, input: &Input, dt: f32) {
63        (self.0)(scene, input, dt);
64    }
65}
66
67/// Tag component indicating a split primitive node.
68#[derive(Debug, Clone, Copy, Default)]
69pub struct SplitPrimitiveTag;
70
71/// The scene graph container.
72///
73/// Scene is the pure data layer that stores scene graph hierarchy and component data.
74/// Uses `SlotMap` + `SecondaryMap` for high-performance component-based storage.
75///
76/// # Storage Layout
77///
78/// - `nodes`: Core node data (hierarchy and transforms) using `SlotMap`
79/// - Dense components (names, meshes): Use `SecondaryMap`
80/// - Sparse components (cameras, lights, skins): Use `SparseSecondaryMap`
81///
82/// # Example
83///
84/// ```rust,ignore
85/// let mut scene = Scene::new();
86///
87/// // Create nodes
88/// let root = scene.create_node_with_name("Root");
89/// let child = scene.create_node_with_name("Child");
90/// scene.attach(child, root);
91///
92/// // Add mesh component
93/// scene.set_mesh(child, Mesh::new(geometry, material));
94/// ```
95pub struct Scene {
96    /// Unique scene identifier (assigned automatically, read-only)
97    id: u32,
98
99    // === Core Node Storage ===
100    /// All nodes in the scene (`SlotMap` for O(1) access)
101    #[doc(hidden)]
102    pub nodes: SlotMap<NodeHandle, Node>,
103    /// Root-level nodes (no parent)
104    root_nodes: Vec<NodeHandle>,
105
106    // === Dense Components (most nodes have these) ===
107    /// Node names - almost all nodes have a name
108    pub names: SecondaryMap<NodeHandle, Cow<'static, str>>,
109
110    // === Sparse Components (only some nodes have these) ===
111    /// Mesh components stored directly on nodes
112    pub meshes: SparseSecondaryMap<NodeHandle, Mesh>,
113    /// Camera components stored directly on nodes
114    pub cameras: SparseSecondaryMap<NodeHandle, Camera>,
115    /// Light components stored directly on nodes
116    pub lights: SparseSecondaryMap<NodeHandle, Light>,
117    /// Skeletal skin bindings
118    pub skins: SparseSecondaryMap<NodeHandle, SkinBinding>,
119    /// Morph target weights
120    pub morph_weights: SparseSecondaryMap<NodeHandle, Vec<f32>>,
121    /// Animation mixer components (sparse, only character roots have animations)
122    pub animation_mixers: SparseSecondaryMap<NodeHandle, AnimationMixer>,
123    /// Rest pose transforms recorded before animation takes over.
124    /// Used to restore nodes when animations stop or blend with weight < 1.0.
125    pub rest_transforms: SparseSecondaryMap<NodeHandle, Transform>,
126    /// Split primitive tags
127    pub split_primitive_tags: SparseSecondaryMap<NodeHandle, SplitPrimitiveTag>,
128
129    // === Resource Pools (only truly shared resources) ===
130    /// Skeleton is a shared resource - multiple characters may reference the same skeleton definition
131    pub skeleton_pool: SlotMap<SkeletonKey, Skeleton>,
132
133    // === Environment and Global Settings ===
134    /// Scene environment settings (skybox, IBL)
135    pub environment: Environment,
136    /// Tone mapping settings (exposure, mode)
137    pub tone_mapping: ToneMappingSettings,
138    /// Bloom post-processing settings
139    pub bloom: BloomSettings,
140    /// SSAO (Screen Space Ambient Occlusion) settings
141    pub ssao: SsaoSettings,
142    /// Screen space effects settings (SSS, SSR)
143    pub screen_space: ScreenSpaceSettings,
144    /// Background rendering settings (mode + skybox uniform buffer)
145    pub background: BackgroundSettings,
146    /// Currently active camera for rendering
147    pub active_camera: Option<NodeHandle>,
148
149    // === GPU Resource Descriptors ===
150    #[doc(hidden)]
151    pub light_storage_buffer: CpuBuffer<Vec<GpuLightStorage>>,
152    #[doc(hidden)]
153    pub uniforms_buffer: CpuBuffer<EnvironmentUniforms>,
154    light_data_cache: Vec<GpuLightStorage>,
155
156    shader_defines: ShaderDefines,
157
158    last_env_version: u64,
159
160    // === Scene Logic System ===
161    pub(crate) logics: Vec<Box<dyn SceneLogic>>,
162}
163
164impl Default for Scene {
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170impl Scene {
171    pub fn new() -> Self {
172        Self {
173            id: NEXT_SCENE_ID.fetch_add(1, Ordering::Relaxed),
174
175            nodes: SlotMap::with_key(),
176            root_nodes: Vec::new(),
177
178            // Dense components
179            names: SecondaryMap::new(),
180
181            // Sparse components (direct storage)
182            meshes: SparseSecondaryMap::new(),
183            cameras: SparseSecondaryMap::new(),
184            lights: SparseSecondaryMap::new(),
185            skins: SparseSecondaryMap::new(),
186            morph_weights: SparseSecondaryMap::new(),
187            animation_mixers: SparseSecondaryMap::new(),
188            rest_transforms: SparseSecondaryMap::new(),
189
190            split_primitive_tags: SparseSecondaryMap::new(),
191
192            // Resource pools (only truly shared resources)
193            skeleton_pool: SlotMap::with_key(),
194
195            environment: Environment::new(),
196            tone_mapping: ToneMappingSettings::default(),
197            bloom: BloomSettings::default(),
198            ssao: SsaoSettings::default(),
199            screen_space: ScreenSpaceSettings::default(),
200            background: BackgroundSettings::default(),
201
202            active_camera: None,
203
204            light_storage_buffer: CpuBuffer::new(
205                [GpuLightStorage::default(); 16].to_vec(),
206                wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
207                Some("SceneLightStorageBuffer"),
208            ),
209            uniforms_buffer: CpuBuffer::new(
210                EnvironmentUniforms::default(),
211                wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
212                Some("SceneEnvironmentUniforms"),
213            ),
214
215            light_data_cache: Vec::with_capacity(16),
216
217            shader_defines: ShaderDefines::default(),
218            last_env_version: 0,
219
220            logics: Vec::new(),
221        }
222    }
223
224    // ========================================================================
225    // Accessors
226    // ========================================================================
227
228    /// Returns the unique scene identifier.
229    #[inline]
230    #[must_use]
231    pub fn id(&self) -> u32 {
232        self.id
233    }
234
235    /// Returns a read-only slice of root-level node handles.
236    #[inline]
237    #[must_use]
238    pub fn root_nodes(&self) -> &[NodeHandle] {
239        &self.root_nodes
240    }
241
242    /// Registers a node as a root-level node (no parent).
243    pub fn push_root_node(&mut self, handle: NodeHandle) {
244        self.root_nodes.push(handle);
245    }
246
247    /// Returns a read-only reference to the node storage.
248    #[inline]
249    #[must_use]
250    pub fn nodes(&self) -> &SlotMap<NodeHandle, Node> {
251        &self.nodes
252    }
253
254    // ========================================================================
255    // Node Management API
256    // ========================================================================
257
258    /// Creates a new node and returns its handle.
259    pub fn create_node(&mut self) -> NodeHandle {
260        self.nodes.insert(Node::new())
261    }
262
263    /// Creates a new node with a name.
264    pub fn create_node_with_name(&mut self, name: &str) -> NodeHandle {
265        let handle = self.nodes.insert(Node::new());
266        self.names.insert(handle, Cow::Owned(name.to_string()));
267        handle
268    }
269
270    /// Adds a node to the scene (defaults to root level).
271    pub fn add_node(&mut self, node: Node) -> NodeHandle {
272        let handle = self.nodes.insert(node);
273        self.root_nodes.push(handle);
274        handle
275    }
276
277    /// Adds a node as a child of the specified parent.
278    pub fn add_to_parent(&mut self, child: Node, parent_handle: NodeHandle) -> NodeHandle {
279        let handle = self.nodes.insert(child);
280
281        // Establish parent-child relationship
282        if let Some(parent) = self.nodes.get_mut(parent_handle) {
283            parent.children.push(handle);
284        }
285        if let Some(child_node) = self.nodes.get_mut(handle) {
286            child_node.parent = Some(parent_handle);
287        }
288
289        handle
290    }
291
292    /// Removes a node and all its descendants recursively.
293    pub fn remove_node(&mut self, handle: NodeHandle) {
294        // 1. Collect all nodes to remove (depth-first)
295        let mut to_remove = Vec::new();
296        self.collect_subtree(handle, &mut to_remove);
297
298        // 2. Handle parent relationship
299        if let Some(node) = self.nodes.get(handle) {
300            if let Some(parent_handle) = node.parent {
301                if let Some(parent) = self.nodes.get_mut(parent_handle) {
302                    parent.children.retain(|&h| h != handle);
303                }
304            } else {
305                self.root_nodes.retain(|&h| h != handle);
306            }
307        }
308
309        // 3. Remove all nodes and their components
310        for node_handle in to_remove {
311            self.meshes.remove(node_handle);
312            self.cameras.remove(node_handle);
313            self.lights.remove(node_handle);
314            self.skins.remove(node_handle);
315            self.morph_weights.remove(node_handle);
316            self.names.remove(node_handle);
317            self.animation_mixers.remove(node_handle);
318            self.rest_transforms.remove(node_handle);
319
320            self.nodes.remove(node_handle);
321        }
322    }
323
324    /// Collects all nodes in a subtree (depth-first).
325    fn collect_subtree(&self, handle: NodeHandle, result: &mut Vec<NodeHandle>) {
326        result.push(handle);
327        if let Some(node) = self.nodes.get(handle) {
328            for &child in &node.children {
329                self.collect_subtree(child, result);
330            }
331        }
332    }
333
334    /// Attaches a node as a child of another (establishes parent-child relationship).
335    pub fn attach(&mut self, child_handle: NodeHandle, parent_handle: NodeHandle) {
336        if child_handle == parent_handle {
337            log::warn!("Cannot attach node to itself!");
338            return;
339        }
340
341        // 1. Detach from old parent
342        if let Some(child_node) = self.nodes.get(child_handle) {
343            if let Some(old_parent) = child_node.parent {
344                if let Some(parent) = self.nodes.get_mut(old_parent) {
345                    parent.children.retain(|&h| h != child_handle);
346                }
347            } else {
348                self.root_nodes.retain(|&h| h != child_handle);
349            }
350        }
351
352        // 2. Attach to new parent
353        if let Some(parent) = self.nodes.get_mut(parent_handle) {
354            parent.children.push(child_handle);
355        } else {
356            log::error!("Parent node not found during attach!");
357            self.root_nodes.push(child_handle);
358            return;
359        }
360
361        // 3. Update child
362        if let Some(child) = self.nodes.get_mut(child_handle) {
363            child.parent = Some(parent_handle);
364            child.transform.mark_dirty();
365        }
366    }
367
368    /// Returns a read-only reference to a node.
369    #[inline]
370    pub fn get_node(&self, handle: NodeHandle) -> Option<&Node> {
371        self.nodes.get(handle)
372    }
373
374    /// Returns a mutable reference to a node.
375    #[inline]
376    pub fn get_node_mut(&mut self, handle: NodeHandle) -> Option<&mut Node> {
377        self.nodes.get_mut(handle)
378    }
379
380    // ========================================================================
381    // Component Management API (ECS-style)
382    // ========================================================================
383
384    /// Sets the name for a node.
385    pub fn set_name(&mut self, handle: NodeHandle, name: &str) {
386        self.names.insert(handle, Cow::Owned(name.to_string()));
387    }
388
389    /// Returns the name of a node.
390    pub fn get_name(&self, handle: NodeHandle) -> Option<&str> {
391        self.names.get(handle).map(std::convert::AsRef::as_ref)
392    }
393
394    /// Sets the mesh component for a node.
395    pub fn set_mesh(&mut self, handle: NodeHandle, mesh: Mesh) {
396        self.meshes.insert(handle, mesh);
397    }
398
399    /// Gets a reference to the node's Mesh component
400    pub fn get_mesh(&self, handle: NodeHandle) -> Option<&Mesh> {
401        self.meshes.get(handle)
402    }
403
404    /// Gets a mutable reference to the node's Mesh component
405    pub fn get_mesh_mut(&mut self, handle: NodeHandle) -> Option<&mut Mesh> {
406        self.meshes.get_mut(handle)
407    }
408
409    /// Sets the Camera component for a node
410    pub fn set_camera(&mut self, handle: NodeHandle, camera: Camera) {
411        self.cameras.insert(handle, camera);
412    }
413
414    /// Gets a reference to the node's Camera component
415    pub fn get_camera(&self, handle: NodeHandle) -> Option<&Camera> {
416        self.cameras.get(handle)
417    }
418
419    /// Gets a mutable reference to the node's Camera component
420    pub fn get_camera_mut(&mut self, handle: NodeHandle) -> Option<&mut Camera> {
421        self.cameras.get_mut(handle)
422    }
423
424    /// Sets the Light component for a node
425    pub fn set_light(&mut self, handle: NodeHandle, light: Light) {
426        self.lights.insert(handle, light);
427    }
428
429    /// Gets a reference to the node's Light component
430    pub fn get_light(&self, handle: NodeHandle) -> Option<&Light> {
431        self.lights.get(handle)
432    }
433
434    /// Gets a mutable reference to the node's Light component
435    pub fn get_light_mut(&mut self, handle: NodeHandle) -> Option<&mut Light> {
436        self.lights.get_mut(handle)
437    }
438
439    /// Gets both the Light component and Transform for a node (for light processing)
440    pub fn get_light_bundle(&mut self, handle: NodeHandle) -> Option<(&mut Light, &mut Node)> {
441        let light = self.lights.get_mut(handle)?;
442        let node = self.nodes.get_mut(handle)?;
443        Some((light, node))
444    }
445
446    /// Binds a skeleton to a node
447    pub fn bind_skeleton(
448        &mut self,
449        handle: NodeHandle,
450        skeleton_key: SkeletonKey,
451        bind_mode: BindMode,
452    ) {
453        if let Some(node) = self.nodes.get(handle) {
454            let bind_matrix_inv = node.transform.world_matrix.inverse();
455            self.skins.insert(
456                handle,
457                SkinBinding {
458                    skeleton: skeleton_key,
459                    bind_mode,
460                    bind_matrix_inv,
461                },
462            );
463        }
464    }
465
466    /// Gets the node's skin binding
467    pub fn get_skin(&self, handle: NodeHandle) -> Option<&SkinBinding> {
468        self.skins.get(handle)
469    }
470
471    /// Sets morph weights
472    pub fn set_morph_weights(&mut self, handle: NodeHandle, weights: Vec<f32>) {
473        self.morph_weights.insert(handle, weights);
474    }
475
476    /// Gets morph weights
477    pub fn get_morph_weights(&self, handle: NodeHandle) -> Option<&Vec<f32>> {
478        self.morph_weights.get(handle)
479    }
480
481    /// Gets a mutable reference to morph weights
482    pub fn get_morph_weights_mut(&mut self, handle: NodeHandle) -> Option<&mut Vec<f32>> {
483        self.morph_weights.get_mut(handle)
484    }
485
486    /// Sets morph weights for a node (from POD data)
487    pub fn set_morph_weights_from_pod(
488        &mut self,
489        handle: NodeHandle,
490        data: &myth_animation::values::MorphWeightData,
491    ) {
492        let weights = self.morph_weights.entry(handle).unwrap().or_default();
493
494        if weights.len() != data.weights.len() {
495            weights.resize(data.weights.len(), 0.0);
496        }
497        weights.copy_from_slice(&data.weights);
498    }
499
500    // ========================================================================
501    // Iterate over all active lights in the scene
502    // ========================================================================
503
504    pub fn iter_active_lights(&self) -> impl Iterator<Item = (&Light, &Affine3A)> {
505        self.lights.iter().filter_map(move |(node_handle, light)| {
506            let node = self.nodes.get(node_handle)?;
507            if node.visible {
508                Some((light, &node.transform.world_matrix))
509            } else {
510                None
511            }
512        })
513    }
514
515    // ========================================================================
516    // Component Query API
517    // ========================================================================
518
519    /// Gets the (Transform, Camera) bundle for the main camera
520    pub fn query_main_camera_bundle(&mut self) -> Option<(&mut Transform, &mut Camera)> {
521        let node_handle = self.active_camera?;
522        self.query_camera_bundle(node_handle)
523    }
524
525    pub fn query_camera_bundle(
526        &mut self,
527        node_handle: NodeHandle,
528    ) -> Option<(&mut Transform, &mut Camera)> {
529        // Check if camera component exists
530        if !self.cameras.contains_key(node_handle) {
531            return None;
532        }
533
534        // Use pointers to avoid simultaneous borrow conflict between nodes and cameras
535        let transform_ptr = self
536            .nodes
537            .get_mut(node_handle)
538            .map(|n| &raw mut n.transform)?;
539        let camera = self.cameras.get_mut(node_handle)?;
540
541        // SAFETY: transform and camera are disjoint memory regions
542        unsafe { Some((&mut *transform_ptr, camera)) }
543    }
544
545    /// Queries the Transform and Light for a specified node
546    pub fn query_light_bundle(
547        &mut self,
548        node_handle: NodeHandle,
549    ) -> Option<(&mut Transform, &Light)> {
550        let light = self.lights.get(node_handle)?;
551        let transform = &mut self.nodes.get_mut(node_handle)?.transform;
552        Some((transform, light))
553    }
554
555    /// Queries the Transform and Mesh for a specified node
556    pub fn query_mesh_bundle(
557        &mut self,
558        node_handle: NodeHandle,
559    ) -> Option<(&mut Transform, &Mesh)> {
560        let mesh = self.meshes.get(node_handle)?;
561        let transform = &mut self.nodes.get_mut(node_handle)?.transform;
562        Some((transform, mesh))
563    }
564
565    // ========================================================================
566    // Matrix Update Pipeline
567    // ========================================================================
568
569    /// Updates world matrices for the entire scene
570    pub fn update_matrix_world(&mut self) {
571        transform_system::update_hierarchy_iterative(
572            &mut self.nodes,
573            &mut self.cameras,
574            &self.root_nodes,
575        );
576    }
577
578    /// Updates world matrices for a specified subtree
579    pub fn update_subtree(&mut self, root_handle: NodeHandle) {
580        transform_system::update_subtree(&mut self.nodes, &mut self.cameras, root_handle);
581    }
582
583    // ========================================================================
584    // Resource Management API
585    // ========================================================================
586
587    pub fn add_mesh(&mut self, mesh: Mesh) -> NodeHandle {
588        let node_handle = self.create_node_with_name(&mesh.name);
589        self.meshes.insert(node_handle, mesh);
590        self.root_nodes.push(node_handle);
591        node_handle
592    }
593
594    pub fn add_mesh_to_parent(&mut self, mesh: Mesh, parent: NodeHandle) -> NodeHandle {
595        let node_handle = self.create_node_with_name(&mesh.name);
596        self.meshes.insert(node_handle, mesh);
597        self.attach(node_handle, parent);
598        node_handle
599    }
600
601    pub fn add_skeleton(&mut self, skeleton: Skeleton) -> SkeletonKey {
602        self.skeleton_pool.insert(skeleton)
603    }
604
605    pub fn add_camera(&mut self, camera: Camera) -> NodeHandle {
606        let node_handle = self.create_node_with_name("Camera");
607        self.cameras.insert(node_handle, camera);
608        self.root_nodes.push(node_handle);
609        node_handle
610    }
611
612    pub fn add_camera_to_parent(&mut self, camera: Camera, parent: NodeHandle) -> NodeHandle {
613        let node_handle = self.create_node_with_name("Camera");
614        self.cameras.insert(node_handle, camera);
615        self.attach(node_handle, parent);
616        node_handle
617    }
618
619    pub fn add_light(&mut self, light: Light) -> NodeHandle {
620        let node_handle = self.create_node_with_name("Light");
621        self.lights.insert(node_handle, light);
622        self.root_nodes.push(node_handle);
623        node_handle
624    }
625
626    pub fn add_light_to_parent(&mut self, light: Light, parent: NodeHandle) -> NodeHandle {
627        let node_handle = self.create_node_with_name("Light");
628        self.lights.insert(node_handle, light);
629        self.attach(node_handle, parent);
630        node_handle
631    }
632
633    pub fn mark_as_split_primitive(&mut self, handle: NodeHandle) {
634        self.split_primitive_tags.insert(handle, SplitPrimitiveTag);
635    }
636
637    /// Synchronizes shader macro definitions based on the current scene state.
638    fn sync_shader_defines(&mut self) {
639        let current_env_version = self.environment.version();
640
641        // Only recompute if the environment version has changed since the last computation
642        if self.last_env_version != current_env_version {
643            let mut defines = ShaderDefines::new();
644
645            // Recompute logic
646            if self.environment.has_env_map() {
647                defines.set("HAS_ENV_MAP", "1");
648            }
649            // ... additional defines based on scene state can be added here ...
650
651            self.shader_defines = defines;
652            self.last_env_version = current_env_version;
653        }
654    }
655
656    /// Computes the scene's shader macro definitions
657    ///
658    /// Uses internal caching mechanism, only recalculates when Environment version changes.
659    pub fn shader_defines(&self) -> &ShaderDefines {
660        &self.shader_defines
661    }
662
663    // ========================================================================
664    // Scene Update and Logic System
665    // ========================================================================
666
667    pub fn add_logic<L: SceneLogic>(&mut self, logic: L) {
668        self.logics.push(Box::new(logic));
669    }
670
671    /// Shortcut method: Add closure logic (for quick prototyping)
672    pub fn on_update<F>(&mut self, f: F)
673    where
674        F: FnMut(&mut Scene, &Input, f32) + Send + Sync + 'static,
675    {
676        self.add_logic(CallbackLogic(f));
677    }
678
679    /// Updates scene state (called every frame)
680    pub fn update(&mut self, input: &Input, dt: f32) {
681        // 1. Execute user scripts (Gameplay)
682        let mut logics = std::mem::take(&mut self.logics);
683        for logic in &mut logics {
684            logic.update(self, input, dt);
685        }
686        self.logics.append(&mut logics);
687
688        // 2. Animation system update (modifies node Transform)
689        {
690            let mut mixers = std::mem::take(&mut self.animation_mixers);
691            for (_handle, mixer) in &mut mixers {
692                mixer.update(dt, self);
693            }
694            self.animation_mixers = mixers;
695        }
696
697        // 3. Execute internal engine systems (Transform, Skeleton, Morph)
698        self.update_matrix_world();
699        self.update_skeletons();
700        self.sync_morph_weights();
701        self.sync_shader_defines();
702        self.sync_gpu_buffers();
703    }
704
705    /// Syncs GPU Buffer data
706    pub fn sync_gpu_buffers(&mut self) {
707        self.sync_light_buffer();
708        self.sync_environment_buffer();
709    }
710
711    /// Syncs light data to GPU Buffer
712    fn sync_light_buffer(&mut self) {
713        let mut cache = std::mem::take(&mut self.light_data_cache);
714
715        cache.clear();
716
717        for (light, world_matrix) in self.iter_active_lights() {
718            let pos = world_matrix.translation.to_vec3();
719            let dir = world_matrix.transform_vector3(-Vec3::Z).normalize();
720
721            let mut gpu_light = GpuLightStorage {
722                color: light.color,
723                intensity: light.intensity,
724                position: pos,
725                direction: dir,
726                shadow_layer_index: -1,
727                ..Default::default()
728            };
729
730            match &light.kind {
731                LightKind::Point(point) => {
732                    gpu_light.light_type = 1;
733                    gpu_light.range = point.range;
734                }
735                LightKind::Spot(spot) => {
736                    gpu_light.light_type = 2;
737                    gpu_light.range = spot.range;
738                    gpu_light.inner_cone_cos = spot.inner_cone.cos();
739                    gpu_light.outer_cone_cos = spot.outer_cone.cos();
740                }
741                LightKind::Directional(_) => {
742                    gpu_light.light_type = 0;
743                }
744            }
745
746            cache.push(gpu_light);
747        }
748
749        if cache.is_empty() {
750            cache.push(GpuLightStorage::default());
751        }
752
753        self.light_data_cache = cache;
754
755        let needs_update =
756            self.light_storage_buffer.read().as_slice() != self.light_data_cache.as_slice();
757
758        if needs_update {
759            self.light_storage_buffer
760                .write()
761                .clone_from(&self.light_data_cache);
762        }
763    }
764
765    /// Syncs environment data to GPU Buffer
766    fn sync_environment_buffer(&mut self) {
767        let env = &self.environment;
768        let light_count = self.lights.len();
769
770        let new_uniforms = EnvironmentUniforms {
771            ambient_light: env.ambient,
772            num_lights: light_count as u32,
773            env_map_intensity: env.intensity,
774            env_map_rotation: env.rotation,
775            // env_map_max_mip_level is set by ResourceManager::resolve_gpu_environment
776            // during the prepare phase, so we preserve the existing value here.
777            env_map_max_mip_level: self.uniforms_buffer.read().env_map_max_mip_level,
778            ..Default::default()
779        };
780
781        let needs_update = *self.uniforms_buffer.read() != new_uniforms;
782
783        if needs_update {
784            *self.uniforms_buffer.write() = new_uniforms;
785        }
786    }
787
788    // ========================================================================
789    // GPU Resource Access Interface
790    // ========================================================================
791
792    pub fn light_storage(&self) -> &CpuBuffer<Vec<GpuLightStorage>> {
793        &self.light_storage_buffer
794    }
795
796    pub fn environment_uniforms(&self) -> &CpuBuffer<EnvironmentUniforms> {
797        &self.uniforms_buffer
798    }
799
800    pub fn update_skeletons(&mut self) {
801        let mut tasks = Vec::new();
802
803        for (node_handle, binding) in &self.skins {
804            if let Some(node) = self.nodes.get(node_handle) {
805                let root_inv = match binding.bind_mode {
806                    BindMode::Attached => node.transform.world_matrix.inverse(),
807                    BindMode::Detached => binding.bind_matrix_inv,
808                };
809                tasks.push((binding.skeleton, root_inv));
810            }
811        }
812
813        for (skeleton_id, root_inv) in tasks {
814            if let Some(skeleton) = self.skeleton_pool.get_mut(skeleton_id) {
815                skeleton.compute_joint_matrices(&self.nodes, root_inv);
816                // Lazy compute bounding box (only computed when first needed)
817                if skeleton.local_bounds.is_none() {
818                    skeleton.compute_local_bounds(&self.nodes);
819                }
820            }
821        }
822    }
823
824    pub fn sync_morph_weights(&mut self) {
825        for (handle, weights) in &self.morph_weights {
826            if weights.is_empty() {
827                continue;
828            }
829
830            let weights_slice = weights.as_slice();
831
832            if let Some(mesh) = self.meshes.get_mut(handle) {
833                mesh.set_morph_target_influences(weights_slice);
834                mesh.update_morph_uniforms();
835            } else if let Some(node) = self.nodes.get(handle) {
836                for &child_handle in &node.children {
837                    // Broadcast to child nodes that have SplitPrimitiveTag
838                    if self.split_primitive_tags.contains_key(child_handle)
839                        && let Some(child_mesh) = self.meshes.get_mut(child_handle)
840                    {
841                        child_mesh.set_morph_target_influences(weights_slice);
842                        child_mesh.update_morph_uniforms();
843                    }
844                }
845            }
846        }
847    }
848
849    pub fn main_camera_node_mut(&mut self) -> Option<&mut Node> {
850        let handle = self.active_camera?;
851        self.get_node_mut(handle)
852    }
853
854    pub fn main_camera_node(&self) -> Option<&Node> {
855        let handle = self.active_camera?;
856        self.get_node(handle)
857    }
858
859    // ========================================================================
860    // Background API
861    // ========================================================================
862
863    /// Sets the background to a solid color.
864    pub fn set_background_color(&mut self, r: f32, g: f32, b: f32) {
865        self.background.set_mode(BackgroundMode::color(r, g, b));
866    }
867
868    // ========================================================================
869    // High-Level Helpers (node wrapper, builder)
870    // ========================================================================
871
872    /// Returns a chainable wrapper for the given node.
873    ///
874    /// Silently no-ops if the handle is stale, avoiding `unwrap()`.
875    ///
876    /// # Example
877    ///
878    /// ```rust,ignore
879    /// scene.node(&handle)
880    ///     .set_position(0.0, 3.0, 0.0)
881    ///     .set_scale(2.0)
882    ///     .look_at(Vec3::ZERO);
883    /// ```
884    pub fn node(&mut self, handle: &NodeHandle) -> SceneNode<'_> {
885        SceneNode::new(self, *handle)
886    }
887
888    /// Starts building a node
889    pub fn build_node(&mut self, name: &str) -> NodeBuilder<'_> {
890        NodeBuilder::new(self, name)
891    }
892
893    /// Finds a node by name
894    pub fn find_node_by_name(&self, name: &str) -> Option<NodeHandle> {
895        for (handle, node_name) in &self.names {
896            if node_name.as_ref() == name {
897                return Some(handle);
898            }
899        }
900        None
901    }
902
903    /// Gets the global transform matrix of a node
904    pub fn get_global_transform(&self, handle: NodeHandle) -> Affine3A {
905        self.nodes
906            .get(handle)
907            .map_or(Affine3A::IDENTITY, |n| n.transform.world_matrix)
908    }
909
910    /// Plays a specific animation clip on the node (if an AnimationMixer is present)
911    pub fn play_animation(&mut self, node_handle: NodeHandle, clip_name: &str) {
912        if let Some(mixer) = self.animation_mixers.get_mut(node_handle) {
913            mixer.play(clip_name);
914        } else {
915            log::warn!("No animation mixer found for node {node_handle:?}");
916        }
917    }
918
919    /// Plays any animation on the node (used for simple cases where clip name is not important)
920    pub fn play_if_any_animation(&mut self, node_handle: NodeHandle) {
921        if let Some(mixer) = self.animation_mixers.get_mut(node_handle) {
922            mixer
923                .any_action()
924                .map(myth_animation::mixer::ActionControl::play);
925        } else {
926            log::info!("No animation mixer found for node {node_handle:?}");
927        }
928    }
929
930    // ========================================================================
931    // Bounding Box Queries
932    // ========================================================================
933
934    /// Computes the world-space bounding box of a single node (not including children).
935    ///
936    /// `query` implements [`GeometryQuery`] to map geometry handles to local-space bounding boxes.
937    fn get_bbox_of_one_node(
938        &self,
939        node_handle: NodeHandle,
940        query: &impl crate::GeometryQuery,
941    ) -> Option<myth_resources::BoundingBox> {
942        let node = self.get_node(node_handle)?;
943        if !node.visible {
944            return None;
945        }
946        let mesh = self.meshes.get(node_handle)?;
947        if !mesh.visible {
948            return None;
949        }
950
951        // When there's a skeleton binding, use the skeleton's bounding box
952        if let Some(skeleton_binding) = self.skins.get(node_handle)
953            && let Some(skeleton) = self.skeleton_pool.get(skeleton_binding.skeleton)
954        {
955            return skeleton.compute_tight_world_bounds(&self.nodes);
956        }
957
958        // Otherwise compute from the geometry's static bounding box
959        let local_bbox = query.get_geometry_bbox(mesh.geometry)?;
960        Some(local_bbox.transform(&node.transform.world_matrix))
961    }
962
963    /// Recursively computes the world-space bounding box enclosing a node and all its descendants.
964    ///
965    /// `query` implements [`crate::GeometryQuery`] to map geometry handles to local-space bounding boxes.
966    pub fn get_bbox_of_node(
967        &self,
968        node_handle: NodeHandle,
969        query: &impl crate::GeometryQuery,
970    ) -> Option<myth_resources::BoundingBox> {
971        let mut combined_bbox = self.get_bbox_of_one_node(node_handle, query);
972        let node = self.get_node(node_handle)?;
973        for &child_handle in &node.children {
974            if let Some(child_bbox) = self.get_bbox_of_node(child_handle, query) {
975                combined_bbox = match combined_bbox {
976                    Some(existing) => Some(existing.union(&child_bbox)),
977                    None => Some(child_bbox),
978                };
979            }
980        }
981
982        combined_bbox
983    }
984}
985
986// ============================================================================
987// NodeBuilder
988// ============================================================================
989
990pub struct NodeBuilder<'a> {
991    scene: &'a mut Scene,
992    handle: NodeHandle,
993    parent: Option<NodeHandle>,
994    mesh: Option<Mesh>,
995}
996
997impl<'a> NodeBuilder<'a> {
998    pub fn new(scene: &'a mut Scene, name: &str) -> Self {
999        let handle = scene.nodes.insert(Node::new());
1000        scene.names.insert(handle, Cow::Owned(name.to_string()));
1001        Self {
1002            scene,
1003            handle,
1004            parent: None,
1005            mesh: None,
1006        }
1007    }
1008
1009    #[must_use]
1010    pub fn with_position(self, x: f32, y: f32, z: f32) -> Self {
1011        if let Some(node) = self.scene.nodes.get_mut(self.handle) {
1012            node.transform.position = glam::Vec3::new(x, y, z);
1013        }
1014        self
1015    }
1016
1017    #[must_use]
1018    pub fn with_scale(self, s: f32) -> Self {
1019        if let Some(node) = self.scene.nodes.get_mut(self.handle) {
1020            node.transform.scale = glam::Vec3::splat(s);
1021        }
1022        self
1023    }
1024
1025    #[must_use]
1026    pub fn with_parent(mut self, parent: NodeHandle) -> Self {
1027        self.parent = Some(parent);
1028        self
1029    }
1030
1031    #[must_use]
1032    pub fn with_mesh(mut self, mesh: Mesh) -> Self {
1033        self.mesh = Some(mesh);
1034        self
1035    }
1036
1037    pub fn build(self) -> NodeHandle {
1038        let handle = self.handle;
1039
1040        // Set Mesh component
1041        if let Some(mesh) = self.mesh {
1042            self.scene.meshes.insert(handle, mesh);
1043        }
1044
1045        // Handle parent-child relationship
1046        if let Some(parent) = self.parent {
1047            self.scene.attach(handle, parent);
1048        } else {
1049            self.scene.root_nodes.push(handle);
1050        }
1051
1052        handle
1053    }
1054}
1055
1056// ============================================================================
1057// AnimationTarget Implementation
1058// ============================================================================
1059
1060impl AnimationTarget for Scene {
1061    fn node_children(&self, handle: NodeHandle) -> Option<Vec<NodeHandle>> {
1062        self.nodes.get(handle).map(|n| n.children().to_vec())
1063    }
1064
1065    fn node_name(&self, handle: NodeHandle) -> Option<String> {
1066        self.names.get(handle).map(|n| n.as_ref().to_string())
1067    }
1068
1069    fn node_transform(&self, handle: NodeHandle) -> Option<Transform> {
1070        self.nodes.get(handle).map(|n| n.transform)
1071    }
1072
1073    fn set_node_position(&mut self, handle: NodeHandle, position: Vec3) {
1074        if let Some(node) = self.nodes.get_mut(handle) {
1075            node.transform.position = position;
1076        }
1077    }
1078
1079    fn set_node_rotation(&mut self, handle: NodeHandle, rotation: Quat) {
1080        if let Some(node) = self.nodes.get_mut(handle) {
1081            node.transform.rotation = rotation;
1082        }
1083    }
1084
1085    fn set_node_scale(&mut self, handle: NodeHandle, scale: Vec3) {
1086        if let Some(node) = self.nodes.get_mut(handle) {
1087            node.transform.scale = scale;
1088        }
1089    }
1090
1091    fn mark_node_dirty(&mut self, handle: NodeHandle) {
1092        if let Some(node) = self.nodes.get_mut(handle) {
1093            node.transform.mark_dirty();
1094        }
1095    }
1096
1097    fn has_rest_transform(&self, handle: NodeHandle) -> bool {
1098        self.rest_transforms.contains_key(handle)
1099    }
1100
1101    fn rest_transform(&self, handle: NodeHandle) -> Option<Transform> {
1102        self.rest_transforms.get(handle).copied()
1103    }
1104
1105    fn store_rest_transform(&mut self, handle: NodeHandle, transform: Transform) {
1106        self.rest_transforms.insert(handle, transform);
1107    }
1108
1109    fn morph_weights_mut(&mut self, handle: NodeHandle) -> &mut Vec<f32> {
1110        self.morph_weights.entry(handle).unwrap().or_default()
1111    }
1112}