rg3d/scene/
mod.rs

1#![warn(missing_docs)]
2
3//! Contains all structures and methods to create and manage 3D scenes.
4//!
5//! A `Scene` is a container for graph nodes, animations and physics.
6
7pub mod accel;
8pub mod base;
9pub mod camera;
10pub mod collider;
11pub mod debug;
12pub mod decal;
13pub mod dim2;
14pub mod graph;
15pub mod joint;
16pub mod legacy_physics;
17pub mod light;
18pub mod mesh;
19pub mod node;
20pub mod particle_system;
21pub mod rigidbody;
22pub mod sprite;
23pub mod terrain;
24pub mod transform;
25pub mod variable;
26pub mod visibility;
27
28use crate::scene::base::legacy::PhysicsBinding;
29use crate::scene::legacy_physics::dim3::RigidBodyHandle;
30use crate::{
31    animation::AnimationContainer,
32    core::{
33        algebra::{UnitQuaternion, Vector2},
34        color::Color,
35        instant,
36        pool::{Handle, Pool, PoolIterator, PoolIteratorMut, Ticket},
37        sstorage::ImmutableString,
38        visitor::{Visit, VisitError, VisitResult, Visitor},
39    },
40    engine::{resource_manager::ResourceManager, PhysicsBinder},
41    material::{shader::SamplerFallback, PropertyValue},
42    resource::texture::Texture,
43    scene::{
44        base::BaseBuilder,
45        collider::{ColliderBuilder, ColliderShape, GeometrySource},
46        debug::SceneDrawingContext,
47        graph::{
48            physics::{
49                collider_shape_from_native_collider, joint_params_from_native,
50                PhysicsPerformanceStatistics,
51            },
52            Graph,
53        },
54        joint::JointBuilder,
55        legacy_physics::LegacyPhysics,
56        mesh::buffer::{
57            VertexAttributeDataType, VertexAttributeDescriptor, VertexAttributeUsage,
58            VertexWriteTrait,
59        },
60        node::Node,
61        rigidbody::{RigidBodyBuilder, RigidBodyType},
62        transform::TransformBuilder,
63    },
64    sound::{context::SoundContext, engine::SoundEngine},
65    utils::{lightmap::Lightmap, log::Log, log::MessageKind, navmesh::Navmesh},
66};
67use fxhash::FxHashMap;
68use std::{
69    fmt::{Display, Formatter},
70    ops::{Index, IndexMut},
71    path::Path,
72    sync::{Arc, Mutex},
73};
74
75/// A container for navigational meshes.
76#[derive(Default, Clone, Debug)]
77pub struct NavMeshContainer {
78    pool: Pool<Navmesh>,
79}
80
81impl NavMeshContainer {
82    /// Adds new navigational mesh to the container and returns its handle.
83    pub fn add(&mut self, navmesh: Navmesh) -> Handle<Navmesh> {
84        self.pool.spawn(navmesh)
85    }
86
87    /// Removes navigational mesh by its handle.
88    pub fn remove(&mut self, handle: Handle<Navmesh>) -> Navmesh {
89        self.pool.free(handle)
90    }
91
92    /// Creates new immutable iterator.
93    pub fn iter(&self) -> impl Iterator<Item = &Navmesh> {
94        self.pool.iter()
95    }
96
97    /// Creates new immutable iterator.
98    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Navmesh> {
99        self.pool.iter_mut()
100    }
101
102    /// Creates a handle to navmesh from its index.
103    pub fn handle_from_index(&self, i: u32) -> Handle<Navmesh> {
104        self.pool.handle_from_index(i)
105    }
106
107    /// Destroys all navmeshes. All handles will become invalid.
108    pub fn clear(&mut self) {
109        self.pool.clear()
110    }
111
112    /// Checks if given handle is valid.
113    pub fn is_valid_handle(&self, handle: Handle<Navmesh>) -> bool {
114        self.pool.is_valid_handle(handle)
115    }
116}
117
118impl Index<Handle<Navmesh>> for NavMeshContainer {
119    type Output = Navmesh;
120
121    fn index(&self, index: Handle<Navmesh>) -> &Self::Output {
122        &self.pool[index]
123    }
124}
125
126impl IndexMut<Handle<Navmesh>> for NavMeshContainer {
127    fn index_mut(&mut self, index: Handle<Navmesh>) -> &mut Self::Output {
128        &mut self.pool[index]
129    }
130}
131
132impl Visit for NavMeshContainer {
133    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
134        visitor.enter_region(name)?;
135
136        self.pool.visit("Pool", visitor)?;
137
138        visitor.leave_region()
139    }
140}
141
142/// See module docs.
143#[derive(Debug)]
144pub struct Scene {
145    /// Graph is main container for all scene nodes. It calculates global transforms for nodes,
146    /// updates them and performs all other important work. See `graph` module docs for more
147    /// info.
148    pub graph: Graph,
149
150    /// Animations container controls all animation on scene. Each animation can have tracks which
151    /// has handles to graph nodes. See `animation` module docs for more info.
152    pub animations: AnimationContainer,
153
154    /// Texture to draw scene to. If empty, scene will be drawn on screen directly.
155    /// It is useful to "embed" some scene into other by drawing a quad with this
156    /// texture. This can be used to make in-game video conference - you can make
157    /// separate scene with your characters and draw scene into texture, then in
158    /// main scene you can attach this texture to some quad which will be used as
159    /// monitor. Other usage could be previewer of models, like pictogram of character
160    /// in real-time strategies, in other words there are plenty of possible uses.
161    pub render_target: Option<Texture>,
162
163    /// Drawing context for simple graphics.
164    pub drawing_context: SceneDrawingContext,
165
166    /// A sound context that holds all sound sources, effects, etc. belonging to the scene.
167    pub sound_context: SoundContext,
168
169    /// A container for navigational meshes.
170    pub navmeshes: NavMeshContainer,
171
172    /// Current lightmap.
173    lightmap: Option<Lightmap>,
174
175    /// Performance statistics from last `update` call.
176    pub performance_statistics: PerformanceStatistics,
177
178    /// Color of ambient lighting.
179    pub ambient_lighting_color: Color,
180
181    /// Whether the scene will be updated and rendered or not. Default is true.
182    /// This flag allowing you to build a scene manager for your game. For example,
183    /// you may have a scene for menu and one per level. Menu's scene is persistent,
184    /// however you don't want it to be updated and renderer while you have a level
185    /// loaded and playing a game. When you're start playing, just set `enabled` flag
186    /// to false for menu's scene and when you need to open a menu - set it to true and
187    /// set `enabled` flag to false for level's scene.
188    pub enabled: bool,
189
190    // Legacy physics world.
191    legacy_physics: LegacyPhysics,
192
193    // Legacy physics binder.
194    legacy_physics_binder: PhysicsBinder<Node, RigidBodyHandle>,
195}
196
197impl Default for Scene {
198    fn default() -> Self {
199        Self {
200            graph: Default::default(),
201            animations: Default::default(),
202            legacy_physics: Default::default(),
203            legacy_physics_binder: Default::default(),
204            render_target: None,
205            lightmap: None,
206            drawing_context: Default::default(),
207            sound_context: Default::default(),
208            navmeshes: Default::default(),
209            performance_statistics: Default::default(),
210            ambient_lighting_color: Color::opaque(100, 100, 100),
211            enabled: true,
212        }
213    }
214}
215
216fn map_texture(tex: Option<Texture>, rm: ResourceManager) -> Option<Texture> {
217    if let Some(shallow_texture) = tex {
218        let shallow_texture = shallow_texture.state();
219        Some(rm.request_texture(shallow_texture.path()))
220    } else {
221        None
222    }
223}
224
225/// A structure that holds times that specific update step took.
226#[derive(Clone, Default, Debug)]
227pub struct PerformanceStatistics {
228    /// Physics performance statistics.
229    pub physics: PhysicsPerformanceStatistics,
230
231    /// 2D Physics performance statistics.
232    pub physics2d: PhysicsPerformanceStatistics,
233
234    /// A time (in seconds) which was required to update graph.
235    pub graph_update_time: f32,
236
237    /// A time (in seconds) which was required to update animations.
238    pub animations_update_time: f32,
239
240    /// A time (in seconds) which was required to render sounds.
241    pub sound_update_time: f32,
242}
243
244impl Display for PerformanceStatistics {
245    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
246        write!(
247            f,
248            "{}\nGraph: {} ms\nAnimations: {} ms\nSounds: {} ms",
249            self.physics,
250            self.graph_update_time * 1000.0,
251            self.animations_update_time * 1000.0,
252            self.sound_update_time * 1000.0
253        )
254    }
255}
256
257impl Scene {
258    /// Creates new scene with single root node.
259    ///
260    /// # Notes
261    ///
262    /// This method differs from Default trait implementation! Scene::default() creates
263    /// empty graph with no nodes.
264    #[inline]
265    pub fn new() -> Self {
266        Self {
267            // Graph must be created with `new` method because it differs from `default`
268            graph: Graph::new(),
269            legacy_physics: Default::default(),
270            animations: Default::default(),
271            legacy_physics_binder: Default::default(),
272            render_target: None,
273            lightmap: None,
274            drawing_context: Default::default(),
275            sound_context: SoundContext::new(),
276            navmeshes: Default::default(),
277            performance_statistics: Default::default(),
278            ambient_lighting_color: Color::opaque(100, 100, 100),
279            enabled: true,
280        }
281    }
282
283    /// Tries to load scene from given file. File can contain any scene in native engine format.
284    /// Such scenes can be made in rusty editor.
285    pub async fn from_file<P: AsRef<Path>>(
286        path: P,
287        resource_manager: ResourceManager,
288    ) -> Result<Self, VisitError> {
289        let mut scene = Scene::default();
290        {
291            let mut visitor = Visitor::load_binary(path.as_ref()).await?;
292            scene.visit("Scene", &mut visitor)?;
293        }
294
295        // Collect all used resources and wait for them.
296        let mut resources = Vec::new();
297        for node in scene.graph.linear_iter_mut() {
298            if let Some(shallow_resource) = node.resource.clone() {
299                let resource = resource_manager
300                    .clone()
301                    .request_model(&shallow_resource.state().path());
302                node.resource = Some(resource.clone());
303                resources.push(resource);
304            }
305        }
306
307        let _ = crate::core::futures::future::join_all(resources).await;
308
309        // Restore pointers to resources. Scene saves only paths to resources, here we must
310        // find real resources instead.
311
312        for node in scene.graph.linear_iter_mut() {
313            match node {
314                Node::Mesh(mesh) => {
315                    for surface in mesh.surfaces_mut() {
316                        surface.material().lock().resolve(resource_manager.clone());
317                    }
318                }
319                Node::Sprite(sprite) => {
320                    sprite.set_texture(map_texture(sprite.texture(), resource_manager.clone()));
321                }
322                Node::ParticleSystem(particle_system) => {
323                    particle_system.set_texture(map_texture(
324                        particle_system.texture(),
325                        resource_manager.clone(),
326                    ));
327                }
328                Node::Camera(camera) => {
329                    camera.set_environment(map_texture(
330                        camera.environment_map(),
331                        resource_manager.clone(),
332                    ));
333
334                    if let Some(skybox) = camera.skybox_mut() {
335                        skybox.bottom =
336                            map_texture(skybox.bottom.clone(), resource_manager.clone());
337                        skybox.top = map_texture(skybox.top.clone(), resource_manager.clone());
338                        skybox.left = map_texture(skybox.left.clone(), resource_manager.clone());
339                        skybox.right = map_texture(skybox.right.clone(), resource_manager.clone());
340                        skybox.front = map_texture(skybox.front.clone(), resource_manager.clone());
341                        skybox.back = map_texture(skybox.back.clone(), resource_manager.clone());
342                    }
343                }
344                Node::Terrain(terrain) => {
345                    for layer in terrain.layers() {
346                        layer.material.lock().resolve(resource_manager.clone());
347                    }
348                }
349                Node::Rectangle(rectangle) => {
350                    rectangle.set_texture(map_texture(
351                        rectangle.texture_value(),
352                        resource_manager.clone(),
353                    ));
354                }
355                Node::Decal(decal) => {
356                    decal.set_diffuse_texture(map_texture(
357                        decal.diffuse_texture_value(),
358                        resource_manager.clone(),
359                    ));
360                    decal.set_normal_texture(map_texture(
361                        decal.normal_texture_value(),
362                        resource_manager.clone(),
363                    ));
364                }
365                _ => (),
366            }
367        }
368
369        if let Some(lightmap) = scene.lightmap.as_mut() {
370            for entries in lightmap.map.values_mut() {
371                for entry in entries.iter_mut() {
372                    entry.texture = map_texture(entry.texture.clone(), resource_manager.clone());
373                }
374            }
375        }
376
377        // We have to wait until skybox textures are all loaded, because we need to read their data
378        // to re-create cube map.
379        let mut skybox_textures = Vec::new();
380        for node in scene.graph.linear_iter() {
381            if let Node::Camera(camera) = node {
382                if let Some(skybox) = camera.skybox_ref() {
383                    skybox_textures.extend(skybox.textures().iter().filter_map(|t| t.clone()));
384                }
385            }
386        }
387        crate::core::futures::future::join_all(skybox_textures).await;
388
389        // And do resolve to extract correct graphical data and so on.
390        scene.resolve();
391
392        Ok(scene)
393    }
394
395    /// Removes node from scene with all associated entities, like animations etc. This method
396    /// should be used all times instead of [Graph::remove_node](crate::scene::graph::Graph::remove_node).
397    ///
398    /// # Panics
399    ///
400    /// Panics if handle is invalid.
401    pub fn remove_node(&mut self, handle: Handle<Node>) {
402        for descendant in self.graph.traverse_handle_iter(handle) {
403            // Remove all associated animations.
404            self.animations.retain(|animation| {
405                for track in animation.get_tracks() {
406                    if track.get_node() == descendant {
407                        return false;
408                    }
409                }
410                true
411            });
412        }
413
414        self.graph.remove_node(handle)
415    }
416
417    fn convert_legacy_physics(&mut self) {
418        // Convert rigid bodies and colliders.
419        let mut body_map = FxHashMap::default();
420        for (node, body_handle) in self.legacy_physics_binder.forward_map() {
421            let body_ref = if let Some(body_ref) = self.legacy_physics.bodies.get(body_handle) {
422                body_ref
423            } else {
424                continue;
425            };
426
427            let [x_rotation_locked, y_rotation_locked, z_rotation_locked] =
428                body_ref.is_rotation_locked();
429
430            let body_node_handle = RigidBodyBuilder::new(
431                BaseBuilder::new()
432                    .with_name("Rigid Body")
433                    .with_local_transform(
434                        TransformBuilder::new()
435                            .with_local_position(body_ref.position().translation.vector)
436                            .with_local_rotation(body_ref.position().rotation)
437                            .build(),
438                    ),
439            )
440            .with_body_type(RigidBodyType::from(body_ref.body_type()))
441            .with_mass(body_ref.mass())
442            .with_ang_vel(*body_ref.angvel())
443            .with_lin_vel(*body_ref.linvel())
444            .with_lin_damping(body_ref.linear_damping())
445            .with_ang_damping(body_ref.angular_damping())
446            .with_x_rotation_locked(x_rotation_locked)
447            .with_y_rotation_locked(y_rotation_locked)
448            .with_z_rotation_locked(z_rotation_locked)
449            .with_translation_locked(body_ref.is_translation_locked())
450            .with_ccd_enabled(body_ref.is_ccd_enabled())
451            .build(&mut self.graph);
452
453            body_map.insert(body_handle, body_node_handle);
454
455            for c in body_ref.colliders() {
456                let collider_ref =
457                    if let Some(collider_ref) = self.legacy_physics.colliders.native_ref(*c) {
458                        collider_ref
459                    } else {
460                        continue;
461                    };
462
463                let mut shape = collider_shape_from_native_collider(collider_ref.shape());
464
465                let name = match shape {
466                    ColliderShape::Ball(_) => "Ball Collider",
467                    ColliderShape::Cylinder(_) => "Cylinder Collider",
468                    ColliderShape::Cone(_) => "Cone Collider",
469                    ColliderShape::Cuboid(_) => "Cuboid Collider",
470                    ColliderShape::Capsule(_) => "Capsule Collider",
471                    ColliderShape::Segment(_) => "Segment Collider",
472                    ColliderShape::Triangle(_) => "Triangle Collider",
473                    ColliderShape::Trimesh(_) => "Trimesh Collider",
474                    ColliderShape::Heightfield(_) => "Heightfield Collider",
475                    ColliderShape::Polyhedron(_) => "Convex Polyhedron",
476                };
477
478                // Trimesh and heightfield needs extra care.
479                match shape {
480                    ColliderShape::Trimesh(ref mut trimesh) => {
481                        trimesh.sources = self
482                            .graph
483                            .traverse_handle_iter(*node)
484                            .filter(|h| self.graph[*h].is_mesh())
485                            .map(GeometrySource)
486                            .collect::<Vec<_>>();
487                    }
488                    ColliderShape::Heightfield(ref mut heightfield) => {
489                        heightfield.geometry_source = GeometrySource(*node);
490                    }
491                    _ => (),
492                }
493
494                let collider_handle = ColliderBuilder::new(
495                    BaseBuilder::new().with_name(name).with_local_transform(
496                        TransformBuilder::new()
497                            .with_local_position(
498                                collider_ref
499                                    .position_wrt_parent()
500                                    .map(|p| p.translation.vector)
501                                    .unwrap_or_default(),
502                            )
503                            .with_local_rotation(
504                                collider_ref
505                                    .position_wrt_parent()
506                                    .map(|p| p.rotation)
507                                    .unwrap_or_default(),
508                            )
509                            .build(),
510                    ),
511                )
512                .with_friction_combine_rule(collider_ref.friction_combine_rule().into())
513                .with_restitution_combine_rule(collider_ref.restitution_combine_rule().into())
514                .with_shape(shape)
515                .with_sensor(collider_ref.is_sensor())
516                .with_restitution(collider_ref.restitution())
517                .with_density(collider_ref.density())
518                .with_collision_groups(collider_ref.collision_groups().into())
519                .with_solver_groups(collider_ref.solver_groups().into())
520                .with_friction(collider_ref.friction())
521                .build(&mut self.graph);
522
523                self.graph.link_nodes(collider_handle, body_node_handle);
524            }
525
526            let node_ref = &mut self.graph[*node];
527            node_ref
528                .local_transform_mut()
529                .set_position(Default::default())
530                .set_rotation(UnitQuaternion::default());
531            let parent = node_ref.parent();
532
533            match node_ref.physics_binding {
534                PhysicsBinding::NodeWithBody => {
535                    self.graph.link_nodes(*node, body_node_handle);
536                    self.graph.link_nodes(body_node_handle, parent);
537                }
538                PhysicsBinding::BodyWithNode => {
539                    self.graph.link_nodes(body_node_handle, *node);
540                }
541            }
542        }
543
544        // Convert joints.
545        for joint in self.legacy_physics.joints.iter() {
546            let body1 = if let Some(body1) = self
547                .legacy_physics
548                .bodies
549                .handle_map()
550                .key_of(&joint.body1)
551                .and_then(|h| body_map.get(h))
552            {
553                *body1
554            } else {
555                continue;
556            };
557
558            let body2 = if let Some(body2) = self
559                .legacy_physics
560                .bodies
561                .handle_map()
562                .key_of(&joint.body2)
563                .and_then(|h| body_map.get(h))
564            {
565                *body2
566            } else {
567                continue;
568            };
569
570            let joint_handle = JointBuilder::new(BaseBuilder::new())
571                .with_params(joint_params_from_native(&joint.params))
572                .with_body1(body1)
573                .with_body2(body2)
574                .build(&mut self.graph);
575
576            self.graph.link_nodes(joint_handle, body1);
577        }
578    }
579
580    pub(in crate) fn resolve(&mut self) {
581        Log::writeln(MessageKind::Information, "Starting resolve...".to_owned());
582
583        self.graph.resolve();
584        self.animations.resolve(&self.graph);
585
586        self.graph.update_hierarchical_data();
587        self.legacy_physics
588            .resolve(&self.legacy_physics_binder, &self.graph, None);
589
590        self.convert_legacy_physics();
591
592        // Re-apply lightmap if any. This has to be done after resolve because we must patch surface
593        // data at this stage, but if we'd do this before we wouldn't be able to do this because
594        // meshes contains invalid surface data.
595        if let Some(lightmap) = self.lightmap.as_mut() {
596            // Patch surface data first. To do this we gather all surface data instances and
597            // look in patch data if we have patch for data.
598            let mut unique_data_set = FxHashMap::default();
599            for &handle in lightmap.map.keys() {
600                if let Node::Mesh(mesh) = &mut self.graph[handle] {
601                    for surface in mesh.surfaces() {
602                        let data = surface.data();
603                        let key = &*data as *const _ as u64;
604                        unique_data_set.entry(key).or_insert(data);
605                    }
606                }
607            }
608
609            for (_, data) in unique_data_set.into_iter() {
610                let mut data = data.lock();
611
612                if let Some(patch) = lightmap.patches.get(&data.content_hash()) {
613                    if !data
614                        .vertex_buffer
615                        .has_attribute(VertexAttributeUsage::TexCoord1)
616                    {
617                        data.vertex_buffer
618                            .modify()
619                            .add_attribute(
620                                VertexAttributeDescriptor {
621                                    usage: VertexAttributeUsage::TexCoord1,
622                                    data_type: VertexAttributeDataType::F32,
623                                    size: 2,
624                                    divisor: 0,
625                                    shader_location: 6, // HACK: GBuffer renderer expects it to be at 6
626                                },
627                                Vector2::<f32>::default(),
628                            )
629                            .unwrap();
630                    }
631
632                    data.geometry_buffer.set_triangles(patch.triangles.clone());
633
634                    let mut vertex_buffer_mut = data.vertex_buffer.modify();
635                    for &v in patch.additional_vertices.iter() {
636                        vertex_buffer_mut.duplicate(v as usize);
637                    }
638
639                    assert_eq!(
640                        vertex_buffer_mut.vertex_count() as usize,
641                        patch.second_tex_coords.len()
642                    );
643                    for (mut view, &tex_coord) in vertex_buffer_mut
644                        .iter_mut()
645                        .zip(patch.second_tex_coords.iter())
646                    {
647                        view.write_2_f32(VertexAttributeUsage::TexCoord1, tex_coord)
648                            .unwrap();
649                    }
650                } else {
651                    Log::writeln(
652                        MessageKind::Warning,
653                        "Failed to get surface data patch while resolving lightmap!\
654                    This means that surface has changed and lightmap must be regenerated!"
655                            .to_owned(),
656                    );
657                }
658            }
659
660            // Apply textures.
661            for (&handle, entries) in lightmap.map.iter_mut() {
662                if let Node::Mesh(mesh) = &mut self.graph[handle] {
663                    for (entry, surface) in entries.iter_mut().zip(mesh.surfaces_mut()) {
664                        if let Err(e) = surface.material().lock().set_property(
665                            &ImmutableString::new("lightmapTexture"),
666                            PropertyValue::Sampler {
667                                value: entry.texture.clone(),
668                                fallback: SamplerFallback::Black,
669                            },
670                        ) {
671                            Log::writeln(
672                                MessageKind::Error,
673                                format!(
674                                    "Failed to apply light map texture to material. Reason {:?}",
675                                    e
676                                ),
677                            )
678                        }
679                    }
680                }
681            }
682        }
683
684        Log::writeln(MessageKind::Information, "Resolve succeeded!".to_owned());
685    }
686
687    /// Tries to set new lightmap to scene.
688    pub fn set_lightmap(&mut self, lightmap: Lightmap) -> Result<Option<Lightmap>, &'static str> {
689        // Assign textures to surfaces.
690        for (handle, lightmaps) in lightmap.map.iter() {
691            if let Node::Mesh(mesh) = &mut self.graph[*handle] {
692                if mesh.surfaces().len() != lightmaps.len() {
693                    return Err("failed to set lightmap, surface count mismatch");
694                }
695
696                for (surface, entry) in mesh.surfaces_mut().iter_mut().zip(lightmaps) {
697                    // This unwrap() call must never panic in normal conditions, because texture wrapped in Option
698                    // only to implement Default trait to be serializable.
699                    let texture = entry.texture.clone().unwrap();
700                    if let Err(e) = surface.material().lock().set_property(
701                        &ImmutableString::new("lightmapTexture"),
702                        PropertyValue::Sampler {
703                            value: Some(texture),
704                            fallback: SamplerFallback::Black,
705                        },
706                    ) {
707                        Log::writeln(
708                            MessageKind::Error,
709                            format!(
710                                "Failed to apply light map texture to material. Reason {:?}",
711                                e
712                            ),
713                        )
714                    }
715                }
716            }
717        }
718        Ok(std::mem::replace(&mut self.lightmap, Some(lightmap)))
719    }
720
721    /// Performs single update tick with given delta time from last frame. Internally
722    /// it updates physics, animations, and each graph node. In most cases there is
723    /// no need to call it directly, engine automatically updates all available scenes.
724    pub fn update(&mut self, frame_size: Vector2<f32>, dt: f32) {
725        let last = instant::Instant::now();
726        self.animations.update_animations(dt);
727        self.performance_statistics.animations_update_time =
728            (instant::Instant::now() - last).as_secs_f32();
729
730        let last = instant::Instant::now();
731        self.graph.update(frame_size, dt);
732        self.performance_statistics.physics = self.graph.physics.performance_statistics.clone();
733        self.performance_statistics.physics2d = self.graph.physics2d.performance_statistics.clone();
734        self.performance_statistics.graph_update_time =
735            (instant::Instant::now() - last).as_secs_f32();
736
737        self.performance_statistics.sound_update_time = self
738            .sound_context
739            .state()
740            .full_render_duration()
741            .as_secs_f32();
742    }
743
744    /// Creates deep copy of a scene, filter predicate allows you to filter out nodes
745    /// by your criteria.
746    pub fn clone<F>(&self, filter: &mut F) -> (Self, FxHashMap<Handle<Node>, Handle<Node>>)
747    where
748        F: FnMut(Handle<Node>, &Node) -> bool,
749    {
750        let (graph, old_new_map) = self.graph.clone(filter);
751        let mut animations = self.animations.clone();
752        for animation in animations.iter_mut() {
753            // Remove all tracks for nodes that were filtered out.
754            animation.retain_tracks(|track| old_new_map.contains_key(&track.get_node()));
755            // Remap track nodes.
756            for track in animation.get_tracks_mut() {
757                track.set_node(old_new_map[&track.get_node()]);
758            }
759        }
760        (
761            Self {
762                graph,
763                animations,
764                legacy_physics: Default::default(),
765                legacy_physics_binder: Default::default(),
766                // Render target is intentionally not copied, because it does not makes sense - a copy
767                // will redraw frame completely.
768                render_target: Default::default(),
769                lightmap: self.lightmap.clone(),
770                drawing_context: self.drawing_context.clone(),
771                sound_context: self.sound_context.deep_clone(),
772                navmeshes: self.navmeshes.clone(),
773                performance_statistics: Default::default(),
774                ambient_lighting_color: self.ambient_lighting_color,
775                enabled: self.enabled,
776            },
777            old_new_map,
778        )
779    }
780}
781
782impl Visit for Scene {
783    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
784        visitor.enter_region(name)?;
785        self.graph.visit("Graph", visitor)?;
786        self.animations.visit("Animations", visitor)?;
787        self.lightmap.visit("Lightmap", visitor)?;
788        self.sound_context.visit("SoundContext", visitor)?;
789        self.navmeshes.visit("NavMeshes", visitor)?;
790        self.ambient_lighting_color
791            .visit("AmbientLightingColor", visitor)?;
792        self.enabled.visit("Enabled", visitor)?;
793        // Load legacy stuff for backward compatibility.
794        if visitor.is_reading() {
795            let _ = self.legacy_physics.visit("Physics", visitor);
796            let _ = self.legacy_physics_binder.visit("PhysicsBinder", visitor);
797        }
798        visitor.leave_region()
799    }
800}
801
802/// Container for scenes in the engine.
803#[derive(Default)]
804pub struct SceneContainer {
805    pool: Pool<Scene>,
806    sound_engine: Arc<Mutex<SoundEngine>>,
807}
808
809impl SceneContainer {
810    pub(in crate) fn new(sound_engine: Arc<Mutex<SoundEngine>>) -> Self {
811        Self {
812            pool: Pool::new(),
813            sound_engine,
814        }
815    }
816
817    /// Return true if given handle is valid and "points" to "alive" scene.
818    pub fn is_valid_handle(&self, handle: Handle<Scene>) -> bool {
819        self.pool.is_valid_handle(handle)
820    }
821
822    /// Returns pair iterator which yields (handle, scene_ref) pairs.
823    pub fn pair_iter(&self) -> impl Iterator<Item = (Handle<Scene>, &Scene)> {
824        self.pool.pair_iter()
825    }
826
827    /// Creates new iterator over scenes in container.
828    #[inline]
829    pub fn iter(&self) -> PoolIterator<Scene> {
830        self.pool.iter()
831    }
832
833    /// Creates new mutable iterator over scenes in container.
834    #[inline]
835    pub fn iter_mut(&mut self) -> PoolIteratorMut<Scene> {
836        self.pool.iter_mut()
837    }
838
839    /// Adds new scene into container.
840    #[inline]
841    pub fn add(&mut self, scene: Scene) -> Handle<Scene> {
842        self.sound_engine
843            .lock()
844            .unwrap()
845            .add_context(scene.sound_context.clone());
846        self.pool.spawn(scene)
847    }
848
849    /// Removes all scenes from container.
850    #[inline]
851    pub fn clear(&mut self) {
852        self.pool.clear()
853    }
854
855    /// Removes given scene from container.
856    #[inline]
857    pub fn remove(&mut self, handle: Handle<Scene>) {
858        self.sound_engine
859            .lock()
860            .unwrap()
861            .remove_context(self.pool[handle].sound_context.clone());
862        self.pool.free(handle);
863    }
864
865    /// Takes scene from the container and transfers ownership to caller. You must either
866    /// put scene back using ticket or call `forget_ticket` to make memory used by scene
867    /// vacant again.
868    pub fn take_reserve(&mut self, handle: Handle<Scene>) -> (Ticket<Scene>, Scene) {
869        self.pool.take_reserve(handle)
870    }
871
872    /// Puts scene back using its ticket.
873    pub fn put_back(&mut self, ticket: Ticket<Scene>, scene: Scene) -> Handle<Scene> {
874        self.pool.put_back(ticket, scene)
875    }
876
877    /// Forgets ticket of a scene, making place at which ticket points, vacant again.
878    pub fn forget_ticket(&mut self, ticket: Ticket<Scene>) {
879        self.pool.forget_ticket(ticket)
880    }
881}
882
883impl Index<Handle<Scene>> for SceneContainer {
884    type Output = Scene;
885
886    #[inline]
887    fn index(&self, index: Handle<Scene>) -> &Self::Output {
888        &self.pool[index]
889    }
890}
891
892impl IndexMut<Handle<Scene>> for SceneContainer {
893    #[inline]
894    fn index_mut(&mut self, index: Handle<Scene>) -> &mut Self::Output {
895        &mut self.pool[index]
896    }
897}
898
899impl Visit for SceneContainer {
900    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
901        visitor.enter_region(name)?;
902
903        self.pool.visit("Pool", visitor)?;
904        self.sound_engine.visit("SoundEngine", visitor)?;
905
906        visitor.leave_region()
907    }
908}