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::{
55 self, io::ResourceIo, manager::ResourceManager, registry::ResourceRegistryStatus,
56 untyped::UntypedResource,
57 },
58 core::{
59 algebra::Vector2,
60 color::Color,
61 futures::future::join_all,
62 log::{Log, MessageKind},
63 pool::{Handle, Pool, Ticket},
64 reflect::prelude::*,
65 type_traits::prelude::*,
66 variable::InheritableVariable,
67 visitor::{error::VisitError, Visit, VisitResult, Visitor},
68 SafeLock,
69 },
70 engine::SerializationContext,
71 graph::NodeHandleMap,
72 graphics::PolygonFillMode,
73 resource::texture::TextureResource,
74 scene::{
75 base::BaseBuilder,
76 debug::SceneDrawingContext,
77 graph::{Graph, GraphPerformanceStatistics, GraphUpdateSwitches},
78 navmesh::NavigationalMeshBuilder,
79 node::Node,
80 skybox::{SkyBox, SkyBoxKind},
81 sound::SoundEngine,
82 },
83 utils::navmesh::Navmesh,
84};
85use fxhash::FxHashSet;
86use std::{
87 fmt::{Display, Formatter},
88 ops::{Index, IndexMut},
89 path::Path,
90 path::PathBuf,
91 sync::Arc,
92};
93use strum_macros::{AsRefStr, EnumString, VariantNames};
94
95#[derive(Default, Clone, Debug, Visit)]
97pub struct NavMeshContainer {
98 pool: Pool<Navmesh>,
99}
100
101impl NavMeshContainer {
102 pub fn add(&mut self, navmesh: Navmesh) -> Handle<Navmesh> {
104 self.pool.spawn(navmesh)
105 }
106
107 pub fn remove(&mut self, handle: Handle<Navmesh>) -> Navmesh {
109 self.pool.free(handle)
110 }
111
112 pub fn iter(&self) -> impl Iterator<Item = &Navmesh> {
114 self.pool.iter()
115 }
116
117 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Navmesh> {
119 self.pool.iter_mut()
120 }
121
122 pub fn handle_from_index(&self, i: u32) -> Handle<Navmesh> {
124 self.pool.handle_from_index(i)
125 }
126
127 pub fn clear(&mut self) {
129 self.pool.clear()
130 }
131
132 pub fn is_valid_handle(&self, handle: Handle<Navmesh>) -> bool {
134 self.pool.is_valid_handle(handle)
135 }
136
137 pub fn at(&self, i: u32) -> Option<&Navmesh> {
139 self.pool.at(i)
140 }
141
142 pub fn try_get(&self, handle: Handle<Navmesh>) -> Option<&Navmesh> {
144 self.pool.try_borrow(handle)
145 }
146
147 pub fn at_mut(&mut self, i: u32) -> Option<&mut Navmesh> {
149 self.pool.at_mut(i)
150 }
151
152 pub fn try_get_mut(&mut self, handle: Handle<Navmesh>) -> Option<&mut Navmesh> {
154 self.pool.try_borrow_mut(handle)
155 }
156}
157
158impl Index<Handle<Navmesh>> for NavMeshContainer {
159 type Output = Navmesh;
160
161 fn index(&self, index: Handle<Navmesh>) -> &Self::Output {
162 &self.pool[index]
163 }
164}
165
166impl IndexMut<Handle<Navmesh>> for NavMeshContainer {
167 fn index_mut(&mut self, index: Handle<Navmesh>) -> &mut Self::Output {
168 &mut self.pool[index]
169 }
170}
171
172#[derive(
175 Reflect,
176 Visit,
177 Debug,
178 Default,
179 Clone,
180 Copy,
181 PartialEq,
182 AsRefStr,
183 EnumString,
184 VariantNames,
185 TypeUuidProvider,
186)]
187#[type_uuid(id = "28f22fe7-22ed-47e1-ae43-779866a46cdf")]
188pub enum EnvironmentLightingSource {
189 #[default]
191 SkyBox,
192 AmbientColor,
194}
195
196#[derive(Debug, Visit, Reflect, PartialEq)]
198pub struct SceneRenderingOptions {
199 pub render_target: Option<TextureResource>,
205
206 pub clear_color: Option<Color>,
209
210 pub polygon_rasterization_mode: PolygonFillMode,
213
214 pub ambient_lighting_color: Color,
217
218 pub environment_lighting_source: EnvironmentLightingSource,
221}
222
223impl Default for SceneRenderingOptions {
224 fn default() -> Self {
225 Self {
226 render_target: None,
227 clear_color: None,
228 polygon_rasterization_mode: Default::default(),
229 ambient_lighting_color: Color::opaque(100, 100, 100),
230 environment_lighting_source: Default::default(),
231 }
232 }
233}
234
235impl Clone for SceneRenderingOptions {
236 fn clone(&self) -> Self {
237 Self {
238 render_target: None, clear_color: self.clear_color,
240 polygon_rasterization_mode: self.polygon_rasterization_mode,
241 ambient_lighting_color: self.ambient_lighting_color,
242 environment_lighting_source: self.environment_lighting_source,
243 }
244 }
245}
246
247#[derive(Debug, Reflect)]
249pub struct Scene {
250 pub graph: Graph,
254
255 pub rendering_options: InheritableVariable<SceneRenderingOptions>,
257
258 #[reflect(hidden)]
260 pub drawing_context: SceneDrawingContext,
261
262 #[reflect(hidden)]
264 pub performance_statistics: PerformanceStatistics,
265
266 #[reflect(setter = "set_skybox")]
267 sky_box: InheritableVariable<Option<SkyBox>>,
268
269 pub enabled: InheritableVariable<bool>,
277}
278
279impl Clone for Scene {
280 fn clone(&self) -> Self {
281 self.clone_one_to_one().0
282 }
283}
284
285impl Default for Scene {
286 fn default() -> Self {
287 Self {
288 graph: Default::default(),
289 rendering_options: Default::default(),
290 drawing_context: Default::default(),
291 performance_statistics: Default::default(),
292 enabled: true.into(),
293 sky_box: Some(SkyBoxKind::built_in_skybox().clone()).into(),
294 }
295 }
296}
297
298#[derive(Clone, Default, Debug)]
300pub struct PerformanceStatistics {
301 pub graph: GraphPerformanceStatistics,
303}
304
305impl Display for PerformanceStatistics {
306 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
307 write!(
308 f,
309 "Graph: {:?}\n\
310 \tSync Time: {:?}\n\
311 \tSound: {:?}\n\
312 \tPhysics: {:?}\n\
313 \t\tSimulation: {:?}\n\
314 \t\tRay cast: {:?}\n\
315 \tPhysics 2D: {:?}\n\
316 \t\tSimulation: {:?}\n\
317 \t\tRay cast: {:?}\n\
318 \tHierarchy: {:?}",
319 self.graph.total(),
320 self.graph.sync_time,
321 self.graph.sound_update_time,
322 self.graph.physics.total(),
323 self.graph.physics.step_time,
324 self.graph.physics.total_ray_cast_time.get(),
325 self.graph.physics2d.total(),
326 self.graph.physics2d.step_time,
327 self.graph.physics2d.total_ray_cast_time.get(),
328 self.graph.hierarchical_properties_time,
329 )
330 }
331}
332
333pub struct SceneLoader {
335 scene: Scene,
336 path: Option<PathBuf>,
337 resource_manager: ResourceManager,
338}
339
340impl SceneLoader {
341 pub async fn from_file<P: AsRef<Path>>(
344 path: P,
345 io: &dyn ResourceIo,
346 serialization_context: Arc<SerializationContext>,
347 resource_manager: ResourceManager,
348 ) -> Result<(Self, Vec<u8>), VisitError> {
349 let registry_status = resource_manager
350 .state()
351 .resource_registry
352 .safe_lock()
353 .status_flag();
354 let registry_status = registry_status.await;
356 if registry_status == ResourceRegistryStatus::Unknown {
357 return Err(VisitError::User(format!(
358 "Unable to load a scene from {} path, because the \
359 resource registry isn't loaded!",
360 path.as_ref().display()
361 )));
362 }
363
364 let data = io.load_file(path.as_ref()).await?;
365 let mut visitor = Visitor::load_from_memory(&data)?;
366 let loader = Self::load(
367 "Scene",
368 serialization_context,
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 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
392 .blackboard
393 .register(Arc::new(resource_manager.clone()));
394
395 let mut scene = Scene::default();
396 scene.visit(region_name, visitor)?;
397
398 Ok(Self {
399 scene,
400 path,
401 resource_manager,
402 })
403 }
404
405 pub async fn finish(self) -> Scene {
407 let mut scene = self.scene;
408
409 Log::info("SceneLoader::finish() - Collecting resources used by the scene...");
410
411 let mut used_resources = scene.collect_used_resources();
412
413 if let Some(path) = self.path {
415 let exclusion_list = used_resources
416 .iter()
417 .filter(|res| {
418 let uuid = res.resource_uuid();
419 let state = self.resource_manager.state();
420 let registry = state.resource_registry.safe_lock();
421 registry.uuid_to_path(uuid) == Some(&path)
422 })
423 .cloned()
424 .collect::<Vec<_>>();
425
426 for excluded_resource in exclusion_list {
427 assert!(used_resources.remove(&excluded_resource));
428 }
429 }
430
431 let used_resources_count = used_resources.len();
432
433 Log::info(format!(
434 "SceneLoader::finish() - {used_resources_count} resources collected. Waiting them to load..."
435 ));
436
437 let results = join_all(used_resources.into_iter()).await;
439
440 for result in results {
441 if let Err(err) = result {
442 Log::err(format!("Scene resource loading error: {:?}", err));
443 }
444 }
445
446 Log::info(format!(
447 "SceneLoader::finish() - All {used_resources_count} resources have finished loading."
448 ));
449
450 let mut skybox_textures = Vec::new();
453 if let Some(skybox) = scene.skybox_ref() {
454 skybox_textures.extend(skybox.textures().iter().filter_map(|t| t.clone()));
455 }
456 join_all(skybox_textures).await;
457
458 scene.resolve();
460
461 scene
462 }
463}
464
465impl Scene {
466 #[inline]
473 pub fn new() -> Self {
474 Self {
475 graph: Graph::new(),
477 rendering_options: Default::default(),
478 drawing_context: Default::default(),
479 performance_statistics: Default::default(),
480 enabled: true.into(),
481 sky_box: Some(SkyBoxKind::built_in_skybox().clone()).into(),
482 }
483 }
484
485 pub fn set_skybox(&mut self, skybox: Option<SkyBox>) -> Option<SkyBox> {
487 self.sky_box.set_value_and_mark_modified(skybox)
488 }
489
490 pub fn skybox_mut(&mut self) -> Option<&mut SkyBox> {
492 self.sky_box.get_value_mut_and_mark_modified().as_mut()
493 }
494
495 pub fn skybox_ref(&self) -> Option<&SkyBox> {
497 self.sky_box.as_ref()
498 }
499
500 pub fn replace_skybox(&mut self, new: Option<SkyBox>) -> Option<SkyBox> {
502 std::mem::replace(self.sky_box.get_value_mut_and_mark_modified(), new)
503 }
504
505 pub fn resolve(&mut self) {
507 Log::writeln(MessageKind::Information, "Starting resolve...");
508
509 if let Some(skybox) = self.skybox_mut() {
511 Log::verify(skybox.create_cubemap());
512 }
513
514 self.graph.resolve();
515
516 Log::writeln(MessageKind::Information, "Resolve succeeded!");
517 }
518
519 pub fn collect_used_resources(&self) -> FxHashSet<UntypedResource> {
522 let mut collection = FxHashSet::default();
523 asset::collect_used_resources(self, &mut collection);
524 collection
525 }
526
527 pub fn update(&mut self, frame_size: Vector2<f32>, dt: f32, switches: GraphUpdateSwitches) {
531 self.graph.update(frame_size, dt, switches);
532 self.performance_statistics.graph = self.graph.performance_statistics.clone();
533 }
534
535 pub fn clone_ex<F, Pre, Post>(
538 &self,
539 root: Handle<Node>,
540 filter: &mut F,
541 pre_process_callback: &mut Pre,
542 post_process_callback: &mut Post,
543 ) -> (Self, NodeHandleMap<Node>)
544 where
545 F: FnMut(Handle<Node>, &Node) -> bool,
546 Pre: FnMut(Handle<Node>, &mut Node),
547 Post: FnMut(Handle<Node>, Handle<Node>, &mut Node),
548 {
549 let (graph, old_new_map) =
550 self.graph
551 .clone_ex(root, filter, pre_process_callback, post_process_callback);
552
553 (
554 Self {
555 graph,
556 rendering_options: self.rendering_options.clone(),
557 drawing_context: self.drawing_context.clone(),
558 performance_statistics: Default::default(),
559 enabled: self.enabled.clone(),
560 sky_box: self.sky_box.clone(),
561 },
562 old_new_map,
563 )
564 }
565
566 pub fn clone_one_to_one(&self) -> (Self, NodeHandleMap<Node>) {
568 self.clone_ex(
569 self.graph.get_root(),
570 &mut |_, _| true,
571 &mut |_, _| {},
572 &mut |_, _, _| {},
573 )
574 }
575
576 fn visit(&mut self, region_name: &str, visitor: &mut Visitor) -> VisitResult {
577 let mut region = visitor.enter_region(region_name)?;
578
579 self.graph.visit("Graph", &mut region)?;
580 self.enabled.visit("Enabled", &mut region)?;
581 let _ = self
582 .rendering_options
583 .visit("RenderingOptions", &mut region);
584 let _ = self.sky_box.visit("SkyBox", &mut region);
585
586 if region.is_reading() {
588 let mut navmeshes = NavMeshContainer::default();
589 if navmeshes.visit("NavMeshes", &mut region).is_ok() {
590 for (i, navmesh) in navmeshes.iter().enumerate() {
591 NavigationalMeshBuilder::new(
592 BaseBuilder::new().with_name(format!("Navmesh{i}")),
593 )
594 .with_navmesh(navmesh.clone())
595 .build(&mut self.graph);
596 }
597 }
598 }
599
600 Ok(())
601 }
602
603 pub fn save(&mut self, region_name: &str, visitor: &mut Visitor) -> VisitResult {
641 if visitor.is_reading() {
642 return Err(VisitError::User(
643 "Visitor must be in write mode!".to_string(),
644 ));
645 }
646
647 self.visit(region_name, visitor)
648 }
649}
650
651pub struct SceneContainer {
653 pool: Pool<Scene>,
654 sound_engine: SoundEngine,
655 pub(crate) destruction_list: Vec<(Handle<Scene>, Scene)>,
656}
657
658impl SceneContainer {
659 pub(crate) fn new(sound_engine: SoundEngine) -> Self {
660 Self {
661 pool: Pool::new(),
662 sound_engine,
663 destruction_list: Default::default(),
664 }
665 }
666
667 pub fn is_valid_handle(&self, handle: Handle<Scene>) -> bool {
669 self.pool.is_valid_handle(handle)
670 }
671
672 pub fn pair_iter(&self) -> impl Iterator<Item = (Handle<Scene>, &Scene)> {
674 self.pool.pair_iter()
675 }
676
677 pub fn pair_iter_mut(&mut self) -> impl Iterator<Item = (Handle<Scene>, &mut Scene)> {
679 self.pool.pair_iter_mut()
680 }
681
682 pub fn try_get(&self, handle: Handle<Scene>) -> Option<&Scene> {
684 self.pool.try_borrow(handle)
685 }
686
687 pub fn try_get_mut(&mut self, handle: Handle<Scene>) -> Option<&mut Scene> {
689 self.pool.try_borrow_mut(handle)
690 }
691
692 #[inline]
694 pub fn iter(&self) -> impl Iterator<Item = &Scene> {
695 self.pool.iter()
696 }
697
698 #[inline]
700 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Scene> {
701 self.pool.iter_mut()
702 }
703
704 #[inline]
706 pub fn add(&mut self, scene: Scene) -> Handle<Scene> {
707 self.sound_engine
708 .state()
709 .add_context(scene.graph.sound_context.native.clone());
710 self.pool.spawn(scene)
711 }
712
713 #[inline]
715 pub fn clear(&mut self) {
716 self.pool.clear()
717 }
718
719 #[inline]
721 pub fn remove(&mut self, handle: Handle<Scene>) {
722 self.sound_engine
723 .state()
724 .remove_context(self.pool[handle].graph.sound_context.native.clone());
725 self.destruction_list.push((handle, self.pool.free(handle)));
726 }
727
728 pub fn take_reserve(&mut self, handle: Handle<Scene>) -> (Ticket<Scene>, Scene) {
732 self.pool.take_reserve(handle)
733 }
734
735 pub fn put_back(&mut self, ticket: Ticket<Scene>, scene: Scene) -> Handle<Scene> {
737 self.pool.put_back(ticket, scene)
738 }
739
740 pub fn forget_ticket(&mut self, ticket: Ticket<Scene>) {
742 self.pool.forget_ticket(ticket)
743 }
744}
745
746impl Index<Handle<Scene>> for SceneContainer {
747 type Output = Scene;
748
749 #[inline]
750 fn index(&self, index: Handle<Scene>) -> &Self::Output {
751 &self.pool[index]
752 }
753}
754
755impl IndexMut<Handle<Scene>> for SceneContainer {
756 #[inline]
757 fn index_mut(&mut self, index: Handle<Scene>) -> &mut Self::Output {
758 &mut self.pool[index]
759 }
760}