Compare commits
6 Commits
01a74ab9a6
...
60ec62c558
Author | SHA1 | Date |
---|---|---|
SeanOMik | 60ec62c558 | |
SeanOMik | 2daf617ba3 | |
SeanOMik | 0668be06e2 | |
SeanOMik | 347427a841 | |
SeanOMik | 4162150c5f | |
SeanOMik | 4a0d003181 |
|
@ -1,6 +1,6 @@
|
||||||
use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::DerefMut};
|
use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::DerefMut};
|
||||||
|
|
||||||
use crate::{world::ArchetypeEntityId, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick, Entity};
|
use crate::{bundle::Bundle, component_info::ComponentInfo, world::ArchetypeEntityId, DynTypeId, Entity, Tick};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ComponentColumn {
|
pub struct ComponentColumn {
|
||||||
|
@ -295,21 +295,32 @@ impl Archetype {
|
||||||
self.capacity = new_cap;
|
self.capacity = new_cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert_eq!(self.entity_ids.len(), self.entities.len(), "Somehow the Archetype's entity storage got unsynced");
|
self.ensure_synced();
|
||||||
let entity_index = ArchetypeEntityId(self.entity_ids.len() as u64);
|
let entity_index = ArchetypeEntityId(self.entity_ids.len() as u64);
|
||||||
self.entity_ids.insert(entity, entity_index);
|
self.entity_ids.insert(entity, entity_index);
|
||||||
self.entities.push(entity);
|
self.entities.push(entity);
|
||||||
|
|
||||||
bundle.take(|data, type_id, _size| {
|
bundle.take(|data, type_id, info| {
|
||||||
let col = self.get_column_mut(type_id).unwrap();
|
self.put_component_at(tick, data, type_id, info.layout().size(), entity_index.0 as _);
|
||||||
unsafe { col.insert_entity(entity_index.0 as usize, data, *tick); }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
entity_index
|
entity_index
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes an entity from the Archetype and frees its components. Returns the entity record
|
pub(crate) fn put_component_at(&mut self, tick: &Tick, ptr: NonNull<u8>, type_id: DynTypeId, size: usize, index: usize) {
|
||||||
/// that took its place in the component column.
|
let _ = size;
|
||||||
|
|
||||||
|
let col = self.get_column_mut(type_id).unwrap();
|
||||||
|
//unsafe { col.set_at(index, ptr, *tick) };
|
||||||
|
unsafe { col.insert_entity(index, ptr, *tick); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes an entity from the Archetype and frees its components.
|
||||||
|
///
|
||||||
|
/// Inside the component columns, the entities are swap-removed. Meaning that the last
|
||||||
|
/// entity in the column is moved in the position of the entity that was removed.
|
||||||
|
/// If there was an entity that was swapped, this function returns the entity, and its
|
||||||
|
/// new index in the archetype that was put in place of the removed entity.
|
||||||
pub(crate) fn remove_entity(&mut self, entity: Entity, tick: &Tick) -> Option<(Entity, ArchetypeEntityId)> {
|
pub(crate) fn remove_entity(&mut self, entity: Entity, tick: &Tick) -> Option<(Entity, ArchetypeEntityId)> {
|
||||||
let entity_index = self.entity_ids.remove(&entity)
|
let entity_index = self.entity_ids.remove(&entity)
|
||||||
.expect("The entity is not in this Archetype!");
|
.expect("The entity is not in this Archetype!");
|
||||||
|
@ -389,6 +400,11 @@ impl Archetype {
|
||||||
self.capacity = new_capacity;
|
self.capacity = new_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to find the column storing components of `type_id`
|
||||||
|
pub fn get_column_at(&self, idx: usize) -> Option<&ComponentColumn> {
|
||||||
|
self.columns.get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to find the column storing components of `type_id`
|
/// Attempts to find the column storing components of `type_id`
|
||||||
pub fn get_column<I: Into<DynTypeId>>(&self, type_id: I) -> Option<&ComponentColumn> {
|
pub fn get_column<I: Into<DynTypeId>>(&self, type_id: I) -> Option<&ComponentColumn> {
|
||||||
let type_id = type_id.into();
|
let type_id = type_id.into();
|
||||||
|
@ -436,7 +452,7 @@ impl Archetype {
|
||||||
/// The entity IS NOT removed from the old archetype. You must manually call [`Archetype::remove_entity`](crate::Archetype).
|
/// The entity IS NOT removed from the old archetype. You must manually call [`Archetype::remove_entity`](crate::Archetype).
|
||||||
/// It was done this way because I had some borrow check issues when writing [`World::insert`](crate::World)
|
/// It was done this way because I had some borrow check issues when writing [`World::insert`](crate::World)
|
||||||
/// related to borrowing mutably from self more than once.
|
/// related to borrowing mutably from self more than once.
|
||||||
/* pub fn move_into<B>(&self, into_arch: &mut Archetype, entity: Entity, new_components: B)
|
/* pub fn move_into<B>(&mut self, entities: &mut Entities, tick: &Tick, into_arch: &mut Archetype, entity: Entity, new_components: B)
|
||||||
where
|
where
|
||||||
B: Bundle
|
B: Bundle
|
||||||
{
|
{
|
||||||
|
@ -446,25 +462,40 @@ impl Archetype {
|
||||||
|
|
||||||
// move the existing components into the new archetype
|
// move the existing components into the new archetype
|
||||||
for col in self.columns.iter() {
|
for col in self.columns.iter() {
|
||||||
let into_col = into_arch.get_column_mut(col.info.type_id).unwrap();
|
let into_col = into_arch.get_column_mut(col.info.type_id()).unwrap();
|
||||||
|
|
||||||
// copy from the old column into the new column, then remove it from the old one
|
// copy from the old column into the new column, then remove it from the old one
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = col.borrow_ptr();
|
let ptr = col.borrow_ptr();
|
||||||
let ptr = NonNull::new_unchecked(ptr.as_ptr()
|
let ptr = NonNull::new_unchecked(ptr.as_ptr()
|
||||||
.add(new_index.0 as usize * col.info.layout.size));
|
.add(new_index.0 as usize * col.info.layout().size()));
|
||||||
into_col.set_at(new_index.0 as _, ptr);
|
into_col.set_at(new_index.0 as _, ptr, *tick);
|
||||||
|
//into_col.set_at(new_index.0 as _, ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now move the new components into the new archetype
|
// now move the new components into the new archetype
|
||||||
new_components.take(|data, type_id, _size| {
|
new_components.take(|data, type_id, _size| {
|
||||||
let col = into_arch.get_column_mut(type_id).unwrap();
|
let col = into_arch.get_column_mut(type_id).unwrap();
|
||||||
unsafe { col.set_at(new_index.0 as _, data); }
|
unsafe { col.set_at(new_index.0 as _, data, *tick); }
|
||||||
col.len += 1;
|
col.len += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
//self.remove_entity(entity);
|
if let Some((en, new_idx)) = self.remove_entity(entity, tick) {
|
||||||
|
let moved_rec = Record {
|
||||||
|
id: self.id,
|
||||||
|
index: new_idx,
|
||||||
|
};
|
||||||
|
entities.insert_entity_record(en, moved_rec);
|
||||||
|
}
|
||||||
|
let new_rec = Record {
|
||||||
|
id: into_arch.id,
|
||||||
|
index: new_index,
|
||||||
|
};
|
||||||
|
entities.insert_entity_record(entity, new_rec);
|
||||||
|
|
||||||
|
into_arch.ensure_synced();
|
||||||
|
self.ensure_synced();
|
||||||
} */
|
} */
|
||||||
|
|
||||||
/// Returns a borrow to the map used to find the column indices of the entity.
|
/// Returns a borrow to the map used to find the column indices of the entity.
|
||||||
|
@ -700,7 +731,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn dynamic_archetype() {
|
fn dynamic_archetype() {
|
||||||
let layout = Layout::new::<u32>();
|
let layout = Layout::new::<u32>();
|
||||||
let info = ComponentInfo::new_unknown(DynTypeId::Unknown(100), "u32", layout);
|
let info = ComponentInfo::new_unknown(Some("u32".to_string()), DynTypeId::Unknown(100), layout);
|
||||||
let infos = vec![info.clone()];
|
let infos = vec![info.clone()];
|
||||||
|
|
||||||
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), infos);
|
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), infos);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{ptr::NonNull, mem::size_of, alloc::Layout};
|
use std::{ptr::NonNull, alloc::Layout};
|
||||||
|
|
||||||
use crate::{component::Component, component_info::ComponentInfo, DynTypeId};
|
use crate::{component::Component, component_info::ComponentInfo, DynTypeId};
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ pub trait Bundle {
|
||||||
|
|
||||||
/// Take the bundle by calling the closure with pointers to each component, its type and size.
|
/// Take the bundle by calling the closure with pointers to each component, its type and size.
|
||||||
/// The closure is expected to take ownership of the pointer.
|
/// The closure is expected to take ownership of the pointer.
|
||||||
fn take(self, f: impl FnMut(NonNull<u8>, DynTypeId, usize));
|
fn take(self, f: impl FnMut(NonNull<u8>, DynTypeId, ComponentInfo));
|
||||||
|
|
||||||
/// Returns a boolean indicating if this Bundle is dynamic. See [`DynamicBundle`]
|
/// Returns a boolean indicating if this Bundle is dynamic. See [`DynamicBundle`]
|
||||||
fn is_dynamic(&self) -> bool;
|
fn is_dynamic(&self) -> bool;
|
||||||
|
@ -26,8 +26,8 @@ impl Bundle for () {
|
||||||
vec![ComponentInfo::new::<()>()]
|
vec![ComponentInfo::new::<()>()]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, usize)) {
|
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, ComponentInfo)) {
|
||||||
f(NonNull::from(&self).cast(), DynTypeId::of::<()>(), size_of::<()>());
|
f(NonNull::from(&self).cast(), DynTypeId::of::<()>(), ComponentInfo::new::<()>());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_dynamic(&self) -> bool {
|
fn is_dynamic(&self) -> bool {
|
||||||
|
@ -44,8 +44,8 @@ impl<C: Component> Bundle for C {
|
||||||
vec![ComponentInfo::new::<C>()]
|
vec![ComponentInfo::new::<C>()]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, usize)) {
|
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, ComponentInfo)) {
|
||||||
f(NonNull::from(&self).cast(), DynTypeId::of::<C>(), size_of::<C>());
|
f(NonNull::from(&self).cast(), DynTypeId::of::<C>(), ComponentInfo::new::<C>());
|
||||||
|
|
||||||
// this must be done to avoid calling drop on heap memory that the component
|
// this must be done to avoid calling drop on heap memory that the component
|
||||||
// may manage. So something like a Vec, or HashMap, etc.
|
// may manage. So something like a Vec, or HashMap, etc.
|
||||||
|
@ -70,12 +70,12 @@ macro_rules! impl_bundle_tuple {
|
||||||
vec![$(ComponentInfo::new::<$name>()),+]
|
vec![$(ComponentInfo::new::<$name>()),+]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, usize)) {
|
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, ComponentInfo)) {
|
||||||
// these names wont follow rust convention, but its a macro so deal with it
|
// these names wont follow rust convention, but its a macro so deal with it
|
||||||
let ($($name,)+) = self;
|
let ($($name,)+) = self;
|
||||||
|
|
||||||
$(
|
$(
|
||||||
f(NonNull::from(&$name).cast(), DynTypeId::of::<$name>(), size_of::<$name>());
|
f(NonNull::from(&$name).cast(), DynTypeId::of::<$name>(), ComponentInfo::new::<$name>());
|
||||||
// this must be done to avoid calling drop on heap memory that the component
|
// this must be done to avoid calling drop on heap memory that the component
|
||||||
// may manage. So something like a Vec, or HashMap, etc.
|
// may manage. So something like a Vec, or HashMap, etc.
|
||||||
std::mem::forget($name);
|
std::mem::forget($name);
|
||||||
|
@ -136,8 +136,6 @@ impl DynamicBundle {
|
||||||
where
|
where
|
||||||
C: Component
|
C: Component
|
||||||
{
|
{
|
||||||
let info = ComponentInfo::new::<C>();
|
|
||||||
|
|
||||||
// an owned pointer must be created from the provided component since comp would drop
|
// an owned pointer must be created from the provided component since comp would drop
|
||||||
// out of scope and the data would become invalid
|
// out of scope and the data would become invalid
|
||||||
let ptr = unsafe {
|
let ptr = unsafe {
|
||||||
|
@ -147,16 +145,42 @@ impl DynamicBundle {
|
||||||
let alloc_ptr = NonNull::new_unchecked(std::alloc::alloc(layout)).cast::<C>();
|
let alloc_ptr = NonNull::new_unchecked(std::alloc::alloc(layout)).cast::<C>();
|
||||||
std::ptr::copy_nonoverlapping(data.as_ptr(), alloc_ptr.as_ptr(), 1);
|
std::ptr::copy_nonoverlapping(data.as_ptr(), alloc_ptr.as_ptr(), 1);
|
||||||
|
|
||||||
|
// this must be done to avoid calling drop on heap memory that the component
|
||||||
|
// may manage. So something like a Vec, or HashMap, etc.
|
||||||
|
std::mem::forget(comp);
|
||||||
|
|
||||||
alloc_ptr.cast()
|
alloc_ptr.cast()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let info = ComponentInfo::new::<C>();
|
||||||
self.bundle.push((ptr, info));
|
self.bundle.push((ptr, info));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push an unknown type to the bundle
|
/// Push an unknown type to the end of the bundle.
|
||||||
pub fn push_unknown(&mut self, data: NonNull<u8>, info: ComponentInfo) {
|
pub fn push_unknown(&mut self, data: NonNull<u8>, info: ComponentInfo) {
|
||||||
self.bundle.push((data, info));
|
self.bundle.push((data, info));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Push a bundle to the end of this dynamic bundle.
|
||||||
|
pub fn push_bundle<B>(&mut self, bundle: B)
|
||||||
|
where
|
||||||
|
B: Bundle
|
||||||
|
{
|
||||||
|
bundle.take(|ptr, _, info| {
|
||||||
|
// unfortunately the components in the bundle must be copied since there is no guarantee that
|
||||||
|
// `bundle` lasts for as long as the `DynamicBundle`. If the data wasn't copied, the pointers
|
||||||
|
// could be invalid later.
|
||||||
|
let p = unsafe {
|
||||||
|
let alloc_ptr = NonNull::new_unchecked(std::alloc::alloc(info.layout()));
|
||||||
|
std::ptr::copy_nonoverlapping(ptr.as_ptr(), alloc_ptr.as_ptr(), info.layout().size());
|
||||||
|
|
||||||
|
alloc_ptr
|
||||||
|
};
|
||||||
|
|
||||||
|
self.push_unknown(p, info);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bundle for DynamicBundle {
|
impl Bundle for DynamicBundle {
|
||||||
|
@ -168,9 +192,9 @@ impl Bundle for DynamicBundle {
|
||||||
self.bundle.iter().map(|b| b.1).collect()
|
self.bundle.iter().map(|b| b.1).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, usize)) {
|
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, ComponentInfo)) {
|
||||||
for (data, info) in self.bundle.iter() {
|
for (data, info) in self.bundle.into_iter() {
|
||||||
f(*data, info.type_id(), info.layout().size());
|
f(data, info.type_id(), info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl ComponentInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create ComponentInfo from a type that is not known to rust
|
/// Create ComponentInfo from a type that is not known to rust
|
||||||
pub fn new_unknown<D>(type_id: D, name: &str, layout: Layout) -> Self
|
pub fn new_unknown<D>(name: Option<String>, type_id: D, layout: Layout) -> Self
|
||||||
where
|
where
|
||||||
D: Into<DynTypeId>,
|
D: Into<DynTypeId>,
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,16 @@ pub struct Entity {
|
||||||
pub(crate) generation: u64,
|
pub(crate) generation: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Entity {
|
||||||
|
pub fn id(&self) -> EntityId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generation(&self) -> u64 {
|
||||||
|
self.generation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Entities {
|
pub struct Entities {
|
||||||
pub(crate) arch_index: HashMap<EntityId, Record>,
|
pub(crate) arch_index: HashMap<EntityId, Record>,
|
||||||
dead: VecDeque<Entity>,
|
dead: VecDeque<Entity>,
|
||||||
|
|
|
@ -111,7 +111,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn single_dynamic_view() {
|
fn single_dynamic_view() {
|
||||||
let comp_layout = Layout::new::<u32>();
|
let comp_layout = Layout::new::<u32>();
|
||||||
let comp_info = ComponentInfo::new_unknown(DynTypeId::Unknown(100), "u32", comp_layout);
|
let comp_info = ComponentInfo::new_unknown(Some("u32".to_string()), DynTypeId::Unknown(100), comp_layout);
|
||||||
|
|
||||||
let mut dynamic_bundle = DynamicBundle::default();
|
let mut dynamic_bundle = DynamicBundle::default();
|
||||||
let comp = 50u32;
|
let comp = 50u32;
|
||||||
|
|
|
@ -2,21 +2,20 @@ use crate::{archetype::Archetype, World, Entity};
|
||||||
|
|
||||||
use super::{Fetch, Query, AsQuery};
|
use super::{Fetch, Query, AsQuery};
|
||||||
|
|
||||||
pub struct EntitiesFetch {
|
pub struct EntitiesFetch<'a> {
|
||||||
entities: Vec<Entity>,
|
archetype_entities: Option<&'a [Entity]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Fetch<'a> for EntitiesFetch {
|
impl<'a> Fetch<'a> for EntitiesFetch<'a> {
|
||||||
type Item = Entity;
|
type Item = Entity;
|
||||||
|
|
||||||
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||||
let e = *self.entities.get_unchecked(entity.0 as usize);
|
self.archetype_entities.unwrap()[entity.0 as usize]
|
||||||
e
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dangling() -> Self {
|
fn dangling() -> Self {
|
||||||
Self {
|
Self {
|
||||||
entities: vec![],
|
archetype_entities: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +26,7 @@ pub struct Entities;
|
||||||
impl Query for Entities {
|
impl Query for Entities {
|
||||||
type Item<'a> = Entity;
|
type Item<'a> = Entity;
|
||||||
|
|
||||||
type Fetch<'a> = EntitiesFetch;
|
type Fetch<'a> = EntitiesFetch<'a>;
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Entities
|
Entities
|
||||||
|
@ -41,7 +40,7 @@ impl Query for Entities {
|
||||||
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
||||||
let _ = tick; // ignore unused warnings
|
let _ = tick; // ignore unused warnings
|
||||||
EntitiesFetch {
|
EntitiesFetch {
|
||||||
entities: archetype.entity_ids.keys().cloned().collect::<Vec<Entity>>(),
|
archetype_entities: Some(&archetype.entities),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@ use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{query::{AsQuery, Query}, Archetype, Component, DynTypeId, World};
|
use crate::{query::{AsQuery, Query}, Archetype, Component, DynTypeId, World};
|
||||||
|
|
||||||
|
/// A filter query that fetches when the entity has the component `C`.
|
||||||
|
///
|
||||||
|
/// This does not return a reference to the component, it returns `()` if the entity has
|
||||||
|
/// the component. This query is great when its used with [`Or`](super::Or).
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Has<C: Component> {
|
pub struct Has<C: Component> {
|
||||||
_marker: PhantomData<C>
|
_marker: PhantomData<C>
|
|
@ -1,5 +1,8 @@
|
||||||
pub mod has_component;
|
mod has;
|
||||||
pub use has_component::*;
|
pub use has::*;
|
||||||
|
|
||||||
pub mod or;
|
mod or;
|
||||||
pub use or::*;
|
pub use or::*;
|
||||||
|
|
||||||
|
mod not;
|
||||||
|
pub use not::*;
|
|
@ -0,0 +1,44 @@
|
||||||
|
use crate::{query::{AsQuery, Query}, Archetype, World};
|
||||||
|
|
||||||
|
/// A filter query that fetches the inverse of `Q`.
|
||||||
|
///
|
||||||
|
/// This means that entities that `Q` fetches are skipped, and entities that
|
||||||
|
/// `Q` does not fetch are not skipped.
|
||||||
|
///
|
||||||
|
/// ```rust,nobuild
|
||||||
|
/// // Iterate over entities that has a transform, and are not the origin of a `ChildOf` relationship.
|
||||||
|
/// for (en, pos, _) in world
|
||||||
|
/// .view::<(Entities, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>()
|
||||||
|
/// .iter()
|
||||||
|
/// {
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct Not<Q: Query> {
|
||||||
|
query: Q,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: Query> Query for Not<Q> {
|
||||||
|
type Item<'a> = ();
|
||||||
|
|
||||||
|
type Fetch<'a> = ();
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Not {
|
||||||
|
query: Q::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_visit_archetype(&self, archetype: &Archetype) -> bool {
|
||||||
|
!self.query.can_visit_archetype(archetype)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn fetch<'a>(&self, _world: &'a World, _: &'a Archetype, _: crate::Tick) -> Self::Fetch<'a> {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: Query> AsQuery for Not<Q> {
|
||||||
|
type Query = Self;
|
||||||
|
}
|
|
@ -1,5 +1,26 @@
|
||||||
use crate::{query::{AsQuery, Query}, Archetype, World};
|
use crate::{query::{AsQuery, Query}, Archetype, World};
|
||||||
|
|
||||||
|
/// A filter query returning when either `Q1` or `Q2` returns.
|
||||||
|
///
|
||||||
|
/// This checks if `Q1` can fetch before checking `Q2`.
|
||||||
|
///
|
||||||
|
/// ```rust,nobuild
|
||||||
|
/// for (en, pos, _) in world
|
||||||
|
/// .view::<(Entities, &Transform, Or<Has<Mesh>, Has<Scene>>)>()
|
||||||
|
/// .iter()
|
||||||
|
/// {
|
||||||
|
/// // do some things with the position of the entities
|
||||||
|
///
|
||||||
|
/// // now handle do things with the Mesh or Scene that the entity could have
|
||||||
|
/// if let Some(mesh) = world.view_one::<&Mesh>(en).get() {
|
||||||
|
/// // do mesh things
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// if let Some(scene) = world.view_one::<&Scene>(en).get() {
|
||||||
|
/// // do scene things
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Or<Q1: AsQuery, Q2: AsQuery> {
|
pub struct Or<Q1: AsQuery, Q2: AsQuery> {
|
||||||
left: Q1::Query,
|
left: Q1::Query,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{any::TypeId, collections::HashMap, ptr::NonNull};
|
||||||
|
|
||||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||||
|
|
||||||
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsQuery, Query, ViewState, ViewIter, ViewOne}, resource::ResourceData, ComponentInfo, DynTypeId, Entities, Entity, ResourceObject, Tick, TickTracker};
|
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsQuery, Query, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker};
|
||||||
|
|
||||||
/// The id of the entity for the Archetype.
|
/// The id of the entity for the Archetype.
|
||||||
///
|
///
|
||||||
|
@ -144,37 +144,32 @@ impl World {
|
||||||
let current_arch = self.archetypes.get(&record.id).unwrap();
|
let current_arch = self.archetypes.get(&record.id).unwrap();
|
||||||
let current_arch_len = current_arch.len();
|
let current_arch_len = current_arch.len();
|
||||||
|
|
||||||
let mut col_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id()).collect();
|
// contains the type ids for the old component columns + the ids for the new components
|
||||||
let orig_col = col_types.clone();
|
let mut combined_column_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id()).collect();
|
||||||
col_types.extend(bundle.type_ids());
|
combined_column_types.extend(bundle.type_ids());
|
||||||
|
|
||||||
let mut col_infos: Vec<ComponentInfo> = current_arch.columns.iter().map(|c| c.info).collect();
|
// contains the ComponentInfo for the old component columns + the info for the new components
|
||||||
col_infos.extend(bundle.info());
|
let mut combined_column_infos: Vec<ComponentInfo> = current_arch.columns.iter().map(|c| c.info).collect();
|
||||||
|
combined_column_infos.extend(bundle.info());
|
||||||
|
|
||||||
let col_ptrs: Vec<(NonNull<u8>, ComponentInfo)> = current_arch.columns.iter()
|
// pointers only for the old columns
|
||||||
|
let old_columns: Vec<(NonNull<u8>, ComponentInfo)> = current_arch.columns.iter()
|
||||||
.map(|c| unsafe { (NonNull::new_unchecked(c.borrow_ptr().as_ptr()), c.info) })
|
.map(|c| unsafe { (NonNull::new_unchecked(c.borrow_ptr().as_ptr()), c.info) })
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(arch) = self.archetypes.values_mut().find(|a| a.is_archetype_for(&col_types)) {
|
if let Some(arch) = self.archetypes.values_mut().find(|a| a.is_archetype_for(&combined_column_types)) {
|
||||||
let res_index = arch.reserve_one(entity);
|
let mut dbun = DynamicBundle::new();
|
||||||
|
// move old entity components into new archetype columns
|
||||||
for (col_type, (col_ptr, col_info)) in orig_col.into_iter().zip(col_ptrs.into_iter()) {
|
for (col_ptr, col_info) in old_columns.into_iter() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = NonNull::new_unchecked(col_ptr.as_ptr()
|
let ptr = NonNull::new_unchecked(col_ptr.as_ptr()
|
||||||
.add(record.index.0 as usize * col_info.layout().size()));
|
.add(record.index.0 as usize * col_info.layout().size()));
|
||||||
let col = arch.get_column_mut(col_type).unwrap();
|
dbun.push_unknown(ptr, col_info);
|
||||||
// set_at is used since the entity was reserved
|
|
||||||
col.set_at(res_index.0 as _, ptr, tick);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dbun.push_bundle(bundle);
|
||||||
|
|
||||||
bundle.take(|data, type_id, _size| {
|
let res_index = arch.add_entity(entity, dbun, &tick);
|
||||||
let col = arch.get_column_mut(type_id).unwrap();
|
|
||||||
// set_at is used since the entity was reserved
|
|
||||||
unsafe { col.set_at(res_index.0 as _, data, tick); }
|
|
||||||
});
|
|
||||||
|
|
||||||
arch.entity_ids.insert(entity, res_index);
|
|
||||||
arch.ensure_synced();
|
arch.ensure_synced();
|
||||||
|
|
||||||
let new_record = Record {
|
let new_record = Record {
|
||||||
|
@ -187,25 +182,24 @@ impl World {
|
||||||
// if this entity is the only entity for this archetype, add more columns to it
|
// if this entity is the only entity for this archetype, add more columns to it
|
||||||
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
||||||
current_arch.extend(&tick, vec![bundle]);
|
current_arch.extend(&tick, vec![bundle]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_arch_id = self.next_archetype_id.increment();
|
let new_arch_id = self.next_archetype_id.increment();
|
||||||
let mut archetype = Archetype::from_bundle_info(new_arch_id, col_infos);
|
let mut archetype = Archetype::from_bundle_info(new_arch_id, combined_column_infos);
|
||||||
let entity_arch_id = archetype.add_entity(entity, bundle, &tick);
|
|
||||||
|
|
||||||
// move the old components into the new archetype
|
let mut dbun = DynamicBundle::new();
|
||||||
for (column_ptr, column_info) in col_ptrs.into_iter() {
|
for (column_ptr, column_info) in old_columns.into_iter() {
|
||||||
unsafe {
|
unsafe {
|
||||||
// ptr of component for the entity
|
// ptr of component for the entity
|
||||||
let comp_ptr = NonNull::new_unchecked(column_ptr.as_ptr()
|
let comp_ptr = NonNull::new_unchecked(column_ptr.as_ptr()
|
||||||
.add(record.index.0 as usize * column_info.layout().size()));
|
.add(record.index.0 as usize * column_info.layout().size()));
|
||||||
|
dbun.push_unknown(comp_ptr, column_info);
|
||||||
let col = archetype.get_column_mut(column_info.type_id()).unwrap();
|
|
||||||
col.insert_entity(entity_arch_id.0 as _, comp_ptr, tick);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dbun.push_bundle(bundle);
|
||||||
|
|
||||||
|
let entity_arch_id = archetype.add_entity(entity, dbun, &tick);
|
||||||
|
|
||||||
self.archetypes.insert(new_arch_id, archetype);
|
self.archetypes.insert(new_arch_id, archetype);
|
||||||
|
|
||||||
|
@ -219,12 +213,103 @@ impl World {
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
||||||
if current_arch.len() > 1 {
|
if let Some((en, enar)) = current_arch.remove_entity(entity, &tick) {
|
||||||
current_arch.remove_entity(entity, &tick);
|
let rec = Record {
|
||||||
} else if current_arch.len() == 1 {
|
id: current_arch.id(),
|
||||||
// The old archetype will only be removed if there was another archetype that would
|
index: enar
|
||||||
// work for the entity's components, and the old archetype only had a single entity.
|
};
|
||||||
self.archetypes.remove(&record.id).unwrap();
|
self.entities.insert_entity_record(en, rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_arch.ensure_synced();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A method used for debugging implementation details of the ECS.
|
||||||
|
///
|
||||||
|
/// Here's an example of the output:
|
||||||
|
/// ```
|
||||||
|
/// Entities
|
||||||
|
/// 1 in archetype 0 at 0
|
||||||
|
/// 0 in archetype 1 at 0
|
||||||
|
/// 2 in archetype 0 at 1
|
||||||
|
/// 3 in archetype 2 at 0
|
||||||
|
/// Arch 1 -- 1 entities
|
||||||
|
/// Col 175564825027445222460146453544114453753
|
||||||
|
/// 0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 78 86 0 0
|
||||||
|
/// Col 162279302565774655543278578489329315472
|
||||||
|
/// 0: 0 0 32 65 0 0 32 65 0 0 32 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0
|
||||||
|
/// Col 24291284537013640759061027938209843602
|
||||||
|
/// 0: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
/// Arch 2 -- 1 entities
|
||||||
|
/// Col 175564825027445222460146453544114453753
|
||||||
|
/// 0: 0 0 0 0 0 0 0 0 0 0 0 0 237 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 78 86 0 0
|
||||||
|
/// Col 162279302565774655543278578489329315472
|
||||||
|
/// 0: 0 0 76 66 0 0 170 66 0 0 136 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0
|
||||||
|
/// Col 142862377085187052737282554588643015580
|
||||||
|
/// 0: 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
/// Arch 0 -- 2 entities
|
||||||
|
/// Col 175564825027445222460146453544114453753
|
||||||
|
/// 0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0
|
||||||
|
/// 1: 0 0 0 0 0 0 0 0 0 0 0 0 237 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 78 86 0 0
|
||||||
|
/// Col 162279302565774655543278578489329315472
|
||||||
|
/// 0: 0 0 112 65 0 0 112 65 0 0 112 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0
|
||||||
|
/// 1: 0 0 27 67 0 0 184 65 0 0 192 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 128 63 0 0 128 63 0 0 128 63 0 0 128 63 0 0 0 0
|
||||||
|
/// Col 142862377085187052737282554588643015580
|
||||||
|
/// 0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
/// 1: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
/// Col 24291284537013640759061027938209843602
|
||||||
|
/// 0: 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
/// 1: 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||||
|
/// Arch 3 -- 0 entities
|
||||||
|
/// Col 175564825027445222460146453544114453753
|
||||||
|
/// Col 162279302565774655543278578489329315472
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This output prints all Entity ids, the archetype they're in, and the index that they're
|
||||||
|
/// in inside of the archetype. Additionally, the archetypes are printing, including their
|
||||||
|
/// columns type ids, and the contents of the type ids. This output can be used to debug
|
||||||
|
/// the contents of entities inside the archetypes.
|
||||||
|
///
|
||||||
|
/// Below is a template of the output:
|
||||||
|
/// ```
|
||||||
|
/// Entities
|
||||||
|
/// %ENTITY_ID% in archetype %ARCHETYPE_ID% at %INDEX%
|
||||||
|
/// Arch ID -- %ARCHETYPE_LEN% entities
|
||||||
|
/// %FOR EACH COL%
|
||||||
|
/// Col COLUMN_COMPONENT_TYPE_ID
|
||||||
|
/// %FOR EACH ENTITY%
|
||||||
|
/// %ENTITY_INDEX%: %COMPONENT_BYTES%
|
||||||
|
/// ```
|
||||||
|
/// If the template above doesn't help you in understanding the output, read the source code
|
||||||
|
/// of the function. The source code is pretty simple.
|
||||||
|
pub fn debug_print_world(&self) {
|
||||||
|
println!("Entities");
|
||||||
|
for (en, rec) in &self.entities.arch_index {
|
||||||
|
println!(" {} in archetype {} at {}", en.0, rec.id.0, rec.index.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for arch in self.archetypes.values() {
|
||||||
|
println!("Arch {} -- {} entities", arch.id().0, arch.len());
|
||||||
|
|
||||||
|
for col in &arch.columns {
|
||||||
|
// no clue if doing this is stable, but this is a debug function so :shrug:
|
||||||
|
let tyid: u128 = unsafe { std::mem::transmute(col.info.type_id().as_rust()) };
|
||||||
|
println!(" Col {}", tyid);
|
||||||
|
|
||||||
|
for en in 0..col.len {
|
||||||
|
// get the ptr starting at the component
|
||||||
|
let p = col.borrow_ptr();
|
||||||
|
let p = unsafe { p.as_ptr().add(en * col.info.layout().size()) };
|
||||||
|
|
||||||
|
print!(" {}: ", en);
|
||||||
|
// print each byte of the component
|
||||||
|
for i in 0..col.info.layout().size() {
|
||||||
|
let d = unsafe { *p.add(i) };
|
||||||
|
print!("{} ", d);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,6 +449,10 @@ impl World {
|
||||||
self.resources.get(&TypeId::of::<T>())
|
self.resources.get(&TypeId::of::<T>())
|
||||||
.map(|d| unsafe { NonNull::new_unchecked(d.data.as_ptr() as *mut T) })
|
.map(|d| unsafe { NonNull::new_unchecked(d.data.as_ptr() as *mut T) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn archetype_count(&self) -> usize {
|
||||||
|
self.archetypes.len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Ensure that all non-send resources are only accessible on the main thread.
|
// TODO: Ensure that all non-send resources are only accessible on the main thread.
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -21,7 +22,7 @@ pub struct SceneNodeFlag;
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct SceneNodeRoot;
|
pub struct SceneNodeRoot;
|
||||||
|
|
||||||
/// A SceneGraph is a Graph of nodes that represents the hierarchy of a scene.
|
/// A Graph of nodes that represents the hierarchy of a Scene.
|
||||||
///
|
///
|
||||||
/// This SceneGraph is special in the sense that it is literally just an ECS world with methods
|
/// This SceneGraph is special in the sense that it is literally just an ECS world with methods
|
||||||
/// implemented for it that make it easier to use for a SceneGraph.
|
/// implemented for it that make it easier to use for a SceneGraph.
|
||||||
|
@ -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,11 +1,10 @@
|
||||||
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;
|
||||||
|
|
||||||
|
/// A node inside of the Scene
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct SceneNode {
|
pub struct SceneNode {
|
||||||
//world: WorldLock,
|
|
||||||
parent: Option<Entity>,
|
parent: Option<Entity>,
|
||||||
entity: Entity
|
entity: Entity
|
||||||
}
|
}
|
||||||
|
@ -52,50 +51,33 @@ 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 {
|
|
||||||
let v = graph.world.view_one::<&Transform>(self.entity);
|
|
||||||
|
|
||||||
let t = v.get().expect("Somehow the SceneNode is missing its Transform!");
|
|
||||||
t.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the world transform of the Node.
|
|
||||||
///
|
|
||||||
/// This traverses up the SceneGraph from this node.
|
|
||||||
pub fn world_transform(&self, graph: &SceneGraph) -> Transform {
|
|
||||||
match self.parent(graph) {
|
|
||||||
Some(parent) => {
|
|
||||||
let pt = parent.world_transform(graph);
|
|
||||||
pt + self.local_transform(graph)
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
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,171 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// The world transform of an entity.
|
||||||
|
///
|
||||||
|
/// A Transform represents the relative position of the entity to its parent entity, while
|
||||||
|
/// a world transform is the position relative to the World. When wanting to move an entity,
|
||||||
|
/// you should use its [`Transform`]. You cannot mutate [`WorldTransform`] as its managed completey
|
||||||
|
/// by the [`system_update_world_transforms`] system. For the WorldTransform to work properly, you
|
||||||
|
/// must have both a [`Transform`] and [`WorldTransform`] on the entities in the scene.
|
||||||
|
#[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 [`WorldTransform`]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;
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (en, pos) in next_entities.into_iter() {
|
||||||
|
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