bevy/bevy_transform/src/hierarchy_maintenance_system.rs
2020-01-19 02:02:12 -08:00

215 lines
7.8 KiB
Rust

#![allow(dead_code)]
use crate::{components::*, ecs::prelude::*};
use smallvec::SmallVec;
use std::collections::HashMap;
pub fn build(_: &mut World) -> Vec<Box<dyn Schedulable>> {
let missing_previous_parent_system = SystemBuilder::<()>::new("MissingPreviousParentSystem")
// Entities with missing `PreviousParent`
.with_query(<Read<Parent>>::query().filter(
!component::<PreviousParent>(),
))
.build(move |commands, world, _resource, query| {
// Add missing `PreviousParent` components
for (entity, _parent) in query.iter_entities(world) {
log::trace!("Adding missing PreviousParent to {}", entity);
commands.add_component(entity, PreviousParent(None));
}
});
let parent_update_system = SystemBuilder::<()>::new("ParentUpdateSystem")
// Entities with a removed `Parent`
.with_query(<Read<PreviousParent>>::query().filter(!component::<Parent>()))
// Entities with a changed `Parent`
.with_query(<(Read<Parent>, Write<PreviousParent>)>::query().filter(
changed::<Parent>(),
))
// Deleted Parents (ie Entities with `Children` and without a `LocalToWorld`).
.write_component::<Children>()
.build(move |commands, world, _resource, queries| {
// Entities with a missing `Parent` (ie. ones that have a `PreviousParent`), remove
// them from the `Children` of the `PreviousParent`.
for (entity, previous_parent) in queries.0.iter_entities(world) {
log::trace!("Parent was removed from {}", entity);
if let Some(previous_parent_entity) = previous_parent.0 {
if let Some(mut previous_parent_children) =
world.get_component_mut::<Children>(previous_parent_entity)
{
log::trace!(" > Removing {} from it's prev parent's children", entity);
previous_parent_children.0.retain(|e| *e != entity);
}
}
}
// Tracks all newly created `Children` Components this frame.
let mut children_additions =
HashMap::<Entity, SmallVec<[Entity; 8]>>::with_capacity(16);
// Entities with a changed Parent (that also have a PreviousParent, even if None)
for (entity, (parent, mut previous_parent)) in queries.1.iter_entities_mut(world) {
log::trace!("Parent changed for {}", entity);
// If the `PreviousParent` is not None.
if let Some(previous_parent_entity) = previous_parent.0 {
// New and previous point to the same Entity, carry on, nothing to see here.
if previous_parent_entity == parent.0 {
log::trace!(" > But the previous parent is the same, ignoring...");
continue;
}
// Remove from `PreviousParent.Children`.
if let Some(mut previous_parent_children) =
world.get_component_mut::<Children>(previous_parent_entity)
{
log::trace!(" > Removing {} from prev parent's children", entity);
(*previous_parent_children).0.retain(|e| *e != entity);
}
}
// Set `PreviousParent = Parent`.
*previous_parent = PreviousParent(Some(parent.0));
// Add to the parent's `Children` (either the real component, or
// `children_additions`).
log::trace!("Adding {} to it's new parent {}", entity, parent.0);
if let Some(mut new_parent_children) = world.get_component_mut::<Children>(parent.0)
{
// This is the parent
log::trace!(
" > The new parent {} already has a `Children`, adding to it.",
parent.0
);
(*new_parent_children).0.push(entity);
} else {
// The parent doesn't have a children entity, lets add it
log::trace!(
"The new parent {} doesn't yet have `Children` component.",
parent.0
);
children_additions
.entry(parent.0)
.or_insert_with(Default::default)
.push(entity);
}
}
// Flush the `children_additions` to the command buffer. It is stored separate to
// collect multiple new children that point to the same parent into the same
// SmallVec, and to prevent redundant add+remove operations.
children_additions.iter().for_each(|(k, v)| {
log::trace!("Flushing: Entity {} adding `Children` component {:?}", k, v);
commands.add_component(*k, Children::with(v));
});
});
vec![missing_previous_parent_system, parent_update_system]
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn correct_children() {
let _ = env_logger::builder().is_test(true).try_init();
let mut world = Universe::new().create_world();
let systems = build(&mut world);
// Add parent entities
let parent = *world
.insert(
(),
vec![(Translation::identity(), LocalToWorld::identity())],
)
.first()
.unwrap();
let children = world.insert(
(),
vec![
(
Translation::identity(),
LocalToParent::identity(),
LocalToWorld::identity(),
),
(
Translation::identity(),
LocalToParent::identity(),
LocalToWorld::identity(),
),
],
);
let (e1, e2) = (children[0], children[1]);
// Parent `e1` and `e2` to `parent`.
world.add_component(e1, Parent(parent));
world.add_component(e2, Parent(parent));
for system in systems.iter() {
system.run(&mut world);
system.command_buffer_mut().write(&mut world);
}
assert_eq!(
world
.get_component::<Children>(parent)
.unwrap()
.0
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![e1, e2]
);
// Parent `e1` to `e2`.
(*world.get_component_mut::<Parent>(e1).unwrap()).0 = e2;
// Run the system on it
for system in systems.iter() {
system.run(&mut world);
system.command_buffer_mut().write(&mut world);
}
assert_eq!(
world
.get_component::<Children>(parent)
.unwrap()
.0
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![e2]
);
assert_eq!(
world
.get_component::<Children>(e2)
.unwrap()
.0
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![e1]
);
world.delete(e1);
// Run the system on it
for system in systems.iter() {
system.run(&mut world);
system.command_buffer_mut().write(&mut world);
}
assert_eq!(
world
.get_component::<Children>(parent)
.unwrap()
.0
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![e2]
);
}
}