1#![warn(missing_docs)]
26
27use crate::{
28 asset::{untyped::UntypedResource, Resource},
29 core::{
30 algebra::{Matrix4, Vector2},
31 math::{aabb::AxisAlignedBoundingBox, frustum::Frustum},
32 pool::Handle,
33 reflect::prelude::*,
34 uuid::Uuid,
35 uuid_provider, variable,
36 variable::mark_inheritable_properties_non_modified,
37 visitor::{Visit, VisitResult, Visitor},
38 ComponentProvider, NameProvider,
39 },
40 graph::SceneGraphNode,
41 renderer::bundle::RenderContext,
42 resource::model::{Model, ModelResource},
43 scene::{
44 self,
45 animation::{absm::AnimationBlendingStateMachine, AnimationPlayer},
46 base::Base,
47 camera::Camera,
48 debug::SceneDrawingContext,
49 decal::Decal,
50 dim2::{self, rectangle::Rectangle},
51 graph::{self, Graph, GraphUpdateSwitches, NodePool},
52 light::{directional::DirectionalLight, point::PointLight, spot::SpotLight},
53 mesh::Mesh,
54 navmesh::NavigationalMesh,
55 particle_system::ParticleSystem,
56 pivot::Pivot,
57 ragdoll::Ragdoll,
58 sound::{context::SoundContext, listener::Listener, Sound},
59 sprite::Sprite,
60 terrain::Terrain,
61 Scene,
62 },
63};
64use fyrox_core::{define_as_any_trait, pool::ObjectOrVariantHelper};
65use std::{
66 any::{Any, TypeId},
67 fmt::Debug,
68 marker::PhantomData,
69 ops::{Deref, DerefMut},
70};
71
72use super::collider::BitMask;
73
74pub mod constructor;
75pub mod container;
76
77define_as_any_trait!(NodeAsAny => BaseNodeTrait);
78
79pub trait BaseNodeTrait: NodeAsAny + Debug + Deref<Target = Base> + DerefMut + Send {
81 fn clone_box(&self) -> Node;
85}
86
87impl<T> BaseNodeTrait for T
88where
89 T: Clone + NodeTrait + 'static,
90{
91 fn clone_box(&self) -> Node {
92 Node(Box::new(self.clone()))
93 }
94}
95
96pub struct SyncContext<'a, 'b> {
98 pub nodes: &'a NodePool,
100 pub physics: &'a mut graph::physics::PhysicsWorld,
102 pub physics2d: &'a mut dim2::physics::PhysicsWorld,
104 pub sound_context: &'a mut SoundContext,
106 pub switches: Option<&'b GraphUpdateSwitches>,
108}
109
110pub struct UpdateContext<'a> {
112 pub frame_size: Vector2<f32>,
114 pub dt: f32,
116 pub nodes: &'a mut NodePool,
118 pub physics: &'a mut graph::physics::PhysicsWorld,
120 pub physics2d: &'a mut dim2::physics::PhysicsWorld,
122 pub sound_context: &'a mut SoundContext,
124}
125
126#[derive(Copy, Clone, Hash, Eq, PartialEq)]
128pub enum RdcControlFlow {
129 Continue,
131 Break,
133}
134
135pub trait NodeTrait: BaseNodeTrait + Reflect + Visit + ComponentProvider {
137 fn summary(&self) -> String {
139 use std::fmt::Write;
140 let mut result = String::new();
141 let type_name = self
142 .type_name()
143 .strip_prefix("fyrox_impl::scene::")
144 .unwrap_or(self.type_name());
145 write!(result, "{} {}<{}>", self.handle(), self.name(), type_name,).unwrap();
146 if self.children().len() == 1 {
147 result.push_str(" 1 child");
148 } else if self.children().len() > 1 {
149 write!(result, " {} children", self.children().len()).unwrap();
150 }
151 if self.script_count() > 0 {
152 write!(result, " {} scripts", self.script_count()).unwrap();
153 }
154 if self.is_resource_instance_root() {
155 result.push_str(" root");
156 }
157 let origin = self.original_handle_in_resource();
158 if origin.is_some() {
159 write!(result, " from:{}", origin).unwrap();
160 }
161 if let Some(r) = self.resource() {
162 write!(result, " {}", r.summary()).unwrap();
163 }
164 result
165 }
166
167 fn local_bounding_box(&self) -> AxisAlignedBoundingBox;
169
170 fn world_bounding_box(&self) -> AxisAlignedBoundingBox;
179
180 fn id(&self) -> Uuid;
183
184 fn on_removed_from_graph(&mut self, #[allow(unused_variables)] graph: &mut Graph) {}
187
188 fn on_unlink(&mut self, #[allow(unused_variables)] graph: &mut Graph) {}
190
191 fn sync_native(
196 &self,
197 #[allow(unused_variables)] self_handle: Handle<Node>,
198 #[allow(unused_variables)] context: &mut SyncContext,
199 ) {
200 }
201
202 fn on_global_transform_changed(
204 &self,
205 #[allow(unused_variables)] new_global_transform: &Matrix4<f32>,
206 #[allow(unused_variables)] context: &mut SyncContext,
207 ) {
208 }
209
210 fn on_local_transform_changed(&self, #[allow(unused_variables)] context: &mut SyncContext) {}
212
213 fn is_alive(&self) -> bool {
215 true
216 }
217
218 fn update(&mut self, #[allow(unused_variables)] context: &mut UpdateContext) {}
220
221 fn collect_render_data(
225 &self,
226 #[allow(unused_variables)] ctx: &mut RenderContext,
227 ) -> RdcControlFlow {
228 RdcControlFlow::Continue
229 }
230
231 #[inline]
234 fn should_be_rendered(&self, frustum: Option<&Frustum>, render_mask: BitMask) -> bool {
235 if *self.render_mask & render_mask == BitMask::none() {
236 return false;
237 }
238
239 if !self.global_visibility() {
240 return false;
241 }
242
243 if !self.is_globally_enabled() {
244 return false;
245 }
246
247 if self.frustum_culling() {
248 if let Some(frustum) = frustum {
249 if !frustum.is_intersects_aabb(&self.world_bounding_box()) {
250 return false;
251 }
252 }
253 }
254
255 true
256 }
257
258 fn debug_draw(&self, #[allow(unused_variables)] ctx: &mut SceneDrawingContext) {}
260
261 fn validate(&self, #[allow(unused_variables)] scene: &Scene) -> Result<(), String> {
265 Ok(())
266 }
267}
268
269impl<T: NodeTrait> ObjectOrVariantHelper<Node, T> for PhantomData<T> {
272 fn convert_to_dest_type_helper(node: &Node) -> Option<&T> {
273 NodeAsAny::as_any(node.0.deref()).downcast_ref()
274 }
275 fn convert_to_dest_type_helper_mut(node: &mut Node) -> Option<&mut T> {
276 NodeAsAny::as_any_mut(node.0.deref_mut()).downcast_mut()
277 }
278}
279
280#[derive(Debug)]
361pub struct Node(pub(crate) Box<dyn NodeTrait>);
362
363impl<T: NodeTrait> From<T> for Node {
364 fn from(value: T) -> Self {
365 Self(Box::new(value))
366 }
367}
368
369impl Clone for Node {
370 fn clone(&self) -> Self {
371 self.0.clone_box()
372 }
373}
374
375impl SceneGraphNode for Node {
376 type Base = Base;
377 type SceneGraph = Graph;
378 type ResourceData = Model;
379
380 fn base(&self) -> &Self::Base {
381 self.0.deref()
382 }
383
384 fn set_base(&mut self, base: Self::Base) {
385 ***self = base;
386 }
387
388 fn is_resource_instance_root(&self) -> bool {
389 self.is_resource_instance_root
390 }
391
392 fn original_handle_in_resource(&self) -> Handle<Self> {
393 self.original_handle_in_resource
394 }
395
396 fn set_original_handle_in_resource(&mut self, handle: Handle<Self>) {
397 self.original_handle_in_resource = handle;
398 }
399
400 fn resource(&self) -> Option<Resource<Self::ResourceData>> {
401 self.resource.clone()
402 }
403
404 fn self_handle(&self) -> Handle<Self> {
405 self.handle()
406 }
407
408 fn parent(&self) -> Handle<Self> {
409 self.parent
410 }
411
412 fn children(&self) -> &[Handle<Self>] {
413 &self.children
414 }
415
416 fn children_mut(&mut self) -> &mut [Handle<Self>] {
417 &mut self.children
418 }
419}
420
421impl NameProvider for Node {
422 fn name(&self) -> &str {
423 &self.0.name
424 }
425}
426
427impl ComponentProvider for Node {
428 fn query_component_ref(&self, type_id: TypeId) -> Option<&dyn Any> {
429 self.0.query_component_ref(type_id)
430 }
431
432 fn query_component_mut(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
433 self.0.query_component_mut(type_id)
434 }
435}
436
437uuid_provider!(Node = "a9bc5231-155c-4564-b0ca-f23972673925");
438
439impl Deref for Node {
440 type Target = dyn NodeTrait;
441
442 fn deref(&self) -> &Self::Target {
443 self.0.deref()
444 }
445}
446
447impl DerefMut for Node {
448 fn deref_mut(&mut self) -> &mut Self::Target {
449 self.0.deref_mut()
450 }
451}
452
453#[macro_export]
455macro_rules! define_is_as {
456 ($typ:ty => fn $is:ident, fn $as_ref:ident, fn $as_mut:ident) => {
457 #[inline]
459 pub fn $is(&self) -> bool {
460 self.cast::<$typ>().is_some()
461 }
462
463 #[inline]
466 pub fn $as_ref(&self) -> &$typ {
467 self.cast::<$typ>()
468 .unwrap_or_else(|| panic!("Cast to {} failed!", stringify!($kind)))
469 }
470
471 #[inline]
474 pub fn $as_mut(&mut self) -> &mut $typ {
475 self.cast_mut::<$typ>()
476 .unwrap_or_else(|| panic!("Cast to {} failed!", stringify!($kind)))
477 }
478 };
479}
480
481impl Node {
482 #[inline]
484 pub fn new<T: NodeTrait>(node: T) -> Self {
485 Self(Box::new(node))
486 }
487
488 #[inline]
501 pub fn cast<T: NodeTrait>(&self) -> Option<&T> {
502 NodeAsAny::as_any(self.0.deref()).downcast_ref::<T>()
503 }
504
505 #[inline]
518 pub fn cast_mut<T: NodeTrait>(&mut self) -> Option<&mut T> {
519 NodeAsAny::as_any_mut(self.0.deref_mut()).downcast_mut::<T>()
520 }
521
522 pub(crate) fn mark_inheritable_variables_as_modified(&mut self) {
523 variable::mark_inheritable_properties_modified(self, &[TypeId::of::<UntypedResource>()])
524 }
525
526 pub(crate) fn set_inheritance_data(
527 &mut self,
528 original_handle: Handle<Node>,
529 model: ModelResource,
530 ) {
531 self.resource = Some(model.clone());
533
534 self.is_resource_instance_root = false;
537
538 self.as_reflect_mut(&mut |reflect| {
541 mark_inheritable_properties_non_modified(reflect, &[TypeId::of::<UntypedResource>()])
542 });
543
544 self.original_handle_in_resource = original_handle;
546 }
547
548 define_is_as!(Mesh => fn is_mesh, fn as_mesh, fn as_mesh_mut);
549 define_is_as!(Pivot => fn is_pivot, fn as_pivot, fn as_pivot_mut);
550 define_is_as!(Camera => fn is_camera, fn as_camera, fn as_camera_mut);
551 define_is_as!(SpotLight => fn is_spot_light, fn as_spot_light, fn as_spot_light_mut);
552 define_is_as!(PointLight => fn is_point_light, fn as_point_light, fn as_point_light_mut);
553 define_is_as!(DirectionalLight => fn is_directional_light, fn as_directional_light, fn as_directional_light_mut);
554 define_is_as!(ParticleSystem => fn is_particle_system, fn as_particle_system, fn as_particle_system_mut);
555 define_is_as!(Sprite => fn is_sprite, fn as_sprite, fn as_sprite_mut);
556 define_is_as!(Terrain => fn is_terrain, fn as_terrain, fn as_terrain_mut);
557 define_is_as!(Decal => fn is_decal, fn as_decal, fn as_decal_mut);
558 define_is_as!(Rectangle => fn is_rectangle, fn as_rectangle, fn as_rectangle_mut);
559 define_is_as!(scene::rigidbody::RigidBody => fn is_rigid_body, fn as_rigid_body, fn as_rigid_body_mut);
560 define_is_as!(scene::collider::Collider => fn is_collider, fn as_collider, fn as_collider_mut);
561 define_is_as!(scene::joint::Joint => fn is_joint, fn as_joint, fn as_joint_mut);
562 define_is_as!(dim2::rigidbody::RigidBody => fn is_rigid_body2d, fn as_rigid_body2d, fn as_rigid_body2d_mut);
563 define_is_as!(dim2::collider::Collider => fn is_collider2d, fn as_collider2d, fn as_collider2d_mut);
564 define_is_as!(dim2::joint::Joint => fn is_joint2d, fn as_joint2d, fn as_joint2d_mut);
565 define_is_as!(Sound => fn is_sound, fn as_sound, fn as_sound_mut);
566 define_is_as!(Listener => fn is_listener, fn as_listener, fn as_listener_mut);
567 define_is_as!(NavigationalMesh => fn is_navigational_mesh, fn as_navigational_mesh, fn as_navigational_mesh_mut);
568 define_is_as!(AnimationBlendingStateMachine => fn is_absm, fn as_absm, fn as_absm_mut);
569 define_is_as!(AnimationPlayer => fn is_animation_player, fn as_animation_player, fn as_animation_player_mut);
570 define_is_as!(Ragdoll => fn is_ragdoll, fn as_ragdoll, fn as_ragdoll_mut);
571}
572
573impl Visit for Node {
574 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
575 self.0.visit(name, visitor)
576 }
577}
578
579impl Reflect for Node {
580 fn source_path() -> &'static str {
581 file!()
582 }
583
584 fn derived_types() -> &'static [TypeId] {
585 &[]
586 }
587
588 fn query_derived_types(&self) -> &'static [TypeId] {
589 Self::derived_types()
590 }
591
592 fn type_name(&self) -> &'static str {
593 self.0.deref().type_name()
594 }
595
596 fn doc(&self) -> &'static str {
597 self.0.deref().doc()
598 }
599
600 fn assembly_name(&self) -> &'static str {
601 self.0.deref().assembly_name()
602 }
603
604 fn type_assembly_name() -> &'static str {
605 env!("CARGO_PKG_NAME")
606 }
607
608 fn fields_ref(&self, func: &mut dyn FnMut(&[FieldRef])) {
609 self.0.deref().fields_ref(func)
610 }
611
612 fn fields_mut(&mut self, func: &mut dyn FnMut(&mut [FieldMut])) {
613 self.0.deref_mut().fields_mut(func)
614 }
615
616 fn into_any(self: Box<Self>) -> Box<dyn Any> {
617 Reflect::into_any(self.0)
618 }
619
620 fn as_any(&self, func: &mut dyn FnMut(&dyn Any)) {
621 Reflect::as_any(self.0.deref(), func)
622 }
623
624 fn as_any_mut(&mut self, func: &mut dyn FnMut(&mut dyn Any)) {
625 Reflect::as_any_mut(self.0.deref_mut(), func)
626 }
627
628 fn as_reflect(&self, func: &mut dyn FnMut(&dyn Reflect)) {
629 self.0.deref().as_reflect(func)
630 }
631
632 fn as_reflect_mut(&mut self, func: &mut dyn FnMut(&mut dyn Reflect)) {
633 self.0.deref_mut().as_reflect_mut(func)
634 }
635
636 fn set(&mut self, value: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
637 self.0.deref_mut().set(value)
638 }
639
640 fn set_field(
641 &mut self,
642 field: &str,
643 value: Box<dyn Reflect>,
644 func: &mut dyn FnMut(Result<Box<dyn Reflect>, SetFieldError>),
645 ) {
646 self.0.deref_mut().set_field(field, value, func)
647 }
648
649 fn field(&self, name: &str, func: &mut dyn FnMut(Option<&dyn Reflect>)) {
650 self.0.deref().field(name, func)
651 }
652
653 fn field_mut(&mut self, name: &str, func: &mut dyn FnMut(Option<&mut dyn Reflect>)) {
654 self.0.deref_mut().field_mut(name, func)
655 }
656
657 fn try_clone_box(&self) -> Option<Box<dyn Reflect>> {
658 Some(Box::new(self.clone()))
659 }
660}
661
662#[cfg(test)]
663mod test {
664 use crate::{
665 asset::manager::ResourceManager,
666 core::{
667 algebra::{Matrix4, Vector3},
668 futures::executor::block_on,
669 impl_component_provider,
670 reflect::prelude::*,
671 uuid::{uuid, Uuid},
672 variable::InheritableVariable,
673 visitor::{prelude::*, Visitor},
674 SafeLock, TypeUuidProvider,
675 },
676 engine::{self, SerializationContext},
677 resource::model::{Model, ModelResourceExtension},
678 scene::{
679 base::BaseBuilder,
680 mesh::{
681 surface::{SurfaceBuilder, SurfaceData, SurfaceResource},
682 MeshBuilder,
683 },
684 pivot::PivotBuilder,
685 transform::TransformBuilder,
686 Scene,
687 },
688 script::ScriptTrait,
689 };
690 use fyrox_graph::SceneGraph;
691 use fyrox_resource::io::FsResourceIo;
692 use fyrox_resource::untyped::ResourceKind;
693 use std::{fs, path::Path, sync::Arc};
694
695 #[derive(Debug, Clone, Reflect, Visit, Default)]
696 struct MyScript {
697 some_field: InheritableVariable<String>,
698 some_collection: InheritableVariable<Vec<u32>>,
699 }
700
701 impl_component_provider!(MyScript);
702
703 impl TypeUuidProvider for MyScript {
704 fn type_uuid() -> Uuid {
705 uuid!("d3f66902-803f-4ace-8170-0aa485d98b40")
706 }
707 }
708
709 impl ScriptTrait for MyScript {}
710
711 fn create_scene() -> Scene {
712 let mut scene = Scene::new();
713
714 let mesh;
715 PivotBuilder::new(
716 BaseBuilder::new()
717 .with_name("Pivot")
718 .with_script(MyScript {
719 some_field: "Foobar".to_string().into(),
720 some_collection: vec![1, 2, 3].into(),
721 })
722 .with_children(&[{
723 mesh = MeshBuilder::new(
724 BaseBuilder::new().with_name("Mesh").with_local_transform(
725 TransformBuilder::new()
726 .with_local_position(Vector3::new(3.0, 2.0, 1.0))
727 .build(),
728 ),
729 )
730 .with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok(
731 Uuid::new_v4(),
732 ResourceKind::Embedded,
733 SurfaceData::make_cone(16, 1.0, 1.0, &Matrix4::identity()),
734 ))
735 .build()])
736 .build(&mut scene.graph);
737 mesh
738 }]),
739 )
740 .build(&mut scene.graph);
741
742 let mesh = scene.graph[mesh].as_mesh();
743 assert_eq!(mesh.surfaces().len(), 1);
744 assert!(mesh.surfaces()[0].bones.is_modified());
745 assert!(mesh.surfaces()[0].data.is_modified());
746 assert!(mesh.surfaces()[0].material.is_modified());
747
748 scene
749 }
750
751 fn save_scene(scene: &mut Scene, path: &Path) {
752 let mut visitor = Visitor::new();
753 scene.save("Scene", &mut visitor).unwrap();
754 visitor.save_binary_to_file(path).unwrap();
755 }
756
757 #[test]
758 fn test_property_inheritance() {
759 if !Path::new("test_output").exists() {
760 fs::create_dir_all("test_output").unwrap();
761 }
762
763 let root_asset_path = Path::new("test_output/root.rgs");
764 let derived_asset_path = Path::new("test_output/derived.rgs");
765
766 {
768 let mut scene = create_scene();
769 save_scene(&mut scene, root_asset_path);
770 }
771
772 let resource_manager =
774 ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
775
776 resource_manager
777 .state()
778 .resource_registry
779 .safe_lock()
780 .set_path("test_output/resources.registry");
781
782 let serialization_context = SerializationContext::new();
783 serialization_context
784 .script_constructors
785 .add::<MyScript>("MyScript");
786
787 assert!(serialization_context
788 .script_constructors
789 .map()
790 .iter()
791 .any(|s| s.1.source_path == file!()));
792
793 engine::initialize_resource_manager_loaders(
794 &resource_manager,
795 Arc::new(serialization_context),
796 );
797
798 resource_manager.update_or_load_registry();
799
800 let root_asset = block_on(resource_manager.request::<Model>(root_asset_path)).unwrap();
801
802 {
804 let mut derived = Scene::new();
805 root_asset.instantiate(&mut derived);
806 let pivot = derived.graph.find_by_name_from_root("Pivot").unwrap().0;
807 let mesh = derived.graph.find_by_name_from_root("Mesh").unwrap().0;
808 let pivot = &mut derived.graph[pivot];
810 pivot
811 .local_transform_mut()
812 .set_position(Vector3::new(1.0, 2.0, 3.0));
813 let my_script = pivot.try_get_script_mut::<MyScript>().unwrap();
814 my_script.some_collection.push(4);
815 let mesh = derived.graph[mesh].as_mesh_mut();
816 assert_eq!(
817 **mesh.local_transform().position(),
818 Vector3::new(3.0, 2.0, 1.0)
819 );
820 assert_eq!(mesh.surfaces().len(), 1);
821 assert!(!mesh.surfaces()[0].bones.is_modified());
822 assert!(!mesh.surfaces()[0].data.is_modified());
823 assert!(!mesh.surfaces()[0].material.is_modified());
824 mesh.set_cast_shadows(false);
825 save_scene(&mut derived, derived_asset_path);
826 let registry = resource_manager.state().resource_registry.clone();
827 let mut registry = registry.safe_lock();
828 let mut ctx = registry.modify();
829 ctx.write_metadata(Uuid::new_v4(), derived_asset_path.to_path_buf())
830 .unwrap();
831 }
832
833 {
835 let derived_asset =
836 block_on(resource_manager.request::<Model>(derived_asset_path)).unwrap();
837
838 let derived_data = derived_asset.data_ref();
839 let derived_scene = derived_data.get_scene();
840
841 let pivot = derived_scene
842 .graph
843 .find_by_name_from_root("Pivot")
844 .unwrap()
845 .0;
846 let mesh = derived_scene
847 .graph
848 .find_by_name_from_root("Mesh")
849 .unwrap()
850 .0;
851 let pivot = &derived_scene.graph[pivot];
852 let my_script = pivot.try_get_script::<MyScript>().unwrap();
853 assert_eq!(
854 **pivot.local_transform().position(),
855 Vector3::new(1.0, 2.0, 3.0)
856 );
857 assert_eq!(*my_script.some_field, "Foobar");
858 assert_eq!(*my_script.some_collection, &[1, 2, 3, 4]);
859 let mesh = derived_scene.graph[mesh].as_mesh();
860 assert!(!mesh.cast_shadows());
861 assert_eq!(mesh.surfaces().len(), 1);
862 assert!(!mesh.surfaces()[0].bones.is_modified());
863 assert!(!mesh.surfaces()[0].data.is_modified());
864 assert!(!mesh.surfaces()[0].material.is_modified());
865 assert_eq!(
867 **mesh.local_transform().position(),
868 Vector3::new(3.0, 2.0, 1.0)
869 );
870 }
871 }
872}