use super::collider::BitMask;
use crate::{
core::{
algebra::{Matrix4, Vector3},
log::Log,
math::{aabb::AxisAlignedBoundingBox, Matrix4Ext},
pool::{ErasedHandle, Handle},
reflect::prelude::*,
type_traits::prelude::*,
variable::InheritableVariable,
visitor::{Visit, VisitResult, Visitor},
ImmutableString,
},
engine::SerializationContext,
graph::SceneGraph,
resource::model::ModelResource,
scene::{node::Node, transform::Transform},
script::{Script, ScriptTrait},
};
use fyrox_core::algebra::UnitQuaternion;
use fyrox_core::pool::ObjectOrVariant;
use fyrox_core::visitor::error::VisitError;
use serde::{Deserialize, Serialize};
use std::{
any::Any,
cell::Cell,
ops::{Deref, DerefMut},
sync::mpsc::Sender,
};
use strum_macros::{AsRefStr, EnumString, VariantNames};
#[derive(Debug, Default, Clone, Visit, Reflect, PartialEq, TypeUuidProvider)]
#[type_uuid(id = "576b31a2-2b39-4c79-95dd-26aeaf381d8b")]
pub struct LevelOfDetail {
begin: f32,
end: f32,
pub objects: Vec<Handle<Node>>,
}
impl LevelOfDetail {
pub fn new(begin: f32, end: f32, objects: Vec<Handle<Node>>) -> Self {
for object in objects.iter() {
assert!(object.is_some());
}
let begin = begin.min(end);
let end = end.max(begin);
Self {
begin: begin.clamp(0.0, 1.0),
end: end.clamp(0.0, 1.0),
objects,
}
}
pub fn set_begin(&mut self, percent: f32) {
self.begin = percent.clamp(0.0, 1.0);
if self.begin > self.end {
std::mem::swap(&mut self.begin, &mut self.end);
}
}
pub fn begin(&self) -> f32 {
self.begin
}
pub fn set_end(&mut self, percent: f32) {
self.end = percent.clamp(0.0, 1.0);
if self.end < self.begin {
std::mem::swap(&mut self.begin, &mut self.end);
}
}
pub fn end(&self) -> f32 {
self.end
}
}
#[derive(Debug, Default, Clone, Visit, Reflect, PartialEq, TypeUuidProvider)]
#[type_uuid(id = "8e7b18b1-c1e0-47d7-b952-4394c1d049e5")]
pub struct LodGroup {
pub levels: Vec<LevelOfDetail>,
}
#[derive(
Debug, Visit, Reflect, PartialEq, Clone, AsRefStr, EnumString, VariantNames, TypeUuidProvider,
)]
#[type_uuid(id = "cce94b60-a57e-48ba-b6f4-e5e84788f7f8")]
pub enum PropertyValue {
NodeHandle(Handle<Node>),
Handle(ErasedHandle),
String(String),
I64(i64),
U64(u64),
I32(i32),
U32(u32),
I16(i16),
U16(u16),
I8(i8),
U8(u8),
F32(f32),
F64(f64),
}
impl Default for PropertyValue {
fn default() -> Self {
Self::I8(0)
}
}
#[derive(Debug, Visit, Reflect, Default, Clone, PartialEq, TypeUuidProvider)]
#[type_uuid(id = "fc87fd21-a5e6-40d5-a79d-19f96b25d6c9")]
pub struct Property {
pub name: String,
pub value: PropertyValue,
}
pub enum NodeScriptMessage {
InitializeScript {
handle: Handle<Node>,
script_index: usize,
},
DestroyScript {
script: Script,
handle: Handle<Node>,
script_index: usize,
},
}
#[derive(
Clone,
Copy,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Default,
Debug,
Reflect,
Serialize,
Deserialize,
)]
#[repr(transparent)]
pub struct SceneNodeId(pub Uuid);
impl Visit for SceneNodeId {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.0.visit(name, visitor)
}
}
#[derive(Clone, Reflect, Debug, Default, TypeUuidProvider)]
#[type_uuid(id = "51bc577b-5a50-4a97-9b31-eda2f3d46c9d")]
pub struct ScriptRecord {
pub(crate) script: Option<Script>,
#[reflect(hidden)]
pub(crate) should_be_deleted: bool,
}
impl ScriptRecord {
pub(crate) fn new(script: Script) -> Self {
Self {
script: Some(script),
should_be_deleted: false,
}
}
}
impl Deref for ScriptRecord {
type Target = Option<Script>;
fn deref(&self) -> &Self::Target {
&self.script
}
}
impl DerefMut for ScriptRecord {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.script
}
}
impl Visit for ScriptRecord {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visit_opt_script(name, &mut self.script, visitor)
}
}
#[allow(clippy::enum_variant_names)] #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
pub(crate) enum NodeMessageKind {
TransformChanged,
VisibilityChanged,
EnabledFlagChanged,
}
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
pub(crate) struct NodeMessage {
pub node: Handle<Node>,
pub kind: NodeMessageKind,
}
impl NodeMessage {
pub fn new(node: Handle<Node>, kind: NodeMessageKind) -> Self {
Self { node, kind }
}
}
#[derive(Clone, Debug)]
struct TrackedProperty<T> {
property: T,
node_message_kind: NodeMessageKind,
node_handle: Handle<Node>,
sender: Option<Sender<NodeMessage>>,
}
impl<T> TrackedProperty<T> {
fn unbound(property: T, kind: NodeMessageKind) -> Self {
Self {
property,
node_message_kind: kind,
node_handle: Default::default(),
sender: None,
}
}
fn set_message_data(&mut self, sender: Sender<NodeMessage>, node_handle: Handle<Node>) {
self.sender = Some(sender);
self.node_handle = node_handle;
}
}
impl<T: Visit> Visit for TrackedProperty<T> {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.property.visit(name, visitor)
}
}
impl<T> Deref for TrackedProperty<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.property
}
}
impl<T> DerefMut for TrackedProperty<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
if let Some(sender) = self.sender.as_ref() {
Log::verify(sender.send(NodeMessage {
node: self.node_handle,
kind: self.node_message_kind,
}))
}
&mut self.property
}
}
#[derive(Debug, Reflect, Clone)]
pub struct Base {
#[reflect(hidden)]
self_handle: Handle<Node>,
#[reflect(hidden)]
script_message_sender: Option<Sender<NodeScriptMessage>>,
#[reflect(hidden)]
message_sender: Option<Sender<NodeMessage>>,
#[reflect(setter = "set_name_internal")]
pub(crate) name: ImmutableString,
#[reflect(deref)]
local_transform: TrackedProperty<Transform>,
#[reflect(deref)]
visibility: TrackedProperty<InheritableVariable<bool>>,
#[reflect(deref)]
enabled: TrackedProperty<InheritableVariable<bool>>,
pub render_mask: InheritableVariable<BitMask>,
pub(crate) lifetime: InheritableVariable<Option<f32>>,
#[reflect(setter = "set_lod_group")]
lod_group: InheritableVariable<Option<LodGroup>>,
#[reflect(setter = "set_tag")]
tag: InheritableVariable<String>,
#[reflect(setter = "set_cast_shadows")]
cast_shadows: InheritableVariable<bool>,
#[reflect(setter = "set_properties")]
pub properties: InheritableVariable<Vec<Property>>,
#[reflect(setter = "set_frustum_culling")]
frustum_culling: InheritableVariable<bool>,
#[reflect(read_only)]
pub(crate) is_resource_instance_root: bool,
#[reflect(read_only)]
pub(crate) global_visibility: Cell<bool>,
#[reflect(hidden)]
pub(crate) parent: Handle<Node>,
#[reflect(hidden)]
pub(crate) children: Vec<Handle<Node>>,
#[reflect(read_only)]
pub(crate) global_transform: Cell<Matrix4<f32>>,
#[reflect(hidden)]
pub(crate) inv_bind_pose_transform: Matrix4<f32>,
#[reflect(read_only)]
pub(crate) resource: Option<ModelResource>,
#[reflect(read_only)]
#[reflect(hidden)]
pub(crate) original_handle_in_resource: Handle<Node>,
pub(crate) instance_id: SceneNodeId,
pub(crate) scripts: Vec<ScriptRecord>,
#[reflect(read_only)]
pub(crate) global_enabled: Cell<bool>,
}
impl Drop for Base {
fn drop(&mut self) {
self.remove_all_scripts();
}
}
impl Base {
#[inline]
pub fn handle(&self) -> Handle<Node> {
self.self_handle
}
#[inline]
pub fn set_name<N: AsRef<str>>(&mut self, name: N) {
self.set_name_internal(ImmutableString::new(name));
}
fn set_name_internal(&mut self, name: ImmutableString) -> ImmutableString {
std::mem::replace(&mut self.name, name)
}
#[inline]
pub fn name(&self) -> &str {
self.name.as_str()
}
#[inline]
pub fn name_owned(&self) -> String {
self.name.to_mutable()
}
pub(crate) fn on_connected_to_graph(
&mut self,
self_handle: Handle<Node>,
message_sender: Sender<NodeMessage>,
script_message_sender: Sender<NodeScriptMessage>,
) {
self.self_handle = self_handle;
self.message_sender = Some(message_sender.clone());
self.script_message_sender = Some(script_message_sender);
self.local_transform
.set_message_data(message_sender.clone(), self_handle);
self.visibility
.set_message_data(message_sender.clone(), self_handle);
self.enabled.set_message_data(message_sender, self_handle);
self.notify(self.self_handle, NodeMessageKind::TransformChanged);
self.notify(self.self_handle, NodeMessageKind::VisibilityChanged);
self.notify(self.self_handle, NodeMessageKind::EnabledFlagChanged);
}
fn notify(&self, node: Handle<Node>, kind: NodeMessageKind) {
let Some(sender) = self.message_sender.as_ref() else {
return;
};
Log::verify(sender.send(NodeMessage::new(node, kind)));
}
#[inline]
pub fn local_transform(&self) -> &Transform {
&self.local_transform
}
#[inline]
pub fn local_transform_mut(&mut self) -> &mut Transform {
&mut self.local_transform
}
#[inline]
pub fn set_local_transform(&mut self, transform: Transform) {
self.local_transform.property = transform;
self.notify(self.self_handle, NodeMessageKind::TransformChanged);
}
#[inline]
pub fn set_position(&mut self, position: Vector3<f32>) {
self.local_transform_mut().set_position(position);
}
#[inline]
pub fn set_position_xyz(&mut self, x: f32, y: f32, z: f32) {
self.set_position(Vector3::new(x, y, z))
}
#[inline]
pub fn set_rotation(&mut self, rotation: UnitQuaternion<f32>) {
self.local_transform_mut().set_rotation(rotation);
}
#[inline]
pub fn set_rotation_angles(&mut self, roll: f32, pitch: f32, yaw: f32) {
self.set_rotation(UnitQuaternion::from_euler_angles(roll, pitch, yaw))
}
#[inline]
pub fn set_rotation_x(&mut self, angle: f32) {
self.set_rotation(UnitQuaternion::from_axis_angle(&Vector3::x_axis(), angle))
}
#[inline]
pub fn set_rotation_y(&mut self, angle: f32) {
self.set_rotation(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), angle))
}
#[inline]
pub fn set_rotation_z(&mut self, angle: f32) {
self.set_rotation(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), angle))
}
#[inline]
pub fn set_scale(&mut self, scale: Vector3<f32>) {
self.local_transform_mut().set_scale(scale);
}
#[inline]
pub fn set_scale_xyz(&mut self, x: f32, y: f32, z: f32) {
self.set_scale(Vector3::new(x, y, z));
}
#[inline]
pub fn set_uniform_scale(&mut self, scale: f32) {
self.set_scale(Vector3::repeat(scale))
}
#[inline]
pub fn find_properties_ref<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a Property> {
self.properties.iter().filter(move |p| p.name == name)
}
#[inline]
pub fn find_first_property_ref(&self, name: &str) -> Option<&Property> {
self.properties.iter().find(|p| p.name == name)
}
#[inline]
pub fn set_properties(&mut self, properties: Vec<Property>) -> Vec<Property> {
std::mem::replace(
self.properties.get_value_mut_and_mark_modified(),
properties,
)
}
#[inline]
pub fn set_lifetime(&mut self, time_seconds: Option<f32>) -> &mut Self {
self.lifetime.set_value_and_mark_modified(time_seconds);
self
}
#[inline]
pub fn lifetime(&self) -> Option<f32> {
*self.lifetime
}
#[inline]
pub fn parent(&self) -> Handle<Node> {
self.parent
}
#[inline]
pub fn children(&self) -> &[Handle<Node>] {
self.children.as_slice()
}
#[inline]
pub fn global_transform(&self) -> Matrix4<f32> {
self.global_transform.get()
}
#[inline]
pub fn global_transform_without_scaling(&self) -> Matrix4<f32> {
const EPSILON: f32 = 10.0 * f32::EPSILON;
let basis = self.global_transform().basis();
let rotation = UnitQuaternion::from_matrix_eps(&basis, EPSILON, 16, Default::default());
Matrix4::new_translation(&self.global_position()) * rotation.to_homogeneous()
}
#[inline]
pub fn inv_bind_pose_transform(&self) -> Matrix4<f32> {
self.inv_bind_pose_transform
}
#[inline]
pub fn is_resource_instance_root(&self) -> bool {
self.is_resource_instance_root
}
#[inline]
pub fn resource(&self) -> Option<ModelResource> {
self.resource.clone()
}
#[inline]
pub fn set_visibility(&mut self, visibility: bool) -> bool {
self.visibility.set_value_and_mark_modified(visibility)
}
#[inline]
pub fn visibility(&self) -> bool {
*self.visibility.property
}
#[inline]
pub fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
AxisAlignedBoundingBox::unit()
}
#[inline]
pub fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.local_bounding_box()
.transform(&self.global_transform())
}
#[inline]
pub fn global_visibility(&self) -> bool {
self.global_visibility.get()
}
#[inline]
pub fn original_handle_in_resource(&self) -> Handle<Node> {
self.original_handle_in_resource
}
#[inline]
pub fn has_inheritance_parent(&self) -> bool {
self.original_handle_in_resource.is_some() && self.resource.is_some()
}
#[inline]
pub fn global_position(&self) -> Vector3<f32> {
self.global_transform.get().position()
}
#[inline]
pub fn look_vector(&self) -> Vector3<f32> {
self.global_transform.get().look()
}
#[inline]
pub fn side_vector(&self) -> Vector3<f32> {
self.global_transform.get().side()
}
#[inline]
pub fn up_vector(&self) -> Vector3<f32> {
self.global_transform.get().up()
}
#[inline]
pub fn set_lod_group(&mut self, lod_group: Option<LodGroup>) -> Option<LodGroup> {
std::mem::replace(self.lod_group.get_value_mut_and_mark_modified(), lod_group)
}
#[inline]
pub fn take_lod_group(&mut self) -> Option<LodGroup> {
std::mem::take(self.lod_group.get_value_mut_and_mark_modified())
}
#[inline]
pub fn lod_group(&self) -> Option<&LodGroup> {
self.lod_group.as_ref()
}
#[inline]
pub fn lod_group_mut(&mut self) -> Option<&mut LodGroup> {
self.lod_group.get_value_mut_and_mark_modified().as_mut()
}
#[inline]
pub fn tag(&self) -> &str {
&self.tag
}
#[inline]
pub fn tag_owned(&self) -> String {
(*self.tag).clone()
}
#[inline]
pub fn set_tag(&mut self, tag: String) -> String {
self.tag.set_value_and_mark_modified(tag)
}
#[inline]
pub fn frustum_culling(&self) -> bool {
*self.frustum_culling
}
#[inline]
pub fn set_frustum_culling(&mut self, frustum_culling: bool) -> bool {
self.frustum_culling
.set_value_and_mark_modified(frustum_culling)
}
#[inline]
pub fn cast_shadows(&self) -> bool {
*self.cast_shadows
}
#[inline]
pub fn set_cast_shadows(&mut self, cast_shadows: bool) -> bool {
self.cast_shadows.set_value_and_mark_modified(cast_shadows)
}
pub fn instance_id(&self) -> SceneNodeId {
self.instance_id
}
pub fn remove_script(&mut self, index: usize) {
if let Some(entry) = self.scripts.get_mut(index) {
entry.should_be_deleted = true;
if let Some(script) = entry.take() {
if let Some(sender) = self.script_message_sender.as_ref() {
Log::verify(sender.send(NodeScriptMessage::DestroyScript {
script,
handle: self.self_handle,
script_index: index,
}));
} else {
Log::warn(format!(
"There is a script instance on a node {}, but no message sender. \
The script won't be correctly destroyed!",
self.name(),
));
}
}
}
}
pub fn remove_all_scripts(&mut self) {
let script_count = self.scripts.len();
for i in 0..script_count {
self.remove_script(i);
}
}
#[inline]
pub fn replace_script(&mut self, index: usize, script: Option<Script>) {
self.remove_script(index);
if let Some(entry) = self.scripts.get_mut(index) {
entry.script = script;
if let Some(sender) = self.script_message_sender.as_ref() {
if entry.script.is_some() {
Log::verify(sender.send(NodeScriptMessage::InitializeScript {
handle: self.self_handle,
script_index: index,
}));
}
}
}
}
#[inline]
pub fn add_script<T>(&mut self, script: T)
where
T: ScriptTrait,
{
let script_index = self.scripts.len();
self.scripts.push(ScriptRecord::new(Script::new(script)));
if let Some(sender) = self.script_message_sender.as_ref() {
Log::verify(sender.send(NodeScriptMessage::InitializeScript {
handle: self.self_handle,
script_index,
}));
}
}
#[inline]
pub fn has_script<T>(&self) -> bool
where
T: ScriptTrait,
{
self.try_get_script::<T>().is_some()
}
#[inline]
pub fn has_scripts_assigned(&self) -> bool {
self.scripts.iter().any(|script| script.is_some())
}
#[inline]
pub fn try_get_script<T>(&self) -> Option<&T>
where
T: ScriptTrait,
{
self.scripts
.iter()
.find_map(|s| s.as_ref().and_then(|s| s.cast::<T>()))
}
#[inline]
pub fn try_get_scripts<T>(&self) -> impl Iterator<Item = &T>
where
T: ScriptTrait,
{
self.scripts
.iter()
.filter_map(|e| e.script.as_ref().and_then(|s| s.cast::<T>()))
}
#[inline]
pub fn try_get_script_mut<T>(&mut self) -> Option<&mut T>
where
T: ScriptTrait,
{
self.scripts
.iter_mut()
.find_map(|s| s.as_mut().and_then(|s| s.cast_mut::<T>()))
}
#[inline]
pub fn try_get_scripts_mut<T>(&mut self) -> impl Iterator<Item = &mut T>
where
T: ScriptTrait,
{
self.scripts
.iter_mut()
.filter_map(|e| e.script.as_mut().and_then(|s| s.cast_mut::<T>()))
}
#[inline]
pub fn try_get_script_component<C>(&self) -> Option<&C>
where
C: Any,
{
self.scripts
.iter()
.find_map(|s| s.as_ref().and_then(|s| s.query_component_ref::<C>()))
}
#[inline]
pub fn try_get_script_component_mut<C>(&mut self) -> Option<&mut C>
where
C: Any,
{
self.scripts
.iter_mut()
.find_map(|s| s.as_mut().and_then(|s| s.query_component_mut::<C>()))
}
#[inline]
pub fn script_count(&self) -> usize {
self.scripts.len()
}
#[inline]
pub fn script(&self, index: usize) -> Option<&Script> {
self.scripts.get(index).and_then(|s| s.as_ref())
}
#[inline]
pub fn scripts(&self) -> impl Iterator<Item = &Script> {
self.scripts.iter().filter_map(|s| s.as_ref())
}
#[inline]
pub fn script_mut(&mut self, index: usize) -> Option<&mut Script> {
self.scripts.get_mut(index).and_then(|s| s.as_mut())
}
#[inline]
pub fn scripts_mut(&mut self) -> impl Iterator<Item = &mut Script> {
self.scripts.iter_mut().filter_map(|s| s.as_mut())
}
#[inline]
pub fn set_enabled(&mut self, enabled: bool) -> bool {
self.enabled.set_value_and_mark_modified(enabled)
}
#[inline]
pub fn is_enabled(&self) -> bool {
*self.enabled.property
}
#[inline]
pub fn is_globally_enabled(&self) -> bool {
self.global_enabled.get()
}
#[inline]
pub fn root_resource(&self) -> Option<ModelResource> {
if let Some(resource) = self.resource.as_ref() {
let mut state = resource.state();
if let Some(model) = state.data() {
if let Ok(ancestor_node) = model
.get_scene()
.graph
.try_get_node(self.original_handle_in_resource)
{
return if ancestor_node.resource.is_none() {
Some(resource.clone())
} else {
ancestor_node.root_resource()
};
}
}
}
None
}
}
impl Default for Base {
fn default() -> Self {
BaseBuilder::new().build_base()
}
}
pub(crate) fn visit_opt_script(
name: &str,
script: &mut Option<Script>,
visitor: &mut Visitor,
) -> VisitResult {
let mut region = visitor.enter_region(name)?;
let mut script_type_uuid = script.as_ref().map(|s| s.id()).unwrap_or_default();
script_type_uuid.visit("TypeUuid", &mut region)?;
if region.is_reading() {
*script = if script_type_uuid.is_nil() {
None
} else {
let serialization_context = region
.blackboard
.get::<SerializationContext>()
.expect("Visitor blackboard must contain serialization context!");
Some(
serialization_context
.script_constructors
.try_create(&script_type_uuid)
.ok_or_else(|| {
VisitError::User(format!(
"There is no corresponding script constructor for {script_type_uuid} type!"
))
})?,
)
};
}
if let Some(script) = script {
script.visit("ScriptData", &mut region)?;
}
Ok(())
}
impl Visit for Base {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut region = visitor.enter_region(name)?;
self.name.visit("Name", &mut region)?;
self.local_transform.visit("Transform", &mut region)?;
self.visibility.visit("Visibility", &mut region)?;
self.parent.visit("Parent", &mut region)?;
self.children.visit("Children", &mut region)?;
self.resource.visit("Resource", &mut region)?;
self.is_resource_instance_root
.visit("IsResourceInstance", &mut region)?;
self.lifetime.visit("Lifetime", &mut region)?;
self.lod_group.visit("LodGroup", &mut region)?;
self.original_handle_in_resource
.visit("Original", &mut region)?;
self.tag.visit("Tag", &mut region)?;
self.properties.visit("Properties", &mut region)?;
self.frustum_culling.visit("FrustumCulling", &mut region)?;
self.cast_shadows.visit("CastShadows", &mut region)?;
self.instance_id.visit("InstanceId", &mut region)?;
self.enabled.visit("Enabled", &mut region)?;
self.render_mask.visit("RenderMask", &mut region)?;
let _ = self.scripts.visit("Scripts", &mut region);
Ok(())
}
}
pub struct BaseBuilder {
name: String,
visibility: bool,
local_transform: Transform,
children: Vec<Handle<Node>>,
lifetime: Option<f32>,
lod_group: Option<LodGroup>,
inv_bind_pose_transform: Matrix4<f32>,
tag: String,
frustum_culling: bool,
cast_shadows: bool,
scripts: Vec<ScriptRecord>,
instance_id: SceneNodeId,
enabled: bool,
}
impl Default for BaseBuilder {
fn default() -> Self {
Self::new()
}
}
impl BaseBuilder {
#[inline]
pub fn new() -> Self {
Self {
name: Default::default(),
visibility: true,
local_transform: Default::default(),
children: Default::default(),
lifetime: None,
lod_group: None,
inv_bind_pose_transform: Matrix4::identity(),
tag: Default::default(),
frustum_culling: true,
cast_shadows: true,
scripts: vec![],
instance_id: SceneNodeId(Uuid::new_v4()),
enabled: true,
}
}
#[inline]
pub fn with_name<P: AsRef<str>>(mut self, name: P) -> Self {
name.as_ref().clone_into(&mut self.name);
self
}
#[inline]
pub fn with_visibility(mut self, visibility: bool) -> Self {
self.visibility = visibility;
self
}
#[inline]
pub fn with_local_transform(mut self, transform: Transform) -> Self {
self.local_transform = transform;
self
}
#[inline]
pub fn with_inv_bind_pose_transform(mut self, inv_bind_pose: Matrix4<f32>) -> Self {
self.inv_bind_pose_transform = inv_bind_pose;
self
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_child(mut self, handle: Handle<impl ObjectOrVariant<Node>>) -> Self {
if handle.is_some() {
self.children.push(handle.to_base())
}
self
}
#[inline]
pub fn with_lifetime(mut self, time_seconds: f32) -> Self {
self.lifetime = Some(time_seconds);
self
}
#[inline]
pub fn with_lod_group(mut self, lod_group: LodGroup) -> Self {
self.lod_group = Some(lod_group);
self
}
#[inline]
pub fn with_tag(mut self, tag: String) -> Self {
self.tag = tag;
self
}
#[inline]
pub fn with_frustum_culling(mut self, frustum_culling: bool) -> Self {
self.frustum_culling = frustum_culling;
self
}
#[inline]
pub fn with_cast_shadows(mut self, cast_shadows: bool) -> Self {
self.cast_shadows = cast_shadows;
self
}
#[inline]
pub fn with_script<T>(mut self, script: T) -> Self
where
T: ScriptTrait,
{
self.scripts.push(ScriptRecord::new(Script::new(script)));
self
}
pub fn with_instance_id(mut self, id: SceneNodeId) -> Self {
self.instance_id = id;
self
}
#[inline]
pub fn build_base(self) -> Base {
Base {
self_handle: Default::default(),
script_message_sender: None,
message_sender: None,
name: self.name.into(),
children: self.children,
local_transform: TrackedProperty::unbound(
self.local_transform,
NodeMessageKind::TransformChanged,
),
lifetime: self.lifetime.into(),
visibility: TrackedProperty::unbound(
self.visibility.into(),
NodeMessageKind::VisibilityChanged,
),
enabled: TrackedProperty::unbound(
self.enabled.into(),
NodeMessageKind::EnabledFlagChanged,
),
render_mask: BitMask::all().into(),
global_visibility: Cell::new(true),
parent: Handle::NONE,
global_transform: Cell::new(Matrix4::identity()),
inv_bind_pose_transform: self.inv_bind_pose_transform,
resource: None,
original_handle_in_resource: Handle::NONE,
is_resource_instance_root: false,
lod_group: self.lod_group.into(),
tag: self.tag.into(),
properties: Default::default(),
frustum_culling: self.frustum_culling.into(),
cast_shadows: self.cast_shadows.into(),
scripts: self.scripts,
instance_id: SceneNodeId(Uuid::new_v4()),
global_enabled: Cell::new(true),
}
}
}