fyrox_impl/scene/
mod.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#![warn(missing_docs)]
22
23//! Contains all structures and methods to create and manage 3D scenes.
24//!
25//! A `Scene` is a container for graph nodes, animations and physics.
26
27pub mod accel;
28pub mod animation;
29pub mod base;
30pub mod camera;
31pub mod collider;
32pub mod debug;
33pub mod decal;
34pub mod dim2;
35pub mod graph;
36pub mod joint;
37pub mod light;
38pub mod mesh;
39pub mod navmesh;
40pub mod node;
41pub mod particle_system;
42pub mod pivot;
43pub mod ragdoll;
44pub mod rigidbody;
45pub mod sound;
46pub mod sprite;
47pub mod terrain;
48pub mod tilemap;
49pub mod transform;
50
51use crate::renderer::framework::PolygonFillMode;
52use crate::{
53    asset::{self, manager::ResourceManager, untyped::UntypedResource},
54    core::{
55        algebra::Vector2,
56        color::Color,
57        futures::future::join_all,
58        log::{Log, MessageKind},
59        pool::{Handle, Pool, Ticket},
60        reflect::prelude::*,
61        visitor::{Visit, VisitError, VisitResult, Visitor},
62    },
63    engine::SerializationContext,
64    graph::NodeHandleMap,
65    resource::texture::TextureResource,
66    scene::{
67        base::BaseBuilder,
68        camera::Camera,
69        debug::SceneDrawingContext,
70        graph::{Graph, GraphPerformanceStatistics, GraphUpdateSwitches},
71        navmesh::NavigationalMeshBuilder,
72        node::Node,
73        sound::SoundEngine,
74    },
75    utils::navmesh::Navmesh,
76};
77use asset::io::ResourceIo;
78use fxhash::FxHashSet;
79use fyrox_core::variable::InheritableVariable;
80use std::{
81    fmt::{Display, Formatter},
82    ops::{Index, IndexMut},
83    path::Path,
84    path::PathBuf,
85    sync::Arc,
86};
87
88/// A container for navigational meshes.
89#[derive(Default, Clone, Debug, Visit)]
90pub struct NavMeshContainer {
91    pool: Pool<Navmesh>,
92}
93
94impl NavMeshContainer {
95    /// Adds new navigational mesh to the container and returns its handle.
96    pub fn add(&mut self, navmesh: Navmesh) -> Handle<Navmesh> {
97        self.pool.spawn(navmesh)
98    }
99
100    /// Removes navigational mesh by its handle.
101    pub fn remove(&mut self, handle: Handle<Navmesh>) -> Navmesh {
102        self.pool.free(handle)
103    }
104
105    /// Creates new immutable iterator.
106    pub fn iter(&self) -> impl Iterator<Item = &Navmesh> {
107        self.pool.iter()
108    }
109
110    /// Creates new immutable iterator.
111    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Navmesh> {
112        self.pool.iter_mut()
113    }
114
115    /// Creates a handle to navmesh from its index.
116    pub fn handle_from_index(&self, i: u32) -> Handle<Navmesh> {
117        self.pool.handle_from_index(i)
118    }
119
120    /// Destroys all navmeshes. All handles will become invalid.
121    pub fn clear(&mut self) {
122        self.pool.clear()
123    }
124
125    /// Checks if given handle is valid.
126    pub fn is_valid_handle(&self, handle: Handle<Navmesh>) -> bool {
127        self.pool.is_valid_handle(handle)
128    }
129
130    /// Tries to borrow a navmesh by its index.
131    pub fn at(&self, i: u32) -> Option<&Navmesh> {
132        self.pool.at(i)
133    }
134
135    /// Tries to borrow a navmesh by its handle.
136    pub fn try_get(&self, handle: Handle<Navmesh>) -> Option<&Navmesh> {
137        self.pool.try_borrow(handle)
138    }
139
140    /// Tries to borrow a navmesh by its index.
141    pub fn at_mut(&mut self, i: u32) -> Option<&mut Navmesh> {
142        self.pool.at_mut(i)
143    }
144
145    /// Tries to borrow a navmesh by its handle.
146    pub fn try_get_mut(&mut self, handle: Handle<Navmesh>) -> Option<&mut Navmesh> {
147        self.pool.try_borrow_mut(handle)
148    }
149}
150
151impl Index<Handle<Navmesh>> for NavMeshContainer {
152    type Output = Navmesh;
153
154    fn index(&self, index: Handle<Navmesh>) -> &Self::Output {
155        &self.pool[index]
156    }
157}
158
159impl IndexMut<Handle<Navmesh>> for NavMeshContainer {
160    fn index_mut(&mut self, index: Handle<Navmesh>) -> &mut Self::Output {
161        &mut self.pool[index]
162    }
163}
164
165/// Rendering options of a scene. It allows you to specify a render target to render the scene to, change its clear color, etc.
166#[derive(Debug, Visit, Reflect, PartialEq)]
167pub struct SceneRenderingOptions {
168    /// A texture to draw the scene to. If empty, then the scene will be drawn on screen directly. It is useful to "embed" some scene into other
169    /// by drawing a quad with this texture. This can be used to make in-game video conference - you can make separate scene with
170    /// your characters and draw scene into a texture, then in the main scene you can attach this texture to some quad which will be used
171    /// as a monitor. Other usage could be a previewer of models, like a pictogram of character in real-time strategies, in other words
172    /// there are plenty of possible uses.
173    pub render_target: Option<TextureResource>,
174
175    /// Default color of the render target. Default is [`None`], which forces the renderer to use clear color of the back buffer.
176    /// Could be set to transparent to make background transparent.
177    pub clear_color: Option<Color>,
178
179    /// Defines how polygons of the scene will be rasterized. By default it set to [`PolygonFillMode::Fill`],
180    /// [`PolygonFillMode::Line`] could be used to render the scene in wireframe mode.
181    pub polygon_rasterization_mode: PolygonFillMode,
182
183    /// Color of the ambient lighting.
184    pub ambient_lighting_color: Color,
185}
186
187impl Default for SceneRenderingOptions {
188    fn default() -> Self {
189        Self {
190            render_target: None,
191            clear_color: None,
192            polygon_rasterization_mode: Default::default(),
193            ambient_lighting_color: Color::opaque(100, 100, 100),
194        }
195    }
196}
197
198impl Clone for SceneRenderingOptions {
199    fn clone(&self) -> Self {
200        Self {
201            render_target: None, // Intentionally not copied!
202            clear_color: self.clear_color,
203            polygon_rasterization_mode: self.polygon_rasterization_mode,
204            ambient_lighting_color: self.ambient_lighting_color,
205        }
206    }
207}
208
209/// See module docs.
210#[derive(Debug, Reflect)]
211pub struct Scene {
212    /// Graph is main container for all scene nodes. It calculates global transforms for nodes,
213    /// updates them and performs all other important work. See `graph` module docs for more
214    /// info.
215    pub graph: Graph,
216
217    /// Rendering options of a scene. See [`SceneRenderingOptions`] docs for more info.
218    pub rendering_options: InheritableVariable<SceneRenderingOptions>,
219
220    /// Drawing context for simple graphics.
221    #[reflect(hidden)]
222    pub drawing_context: SceneDrawingContext,
223
224    /// Performance statistics from last `update` call.
225    #[reflect(hidden)]
226    pub performance_statistics: PerformanceStatistics,
227
228    /// Whether the scene will be updated and rendered or not. Default is true.
229    /// This flag allowing you to build a scene manager for your game. For example,
230    /// you may have a scene for menu and one per level. Menu's scene is persistent,
231    /// however you don't want it to be updated and renderer while you have a level
232    /// loaded and playing a game. When you're start playing, just set `enabled` flag
233    /// to false for menu's scene and when you need to open a menu - set it to true and
234    /// set `enabled` flag to false for level's scene.
235    pub enabled: InheritableVariable<bool>,
236}
237
238impl Default for Scene {
239    fn default() -> Self {
240        Self {
241            graph: Default::default(),
242            rendering_options: Default::default(),
243            drawing_context: Default::default(),
244            performance_statistics: Default::default(),
245            enabled: true.into(),
246        }
247    }
248}
249
250/// A structure that holds times that specific update step took.
251#[derive(Clone, Default, Debug)]
252pub struct PerformanceStatistics {
253    /// Graph performance statistics.
254    pub graph: GraphPerformanceStatistics,
255}
256
257impl Display for PerformanceStatistics {
258    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
259        write!(
260            f,
261            "Graph: {:?}\n\
262            \tSync Time: {:?}\n\
263            \tSound: {:?}\n\
264            \tPhysics: {:?}\n\
265            \t\tSimulation: {:?}\n\
266            \t\tRay cast: {:?}\n\
267            \tPhysics 2D: {:?}\n\
268            \t\tSimulation: {:?}\n\
269            \t\tRay cast: {:?}\n\
270            \tHierarchy: {:?}",
271            self.graph.total(),
272            self.graph.sync_time,
273            self.graph.sound_update_time,
274            self.graph.physics.total(),
275            self.graph.physics.step_time,
276            self.graph.physics.total_ray_cast_time.get(),
277            self.graph.physics2d.total(),
278            self.graph.physics2d.step_time,
279            self.graph.physics2d.total_ray_cast_time.get(),
280            self.graph.hierarchical_properties_time,
281        )
282    }
283}
284
285/// Scene loader.
286pub struct SceneLoader {
287    scene: Scene,
288    path: Option<PathBuf>,
289}
290
291impl SceneLoader {
292    /// Tries to load scene from given file. File can contain any scene in native engine format.
293    /// Such scenes can be made in rusty editor.
294    pub async fn from_file<P: AsRef<Path>>(
295        path: P,
296        io: &dyn ResourceIo,
297        serialization_context: Arc<SerializationContext>,
298        resource_manager: ResourceManager,
299    ) -> Result<(Self, Vec<u8>), VisitError> {
300        let data = io.load_file(path.as_ref()).await?;
301        let mut visitor = Visitor::load_from_memory(&data)?;
302        let loader = Self::load(
303            "Scene",
304            serialization_context,
305            resource_manager,
306            &mut visitor,
307            Some(path.as_ref().to_path_buf()),
308        )?;
309        Ok((loader, data))
310    }
311
312    /// Tries to load a scene using specified visitor and region name.
313    pub fn load(
314        region_name: &str,
315        serialization_context: Arc<SerializationContext>,
316        resource_manager: ResourceManager,
317        visitor: &mut Visitor,
318        path: Option<PathBuf>,
319    ) -> Result<Self, VisitError> {
320        if !visitor.is_reading() {
321            return Err(VisitError::User(
322                "Visitor must be in read mode!".to_string(),
323            ));
324        }
325
326        visitor.blackboard.register(serialization_context);
327        visitor.blackboard.register(Arc::new(resource_manager));
328
329        let mut scene = Scene::default();
330        scene.visit(region_name, visitor)?;
331
332        Ok(Self { scene, path })
333    }
334
335    /// Finishes scene loading.
336    pub async fn finish(self) -> Scene {
337        let mut scene = self.scene;
338
339        Log::info("SceneLoader::finish() - Collecting resources used by the scene...");
340
341        let mut used_resources = scene.collect_used_resources();
342
343        // Do not wait for self resources.
344        if let Some(path) = self.path {
345            let exclusion_list = used_resources
346                .iter()
347                .filter(|res| res.kind().path() == Some(&path))
348                .cloned()
349                .collect::<Vec<_>>();
350
351            for excluded_resource in exclusion_list {
352                assert!(used_resources.remove(&excluded_resource));
353            }
354        }
355
356        let used_resources_count = used_resources.len();
357
358        Log::info(format!(
359            "SceneLoader::finish() - {used_resources_count} resources collected. Waiting them to load..."
360        ));
361
362        // Wait everything.
363        join_all(used_resources.into_iter()).await;
364
365        Log::info(format!(
366            "SceneLoader::finish() - All {used_resources_count} resources have finished loading."
367        ));
368
369        // TODO: Move into Camera::restore_resources?
370        // We have to wait until skybox textures are all loaded, because we need to read their data
371        // to re-create cube map.
372        let mut skybox_textures = Vec::new();
373        for node in scene.graph.linear_iter() {
374            if let Some(camera) = node.cast::<Camera>() {
375                if let Some(skybox) = camera.skybox_ref() {
376                    skybox_textures.extend(skybox.textures().iter().filter_map(|t| t.clone()));
377                }
378            }
379        }
380        join_all(skybox_textures).await;
381
382        // And do resolve to extract correct graphical data and so on.
383        scene.resolve();
384
385        scene
386    }
387}
388
389impl Scene {
390    /// Creates new scene with single root node.
391    ///
392    /// # Notes
393    ///
394    /// This method differs from Default trait implementation! Scene::default() creates
395    /// empty graph with no nodes.
396    #[inline]
397    pub fn new() -> Self {
398        Self {
399            // Graph must be created with `new` method because it differs from `default`
400            graph: Graph::new(),
401            rendering_options: Default::default(),
402            drawing_context: Default::default(),
403            performance_statistics: Default::default(),
404            enabled: true.into(),
405        }
406    }
407
408    /// Synchronizes the state of the scene with external resources.
409    pub fn resolve(&mut self) {
410        Log::writeln(MessageKind::Information, "Starting resolve...");
411
412        self.graph.resolve();
413
414        Log::writeln(MessageKind::Information, "Resolve succeeded!");
415    }
416
417    /// Collects all resources used by the scene. It uses reflection to "scan" the contents of the scene, so
418    /// if some fields marked with `#[reflect(hidden)]` attribute, then such field will be ignored!
419    pub fn collect_used_resources(&self) -> FxHashSet<UntypedResource> {
420        let mut collection = FxHashSet::default();
421        asset::collect_used_resources(self, &mut collection);
422        collection
423    }
424
425    /// Performs single update tick with given delta time from last frame. Internally
426    /// it updates physics, animations, and each graph node. In most cases there is
427    /// no need to call it directly, engine automatically updates all available scenes.
428    pub fn update(&mut self, frame_size: Vector2<f32>, dt: f32, switches: GraphUpdateSwitches) {
429        self.graph.update(frame_size, dt, switches);
430        self.performance_statistics.graph = self.graph.performance_statistics.clone();
431    }
432
433    /// Creates deep copy of a scene, filter predicate allows you to filter out nodes
434    /// by your criteria.
435    pub fn clone<F, Pre, Post>(
436        &self,
437        root: Handle<Node>,
438        filter: &mut F,
439        pre_process_callback: &mut Pre,
440        post_process_callback: &mut Post,
441    ) -> (Self, NodeHandleMap<Node>)
442    where
443        F: FnMut(Handle<Node>, &Node) -> bool,
444        Pre: FnMut(Handle<Node>, &mut Node),
445        Post: FnMut(Handle<Node>, Handle<Node>, &mut Node),
446    {
447        let (graph, old_new_map) =
448            self.graph
449                .clone(root, filter, pre_process_callback, post_process_callback);
450
451        (
452            Self {
453                graph,
454                rendering_options: self.rendering_options.clone(),
455                drawing_context: self.drawing_context.clone(),
456                performance_statistics: Default::default(),
457                enabled: self.enabled.clone(),
458            },
459            old_new_map,
460        )
461    }
462
463    /// Creates deep copy of a scene. Same as [`Self::clone`], but does 1:1 cloning.
464    pub fn clone_one_to_one(&self) -> (Self, NodeHandleMap<Node>) {
465        self.clone(
466            self.graph.get_root(),
467            &mut |_, _| true,
468            &mut |_, _| {},
469            &mut |_, _, _| {},
470        )
471    }
472
473    fn visit(&mut self, region_name: &str, visitor: &mut Visitor) -> VisitResult {
474        let mut region = visitor.enter_region(region_name)?;
475
476        self.graph.visit("Graph", &mut region)?;
477
478        self.enabled.visit("Enabled", &mut region)?;
479        let _ = self
480            .rendering_options
481            .visit("RenderingOptions", &mut region);
482
483        // Backward compatibility.
484        let mut navmeshes = NavMeshContainer::default();
485        if navmeshes.visit("NavMeshes", &mut region).is_ok() {
486            for (i, navmesh) in navmeshes.iter().enumerate() {
487                NavigationalMeshBuilder::new(BaseBuilder::new().with_name(format!("Navmesh{i}")))
488                    .with_navmesh(navmesh.clone())
489                    .build(&mut self.graph);
490            }
491        }
492
493        Ok(())
494    }
495
496    /// Tries to serialize the scene using the specified serializer. The serializer must be in write mode, otherwise
497    /// serialization will fail. The `region_name` argument must be `Scene` (scene loader expects this value, you can
498    /// use any other if you don't plan to load scenes using the standard mechanism).. Keep in mind, that this method
499    /// does **not** write anything to a file, instead it just fills in the serializer.
500    ///
501    /// ## Example
502    ///
503    /// ```rust,no_run
504    /// # use fyrox_impl::{
505    /// #     core::visitor::Visitor,
506    /// #     scene::{
507    /// #         base::BaseBuilder,
508    /// #         mesh::{
509    /// #             surface::{SurfaceBuilder, SurfaceData, SurfaceResource},
510    /// #             MeshBuilder,
511    /// #         },
512    /// #         Scene,
513    /// #     },
514    /// # };
515    /// use fyrox_resource::untyped::ResourceKind;
516    /// #
517    /// // Create a scene.
518    /// let mut scene = Scene::new();
519    ///
520    /// MeshBuilder::new(BaseBuilder::new())
521    ///     .with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok( ResourceKind::Embedded,
522    ///         SurfaceData::make_cube(Default::default()),
523    ///     ))
524    ///     .build()])
525    ///     .build(&mut scene.graph);
526    ///
527    /// // Serialize the content.
528    /// let mut visitor = Visitor::new();
529    /// scene.save("Scene", &mut visitor).unwrap();
530    ///
531    /// // Write the data to a file.
532    /// visitor.save_binary("path/to/a/scene.rgs").unwrap();
533    /// ```
534    pub fn save(&mut self, region_name: &str, visitor: &mut Visitor) -> VisitResult {
535        if visitor.is_reading() {
536            return Err(VisitError::User(
537                "Visitor must be in write mode!".to_string(),
538            ));
539        }
540
541        self.visit(region_name, visitor)
542    }
543}
544
545/// Container for scenes in the engine.
546pub struct SceneContainer {
547    pool: Pool<Scene>,
548    sound_engine: SoundEngine,
549    pub(crate) destruction_list: Vec<(Handle<Scene>, Scene)>,
550}
551
552impl SceneContainer {
553    pub(crate) fn new(sound_engine: SoundEngine) -> Self {
554        Self {
555            pool: Pool::new(),
556            sound_engine,
557            destruction_list: Default::default(),
558        }
559    }
560
561    /// Return true if given handle is valid and "points" to "alive" scene.
562    pub fn is_valid_handle(&self, handle: Handle<Scene>) -> bool {
563        self.pool.is_valid_handle(handle)
564    }
565
566    /// Returns pair iterator which yields (handle, scene_ref) pairs.
567    pub fn pair_iter(&self) -> impl Iterator<Item = (Handle<Scene>, &Scene)> {
568        self.pool.pair_iter()
569    }
570
571    /// Returns pair iterator which yields (handle, scene_ref) pairs.
572    pub fn pair_iter_mut(&mut self) -> impl Iterator<Item = (Handle<Scene>, &mut Scene)> {
573        self.pool.pair_iter_mut()
574    }
575
576    /// Tries to borrow a scene using its handle.
577    pub fn try_get(&self, handle: Handle<Scene>) -> Option<&Scene> {
578        self.pool.try_borrow(handle)
579    }
580
581    /// Tries to borrow a scene using its handle.
582    pub fn try_get_mut(&mut self, handle: Handle<Scene>) -> Option<&mut Scene> {
583        self.pool.try_borrow_mut(handle)
584    }
585
586    /// Creates new iterator over scenes in container.
587    #[inline]
588    pub fn iter(&self) -> impl Iterator<Item = &Scene> {
589        self.pool.iter()
590    }
591
592    /// Creates new mutable iterator over scenes in container.
593    #[inline]
594    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Scene> {
595        self.pool.iter_mut()
596    }
597
598    /// Adds new scene into container.
599    #[inline]
600    pub fn add(&mut self, scene: Scene) -> Handle<Scene> {
601        self.sound_engine
602            .state()
603            .add_context(scene.graph.sound_context.native.clone());
604        self.pool.spawn(scene)
605    }
606
607    /// Removes all scenes from container.
608    #[inline]
609    pub fn clear(&mut self) {
610        self.pool.clear()
611    }
612
613    /// Removes given scene from container. The scene will be destroyed on a next update call.
614    #[inline]
615    pub fn remove(&mut self, handle: Handle<Scene>) {
616        self.sound_engine
617            .state()
618            .remove_context(self.pool[handle].graph.sound_context.native.clone());
619        self.destruction_list.push((handle, self.pool.free(handle)));
620    }
621
622    /// Takes scene from the container and transfers ownership to caller. You must either
623    /// put scene back using ticket or call `forget_ticket` to make memory used by scene
624    /// vacant again.
625    pub fn take_reserve(&mut self, handle: Handle<Scene>) -> (Ticket<Scene>, Scene) {
626        self.pool.take_reserve(handle)
627    }
628
629    /// Puts scene back using its ticket.
630    pub fn put_back(&mut self, ticket: Ticket<Scene>, scene: Scene) -> Handle<Scene> {
631        self.pool.put_back(ticket, scene)
632    }
633
634    /// Forgets ticket of a scene, making place at which ticket points, vacant again.
635    pub fn forget_ticket(&mut self, ticket: Ticket<Scene>) {
636        self.pool.forget_ticket(ticket)
637    }
638}
639
640impl Index<Handle<Scene>> for SceneContainer {
641    type Output = Scene;
642
643    #[inline]
644    fn index(&self, index: Handle<Scene>) -> &Self::Output {
645        &self.pool[index]
646    }
647}
648
649impl IndexMut<Handle<Scene>> for SceneContainer {
650    #[inline]
651    fn index_mut(&mut self, index: Handle<Scene>) -> &mut Self::Output {
652        &mut self.pool[index]
653    }
654}