Create queries and views
This commit is contained in:
parent
f62f21e69f
commit
92e284e625
|
@ -31,7 +31,9 @@
|
|||
"test",
|
||||
"--no-run",
|
||||
"--bin=lyra-ecs",
|
||||
"--package=lyra-ecs"
|
||||
"--package=lyra-ecs",
|
||||
"--",
|
||||
"--nocapture"
|
||||
],
|
||||
"filter": {
|
||||
"name": "lyra-ecs",
|
||||
|
|
|
@ -1,55 +1,96 @@
|
|||
use std::any::{Any, TypeId};
|
||||
use std::{any::{Any, TypeId, type_name}, ptr::{NonNull, self}, alloc::{self, Layout, alloc}, mem::size_of};
|
||||
|
||||
use crate::{world::{Entity, ArchetypeEntityId}, bundle::Bundle, component::Component};
|
||||
use crate::{world::{Entity, ArchetypeEntityId}, bundle::Bundle, component::Component, component_info::ComponentInfo};
|
||||
|
||||
pub trait ComponentColumn: Any {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
fn new_empty_column(&self) -> Box<dyn ComponentColumn>;
|
||||
fn is_same_type(&self, column: &dyn ComponentColumn) -> bool;
|
||||
fn len(&self) -> usize;
|
||||
fn append(&mut self, column: &mut dyn ComponentColumn);
|
||||
|
||||
fn component_type_id(&self) -> TypeId;
|
||||
// used for debugging
|
||||
fn component_type_name(&self) -> String;
|
||||
pub struct ComponentColumn {
|
||||
pub data: NonNull<u8>,
|
||||
pub capacity: usize,
|
||||
pub info: ComponentInfo,
|
||||
pub entry_size: usize,
|
||||
}
|
||||
|
||||
impl<T: Component> ComponentColumn for Vec<T> {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
impl ComponentColumn {
|
||||
/// Creates an invalid component column. Do not attempt to use it.
|
||||
pub unsafe fn dangling() -> Self {
|
||||
ComponentColumn {
|
||||
data: NonNull::dangling(),
|
||||
capacity: 0,
|
||||
info: ComponentInfo::new::<()>(),
|
||||
entry_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
pub unsafe fn alloc(component_layout: Layout, capacity: usize) -> NonNull<u8> {
|
||||
let new_layout = Layout::from_size_align(
|
||||
component_layout.size().checked_mul(capacity).unwrap(),
|
||||
component_layout.align()
|
||||
).unwrap();
|
||||
|
||||
if let Some(data) = NonNull::new(alloc(new_layout)) {
|
||||
data
|
||||
} else {
|
||||
alloc::handle_alloc_error(new_layout)
|
||||
}
|
||||
}
|
||||
|
||||
fn new_empty_column(&self) -> Box<dyn ComponentColumn> {
|
||||
Box::new(Vec::<T>::new())
|
||||
pub unsafe fn new(info: ComponentInfo, capacity: usize) -> Self {
|
||||
let data = ComponentColumn::alloc(info.layout, capacity);
|
||||
|
||||
let size = info.layout.size();
|
||||
Self {
|
||||
data,
|
||||
capacity,
|
||||
info,
|
||||
entry_size: size,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_same_type(&self, column: &dyn ComponentColumn) -> bool {
|
||||
column.as_any().downcast_ref::<Self>().is_some()
|
||||
/// Creates an empty column of the same type
|
||||
pub unsafe fn create_empty(&self, capacity: usize) -> Self {
|
||||
let data = ComponentColumn::alloc(self.info.layout, capacity);
|
||||
|
||||
let size = self.info.layout.size();
|
||||
Self {
|
||||
data,
|
||||
capacity,
|
||||
info: self.info.clone(),
|
||||
entry_size: size,
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
Vec::len(self)
|
||||
/// Set a component from pointer at an entity index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This column must have space to fit the component, if it does not have room it will panic.
|
||||
pub unsafe fn set_at(&mut self, entity_index: usize, comp_src: NonNull<u8>) {
|
||||
assert!(entity_index < self.capacity);
|
||||
let dest = NonNull::new_unchecked(self.data.as_ptr().add(entity_index * self.entry_size));
|
||||
ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), self.entry_size);
|
||||
}
|
||||
|
||||
fn append(&mut self, column: &mut dyn ComponentColumn) {
|
||||
let column: &mut Self = column.as_any_mut().downcast_mut()
|
||||
.expect("Attempt at appending an different column type!");
|
||||
|
||||
self.append(column);
|
||||
/// Get a component at an entities index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This column MUST have the entity. If it does not, it WILL NOT panic and will cause UB.
|
||||
pub unsafe fn get<T>(&self, entity_index: usize) -> &T {
|
||||
let p = self.data.as_ptr()
|
||||
.cast::<T>()
|
||||
.add(entity_index * self.entry_size);
|
||||
&*p
|
||||
}
|
||||
|
||||
fn component_type_id(&self) -> TypeId {
|
||||
self.first().unwrap().type_id()
|
||||
}
|
||||
|
||||
fn component_type_name(&self) -> String {
|
||||
//self.first().unwrap().type_id()
|
||||
std::any::type_name::<T>().to_string()
|
||||
/// Get a component at an entities index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This column must have the entity.
|
||||
pub unsafe fn get_mut<T>(&mut self, entity_index: usize) -> &mut T {
|
||||
let p = self.data.as_ptr()
|
||||
.cast::<T>()
|
||||
.add(entity_index * self.entry_size);
|
||||
&mut *p
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,24 +109,25 @@ impl ArchetypeId {
|
|||
|
||||
pub struct Archetype {
|
||||
pub(crate) id: ArchetypeId,
|
||||
entities: Vec<Entity>,
|
||||
pub(crate) columns: Vec<Box<dyn ComponentColumn>>,
|
||||
pub(crate) entities: Vec<Entity>,
|
||||
pub(crate) columns: Vec<ComponentColumn>,
|
||||
}
|
||||
|
||||
/// The default capacity of the columns
|
||||
const DEFAULT_CAPACITY: usize = 32;
|
||||
|
||||
impl Archetype {
|
||||
/// Create a new archetype from another archetype and add a column
|
||||
pub fn new_archetype_add<T: Component>(new_id: ArchetypeId, archetype: &Archetype) -> Archetype {
|
||||
let mut columns: Vec<_> = archetype
|
||||
.columns
|
||||
.iter()
|
||||
.map(|c| c.new_empty_column())
|
||||
.map(|c| unsafe { c.create_empty(DEFAULT_CAPACITY) })
|
||||
.collect();
|
||||
|
||||
assert!(columns
|
||||
.iter()
|
||||
.find(|column| column.as_any().is::<Vec<T>>())
|
||||
.is_none());
|
||||
columns.push(Box::new(Vec::<T>::new()));
|
||||
// Make sure a column for the new component does not exist
|
||||
assert_ne!(true, columns.iter().any(|c| c.info.type_id == TypeId::of::<T>()) );
|
||||
columns.push(unsafe { ComponentColumn::new(ComponentInfo::new::<T>(), DEFAULT_CAPACITY) });
|
||||
|
||||
Archetype {
|
||||
id: new_id,
|
||||
|
@ -99,12 +141,12 @@ impl Archetype {
|
|||
let mut columns: Vec<_> = archetype
|
||||
.columns
|
||||
.iter()
|
||||
.map(|c| c.new_empty_column())
|
||||
.map(|c| unsafe { c.create_empty(DEFAULT_CAPACITY) })
|
||||
.collect();
|
||||
|
||||
let idx = columns
|
||||
.iter()
|
||||
.position(|column| column.as_any().is::<Vec<T>>())
|
||||
.position(|column| column.info.type_id == TypeId::of::<T>())
|
||||
.unwrap();
|
||||
columns.remove(idx);
|
||||
|
||||
|
@ -115,7 +157,11 @@ impl Archetype {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_columns(new_id: ArchetypeId, columns: Vec<Box<dyn ComponentColumn>>) -> Archetype {
|
||||
pub fn from_bundle_info(new_id: ArchetypeId, bundle_info: Vec<ComponentInfo>) -> Archetype {
|
||||
let columns = bundle_info.into_iter().map(|i| {
|
||||
unsafe { ComponentColumn::new(i, DEFAULT_CAPACITY) }
|
||||
}).collect();
|
||||
|
||||
Archetype {
|
||||
id: new_id,
|
||||
entities: Vec::new(),
|
||||
|
@ -124,64 +170,42 @@ impl Archetype {
|
|||
}
|
||||
|
||||
pub fn get_component_mut<T: Component>(&mut self, entity: ArchetypeEntityId) -> Option<&mut T> {
|
||||
for col in self.columns.iter_mut() {
|
||||
if col.as_any().is::<Vec<T>>() {
|
||||
let components: &mut Vec<T> = col.as_any_mut().downcast_mut().unwrap();
|
||||
|
||||
return components.get_mut(entity.0 as usize);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn get_component<T: Component>(&self, entity: ArchetypeEntityId) -> Option<&T> {
|
||||
for col in self.columns.iter() {
|
||||
if col.as_ref().as_any().is::<Vec<T>>() {
|
||||
let components: &Vec<T> = col.as_any().downcast_ref().unwrap();
|
||||
|
||||
return components.get(entity.0 as usize);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
pub fn get_entity_component<T: Component>(&self, entity: ArchetypeEntityId) -> Option<&T> {
|
||||
let type_id = TypeId::of::<T>();
|
||||
|
||||
self.columns.iter().find(|c| c.info.type_id == type_id)
|
||||
.map(|c| unsafe { c.get(entity.0 as usize) })
|
||||
}
|
||||
|
||||
pub fn get_component_column<T: Component>(&self) -> Option<&Vec<T>> {
|
||||
let col = self.columns.iter().find(|c| c.as_any().is::<Vec<T>>())?;
|
||||
col.as_any().downcast_ref()
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn add_entity(&mut self, components: Vec<Box<dyn ComponentColumn>>) -> ArchetypeEntityId {
|
||||
let mut created_entity: Option<ArchetypeEntityId> = None;
|
||||
/// Add an entity and its component bundle to the Archetype
|
||||
///
|
||||
/// # Safety:
|
||||
///
|
||||
/// Archetype must contain all of the components
|
||||
pub(crate) fn add_entity<B>(&mut self, entity: Entity, bundle: B) -> ArchetypeEntityId
|
||||
where
|
||||
B: Bundle
|
||||
{
|
||||
let entity_index = self.entities.len();
|
||||
self.entities.push(entity);
|
||||
|
||||
for mut component in components.into_iter() {
|
||||
for col in self.columns.iter_mut() {
|
||||
if col.is_same_type(component.as_ref()) {
|
||||
match created_entity {
|
||||
Some(e) => {
|
||||
assert!(e.0 == col.len() as u64);
|
||||
},
|
||||
None => {
|
||||
created_entity = Some(ArchetypeEntityId(col.len() as u64));
|
||||
}
|
||||
}
|
||||
|
||||
col.append(component.as_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
created_entity.expect("Failure to create entity!")
|
||||
bundle.takefn(|data, type_id, size| {
|
||||
let col = self.columns.iter_mut().find(|c| c.info.type_id == type_id).unwrap();
|
||||
unsafe { col.set_at(entity_index, data); }
|
||||
});
|
||||
|
||||
ArchetypeEntityId(entity_index as u64)
|
||||
}
|
||||
|
||||
/// returns a boolean indicating whether this archetype can store the TypeIds given
|
||||
pub(crate) fn is_archetype_for(&self, types: Vec<TypeId>) -> bool {
|
||||
let types_iter = types.into_iter();
|
||||
|
||||
self.columns
|
||||
.iter()
|
||||
.map(|c| c.component_type_id())
|
||||
.eq(types_iter)
|
||||
self.columns.iter().all(|c| types.contains(&c.info.type_id))
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use std::any::{TypeId, Any};
|
||||
use std::{any::{TypeId, Any}, cell::Ref, ptr::NonNull, mem::size_of};
|
||||
|
||||
use crate::{archetype::ComponentColumn, component::Component};
|
||||
use crate::{archetype::{ComponentColumn, Archetype}, component::Component, component_info::ComponentInfo};
|
||||
|
||||
pub trait Bundle {
|
||||
// Get a list of type ids that this bundle is storing
|
||||
|
@ -8,10 +8,35 @@ pub trait Bundle {
|
|||
/// Take components into a list.
|
||||
/// The return value could be seen as a list of a list of components, but that inner list
|
||||
/// only contains a single value to make it easier to add it to an archetype.
|
||||
fn take_components(self) -> Vec<Box<dyn ComponentColumn>>;
|
||||
//fn take_components(self) -> Vec<Box<dyn ComponentColumn>>;
|
||||
|
||||
//fn from_component_columns(columns: Vec<&Box<dyn ComponentColumn>>) -> Self;
|
||||
//fn from_archetype(archetype: &Archetype) -> Self;
|
||||
|
||||
fn info(&self) -> Vec<ComponentInfo>;
|
||||
|
||||
/// 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.
|
||||
fn takefn(self, cb: impl FnMut(NonNull<u8>, TypeId, usize));
|
||||
}
|
||||
|
||||
macro_rules! impl_bundle_tuple {
|
||||
impl<C1: Component> Bundle for (C1,) {
|
||||
fn types(&self) -> Vec<TypeId> {
|
||||
vec![self.0.type_id()]
|
||||
}
|
||||
|
||||
fn info(&self) -> Vec<ComponentInfo> {
|
||||
vec![ComponentInfo::new::<C1>()]
|
||||
}
|
||||
|
||||
fn takefn(self, mut cb: impl FnMut(NonNull<u8>, TypeId, usize)) {
|
||||
let (c1, ) = self;
|
||||
|
||||
cb(NonNull::from(&c1).cast(), TypeId::of::<C1>(), size_of::<C1>());
|
||||
}
|
||||
}
|
||||
|
||||
/* macro_rules! impl_bundle_tuple {
|
||||
( $(($name: ident, $index: tt))+ ) => (
|
||||
impl<$($name: Component),+> Bundle for ($($name,)+) {
|
||||
fn types(&self) -> Vec<TypeId> {
|
||||
|
@ -21,13 +46,23 @@ macro_rules! impl_bundle_tuple {
|
|||
fn take_components(self) -> Vec<Box<dyn ComponentColumn>> {
|
||||
vec![$(Box::new(vec![self.$index])),+]
|
||||
}
|
||||
|
||||
fn from_component_columns(columns: Vec<&Box<dyn ComponentColumn>>) -> Self {
|
||||
let mut chains = Vec::new();
|
||||
|
||||
for col in columns.iter() {
|
||||
col.
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
} */
|
||||
|
||||
// hopefully 16 components in a bundle is enough
|
||||
impl_bundle_tuple! { (C1, 0) }
|
||||
impl_bundle_tuple! { (C1, 0) (C2, 1) }
|
||||
//impl_bundle_tuple! { (C1, 0) }
|
||||
/* impl_bundle_tuple! { (C1, 0) (C2, 1) }
|
||||
impl_bundle_tuple! { (C1, 0) (C2, 1) (C3, 2) }
|
||||
impl_bundle_tuple! { (C1, 0) (C2, 1) (C3, 2) (C4, 3) }
|
||||
impl_bundle_tuple! { (C1, 0) (C2, 1) (C3, 2) (C4, 3) (C5, 4) }
|
||||
|
@ -41,3 +76,4 @@ impl_bundle_tuple! { (C1, 0) (C2, 1) (C3, 2) (C4, 3) (C5, 4) (C6, 5) (C7, 6) (C8
|
|||
impl_bundle_tuple! { (C1, 0) (C2, 1) (C3, 2) (C4, 3) (C5, 4) (C6, 5) (C7, 6) (C8, 7) (C9, 8) (C10, 9) (C11, 10) (C12, 11) (C13, 12) }
|
||||
impl_bundle_tuple! { (C1, 0) (C2, 1) (C3, 2) (C4, 3) (C5, 4) (C6, 5) (C7, 6) (C8, 7) (C9, 8) (C10, 9) (C11, 10) (C12, 11) (C13, 12) (C14, 13) }
|
||||
impl_bundle_tuple! { (C1, 0) (C2, 1) (C3, 2) (C4, 3) (C5, 4) (C6, 5) (C7, 6) (C8, 7) (C9, 8) (C10, 9) (C11, 10) (C12, 11) (C13, 12) (C14, 13) (C15, 14) }
|
||||
*/
|
|
@ -0,0 +1,18 @@
|
|||
use std::{any::{TypeId, type_name}, alloc::Layout};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ComponentInfo {
|
||||
pub type_id: TypeId,
|
||||
pub name: String,
|
||||
pub layout: Layout,
|
||||
}
|
||||
|
||||
impl ComponentInfo {
|
||||
pub fn new<T: 'static>() -> Self {
|
||||
Self {
|
||||
type_id: TypeId::of::<T>(),
|
||||
name: type_name::<T>().to_string(),
|
||||
layout: Layout::new::<T>(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
//#![feature(cell_leak)]
|
||||
use crate::world::World;
|
||||
|
||||
mod archetype;
|
||||
mod world;
|
||||
mod bundle;
|
||||
mod component;
|
||||
mod query;
|
||||
mod component_info;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Position2d(i32, i32);
|
||||
|
@ -25,7 +28,14 @@ fn main() {
|
|||
|
||||
println!("\nstart of querying!\n");
|
||||
|
||||
for pos in world.query::<Position2d>() {
|
||||
/* for pos in world.query::<Position2d>() {
|
||||
println!("Queried Position2d: {:?}", pos);
|
||||
} */
|
||||
|
||||
let mut q = world.query_better();
|
||||
q.with_component::<Position2d>();
|
||||
|
||||
for pos in q.run::<Position2d>() {
|
||||
println!("Queried Position2d: {:?}", pos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
use std::{marker::PhantomData, any::TypeId, ptr::NonNull};
|
||||
|
||||
use super::{Fetch, Query};
|
||||
|
||||
pub struct FetchBorrow<'a, T> {
|
||||
ptr: NonNull<u8>,
|
||||
size: usize,
|
||||
_phantom: PhantomData<&'a T>
|
||||
}
|
||||
|
||||
impl<'a, T> Fetch<'a> for FetchBorrow<'a, T>
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
type Item = &'a T;
|
||||
|
||||
fn create_empty() -> Self {
|
||||
FetchBorrow {
|
||||
ptr: NonNull::dangling(),
|
||||
size: 0,
|
||||
_phantom: PhantomData::default()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||
let ptr = NonNull::new_unchecked(self.ptr.as_ptr()
|
||||
.add(entity.0 as usize * self.size))
|
||||
.cast();
|
||||
&*ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryBorrow<T> {
|
||||
type_id: TypeId,
|
||||
_phantom: PhantomData<T>
|
||||
}
|
||||
|
||||
impl<T> Query for QueryBorrow<T>
|
||||
where
|
||||
T: 'static
|
||||
{
|
||||
type Item<'a> = &'a T;
|
||||
|
||||
type Fetch<'a> = FetchBorrow<'a, T>;
|
||||
|
||||
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
|
||||
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
|
||||
}
|
||||
|
||||
unsafe fn fetch<'a>(&self, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
|
||||
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
|
||||
.expect("You ignored 'can_visit_archetype'!");
|
||||
let col_data = col.data;
|
||||
|
||||
FetchBorrow {
|
||||
ptr: NonNull::new_unchecked(col_data.as_ptr()),
|
||||
size: col.info.layout.size(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FetchBorrowMut<'a, T> {
|
||||
ptr: NonNull<u8>,
|
||||
size: usize,
|
||||
_phantom: PhantomData<&'a T>
|
||||
}
|
||||
|
||||
impl<'a, T> Fetch<'a> for FetchBorrowMut<'a, T>
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn create_empty() -> Self {
|
||||
FetchBorrowMut {
|
||||
ptr: NonNull::dangling(),
|
||||
size: 0,
|
||||
_phantom: PhantomData::default()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||
let ptr = NonNull::new_unchecked(self.ptr.as_ptr()
|
||||
.add(entity.0 as usize * self.size))
|
||||
.cast();
|
||||
&mut *ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryBorrowMut<T> {
|
||||
type_id: TypeId,
|
||||
_phantom: PhantomData<T>
|
||||
}
|
||||
|
||||
impl<T> Query for QueryBorrowMut<T>
|
||||
where
|
||||
T: 'static
|
||||
{
|
||||
type Item<'a> = &'a mut T;
|
||||
|
||||
type Fetch<'a> = FetchBorrowMut<'a, T>;
|
||||
|
||||
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
|
||||
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
|
||||
}
|
||||
|
||||
unsafe fn fetch<'a>(&self, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
|
||||
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
|
||||
.expect("You ignored 'can_visit_archetype'!");
|
||||
let col_data = col.data;
|
||||
|
||||
FetchBorrowMut {
|
||||
ptr: NonNull::new_unchecked(col_data.as_ptr()),
|
||||
size: col.info.layout.size(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::any::TypeId;
|
||||
|
||||
use crate::{world::World, archetype::Archetype, query::View};
|
||||
|
||||
use super::{QueryBorrow, QueryBorrowMut};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct Vec2 {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
impl Vec2 {
|
||||
pub fn new(x: f32, y: f32) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a world with two entities, one at Vec(10, 50) and
|
||||
/// the other at Vec2(25, 30).
|
||||
fn prepare_world() -> World {
|
||||
let mut world = World::new();
|
||||
|
||||
world.spawn((Vec2::new(10.0, 50.0),));
|
||||
world.spawn((Vec2::new(25.0, 30.0),));
|
||||
world
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn borrow_query() {
|
||||
let world = prepare_world();
|
||||
|
||||
let borrow = QueryBorrow::<Vec2> {
|
||||
type_id: TypeId::of::<Vec2>(),
|
||||
_phantom: std::marker::PhantomData,
|
||||
};
|
||||
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
|
||||
let v = View::new(borrow, archetypes);
|
||||
|
||||
for e in v.into_iter() {
|
||||
println!("Found entity at {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn borrow_mut_query() {
|
||||
let world = prepare_world();
|
||||
|
||||
let borrow = QueryBorrowMut::<Vec2> {
|
||||
type_id: TypeId::of::<Vec2>(),
|
||||
_phantom: std::marker::PhantomData,
|
||||
};
|
||||
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
|
||||
let v = View::new(borrow, archetypes);
|
||||
|
||||
let mut orig = vec![];
|
||||
|
||||
for v in v.into_iter() {
|
||||
orig.push(v.clone());
|
||||
v.x += 10.0;
|
||||
v.y += 10.0;
|
||||
}
|
||||
|
||||
// Now make sure the changes were actually made
|
||||
|
||||
let borrow = QueryBorrow::<Vec2> {
|
||||
type_id: TypeId::of::<Vec2>(),
|
||||
_phantom: std::marker::PhantomData,
|
||||
};
|
||||
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
|
||||
let v = View::new(borrow, archetypes);
|
||||
|
||||
for (new, orig) in v.into_iter().zip(orig.iter()) {
|
||||
assert!(new.x - orig.x == 10.0);
|
||||
assert!(new.y - orig.y == 10.0);
|
||||
}
|
||||
println!("They were modified!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
use crate::{world::Entity, archetype::{Archetype, ArchetypeId}};
|
||||
|
||||
use super::{Fetch, Query};
|
||||
|
||||
pub struct EntitiesFetch<'a> {
|
||||
entities: &'a [Entity],
|
||||
}
|
||||
|
||||
impl<'a> Fetch<'a> for EntitiesFetch<'a> {
|
||||
type Item = Entity;
|
||||
|
||||
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||
let e = *self.entities.get_unchecked(entity.0 as usize);
|
||||
e
|
||||
}
|
||||
|
||||
fn create_empty() -> Self {
|
||||
Self {
|
||||
entities: &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Entities;
|
||||
|
||||
impl Query for Entities {
|
||||
type Item<'a> = Entity;
|
||||
|
||||
type Fetch<'a> = EntitiesFetch<'a>;
|
||||
|
||||
fn can_visit_archetype(&self, archetype: &Archetype) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn fetch<'a>(&self, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a> {
|
||||
EntitiesFetch {
|
||||
entities: &archetype.entities,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
use std::{any::{TypeId, Any}, cell::RefCell, rc::Rc, collections::{HashMap, VecDeque}, io::Read, slice::Iter, ops::Range};
|
||||
|
||||
use crate::{archetype::{Archetype, ComponentColumn, ArchetypeId}, bundle::Bundle, component::Component, world::{Record, EntityId, ArchetypeEntityId}};
|
||||
|
||||
pub mod view;
|
||||
pub use view::*;
|
||||
|
||||
pub mod entities;
|
||||
pub use entities::*;
|
||||
|
||||
pub mod borrow;
|
||||
pub use borrow::*;
|
||||
|
||||
pub struct QuerySimple<'a> {
|
||||
//entities: Iter<Entity
|
||||
archetypes: Vec<&'a Archetype>,
|
||||
type_ids: Vec<TypeId>,
|
||||
}
|
||||
|
||||
impl<'a> QuerySimple<'a> {
|
||||
pub fn new(archetypes: Vec<&'a Archetype>) -> Self {
|
||||
Self {
|
||||
archetypes,
|
||||
type_ids: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_component<T: Any>(&mut self) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
self.type_ids.push(type_id);
|
||||
}
|
||||
|
||||
pub fn run<C1: Component>(&self) -> impl Iterator<Item = &C1>
|
||||
{
|
||||
//let mut columns = Vec::new();
|
||||
|
||||
/* for type_id in self.type_ids.iter() {
|
||||
for arch in self.archetypes.iter() {
|
||||
if let Some(col) = arch.columns
|
||||
.iter().find(|c| c.component_type_id() == type_id.clone()) {
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut entities: Vec<&C1> = columns.iter().map(|bx| bx.as_any().downcast_ref::<C1>()
|
||||
.expect("Failure to downcast component column"))
|
||||
.collect(); */
|
||||
|
||||
let entities = self.archetypes
|
||||
.iter()
|
||||
.filter_map(|a| a.get_component_column::<C1>())
|
||||
.flatten();
|
||||
|
||||
|
||||
/* let columns = self.type_ids
|
||||
.iter()
|
||||
.map(|type_id| {
|
||||
let a = self.archetypes
|
||||
.iter()
|
||||
.filter_map(|arch| {
|
||||
arch.columns
|
||||
.iter()
|
||||
.find(|c| c.component_type_id() == type_id.clone())
|
||||
});
|
||||
}); */
|
||||
|
||||
/* self.type_ids
|
||||
.iter()
|
||||
.map(|type_id| {
|
||||
self.archetypes
|
||||
.iter()
|
||||
.map(|arch| {
|
||||
arch.get_component_column_typeid(type_id.clone())
|
||||
})
|
||||
}); */
|
||||
|
||||
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Fetch`]er implementation gets data out of an archetype.
|
||||
pub trait Fetch<'a> {
|
||||
/// The type that this Fetch yields
|
||||
type Item: 'a;
|
||||
|
||||
/// Returns true if the entity should be visited or skipped.
|
||||
fn can_visit_item(&mut self, entity: ArchetypeEntityId) -> bool {
|
||||
let _ = entity; // ignore compiler warning
|
||||
true
|
||||
}
|
||||
|
||||
/// Creates an empty Fetch. DO NOT try to `get_item` on this
|
||||
fn create_empty() -> Self;
|
||||
|
||||
unsafe fn get_item(&mut self, entity: ArchetypeEntityId) -> Self::Item;
|
||||
}
|
||||
|
||||
pub trait Query {
|
||||
/// The item that this query yields
|
||||
type Item<'a>: 'a;
|
||||
|
||||
/// The fetcher used for this query
|
||||
type Fetch<'a>: Fetch<'a, Item = Self::Item<'a>>;
|
||||
|
||||
/// Returns true if the archetype should be visited or skipped.
|
||||
fn can_visit_archetype(&self, archetype: &Archetype) -> bool;
|
||||
|
||||
unsafe fn fetch<'a>(&self, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{world::World, archetype::Archetype};
|
||||
|
||||
use super::{View, Entities};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct Vec2 {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
impl Vec2 {
|
||||
pub fn new(x: f32, y: f32) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_view() {
|
||||
let mut world = World::new();
|
||||
|
||||
world.spawn((Vec2::new(10.0, 50.0),));
|
||||
world.spawn((Vec2::new(25.0, 30.0),));
|
||||
|
||||
let entities = Entities {};
|
||||
|
||||
let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
|
||||
|
||||
let v = View::new(entities, archetypes);
|
||||
|
||||
for e in v.into_iter() {
|
||||
println!("Got entity! {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use crate::{archetype::{Archetype, ArchetypeId}, world::ArchetypeEntityId};
|
||||
|
||||
use super::{Query, Fetch};
|
||||
|
||||
pub struct View<'a, Q: Query> {
|
||||
query: Q,
|
||||
archetypes: Vec<&'a Archetype>,
|
||||
}
|
||||
|
||||
impl<'a, Q> View<'a, Q>
|
||||
where
|
||||
Q: Query,
|
||||
{
|
||||
pub fn new(query: Q, archetypes: Vec<&'a Archetype>) -> Self {
|
||||
Self {
|
||||
query,
|
||||
archetypes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Q> IntoIterator for View<'a, Q>
|
||||
where
|
||||
Q: Query,
|
||||
{
|
||||
type Item = Q::Item<'a>;
|
||||
|
||||
type IntoIter = ViewIter<'a, Q>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
ViewIter {
|
||||
query: self.query,
|
||||
fetcher: Q::Fetch::create_empty(),
|
||||
archetypes: self.archetypes,
|
||||
next_archetype: 0,
|
||||
component_indices: 0..0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ViewIter<'a, Q: Query> {
|
||||
query: Q,
|
||||
fetcher: Q::Fetch<'a>,
|
||||
archetypes: Vec<&'a Archetype>,
|
||||
next_archetype: usize,
|
||||
component_indices: Range<u64>,
|
||||
}
|
||||
|
||||
impl<'a, Q> Iterator for ViewIter<'a, Q>
|
||||
where
|
||||
Q: Query,
|
||||
{
|
||||
type Item = Q::Item<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let Some(entity_index) = self.component_indices.next() {
|
||||
let entity_index = ArchetypeEntityId(entity_index);
|
||||
if !self.fetcher.can_visit_item(entity_index) {
|
||||
continue;
|
||||
} else {
|
||||
let i = unsafe { self.fetcher.get_item(entity_index) };
|
||||
return Some(i);
|
||||
}
|
||||
} else {
|
||||
if self.next_archetype >= self.archetypes.len() {
|
||||
return None; // ran out of archetypes to go through
|
||||
}
|
||||
|
||||
let arch_id = self.next_archetype;
|
||||
self.next_archetype += 1;
|
||||
let arch = unsafe { self.archetypes.get_unchecked(arch_id) };
|
||||
|
||||
if arch.entities.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !self.query.can_visit_archetype(arch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.fetcher = unsafe { self.query.fetch(ArchetypeId(arch_id as u64), arch) };
|
||||
self.component_indices = 0..arch.entities.len() as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use std::{collections::{HashMap, VecDeque}, any::{Any, TypeId}};
|
||||
use std::slice::Iter;
|
||||
|
||||
use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, component::Component};
|
||||
use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, component::Component, query::QuerySimple};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct EntityId(pub u64);
|
||||
|
@ -11,18 +11,19 @@ pub struct EntityId(pub u64);
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArchetypeEntityId(pub u64);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Entity {
|
||||
id: EntityId,
|
||||
generation: u64,
|
||||
}
|
||||
|
||||
struct Record {
|
||||
pub struct Record {
|
||||
id: ArchetypeId,
|
||||
index: ArchetypeEntityId,
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
archetypes: HashMap<ArchetypeId, Archetype>,
|
||||
pub(crate) archetypes: HashMap<ArchetypeId, Archetype>,
|
||||
next_archetype_id: ArchetypeId,
|
||||
entity_index: HashMap<EntityId, Record>,
|
||||
dead_entities: VecDeque<Entity>,
|
||||
|
@ -68,9 +69,7 @@ impl World {
|
|||
.find(|a| a.is_archetype_for(bundle_types.clone()));
|
||||
|
||||
if let Some(archetype) = archetype {
|
||||
// take components from the bundle and add it to the archetype
|
||||
let columns = bundle.take_components();
|
||||
let arche_idx = archetype.add_entity(columns);
|
||||
let arche_idx = archetype.add_entity(new_entity, bundle);
|
||||
|
||||
// Create entity record and store it
|
||||
let record = Record {
|
||||
|
@ -82,11 +81,10 @@ impl World {
|
|||
}
|
||||
// create a new archetype if one isn't found
|
||||
else {
|
||||
let columns = bundle.take_components();
|
||||
|
||||
// create archetype
|
||||
let new_arch_id = self.next_archetype_id.increment();
|
||||
let archetype = Archetype::from_columns(new_arch_id, columns);
|
||||
let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info());
|
||||
let entity_arch_id = archetype.add_entity(new_entity, bundle);
|
||||
|
||||
// store archetype
|
||||
self.archetypes.insert(new_arch_id, archetype);
|
||||
|
@ -95,7 +93,7 @@ impl World {
|
|||
let record = Record {
|
||||
id: new_arch_id,
|
||||
// this is the first entity in the archetype
|
||||
index: ArchetypeEntityId(0),
|
||||
index: entity_arch_id,
|
||||
};
|
||||
|
||||
self.entity_index.insert(new_entity.id, record);
|
||||
|
@ -108,7 +106,7 @@ impl World {
|
|||
let record = self.entity_index.get(&entity.id)?;
|
||||
let archetype = self.archetypes.get(&record.id)?;
|
||||
|
||||
archetype.get_component(record.index)
|
||||
archetype.get_entity_component(record.index)
|
||||
}
|
||||
|
||||
pub fn query<T: Component>(&self) -> impl Iterator<Item = &T> {
|
||||
|
@ -118,10 +116,34 @@ impl World {
|
|||
.flatten()
|
||||
}
|
||||
|
||||
pub fn query_better(&self) -> QuerySimple {
|
||||
let archetypes = self.archetypes.values().collect();
|
||||
QuerySimple::new(archetypes)
|
||||
}
|
||||
|
||||
/* pub fn query_m<B: Bundle>(&self) -> impl Iterator<Item = &T> {
|
||||
self.archetypes
|
||||
.iter()
|
||||
.filter_map(|(_, a)| a.get_component_column::<T>())
|
||||
.flatten()
|
||||
} */
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::World;
|
||||
|
||||
struct Vec2 {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawning_entity() {
|
||||
let mut world = World::new();
|
||||
let _e = world.spawn((Vec2 {
|
||||
x: 10.0,
|
||||
y: 15.0,
|
||||
}, ));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue