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().await {
356 return Err(VisitError::User(format!(
357 "Unable to load a scene from {} path, because the \
358 resource registry isn't loaded!",
359 path.as_ref().display()
360 )));
361 }
362
363 let data = io.load_file(path.as_ref()).await?;
364 let mut visitor = Visitor::load_from_memory(&data)?;
365 let loader = Self::load(
366 "Scene",
367 serialization_context,
368 dyn_type_constructors,
369 resource_manager,
370 &mut visitor,
371 Some(path.as_ref().to_path_buf()),
372 )?;
373 Ok((loader, data))
374 }
375
376 pub fn load(
378 region_name: &str,
379 serialization_context: Arc<SerializationContext>,
380 dyn_type_constructors: Arc<DynTypeConstructorContainer>,
381 resource_manager: ResourceManager,
382 visitor: &mut Visitor,
383 path: Option<PathBuf>,
384 ) -> Result<Self, VisitError> {
385 if !visitor.is_reading() {
386 return Err(VisitError::User(
387 "Visitor must be in read mode!".to_string(),
388 ));
389 }
390
391 visitor.blackboard.register(serialization_context);
392 visitor.blackboard.register(dyn_type_constructors);
393 visitor
394 .blackboard
395 .register(Arc::new(resource_manager.clone()));
396
397 let mut scene = Scene::default();
398 scene.visit(region_name, visitor)?;
399
400 Ok(Self {
401 scene,
402 path,
403 resource_manager,
404 })
405 }
406
407 pub async fn finish(self) -> Scene {
409 let mut scene = self.scene;
410
411 Log::info("SceneLoader::finish() - Collecting resources used by the scene...");
412
413 let mut used_resources = scene.collect_used_resources();
414
415 if let Some(path) = self.path {
417 let exclusion_list = used_resources
418 .iter()
419 .filter(|res| {
420 let uuid = res.resource_uuid();
421 let state = self.resource_manager.state();
422 let registry = state.resource_registry.safe_lock();
423 registry.uuid_to_path(uuid) == Some(&path)
424 })
425 .cloned()
426 .collect::<Vec<_>>();
427
428 for excluded_resource in exclusion_list {
429 assert!(used_resources.remove(&excluded_resource));
430 }
431 }
432
433 let used_resources_count = used_resources.len();
434
435 Log::info(format!(
436 "SceneLoader::finish() - {used_resources_count} resources collected. Waiting them to load..."
437 ));
438
439 let results = join_all(used_resources.into_iter()).await;
441
442 for result in results {
443 if let Err(err) = result {
444 Log::err(format!("Scene resource loading error: {:?}", err));
445 }
446 }
447
448 Log::info(format!(
449 "SceneLoader::finish() - All {used_resources_count} resources have finished loading."
450 ));
451
452 let mut skybox_textures = Vec::new();
455 if let Some(skybox) = scene.skybox_ref() {
456 skybox_textures.extend(skybox.textures().iter().filter_map(|t| t.clone()));
457 }
458 join_all(skybox_textures).await;
459
460 scene.resolve();
462
463 scene
464 }
465}
466
467impl Scene {
468 #[inline]
475 pub fn new() -> Self {
476 Self {
477 graph: Graph::new(),
479 rendering_options: Default::default(),
480 drawing_context: Default::default(),
481 performance_statistics: Default::default(),
482 enabled: true.into(),
483 sky_box: Some(SkyBoxKind::built_in_skybox().clone()).into(),
484 }
485 }
486
487 pub fn set_skybox(&mut self, skybox: Option<SkyBox>) -> Option<SkyBox> {
489 self.sky_box.set_value_and_mark_modified(skybox)
490 }
491
492 pub fn skybox_mut(&mut self) -> Option<&mut SkyBox> {
494 self.sky_box.get_value_mut_and_mark_modified().as_mut()
495 }
496
497 pub fn skybox_ref(&self) -> Option<&SkyBox> {
499 self.sky_box.as_ref()
500 }
501
502 pub fn replace_skybox(&mut self, new: Option<SkyBox>) -> Option<SkyBox> {
504 std::mem::replace(self.sky_box.get_value_mut_and_mark_modified(), new)
505 }
506
507 pub fn resolve(&mut self) {
509 Log::writeln(MessageKind::Information, "Starting resolve...");
510
511 if let Some(skybox) = self.skybox_mut() {
513 Log::verify(skybox.create_cubemap());
514 }
515
516 self.graph.resolve();
517
518 Log::writeln(MessageKind::Information, "Resolve succeeded!");
519 }
520
521 pub fn collect_used_resources(&self) -> FxHashSet<UntypedResource> {
524 let mut collection = FxHashSet::default();
525 asset::collect_used_resources(self, &mut collection);
526 collection
527 }
528
529 pub fn update(&mut self, frame_size: Vector2<f32>, dt: f32, switches: GraphUpdateSwitches) {
533 self.graph.update(frame_size, dt, switches);
534 self.performance_statistics.graph = self.graph.performance_statistics.clone();
535 }
536
537 pub fn clone_ex<F, Pre, Post>(
540 &self,
541 root: Handle<Node>,
542 preserve_handles: bool,
543 filter: &mut F,
544 pre_process_callback: &mut Pre,
545 post_process_callback: &mut Post,
546 ) -> (Self, NodeHandleMap<Node>)
547 where
548 F: FnMut(Handle<Node>, &Node) -> bool,
549 Pre: FnMut(Handle<Node>, &mut Node),
550 Post: FnMut(Handle<Node>, Handle<Node>, &mut Node),
551 {
552 let (graph, old_new_map) = self.graph.clone_ex(
553 root,
554 preserve_handles,
555 filter,
556 pre_process_callback,
557 post_process_callback,
558 );
559
560 (
561 Self {
562 graph,
563 rendering_options: self.rendering_options.clone(),
564 drawing_context: self.drawing_context.clone(),
565 performance_statistics: Default::default(),
566 enabled: self.enabled.clone(),
567 sky_box: self.sky_box.clone(),
568 },
569 old_new_map,
570 )
571 }
572
573 pub fn clone_one_to_one(&self) -> (Self, NodeHandleMap<Node>) {
575 self.clone_ex(
576 self.graph.get_root(),
577 true,
578 &mut |_, _| true,
579 &mut |_, _| {},
580 &mut |_, _, _| {},
581 )
582 }
583
584 fn visit(&mut self, region_name: &str, visitor: &mut Visitor) -> VisitResult {
585 let mut region = visitor.enter_region(region_name)?;
586
587 self.graph.visit("Graph", &mut region)?;
588 self.enabled.visit("Enabled", &mut region)?;
589 self.rendering_options
590 .visit("RenderingOptions", &mut region)?;
591 self.sky_box.visit("SkyBox", &mut region)?;
592
593 Ok(())
594 }
595
596 pub fn save(&mut self, region_name: &str, visitor: &mut Visitor) -> VisitResult {
634 if visitor.is_reading() {
635 return Err(VisitError::User(
636 "Visitor must be in write mode!".to_string(),
637 ));
638 }
639
640 self.visit(region_name, visitor)
641 }
642}
643
644pub struct SceneContainer {
646 pool: Pool<Scene>,
647 sound_engine: SoundEngine,
648 pub(crate) destruction_list: Vec<(Handle<Scene>, Scene)>,
649}
650
651impl SceneContainer {
652 pub(crate) fn new(sound_engine: SoundEngine) -> Self {
653 Self {
654 pool: Pool::new(),
655 sound_engine,
656 destruction_list: Default::default(),
657 }
658 }
659
660 pub fn is_valid_handle(&self, handle: Handle<Scene>) -> bool {
662 self.pool.is_valid_handle(handle)
663 }
664
665 pub fn pair_iter(&self) -> impl Iterator<Item = (Handle<Scene>, &Scene)> {
667 self.pool.pair_iter()
668 }
669
670 pub fn pair_iter_mut(&mut self) -> impl Iterator<Item = (Handle<Scene>, &mut Scene)> {
672 self.pool.pair_iter_mut()
673 }
674
675 pub fn try_get(&self, handle: Handle<Scene>) -> Result<&Scene, PoolError> {
677 self.pool.try_borrow(handle)
678 }
679
680 pub fn try_get_mut(&mut self, handle: Handle<Scene>) -> Result<&mut Scene, PoolError> {
682 self.pool.try_borrow_mut(handle)
683 }
684
685 #[inline]
687 pub fn iter(&self) -> impl Iterator<Item = &Scene> {
688 self.pool.iter()
689 }
690
691 #[inline]
693 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Scene> {
694 self.pool.iter_mut()
695 }
696
697 #[inline]
699 pub fn add(&mut self, scene: Scene) -> Handle<Scene> {
700 self.sound_engine
701 .state()
702 .add_context(scene.graph.sound_context.native.clone());
703 self.pool.spawn(scene)
704 }
705
706 #[inline]
708 pub fn clear(&mut self) {
709 self.pool.clear()
710 }
711
712 #[inline]
714 pub fn remove(&mut self, handle: Handle<Scene>) {
715 self.sound_engine
716 .state()
717 .remove_context(self.pool[handle].graph.sound_context.native.clone());
718 self.destruction_list.push((handle, self.pool.free(handle)));
719 }
720
721 pub fn take_reserve(&mut self, handle: Handle<Scene>) -> (Ticket<Scene>, Scene) {
725 self.pool.take_reserve(handle)
726 }
727
728 pub fn put_back(&mut self, ticket: Ticket<Scene>, scene: Scene) -> Handle<Scene> {
730 self.pool.put_back(ticket, scene)
731 }
732
733 pub fn forget_ticket(&mut self, ticket: Ticket<Scene>) {
735 self.pool.forget_ticket(ticket)
736 }
737}
738
739impl Index<Handle<Scene>> for SceneContainer {
740 type Output = Scene;
741
742 #[inline]
743 fn index(&self, index: Handle<Scene>) -> &Self::Output {
744 &self.pool[index]
745 }
746}
747
748impl IndexMut<Handle<Scene>> for SceneContainer {
749 #[inline]
750 fn index_mut(&mut self, index: Handle<Scene>) -> &mut Self::Output {
751 &mut self.pool[index]
752 }
753}