From 2bf26782e067e8f11ab00936c1e5be596cae115f Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Fri, 9 Oct 2026 20:03:05 +0300 Subject: [PATCH] universal `find_references_to` + support derived handles + tests --- fyrox-graph/src/lib.rs | 277 ++++++++++++++++++------------ fyrox-impl/src/scene/graph/mod.rs | 23 --- 2 files changed, 164 insertions(+), 136 deletions(-) diff --git a/fyrox-graph/src/lib.rs b/fyrox-graph/src/lib.rs index d78d18f7e..d879c674d 100644 --- a/fyrox-graph/src/lib.rs +++ b/fyrox-graph/src/lib.rs @@ -1492,6 +1492,35 @@ pub trait SceneGraph: 'static { } } } + + /// Tries to find references of the given node in other nodes. It could be used to check if the + /// node is used by some other scene node or not. Returns an array of handles, that references + /// the given node. This method is reflection-based, so it is quite slow and should not be used + /// every frame. It also correctly processes handles of derived types. + fn find_references_to( + &self, + target: Handle>, + ) -> Vec> { + let mut references = Vec::new(); + for (node_handle, node) in self.pair_iter() { + (node as &dyn Reflect).apply_recursively( + &mut |object| { + if let Some(handle) = object.as_handle() { + if handle.reflect_as_erased() == target.into() + && handle + .type_info_ref() + .derived_types + .contains(&TypeId::of::()) + { + references.push(node_handle); + } + } + }, + &[TypeId::of::()], + ); + } + references + } } /// Iterator that traverses tree in depth and returns shared references to nodes. @@ -1658,18 +1687,22 @@ mod test { #[derive(Visit, Reflect, PartialEq, Debug, Clone)] #[reflect(type_uuid = "34f5e3ea-3008-45e9-9e6e-0b8fbff1ac28")] - pub struct Base { + pub struct TestBase { name: String, - self_handle: Handle, + #[reflect(hidden)] + self_handle: Handle, is_resource_instance_root: bool, - original_handle_in_resource: Handle, - resource: Option>, - parent: Handle, - children: Vec>, + #[reflect(hidden)] + original_handle_in_resource: Handle, + resource: Option>, + #[reflect(hidden)] + parent: Handle, + #[reflect(hidden)] + children: Vec>, instance_id: Uuid, } - impl Default for Base { + impl Default for TestBase { fn default() -> Self { Self { name: Default::default(), @@ -1685,19 +1718,19 @@ mod test { } /// A set of useful methods that is possible to auto-implement. - pub trait BaseNodeTrait: Any + Debug + Deref + DerefMut + Send { + pub trait BaseNodeTrait: Any + Debug + Deref + DerefMut + Send { /// This method creates raw copy of a node, it should never be called in normal circumstances /// because internally nodes may (and most likely will) contain handles to other nodes. To /// correctly clone a node you have to use [copy_node](struct.Graph.html#method.copy_node). - fn clone_box(&self) -> Node; + fn clone_box(&self) -> TestNode; } impl BaseNodeTrait for T where T: Clone + NodeTrait + 'static, { - fn clone_box(&self) -> Node { - Node(Box::new(self.clone())) + fn clone_box(&self) -> TestNode { + TestNode(Box::new(self.clone())) } } @@ -1707,58 +1740,58 @@ mod test { // Essentially implements ObjectOrVariant for NodeTrait types. // See ObjectOrVariantHelper for the cause of the indirection. - impl ObjectOrVariantHelper for PhantomData { - fn convert_to_dest_type_helper(node: &Node) -> Option<&T> { + impl ObjectOrVariantHelper for PhantomData { + fn convert_to_dest_type_helper(node: &TestNode) -> Option<&T> { NodeAsAny::as_any(node.0.deref()).downcast_ref() } - fn convert_to_dest_type_helper_mut(node: &mut Node) -> Option<&mut T> { + fn convert_to_dest_type_helper_mut(node: &mut TestNode) -> Option<&mut T> { NodeAsAny::as_any_mut(node.0.deref_mut()).downcast_mut() } } #[derive(Debug, Reflect)] #[reflect(type_uuid = "e2c18cd6-f1bf-40e5-9ec6-0dc48cc75409")] - pub struct Node(#[reflect(deref, display_name = "Node")] Box); + pub struct TestNode(#[reflect(deref, display_name = "Node")] Box); - impl Clone for Node { + impl Clone for TestNode { fn clone(&self) -> Self { self.0.clone_box() } } - impl PartialEq for Node { + impl PartialEq for TestNode { fn eq(&self, other: &Self) -> bool { self.0.try_compare(other.0.deref()).unwrap_or_default() } } - impl Node { + impl TestNode { fn new(node: impl NodeTrait) -> Self { Self(Box::new(node)) } } - impl Visit for Node { + impl Visit for TestNode { fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult { self.0.visit(name, visitor) } } - impl NameProvider for Node { + impl NameProvider for TestNode { fn name(&self) -> &str { &self.name } } - impl Deref for Node { - type Target = Base; + impl Deref for TestNode { + type Target = TestBase; fn deref(&self) -> &Self::Target { self.0.deref() } } - impl DerefMut for Node { + impl DerefMut for TestNode { fn deref_mut(&mut self) -> &mut Self::Target { self.0.deref_mut() } @@ -1768,17 +1801,17 @@ mod test { /// control over instantiation process at deserialization. #[derive(Debug, Default, Clone, PartialEq, Reflect)] #[reflect(type_uuid = "08052dac-c0c0-4df3-8005-1330827bf9c2")] - pub struct NodeContainer(Option); + pub struct TestNodeContainer(Option); - impl Visit for NodeContainer { + impl Visit for TestNodeContainer { fn visit(&mut self, _name: &str, _visitor: &mut Visitor) -> VisitResult { // Dummy impl. Ok(()) } } - impl PayloadContainer for NodeContainer { - type Element = Node; + impl PayloadContainer for TestNodeContainer { + type Element = TestNode; fn new_empty() -> Self { Self(None) @@ -1809,10 +1842,10 @@ mod test { } } - impl NodeWrapper for Node { - type Base = Base; - type SceneGraph = Graph; - type ResourceData = Graph; + impl NodeWrapper for TestNode { + type Base = TestBase; + type SceneGraph = TestGraph; + type ResourceData = TestGraph; fn inner_ref(&self) -> &dyn Reflect { self.0.deref() @@ -1870,12 +1903,12 @@ mod test { #[derive(Default, Clone, Visit, Reflect, PartialEq, Debug)] #[reflect(type_uuid = "fc887063-7780-44af-8710-5e0bcf9a83fd")] - pub struct Graph { - root: Handle, - nodes: Pool, + pub struct TestGraph { + root: Handle, + nodes: Pool, } - impl ResourceData for Graph { + impl ResourceData for TestGraph { fn save(&mut self, _path: &Path) -> Result<(), Box> { Ok(()) } @@ -1889,8 +1922,8 @@ mod test { } } - impl PrefabData for Graph { - type Graph = Graph; + impl PrefabData for TestGraph { + type Graph = TestGraph; fn graph(&self) -> &Self::Graph { self @@ -1901,25 +1934,25 @@ mod test { } } - impl Index> for Graph { - type Output = Node; + impl Index> for TestGraph { + type Output = TestNode; #[inline] - fn index(&self, index: Handle) -> &Self::Output { + fn index(&self, index: Handle) -> &Self::Output { &self.nodes[index] } } - impl IndexMut> for Graph { + impl IndexMut> for TestGraph { #[inline] - fn index_mut(&mut self, index: Handle) -> &mut Self::Output { + fn index_mut(&mut self, index: Handle) -> &mut Self::Output { &mut self.nodes[index] } } - impl SceneGraph for Graph { - type Prefab = Graph; - type NodeWrapper = Node; + impl SceneGraph for TestGraph { + type Prefab = TestGraph; + type NodeWrapper = TestNode; fn summary(&self) -> String { "Summary".to_string() @@ -2037,11 +2070,14 @@ mod test { self.nodes.iter_mut() } - fn try_get>(&self, handle: Handle) -> Result<&U, PoolError> { + fn try_get>( + &self, + handle: Handle, + ) -> Result<&U, PoolError> { self.nodes.try_get(handle) } - fn try_get_mut>( + fn try_get_mut>( &mut self, handle: Handle, ) -> Result<&mut U, PoolError> { @@ -2049,7 +2085,7 @@ mod test { } } - fn remap_handles(old_new_mapping: &NodeHandleMap, dest_graph: &mut Graph) { + fn remap_handles(old_new_mapping: &NodeHandleMap, dest_graph: &mut TestGraph) { // Iterate over instantiated nodes and remap handles. for &new_node_handle in old_new_mapping.inner().values() { old_new_mapping.remap_handles( @@ -2059,19 +2095,19 @@ mod test { } } - fn clear_links(mut node: Node) -> Node { + fn clear_links(mut node: TestNode) -> TestNode { node.children.clear(); node.parent = Handle::NONE; node } - impl Graph { + impl TestGraph { #[inline] pub fn copy_node( &self, - node_handle: Handle, - dest_graph: &mut Graph, - ) -> (Handle, NodeHandleMap) { + node_handle: Handle, + dest_graph: &mut TestGraph, + ) -> (Handle, NodeHandleMap) { let mut old_new_mapping = NodeHandleMap::default(); let root_handle = self.copy_node_raw(node_handle, dest_graph, &mut old_new_mapping); @@ -2081,10 +2117,10 @@ mod test { } fn copy_node_raw( &self, - root_handle: Handle, - dest_graph: &mut Graph, - old_new_mapping: &mut NodeHandleMap, - ) -> Handle { + root_handle: Handle, + dest_graph: &mut TestGraph, + old_new_mapping: &mut NodeHandleMap, + ) -> Handle { let src_node = &self.nodes[root_handle]; let dest_node = clear_links(src_node.clone()); let dest_copy_handle = dest_graph.add_node(dest_node); @@ -2101,39 +2137,39 @@ mod test { } #[derive(Clone, Reflect, Visit, PartialEq, Default, Debug)] - #[reflect(derived_type = "Node")] + #[reflect(derived_type = "TestNode")] #[reflect(type_uuid = "c7452fb6-78c1-4e77-a2f9-d9ae31f50327")] - pub struct Pivot { - base: Base, + pub struct TestPivot { + base: TestBase, } - impl NodeTrait for Pivot {} + impl NodeTrait for TestPivot {} - impl Deref for Pivot { - type Target = Base; + impl Deref for TestPivot { + type Target = TestBase; fn deref(&self) -> &Self::Target { &self.base } } - impl DerefMut for Pivot { + impl DerefMut for TestPivot { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.base } } #[derive(Clone, Reflect, Visit, PartialEq, Default, Debug)] - #[reflect(derived_type = "Node")] + #[reflect(derived_type = "TestNode")] #[reflect(type_uuid = "17cc2d25-6ba4-4e2d-a31e-867e429bc659")] pub struct RigidBody { - base: Base, + base: TestBase, } impl NodeTrait for RigidBody {} impl Deref for RigidBody { - type Target = Base; + type Target = TestBase; fn deref(&self) -> &Self::Target { &self.base @@ -2147,25 +2183,25 @@ mod test { } #[derive(Clone, Reflect, Visit, PartialEq, Default, Debug)] - #[reflect(derived_type = "Node")] + #[reflect(derived_type = "TestNode")] #[reflect(type_uuid = "1f869298-37b1-4153-a2a9-6576daa0e8b3")] - pub struct Joint { - base: Base, + pub struct TestJoint { + base: TestBase, connected_body1: Handle, connected_body2: Handle, } - impl NodeTrait for Joint {} + impl NodeTrait for TestJoint {} - impl Deref for Joint { - type Target = Base; + impl Deref for TestJoint { + type Target = TestBase; fn deref(&self) -> &Self::Target { &self.base } } - impl DerefMut for Joint { + impl DerefMut for TestJoint { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.base } @@ -2173,19 +2209,19 @@ mod test { #[test] fn test_set_child_position() { - let mut graph = Graph::default(); + let mut graph = TestGraph::default(); - let root = graph.add_node(Node::new(Pivot::default())); - let a = graph.add_node(Node::new(Pivot::default())); - let b = graph.add_node(Node::new(Pivot::default())); - let c = graph.add_node(Node::new(Pivot::default())); - let d = graph.add_node(Node::new(Pivot::default())); + let root = graph.add_node(TestNode::new(TestPivot::default())); + let a = graph.add_node(TestNode::new(TestPivot::default())); + let b = graph.add_node(TestNode::new(TestPivot::default())); + let c = graph.add_node(TestNode::new(TestPivot::default())); + let d = graph.add_node(TestNode::new(TestPivot::default())); graph.link_nodes(a, root); graph.link_nodes(b, root); graph.link_nodes(c, root); graph.link_nodes(d, root); - assert!(graph.try_get_of_type::(a).is_ok()); + assert!(graph.try_get_of_type::(a).is_ok()); let root_ref = &mut graph[root]; assert_eq!(root_ref.set_child_position(a, 0), Some(0)); @@ -2226,19 +2262,19 @@ mod test { #[test] fn test_derived_handles_mapping() { - let mut prefab_graph = Graph::default(); + let mut prefab_graph = TestGraph::default(); - prefab_graph.add_node(Node::new(Pivot::default())); - let rigid_body = prefab_graph.add_node(Node::new(RigidBody::default())); - let rigid_body2 = prefab_graph.add_node(Node::new(RigidBody::default())); - let joint = prefab_graph.add_node(Node::new(Joint { - base: Base::default(), + prefab_graph.add_node(TestNode::new(TestPivot::default())); + let rigid_body = prefab_graph.add_node(TestNode::new(RigidBody::default())); + let rigid_body2 = prefab_graph.add_node(TestNode::new(RigidBody::default())); + let joint = prefab_graph.add_node(TestNode::new(TestJoint { + base: TestBase::default(), connected_body1: rigid_body.transmute(), connected_body2: rigid_body2.transmute(), })); - let mut scene_graph = Graph::default(); - let root = scene_graph.add_node(Node::new(Pivot::default())); + let mut scene_graph = TestGraph::default(); + let root = scene_graph.add_node(TestNode::new(TestPivot::default())); let (_, mapping) = prefab_graph.copy_node(root, &mut scene_graph); let rigid_body_copy = mapping @@ -2256,7 +2292,7 @@ mod test { let joint_copy = mapping.inner().get(&joint).cloned().unwrap(); let joint_copy_ref = scene_graph.nodes[joint_copy] .inner_ref() - .downcast_ref::() + .downcast_ref::() .unwrap(); assert_eq!(joint_copy_ref.connected_body1, rigid_body_copy); assert_eq!(joint_copy_ref.connected_body2, rigid_body2_copy); @@ -2264,24 +2300,24 @@ mod test { #[test] fn test_change_root() { - let mut graph = Graph::default(); + let mut graph = TestGraph::default(); // Root_ // |_A_ // |_B // |_C_ // |_D - let root = graph.add_node(Node::new(Pivot::default())); - let d = graph.add_node(Node::new(Pivot::default())); - let c = graph.add_node(Node::new(Pivot { - base: Base { + let root = graph.add_node(TestNode::new(TestPivot::default())); + let d = graph.add_node(TestNode::new(TestPivot::default())); + let c = graph.add_node(TestNode::new(TestPivot { + base: TestBase { children: vec![d], ..Default::default() }, })); - let b = graph.add_node(Node::new(Pivot::default())); - let a = graph.add_node(Node::new(Pivot { - base: Base { + let b = graph.add_node(TestNode::new(TestPivot::default())); + let a = graph.add_node(TestNode::new(TestPivot { + base: TestBase { children: vec![b, c], ..Default::default() }, @@ -2299,7 +2335,7 @@ mod test { // |_Root assert_eq!(graph.root, c); - assert_eq!(graph[graph.root].parent, Handle::::NONE); + assert_eq!(graph[graph.root].parent, Handle::::NONE); assert_eq!(graph[graph.root].children.len(), 3); assert_eq!(graph[graph.root].children[0], d); @@ -2322,14 +2358,14 @@ mod test { graph.apply_link_scheme(link_scheme); assert_eq!(graph.root, root); - assert_eq!(graph[graph.root].parent, Handle::::NONE); + assert_eq!(graph[graph.root].parent, Handle::::NONE); assert_eq!(graph[graph.root].children, vec![a]); assert_eq!(graph[a].parent, root); assert_eq!(graph[a].children, vec![b, c]); assert_eq!(graph[b].parent, a); - assert_eq!(graph[b].children, Vec::>::new()); + assert_eq!(graph[b].children, Vec::>::new()); assert_eq!(graph[c].parent, a); assert_eq!(graph[c].children, vec![d]); @@ -2337,7 +2373,7 @@ mod test { #[test] fn test_traverse_iter() { - let mut graph = Graph::default(); + let mut graph = TestGraph::default(); // Root_ // |_A_ @@ -2347,29 +2383,29 @@ mod test { // | | |_E // | |_F // |_X - let root = graph.add_node(Node::new(Pivot::default())); - let e = graph.add_node(Node::new(Pivot::default())); - let d = graph.add_node(Node::new(Pivot { - base: Base { + let root = graph.add_node(TestNode::new(TestPivot::default())); + let e = graph.add_node(TestNode::new(TestPivot::default())); + let d = graph.add_node(TestNode::new(TestPivot { + base: TestBase { children: vec![e], ..Default::default() }, })); - let c = graph.add_node(Node::new(Pivot { - base: Base { + let c = graph.add_node(TestNode::new(TestPivot { + base: TestBase { children: vec![d], ..Default::default() }, })); - let b = graph.add_node(Node::new(Pivot::default())); - let a = graph.add_node(Node::new(Pivot { - base: Base { + let b = graph.add_node(TestNode::new(TestPivot::default())); + let a = graph.add_node(TestNode::new(TestPivot { + base: TestBase { children: vec![b, c], ..Default::default() }, })); - let f = graph.add_node(Node::new(Pivot::default())); - let x = graph.add_node(Node::new(Pivot::default())); + let f = graph.add_node(TestNode::new(TestPivot::default())); + let x = graph.add_node(TestNode::new(TestPivot::default())); graph.link_nodes(a, root); graph.link_nodes(f, a); graph.link_nodes(x, root); @@ -2433,4 +2469,19 @@ mod test { drop(iter_mut); } + + #[test] + fn test_find_references() { + let mut graph = TestGraph::default(); + graph.add_node(TestNode::new(TestPivot::default())); + let rigid_body = graph.add_node(TestNode::new(RigidBody::default())); + let rigid_body2 = graph.add_node(TestNode::new(RigidBody::default())); + let joint = graph.add_node(TestNode::new(TestJoint { + base: TestBase::default(), + connected_body1: rigid_body.transmute(), + connected_body2: rigid_body2.transmute(), + })); + assert_eq!(graph.find_references_to(rigid_body), vec![joint]); + assert_eq!(graph.find_references_to(rigid_body2), vec![joint]); + } } diff --git a/fyrox-impl/src/scene/graph/mod.rs b/fyrox-impl/src/scene/graph/mod.rs index e2a51648f..2ef49fc5b 100644 --- a/fyrox-impl/src/scene/graph/mod.rs +++ b/fyrox-impl/src/scene/graph/mod.rs @@ -533,29 +533,6 @@ impl Graph { self.link_nodes(prev_root, handle); } - /// Tries to find references of the given node in other scene nodes. It could be used to check if the node is - /// used by some other scene node or not. Returns an array of nodes, that references the given node. This method - /// is reflection-based, so it is quite slow and should not be used every frame. - pub fn find_references_to( - &self, - target: Handle>, - ) -> Vec> { - let mut references = Vec::new(); - for (node_handle, node) in self.pair_iter() { - (node as &dyn Reflect).apply_recursively( - &mut |object| { - if let Some(handle) = object.downcast_ref::>() { - if *handle == target { - references.push(node_handle); - } - } - }, - &[TypeId::of::()], - ); - } - references - } - /// Sets global position of a scene node. Internally, this method converts the given position /// to the local space of the parent node of the given scene node and sets it as local position /// of the node. In other words, this method does not modify global position itself, but calculates