1use crate::{
26 core::{
27 algebra::{Matrix4, Vector3},
28 log::Log,
29 math::{aabb::AxisAlignedBoundingBox, Matrix4Ext},
30 pool::{ErasedHandle, Handle},
31 reflect::prelude::*,
32 type_traits::prelude::*,
33 variable::InheritableVariable,
34 visitor::{Visit, VisitResult, Visitor},
35 ImmutableString,
36 },
37 engine::SerializationContext,
38 graph::BaseSceneGraph,
39 resource::model::ModelResource,
40 scene::{node::Node, transform::Transform},
41 script::{Script, ScriptTrait},
42};
43use fyrox_core::algebra::UnitQuaternion;
44use fyrox_core::visitor::error::VisitError;
45use serde::{Deserialize, Serialize};
46use std::{
47 any::Any,
48 cell::Cell,
49 ops::{Deref, DerefMut},
50 sync::mpsc::Sender,
51};
52use strum_macros::{AsRefStr, EnumString, VariantNames};
53
54use super::collider::BitMask;
55
56#[derive(Debug, Default, Clone, Visit, Reflect, PartialEq, TypeUuidProvider)]
62#[type_uuid(id = "576b31a2-2b39-4c79-95dd-26aeaf381d8b")]
63pub struct LevelOfDetail {
64 begin: f32,
67 end: f32,
70 pub objects: Vec<Handle<Node>>,
72}
73
74impl LevelOfDetail {
75 pub fn new(begin: f32, end: f32, objects: Vec<Handle<Node>>) -> Self {
77 for object in objects.iter() {
78 assert!(object.is_some());
80 }
81 let begin = begin.min(end);
82 let end = end.max(begin);
83 Self {
84 begin: begin.clamp(0.0, 1.0),
85 end: end.clamp(0.0, 1.0),
86 objects,
87 }
88 }
89
90 pub fn set_begin(&mut self, percent: f32) {
93 self.begin = percent.clamp(0.0, 1.0);
94 if self.begin > self.end {
95 std::mem::swap(&mut self.begin, &mut self.end);
96 }
97 }
98
99 pub fn begin(&self) -> f32 {
101 self.begin
102 }
103
104 pub fn set_end(&mut self, percent: f32) {
107 self.end = percent.clamp(0.0, 1.0);
108 if self.end < self.begin {
109 std::mem::swap(&mut self.begin, &mut self.end);
110 }
111 }
112
113 pub fn end(&self) -> f32 {
115 self.end
116 }
117}
118
119#[derive(Debug, Default, Clone, Visit, Reflect, PartialEq, TypeUuidProvider)]
130#[type_uuid(id = "8e7b18b1-c1e0-47d7-b952-4394c1d049e5")]
131pub struct LodGroup {
132 pub levels: Vec<LevelOfDetail>,
134}
135
136#[derive(
139 Default,
140 Copy,
141 Clone,
142 PartialOrd,
143 PartialEq,
144 Ord,
145 Eq,
146 Debug,
147 Visit,
148 Reflect,
149 AsRefStr,
150 EnumString,
151 VariantNames,
152 TypeUuidProvider,
153)]
154#[type_uuid(id = "57c125ff-e408-4318-9874-f59485e95764")]
155#[repr(u32)]
156pub enum Mobility {
157 #[default]
174 Static = 0,
175
176 Stationary = 1,
191
192 Dynamic = 2,
199}
200
201#[derive(
203 Debug, Visit, Reflect, PartialEq, Clone, AsRefStr, EnumString, VariantNames, TypeUuidProvider,
204)]
205#[type_uuid(id = "cce94b60-a57e-48ba-b6f4-e5e84788f7f8")]
206pub enum PropertyValue {
207 NodeHandle(Handle<Node>),
214 Handle(ErasedHandle),
221 String(String),
223 I64(i64),
225 U64(u64),
227 I32(i32),
229 U32(u32),
231 I16(i16),
233 U16(u16),
235 I8(i8),
237 U8(u8),
239 F32(f32),
241 F64(f64),
243}
244
245impl Default for PropertyValue {
246 fn default() -> Self {
247 Self::I8(0)
248 }
249}
250
251#[derive(Debug, Visit, Reflect, Default, Clone, PartialEq, TypeUuidProvider)]
253#[type_uuid(id = "fc87fd21-a5e6-40d5-a79d-19f96b25d6c9")]
254pub struct Property {
255 pub name: String,
257 pub value: PropertyValue,
259}
260
261pub enum NodeScriptMessage {
263 InitializeScript {
265 handle: Handle<Node>,
267 script_index: usize,
269 },
270 DestroyScript {
273 script: Script,
275 handle: Handle<Node>,
277 script_index: usize,
279 },
280}
281
282#[derive(
285 Clone,
286 Copy,
287 Eq,
288 Hash,
289 Ord,
290 PartialEq,
291 PartialOrd,
292 Default,
293 Debug,
294 Reflect,
295 Serialize,
296 Deserialize,
297)]
298#[repr(transparent)]
299pub struct SceneNodeId(pub Uuid);
300
301impl Visit for SceneNodeId {
302 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
303 self.0.visit(name, visitor)
304 }
305}
306
307#[derive(Clone, Reflect, Debug, Default, TypeUuidProvider)]
309#[type_uuid(id = "51bc577b-5a50-4a97-9b31-eda2f3d46c9d")]
310pub struct ScriptRecord {
311 pub(crate) script: Option<Script>,
314 #[reflect(hidden)]
315 pub(crate) should_be_deleted: bool,
316}
317
318impl ScriptRecord {
319 pub(crate) fn new(script: Script) -> Self {
320 Self {
321 script: Some(script),
322 should_be_deleted: false,
323 }
324 }
325}
326
327impl Deref for ScriptRecord {
328 type Target = Option<Script>;
329
330 fn deref(&self) -> &Self::Target {
331 &self.script
332 }
333}
334
335impl DerefMut for ScriptRecord {
336 fn deref_mut(&mut self) -> &mut Self::Target {
337 &mut self.script
338 }
339}
340
341impl Visit for ScriptRecord {
342 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
343 visit_opt_script(name, &mut self.script, visitor)
344 }
345}
346
347#[allow(clippy::enum_variant_names)] #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
349pub(crate) enum NodeMessageKind {
350 TransformChanged,
351 VisibilityChanged,
352 EnabledFlagChanged,
353}
354
355#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
356pub(crate) struct NodeMessage {
357 pub node: Handle<Node>,
358 pub kind: NodeMessageKind,
359}
360
361impl NodeMessage {
362 pub fn new(node: Handle<Node>, kind: NodeMessageKind) -> Self {
363 Self { node, kind }
364 }
365}
366
367#[derive(Clone, Debug)]
368struct TrackedProperty<T> {
369 property: T,
370 node_message_kind: NodeMessageKind,
371 node_handle: Handle<Node>,
372 sender: Option<Sender<NodeMessage>>,
373}
374
375impl<T> TrackedProperty<T> {
376 fn unbound(property: T, kind: NodeMessageKind) -> Self {
377 Self {
378 property,
379 node_message_kind: kind,
380 node_handle: Default::default(),
381 sender: None,
382 }
383 }
384
385 fn set_message_data(&mut self, sender: Sender<NodeMessage>, node_handle: Handle<Node>) {
386 self.sender = Some(sender);
387 self.node_handle = node_handle;
388 }
389}
390
391impl<T: Visit> Visit for TrackedProperty<T> {
392 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
393 self.property.visit(name, visitor)
394 }
395}
396
397impl<T> Deref for TrackedProperty<T> {
398 type Target = T;
399
400 fn deref(&self) -> &Self::Target {
401 &self.property
402 }
403}
404
405impl<T> DerefMut for TrackedProperty<T> {
406 fn deref_mut(&mut self) -> &mut Self::Target {
407 if let Some(sender) = self.sender.as_ref() {
408 Log::verify(sender.send(NodeMessage {
409 node: self.node_handle,
410 kind: self.node_message_kind,
411 }))
412 }
413 &mut self.property
414 }
415}
416
417#[derive(Debug, Reflect, Clone)]
438pub struct Base {
439 #[reflect(hidden)]
440 self_handle: Handle<Node>,
441
442 #[reflect(hidden)]
443 script_message_sender: Option<Sender<NodeScriptMessage>>,
444
445 #[reflect(hidden)]
446 message_sender: Option<Sender<NodeMessage>>,
447
448 #[reflect(setter = "set_name_internal")]
451 pub(crate) name: ImmutableString,
452
453 #[reflect(deref)]
454 local_transform: TrackedProperty<Transform>,
455
456 #[reflect(deref)]
457 visibility: TrackedProperty<InheritableVariable<bool>>,
458
459 #[reflect(deref)]
460 enabled: TrackedProperty<InheritableVariable<bool>>,
461
462 pub render_mask: InheritableVariable<BitMask>,
465
466 pub(crate) lifetime: InheritableVariable<Option<f32>>,
468
469 #[reflect(setter = "set_lod_group")]
470 lod_group: InheritableVariable<Option<LodGroup>>,
471
472 #[reflect(setter = "set_mobility")]
473 mobility: InheritableVariable<Mobility>,
474
475 #[reflect(setter = "set_tag")]
476 tag: InheritableVariable<String>,
477
478 #[reflect(setter = "set_cast_shadows")]
479 cast_shadows: InheritableVariable<bool>,
480
481 #[reflect(setter = "set_properties")]
484 pub properties: InheritableVariable<Vec<Property>>,
485
486 #[reflect(setter = "set_frustum_culling")]
487 frustum_culling: InheritableVariable<bool>,
488
489 #[reflect(read_only)]
493 pub(crate) is_resource_instance_root: bool,
494
495 #[reflect(read_only)]
496 pub(crate) global_visibility: Cell<bool>,
497
498 #[reflect(hidden)]
499 pub(crate) parent: Handle<Node>,
500
501 #[reflect(hidden)]
502 pub(crate) children: Vec<Handle<Node>>,
503
504 #[reflect(read_only)]
505 pub(crate) global_transform: Cell<Matrix4<f32>>,
506
507 #[reflect(hidden)]
509 pub(crate) inv_bind_pose_transform: Matrix4<f32>,
510
511 #[reflect(read_only)]
514 pub(crate) resource: Option<ModelResource>,
515
516 #[reflect(read_only)]
519 #[reflect(hidden)]
520 pub(crate) original_handle_in_resource: Handle<Node>,
521
522 pub(crate) instance_id: SceneNodeId,
527
528 pub(crate) scripts: Vec<ScriptRecord>,
535
536 #[reflect(read_only)]
537 pub(crate) global_enabled: Cell<bool>,
538}
539
540impl Drop for Base {
541 fn drop(&mut self) {
542 self.remove_all_scripts();
543 }
544}
545
546impl Base {
547 #[inline]
549 pub fn handle(&self) -> Handle<Node> {
550 self.self_handle
551 }
552
553 #[inline]
555 pub fn set_name<N: AsRef<str>>(&mut self, name: N) {
556 self.set_name_internal(ImmutableString::new(name));
557 }
558
559 fn set_name_internal(&mut self, name: ImmutableString) -> ImmutableString {
560 std::mem::replace(&mut self.name, name)
561 }
562
563 #[inline]
565 pub fn name(&self) -> &str {
566 self.name.as_str()
567 }
568
569 #[inline]
571 pub fn name_owned(&self) -> String {
572 self.name.to_mutable()
573 }
574
575 #[inline]
578 pub fn local_transform(&self) -> &Transform {
579 &self.local_transform
580 }
581
582 pub(crate) fn on_connected_to_graph(
583 &mut self,
584 self_handle: Handle<Node>,
585 message_sender: Sender<NodeMessage>,
586 script_message_sender: Sender<NodeScriptMessage>,
587 ) {
588 self.self_handle = self_handle;
589 self.message_sender = Some(message_sender.clone());
590 self.script_message_sender = Some(script_message_sender);
591 self.local_transform
592 .set_message_data(message_sender.clone(), self_handle);
593 self.visibility
594 .set_message_data(message_sender.clone(), self_handle);
595 self.enabled.set_message_data(message_sender, self_handle);
596 self.notify(self.self_handle, NodeMessageKind::TransformChanged);
598 self.notify(self.self_handle, NodeMessageKind::VisibilityChanged);
599 self.notify(self.self_handle, NodeMessageKind::EnabledFlagChanged);
600 }
601
602 fn notify(&self, node: Handle<Node>, kind: NodeMessageKind) {
603 let Some(sender) = self.message_sender.as_ref() else {
604 return;
605 };
606 Log::verify(sender.send(NodeMessage::new(node, kind)));
607 }
608
609 #[inline]
613 pub fn local_transform_mut(&mut self) -> &mut Transform {
614 &mut self.local_transform
615 }
616
617 #[inline]
620 pub fn set_local_transform(&mut self, transform: Transform) {
621 self.local_transform.property = transform;
622 self.notify(self.self_handle, NodeMessageKind::TransformChanged);
623 }
624
625 #[inline]
628 pub fn find_properties_ref<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a Property> {
629 self.properties.iter().filter(move |p| p.name == name)
630 }
631
632 #[inline]
634 pub fn find_first_property_ref(&self, name: &str) -> Option<&Property> {
635 self.properties.iter().find(|p| p.name == name)
636 }
637
638 #[inline]
640 pub fn set_properties(&mut self, properties: Vec<Property>) -> Vec<Property> {
641 std::mem::replace(
642 self.properties.get_value_mut_and_mark_modified(),
643 properties,
644 )
645 }
646
647 #[inline]
657 pub fn set_lifetime(&mut self, time_seconds: Option<f32>) -> &mut Self {
658 self.lifetime.set_value_and_mark_modified(time_seconds);
659 self
660 }
661
662 #[inline]
665 pub fn lifetime(&self) -> Option<f32> {
666 *self.lifetime
667 }
668
669 #[inline]
671 pub fn parent(&self) -> Handle<Node> {
672 self.parent
673 }
674
675 #[inline]
678 pub fn children(&self) -> &[Handle<Node>] {
679 self.children.as_slice()
680 }
681
682 #[inline]
686 pub fn global_transform(&self) -> Matrix4<f32> {
687 self.global_transform.get()
688 }
689
690 #[inline]
692 pub fn global_transform_without_scaling(&self) -> Matrix4<f32> {
693 const EPSILON: f32 = 10.0 * f32::EPSILON;
694 let basis = self.global_transform().basis();
695 let rotation = UnitQuaternion::from_matrix_eps(&basis, EPSILON, 16, Default::default());
696 Matrix4::new_translation(&self.global_position()) * rotation.to_homogeneous()
697 }
698
699 #[inline]
703 pub fn inv_bind_pose_transform(&self) -> Matrix4<f32> {
704 self.inv_bind_pose_transform
705 }
706
707 #[inline]
709 pub fn is_resource_instance_root(&self) -> bool {
710 self.is_resource_instance_root
711 }
712
713 #[inline]
715 pub fn resource(&self) -> Option<ModelResource> {
716 self.resource.clone()
717 }
718
719 #[inline]
721 pub fn set_visibility(&mut self, visibility: bool) -> bool {
722 self.visibility.set_value_and_mark_modified(visibility)
723 }
724
725 #[inline]
727 pub fn visibility(&self) -> bool {
728 *self.visibility.property
729 }
730
731 #[inline]
734 pub fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
735 AxisAlignedBoundingBox::unit()
736 }
737
738 #[inline]
740 pub fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
741 self.local_bounding_box()
742 .transform(&self.global_transform())
743 }
744
745 #[inline]
747 pub fn set_mobility(&mut self, mobility: Mobility) -> Mobility {
748 self.mobility.set_value_and_mark_modified(mobility)
749 }
750
751 #[inline]
753 pub fn mobility(&self) -> Mobility {
754 *self.mobility
755 }
756
757 #[inline]
762 pub fn global_visibility(&self) -> bool {
763 self.global_visibility.get()
764 }
765
766 #[inline]
773 pub fn original_handle_in_resource(&self) -> Handle<Node> {
774 self.original_handle_in_resource
775 }
776
777 #[inline]
780 pub fn has_inheritance_parent(&self) -> bool {
781 self.original_handle_in_resource.is_some() && self.resource.is_some()
782 }
783
784 #[inline]
786 pub fn global_position(&self) -> Vector3<f32> {
787 self.global_transform.get().position()
788 }
789
790 #[inline]
792 pub fn look_vector(&self) -> Vector3<f32> {
793 self.global_transform.get().look()
794 }
795
796 #[inline]
798 pub fn side_vector(&self) -> Vector3<f32> {
799 self.global_transform.get().side()
800 }
801
802 #[inline]
804 pub fn up_vector(&self) -> Vector3<f32> {
805 self.global_transform.get().up()
806 }
807
808 #[inline]
810 pub fn set_lod_group(&mut self, lod_group: Option<LodGroup>) -> Option<LodGroup> {
811 std::mem::replace(self.lod_group.get_value_mut_and_mark_modified(), lod_group)
812 }
813
814 #[inline]
816 pub fn take_lod_group(&mut self) -> Option<LodGroup> {
817 std::mem::take(self.lod_group.get_value_mut_and_mark_modified())
818 }
819
820 #[inline]
822 pub fn lod_group(&self) -> Option<&LodGroup> {
823 self.lod_group.as_ref()
824 }
825
826 #[inline]
828 pub fn lod_group_mut(&mut self) -> Option<&mut LodGroup> {
829 self.lod_group.get_value_mut_and_mark_modified().as_mut()
830 }
831
832 #[inline]
834 pub fn tag(&self) -> &str {
835 &self.tag
836 }
837
838 #[inline]
840 pub fn tag_owned(&self) -> String {
841 (*self.tag).clone()
842 }
843
844 #[inline]
846 pub fn set_tag(&mut self, tag: String) -> String {
847 self.tag.set_value_and_mark_modified(tag)
848 }
849
850 #[inline]
852 pub fn frustum_culling(&self) -> bool {
853 *self.frustum_culling
854 }
855
856 #[inline]
858 pub fn set_frustum_culling(&mut self, frustum_culling: bool) -> bool {
859 self.frustum_culling
860 .set_value_and_mark_modified(frustum_culling)
861 }
862
863 #[inline]
865 pub fn cast_shadows(&self) -> bool {
866 *self.cast_shadows
867 }
868
869 #[inline]
871 pub fn set_cast_shadows(&mut self, cast_shadows: bool) -> bool {
872 self.cast_shadows.set_value_and_mark_modified(cast_shadows)
873 }
874
875 pub fn instance_id(&self) -> SceneNodeId {
877 self.instance_id
878 }
879
880 pub fn remove_script(&mut self, index: usize) {
884 if let Some(entry) = self.scripts.get_mut(index) {
886 entry.should_be_deleted = true;
887
888 if let Some(script) = entry.take() {
891 if let Some(sender) = self.script_message_sender.as_ref() {
892 Log::verify(sender.send(NodeScriptMessage::DestroyScript {
893 script,
894 handle: self.self_handle,
895 script_index: index,
896 }));
897 } else {
898 Log::warn(format!(
899 "There is a script instance on a node {}, but no message sender. \
900 The script won't be correctly destroyed!",
901 self.name(),
902 ));
903 }
904 }
905 }
906 }
907
908 pub fn remove_all_scripts(&mut self) {
912 let script_count = self.scripts.len();
913 for i in 0..script_count {
914 self.remove_script(i);
915 }
916 }
917
918 #[inline]
921 pub fn replace_script(&mut self, index: usize, script: Option<Script>) {
922 self.remove_script(index);
923
924 if let Some(entry) = self.scripts.get_mut(index) {
925 entry.script = script;
926 if let Some(sender) = self.script_message_sender.as_ref() {
927 if entry.script.is_some() {
928 Log::verify(sender.send(NodeScriptMessage::InitializeScript {
929 handle: self.self_handle,
930 script_index: index,
931 }));
932 }
933 }
934 }
935 }
936
937 #[inline]
941 pub fn add_script<T>(&mut self, script: T)
942 where
943 T: ScriptTrait,
944 {
945 let script_index = self.scripts.len();
946 self.scripts.push(ScriptRecord::new(Script::new(script)));
947 if let Some(sender) = self.script_message_sender.as_ref() {
948 Log::verify(sender.send(NodeScriptMessage::InitializeScript {
949 handle: self.self_handle,
950 script_index,
951 }));
952 }
953 }
954
955 #[inline]
958 pub fn has_script<T>(&self) -> bool
959 where
960 T: ScriptTrait,
961 {
962 self.try_get_script::<T>().is_some()
963 }
964
965 #[inline]
967 pub fn has_scripts_assigned(&self) -> bool {
968 self.scripts.iter().any(|script| script.is_some())
969 }
970
971 #[inline]
974 pub fn try_get_script<T>(&self) -> Option<&T>
975 where
976 T: ScriptTrait,
977 {
978 self.scripts
979 .iter()
980 .find_map(|s| s.as_ref().and_then(|s| s.cast::<T>()))
981 }
982
983 #[inline]
985 pub fn try_get_scripts<T>(&self) -> impl Iterator<Item = &T>
986 where
987 T: ScriptTrait,
988 {
989 self.scripts
990 .iter()
991 .filter_map(|e| e.script.as_ref().and_then(|s| s.cast::<T>()))
992 }
993
994 #[inline]
997 pub fn try_get_script_mut<T>(&mut self) -> Option<&mut T>
998 where
999 T: ScriptTrait,
1000 {
1001 self.scripts
1002 .iter_mut()
1003 .find_map(|s| s.as_mut().and_then(|s| s.cast_mut::<T>()))
1004 }
1005
1006 #[inline]
1008 pub fn try_get_scripts_mut<T>(&mut self) -> impl Iterator<Item = &mut T>
1009 where
1010 T: ScriptTrait,
1011 {
1012 self.scripts
1013 .iter_mut()
1014 .filter_map(|e| e.script.as_mut().and_then(|s| s.cast_mut::<T>()))
1015 }
1016
1017 #[inline]
1021 pub fn try_get_script_component<C>(&self) -> Option<&C>
1022 where
1023 C: Any,
1024 {
1025 self.scripts
1026 .iter()
1027 .find_map(|s| s.as_ref().and_then(|s| s.query_component_ref::<C>()))
1028 }
1029
1030 #[inline]
1034 pub fn try_get_script_component_mut<C>(&mut self) -> Option<&mut C>
1035 where
1036 C: Any,
1037 {
1038 self.scripts
1039 .iter_mut()
1040 .find_map(|s| s.as_mut().and_then(|s| s.query_component_mut::<C>()))
1041 }
1042
1043 #[inline]
1045 pub fn script_count(&self) -> usize {
1046 self.scripts.len()
1047 }
1048
1049 #[inline]
1055 pub fn script(&self, index: usize) -> Option<&Script> {
1056 self.scripts.get(index).and_then(|s| s.as_ref())
1057 }
1058
1059 #[inline]
1061 pub fn scripts(&self) -> impl Iterator<Item = &Script> {
1062 self.scripts.iter().filter_map(|s| s.as_ref())
1063 }
1064
1065 #[inline]
1077 pub fn script_mut(&mut self, index: usize) -> Option<&mut Script> {
1078 self.scripts.get_mut(index).and_then(|s| s.as_mut())
1079 }
1080
1081 #[inline]
1083 pub fn scripts_mut(&mut self) -> impl Iterator<Item = &mut Script> {
1084 self.scripts.iter_mut().filter_map(|s| s.as_mut())
1085 }
1086
1087 #[inline]
1095 pub fn set_enabled(&mut self, enabled: bool) -> bool {
1096 self.enabled.set_value_and_mark_modified(enabled)
1097 }
1098
1099 #[inline]
1103 pub fn is_enabled(&self) -> bool {
1104 *self.enabled.property
1105 }
1106
1107 #[inline]
1110 pub fn is_globally_enabled(&self) -> bool {
1111 self.global_enabled.get()
1112 }
1113
1114 #[inline]
1120 pub fn root_resource(&self) -> Option<ModelResource> {
1121 if let Some(resource) = self.resource.as_ref() {
1122 let mut state = resource.state();
1123 if let Some(model) = state.data() {
1124 if let Some(ancestor_node) = model
1125 .get_scene()
1126 .graph
1127 .try_get_node(self.original_handle_in_resource)
1128 {
1129 return if ancestor_node.resource.is_none() {
1130 Some(resource.clone())
1131 } else {
1132 ancestor_node.root_resource()
1133 };
1134 }
1135 }
1136 }
1137 None
1138 }
1139}
1140
1141impl Default for Base {
1142 fn default() -> Self {
1143 BaseBuilder::new().build_base()
1144 }
1145}
1146
1147pub(crate) fn visit_opt_script(
1149 name: &str,
1150 script: &mut Option<Script>,
1151 visitor: &mut Visitor,
1152) -> VisitResult {
1153 let mut region = visitor.enter_region(name)?;
1154
1155 let mut script_type_uuid = script.as_ref().map(|s| s.id()).unwrap_or_default();
1156 script_type_uuid.visit("TypeUuid", &mut region)?;
1157
1158 if region.is_reading() {
1159 *script = if script_type_uuid.is_nil() {
1160 None
1161 } else {
1162 let serialization_context = region
1163 .blackboard
1164 .get::<SerializationContext>()
1165 .expect("Visitor blackboard must contain serialization context!");
1166
1167 Some(
1168 serialization_context
1169 .script_constructors
1170 .try_create(&script_type_uuid)
1171 .ok_or_else(|| {
1172 VisitError::User(format!(
1173 "There is no corresponding script constructor for {script_type_uuid} type!"
1174 ))
1175 })?,
1176 )
1177 };
1178 }
1179
1180 if let Some(script) = script {
1181 script.visit("ScriptData", &mut region)?;
1182 }
1183
1184 Ok(())
1185}
1186
1187impl Visit for Base {
1188 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
1189 let mut region = visitor.enter_region(name)?;
1190
1191 if self.name.visit("Name", &mut region).is_err() {
1192 let mut region = region.enter_region("Name")?;
1195 let mut value = String::default();
1196 value.visit("Value", &mut region)?;
1197 self.name = ImmutableString::new(value);
1198 }
1199 self.local_transform.visit("Transform", &mut region)?;
1200 self.visibility.visit("Visibility", &mut region)?;
1201 self.parent.visit("Parent", &mut region)?;
1202 self.children.visit("Children", &mut region)?;
1203 self.resource.visit("Resource", &mut region)?;
1204 self.is_resource_instance_root
1205 .visit("IsResourceInstance", &mut region)?;
1206 self.lifetime.visit("Lifetime", &mut region)?;
1207 self.lod_group.visit("LodGroup", &mut region)?;
1208 self.mobility.visit("Mobility", &mut region)?;
1209 self.original_handle_in_resource
1210 .visit("Original", &mut region)?;
1211 self.tag.visit("Tag", &mut region)?;
1212 let _ = self.properties.visit("Properties", &mut region);
1213 let _ = self.frustum_culling.visit("FrustumCulling", &mut region);
1214 let _ = self.cast_shadows.visit("CastShadows", &mut region);
1215 let _ = self.instance_id.visit("InstanceId", &mut region);
1216 let _ = self.enabled.visit("Enabled", &mut region);
1217 let _ = self.render_mask.visit("RenderMask", &mut region);
1218
1219 let mut old_script = None;
1230 if region.is_reading() && visit_opt_script("Script", &mut old_script, &mut region).is_ok() {
1231 if let Some(old_script) = old_script {
1232 self.scripts.push(ScriptRecord::new(old_script));
1233 }
1234 return Ok(());
1235 }
1236
1237 let _ = self.scripts.visit("Scripts", &mut region);
1238
1239 Ok(())
1240 }
1241}
1242
1243pub struct BaseBuilder {
1245 name: String,
1246 visibility: bool,
1247 local_transform: Transform,
1248 children: Vec<Handle<Node>>,
1249 lifetime: Option<f32>,
1250 lod_group: Option<LodGroup>,
1251 mobility: Mobility,
1252 inv_bind_pose_transform: Matrix4<f32>,
1253 tag: String,
1254 frustum_culling: bool,
1255 cast_shadows: bool,
1256 scripts: Vec<ScriptRecord>,
1257 instance_id: SceneNodeId,
1258 enabled: bool,
1259}
1260
1261impl Default for BaseBuilder {
1262 fn default() -> Self {
1263 Self::new()
1264 }
1265}
1266
1267impl BaseBuilder {
1268 #[inline]
1270 pub fn new() -> Self {
1271 Self {
1272 name: Default::default(),
1273 visibility: true,
1274 local_transform: Default::default(),
1275 children: Default::default(),
1276 lifetime: None,
1277 lod_group: None,
1278 mobility: Default::default(),
1279 inv_bind_pose_transform: Matrix4::identity(),
1280 tag: Default::default(),
1281 frustum_culling: true,
1282 cast_shadows: true,
1283 scripts: vec![],
1284 instance_id: SceneNodeId(Uuid::new_v4()),
1285 enabled: true,
1286 }
1287 }
1288
1289 #[inline]
1291 pub fn with_mobility(mut self, mobility: Mobility) -> Self {
1292 self.mobility = mobility;
1293 self
1294 }
1295
1296 #[inline]
1298 pub fn with_name<P: AsRef<str>>(mut self, name: P) -> Self {
1299 name.as_ref().clone_into(&mut self.name);
1300 self
1301 }
1302
1303 #[inline]
1305 pub fn with_visibility(mut self, visibility: bool) -> Self {
1306 self.visibility = visibility;
1307 self
1308 }
1309
1310 #[inline]
1312 pub fn with_local_transform(mut self, transform: Transform) -> Self {
1313 self.local_transform = transform;
1314 self
1315 }
1316
1317 #[inline]
1319 pub fn with_inv_bind_pose_transform(mut self, inv_bind_pose: Matrix4<f32>) -> Self {
1320 self.inv_bind_pose_transform = inv_bind_pose;
1321 self
1322 }
1323
1324 pub fn with_enabled(mut self, enabled: bool) -> Self {
1326 self.enabled = enabled;
1327 self
1328 }
1329
1330 #[inline]
1332 pub fn with_children<'a, I: IntoIterator<Item = &'a Handle<Node>>>(
1333 mut self,
1334 children: I,
1335 ) -> Self {
1336 for &child in children.into_iter() {
1337 if child.is_some() {
1338 self.children.push(child)
1339 }
1340 }
1341 self
1342 }
1343
1344 #[inline]
1346 pub fn with_lifetime(mut self, time_seconds: f32) -> Self {
1347 self.lifetime = Some(time_seconds);
1348 self
1349 }
1350
1351 #[inline]
1353 pub fn with_lod_group(mut self, lod_group: LodGroup) -> Self {
1354 self.lod_group = Some(lod_group);
1355 self
1356 }
1357
1358 #[inline]
1360 pub fn with_tag(mut self, tag: String) -> Self {
1361 self.tag = tag;
1362 self
1363 }
1364
1365 #[inline]
1367 pub fn with_frustum_culling(mut self, frustum_culling: bool) -> Self {
1368 self.frustum_culling = frustum_culling;
1369 self
1370 }
1371
1372 #[inline]
1374 pub fn with_cast_shadows(mut self, cast_shadows: bool) -> Self {
1375 self.cast_shadows = cast_shadows;
1376 self
1377 }
1378
1379 #[inline]
1381 pub fn with_script<T>(mut self, script: T) -> Self
1382 where
1383 T: ScriptTrait,
1384 {
1385 self.scripts.push(ScriptRecord::new(Script::new(script)));
1386 self
1387 }
1388
1389 pub fn with_instance_id(mut self, id: SceneNodeId) -> Self {
1391 self.instance_id = id;
1392 self
1393 }
1394
1395 #[inline]
1397 pub fn build_base(self) -> Base {
1398 Base {
1399 self_handle: Default::default(),
1400 script_message_sender: None,
1401 message_sender: None,
1402 name: self.name.into(),
1403 children: self.children,
1404 local_transform: TrackedProperty::unbound(
1405 self.local_transform,
1406 NodeMessageKind::TransformChanged,
1407 ),
1408 lifetime: self.lifetime.into(),
1409 visibility: TrackedProperty::unbound(
1410 self.visibility.into(),
1411 NodeMessageKind::VisibilityChanged,
1412 ),
1413 enabled: TrackedProperty::unbound(
1414 self.enabled.into(),
1415 NodeMessageKind::EnabledFlagChanged,
1416 ),
1417 render_mask: BitMask::all().into(),
1418 global_visibility: Cell::new(true),
1419 parent: Handle::NONE,
1420 global_transform: Cell::new(Matrix4::identity()),
1421 inv_bind_pose_transform: self.inv_bind_pose_transform,
1422 resource: None,
1423 original_handle_in_resource: Handle::NONE,
1424 is_resource_instance_root: false,
1425 lod_group: self.lod_group.into(),
1426 mobility: self.mobility.into(),
1427 tag: self.tag.into(),
1428 properties: Default::default(),
1429 frustum_culling: self.frustum_culling.into(),
1430 cast_shadows: self.cast_shadows.into(),
1431 scripts: self.scripts,
1432 instance_id: SceneNodeId(Uuid::new_v4()),
1433
1434 global_enabled: Cell::new(true),
1435 }
1436 }
1437}