use crate::{
asset::untyped::UntypedResource,
core::{
algebra::{Matrix4, Rotation3, UnitQuaternion, Vector2, Vector3},
dyntype::DynTypeContainer,
instant,
log::{Log, MessageKind},
math::{aabb::AxisAlignedBoundingBox, Matrix4Ext},
pool::{Handle, MultiBorrowContext, ObjectOrVariant, Pool, PoolError, Ticket},
reflect::prelude::*,
visitor::{Visit, VisitResult, Visitor},
},
graph::{NodeHandleMap, SceneGraph, SceneGraphNode},
material::{MaterialResourceBinding, MaterialTextureBinding},
resource::model::{Model, ModelResource, ModelResourceExtension},
scene::{
base::{NodeMessage, NodeMessageKind, NodeScriptMessage, SceneNodeId},
dim2::{self},
graph::{
event::{GraphEvent, GraphEventBroadcaster},
physics::{PhysicsPerformanceStatistics, PhysicsWorld},
},
mesh::Mesh,
navmesh,
node::{container::NodeContainer, Node, NodeAsAny, SyncContext, UpdateContext},
pivot::Pivot,
sound::context::SoundContext,
transform::TransformBuilder,
},
script::ScriptTrait,
utils::lightmap::{self, Lightmap},
};
use bitflags::bitflags;
use fxhash::{FxHashMap, FxHashSet};
use fyrox_material::MaterialResourceExtension;
use std::{
any::{Any, TypeId},
fmt::{Debug, Display, Formatter, Write},
ops::{Deref, Index, IndexMut},
sync::mpsc::{channel, Receiver, Sender},
time::Duration,
};
pub mod event;
pub mod physics;
#[derive(Clone, Default, Debug)]
pub struct GraphPerformanceStatistics {
pub hierarchical_properties_time: Duration,
pub sync_time: Duration,
pub physics: PhysicsPerformanceStatistics,
pub physics2d: PhysicsPerformanceStatistics,
pub sound_update_time: Duration,
}
impl GraphPerformanceStatistics {
pub fn total(&self) -> Duration {
self.hierarchical_properties_time
+ self.sync_time
+ self.physics.total()
+ self.physics2d.total()
+ self.sound_update_time
}
}
pub type NodePool = Pool<Node, NodeContainer>;
#[derive(Reflect)]
pub struct Graph {
#[reflect(hidden)]
root: Handle<Node>,
pool: NodePool,
#[reflect(hidden)]
stack: Vec<Handle<Node>>,
pub user_data: DynTypeContainer,
pub physics: PhysicsWorld,
pub physics2d: dim2::physics::PhysicsWorld,
#[reflect(hidden)]
pub sound_context: SoundContext,
#[reflect(hidden)]
pub performance_statistics: GraphPerformanceStatistics,
#[reflect(hidden)]
pub event_broadcaster: GraphEventBroadcaster,
lightmap: Option<Lightmap>,
#[reflect(hidden)]
pub(crate) script_message_sender: Sender<NodeScriptMessage>,
#[reflect(hidden)]
pub(crate) script_message_receiver: Receiver<NodeScriptMessage>,
#[reflect(hidden)]
pub(crate) message_sender: Sender<NodeMessage>,
#[reflect(hidden)]
pub(crate) message_receiver: Receiver<NodeMessage>,
#[reflect(read_only)]
instance_id_map: FxHashMap<SceneNodeId, Handle<Node>>,
}
impl Debug for Graph {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Graph")
.field("physics", &self.physics)
.field("physics2d", &self.physics2d)
.field("sound_context", &self.sound_context)
.field("performance_statistics", &self.performance_statistics)
.field("event_broadcaster", &self.event_broadcaster)
.field("lightmap", &self.lightmap)
.field("instance_id_map", &self.instance_id_map)
.finish()?;
f.write_char('\n')?;
f.write_str(&self.summary())
}
}
impl Clone for Graph {
fn clone(&self) -> Self {
self.clone_ex(
self.root,
true,
&mut |_, _| true,
&mut |_, _| {},
&mut |_, _, _| {},
)
.0
}
}
impl Default for Graph {
fn default() -> Self {
let (script_message_sender, script_message_receiver) = channel();
let (message_sender, message_receiver) = channel();
Self {
physics: PhysicsWorld::new(),
physics2d: dim2::physics::PhysicsWorld::new(),
root: Handle::NONE,
pool: Pool::new(),
stack: Vec::new(),
sound_context: Default::default(),
performance_statistics: Default::default(),
event_broadcaster: Default::default(),
script_message_receiver,
message_sender,
script_message_sender,
lightmap: None,
instance_id_map: Default::default(),
message_receiver,
user_data: Default::default(),
}
}
}
#[derive(Debug)]
pub struct SubGraph {
pub root: (Ticket<Node>, Node),
pub descendants: Vec<(Ticket<Node>, Node)>,
pub parent: Handle<Node>,
}
fn remap_handles(old_new_mapping: &NodeHandleMap<Node>, dest_graph: &mut Graph) {
for (_, &new_node_handle) in old_new_mapping.inner().iter() {
old_new_mapping.remap_handles(
&mut dest_graph.pool[new_node_handle],
&[TypeId::of::<UntypedResource>()],
);
}
}
pub fn isometric_local_transform(
nodes: &NodePool,
node: Handle<impl ObjectOrVariant<Node>>,
) -> Matrix4<f32> {
let transform = nodes[node.to_base()].local_transform();
TransformBuilder::new()
.with_local_position(**transform.position())
.with_local_rotation(**transform.rotation())
.with_pre_rotation(**transform.pre_rotation())
.with_post_rotation(**transform.post_rotation())
.build()
.matrix()
}
pub fn isometric_global_transform(
nodes: &NodePool,
node: Handle<impl ObjectOrVariant<Node>>,
) -> Matrix4<f32> {
let node = node.to_base();
let parent = nodes[node].parent();
if parent.is_some() {
isometric_global_transform(nodes, parent) * isometric_local_transform(nodes, node)
} else {
isometric_local_transform(nodes, node)
}
}
fn clear_links(mut node: Node) -> Node {
node.children.clear();
node.parent = Handle::NONE;
node
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GraphUpdateSwitches {
pub physics_dt: bool,
pub physics2d: bool,
pub physics: bool,
pub node_overrides: Option<FxHashSet<Handle<Node>>>,
pub delete_dead_nodes: bool,
pub paused: bool,
}
impl Default for GraphUpdateSwitches {
fn default() -> Self {
Self {
physics_dt: true,
physics2d: true,
physics: true,
node_overrides: Default::default(),
delete_dead_nodes: true,
paused: false,
}
}
}
#[derive(PartialEq)]
pub enum GraphError {
PoolError(PoolError),
NoScript {
handle: Handle<Node>,
script_type_name: &'static str,
},
NoScriptComponent {
handle: Handle<Node>,
component_type_name: &'static str,
},
UnknownId(SceneNodeId),
}
impl Display for GraphError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
GraphError::PoolError(err) => std::fmt::Display::fmt(&err, f),
GraphError::NoScript {
handle,
script_type_name,
} => {
write!(
f,
"There's no script {script_type_name} on the scene node {handle}"
)
}
GraphError::NoScriptComponent {
handle,
component_type_name,
} => {
write!(
f,
"There's no script component {component_type_name} on the scene node {handle}"
)
}
GraphError::UnknownId(id) => {
write!(f, "There's no scene node with {id:?} id!")
}
}
}
}
impl Debug for GraphError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl std::error::Error for GraphError {}
impl From<PoolError> for GraphError {
fn from(value: PoolError) -> Self {
Self::PoolError(value)
}
}
impl Graph {
#[inline]
pub fn new() -> Self {
let (script_message_sender, script_message_receiver) = channel();
let (message_sender, message_receiver) = channel();
let mut root_node = Pivot::default();
let instance_id = root_node.instance_id;
root_node.set_name("SceneRoot");
let mut pool = Pool::new();
let root = pool.spawn(Node::new(root_node));
pool[root].on_connected_to_graph(
root,
message_sender.clone(),
script_message_sender.clone(),
);
let instance_id_map = FxHashMap::from_iter([(instance_id, root)]);
Self {
physics: Default::default(),
stack: Vec::new(),
root,
pool,
physics2d: Default::default(),
sound_context: SoundContext::new(),
performance_statistics: Default::default(),
event_broadcaster: Default::default(),
script_message_receiver,
message_sender,
script_message_sender,
lightmap: None,
instance_id_map,
message_receiver,
user_data: Default::default(),
}
}
pub fn begin_multi_borrow_with_physics_3d(
&mut self,
) -> (MultiBorrowContext<Node, NodeContainer>, &PhysicsWorld) {
(self.pool.begin_multi_borrow(), &self.physics)
}
pub fn begin_multi_borrow_with_physics_2d(
&mut self,
) -> (MultiBorrowContext<Node, NodeContainer>, &PhysicsWorld) {
(self.pool.begin_multi_borrow(), &mut self.physics)
}
pub fn begin_multi_borrow_with_physics(
&mut self,
) -> (
MultiBorrowContext<Node, NodeContainer>,
&PhysicsWorld,
&dim2::physics::PhysicsWorld,
) {
(
self.pool.begin_multi_borrow(),
&self.physics,
&self.physics2d,
)
}
pub fn from_hierarchy(root: Handle<Node>, other_graph: &Self) -> Self {
let mut graph = Self::default();
other_graph.copy_node(
root,
&mut graph,
false,
&mut |_, _| true,
&mut |_, _| {},
&mut |_, _, _| {},
);
graph
}
fn recursive_summary(
&self,
indent: usize,
current: Handle<impl ObjectOrVariant<Node>>,
result: &mut String,
) {
let current = current.to_base();
for _ in 0..indent {
result.push_str(" ");
}
let Ok(node) = self.try_get(current) else {
use std::fmt::Write;
writeln!(result, "{}: Failed to get", current).unwrap();
return;
};
result.push_str(&node.summary());
result.push('\n');
for script in node.scripts() {
for _ in 0..indent + 1 {
result.push_str(" ");
}
result.push_str("+ Script: ");
result.push_str(&script.summary());
result.push('\n');
}
for child in node.children() {
self.recursive_summary(indent + 1, *child, result);
}
}
pub fn change_root_node(&mut self, root: Node) {
let prev_root = self.root;
self.root = Handle::NONE;
let handle = self.add_node(root);
assert_eq!(self.root, handle);
self.link_nodes(prev_root, handle);
}
pub fn find_references_to(
&self,
target: Handle<impl ObjectOrVariant<Node>>,
) -> Vec<Handle<Node>> {
let mut references = Vec::new();
for (node_handle, node) in self.pair_iter() {
(node as &dyn Reflect).apply_recursively(
&mut |object| {
object.as_any(&mut |any| {
if let Some(handle) = any.downcast_ref::<Handle<Node>>() {
if *handle == target {
references.push(node_handle);
}
}
})
},
&[TypeId::of::<UntypedResource>()],
);
}
references
}
pub fn set_global_position(
&mut self,
node_handle: Handle<impl ObjectOrVariant<Node>>,
position: Vector3<f32>,
) {
let node_handle = node_handle.to_base();
let (node, parent) = self
.pool
.try_borrow_dependant_mut(node_handle, |node| node.parent());
if let Ok(node) = node {
if let Ok(parent) = parent {
let relative_position = parent
.global_transform()
.try_inverse()
.unwrap_or_default()
.transform_point(&position.into())
.coords;
node.local_transform_mut().set_position(relative_position);
} else {
node.local_transform_mut().set_position(position);
}
self.update_hierarchical_data_for_descendants(node_handle);
}
}
pub fn set_global_rotation(
&mut self,
node: Handle<impl ObjectOrVariant<Node>>,
rotation: UnitQuaternion<f32>,
) {
let node = node.to_base();
let (node, parent) = self
.pool
.try_borrow_dependant_mut(node, |node| node.parent());
if let Ok(node) = node {
if let Ok(parent) = parent {
let basis = parent
.global_transform()
.try_inverse()
.unwrap_or_default()
.basis();
let relative_rotation = UnitQuaternion::from_matrix(&basis) * rotation;
node.local_transform_mut().set_rotation(relative_rotation);
} else {
node.local_transform_mut().set_rotation(rotation);
}
}
}
#[inline]
pub fn get_two_mut(&mut self, nodes: (Handle<Node>, Handle<Node>)) -> (&mut Node, &mut Node) {
self.pool.borrow_two_mut(nodes)
}
#[inline]
pub fn get_three_mut(
&mut self,
nodes: (Handle<Node>, Handle<Node>, Handle<Node>),
) -> (&mut Node, &mut Node, &mut Node) {
self.pool.borrow_three_mut(nodes)
}
#[inline]
pub fn get_four_mut(
&mut self,
nodes: (Handle<Node>, Handle<Node>, Handle<Node>, Handle<Node>),
) -> (&mut Node, &mut Node, &mut Node, &mut Node) {
self.pool.borrow_four_mut(nodes)
}
#[inline]
pub fn get_root(&self) -> Handle<Node> {
self.root
}
#[inline]
pub fn begin_multi_borrow(&mut self) -> MultiBorrowContext<Node, NodeContainer> {
self.pool.begin_multi_borrow()
}
#[inline]
pub fn link_nodes_keep_global_transform(
&mut self,
child: Handle<impl ObjectOrVariant<Node>>,
parent: Handle<impl ObjectOrVariant<Node>>,
) {
let child = child.to_base();
let parent = parent.to_base();
let parent_global_transform_inv = self.pool[parent]
.global_transform()
.try_inverse()
.unwrap_or_default();
let child_global_transform = self.pool[child].global_transform();
let relative_transform = parent_global_transform_inv * child_global_transform;
let local_position = relative_transform.position();
let parent_inv_global_rotation = self.global_rotation(parent).inverse();
let local_rotation = parent_inv_global_rotation * self.global_rotation(child);
let local_scale = self
.global_scale(child)
.component_div(&self.global_scale(parent));
self.pool[child]
.local_transform_mut()
.set_position(local_position)
.set_rotation(local_rotation)
.set_scale(local_scale);
self.link_nodes(child, parent);
}
#[inline]
pub fn find_first_by_script<S>(
&self,
root_node: Handle<impl ObjectOrVariant<Node>>,
) -> Option<(Handle<Node>, &Node)>
where
S: ScriptTrait,
{
self.find(root_node, &mut |node| {
for script in &node.scripts {
if script.as_ref().and_then(|s| s.cast::<S>()).is_some() {
return true;
}
}
false
})
}
#[inline]
pub fn copy_node<F, Pre, Post>(
&self,
node_handle: Handle<impl ObjectOrVariant<Node>>,
dest_graph: &mut Graph,
preserve_handles: bool,
filter: &mut F,
pre_process_callback: &mut Pre,
post_process_callback: &mut Post,
) -> (Handle<Node>, NodeHandleMap<Node>)
where
F: FnMut(Handle<Node>, &Node) -> bool,
Pre: FnMut(Handle<Node>, &mut Node),
Post: FnMut(Handle<Node>, Handle<Node>, &mut Node),
{
let mut old_new_mapping = NodeHandleMap::default();
let root_handle = self.copy_node_raw(
node_handle,
dest_graph,
preserve_handles,
&mut old_new_mapping,
filter,
pre_process_callback,
post_process_callback,
);
remap_handles(&old_new_mapping, dest_graph);
(root_handle, old_new_mapping)
}
#[inline]
pub fn copy_node_inplace<F>(
&mut self,
node_handle: Handle<Node>,
filter: &mut F,
) -> (Handle<Node>, NodeHandleMap<Node>)
where
F: FnMut(Handle<Node>, &Node) -> bool,
{
let mut old_new_mapping = NodeHandleMap::default();
let to_copy = self
.traverse_iter(node_handle)
.map(|(descendant_handle, descendant)| (descendant_handle, descendant.children.clone()))
.collect::<Vec<_>>();
let mut root_handle = Handle::NONE;
for (parent, children) in to_copy.iter() {
let parent_copy = clear_links(self.pool[*parent].clone_box());
let parent_copy_handle = self.add_node(parent_copy);
old_new_mapping.insert(*parent, parent_copy_handle);
if root_handle.is_none() {
root_handle = parent_copy_handle;
}
for &child in children {
if filter(child, &self.pool[child]) {
let child_copy = clear_links(self.pool[child].clone_box());
let child_copy_handle = self.add_node(child_copy);
old_new_mapping.insert(child, child_copy_handle);
self.link_nodes(child_copy_handle, parent_copy_handle);
}
}
}
remap_handles(&old_new_mapping, self);
(root_handle, old_new_mapping)
}
#[inline]
pub fn copy_single_node(&self, node_handle: Handle<Node>) -> Node {
let node = &self.pool[node_handle];
let mut clone = clear_links(node.clone_box());
if let Some(ref mut mesh) = clone.cast_mut::<Mesh>() {
for surface in mesh.surfaces_mut() {
surface.bones.clear();
}
}
clone
}
fn copy_node_raw<F, Pre, Post>(
&self,
root_handle: Handle<impl ObjectOrVariant<Node>>,
dest_graph: &mut Graph,
preserve_handles: bool,
old_new_mapping: &mut NodeHandleMap<Node>,
filter: &mut F,
pre_process_callback: &mut Pre,
post_process_callback: &mut Post,
) -> Handle<Node>
where
F: FnMut(Handle<Node>, &Node) -> bool,
Pre: FnMut(Handle<Node>, &mut Node),
Post: FnMut(Handle<Node>, Handle<Node>, &mut Node),
{
let root_handle = root_handle.to_base();
let src_node = &self.pool[root_handle];
let mut dest_node = clear_links(src_node.clone_box());
pre_process_callback(root_handle, &mut dest_node);
let dest_copy_handle = if preserve_handles {
dest_graph.add_node_at_handle(dest_node, root_handle);
root_handle
} else {
dest_graph.add_node(dest_node)
};
old_new_mapping.insert(root_handle, dest_copy_handle);
for &src_child_handle in src_node.children() {
if filter(src_child_handle, &self.pool[src_child_handle]) {
let dest_child_handle = self.copy_node_raw(
src_child_handle,
dest_graph,
preserve_handles,
old_new_mapping,
filter,
pre_process_callback,
post_process_callback,
);
if !dest_child_handle.is_none() {
dest_graph.link_nodes(dest_child_handle, dest_copy_handle);
}
}
}
post_process_callback(
dest_copy_handle,
root_handle,
&mut dest_graph[dest_copy_handle],
);
dest_copy_handle
}
fn restore_dynamic_node_data(&mut self) {
for (handle, node) in self.pool.pair_iter_mut() {
node.on_connected_to_graph(
handle,
self.message_sender.clone(),
self.script_message_sender.clone(),
);
}
}
pub(crate) fn mark_ancestor_nodes_as_modified(&mut self) {
for node in self.linear_iter_mut() {
if node.resource.is_none() {
node.mark_inheritable_variables_as_modified();
}
}
}
pub(crate) fn resolve(&mut self) {
Log::writeln(MessageKind::Information, "Resolving graph...");
self.restore_dynamic_node_data();
self.mark_ancestor_nodes_as_modified();
self.restore_original_handles_and_inherit_properties(
&[TypeId::of::<navmesh::Container>()],
|resource_node, node| {
node.inv_bind_pose_transform = resource_node.inv_bind_pose_transform;
},
);
self.update_hierarchical_data();
let instances = self.restore_integrity(|model, model_data, handle, dest_graph| {
ModelResource::instantiate_from(model, model_data, handle, dest_graph, &mut |_, _| {})
});
self.remap_handles(&instances);
self.apply_lightmap();
Log::writeln(MessageKind::Information, "Graph resolved successfully!");
}
pub fn set_lightmap(
&mut self,
new_lightmap: Option<Lightmap>,
) -> Result<Option<Lightmap>, &'static str> {
if let Some(lightmap) = new_lightmap.as_ref() {
for (handle, lightmaps) in lightmap.map.iter() {
if let Ok(mesh) = self.try_get_mut_of_type::<Mesh>(*handle) {
if mesh.surfaces().len() != lightmaps.len() {
return Err("failed to set lightmap, surface count mismatch");
}
for (surface, entry) in mesh.surfaces_mut().iter_mut().zip(lightmaps) {
let texture = entry.texture.clone().unwrap();
let material = surface.material.deep_copy();
let mut material_state = material.state();
if let Some(material) = material_state.data() {
material.bind(
&lightmap.texture_name,
MaterialResourceBinding::Texture(MaterialTextureBinding {
value: Some(texture),
}),
);
}
drop(material_state);
surface.material.set_value_and_mark_modified(material);
}
}
}
} else if let Some(existing_lightmap) = self.lightmap.as_ref() {
let texture_name = existing_lightmap.texture_name.clone();
for node in self.linear_iter_mut() {
if let Some(mesh) = node.cast_mut::<Mesh>() {
for surface in mesh.surfaces_mut() {
let mut material_state = surface.material().state();
if let Some(material) = material_state.data() {
material.bind(
&texture_name,
MaterialResourceBinding::Texture(MaterialTextureBinding {
value: None,
}),
);
}
}
}
}
}
Ok(std::mem::replace(&mut self.lightmap, new_lightmap))
}
pub fn lightmap(&self) -> Option<&Lightmap> {
self.lightmap.as_ref()
}
fn apply_lightmap(&mut self) {
if let Some(lightmap) = self.lightmap.as_mut() {
let mut unique_data_set = FxHashMap::default();
for &handle in lightmap.map.keys() {
if let Some(mesh) = self.pool[handle].cast_mut::<Mesh>() {
for surface in mesh.surfaces() {
let data = surface.data();
unique_data_set.entry(data.key()).or_insert(data);
}
}
}
for (_, surface_data) in unique_data_set.into_iter() {
let mut data = surface_data.data_ref();
if let Some(patch) = lightmap.patches.get(&data.content_hash()) {
lightmap::apply_surface_data_patch(
&mut data,
&patch.0,
lightmap.second_tex_coord_location,
);
}
}
for (&handle, entries) in lightmap.map.iter_mut() {
if let Some(mesh) = self.pool[handle].cast_mut::<Mesh>() {
for (entry, surface) in entries.iter_mut().zip(mesh.surfaces_mut()) {
let material = surface.material.deep_copy();
let mut material_state = material.state();
if let Some(material) = material_state.data() {
material.bind(
&lightmap.texture_name,
MaterialResourceBinding::Texture(MaterialTextureBinding {
value: entry.texture.clone(),
}),
);
}
drop(material_state);
surface.material.set_value_and_mark_modified(material);
}
}
}
}
}
pub fn aabb_of_descendants<F>(
&self,
root: Handle<impl ObjectOrVariant<Node>>,
mut filter: F,
) -> Option<AxisAlignedBoundingBox>
where
F: FnMut(Handle<Node>, &Node) -> bool,
{
let root = root.to_base();
fn aabb_of_descendants_recursive<F>(
graph: &Graph,
node: Handle<Node>,
filter: &mut F,
) -> Option<AxisAlignedBoundingBox>
where
F: FnMut(Handle<Node>, &Node) -> bool,
{
graph.try_get_node(node).ok().and_then(|n| {
if filter(node, n) {
let mut aabb = n.local_bounding_box();
if aabb.is_invalid_or_degenerate() {
aabb = AxisAlignedBoundingBox::collapsed().transform(&n.global_transform());
} else {
aabb = aabb.transform(&n.global_transform());
}
for child in n.children() {
if let Some(child_aabb) =
aabb_of_descendants_recursive(graph, *child, filter)
{
aabb.add_box(child_aabb);
}
}
Some(aabb)
} else {
None
}
})
}
aabb_of_descendants_recursive(self, root, &mut filter)
}
pub(crate) fn update_enabled_flag_recursively(nodes: &NodePool, node_handle: Handle<Node>) {
let Ok(node) = nodes.try_borrow(node_handle) else {
return;
};
let parent_enabled = nodes
.try_borrow(node.parent())
.ok()
.is_none_or(|p| p.is_globally_enabled());
node.global_enabled.set(parent_enabled && node.is_enabled());
for &child in node.children() {
Self::update_enabled_flag_recursively(nodes, child);
}
}
pub(crate) fn update_visibility_recursively(nodes: &NodePool, node_handle: Handle<Node>) {
let Ok(node) = nodes.try_borrow(node_handle) else {
return;
};
let parent_visibility = nodes
.try_borrow(node.parent())
.ok()
.is_none_or(|p| p.global_visibility());
node.global_visibility
.set(parent_visibility && node.visibility());
for &child in node.children() {
Self::update_visibility_recursively(nodes, child);
}
}
pub(crate) fn update_global_transform_recursively(
nodes: &NodePool,
sound_context: &mut SoundContext,
physics: &mut PhysicsWorld,
physics2d: &mut dim2::physics::PhysicsWorld,
node_handle: Handle<Node>,
) {
let Ok(node) = nodes.try_borrow(node_handle) else {
return;
};
let parent_global_transform = if let Ok(parent) = nodes.try_borrow(node.parent()) {
parent.global_transform()
} else {
Matrix4::identity()
};
let new_global_transform = parent_global_transform * node.local_transform().matrix();
node.on_global_transform_changed(
&new_global_transform,
&mut SyncContext {
nodes,
physics,
physics2d,
sound_context,
switches: None,
},
);
node.global_transform.set(new_global_transform);
for &child in node.children() {
Self::update_global_transform_recursively(
nodes,
sound_context,
physics,
physics2d,
child,
);
}
}
#[inline]
pub fn update_hierarchical_data_for_descendants(
&mut self,
node_handle: Handle<impl ObjectOrVariant<Node>>,
) {
Self::update_hierarchical_data_recursively(
&self.pool,
&mut self.sound_context,
&mut self.physics,
&mut self.physics2d,
node_handle.to_base(),
);
}
#[inline]
pub fn update_hierarchical_data(&mut self) {
self.update_hierarchical_data_for_descendants(self.root);
}
pub(crate) fn update_hierarchical_data_recursively(
nodes: &NodePool,
sound_context: &mut SoundContext,
physics: &mut PhysicsWorld,
physics2d: &mut dim2::physics::PhysicsWorld,
node_handle: Handle<Node>,
) {
Self::update_global_transform_recursively(
nodes,
sound_context,
physics,
physics2d,
node_handle,
);
Self::update_enabled_flag_recursively(nodes, node_handle);
Self::update_visibility_recursively(nodes, node_handle);
}
pub(crate) fn process_node_messages(&mut self, switches: Option<&GraphUpdateSwitches>) {
bitflags! {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
struct Flags: u8 {
const NONE = 0;
const TRANSFORM_CHANGED = 0b0001;
const VISIBILITY_CHANGED = 0b0010;
const ENABLED_FLAG_CHANGED = 0b0100;
}
}
let mut visited_flags = vec![Flags::NONE; self.pool.get_capacity() as usize];
let mut roots = FxHashMap::default();
while let Ok(message) = self.message_receiver.try_recv() {
if let NodeMessageKind::TransformChanged = message.kind {
if let Ok(node) = self.pool.try_borrow(message.node) {
node.on_local_transform_changed(&mut SyncContext {
nodes: &self.pool,
physics: &mut self.physics,
physics2d: &mut self.physics2d,
sound_context: &mut self.sound_context,
switches,
})
}
}
let message_flag = match message.kind {
NodeMessageKind::TransformChanged => Flags::TRANSFORM_CHANGED,
NodeMessageKind::VisibilityChanged => Flags::VISIBILITY_CHANGED,
NodeMessageKind::EnabledFlagChanged => Flags::ENABLED_FLAG_CHANGED,
};
let visit_flags = &mut visited_flags[message.node.index() as usize];
if visit_flags.contains(message_flag) {
continue;
}
visit_flags.insert(message_flag);
roots
.entry(message.node)
.or_insert(Flags::NONE)
.insert(message_flag);
fn traverse_recursive(
graph: &Graph,
from: Handle<Node>,
func: &mut impl FnMut(Handle<Node>),
) {
func(from);
if let Ok(node) = graph.try_get_node(from) {
for &child in node.children() {
traverse_recursive(graph, child, func)
}
}
}
traverse_recursive(self, message.node, &mut |h| {
visited_flags[h.index() as usize].insert(message_flag);
if h != message.node {
if let Some(flags) = roots.get_mut(&h) {
flags.remove(message_flag);
if flags.is_empty() {
roots.remove(&h);
}
}
}
})
}
for (node, flags) in roots {
if flags.contains(Flags::TRANSFORM_CHANGED) {
Self::update_global_transform_recursively(
&self.pool,
&mut self.sound_context,
&mut self.physics,
&mut self.physics2d,
node,
);
}
if flags.contains(Flags::VISIBILITY_CHANGED) {
Self::update_visibility_recursively(&self.pool, node);
}
if flags.contains(Flags::ENABLED_FLAG_CHANGED) {
Self::update_enabled_flag_recursively(&self.pool, node)
}
}
}
fn sync_native(&mut self, switches: &GraphUpdateSwitches) {
let mut sync_context = SyncContext {
nodes: &self.pool,
physics: &mut self.physics,
physics2d: &mut self.physics2d,
sound_context: &mut self.sound_context,
switches: Some(switches),
};
for (handle, node) in self.pool.pair_iter() {
node.sync_native(handle, &mut sync_context);
}
}
fn update_node(
&mut self,
handle: Handle<Node>,
frame_size: Vector2<f32>,
dt: f32,
delete_dead_nodes: bool,
) {
if let Ok((ticket, mut node)) = self.pool.try_take_reserve(handle) {
let mut is_alive = node.is_alive();
if node.is_globally_enabled() {
node.update(&mut UpdateContext {
frame_size,
dt,
nodes: &mut self.pool,
physics: &mut self.physics,
physics2d: &mut self.physics2d,
sound_context: &mut self.sound_context,
});
if delete_dead_nodes {
if let Some(lifetime) = node.lifetime.get_value_mut_silent().as_mut() {
*lifetime -= dt;
if *lifetime <= 0.0 {
is_alive = false;
}
}
}
}
self.pool.put_back(ticket, node);
if !is_alive && delete_dead_nodes {
self.remove_node(handle);
}
}
}
pub fn update(&mut self, frame_size: Vector2<f32>, dt: f32, switches: GraphUpdateSwitches) {
self.sound_context.state().pause(switches.paused);
if switches.paused {
return;
}
let last_time = instant::Instant::now();
self.process_node_messages(Some(&switches));
self.performance_statistics.hierarchical_properties_time =
instant::Instant::now() - last_time;
let last_time = instant::Instant::now();
self.sync_native(&switches);
self.performance_statistics.sync_time = instant::Instant::now() - last_time;
if switches.physics {
self.physics.performance_statistics.reset();
self.physics.update(dt, switches.physics_dt);
self.performance_statistics.physics = self.physics.performance_statistics.clone();
}
if switches.physics2d {
self.physics2d.performance_statistics.reset();
self.physics2d.update(dt, switches.physics_dt);
self.performance_statistics.physics2d = self.physics2d.performance_statistics.clone();
}
self.performance_statistics.sound_update_time =
self.sound_context.state().full_render_duration();
if let Some(overrides) = switches.node_overrides.as_ref() {
for handle in overrides {
self.update_node(*handle, frame_size, dt, switches.delete_dead_nodes);
}
} else {
for i in 0..self.pool.get_capacity() {
self.update_node(
self.pool.handle_from_index(i),
frame_size,
dt,
switches.delete_dead_nodes,
);
}
}
}
#[inline]
pub fn capacity(&self) -> u32 {
self.pool.get_capacity()
}
#[inline]
pub fn handle_from_index(&self, index: u32) -> Handle<Node> {
self.pool.handle_from_index(index)
}
#[inline]
pub fn generate_free_handles(&self, amount: usize) -> Vec<Handle<Node>> {
self.pool.generate_free_handles(amount)
}
#[inline]
pub fn linear_iter(&self) -> impl Iterator<Item = &Node> {
self.pool.iter()
}
#[inline]
pub fn pair_iter_mut(&mut self) -> impl Iterator<Item = (Handle<Node>, &mut Node)> {
self.pool.pair_iter_mut()
}
#[inline]
pub fn take_reserve(&mut self, handle: Handle<Node>) -> (Ticket<Node>, Node) {
self.isolate_node(handle);
let (ticket, node) = self.take_reserve_internal(handle);
self.instance_id_map.remove(&node.instance_id);
(ticket, node)
}
pub(crate) fn take_reserve_internal(&mut self, handle: Handle<Node>) -> (Ticket<Node>, Node) {
let (ticket, mut node) = self.pool.take_reserve(handle);
self.instance_id_map.remove(&node.instance_id);
node.on_removed_from_graph(self);
(ticket, node)
}
#[inline]
pub fn put_back(&mut self, ticket: Ticket<Node>, node: Node) -> Handle<Node> {
let handle = self.put_back_internal(ticket, node);
self.link_nodes(handle, self.root);
handle
}
pub(crate) fn put_back_internal(&mut self, ticket: Ticket<Node>, node: Node) -> Handle<Node> {
let instance_id = node.instance_id;
let handle = self.pool.put_back(ticket, node);
self.instance_id_map.insert(instance_id, handle);
handle
}
#[inline]
pub fn forget_ticket(&mut self, ticket: Ticket<Node>, node: Node) -> Node {
self.pool.forget_ticket(ticket);
node
}
#[inline]
pub fn take_reserve_sub_graph(&mut self, root: Handle<Node>) -> SubGraph {
let mut descendants = Vec::new();
let root_ref = &mut self[root];
let mut stack = root_ref.children().to_vec();
let parent = root_ref.parent;
while let Some(handle) = stack.pop() {
stack.extend_from_slice(self[handle].children());
descendants.push(self.take_reserve_internal(handle));
}
SubGraph {
root: self.take_reserve(root),
descendants,
parent,
}
}
#[inline]
pub fn put_sub_graph_back(&mut self, sub_graph: SubGraph) -> Handle<Node> {
for (ticket, node) in sub_graph.descendants {
self.pool.put_back(ticket, node);
}
let (ticket, node) = sub_graph.root;
let root_handle = self.put_back(ticket, node);
self.link_nodes(root_handle, sub_graph.parent);
root_handle
}
#[inline]
pub fn forget_sub_graph(&mut self, sub_graph: SubGraph) {
for (ticket, _) in sub_graph.descendants {
self.pool.forget_ticket(ticket);
}
let (ticket, _) = sub_graph.root;
self.pool.forget_ticket(ticket);
}
#[inline]
pub fn node_count(&self) -> u32 {
self.pool.alive_count()
}
#[inline]
pub fn clone_ex<F, Pre, Post>(
&self,
root: Handle<impl ObjectOrVariant<Node>>,
preserve_handles: bool,
filter: &mut F,
pre_process_callback: &mut Pre,
post_process_callback: &mut Post,
) -> (Self, NodeHandleMap<Node>)
where
F: FnMut(Handle<Node>, &Node) -> bool,
Pre: FnMut(Handle<Node>, &mut Node),
Post: FnMut(Handle<Node>, Handle<Node>, &mut Node),
{
let root = root.to_base();
let mut copy = Self {
sound_context: self.sound_context.deep_clone(),
physics: self.physics.clone(),
physics2d: self.physics2d.clone(),
user_data: self.user_data.clone(),
..Default::default()
};
let (copy_root, old_new_map) = self.copy_node(
root,
&mut copy,
preserve_handles,
filter,
pre_process_callback,
post_process_callback,
);
assert_eq!(copy.root, copy_root);
let mut lightmap = self.lightmap.clone();
if let Some(lightmap) = lightmap.as_mut() {
let mut map = FxHashMap::default();
for (mut handle, mut entries) in std::mem::take(&mut lightmap.map) {
for entry in entries.iter_mut() {
for light_handle in entry.lights.iter_mut() {
old_new_map.try_map(light_handle);
}
}
if old_new_map.try_map(&mut handle) {
map.insert(handle, entries);
}
}
lightmap.map = map;
}
copy.lightmap = lightmap;
if let Some(user_data) = copy.user_data.0.as_mut() {
old_new_map.remap_handles_any(
user_data,
"UserData",
&[TypeId::of::<UntypedResource>()],
);
}
(copy, old_new_map)
}
#[inline]
pub fn local_transform_no_scale(
&self,
node: Handle<impl ObjectOrVariant<Node>>,
) -> Matrix4<f32> {
let mut transform = self[node.to_base()].local_transform().clone();
transform.set_scale(Vector3::new(1.0, 1.0, 1.0));
transform.matrix()
}
#[inline]
pub fn global_transform_no_scale(
&self,
node: Handle<impl ObjectOrVariant<Node>>,
) -> Matrix4<f32> {
let node = node.to_base();
let parent = self[node].parent();
if parent.is_some() {
self.global_transform_no_scale(parent) * self.local_transform_no_scale(node)
} else {
self.local_transform_no_scale(node)
}
}
#[inline]
pub fn isometric_local_transform(
&self,
node: Handle<impl ObjectOrVariant<Node>>,
) -> Matrix4<f32> {
isometric_local_transform(&self.pool, node)
}
#[inline]
pub fn isometric_global_transform(
&self,
node: Handle<impl ObjectOrVariant<Node>>,
) -> Matrix4<f32> {
isometric_global_transform(&self.pool, node)
}
#[inline]
pub fn global_scale_matrix(&self, node: Handle<impl ObjectOrVariant<Node>>) -> Matrix4<f32> {
Matrix4::new_nonuniform_scaling(&self.global_scale(node))
}
#[inline]
pub fn global_rotation(&self, node: Handle<impl ObjectOrVariant<Node>>) -> UnitQuaternion<f32> {
UnitQuaternion::from(Rotation3::from_matrix_eps(
&self.global_transform_no_scale(node).basis(),
f32::EPSILON,
16,
Rotation3::identity(),
))
}
#[inline]
pub fn isometric_global_rotation(
&self,
node: Handle<impl ObjectOrVariant<Node>>,
) -> UnitQuaternion<f32> {
UnitQuaternion::from(Rotation3::from_matrix_eps(
&self.isometric_global_transform(node).basis(),
f32::EPSILON,
16,
Rotation3::identity(),
))
}
#[inline]
pub fn global_rotation_position_no_scale(
&self,
node: Handle<impl ObjectOrVariant<Node>>,
) -> (UnitQuaternion<f32>, Vector3<f32>) {
let node = node.to_base();
(self.global_rotation(node), self[node].global_position())
}
#[inline]
pub fn isometric_global_rotation_position(
&self,
node: Handle<impl ObjectOrVariant<Node>>,
) -> (UnitQuaternion<f32>, Vector3<f32>) {
let node = node.to_base();
(
self.isometric_global_rotation(node),
self[node].global_position(),
)
}
#[inline]
pub fn global_scale(&self, handle: Handle<impl ObjectOrVariant<Node>>) -> Vector3<f32> {
let mut handle = handle.to_base();
let mut global_scale = Vector3::repeat(1.0);
while let Ok(node_ref) = self.try_get_node(handle) {
global_scale = global_scale.component_mul(node_ref.local_transform().scale());
handle = node_ref.parent;
}
global_scale
}
#[inline]
pub fn try_get_script_of<T>(
&self,
handle: Handle<impl ObjectOrVariant<Node>>,
) -> Result<&T, GraphError>
where
T: ScriptTrait,
{
let handle = handle.to_base();
let node = self.try_get_node(handle)?;
node.try_get_script::<T>()
.ok_or_else(|| GraphError::NoScript {
handle,
script_type_name: std::any::type_name::<T>(),
})
}
#[inline]
pub fn try_get_scripts_of<T: ScriptTrait>(
&self,
handle: Handle<impl ObjectOrVariant<Node>>,
) -> Result<impl Iterator<Item = &T>, GraphError> {
let handle = handle.to_base();
let node = self.try_get_node(handle)?;
Ok(node.try_get_scripts())
}
#[inline]
pub fn try_get_script_of_mut<T>(
&mut self,
handle: Handle<impl ObjectOrVariant<Node>>,
) -> Result<&mut T, GraphError>
where
T: ScriptTrait,
{
let handle = handle.to_base();
let node = self.try_get_node_mut(handle)?;
node.try_get_script_mut::<T>()
.ok_or_else(|| GraphError::NoScript {
handle,
script_type_name: std::any::type_name::<T>(),
})
}
#[inline]
pub fn try_get_scripts_of_mut<T>(
&mut self,
handle: Handle<impl ObjectOrVariant<Node>>,
) -> Result<impl Iterator<Item = &mut T>, GraphError>
where
T: ScriptTrait,
{
let handle = handle.to_base();
let node = self.try_get_node_mut(handle)?;
Ok(node.try_get_scripts_mut())
}
#[inline]
pub fn try_get_script_component_of<C>(
&self,
handle: Handle<impl ObjectOrVariant<Node>>,
) -> Result<&C, GraphError>
where
C: Any,
{
let handle = handle.to_base();
let node = self.try_get_node(handle)?;
node.try_get_script_component()
.ok_or_else(|| GraphError::NoScriptComponent {
handle,
component_type_name: std::any::type_name::<C>(),
})
}
#[inline]
pub fn try_get_script_component_of_mut<C>(
&mut self,
handle: Handle<impl ObjectOrVariant<Node>>,
) -> Result<&mut C, GraphError>
where
C: Any,
{
let handle = handle.to_base();
let node = self.try_get_node_mut(handle)?;
node.try_get_script_component_mut()
.ok_or_else(|| GraphError::NoScriptComponent {
handle,
component_type_name: std::any::type_name::<C>(),
})
}
pub fn id_to_node_handle(&self, id: SceneNodeId) -> Option<&Handle<Node>> {
self.instance_id_map.get(&id)
}
pub fn node_by_id(&self, id: SceneNodeId) -> Result<(Handle<Node>, &Node), GraphError> {
self.instance_id_map
.get(&id)
.ok_or(GraphError::UnknownId(id))
.and_then(|h| Ok(self.pool.try_borrow(*h).map(|n| (*h, n))?))
}
pub fn node_by_id_mut(
&mut self,
id: SceneNodeId,
) -> Result<(Handle<Node>, &mut Node), GraphError> {
self.instance_id_map
.get(&id)
.ok_or(GraphError::UnknownId(id))
.and_then(|h| Ok(self.pool.try_borrow_mut(*h).map(|n| (*h, n))?))
}
}
impl<T: ObjectOrVariant<Node>> Index<Handle<T>> for Graph {
type Output = T;
#[inline]
fn index(&self, index: Handle<T>) -> &Self::Output {
self.try_get(index).unwrap()
}
}
impl<T: ObjectOrVariant<Node>> IndexMut<Handle<T>> for Graph {
#[inline]
fn index_mut(&mut self, index: Handle<T>) -> &mut Self::Output {
self.try_get_mut(index).unwrap()
}
}
impl Visit for Graph {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
if visitor.is_reading() && self.pool.get_capacity() != 0 {
panic!("Graph pool must be empty on load!")
}
let mut region = visitor.enter_region(name)?;
self.root.visit("Root", &mut region)?;
self.pool.visit("Pool", &mut region)?;
self.sound_context.visit("SoundContext", &mut region)?;
self.physics.visit("PhysicsWorld", &mut region)?;
self.physics2d.visit("PhysicsWorld2D", &mut region)?;
self.lightmap.visit("Lightmap", &mut region)?;
Log::verify(self.user_data.visit("UserData", &mut region));
Ok(())
}
}
impl SceneGraph for Graph {
type Prefab = Model;
type NodeContainer = NodeContainer;
type Node = Node;
fn summary(&self) -> String {
let mut result = String::new();
self.recursive_summary(0, self.root, &mut result);
result
}
#[inline]
fn actual_type_id(&self, handle: Handle<Self::Node>) -> Result<TypeId, PoolError> {
self.pool
.try_borrow(handle)
.map(|n| NodeAsAny::as_any(n.0.deref()).type_id())
}
#[inline]
fn root(&self) -> Handle<Self::Node> {
self.root
}
#[inline]
fn set_root(&mut self, root: Handle<Self::Node>) {
self.root = root;
}
#[inline]
fn is_valid_handle(&self, handle: Handle<impl ObjectOrVariant<Self::Node>>) -> bool {
self.pool.is_valid_handle(handle)
}
#[inline]
fn add_node(&mut self, node: Self::Node) -> Handle<Self::Node> {
let handle = self.pool.next_free_handle();
self.add_node_at_handle(node, handle);
handle
}
fn add_node_at_handle(&mut self, mut node: Self::Node, handle: Handle<Self::Node>) {
let children = node.children.clone();
node.children.clear();
let script_count = node.scripts.len();
self.pool
.spawn_at_handle(handle, node)
.expect("The handle must be valid!");
if self.root.is_none() {
self.root = handle;
} else {
self.link_nodes(handle, self.root);
}
for child in children {
self.link_nodes(child, handle);
}
self.event_broadcaster.broadcast(GraphEvent::Added(handle));
for i in 0..script_count {
self.script_message_sender
.send(NodeScriptMessage::InitializeScript {
handle,
script_index: i,
})
.unwrap();
}
let script_message_sender = self.script_message_sender.clone();
let message_sender = self.message_sender.clone();
let node = &mut self.pool[handle];
node.on_connected_to_graph(handle, message_sender, script_message_sender);
self.instance_id_map.insert(node.instance_id, handle);
}
#[inline]
fn remove_node(&mut self, node_handle: Handle<impl ObjectOrVariant<Self::Node>>) {
let node_handle = node_handle.to_base();
self.isolate_node(node_handle);
self.stack.clear();
self.stack.push(node_handle);
while let Some(handle) = self.stack.pop() {
for &child in self.pool[handle].children().iter() {
self.stack.push(child);
}
let mut node = self.pool.free(handle);
self.instance_id_map.remove(&node.instance_id);
node.on_removed_from_graph(self);
self.event_broadcaster
.broadcast(GraphEvent::Removed(handle));
}
}
#[inline]
fn link_nodes(
&mut self,
child: Handle<impl ObjectOrVariant<Self::Node>>,
parent: Handle<impl ObjectOrVariant<Self::Node>>,
) {
let child = child.to_base();
let parent = parent.to_base();
self.isolate_node(child);
self.pool[child].parent = parent;
self.pool[parent].children.push(child);
self.message_sender
.send(NodeMessage::new(child, NodeMessageKind::TransformChanged))
.unwrap();
}
#[inline]
fn unlink_node(&mut self, node_handle: Handle<impl ObjectOrVariant<Self::Node>>) {
let node_handle = node_handle.to_base();
self.isolate_node(node_handle);
self.link_nodes(node_handle, self.root);
self.pool[node_handle]
.local_transform_mut()
.set_position(Vector3::default());
}
#[inline]
fn isolate_node(&mut self, node_handle: Handle<impl ObjectOrVariant<Self::Node>>) {
let node_handle = node_handle.to_base();
let parent_handle = std::mem::replace(&mut self.pool[node_handle].parent, Handle::NONE);
if let Ok(parent) = self.pool.try_borrow_mut(parent_handle) {
if let Some(i) = parent.children().iter().position(|h| *h == node_handle) {
parent.children.remove(i);
}
}
let (ticket, mut node) = self.pool.take_reserve(node_handle);
node.on_unlink(self);
self.pool.put_back(ticket, node);
}
#[inline]
fn try_get_node(&self, handle: Handle<Self::Node>) -> Result<&Self::Node, PoolError> {
self.pool.try_borrow(handle)
}
#[inline]
fn try_get_node_mut(
&mut self,
handle: Handle<Self::Node>,
) -> Result<&mut Self::Node, PoolError> {
self.pool.try_borrow_mut(handle)
}
fn derived_type_ids(&self, handle: Handle<Self::Node>) -> Result<Vec<TypeId>, PoolError> {
self.pool
.try_borrow(handle)
.map(|n| Box::deref(&n.0).query_derived_types().to_vec())
}
fn actual_type_name(&self, handle: Handle<Self::Node>) -> Result<&'static str, PoolError> {
self.pool
.try_borrow(handle)
.map(|n| n.0.deref().type_name())
}
#[inline]
fn pair_iter(&self) -> impl Iterator<Item = (Handle<Self::Node>, &Self::Node)> {
self.pool.pair_iter()
}
#[inline]
fn linear_iter(&self) -> impl Iterator<Item = &Self::Node> {
self.pool.iter()
}
#[inline]
fn linear_iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Node> {
self.pool.iter_mut()
}
fn try_get<U: ObjectOrVariant<Node>>(&self, handle: Handle<U>) -> Result<&U, PoolError> {
self.pool.try_get(handle)
}
fn try_get_mut<U: ObjectOrVariant<Node>>(
&mut self,
handle: Handle<U>,
) -> Result<&mut U, PoolError> {
self.pool.try_get_mut(handle)
}
}
#[cfg(test)]
mod test {
use crate::scene::rigidbody::{RigidBody, RigidBodyBuilder};
use crate::{
asset::{io::FsResourceIo, manager::ResourceManager},
core::{
algebra::{Matrix4, Vector3},
futures::executor::block_on,
pool::Handle,
reflect::prelude::*,
type_traits::prelude::*,
visitor::prelude::*,
},
engine::{self, SerializationContext},
graph::SceneGraph,
resource::model::{Model, ModelResourceExtension},
scene::{
base::BaseBuilder,
graph::Graph,
mesh::{
surface::{SurfaceBuilder, SurfaceData, SurfaceResource},
MeshBuilder,
},
node::Node,
pivot::{Pivot, PivotBuilder},
transform::TransformBuilder,
Scene, SceneLoader,
},
script::ScriptTrait,
};
use fyrox_core::algebra::Vector2;
use fyrox_resource::untyped::ResourceKind;
use std::{fs, path::Path, sync::Arc};
#[derive(Clone, Debug, PartialEq, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
#[type_uuid(id = "722feb80-a10b-4ee0-8cef-5d1473df8457")]
struct MyScript {
foo: String,
bar: f32,
}
impl ScriptTrait for MyScript {}
#[derive(Clone, Debug, PartialEq, Reflect, Visit, TypeUuidProvider, ComponentProvider)]
#[type_uuid(id = "722feb80-a10b-4ee0-8cef-5d1473df8458")]
struct MyOtherScript {
baz: u32,
foobar: Vec<u32>,
}
impl ScriptTrait for MyOtherScript {}
#[test]
fn test_graph_scripts() {
let node = PivotBuilder::new(
BaseBuilder::new()
.with_script(MyScript {
foo: "Stuff".to_string(),
bar: 123.321,
})
.with_script(MyScript {
foo: "OtherStuff".to_string(),
bar: 321.123,
})
.with_script(MyOtherScript {
baz: 321,
foobar: vec![1, 2, 3],
}),
)
.build_node();
let mut graph = Graph::new();
let handle = graph.add_node(node);
assert_eq!(
graph.try_get_script_of::<MyScript>(handle),
Ok(&MyScript {
foo: "Stuff".to_string(),
bar: 123.321,
})
);
assert_eq!(
graph.try_get_script_of_mut::<MyScript>(handle),
Ok(&mut MyScript {
foo: "Stuff".to_string(),
bar: 123.321,
})
);
let mut immutable_iterator = graph
.try_get_scripts_of::<MyScript>(handle)
.expect("The handle expected to be valid!");
assert_eq!(
immutable_iterator.next(),
Some(&MyScript {
foo: "Stuff".to_string(),
bar: 123.321,
})
);
assert_eq!(
immutable_iterator.next(),
Some(&MyScript {
foo: "OtherStuff".to_string(),
bar: 321.123,
})
);
drop(immutable_iterator);
assert_eq!(
graph.try_get_script_of::<MyOtherScript>(handle),
Ok(&MyOtherScript {
baz: 321,
foobar: vec![1, 2, 3],
})
);
assert_eq!(
graph.try_get_script_of_mut::<MyOtherScript>(handle),
Ok(&mut MyOtherScript {
baz: 321,
foobar: vec![1, 2, 3],
})
);
let mut mutable_iterator = graph
.try_get_scripts_of_mut::<MyScript>(handle)
.expect("The handle expected to be valid!");
assert_eq!(
mutable_iterator.next(),
Some(&mut MyScript {
foo: "Stuff".to_string(),
bar: 123.321,
})
);
assert_eq!(
mutable_iterator.next(),
Some(&mut MyScript {
foo: "OtherStuff".to_string(),
bar: 321.123,
})
);
}
#[test]
fn graph_init_test() {
let graph = Graph::new();
assert_ne!(graph.root, Handle::<Node>::NONE);
assert_eq!(graph.pool.alive_count(), 1);
}
#[test]
fn graph_node_test() {
let mut graph = Graph::new();
graph.add_node(Node::new(Pivot::default()));
graph.add_node(Node::new(Pivot::default()));
graph.add_node(Node::new(Pivot::default()));
assert_eq!(graph.pool.alive_count(), 4);
}
#[test]
fn test_graph_search() {
let mut graph = Graph::new();
let b;
let c;
let d;
let a = PivotBuilder::new(
BaseBuilder::new()
.with_name("A")
.with_child({
b = PivotBuilder::new(BaseBuilder::new().with_name("B")).build(&mut graph);
b
})
.with_child({
c = PivotBuilder::new(BaseBuilder::new().with_name("C").with_child({
d = PivotBuilder::new(BaseBuilder::new().with_name("D")).build(&mut graph);
d
}))
.build(&mut graph);
c
}),
)
.build(&mut graph);
assert!(graph.find_by_name(a, "X").is_none());
assert_eq!(graph.find_by_name(a, "A").unwrap().0, a);
assert_eq!(graph.find_by_name(a, "D").unwrap().0, d);
let result = graph
.find_map(a, &mut |n| if n.name() == "D" { Some("D") } else { None })
.unwrap();
assert_eq!(result.0, d);
assert_eq!(result.1, "D");
assert!(graph.find_up_by_name(d, "X").is_none());
assert_eq!(graph.find_up_by_name(d, "D").unwrap().0, d);
assert_eq!(graph.find_up_by_name(d, "A").unwrap().0, a);
let result = graph
.find_up_map(d, &mut |n| if n.name() == "A" { Some("A") } else { None })
.unwrap();
assert_eq!(result.0, a);
assert_eq!(result.1, "A");
}
fn create_scene() -> Scene {
let mut scene = Scene::new();
PivotBuilder::new(BaseBuilder::new().with_name("Pivot")).build(&mut scene.graph);
PivotBuilder::new(BaseBuilder::new().with_name("MeshPivot").with_child({
MeshBuilder::new(
BaseBuilder::new().with_name("Mesh").with_local_transform(
TransformBuilder::new()
.with_local_position(Vector3::new(3.0, 2.0, 1.0))
.build(),
),
)
.with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_ok(
Uuid::new_v4(),
ResourceKind::Embedded,
SurfaceData::make_cone(16, 1.0, 1.0, &Matrix4::identity()),
))
.build()])
.build(&mut scene.graph)
}))
.build(&mut scene.graph);
scene
}
fn save_scene(scene: &mut Scene, path: &Path) {
let mut visitor = Visitor::new();
scene.save("Scene", &mut visitor).unwrap();
visitor.save_ascii_to_file(path).unwrap();
}
fn make_resource_manager(root: &Path) -> ResourceManager {
let resource_manager =
ResourceManager::new(Arc::new(FsResourceIo), Arc::new(Default::default()));
resource_manager
.state()
.resource_registry
.lock()
.set_path(root.join("resources.registry"));
engine::initialize_resource_manager_loaders(
&resource_manager,
Arc::new(SerializationContext::new()),
Default::default(),
Default::default(),
);
resource_manager.update_or_load_registry();
resource_manager
}
#[test]
fn test_restore_integrity() {
let root = Path::new("test_restore_integrity");
if !root.exists() {
fs::create_dir_all(root).unwrap();
}
let root_asset_path = root.join("root2.rgs");
let derived_asset_path = root.join("derived2.rgs");
{
let mut scene = create_scene();
save_scene(&mut scene, &root_asset_path);
}
{
let resource_manager = make_resource_manager(root);
let root_asset = block_on(resource_manager.request::<Model>(&root_asset_path)).unwrap();
let mut derived = Scene::new();
root_asset.instantiate(&mut derived);
save_scene(&mut derived, &derived_asset_path);
}
{
let resource_manager = make_resource_manager(root);
let mut scene = block_on(
block_on(SceneLoader::from_file(
&root_asset_path,
&FsResourceIo,
Arc::new(SerializationContext::new()),
Default::default(),
resource_manager.clone(),
))
.unwrap()
.0
.finish(),
);
PivotBuilder::new(BaseBuilder::new().with_name("AddedLater")).build(&mut scene.graph);
let mesh = scene.graph.find_by_name_from_root("Mesh").unwrap().0;
let pivot = PivotBuilder::new(BaseBuilder::new().with_name("NewChildOfMesh"))
.build(&mut scene.graph);
scene.graph.link_nodes(pivot, mesh);
let existing_pivot = scene.graph.find_by_name_from_root("Pivot").unwrap().0;
scene.graph.remove_node(existing_pivot);
save_scene(&mut scene, &root_asset_path);
}
{
let resource_manager = make_resource_manager(root);
let derived_asset =
block_on(resource_manager.request::<Model>(derived_asset_path)).unwrap();
let derived_data = derived_asset.data_ref();
let derived_scene = derived_data.get_scene();
assert_eq!(
derived_scene
.graph
.find_by_name_from_root("Pivot")
.map(|(h, _)| h),
None
);
let mesh_pivot = derived_scene
.graph
.find_by_name_from_root("MeshPivot")
.expect("Missing MeshPivot")
.0;
let mesh = derived_scene
.graph
.find_by_name(mesh_pivot, "Mesh")
.expect("Missing Mesh")
.0;
derived_scene
.graph
.find_by_name_from_root("AddedLater")
.expect("Missing AddedLater");
derived_scene
.graph
.find_by_name(mesh, "NewChildOfMesh")
.expect("Missing NewChildOfMesh");
}
}
#[test]
fn test_global_scale() {
let mut graph = Graph::new();
let b;
let c;
let a = PivotBuilder::new(
BaseBuilder::new()
.with_local_transform(
TransformBuilder::new()
.with_local_scale(Vector3::new(1.0, 1.0, 2.0))
.build(),
)
.with_child({
b = PivotBuilder::new(
BaseBuilder::new()
.with_local_transform(
TransformBuilder::new()
.with_local_scale(Vector3::new(3.0, 2.0, 1.0))
.build(),
)
.with_child({
c = PivotBuilder::new(
BaseBuilder::new().with_local_transform(
TransformBuilder::new()
.with_local_scale(Vector3::new(1.0, 2.0, 3.0))
.build(),
),
)
.build(&mut graph);
c
}),
)
.build(&mut graph);
b
}),
)
.build(&mut graph);
assert_eq!(graph.global_scale(a), Vector3::new(1.0, 1.0, 2.0));
assert_eq!(graph.global_scale(b), Vector3::new(3.0, 2.0, 2.0));
assert_eq!(graph.global_scale(c), Vector3::new(3.0, 4.0, 6.0));
}
#[test]
fn test_hierarchy_changes_propagation() {
let mut graph = Graph::new();
let b;
let c;
let d;
let a = PivotBuilder::new(
BaseBuilder::new()
.with_local_transform(
TransformBuilder::new()
.with_local_position(Vector3::new(1.0, 0.0, 0.0))
.build(),
)
.with_child({
b = PivotBuilder::new(
BaseBuilder::new()
.with_visibility(false)
.with_enabled(false)
.with_local_transform(
TransformBuilder::new()
.with_local_position(Vector3::new(0.0, 1.0, 0.0))
.build(),
)
.with_child({
c = PivotBuilder::new(
BaseBuilder::new().with_local_transform(
TransformBuilder::new()
.with_local_position(Vector3::new(0.0, 0.0, 1.0))
.build(),
),
)
.build(&mut graph);
c
}),
)
.build(&mut graph);
b
})
.with_child({
d = PivotBuilder::new(
BaseBuilder::new().with_local_transform(
TransformBuilder::new()
.with_local_position(Vector3::new(1.0, 1.0, 1.0))
.build(),
),
)
.build(&mut graph);
d
}),
)
.build(&mut graph);
graph.update(Vector2::new(1.0, 1.0), 1.0 / 60.0, Default::default());
assert_eq!(graph[a].global_position(), Vector3::new(1.0, 0.0, 0.0));
assert_eq!(graph[b].global_position(), Vector3::new(1.0, 1.0, 0.0));
assert_eq!(graph[c].global_position(), Vector3::new(1.0, 1.0, 1.0));
assert_eq!(graph[d].global_position(), Vector3::new(2.0, 1.0, 1.0));
assert!(graph[a].global_visibility());
assert!(!graph[b].global_visibility());
assert!(!graph[c].global_visibility());
assert!(graph[d].global_visibility());
assert!(graph[a].is_globally_enabled());
assert!(!graph[b].is_globally_enabled());
assert!(!graph[c].is_globally_enabled());
assert!(graph[d].is_globally_enabled());
graph[b]
.local_transform_mut()
.set_position(Vector3::new(0.0, 2.0, 0.0));
graph[a].set_enabled(false);
graph[b].set_visibility(true);
graph.update(Vector2::new(1.0, 1.0), 1.0 / 60.0, Default::default());
assert_eq!(graph[a].global_position(), Vector3::new(1.0, 0.0, 0.0));
assert_eq!(graph[b].global_position(), Vector3::new(1.0, 2.0, 0.0));
assert_eq!(graph[c].global_position(), Vector3::new(1.0, 2.0, 1.0));
assert_eq!(graph[d].global_position(), Vector3::new(2.0, 1.0, 1.0));
assert!(graph[a].global_visibility());
assert!(graph[b].global_visibility());
assert!(graph[c].global_visibility());
assert!(graph[d].global_visibility());
assert!(!graph.pool.try_get(a).unwrap().is_globally_enabled());
assert!(!graph[b].is_globally_enabled());
assert!(!graph[c].is_globally_enabled());
assert!(!graph[d].is_globally_enabled());
}
#[test]
fn test_typed_borrow() {
let mut graph = Graph::new();
let pivot = PivotBuilder::new(BaseBuilder::new()).build(&mut graph);
let rigid_body = RigidBodyBuilder::new(BaseBuilder::new()).build(&mut graph);
assert!(graph.pool.try_get(pivot).is_ok());
assert!(graph.pool.try_get(pivot.transmute::<Pivot>()).is_ok());
assert!(graph.pool.try_get(pivot.transmute::<RigidBody>()).is_err());
assert!(graph.pool.try_get(rigid_body).is_ok());
assert!(graph
.pool
.try_get(rigid_body.transmute::<RigidBody>())
.is_ok());
assert!(graph.pool.try_get(rigid_body.transmute::<Pivot>()).is_err());
}
}