scene: implement WorldTransform struct to simplify getting the world transform of scene nodes
This commit is contained in:
parent
0668be06e2
commit
2daf617ba3
|
@ -6,5 +6,6 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.81"
|
||||||
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
||||||
lyra-math = { path = "../lyra-math" }
|
lyra-math = { path = "../lyra-math" }
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
use std::collections::VecDeque;
|
mod node;
|
||||||
|
pub use node::*;
|
||||||
|
|
||||||
use lyra_ecs::{query::Entities, relation::ChildOf, Bundle, Component, Entity, World};
|
mod world_transform;
|
||||||
|
pub use world_transform::*;
|
||||||
|
|
||||||
|
use lyra_ecs::{query::{Entities, ViewOne}, relation::ChildOf, Bundle, Component, World};
|
||||||
|
use lyra_math::Transform;
|
||||||
|
|
||||||
// So we can use lyra_ecs::Component derive macro
|
// So we can use lyra_ecs::Component derive macro
|
||||||
pub(crate) mod lyra_engine {
|
pub(crate) mod lyra_engine {
|
||||||
|
@ -9,10 +14,6 @@ pub(crate) mod lyra_engine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod node;
|
|
||||||
use lyra_math::{Transform, Vec3};
|
|
||||||
pub use node::*;
|
|
||||||
|
|
||||||
/// A flag spawned on all scene node entities
|
/// A flag spawned on all scene node entities
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct SceneNodeFlag;
|
pub struct SceneNodeFlag;
|
||||||
|
@ -41,7 +42,7 @@ impl SceneGraph {
|
||||||
|
|
||||||
/// Create a new SceneGraph inside an existing ECS World.
|
/// Create a new SceneGraph inside an existing ECS World.
|
||||||
pub fn from_world(mut world: World) -> Self {
|
pub fn from_world(mut world: World) -> Self {
|
||||||
let root_en = world.spawn((Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)), SceneNodeRoot));
|
let root_en = world.spawn((WorldTransform::default(), Transform::default(), SceneNodeRoot));
|
||||||
let root = SceneNode::new(None, root_en);
|
let root = SceneNode::new(None, root_en);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -50,70 +51,21 @@ impl SceneGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume the SceneGraph, inserting its internal world into the provided world.
|
|
||||||
///
|
|
||||||
/// The `into` World will contain all nodes of the SceneGraph. You could iterate through the
|
|
||||||
/// SceneGraph manually if you'd like. The root entity has a [`SceneNodeRoot`] flag component,
|
|
||||||
/// and a Transform component. The children nodes have a [`SceneNodeFlag`] component and a
|
|
||||||
/// Transform component. The transforms of the children will be local, you must find its world
|
|
||||||
/// transform by traversing up the hierarchy of the scene manually with the `ChildOf` relation.
|
|
||||||
///
|
|
||||||
/// Returns: the Entity of the root node in the provided world.
|
|
||||||
pub fn into_world(mut self, into: &mut World) -> Entity {
|
|
||||||
// first insert the root entity into the World.
|
|
||||||
let v = self.world.view_one::<&Transform>(self.root_node.entity());
|
|
||||||
let pos = *v.get().unwrap();
|
|
||||||
let new_root = into.spawn((pos, SceneNodeRoot));
|
|
||||||
|
|
||||||
// now process the children of the root node.
|
|
||||||
Self::traverse_inserting_into(&mut self.world, self.root_node.entity(), new_root, into);
|
|
||||||
new_root
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Recursively traverse the `from` world, starting at `node_en`, inserting the
|
|
||||||
/// children entity into the world.
|
|
||||||
///
|
|
||||||
/// Parameters:
|
|
||||||
/// * `scene_world` - The world that the SceneGraph exists in
|
|
||||||
/// * `scene_en` - The entity of the parent entity inside of the `scene_world`.
|
|
||||||
/// * `parent_en` - The entity of the parent entity inside of the `into` world.
|
|
||||||
/// * `into` - The world to insert the SceneGraph world into.
|
|
||||||
fn traverse_inserting_into(scene_world: &mut World, scene_en: Entity, parent_en: Entity, into: &mut World) {
|
|
||||||
let v = scene_world.view::<(Entities, &Transform)>()
|
|
||||||
.relates_to::<ChildOf>(scene_en);
|
|
||||||
|
|
||||||
// unfortunately, the borrow checker exists, and wasn't happy that `scene_world` was
|
|
||||||
// immutably borrowed with the view (v), and being mutably borrowed in the
|
|
||||||
// recursive call.
|
|
||||||
let mut child_entities = VecDeque::new();
|
|
||||||
|
|
||||||
for ((child_scene_en, pos), _rel) in v.iter() {
|
|
||||||
let into_en = into.spawn((*pos, SceneNodeFlag));
|
|
||||||
into.add_relation(into_en, ChildOf, parent_en);
|
|
||||||
|
|
||||||
child_entities.push_back((child_scene_en, into_en));
|
|
||||||
}
|
|
||||||
|
|
||||||
while let Some((child_scene_en, into_en)) = child_entities.pop_front() {
|
|
||||||
Self::traverse_inserting_into(scene_world, child_scene_en, into_en, into);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a node to the root node of the SceneGraph.
|
/// Adds a node to the root node of the SceneGraph.
|
||||||
///
|
///
|
||||||
/// The spawned entity will have a `ChildOf` relation targeting the root node, the
|
/// The spawned entity will have a `ChildOf` relation targeting the root node, the
|
||||||
/// `SceneNodeFlag` component is also added to the entity.
|
/// `SceneNodeFlag` component is also added to the entity.
|
||||||
pub fn add_node<B: Bundle>(&mut self, local_transform: Transform, bundle: B) -> SceneNode {
|
pub fn add_node<B: Bundle>(&mut self, bundle: B) -> SceneNode {
|
||||||
let node = self.root_node.clone();
|
let node = self.root_node.clone();
|
||||||
self.add_node_under(&node, local_transform, bundle)
|
self.add_node_under(&node, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a node under a parent node.
|
/// Add a node under a parent node.
|
||||||
///
|
///
|
||||||
/// The spawned entity will have a `ChildOf` relation targeting the provided parent node,
|
/// The spawned entity will have a `ChildOf` relation targeting the provided parent node,
|
||||||
/// the `SceneNodeFlag` component is also added to the entity.
|
/// the `SceneNodeFlag` component is also added to the entity.
|
||||||
pub fn add_node_under<B: Bundle>(&mut self, parent: &SceneNode, local_transform: Transform, bundle: B) -> SceneNode {
|
pub fn add_node_under<B: Bundle>(&mut self, parent: &SceneNode, bundle: B) -> SceneNode {
|
||||||
world_add_child_node(&mut self.world, parent, local_transform, bundle)
|
world_add_child_node(&mut self.world, parent, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a component bundle to a SceneNode.
|
/// Insert a component bundle to a SceneNode.
|
||||||
|
@ -133,29 +85,32 @@ impl SceneGraph {
|
||||||
/// Traverses down the SceneGraph, calling `callback` with each SceneNode and its world transform.
|
/// Traverses down the SceneGraph, calling `callback` with each SceneNode and its world transform.
|
||||||
///
|
///
|
||||||
/// The traversal does not include the root scene node.
|
/// The traversal does not include the root scene node.
|
||||||
pub fn traverse_down<F>(&self, mut callback: F)
|
pub fn traverse_down<F, Q>(&self, mut callback: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&World, &SceneNode, Transform),
|
F: FnMut(&World, &SceneNode, ViewOne<Q::Query>),
|
||||||
|
Q: lyra_ecs::query::AsQuery,
|
||||||
{
|
{
|
||||||
self.traverse_down_from(self.root_node.clone(), &mut callback);
|
self.traverse_down_from::<F, Q>(self.root_node.clone(), &mut callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively Traverses down the SceneGraph from a starting node, calling `callback` with each
|
/// Recursively Traverses down the SceneGraph from a starting node, calling `callback` with each
|
||||||
/// SceneNode and its world transform.
|
/// SceneNode and its world transform.
|
||||||
fn traverse_down_from<F>(&self, start: SceneNode, callback: &mut F)
|
fn traverse_down_from<F, Q>(&self, start: SceneNode, callback: &mut F)
|
||||||
where
|
where
|
||||||
F: FnMut(&World, &SceneNode, Transform),
|
F: FnMut(&World, &SceneNode, ViewOne<Q::Query>),
|
||||||
|
Q: lyra_ecs::query::AsQuery,
|
||||||
{
|
{
|
||||||
let v = self.world
|
let v = self.world
|
||||||
.view::<(Entities, &Transform)>()
|
.view::<Entities>()
|
||||||
.relates_to::<ChildOf>(start.entity());
|
.relates_to::<ChildOf>(start.entity());
|
||||||
|
|
||||||
for ((e, _), _rel) in v.iter() {
|
for (e, _rel) in v.iter() {
|
||||||
let node = SceneNode::new(Some(start.entity()), e);
|
let node = SceneNode::new(Some(start.entity()), e);
|
||||||
let world_pos = node.world_transform(self);
|
//let world_pos = node.world_transform(self);
|
||||||
callback(&self.world, &node, world_pos);
|
let v = self.world.view_one::<Q>(e);
|
||||||
|
callback(&self.world, &node, v);
|
||||||
|
|
||||||
self.traverse_down_from(node, callback);
|
self.traverse_down_from::<F, Q>(node, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,9 +128,9 @@ impl SceneGraph {
|
||||||
///
|
///
|
||||||
/// The spawned entity will have a `ChildOf` relation targeting the provided parent node,
|
/// The spawned entity will have a `ChildOf` relation targeting the provided parent node,
|
||||||
/// the `SceneNodeFlag` component is also added to the entity.
|
/// the `SceneNodeFlag` component is also added to the entity.
|
||||||
pub(crate) fn world_add_child_node<B: Bundle>(world: &mut World, parent: &SceneNode, local_transform: Transform, bundle: B) -> SceneNode {
|
pub(crate) fn world_add_child_node<B: Bundle>(world: &mut World, parent: &SceneNode, bundle: B) -> SceneNode {
|
||||||
let e = world.spawn(bundle);
|
let e = world.spawn(bundle);
|
||||||
world.insert(e, (SceneNodeFlag, local_transform));
|
world.insert(e, (SceneNodeFlag,));
|
||||||
world.add_relation(e, ChildOf, parent.entity());
|
world.add_relation(e, ChildOf, parent.entity());
|
||||||
|
|
||||||
SceneNode::new(Some(parent.entity()), e)
|
SceneNode::new(Some(parent.entity()), e)
|
||||||
|
@ -183,10 +138,10 @@ pub(crate) fn world_add_child_node<B: Bundle>(world: &mut World, parent: &SceneN
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use lyra_ecs::Component;
|
use lyra_ecs::{query::{filter::{Has, Not}, Entities}, relation::{ChildOf, RelationOriginComponent}, Component};
|
||||||
use lyra_math::{Transform, Vec3};
|
use lyra_math::{Transform, Vec3};
|
||||||
|
|
||||||
use crate::{lyra_engine, SceneGraph};
|
use crate::{lyra_engine, WorldTransform, SceneGraph};
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct FakeMesh;
|
pub struct FakeMesh;
|
||||||
|
@ -195,7 +150,7 @@ pub mod tests {
|
||||||
fn single_node_hierarchy() {
|
fn single_node_hierarchy() {
|
||||||
let mut scene = SceneGraph::new();
|
let mut scene = SceneGraph::new();
|
||||||
|
|
||||||
let a = scene.add_node(Transform::from_translation(Vec3::new(10.0, 10.0, 10.0)), FakeMesh);
|
let a = scene.add_node((Transform::from_translation(Vec3::new(10.0, 10.0, 10.0)), FakeMesh));
|
||||||
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,10 +158,10 @@ pub mod tests {
|
||||||
fn double_node_hierarchy() {
|
fn double_node_hierarchy() {
|
||||||
let mut scene = SceneGraph::new();
|
let mut scene = SceneGraph::new();
|
||||||
|
|
||||||
let a = scene.add_node(Transform::from_translation(Vec3::new(10.0, 10.0, 10.0)), FakeMesh);
|
let a = scene.add_node((Transform::from_translation(Vec3::new(10.0, 10.0, 10.0)), FakeMesh));
|
||||||
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
||||||
|
|
||||||
let b = a.add_node(&mut scene, Transform::from_translation(Vec3::new(50.0, 50.0, 50.0)), FakeMesh);
|
let b = a.add_node(&mut scene, (Transform::from_translation(Vec3::new(50.0, 50.0, 50.0)), FakeMesh));
|
||||||
assert!(b.parent(&scene).unwrap() == a);
|
assert!(b.parent(&scene).unwrap() == a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,68 +171,25 @@ pub mod tests {
|
||||||
|
|
||||||
let mut scene = SceneGraph::new();
|
let mut scene = SceneGraph::new();
|
||||||
|
|
||||||
let a = scene.add_node(Transform::from_translation(v2s[0]), FakeMesh);
|
let a = scene.add_node((WorldTransform::default(), Transform::from_translation(v2s[0]), FakeMesh));
|
||||||
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
||||||
let b = a.add_node(&mut scene, Transform::from_translation(v2s[1]), FakeMesh);
|
let b = a.add_node(&mut scene, (WorldTransform::default(), Transform::from_translation(v2s[1]), FakeMesh));
|
||||||
assert!(b.parent(&scene).unwrap() == a);
|
assert!(b.parent(&scene).unwrap() == a);
|
||||||
|
|
||||||
|
let view = scene.world.view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
||||||
|
crate::system_update_world_transforms(&scene.world, view).unwrap();
|
||||||
|
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
scene.traverse_down(|_, _, pos| {
|
scene.traverse_down::<_, &WorldTransform>(|_, _, v| {
|
||||||
|
let pos = v.get().unwrap();
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
assert_eq!(pos, Transform::from_translation(v2s[idx]));
|
assert_eq!(**pos, Transform::from_translation(v2s[idx]));
|
||||||
} else if idx == 1 {
|
} else if idx == 1 {
|
||||||
let t = v2s.iter().sum();
|
let t = v2s.iter().sum();
|
||||||
assert_eq!(pos, Transform::from_translation(t));
|
assert_eq!(**pos, Transform::from_translation(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
idx += 1;
|
idx += 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #[test]
|
|
||||||
fn inserting_and_from_world() {
|
|
||||||
let v2s = vec![Vec3::new(10.0, 10.0, 10.0), Vec3::new(50.0, 50.0, 50.0)];
|
|
||||||
|
|
||||||
let mut scene = SceneGraph::new();
|
|
||||||
|
|
||||||
let a = scene.add_node(Transform::from_translation(v2s[0]), FakeMesh);
|
|
||||||
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
|
||||||
let b = a.add_node(&mut scene, Transform::from_translation(v2s[1]), FakeMesh);
|
|
||||||
assert!(b.parent(&scene).unwrap() == a);
|
|
||||||
|
|
||||||
let mut other_world = World::new();
|
|
||||||
let root = scene.into_world(&mut other_world);
|
|
||||||
|
|
||||||
// check all of the entities inside of the World
|
|
||||||
let (root_pos, _) = other_world.view_one::<(&Transform, &SceneNodeRoot)>(root).get().unwrap();
|
|
||||||
assert_eq!(*root_pos, Transform::from_xyz(0.0, 0.0, 0.0));
|
|
||||||
|
|
||||||
let ((child_en, child_pos), _) = other_world.view::<(Entities, &Transform)>()
|
|
||||||
.relates_to::<ChildOf>(root).iter().next().unwrap();
|
|
||||||
assert_eq!(*child_pos, Transform::from_translation(v2s[0]));
|
|
||||||
|
|
||||||
let ((_, childchild_pos), _) = other_world.view::<(Entities, &Transform)>()
|
|
||||||
.relates_to::<ChildOf>(child_en).iter().next().unwrap();
|
|
||||||
assert_eq!(*childchild_pos, Transform::from_translation(v2s[1]));
|
|
||||||
|
|
||||||
drop(root_pos);
|
|
||||||
drop(child_pos);
|
|
||||||
drop(childchild_pos);
|
|
||||||
|
|
||||||
// Now get the SceneGraph inside the World and use the nice utility tools to traverse it.
|
|
||||||
let scene = SceneGraph::from_world(&mut other_world, root)
|
|
||||||
.expect("Failed to get SceneGraph from World!");
|
|
||||||
|
|
||||||
let mut idx = 0;
|
|
||||||
scene.traverse_down(|_e, pos| {
|
|
||||||
if idx == 0 {
|
|
||||||
assert_eq!(pos, Transform::from_translation(v2s[idx]));
|
|
||||||
} else if idx == 1 {
|
|
||||||
let t = v2s.iter().sum();
|
|
||||||
assert_eq!(pos, Transform::from_translation(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
idx += 1;
|
|
||||||
});
|
|
||||||
} */
|
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
use lyra_ecs::{query::Entities, relation::{ChildOf, RelationOriginComponent}, Bundle, Entity};
|
use lyra_ecs::{query::Entities, relation::{ChildOf, RelationOriginComponent}, Bundle, Entity};
|
||||||
use lyra_math::Transform;
|
|
||||||
|
|
||||||
use crate::SceneGraph;
|
use crate::SceneGraph;
|
||||||
|
|
||||||
|
@ -52,11 +51,11 @@ impl SceneNode {
|
||||||
///
|
///
|
||||||
/// The spawned entity backing the scene node will have a `ChildOf` relation targeting
|
/// The spawned entity backing the scene node will have a `ChildOf` relation targeting
|
||||||
/// the provided parent node, the `SceneNodeFlag` component is also added to the entity.
|
/// the provided parent node, the `SceneNodeFlag` component is also added to the entity.
|
||||||
pub fn add_node<B: Bundle>(&self, graph: &mut SceneGraph, local_transform: Transform, bundle: B) -> SceneNode {
|
pub fn add_node<B: Bundle>(&self, graph: &mut SceneGraph, bundle: B) -> SceneNode {
|
||||||
graph.add_node_under(self, local_transform, bundle)
|
graph.add_node_under(self, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_transform(&self, graph: &SceneGraph) -> Transform {
|
/* pub fn local_transform(&self, graph: &SceneGraph) -> Transform {
|
||||||
let v = graph.world.view_one::<&Transform>(self.entity);
|
let v = graph.world.view_one::<&Transform>(self.entity);
|
||||||
|
|
||||||
let t = v.get().expect("Somehow the SceneNode is missing its Transform!");
|
let t = v.get().expect("Somehow the SceneNode is missing its Transform!");
|
||||||
|
@ -76,26 +75,31 @@ impl SceneNode {
|
||||||
self.local_transform(graph)
|
self.local_transform(graph)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use lyra_ecs::{query::{filter::{Has, Not}, Entities}, relation::{ChildOf, RelationOriginComponent}};
|
||||||
use lyra_math::{Transform, Vec3};
|
use lyra_math::{Transform, Vec3};
|
||||||
|
|
||||||
use crate::{tests::FakeMesh, SceneGraph};
|
use crate::{tests::FakeMesh, WorldTransform, SceneGraph};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn node_world_transform() {
|
fn node_world_transform() {
|
||||||
let mut scene = SceneGraph::new();
|
let mut scene = SceneGraph::new();
|
||||||
|
|
||||||
let a = scene.add_node(Transform::from_translation(Vec3::new(10.0, 10.0, 10.0)), FakeMesh);
|
let a = scene.add_node((WorldTransform::default(), Transform::from_translation(Vec3::new(10.0, 10.0, 10.0)), FakeMesh));
|
||||||
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
assert!(a.parent(&scene).unwrap() == scene.root_node);
|
||||||
|
|
||||||
let b = a.add_node(&mut scene, Transform::from_translation(Vec3::new(50.0, 50.0, 50.0)), FakeMesh);
|
let b = a.add_node(&mut scene, (WorldTransform::default(), Transform::from_translation(Vec3::new(50.0, 50.0, 50.0)), FakeMesh));
|
||||||
assert!(b.parent(&scene).unwrap() == a);
|
assert!(b.parent(&scene).unwrap() == a);
|
||||||
|
|
||||||
let wrld_tran = b.world_transform(&scene);
|
// update global transforms
|
||||||
assert_eq!(wrld_tran, Transform::from_translation(Vec3::new(60.0, 60.0, 60.0)));
|
let view = scene.world.view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
||||||
|
crate::system_update_world_transforms(&scene.world, view).unwrap();
|
||||||
|
|
||||||
|
let tran = scene.world.view_one::<&WorldTransform>(b.entity).get().unwrap();
|
||||||
|
assert_eq!(**tran, Transform::from_translation(Vec3::new(60.0, 60.0, 60.0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use lyra_ecs::{query::{filter::{Has, Not}, Entities, View}, relation::{ChildOf, RelationOriginComponent}, Component, Entity, World};
|
||||||
|
use lyra_math::Transform;
|
||||||
|
use crate::lyra_engine;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Default, Component)]
|
||||||
|
pub struct WorldTransform(pub(crate) Transform);
|
||||||
|
|
||||||
|
impl Deref for WorldTransform {
|
||||||
|
type Target = Transform;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A system that updates the [`WorldTransform`]'s for entities and their children.
|
||||||
|
///
|
||||||
|
/// For entities without parents, this will update world transform to match local transform.
|
||||||
|
/// For any children entities, their [`GlobalTransform`]s will be updated to reflect the changes
|
||||||
|
/// of its parent entity.
|
||||||
|
pub fn system_update_world_transforms(world: &World, view: View<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>) -> anyhow::Result<()> {
|
||||||
|
|
||||||
|
for (en, mut world_tran, tran, _) in view.into_iter() {
|
||||||
|
world_tran.0 = *tran;
|
||||||
|
println!("Found entity {:?} at {:?}", en, world_tran.translation);
|
||||||
|
|
||||||
|
recurse_update_trans(world, &world_tran, en);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recurse_update_trans(world: &World, parent_transform: &WorldTransform, entity: Entity) {
|
||||||
|
// store entities and their world transform to process outside of the view.
|
||||||
|
// it must be done after to avoid attempts of multiple mutable borrows to the archetype column
|
||||||
|
// with WorldTransform.
|
||||||
|
let mut next_entities = vec![];
|
||||||
|
|
||||||
|
for ((en, mut world_tran, tran), _) in
|
||||||
|
world.view::<(Entities, &mut WorldTransform, &Transform)>()
|
||||||
|
.relates_to::<ChildOf>(entity)
|
||||||
|
.into_iter()
|
||||||
|
{
|
||||||
|
world_tran.0 = parent_transform.0 + *tran;
|
||||||
|
next_entities.push((en, world_tran.0.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
next_entities.reverse();
|
||||||
|
while let Some((en, pos)) = next_entities.pop() {
|
||||||
|
recurse_update_trans(world, &WorldTransform(pos), en);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use lyra_ecs::{query::{filter::{Has, Not}, Entities}, relation::{ChildOf, RelationOriginComponent}, World};
|
||||||
|
use lyra_math::Transform;
|
||||||
|
|
||||||
|
use crate::{system_update_world_transforms, WorldTransform};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system() {
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
let parent = world.spawn((WorldTransform::default(), Transform::from_xyz(10.0, 10.0, 10.0)));
|
||||||
|
let child = world.spawn((WorldTransform::default(), Transform::from_xyz(15.0, 15.0, 15.0)));
|
||||||
|
world.add_relation(child, ChildOf, parent);
|
||||||
|
|
||||||
|
let view = world.view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
||||||
|
system_update_world_transforms(&world, view).unwrap();
|
||||||
|
|
||||||
|
let g = world.view_one::<&WorldTransform>(child).get().unwrap();
|
||||||
|
assert_eq!(**g, Transform::from_xyz(25.0, 25.0, 25.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_many_entities() {
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
let parent = world.spawn((WorldTransform::default(), Transform::from_xyz(10.0, 10.0, 10.0)));
|
||||||
|
|
||||||
|
let mut children = vec![];
|
||||||
|
let mut base_offset = 15.0;
|
||||||
|
for _ in 0..10 {
|
||||||
|
let en = world.spawn((WorldTransform::default(), Transform::from_xyz(base_offset, base_offset, base_offset)));
|
||||||
|
world.add_relation(en, ChildOf, parent);
|
||||||
|
base_offset += 10.0;
|
||||||
|
children.push(en);
|
||||||
|
}
|
||||||
|
|
||||||
|
let second_child = world.spawn((WorldTransform::default(), Transform::from_xyz(5.0, 3.0, 8.0)));
|
||||||
|
world.add_relation(second_child, ChildOf, parent);
|
||||||
|
|
||||||
|
let view = world.view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
||||||
|
system_update_world_transforms(&world, view).unwrap();
|
||||||
|
|
||||||
|
let mut base_offset = 25.0;
|
||||||
|
for child in children.into_iter() {
|
||||||
|
let g = world.view_one::<&WorldTransform>(child).get().unwrap();
|
||||||
|
println!("Child {:?} at {:?}", child, g.translation);
|
||||||
|
assert_eq!(**g, Transform::from_xyz(base_offset, base_offset, base_offset));
|
||||||
|
|
||||||
|
base_offset += 10.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_many_children() {
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
let parent = world.spawn((WorldTransform::default(), Transform::from_xyz(10.0, 10.0, 10.0)));
|
||||||
|
let first_child = world.spawn((WorldTransform::default(), Transform::from_xyz(15.0, 15.0, 15.0)));
|
||||||
|
world.add_relation(first_child, ChildOf, parent);
|
||||||
|
|
||||||
|
let sec_chi = world.spawn((WorldTransform::default(), Transform::from_xyz(155.0, 23.0, 6.0)));
|
||||||
|
world.add_relation(sec_chi, ChildOf, first_child);
|
||||||
|
|
||||||
|
let thir_chi = world.spawn((WorldTransform::default(), Transform::from_xyz(51.0, 85.0, 17.0)));
|
||||||
|
world.add_relation(thir_chi, ChildOf, sec_chi);
|
||||||
|
|
||||||
|
let four_child = world.spawn((WorldTransform::default(), Transform::from_xyz(24.0, 61.0, 65.0)));
|
||||||
|
world.add_relation(four_child, ChildOf, thir_chi);
|
||||||
|
|
||||||
|
let five_child = world.spawn((WorldTransform::default(), Transform::from_xyz(356.0, 54.0, 786.0)));
|
||||||
|
world.add_relation(five_child, ChildOf, four_child);
|
||||||
|
|
||||||
|
let view = world.view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
||||||
|
system_update_world_transforms(&world, view).unwrap();
|
||||||
|
|
||||||
|
let g = world.view_one::<&WorldTransform>(five_child).get().unwrap();
|
||||||
|
assert_eq!(**g, Transform::from_xyz(611.0, 248.0, 899.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_branched_children() {
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
let parent = world.spawn((WorldTransform::default(), Transform::from_xyz(10.0, 10.0, 10.0)));
|
||||||
|
let first_child = world.spawn((WorldTransform::default(), Transform::from_xyz(15.0, 15.0, 15.0)));
|
||||||
|
world.add_relation(first_child, ChildOf, parent);
|
||||||
|
|
||||||
|
let sec_chi = world.spawn((WorldTransform::default(), Transform::from_xyz(155.0, 23.0, 6.0)));
|
||||||
|
world.add_relation(sec_chi, ChildOf, first_child);
|
||||||
|
|
||||||
|
let thir_chi = world.spawn((WorldTransform::default(), Transform::from_xyz(51.0, 85.0, 17.0)));
|
||||||
|
world.add_relation(thir_chi, ChildOf, first_child);
|
||||||
|
|
||||||
|
let four_child = world.spawn((WorldTransform::default(), Transform::from_xyz(24.0, 61.0, 65.0)));
|
||||||
|
world.add_relation(four_child, ChildOf, thir_chi);
|
||||||
|
|
||||||
|
let five_child = world.spawn((WorldTransform::default(), Transform::from_xyz(356.0, 54.0, 786.0)));
|
||||||
|
world.add_relation(five_child, ChildOf, sec_chi);
|
||||||
|
|
||||||
|
let view = world.view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
||||||
|
system_update_world_transforms(&world, view).unwrap();
|
||||||
|
|
||||||
|
let g = world.view_one::<&WorldTransform>(five_child).get().unwrap();
|
||||||
|
assert_eq!(**g, Transform::from_xyz(536.0, 102.0, 817.0));
|
||||||
|
|
||||||
|
let g = world.view_one::<&WorldTransform>(thir_chi).get().unwrap();
|
||||||
|
assert_eq!(**g, Transform::from_xyz(76.0, 110.0, 42.0));
|
||||||
|
|
||||||
|
let g = world.view_one::<&WorldTransform>(four_child).get().unwrap();
|
||||||
|
assert_eq!(**g, Transform::from_xyz(100.0, 171.0, 107.0));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue