fyrox_impl/scene/
base.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Contains all structures and methods to create and manage base scene graph nodes.
22//!
23//! For more info see [`Base`]
24
25use 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/// Level of detail is a collection of objects for given normalized distance range.
57/// Objects will be rendered **only** if they're in specified range.
58/// Normalized distance is a distance in (0; 1) range where 0 - closest to camera,
59/// 1 - farthest. Real distance can be obtained by multiplying normalized distance
60/// with z_far of current projection matrix.
61#[derive(Debug, Default, Clone, Visit, Reflect, PartialEq, TypeUuidProvider)]
62#[type_uuid(id = "576b31a2-2b39-4c79-95dd-26aeaf381d8b")]
63pub struct LevelOfDetail {
64    /// Beginning of the range in which the level will be visible. It is expressed in normalized
65    /// coordinates: where 0.0 - closest to camera, 1.0 - farthest from camera.
66    begin: f32,
67    /// End of the range in which the level will be visible. It is expressed in normalized coordinates:
68    /// where 0.0 - closest to camera, 1.0 - farthest from camera.
69    end: f32,
70    /// List of objects, where each object represents level of detail of parent's LOD group.
71    pub objects: Vec<Handle<Node>>,
72}
73
74impl LevelOfDetail {
75    /// Creates new level of detail.
76    pub fn new(begin: f32, end: f32, objects: Vec<Handle<Node>>) -> Self {
77        for object in objects.iter() {
78            // Invalid handles are not allowed.
79            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    /// Sets new starting point in distance range. Input value will be clamped in
91    /// (0; 1) range.
92    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    /// Returns starting point of the range.
100    pub fn begin(&self) -> f32 {
101        self.begin
102    }
103
104    /// Sets new end point in distance range. Input value will be clamped in
105    /// (0; 1) range.
106    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    /// Returns end point of the range.
114    pub fn end(&self) -> f32 {
115        self.end
116    }
117}
118
119/// LOD (Level-Of-Detail) group is a set of cascades (levels), where each cascade takes specific
120/// distance range. Each cascade contains list of objects that should or shouldn't be rendered
121/// if distance satisfy cascade range. LOD may significantly improve performance if your scene
122/// contains lots of high poly objects and objects may be far away from camera. Distant objects
123/// in this case will be rendered with lower details freeing precious GPU resources for other
124/// useful tasks.
125///
126/// Lod group must contain non-overlapping cascades, each cascade with its own set of objects
127/// that belongs to level of detail. Engine does not care if you create overlapping cascades,
128/// it is your responsibility to create non-overlapping cascades.
129#[derive(Debug, Default, Clone, Visit, Reflect, PartialEq, TypeUuidProvider)]
130#[type_uuid(id = "8e7b18b1-c1e0-47d7-b952-4394c1d049e5")]
131pub struct LodGroup {
132    /// Set of cascades.
133    pub levels: Vec<LevelOfDetail>,
134}
135
136/// Mobility defines a group for scene node which has direct impact on performance
137/// and capabilities of nodes.
138#[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    /// Transform cannot be changed.
158    ///
159    /// ## Scene and performance.
160    ///
161    /// Nodes with Static mobility should be used all the time you need unchangeable
162    /// node. Such nodes will have maximum optimization during the rendering.
163    ///
164    /// ### Meshes
165    ///
166    /// Static meshes will be baked into larger blocks to reduce draw call count per frame.
167    /// Also static meshes will participate in lightmap generation.
168    ///
169    /// ### Lights
170    ///
171    /// Static lights will be baked in lightmap. They lit only static geometry!
172    /// Specular lighting is not supported.
173    #[default]
174    Static = 0,
175
176    /// Transform cannot be changed, but other node-dependent properties are changeable.
177    ///
178    /// ## Scene and performance.
179    ///
180    /// ### Meshes
181    ///
182    /// Same as Static.
183    ///
184    /// ### Lights
185    ///
186    /// Stationary lights have complex route for shadows:
187    ///   - Shadows from Static/Stationary meshes will be baked into lightmap.
188    ///   - Shadows from Dynamic lights will be re-rendered each frame into shadow map.
189    /// Stationary lights support specular lighting.
190    Stationary = 1,
191
192    /// Transform can be freely changed.
193    ///
194    /// ## Scene and performance.
195    ///
196    /// Dynamic mobility should be used only for the objects that are designed to be
197    /// moving in the scene, for example - objects with physics, or dynamic lights, etc.
198    Dynamic = 2,
199}
200
201/// A property value.
202#[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    /// A node handle.
208    ///
209    /// # Important notes
210    ///
211    /// The value of the property will be remapped when owning node is cloned, this means that the
212    /// handle will always be correct.
213    NodeHandle(Handle<Node>),
214    /// An arbitrary, type-erased handle.
215    ///
216    /// # Important notes
217    ///
218    /// The value of the property will **not** be remapped when owning node is cloned, this means
219    /// that the handle correctness is not guaranteed on copy.
220    Handle(ErasedHandle),
221    /// A string value.
222    String(String),
223    /// A 64-bit signed integer value.
224    I64(i64),
225    /// A 64-bit unsigned integer value.
226    U64(u64),
227    /// A 32-bit signed integer value.
228    I32(i32),
229    /// A 32-bit unsigned integer value.
230    U32(u32),
231    /// A 16-bit signed integer value.
232    I16(i16),
233    /// A 16-bit unsigned integer value.
234    U16(u16),
235    /// A 8-bit signed integer value.
236    I8(i8),
237    /// A 8-bit unsigned integer value.
238    U8(u8),
239    /// A 32-bit floating point value.
240    F32(f32),
241    /// A 64-bit floating point value.
242    F64(f64),
243}
244
245impl Default for PropertyValue {
246    fn default() -> Self {
247        Self::I8(0)
248    }
249}
250
251/// A custom property.
252#[derive(Debug, Visit, Reflect, Default, Clone, PartialEq, TypeUuidProvider)]
253#[type_uuid(id = "fc87fd21-a5e6-40d5-a79d-19f96b25d6c9")]
254pub struct Property {
255    /// Name of the property.
256    pub name: String,
257    /// A value of the property.
258    pub value: PropertyValue,
259}
260
261/// A script message from scene node. It is used for deferred initialization/deinitialization.
262pub enum NodeScriptMessage {
263    /// A script was set to a node and needs to be initialized.
264    InitializeScript {
265        /// Node handle.
266        handle: Handle<Node>,
267        /// Index of the script.
268        script_index: usize,
269    },
270    /// A node script must be destroyed. It can happen if the script was replaced with some other
271    /// or a node was destroyed.
272    DestroyScript {
273        /// Script instance.
274        script: Script,
275        /// Node handle.
276        handle: Handle<Node>,
277        /// Index of the script.
278        script_index: usize,
279    },
280}
281
282/// Unique id of a node, that could be used as a reliable "index" of the node. This id is mostly
283/// useful for network games.
284#[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/// A script container record.
308#[derive(Clone, Reflect, Debug, Default, TypeUuidProvider)]
309#[type_uuid(id = "51bc577b-5a50-4a97-9b31-eda2f3d46c9d")]
310pub struct ScriptRecord {
311    // Script is wrapped into `Option` to be able to do take-return trick to bypass borrow checker
312    // issues.
313    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)] // STFU
348#[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/// Base scene graph node is a simplest possible node, it is used to build more complex ones using composition.
418/// It contains all fundamental properties for each scene graph nodes, like local and global transforms, name,
419/// lifetime, etc. Base node is a building block for all complex node hierarchies - it contains list of children
420/// and handle to parent node.
421///
422/// # Example
423///
424/// ```
425/// # use fyrox_impl::scene::base::BaseBuilder;
426/// # use fyrox_impl::scene::graph::Graph;
427/// # use fyrox_impl::scene::node::Node;
428/// # use fyrox_impl::core::pool::Handle;
429/// # use fyrox_impl::scene::pivot::PivotBuilder;
430///
431/// fn create_pivot_node(graph: &mut Graph) -> Handle<Node> {
432///     PivotBuilder::new(BaseBuilder::new()
433///         .with_name("BaseNode"))
434///         .build(graph)
435/// }
436/// ```
437#[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    // Name is not inheritable, because property inheritance works bad with external 3D models.
449    // They use names to search "original" nodes.
450    #[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    /// Control whether this node should be rendered. A node should be rendered only if its render mask shares
463    /// some set bits in common with the render mask of the camera.
464    pub render_mask: InheritableVariable<BitMask>,
465
466    /// Maximum amount of Some(time) that node will \"live\" or None if the node has unlimited lifetime.
467    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    /// A set of custom properties that can hold almost any data. It can be used to set additional
482    /// properties to scene nodes.
483    #[reflect(setter = "set_properties")]
484    pub properties: InheritableVariable<Vec<Property>>,
485
486    #[reflect(setter = "set_frustum_culling")]
487    frustum_culling: InheritableVariable<bool>,
488
489    /// When `true` it means that this node is instance of `resource`.
490    /// More precisely - this node is root of whole descendant nodes
491    /// hierarchy which was instantiated from resource.
492    #[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    // Bone-specific matrix. Non-serializable.
508    #[reflect(hidden)]
509    pub(crate) inv_bind_pose_transform: Matrix4<f32>,
510
511    // A resource from which this node was instantiated from, can work in pair
512    // with `original` handle to get corresponding node from resource.
513    #[reflect(read_only)]
514    pub(crate) resource: Option<ModelResource>,
515
516    // Handle to node in scene of model resource from which this node
517    // was instantiated from.
518    #[reflect(read_only)]
519    #[reflect(hidden)]
520    pub(crate) original_handle_in_resource: Handle<Node>,
521
522    /// Unique id of a node, that could be used as a reliable "index" of the node. This id is mostly
523    /// useful for network games. Keep in mind, that this id **will** be randomized in case if you're
524    /// instantiating a prefab. In other words, all instances of a prefab will have unique instance
525    /// id.
526    pub(crate) instance_id: SceneNodeId,
527
528    // Scripts of the scene node.
529    //
530    // # Important notes
531    //
532    // WARNING: Setting a new script via reflection will break normal script destruction process!
533    // Use it at your own risk only when you're completely sure what you are doing.
534    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    /// Returns handle of the node. A node has valid handle only after it was inserted in a graph!
548    #[inline]
549    pub fn handle(&self) -> Handle<Node> {
550        self.self_handle
551    }
552
553    /// Sets name of node. Can be useful to mark a node to be able to find it later on.
554    #[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    /// Returns name of node.
564    #[inline]
565    pub fn name(&self) -> &str {
566        self.name.as_str()
567    }
568
569    /// Returns owned name of node.
570    #[inline]
571    pub fn name_owned(&self) -> String {
572        self.name.to_mutable()
573    }
574
575    /// Returns shared reference to local transform of a node, can be used to fetch
576    /// some local spatial properties, such as position, rotation, scale, etc.
577    #[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        // Kick off initial hierarchical property propagation.
597        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    /// Returns mutable reference to local transform of a node, can be used to set some local spatial
610    /// properties, such as position, rotation, scale, etc. To set global position and rotation, use
611    /// [`super::Graph::set_global_position`] and [`super::Graph::set_global_rotation`] methods respectively.
612    #[inline]
613    pub fn local_transform_mut(&mut self) -> &mut Transform {
614        &mut self.local_transform
615    }
616
617    /// Sets new local transform of a node. If you need to modify existing local transformation,
618    /// use [`Self::local_transform_mut`].
619    #[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    /// Tries to find properties by the name. The method returns an iterator because it possible
626    /// to have multiple properties with the same name.
627    #[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    /// Tries to find a first property with the given name.
633    #[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    /// Sets a new set of properties of the node.
639    #[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    /// Sets lifetime of node in seconds, lifetime is useful for temporary objects.
648    /// Example - you firing a gun, it produces two particle systems for each shot:
649    /// one for gunpowder fumes and one when bullet hits some surface. These particle
650    /// systems won't last very long - usually they will disappear in 1-2 seconds
651    /// but nodes will still be in scene consuming precious CPU clocks. This is where
652    /// lifetimes become handy - you just set appropriate lifetime for a particle
653    /// system node and it will be removed from scene when time will end. This is
654    /// efficient algorithm because scene holds every object in pool and allocation
655    /// or deallocation of node takes very little amount of time.
656    #[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    /// Returns current lifetime of a node. Will be None if node has undefined lifetime.
663    /// For more info about lifetimes see [`set_lifetime`](Self::set_lifetime).
664    #[inline]
665    pub fn lifetime(&self) -> Option<f32> {
666        *self.lifetime
667    }
668
669    /// Returns handle of parent node.
670    #[inline]
671    pub fn parent(&self) -> Handle<Node> {
672        self.parent
673    }
674
675    /// Returns slice of handles to children nodes. This can be used, for example, to
676    /// traverse tree starting from some node.
677    #[inline]
678    pub fn children(&self) -> &[Handle<Node>] {
679        self.children.as_slice()
680    }
681
682    /// Returns global transform matrix, such matrix contains combined transformation
683    /// of transforms of parent nodes. This is the final matrix that describes real
684    /// location of object in the world.
685    #[inline]
686    pub fn global_transform(&self) -> Matrix4<f32> {
687        self.global_transform.get()
688    }
689
690    /// Calculates global transform of the node, but discards scaling part of it.
691    #[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    /// Returns inverse of bind pose matrix. Bind pose matrix - is special matrix
700    /// for bone nodes, it stores initial transform of bone node at the moment
701    /// of "binding" vertices to bones.
702    #[inline]
703    pub fn inv_bind_pose_transform(&self) -> Matrix4<f32> {
704        self.inv_bind_pose_transform
705    }
706
707    /// Returns true if this node is model resource instance root node.
708    #[inline]
709    pub fn is_resource_instance_root(&self) -> bool {
710        self.is_resource_instance_root
711    }
712
713    /// Returns resource from which this node was instantiated from.
714    #[inline]
715    pub fn resource(&self) -> Option<ModelResource> {
716        self.resource.clone()
717    }
718
719    /// Sets local visibility of a node.
720    #[inline]
721    pub fn set_visibility(&mut self, visibility: bool) -> bool {
722        self.visibility.set_value_and_mark_modified(visibility)
723    }
724
725    /// Returns local visibility of a node.
726    #[inline]
727    pub fn visibility(&self) -> bool {
728        *self.visibility.property
729    }
730
731    /// Returns current **local-space** bounding box. Keep in mind that this value is just
732    /// a placeholder, because there is not information to calculate actual bounding box.
733    #[inline]
734    pub fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
735        AxisAlignedBoundingBox::unit()
736    }
737
738    /// Returns current **world-space** bounding box.
739    #[inline]
740    pub fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
741        self.local_bounding_box()
742            .transform(&self.global_transform())
743    }
744
745    /// Set new mobility for the node. See [`Mobility`] docs for more info.
746    #[inline]
747    pub fn set_mobility(&mut self, mobility: Mobility) -> Mobility {
748        self.mobility.set_value_and_mark_modified(mobility)
749    }
750
751    /// Return current mobility of the node.
752    #[inline]
753    pub fn mobility(&self) -> Mobility {
754        *self.mobility
755    }
756
757    /// Returns combined visibility of an node. This is the final visibility of a node. Global visibility calculated
758    /// using visibility of all parent nodes until root one, so if some parent node upper on tree is invisible then
759    /// all its children will be invisible. It defines if object will be rendered. It is *not* the same as real
760    /// visibility from point of view of a camera. Use frustum-box intersection test instead.
761    #[inline]
762    pub fn global_visibility(&self) -> bool {
763        self.global_visibility.get()
764    }
765
766    /// Handle to node in scene of model resource from which this node was instantiated from.
767    ///
768    /// # Notes
769    ///
770    /// This handle is extensively used to fetch information about the state of node in the resource
771    /// to sync properties of instance with its original in the resource.
772    #[inline]
773    pub fn original_handle_in_resource(&self) -> Handle<Node> {
774        self.original_handle_in_resource
775    }
776
777    /// Returns `true` if the node has a parent object in a resource from which it may restore
778    /// values of its inheritable properties.
779    #[inline]
780    pub fn has_inheritance_parent(&self) -> bool {
781        self.original_handle_in_resource.is_some() && self.resource.is_some()
782    }
783
784    /// Returns position of the node in absolute coordinates.
785    #[inline]
786    pub fn global_position(&self) -> Vector3<f32> {
787        self.global_transform.get().position()
788    }
789
790    /// Returns "look" vector of global transform basis, in most cases return vector will be non-normalized.
791    #[inline]
792    pub fn look_vector(&self) -> Vector3<f32> {
793        self.global_transform.get().look()
794    }
795
796    /// Returns "side" vector of global transform basis, in most cases return vector will be non-normalized.
797    #[inline]
798    pub fn side_vector(&self) -> Vector3<f32> {
799        self.global_transform.get().side()
800    }
801
802    /// Returns "up" vector of global transform basis, in most cases return vector will be non-normalized.
803    #[inline]
804    pub fn up_vector(&self) -> Vector3<f32> {
805        self.global_transform.get().up()
806    }
807
808    /// Sets new lod group.
809    #[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    /// Extracts lod group, leaving None in the node.
815    #[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    /// Returns shared reference to current lod group.
821    #[inline]
822    pub fn lod_group(&self) -> Option<&LodGroup> {
823        self.lod_group.as_ref()
824    }
825
826    /// Returns mutable reference to current lod group.
827    #[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    /// Returns node tag.
833    #[inline]
834    pub fn tag(&self) -> &str {
835        &self.tag
836    }
837
838    /// Returns a copy of node tag.
839    #[inline]
840    pub fn tag_owned(&self) -> String {
841        (*self.tag).clone()
842    }
843
844    /// Sets new tag.
845    #[inline]
846    pub fn set_tag(&mut self, tag: String) -> String {
847        self.tag.set_value_and_mark_modified(tag)
848    }
849
850    /// Return the frustum_culling flag
851    #[inline]
852    pub fn frustum_culling(&self) -> bool {
853        *self.frustum_culling
854    }
855
856    /// Sets whether to use frustum culling or not
857    #[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    /// Returns true if the node should cast shadows, false - otherwise.
864    #[inline]
865    pub fn cast_shadows(&self) -> bool {
866        *self.cast_shadows
867    }
868
869    /// Sets whether the mesh should cast shadows or not.
870    #[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    /// Returns current instance id.
876    pub fn instance_id(&self) -> SceneNodeId {
877        self.instance_id
878    }
879
880    /// Removes a script with the given `index` from the scene node. The script will be destroyed
881    /// in either the current update tick (if it was removed from some other script) or in the next
882    /// update tick of the parent graph.
883    pub fn remove_script(&mut self, index: usize) {
884        // Send script to the graph to destroy script instances correctly.
885        if let Some(entry) = self.scripts.get_mut(index) {
886            entry.should_be_deleted = true;
887
888            // We might be in a middle of a script method execution, where script is temporarily
889            // extracted from the array.
890            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    /// Removes all assigned scripts from the scene node. The scripts will be removed from
909    /// first-to-last order an their actual destruction will happen either on the current update tick
910    /// of the parent graph (if it was removed from some other script) or in the next update tick.
911    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    /// Sets a new script for the scene node by index. Previous script will be removed (see
919    /// [`Self::remove_script`] docs for more info).
920    #[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    /// Adds a new script to the scene node. The new script will be initialized either in the current
938    /// update tick (if the script was added in one of the [`ScriptTrait`] methods) or on the next
939    /// update tick.
940    #[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    /// Checks if the node has a script of a particular type. Returns `false` if there is no such
956    /// script.
957    #[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    /// Checks if the node has any scripts assigned.
966    #[inline]
967    pub fn has_scripts_assigned(&self) -> bool {
968        self.scripts.iter().any(|script| script.is_some())
969    }
970
971    /// Tries to find a **first** script of the given type `T`, returns `None` if there's no such
972    /// script.
973    #[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    /// Returns an iterator that yields references to the scripts of the given type `T`.
984    #[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    /// Tries to find a **first** script of the given type `T`, returns `None` if there's no such
995    /// script.
996    #[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    /// Returns an iterator that yields references to the scripts of the given type `T`.
1007    #[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    /// Tries find a component of the given type `C` across **all** available scripts of the node.
1018    /// If you want to search a component `C` in a particular script, then use [`Self::try_get_script`]
1019    /// and then search for component in it.
1020    #[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    /// Tries find a component of the given type `C` across **all** available scripts of the node.
1031    /// If you want to search a component `C` in a particular script, then use [`Self::try_get_script`]
1032    /// and then search for component in it.
1033    #[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    /// Returns total count of scripts assigned to the node.
1044    #[inline]
1045    pub fn script_count(&self) -> usize {
1046        self.scripts.len()
1047    }
1048
1049    /// Returns a shared reference to a script instance with the given `index`. This method will
1050    /// return [`None`] if the `index` is out of bounds or the script is temporarily not available.
1051    /// This could happen if this method was called from some method of a [`ScriptTrait`]. It
1052    /// happens because of borrowing rules - you cannot take another reference to a script that is
1053    /// already mutably borrowed.
1054    #[inline]
1055    pub fn script(&self, index: usize) -> Option<&Script> {
1056        self.scripts.get(index).and_then(|s| s.as_ref())
1057    }
1058
1059    /// Returns an iterator that yields all assigned scripts.
1060    #[inline]
1061    pub fn scripts(&self) -> impl Iterator<Item = &Script> {
1062        self.scripts.iter().filter_map(|s| s.as_ref())
1063    }
1064
1065    /// Returns a mutable reference to a script instance with the given `index`. This method will
1066    /// return [`None`] if the `index` is out of bounds or the script is temporarily not available.
1067    /// This could happen if this method was called from some method of a [`ScriptTrait`]. It
1068    /// happens because of borrowing rules - you cannot take another reference to a script that is
1069    /// already mutably borrowed.
1070    ///
1071    /// # Important notes
1072    ///
1073    /// Do **not** replace script instance using mutable reference given to you by this method.
1074    /// This will prevent correct script de-initialization! Use [`Self::replace_script`] if you need
1075    /// to replace the script.
1076    #[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    /// Returns an iterator that yields all assigned scripts.
1082    #[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    /// Enables or disables scene node. Disabled scene nodes won't be updated (including scripts) or rendered.
1088    ///
1089    /// # Important notes
1090    ///
1091    /// Enabled/disabled state will affect children nodes. It means that if you have a node with children nodes,
1092    /// and you disable the node, all children nodes will be disabled too even if their [`Self::is_enabled`] method
1093    /// returns `true`.
1094    #[inline]
1095    pub fn set_enabled(&mut self, enabled: bool) -> bool {
1096        self.enabled.set_value_and_mark_modified(enabled)
1097    }
1098
1099    /// Returns `true` if the node is enabled, `false` - otherwise. The return value does **not** include the state
1100    /// of parent nodes. It should be considered as "local" enabled flag. To get actual enabled state, that includes
1101    /// the state of parent nodes, use [`Self::is_globally_enabled`] method.
1102    #[inline]
1103    pub fn is_enabled(&self) -> bool {
1104        *self.enabled.property
1105    }
1106
1107    /// Returns `true` if the node and every parent up in hierarchy is enabled, `false` - otherwise. This method
1108    /// returns "true" `enabled` flag. Its value could be different from the value returned by [`Self::is_enabled`].
1109    #[inline]
1110    pub fn is_globally_enabled(&self) -> bool {
1111        self.global_enabled.get()
1112    }
1113
1114    /// Returns a root resource of the scene node. This method crawls up on dependency tree until it finds that
1115    /// the ancestor node does not have any dependencies and returns this resource as the root resource. For
1116    /// example, in case of simple scene node instance, this method will return the resource from which the node
1117    /// was instantiated from. In case of 2 or more levels of dependency, it will always return the "top"
1118    /// dependency in the dependency graph.
1119    #[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
1147// Serializes Option<Script> using given serializer.
1148pub(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            // Name was wrapped into `InheritableVariable` previously, so we must maintain
1193            // backward compatibility here.
1194            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        // Script visiting may fail for various reasons:
1220        //
1221        // 1) Data inside a script is not compatible with latest code (there is no backward
1222        //    compatibility for the data)
1223        // 2) Script was removed in the game.
1224        //
1225        // None of the reasons are fatal and we should still give an ability to load such node
1226        // to edit or remove it.
1227
1228        // This block is needed for backward compatibility
1229        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
1243/// Base node builder allows you to create nodes in declarative manner.
1244pub 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    /// Creates new builder instance.
1269    #[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    /// Sets desired mobility.
1290    #[inline]
1291    pub fn with_mobility(mut self, mobility: Mobility) -> Self {
1292        self.mobility = mobility;
1293        self
1294    }
1295
1296    /// Sets desired name.
1297    #[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    /// Sets desired visibility.
1304    #[inline]
1305    pub fn with_visibility(mut self, visibility: bool) -> Self {
1306        self.visibility = visibility;
1307        self
1308    }
1309
1310    /// Sets desired local transform.
1311    #[inline]
1312    pub fn with_local_transform(mut self, transform: Transform) -> Self {
1313        self.local_transform = transform;
1314        self
1315    }
1316
1317    /// Sets desired inverse bind pose transform.
1318    #[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    /// Enables or disables the scene node.
1325    pub fn with_enabled(mut self, enabled: bool) -> Self {
1326        self.enabled = enabled;
1327        self
1328    }
1329
1330    /// Sets desired list of children nodes.
1331    #[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    /// Sets desired lifetime.
1345    #[inline]
1346    pub fn with_lifetime(mut self, time_seconds: f32) -> Self {
1347        self.lifetime = Some(time_seconds);
1348        self
1349    }
1350
1351    /// Sets desired lod group.
1352    #[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    /// Sets desired tag.
1359    #[inline]
1360    pub fn with_tag(mut self, tag: String) -> Self {
1361        self.tag = tag;
1362        self
1363    }
1364
1365    /// Sets desired frustum_culling flag.
1366    #[inline]
1367    pub fn with_frustum_culling(mut self, frustum_culling: bool) -> Self {
1368        self.frustum_culling = frustum_culling;
1369        self
1370    }
1371
1372    /// Sets whether mesh should cast shadows or not.
1373    #[inline]
1374    pub fn with_cast_shadows(mut self, cast_shadows: bool) -> Self {
1375        self.cast_shadows = cast_shadows;
1376        self
1377    }
1378
1379    /// Sets script of the node.
1380    #[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    /// Sets new instance id.
1390    pub fn with_instance_id(mut self, id: SceneNodeId) -> Self {
1391        self.instance_id = id;
1392        self
1393    }
1394
1395    /// Creates an instance of [`Base`].
1396    #[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}