Skip to main content

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 super::collider::BitMask;
26use crate::{
27    core::{
28        algebra::{Matrix4, Vector3},
29        log::Log,
30        math::{aabb::AxisAlignedBoundingBox, Matrix4Ext},
31        pool::{ErasedHandle, Handle},
32        reflect::prelude::*,
33        type_traits::prelude::*,
34        variable::InheritableVariable,
35        visitor::{Visit, VisitResult, Visitor},
36        ImmutableString,
37    },
38    engine::SerializationContext,
39    graph::SceneGraph,
40    resource::model::ModelResource,
41    scene::{node::Node, transform::Transform},
42    script::{Script, ScriptTrait},
43};
44use fyrox_core::algebra::UnitQuaternion;
45use fyrox_core::pool::ObjectOrVariant;
46use fyrox_core::visitor::error::VisitError;
47use serde::{Deserialize, Serialize};
48use std::{
49    any::Any,
50    cell::Cell,
51    ops::{Deref, DerefMut},
52    sync::mpsc::Sender,
53};
54use strum_macros::{AsRefStr, EnumString, VariantNames};
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::{Pivot, PivotBuilder};
430///
431/// fn create_pivot_node(graph: &mut Graph) -> Handle<Pivot> {
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    pub(crate) fn on_connected_to_graph(
576        &mut self,
577        self_handle: Handle<Node>,
578        message_sender: Sender<NodeMessage>,
579        script_message_sender: Sender<NodeScriptMessage>,
580    ) {
581        self.self_handle = self_handle;
582        self.message_sender = Some(message_sender.clone());
583        self.script_message_sender = Some(script_message_sender);
584        self.local_transform
585            .set_message_data(message_sender.clone(), self_handle);
586        self.visibility
587            .set_message_data(message_sender.clone(), self_handle);
588        self.enabled.set_message_data(message_sender, self_handle);
589        // Kick off initial hierarchical property propagation.
590        self.notify(self.self_handle, NodeMessageKind::TransformChanged);
591        self.notify(self.self_handle, NodeMessageKind::VisibilityChanged);
592        self.notify(self.self_handle, NodeMessageKind::EnabledFlagChanged);
593    }
594
595    fn notify(&self, node: Handle<Node>, kind: NodeMessageKind) {
596        let Some(sender) = self.message_sender.as_ref() else {
597            return;
598        };
599        Log::verify(sender.send(NodeMessage::new(node, kind)));
600    }
601
602    /// Returns shared reference to local transform of a node, can be used to fetch
603    /// some local spatial properties, such as position, rotation, scale, etc.
604    #[inline]
605    pub fn local_transform(&self) -> &Transform {
606        &self.local_transform
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    /// Sets the new position of the node in the parent's node coordinate system.
626    #[inline]
627    pub fn set_position(&mut self, position: Vector3<f32>) {
628        self.local_transform_mut().set_position(position);
629    }
630
631    /// Sets the new position of the node in the parent's node coordinate system.
632    #[inline]
633    pub fn set_position_xyz(&mut self, x: f32, y: f32, z: f32) {
634        self.set_position(Vector3::new(x, y, z))
635    }
636
637    /// Sets the new rotation of the node in the parent's node coordinate system.
638    #[inline]
639    pub fn set_rotation(&mut self, rotation: UnitQuaternion<f32>) {
640        self.local_transform_mut().set_rotation(rotation);
641    }
642
643    /// Sets the new rotation of the node in the parent's node coordinate system.
644    #[inline]
645    pub fn set_rotation_angles(&mut self, roll: f32, pitch: f32, yaw: f32) {
646        self.set_rotation(UnitQuaternion::from_euler_angles(roll, pitch, yaw))
647    }
648
649    /// Sets the new rotation of the node around X axis in the parent's node coordinate system.
650    #[inline]
651    pub fn set_rotation_x(&mut self, angle: f32) {
652        self.set_rotation(UnitQuaternion::from_axis_angle(&Vector3::x_axis(), angle))
653    }
654
655    /// Sets the new rotation of the node around Y axis in the parent's node coordinate system.
656    #[inline]
657    pub fn set_rotation_y(&mut self, angle: f32) {
658        self.set_rotation(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), angle))
659    }
660
661    /// Sets the new rotation of the node around Z axis in the parent's node coordinate system.
662    #[inline]
663    pub fn set_rotation_z(&mut self, angle: f32) {
664        self.set_rotation(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), angle))
665    }
666
667    /// Sets the new scale of the node in the parent's node coordinate system.
668    #[inline]
669    pub fn set_scale(&mut self, scale: Vector3<f32>) {
670        self.local_transform_mut().set_scale(scale);
671    }
672
673    /// Sets the new scale of the node in the parent's node coordinate system.
674    #[inline]
675    pub fn set_scale_xyz(&mut self, x: f32, y: f32, z: f32) {
676        self.set_scale(Vector3::new(x, y, z));
677    }
678
679    /// Sets the new scale of the node for all axes at once in the parent's node coordinate system.
680    #[inline]
681    pub fn set_uniform_scale(&mut self, scale: f32) {
682        self.set_scale(Vector3::repeat(scale))
683    }
684
685    /// Tries to find properties by the name. The method returns an iterator because it possible
686    /// to have multiple properties with the same name.
687    #[inline]
688    pub fn find_properties_ref<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a Property> {
689        self.properties.iter().filter(move |p| p.name == name)
690    }
691
692    /// Tries to find a first property with the given name.
693    #[inline]
694    pub fn find_first_property_ref(&self, name: &str) -> Option<&Property> {
695        self.properties.iter().find(|p| p.name == name)
696    }
697
698    /// Sets a new set of properties of the node.
699    #[inline]
700    pub fn set_properties(&mut self, properties: Vec<Property>) -> Vec<Property> {
701        std::mem::replace(
702            self.properties.get_value_mut_and_mark_modified(),
703            properties,
704        )
705    }
706
707    /// Sets lifetime of node in seconds, lifetime is useful for temporary objects.
708    /// Example - you firing a gun, it produces two particle systems for each shot:
709    /// one for gunpowder fumes and one when bullet hits some surface. These particle
710    /// systems won't last very long - usually they will disappear in 1-2 seconds
711    /// but nodes will still be in scene consuming precious CPU clocks. This is where
712    /// lifetimes become handy - you just set appropriate lifetime for a particle
713    /// system node and it will be removed from scene when time will end. This is
714    /// efficient algorithm because scene holds every object in pool and allocation
715    /// or deallocation of node takes very little amount of time.
716    #[inline]
717    pub fn set_lifetime(&mut self, time_seconds: Option<f32>) -> &mut Self {
718        self.lifetime.set_value_and_mark_modified(time_seconds);
719        self
720    }
721
722    /// Returns current lifetime of a node. Will be None if node has undefined lifetime.
723    /// For more info about lifetimes see [`set_lifetime`](Self::set_lifetime).
724    #[inline]
725    pub fn lifetime(&self) -> Option<f32> {
726        *self.lifetime
727    }
728
729    /// Returns handle of parent node.
730    #[inline]
731    pub fn parent(&self) -> Handle<Node> {
732        self.parent
733    }
734
735    /// Returns slice of handles to children nodes. This can be used, for example, to
736    /// traverse tree starting from some node.
737    #[inline]
738    pub fn children(&self) -> &[Handle<Node>] {
739        self.children.as_slice()
740    }
741
742    /// Returns global transform matrix, such matrix contains combined transformation
743    /// of transforms of parent nodes. This is the final matrix that describes real
744    /// location of object in the world.
745    #[inline]
746    pub fn global_transform(&self) -> Matrix4<f32> {
747        self.global_transform.get()
748    }
749
750    /// Calculates global transform of the node, but discards scaling part of it.
751    #[inline]
752    pub fn global_transform_without_scaling(&self) -> Matrix4<f32> {
753        const EPSILON: f32 = 10.0 * f32::EPSILON;
754        let basis = self.global_transform().basis();
755        let rotation = UnitQuaternion::from_matrix_eps(&basis, EPSILON, 16, Default::default());
756        Matrix4::new_translation(&self.global_position()) * rotation.to_homogeneous()
757    }
758
759    /// Returns inverse of bind pose matrix. Bind pose matrix - is special matrix
760    /// for bone nodes, it stores initial transform of bone node at the moment
761    /// of "binding" vertices to bones.
762    #[inline]
763    pub fn inv_bind_pose_transform(&self) -> Matrix4<f32> {
764        self.inv_bind_pose_transform
765    }
766
767    /// Returns true if this node is model resource instance root node.
768    #[inline]
769    pub fn is_resource_instance_root(&self) -> bool {
770        self.is_resource_instance_root
771    }
772
773    /// Returns resource from which this node was instantiated from.
774    #[inline]
775    pub fn resource(&self) -> Option<ModelResource> {
776        self.resource.clone()
777    }
778
779    /// Sets local visibility of a node.
780    #[inline]
781    pub fn set_visibility(&mut self, visibility: bool) -> bool {
782        self.visibility.set_value_and_mark_modified(visibility)
783    }
784
785    /// Returns local visibility of a node.
786    #[inline]
787    pub fn visibility(&self) -> bool {
788        *self.visibility.property
789    }
790
791    /// Returns current **local-space** bounding box. Keep in mind that this value is just
792    /// a placeholder, because there is not information to calculate actual bounding box.
793    #[inline]
794    pub fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
795        AxisAlignedBoundingBox::unit()
796    }
797
798    /// Returns current **world-space** bounding box.
799    #[inline]
800    pub fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
801        self.local_bounding_box()
802            .transform(&self.global_transform())
803    }
804
805    /// Set new mobility for the node. See [`Mobility`] docs for more info.
806    #[inline]
807    pub fn set_mobility(&mut self, mobility: Mobility) -> Mobility {
808        self.mobility.set_value_and_mark_modified(mobility)
809    }
810
811    /// Return current mobility of the node.
812    #[inline]
813    pub fn mobility(&self) -> Mobility {
814        *self.mobility
815    }
816
817    /// Returns combined visibility of an node. This is the final visibility of a node. Global visibility calculated
818    /// using visibility of all parent nodes until root one, so if some parent node upper on tree is invisible then
819    /// all its children will be invisible. It defines if object will be rendered. It is *not* the same as real
820    /// visibility from point of view of a camera. Use frustum-box intersection test instead.
821    #[inline]
822    pub fn global_visibility(&self) -> bool {
823        self.global_visibility.get()
824    }
825
826    /// Handle to node in scene of model resource from which this node was instantiated from.
827    ///
828    /// # Notes
829    ///
830    /// This handle is extensively used to fetch information about the state of node in the resource
831    /// to sync properties of instance with its original in the resource.
832    #[inline]
833    pub fn original_handle_in_resource(&self) -> Handle<Node> {
834        self.original_handle_in_resource
835    }
836
837    /// Returns `true` if the node has a parent object in a resource from which it may restore
838    /// values of its inheritable properties.
839    #[inline]
840    pub fn has_inheritance_parent(&self) -> bool {
841        self.original_handle_in_resource.is_some() && self.resource.is_some()
842    }
843
844    /// Returns position of the node in absolute coordinates.
845    #[inline]
846    pub fn global_position(&self) -> Vector3<f32> {
847        self.global_transform.get().position()
848    }
849
850    /// Returns "look" vector of global transform basis, in most cases return vector will be non-normalized.
851    #[inline]
852    pub fn look_vector(&self) -> Vector3<f32> {
853        self.global_transform.get().look()
854    }
855
856    /// Returns "side" vector of global transform basis, in most cases return vector will be non-normalized.
857    #[inline]
858    pub fn side_vector(&self) -> Vector3<f32> {
859        self.global_transform.get().side()
860    }
861
862    /// Returns "up" vector of global transform basis, in most cases return vector will be non-normalized.
863    #[inline]
864    pub fn up_vector(&self) -> Vector3<f32> {
865        self.global_transform.get().up()
866    }
867
868    /// Sets new lod group.
869    #[inline]
870    pub fn set_lod_group(&mut self, lod_group: Option<LodGroup>) -> Option<LodGroup> {
871        std::mem::replace(self.lod_group.get_value_mut_and_mark_modified(), lod_group)
872    }
873
874    /// Extracts lod group, leaving None in the node.
875    #[inline]
876    pub fn take_lod_group(&mut self) -> Option<LodGroup> {
877        std::mem::take(self.lod_group.get_value_mut_and_mark_modified())
878    }
879
880    /// Returns shared reference to current lod group.
881    #[inline]
882    pub fn lod_group(&self) -> Option<&LodGroup> {
883        self.lod_group.as_ref()
884    }
885
886    /// Returns mutable reference to current lod group.
887    #[inline]
888    pub fn lod_group_mut(&mut self) -> Option<&mut LodGroup> {
889        self.lod_group.get_value_mut_and_mark_modified().as_mut()
890    }
891
892    /// Returns node tag.
893    #[inline]
894    pub fn tag(&self) -> &str {
895        &self.tag
896    }
897
898    /// Returns a copy of node tag.
899    #[inline]
900    pub fn tag_owned(&self) -> String {
901        (*self.tag).clone()
902    }
903
904    /// Sets new tag.
905    #[inline]
906    pub fn set_tag(&mut self, tag: String) -> String {
907        self.tag.set_value_and_mark_modified(tag)
908    }
909
910    /// Return the frustum_culling flag
911    #[inline]
912    pub fn frustum_culling(&self) -> bool {
913        *self.frustum_culling
914    }
915
916    /// Sets whether to use frustum culling or not
917    #[inline]
918    pub fn set_frustum_culling(&mut self, frustum_culling: bool) -> bool {
919        self.frustum_culling
920            .set_value_and_mark_modified(frustum_culling)
921    }
922
923    /// Returns true if the node should cast shadows, false - otherwise.
924    #[inline]
925    pub fn cast_shadows(&self) -> bool {
926        *self.cast_shadows
927    }
928
929    /// Sets whether the mesh should cast shadows or not.
930    #[inline]
931    pub fn set_cast_shadows(&mut self, cast_shadows: bool) -> bool {
932        self.cast_shadows.set_value_and_mark_modified(cast_shadows)
933    }
934
935    /// Returns current instance id.
936    pub fn instance_id(&self) -> SceneNodeId {
937        self.instance_id
938    }
939
940    /// Removes a script with the given `index` from the scene node. The script will be destroyed
941    /// in either the current update tick (if it was removed from some other script) or in the next
942    /// update tick of the parent graph.
943    pub fn remove_script(&mut self, index: usize) {
944        // Send script to the graph to destroy script instances correctly.
945        if let Some(entry) = self.scripts.get_mut(index) {
946            entry.should_be_deleted = true;
947
948            // We might be in a middle of a script method execution, where script is temporarily
949            // extracted from the array.
950            if let Some(script) = entry.take() {
951                if let Some(sender) = self.script_message_sender.as_ref() {
952                    Log::verify(sender.send(NodeScriptMessage::DestroyScript {
953                        script,
954                        handle: self.self_handle,
955                        script_index: index,
956                    }));
957                } else {
958                    Log::warn(format!(
959                        "There is a script instance on a node {}, but no message sender. \
960                    The script won't be correctly destroyed!",
961                        self.name(),
962                    ));
963                }
964            }
965        }
966    }
967
968    /// Removes all assigned scripts from the scene node. The scripts will be removed from
969    /// first-to-last order an their actual destruction will happen either on the current update tick
970    /// of the parent graph (if it was removed from some other script) or in the next update tick.
971    pub fn remove_all_scripts(&mut self) {
972        let script_count = self.scripts.len();
973        for i in 0..script_count {
974            self.remove_script(i);
975        }
976    }
977
978    /// Sets a new script for the scene node by index. Previous script will be removed (see
979    /// [`Self::remove_script`] docs for more info).
980    #[inline]
981    pub fn replace_script(&mut self, index: usize, script: Option<Script>) {
982        self.remove_script(index);
983
984        if let Some(entry) = self.scripts.get_mut(index) {
985            entry.script = script;
986            if let Some(sender) = self.script_message_sender.as_ref() {
987                if entry.script.is_some() {
988                    Log::verify(sender.send(NodeScriptMessage::InitializeScript {
989                        handle: self.self_handle,
990                        script_index: index,
991                    }));
992                }
993            }
994        }
995    }
996
997    /// Adds a new script to the scene node. The new script will be initialized either in the current
998    /// update tick (if the script was added in one of the [`ScriptTrait`] methods) or on the next
999    /// update tick.
1000    #[inline]
1001    pub fn add_script<T>(&mut self, script: T)
1002    where
1003        T: ScriptTrait,
1004    {
1005        let script_index = self.scripts.len();
1006        self.scripts.push(ScriptRecord::new(Script::new(script)));
1007        if let Some(sender) = self.script_message_sender.as_ref() {
1008            Log::verify(sender.send(NodeScriptMessage::InitializeScript {
1009                handle: self.self_handle,
1010                script_index,
1011            }));
1012        }
1013    }
1014
1015    /// Checks if the node has a script of a particular type. Returns `false` if there is no such
1016    /// script.
1017    #[inline]
1018    pub fn has_script<T>(&self) -> bool
1019    where
1020        T: ScriptTrait,
1021    {
1022        self.try_get_script::<T>().is_some()
1023    }
1024
1025    /// Checks if the node has any scripts assigned.
1026    #[inline]
1027    pub fn has_scripts_assigned(&self) -> bool {
1028        self.scripts.iter().any(|script| script.is_some())
1029    }
1030
1031    /// Tries to find a **first** script of the given type `T`, returns `None` if there's no such
1032    /// script.
1033    #[inline]
1034    pub fn try_get_script<T>(&self) -> Option<&T>
1035    where
1036        T: ScriptTrait,
1037    {
1038        self.scripts
1039            .iter()
1040            .find_map(|s| s.as_ref().and_then(|s| s.cast::<T>()))
1041    }
1042
1043    /// Returns an iterator that yields references to the scripts of the given type `T`.
1044    #[inline]
1045    pub fn try_get_scripts<T>(&self) -> impl Iterator<Item = &T>
1046    where
1047        T: ScriptTrait,
1048    {
1049        self.scripts
1050            .iter()
1051            .filter_map(|e| e.script.as_ref().and_then(|s| s.cast::<T>()))
1052    }
1053
1054    /// Tries to find a **first** script of the given type `T`, returns `None` if there's no such
1055    /// script.
1056    #[inline]
1057    pub fn try_get_script_mut<T>(&mut self) -> Option<&mut T>
1058    where
1059        T: ScriptTrait,
1060    {
1061        self.scripts
1062            .iter_mut()
1063            .find_map(|s| s.as_mut().and_then(|s| s.cast_mut::<T>()))
1064    }
1065
1066    /// Returns an iterator that yields references to the scripts of the given type `T`.
1067    #[inline]
1068    pub fn try_get_scripts_mut<T>(&mut self) -> impl Iterator<Item = &mut T>
1069    where
1070        T: ScriptTrait,
1071    {
1072        self.scripts
1073            .iter_mut()
1074            .filter_map(|e| e.script.as_mut().and_then(|s| s.cast_mut::<T>()))
1075    }
1076
1077    /// Tries find a component of the given type `C` across **all** available scripts of the node.
1078    /// If you want to search a component `C` in a particular script, then use [`Self::try_get_script`]
1079    /// and then search for component in it.
1080    #[inline]
1081    pub fn try_get_script_component<C>(&self) -> Option<&C>
1082    where
1083        C: Any,
1084    {
1085        self.scripts
1086            .iter()
1087            .find_map(|s| s.as_ref().and_then(|s| s.query_component_ref::<C>()))
1088    }
1089
1090    /// Tries find a component of the given type `C` across **all** available scripts of the node.
1091    /// If you want to search a component `C` in a particular script, then use [`Self::try_get_script`]
1092    /// and then search for component in it.
1093    #[inline]
1094    pub fn try_get_script_component_mut<C>(&mut self) -> Option<&mut C>
1095    where
1096        C: Any,
1097    {
1098        self.scripts
1099            .iter_mut()
1100            .find_map(|s| s.as_mut().and_then(|s| s.query_component_mut::<C>()))
1101    }
1102
1103    /// Returns total count of scripts assigned to the node.
1104    #[inline]
1105    pub fn script_count(&self) -> usize {
1106        self.scripts.len()
1107    }
1108
1109    /// Returns a shared reference to a script instance with the given `index`. This method will
1110    /// return [`None`] if the `index` is out of bounds or the script is temporarily not available.
1111    /// This could happen if this method was called from some method of a [`ScriptTrait`]. It
1112    /// happens because of borrowing rules - you cannot take another reference to a script that is
1113    /// already mutably borrowed.
1114    #[inline]
1115    pub fn script(&self, index: usize) -> Option<&Script> {
1116        self.scripts.get(index).and_then(|s| s.as_ref())
1117    }
1118
1119    /// Returns an iterator that yields all assigned scripts.
1120    #[inline]
1121    pub fn scripts(&self) -> impl Iterator<Item = &Script> {
1122        self.scripts.iter().filter_map(|s| s.as_ref())
1123    }
1124
1125    /// Returns a mutable reference to a script instance with the given `index`. This method will
1126    /// return [`None`] if the `index` is out of bounds or the script is temporarily not available.
1127    /// This could happen if this method was called from some method of a [`ScriptTrait`]. It
1128    /// happens because of borrowing rules - you cannot take another reference to a script that is
1129    /// already mutably borrowed.
1130    ///
1131    /// # Important notes
1132    ///
1133    /// Do **not** replace script instance using mutable reference given to you by this method.
1134    /// This will prevent correct script de-initialization! Use [`Self::replace_script`] if you need
1135    /// to replace the script.
1136    #[inline]
1137    pub fn script_mut(&mut self, index: usize) -> Option<&mut Script> {
1138        self.scripts.get_mut(index).and_then(|s| s.as_mut())
1139    }
1140
1141    /// Returns an iterator that yields all assigned scripts.
1142    #[inline]
1143    pub fn scripts_mut(&mut self) -> impl Iterator<Item = &mut Script> {
1144        self.scripts.iter_mut().filter_map(|s| s.as_mut())
1145    }
1146
1147    /// Enables or disables scene node. Disabled scene nodes won't be updated (including scripts) or rendered.
1148    ///
1149    /// # Important notes
1150    ///
1151    /// Enabled/disabled state will affect children nodes. It means that if you have a node with children nodes,
1152    /// and you disable the node, all children nodes will be disabled too even if their [`Self::is_enabled`] method
1153    /// returns `true`.
1154    #[inline]
1155    pub fn set_enabled(&mut self, enabled: bool) -> bool {
1156        self.enabled.set_value_and_mark_modified(enabled)
1157    }
1158
1159    /// Returns `true` if the node is enabled, `false` - otherwise. The return value does **not** include the state
1160    /// of parent nodes. It should be considered as "local" enabled flag. To get actual enabled state, that includes
1161    /// the state of parent nodes, use [`Self::is_globally_enabled`] method.
1162    #[inline]
1163    pub fn is_enabled(&self) -> bool {
1164        *self.enabled.property
1165    }
1166
1167    /// Returns `true` if the node and every parent up in hierarchy is enabled, `false` - otherwise. This method
1168    /// returns "true" `enabled` flag. Its value could be different from the value returned by [`Self::is_enabled`].
1169    #[inline]
1170    pub fn is_globally_enabled(&self) -> bool {
1171        self.global_enabled.get()
1172    }
1173
1174    /// Returns a root resource of the scene node. This method crawls up on dependency tree until it finds that
1175    /// the ancestor node does not have any dependencies and returns this resource as the root resource. For
1176    /// example, in case of simple scene node instance, this method will return the resource from which the node
1177    /// was instantiated from. In case of 2 or more levels of dependency, it will always return the "top"
1178    /// dependency in the dependency graph.
1179    #[inline]
1180    pub fn root_resource(&self) -> Option<ModelResource> {
1181        if let Some(resource) = self.resource.as_ref() {
1182            let mut state = resource.state();
1183            if let Some(model) = state.data() {
1184                if let Ok(ancestor_node) = model
1185                    .get_scene()
1186                    .graph
1187                    .try_get_node(self.original_handle_in_resource)
1188                {
1189                    return if ancestor_node.resource.is_none() {
1190                        Some(resource.clone())
1191                    } else {
1192                        ancestor_node.root_resource()
1193                    };
1194                }
1195            }
1196        }
1197        None
1198    }
1199}
1200
1201impl Default for Base {
1202    fn default() -> Self {
1203        BaseBuilder::new().build_base()
1204    }
1205}
1206
1207// Serializes Option<Script> using given serializer.
1208pub(crate) fn visit_opt_script(
1209    name: &str,
1210    script: &mut Option<Script>,
1211    visitor: &mut Visitor,
1212) -> VisitResult {
1213    let mut region = visitor.enter_region(name)?;
1214
1215    let mut script_type_uuid = script.as_ref().map(|s| s.id()).unwrap_or_default();
1216    script_type_uuid.visit("TypeUuid", &mut region)?;
1217
1218    if region.is_reading() {
1219        *script = if script_type_uuid.is_nil() {
1220            None
1221        } else {
1222            let serialization_context = region
1223                .blackboard
1224                .get::<SerializationContext>()
1225                .expect("Visitor blackboard must contain serialization context!");
1226
1227            Some(
1228                serialization_context
1229                    .script_constructors
1230                    .try_create(&script_type_uuid)
1231                    .ok_or_else(|| {
1232                        VisitError::User(format!(
1233                            "There is no corresponding script constructor for {script_type_uuid} type!"
1234                        ))
1235                    })?,
1236            )
1237        };
1238    }
1239
1240    if let Some(script) = script {
1241        script.visit("ScriptData", &mut region)?;
1242    }
1243
1244    Ok(())
1245}
1246
1247impl Visit for Base {
1248    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
1249        let mut region = visitor.enter_region(name)?;
1250
1251        self.name.visit("Name", &mut region)?;
1252        self.local_transform.visit("Transform", &mut region)?;
1253        self.visibility.visit("Visibility", &mut region)?;
1254        self.parent.visit("Parent", &mut region)?;
1255        self.children.visit("Children", &mut region)?;
1256        self.resource.visit("Resource", &mut region)?;
1257        self.is_resource_instance_root
1258            .visit("IsResourceInstance", &mut region)?;
1259        self.lifetime.visit("Lifetime", &mut region)?;
1260        self.lod_group.visit("LodGroup", &mut region)?;
1261        self.mobility.visit("Mobility", &mut region)?;
1262        self.original_handle_in_resource
1263            .visit("Original", &mut region)?;
1264        self.tag.visit("Tag", &mut region)?;
1265        self.properties.visit("Properties", &mut region)?;
1266        self.frustum_culling.visit("FrustumCulling", &mut region)?;
1267        self.cast_shadows.visit("CastShadows", &mut region)?;
1268        self.instance_id.visit("InstanceId", &mut region)?;
1269        self.enabled.visit("Enabled", &mut region)?;
1270        self.render_mask.visit("RenderMask", &mut region)?;
1271
1272        // Script visiting may fail for various reasons:
1273        //
1274        // 1) Data inside a script is not compatible with latest code (there is no backward
1275        //    compatibility for the data)
1276        // 2) Script was removed in the game.
1277        //
1278        // None of the reasons are fatal and we should still give an ability to load such node
1279        // to edit or remove it.
1280
1281        let _ = self.scripts.visit("Scripts", &mut region);
1282
1283        Ok(())
1284    }
1285}
1286
1287/// Base node builder allows you to create nodes in declarative manner.
1288pub struct BaseBuilder {
1289    name: String,
1290    visibility: bool,
1291    local_transform: Transform,
1292    children: Vec<Handle<Node>>,
1293    lifetime: Option<f32>,
1294    lod_group: Option<LodGroup>,
1295    mobility: Mobility,
1296    inv_bind_pose_transform: Matrix4<f32>,
1297    tag: String,
1298    frustum_culling: bool,
1299    cast_shadows: bool,
1300    scripts: Vec<ScriptRecord>,
1301    instance_id: SceneNodeId,
1302    enabled: bool,
1303}
1304
1305impl Default for BaseBuilder {
1306    fn default() -> Self {
1307        Self::new()
1308    }
1309}
1310
1311impl BaseBuilder {
1312    /// Creates new builder instance.
1313    #[inline]
1314    pub fn new() -> Self {
1315        Self {
1316            name: Default::default(),
1317            visibility: true,
1318            local_transform: Default::default(),
1319            children: Default::default(),
1320            lifetime: None,
1321            lod_group: None,
1322            mobility: Default::default(),
1323            inv_bind_pose_transform: Matrix4::identity(),
1324            tag: Default::default(),
1325            frustum_culling: true,
1326            cast_shadows: true,
1327            scripts: vec![],
1328            instance_id: SceneNodeId(Uuid::new_v4()),
1329            enabled: true,
1330        }
1331    }
1332
1333    /// Sets desired mobility.
1334    #[inline]
1335    pub fn with_mobility(mut self, mobility: Mobility) -> Self {
1336        self.mobility = mobility;
1337        self
1338    }
1339
1340    /// Sets desired name.
1341    #[inline]
1342    pub fn with_name<P: AsRef<str>>(mut self, name: P) -> Self {
1343        name.as_ref().clone_into(&mut self.name);
1344        self
1345    }
1346
1347    /// Sets desired visibility.
1348    #[inline]
1349    pub fn with_visibility(mut self, visibility: bool) -> Self {
1350        self.visibility = visibility;
1351        self
1352    }
1353
1354    /// Sets desired local transform.
1355    #[inline]
1356    pub fn with_local_transform(mut self, transform: Transform) -> Self {
1357        self.local_transform = transform;
1358        self
1359    }
1360
1361    /// Sets desired inverse bind pose transform.
1362    #[inline]
1363    pub fn with_inv_bind_pose_transform(mut self, inv_bind_pose: Matrix4<f32>) -> Self {
1364        self.inv_bind_pose_transform = inv_bind_pose;
1365        self
1366    }
1367
1368    /// Enables or disables the scene node.
1369    pub fn with_enabled(mut self, enabled: bool) -> Self {
1370        self.enabled = enabled;
1371        self
1372    }
1373
1374    /// Adds a new child to the node. If the given handle is [`Handle::NONE`], then the handle will
1375    /// be ignored.
1376    pub fn with_child(mut self, handle: Handle<impl ObjectOrVariant<Node>>) -> Self {
1377        if handle.is_some() {
1378            self.children.push(handle.to_base())
1379        }
1380        self
1381    }
1382
1383    /// Sets desired lifetime.
1384    #[inline]
1385    pub fn with_lifetime(mut self, time_seconds: f32) -> Self {
1386        self.lifetime = Some(time_seconds);
1387        self
1388    }
1389
1390    /// Sets desired lod group.
1391    #[inline]
1392    pub fn with_lod_group(mut self, lod_group: LodGroup) -> Self {
1393        self.lod_group = Some(lod_group);
1394        self
1395    }
1396
1397    /// Sets desired tag.
1398    #[inline]
1399    pub fn with_tag(mut self, tag: String) -> Self {
1400        self.tag = tag;
1401        self
1402    }
1403
1404    /// Sets desired frustum_culling flag.
1405    #[inline]
1406    pub fn with_frustum_culling(mut self, frustum_culling: bool) -> Self {
1407        self.frustum_culling = frustum_culling;
1408        self
1409    }
1410
1411    /// Sets whether mesh should cast shadows or not.
1412    #[inline]
1413    pub fn with_cast_shadows(mut self, cast_shadows: bool) -> Self {
1414        self.cast_shadows = cast_shadows;
1415        self
1416    }
1417
1418    /// Sets script of the node.
1419    #[inline]
1420    pub fn with_script<T>(mut self, script: T) -> Self
1421    where
1422        T: ScriptTrait,
1423    {
1424        self.scripts.push(ScriptRecord::new(Script::new(script)));
1425        self
1426    }
1427
1428    /// Sets new instance id.
1429    pub fn with_instance_id(mut self, id: SceneNodeId) -> Self {
1430        self.instance_id = id;
1431        self
1432    }
1433
1434    /// Creates an instance of [`Base`].
1435    #[inline]
1436    pub fn build_base(self) -> Base {
1437        Base {
1438            self_handle: Default::default(),
1439            script_message_sender: None,
1440            message_sender: None,
1441            name: self.name.into(),
1442            children: self.children,
1443            local_transform: TrackedProperty::unbound(
1444                self.local_transform,
1445                NodeMessageKind::TransformChanged,
1446            ),
1447            lifetime: self.lifetime.into(),
1448            visibility: TrackedProperty::unbound(
1449                self.visibility.into(),
1450                NodeMessageKind::VisibilityChanged,
1451            ),
1452            enabled: TrackedProperty::unbound(
1453                self.enabled.into(),
1454                NodeMessageKind::EnabledFlagChanged,
1455            ),
1456            render_mask: BitMask::all().into(),
1457            global_visibility: Cell::new(true),
1458            parent: Handle::NONE,
1459            global_transform: Cell::new(Matrix4::identity()),
1460            inv_bind_pose_transform: self.inv_bind_pose_transform,
1461            resource: None,
1462            original_handle_in_resource: Handle::NONE,
1463            is_resource_instance_root: false,
1464            lod_group: self.lod_group.into(),
1465            mobility: self.mobility.into(),
1466            tag: self.tag.into(),
1467            properties: Default::default(),
1468            frustum_culling: self.frustum_culling.into(),
1469            cast_shadows: self.cast_shadows.into(),
1470            scripts: self.scripts,
1471            instance_id: SceneNodeId(Uuid::new_v4()),
1472
1473            global_enabled: Cell::new(true),
1474        }
1475    }
1476}