1#![warn(missing_docs)]
22
23pub 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 probe;
44pub mod ragdoll;
45pub mod rigidbody;
46pub mod skybox;
47pub mod sound;
48pub mod sprite;
49pub mod terrain;
50pub mod tilemap;
51pub mod transform;
52
53use crate::{
54 asset::{self, io::ResourceIo, manager::ResourceManager, untyped::UntypedResource},
55 core::{
56 algebra::Vector2,
57 color::Color,
58 futures::future::join_all,
59 log::{Log, MessageKind},
60 pool::{Handle, Pool, Ticket},
61 reflect::prelude::*,
62 type_traits::prelude::*,
63 variable::InheritableVariable,
64 visitor::{error::VisitError, Visit, VisitResult, Visitor},
65 SafeLock,
66 },
67 engine::SerializationContext,
68 graph::NodeHandleMap,
69 graphics::PolygonFillMode,
70 resource::texture::TextureResource,
71 scene::{
72 debug::SceneDrawingContext,
73 graph::{Graph, GraphPerformanceStatistics, GraphUpdateSwitches},
74 node::Node,
75 skybox::{SkyBox, SkyBoxKind},
76 sound::SoundEngine,
77 },
78 utils::navmesh::Navmesh,
79};
80use fxhash::FxHashSet;
81use fyrox_core::dyntype::DynTypeConstructorContainer;
82use fyrox_core::pool::PoolError;
83use std::{
84 fmt::{Display, Formatter},
85 ops::{Index, IndexMut},
86 path::Path,
87 path::PathBuf,
88 sync::Arc,
89};
90use strum_macros::{AsRefStr, EnumString, VariantNames};
91
92#[derive(Default, Clone, Debug, Visit)]
94pub struct NavMeshContainer {
95 pool: Pool<Navmesh>,
96}
97
98impl NavMeshContainer {
99 pub fn add(&mut self, navmesh: Navmesh) -> Handle<Navmesh> {
101 self.pool.spawn(navmesh)
102 }
103
104 pub fn remove(&mut self, handle: Handle<Navmesh>) -> Navmesh {
106 self.pool.free(handle)
107 }
108
109 pub fn iter(&self) -> impl Iterator<Item = &Navmesh> {
111 self.pool.iter()
112 }
113
114 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Navmesh> {
116 self.pool.iter_mut()
117 }
118
119 pub fn handle_from_index(&self, i: u32) -> Handle<Navmesh> {
121 self.pool.handle_from_index(i)
122 }
123
124 pub fn clear(&mut self) {
126 self.pool.clear()
127 }
128
129 pub fn is_valid_handle(&self, handle: Handle<Navmesh>) -> bool {
131 self.pool.is_valid_handle(handle)
132 }
133
134 pub fn at(&self, i: u32) -> Result<&Navmesh, PoolError> {
136 self.pool.at(i)
137 }
138
139 pub fn try_get(&self, handle: Handle<Navmesh>) -> Result<&Navmesh, PoolError> {
141 self.pool.try_borrow(handle)
142 }
143
144 pub fn at_mut(&mut self, i: u32) -> Result<&mut Navmesh, PoolError> {
146 self.pool.at_mut(i)
147 }
148
149 pub fn try_get_mut(&mut self, handle: Handle<Navmesh>) -> Result<&mut Navmesh, PoolError> {
151 self.pool.try_borrow_mut(handle)
152 }
153}
154
155impl Index<Handle<Navmesh>> for NavMeshContainer {
156 type Output = Navmesh;
157
158 fn index(&self, index: Handle<Navmesh>) -> &Self::Output {
159 &self.pool[index]
160 }
161}
162
163impl IndexMut<Handle<Navmesh>> for NavMeshContainer {
164 fn index_mut(&mut self, index: Handle<Navmesh>) -> &mut Self::Output {
165 &mut self.pool[index]
166 }
167}
168
169#[derive(
172 Reflect,
173 Visit,
174 Debug,
175 Default,
176 Clone,
177 Copy,
178 PartialEq,
179 AsRefStr,
180 EnumString,
181 VariantNames,
182 TypeUuidProvider,
183)]
184#[type_uuid(id = "28f22fe7-22ed-47e1-ae43-779866a46cdf")]
185pub enum EnvironmentLightingSource {
186 #[default]
188 SkyBox,
189 AmbientColor,
191}
192
193#[derive(Debug, Visit, Reflect, PartialEq)]
195pub struct SceneRenderingOptions {
196 pub render_target: Option<TextureResource>,
202
203 pub clear_color: Option<Color>,
206
207 pub polygon_rasterization_mode: PolygonFillMode,
210
211 pub ambient_lighting_color: Color,
214
215 pub environment_lighting_source: EnvironmentLightingSource,
218
219 #[visit(optional)]
222 pub environment_lighting_brightness: f32,
223}
224
225impl Default for SceneRenderingOptions {
226 fn default() -> Self {
227 Self {
228 render_target: None,
229 clear_color: None,
230 polygon_rasterization_mode: Default::default(),
231 ambient_lighting_color: Color::opaque(100, 100, 100),
232 environment_lighting_source: Default::default(),
233 environment_lighting_brightness: 1.0,
234 }
235 }
236}
237
238impl Clone for SceneRenderingOptions {
239 fn clone(&self) -> Self {
240 Self {
241 render_target: None, clear_color: self.clear_color,
243 polygon_rasterization_mode: self.polygon_rasterization_mode,
244 ambient_lighting_color: self.ambient_lighting_color,
245 environment_lighting_source: self.environment_lighting_source,
246 environment_lighting_brightness: self.environment_lighting_brightness,
247 }
248 }
249}
250
251#[derive(Debug, Reflect)]
253pub struct Scene {
254 pub graph: Graph,
258
259 pub rendering_options: InheritableVariable<SceneRenderingOptions>,
261
262 #[reflect(hidden)]
264 pub drawing_context: SceneDrawingContext,
265
266 #[reflect(hidden)]
268 pub performance_statistics: PerformanceStatistics,
269
270 #[reflect(setter = "set_skybox")]
271 sky_box: InheritableVariable<Option<SkyBox>>,
272
273 pub enabled: InheritableVariable<bool>,
281}
282
283impl Clone for Scene {
284 fn clone(&self) -> Self {
285 self.clone_one_to_one().0
286 }
287}
288
289impl Default for Scene {
290 fn default() -> Self {
291 Self {
292 graph: Default::default(),
293 rendering_options: Default::default(),
294 drawing_context: Default::default(),
295 performance_statistics: Default::default(),
296 enabled: true.into(),
297 sky_box: Some(SkyBoxKind::built_in_skybox().clone()).into(),
298 }
299 }
300}
301
302#[derive(Clone, Default, Debug)]
304pub struct PerformanceStatistics {
305 pub graph: GraphPerformanceStatistics,
307}
308
309impl Display for PerformanceStatistics {
310 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
311 write!(
312 f,
313 "Graph: {:?}\n\
314 \tSync Time: {:?}\n\
315 \tSound: {:?}\n\
316 \tPhysics: {:?}\n\
317 \t\tSimulation: {:?}\n\
318 \t\tRay cast: {:?}\n\
319 \tPhysics 2D: {:?}\n\
320 \t\tSimulation: {:?}\n\
321 \t\tRay cast: {:?}\n\
322 \tHierarchy: {:?}",
323 self.graph.total(),
324 self.graph.sync_time,
325 self.graph.sound_update_time,
326 self.graph.physics.total(),
327 self.graph.physics.step_time,
328 self.graph.physics.total_ray_cast_time.get(),
329 self.graph.physics2d.total(),
330 self.graph.physics2d.step_time,
331 self.graph.physics2d.total_ray_cast_time.get(),
332 self.graph.hierarchical_properties_time,
333 )
334 }
335}
336
337pub struct SceneLoader {
339 scene: Scene,
340 path: Option<PathBuf>,
341 resource_manager: ResourceManager,
342}
343
344impl SceneLoader {
345 pub async fn from_file<P: AsRef<Path>>(
348 path: P,
349 io: &dyn ResourceIo,
350 serialization_context: Arc<SerializationContext>,
351 dyn_type_constructors: Arc<DynTypeConstructorContainer>,
352 resource_manager: ResourceManager,
353 ) -> Result<(Self, Vec<u8>), VisitError> {
354 if !resource_manager.registry_is_loaded() {
355 return Err(VisitError::User(format!(
356 "Unable to load a scene from {} path, because the \
357 resource registry isn't loaded!",
358 path.as_ref().display()
359 )));
360 }
361
362 let data = io.load_file(path.as_ref()).await?;
363 let mut visitor = Visitor::load_from_memory(&data)?;
364 let loader = Self::load(
365 "Scene",
366 serialization_context,
367 dyn_type_constructors,
368 resource_manager,
369 &mut visitor,
370 Some(path.as_ref().to_path_buf()),
371 )?;
372 Ok((loader, data))
373 }
374
375 pub fn load(
377 region_name: &str,
378 serialization_context: Arc<SerializationContext>,
379 dyn_type_constructors: Arc<DynTypeConstructorContainer>,
380 resource_manager: ResourceManager,
381 visitor: &mut Visitor,
382 path: Option<PathBuf>,
383 ) -> Result<Self, VisitError> {
384 if !visitor.is_reading() {
385 return Err(VisitError::User(
386 "Visitor must be in read mode!".to_string(),
387 ));
388 }
389
390 visitor.blackboard.register(serialization_context);
391 visitor.blackboard.register(dyn_type_constructors);
392 visitor
393 .blackboard
394 .register(Arc::new(resource_manager.clone()));
395
396 let mut scene = Scene::default();
397 scene.visit(region_name, visitor)?;
398
399 Ok(Self {
400 scene,
401 path,
402 resource_manager,
403 })
404 }
405
406 pub async fn finish(self) -> Scene {
408 let mut scene = self.scene;
409
410 Log::info("SceneLoader::finish() - Collecting resources used by the scene...");
411
412 let mut used_resources = scene.collect_used_resources();
413
414 if let Some(path) = self.path {
416 let exclusion_list = used_resources
417 .iter()
418 .filter(|res| {
419 let uuid = res.resource_uuid();
420 let state = self.resource_manager.state();
421 let registry = state.resource_registry.safe_lock();
422 registry.uuid_to_path(uuid) == Some(&path)
423 })
424 .cloned()
425 .collect::<Vec<_>>();
426
427 for excluded_resource in exclusion_list {
428 assert!(used_resources.remove(&excluded_resource));
429 }
430 }
431
432 let used_resources_count = used_resources.len();
433
434 Log::info(format!(
435 "SceneLoader::finish() - {used_resources_count} resources collected. Waiting them to load..."
436 ));
437
438 let results = join_all(used_resources.into_iter()).await;
440
441 for result in results {
442 if let Err(err) = result {
443 Log::err(format!("Scene resource loading error: {:?}", err));
444 }
445 }
446
447 Log::info(format!(
448 "SceneLoader::finish() - All {used_resources_count} resources have finished loading."
449 ));
450
451 let mut skybox_textures = Vec::new();
454 if let Some(skybox) = scene.skybox_ref() {
455 skybox_textures.extend(skybox.textures().iter().filter_map(|t| t.clone()));
456 }
457 join_all(skybox_textures).await;
458
459 scene.resolve();
461
462 scene
463 }
464}
465
466impl Scene {
467 #[inline]
474 pub fn new() -> Self {
475 Self {
476 graph: Graph::new(),
478 rendering_options: Default::default(),
479 drawing_context: Default::default(),
480 performance_statistics: Default::default(),
481 enabled: true.into(),
482 sky_box: Some(SkyBoxKind::built_in_skybox().clone()).into(),
483 }
484 }
485
486 pub fn set_skybox(&mut self, skybox: Option<SkyBox>) -> Option<SkyBox> {
488 self.sky_box.set_value_and_mark_modified(skybox)
489 }
490
491 pub fn skybox_mut(&mut self) -> Option<&mut SkyBox> {
493 self.sky_box.get_value_mut_and_mark_modified().as_mut()
494 }
495
496 pub fn skybox_ref(&self) -> Option<&SkyBox> {
498 self.sky_box.as_ref()
499 }
500
501 pub fn replace_skybox(&mut self, new: Option<SkyBox>) -> Option<SkyBox> {
503 std::mem::replace(self.sky_box.get_value_mut_and_mark_modified(), new)
504 }
505
506 pub fn resolve(&mut self) {
508 Log::writeln(MessageKind::Information, "Starting resolve...");
509
510 if let Some(skybox) = self.skybox_mut() {
512 Log::verify(skybox.create_cubemap());
513 }
514
515 self.graph.resolve();
516
517 Log::writeln(MessageKind::Information, "Resolve succeeded!");
518 }
519
520 pub fn collect_used_resources(&self) -> FxHashSet<UntypedResource> {
523 let mut collection = FxHashSet::default();
524 asset::collect_used_resources(self, &mut collection);
525 collection
526 }
527
528 pub fn update(&mut self, frame_size: Vector2<f32>, dt: f32, switches: GraphUpdateSwitches) {
532 self.graph.update(frame_size, dt, switches);
533 self.performance_statistics.graph = self.graph.performance_statistics.clone();
534 }
535
536 pub fn clone_ex<F, Pre, Post>(
539 &self,
540 root: Handle<Node>,
541 preserve_handles: bool,
542 filter: &mut F,
543 pre_process_callback: &mut Pre,
544 post_process_callback: &mut Post,
545 ) -> (Self, NodeHandleMap<Node>)
546 where
547 F: FnMut(Handle<Node>, &Node) -> bool,
548 Pre: FnMut(Handle<Node>, &mut Node),
549 Post: FnMut(Handle<Node>, Handle<Node>, &mut Node),
550 {
551 let (graph, old_new_map) = self.graph.clone_ex(
552 root,
553 preserve_handles,
554 filter,
555 pre_process_callback,
556 post_process_callback,
557 );
558
559 (
560 Self {
561 graph,
562 rendering_options: self.rendering_options.clone(),
563 drawing_context: self.drawing_context.clone(),
564 performance_statistics: Default::default(),
565 enabled: self.enabled.clone(),
566 sky_box: self.sky_box.clone(),
567 },
568 old_new_map,
569 )
570 }
571
572 pub fn clone_one_to_one(&self) -> (Self, NodeHandleMap<Node>) {
574 self.clone_ex(
575 self.graph.get_root(),
576 true,
577 &mut |_, _| true,
578 &mut |_, _| {},
579 &mut |_, _, _| {},
580 )
581 }
582
583 fn visit(&mut self, region_name: &str, visitor: &mut Visitor) -> VisitResult {
584 let mut region = visitor.enter_region(region_name)?;
585
586 self.graph.visit("Graph", &mut region)?;
587 self.enabled.visit("Enabled", &mut region)?;
588 self.rendering_options
589 .visit("RenderingOptions", &mut region)?;
590 self.sky_box.visit("SkyBox", &mut region)?;
591
592 Ok(())
593 }
594
595 pub fn save(&mut self, region_name: &str, visitor: &mut Visitor) -> VisitResult {
633 if visitor.is_reading() {
634 return Err(VisitError::User(
635 "Visitor must be in write mode!".to_string(),
636 ));
637 }
638
639 self.visit(region_name, visitor)
640 }
641}
642
643pub struct SceneContainer {
645 pool: Pool<Scene>,
646 sound_engine: SoundEngine,
647 pub(crate) destruction_list: Vec<(Handle<Scene>, Scene)>,
648}
649
650impl SceneContainer {
651 pub(crate) fn new(sound_engine: SoundEngine) -> Self {
652 Self {
653 pool: Pool::new(),
654 sound_engine,
655 destruction_list: Default::default(),
656 }
657 }
658
659 pub fn is_valid_handle(&self, handle: Handle<Scene>) -> bool {
661 self.pool.is_valid_handle(handle)
662 }
663
664 pub fn pair_iter(&self) -> impl Iterator<Item = (Handle<Scene>, &Scene)> {
666 self.pool.pair_iter()
667 }
668
669 pub fn pair_iter_mut(&mut self) -> impl Iterator<Item = (Handle<Scene>, &mut Scene)> {
671 self.pool.pair_iter_mut()
672 }
673
674 pub fn try_get(&self, handle: Handle<Scene>) -> Result<&Scene, PoolError> {
676 self.pool.try_borrow(handle)
677 }
678
679 pub fn try_get_mut(&mut self, handle: Handle<Scene>) -> Result<&mut Scene, PoolError> {
681 self.pool.try_borrow_mut(handle)
682 }
683
684 #[inline]
686 pub fn iter(&self) -> impl Iterator<Item = &Scene> {
687 self.pool.iter()
688 }
689
690 #[inline]
692 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Scene> {
693 self.pool.iter_mut()
694 }
695
696 #[inline]
698 pub fn add(&mut self, scene: Scene) -> Handle<Scene> {
699 self.sound_engine
700 .state()
701 .add_context(scene.graph.sound_context.native.clone());
702 self.pool.spawn(scene)
703 }
704
705 #[inline]
707 pub fn clear(&mut self) {
708 self.pool.clear()
709 }
710
711 #[inline]
713 pub fn remove(&mut self, handle: Handle<Scene>) {
714 self.sound_engine
715 .state()
716 .remove_context(self.pool[handle].graph.sound_context.native.clone());
717 self.destruction_list.push((handle, self.pool.free(handle)));
718 }
719
720 pub fn take_reserve(&mut self, handle: Handle<Scene>) -> (Ticket<Scene>, Scene) {
724 self.pool.take_reserve(handle)
725 }
726
727 pub fn put_back(&mut self, ticket: Ticket<Scene>, scene: Scene) -> Handle<Scene> {
729 self.pool.put_back(ticket, scene)
730 }
731
732 pub fn forget_ticket(&mut self, ticket: Ticket<Scene>) {
734 self.pool.forget_ticket(ticket)
735 }
736}
737
738impl Index<Handle<Scene>> for SceneContainer {
739 type Output = Scene;
740
741 #[inline]
742 fn index(&self, index: Handle<Scene>) -> &Self::Output {
743 &self.pool[index]
744 }
745}
746
747impl IndexMut<Handle<Scene>> for SceneContainer {
748 #[inline]
749 fn index_mut(&mut self, index: Handle<Scene>) -> &mut Self::Output {
750 &mut self.pool[index]
751 }
752}