Switch to use lyra-ecs

This commit is contained in:
SeanOMik 2023-12-26 14:12:53 -05:00
parent 64519b2b4f
commit 2805399fe4
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
40 changed files with 587 additions and 248 deletions

22
.vscode/launch.json vendored
View File

@ -40,6 +40,28 @@
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'lyra-ecs'",
"cargo": {
"args": [
"test",
"--no-run",
"--lib",
"--package=lyra-ecs",
"world::tests::view_change_tracking",
"--",
"--exact --nocapture"
],
"filter": {
"name": "lyra-ecs",
"kind": "lib"
}
},
"args": [],
"cwd": "${workspaceFolder}/lyra-ecs"
}
]
}

71
Cargo.lock generated
View File

@ -261,7 +261,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -399,7 +399,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -631,7 +631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c94d80dc0f05250a9082bb9455bbf3d6c6c51db388b060df914aebcfb4a9b9f1"
dependencies = [
"edict-proc-lib",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -643,7 +643,7 @@ dependencies = [
"proc-easy",
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -931,7 +931,7 @@ dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -1283,6 +1283,25 @@ dependencies = [
"value-bag",
]
[[package]]
name = "lyra-ecs"
version = "0.1.0"
dependencies = [
"anyhow",
"lyra-ecs-derive",
"rand 0.8.5",
"thiserror",
]
[[package]]
name = "lyra-ecs-derive"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.42",
]
[[package]]
name = "lyra-engine"
version = "0.0.1"
@ -1294,17 +1313,17 @@ dependencies = [
"atomicell",
"bytemuck",
"cfg-if",
"edict",
"gilrs-core",
"glam",
"image",
"instant",
"itertools",
"lyra-ecs",
"lyra-resource",
"petgraph",
"quote",
"stopwatch",
"syn 2.0.26",
"syn 2.0.42",
"tobj",
"tracing",
"tracing-appender",
@ -1685,7 +1704,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -1834,7 +1853,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -1898,7 +1917,7 @@ checksum = "ea59c637cd0e6b71ae18e589854e9de9b7cb17fefdbf2047e42bd38e24285b19"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -1913,9 +1932,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.64"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
"unicode-ident",
]
@ -1937,9 +1956,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.29"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@ -2137,7 +2156,7 @@ checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -2291,9 +2310,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.26"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
"proc-macro2",
"quote",
@ -2315,30 +2334,30 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-std",
"edict",
"fps_counter",
"lyra-ecs",
"lyra-engine",
"tracing",
]
[[package]]
name = "thiserror"
version = "1.0.48"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.48"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -2478,7 +2497,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
]
[[package]]
@ -2613,7 +2632,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
"wasm-bindgen-shared",
]
@ -2647,7 +2666,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.42",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View File

@ -6,11 +6,13 @@ edition = "2021"
[workspace]
members = [
"lyra-resource",
"lyra-ecs",
"examples/testbed"
]
[dependencies]
lyra-resource = { path = "lyra-resource", version = "0.0.1" }
lyra-ecs = { path = "lyra-ecs" }
winit = "0.28.1"
tracing = "0.1.37"
@ -31,7 +33,7 @@ glam = { version = "0.24.0", features = ["bytemuck", "debug-glam-assert"] }
gilrs-core = "0.5.6"
syn = "2.0.26"
quote = "1.0.29"
edict = "0.5.0"
#edict = "0.5.0"
atomicell = "0.1.9"
aligned-vec = "0.5.0"
tracing-appender = "0.2.2"

View File

@ -7,8 +7,8 @@ edition = "2021"
[dependencies]
lyra-engine = { path = "../../", version = "0.0.1" }
lyra-ecs = { path = "../../lyra-ecs"}
anyhow = "1.0.75"
async-std = "1.12.0"
tracing = "0.1.37"
fps_counter = "2.0.0"
edict = "0.5.0"
fps_counter = "2.0.0"

View File

@ -1,6 +1,6 @@
use std::ops::Deref;
use edict::{Component, World};
use lyra_ecs::{Component, world::World};
use lyra_engine::{
ecs::{components::{camera::CameraComponent, DeltaTime}, EventQueue, SimpleSystem},
game::Game,
@ -48,14 +48,14 @@ impl SimpleSystem for FreeFlyCameraPlugin {
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
let mut camera_rot = Vec3::default();
let delta_time = **world.get_resource::<DeltaTime>().unwrap();
let delta_time = **world.get_resource::<DeltaTime>();
let events = world
.get_resource_mut::<EventQueue>()
.try_get_resource_mut::<EventQueue>()
.and_then(|q| q.read_events::<MouseMotion>());
let keys = world
.get_resource::<InputButtons<KeyCode>>()
.try_get_resource::<InputButtons<KeyCode>>()
.map(|r| r.deref().clone());
if let Some(keys) = keys.as_ref() {
@ -84,9 +84,8 @@ impl SimpleSystem for FreeFlyCameraPlugin {
}
}
for (cam, fly) in world
.query_mut::<(&mut CameraComponent, &mut FreeFlyCamera)>()
.iter_mut()
for (mut cam, mut fly) in world
.view_iter::<(&mut CameraComponent, &mut FreeFlyCamera)>()
{
let forward = cam.transform.forward();
let left = cam.transform.left();

View File

@ -1,4 +1,4 @@
use lyra_engine::{math::{self, Vec3}, ecs::{World, components::{transform::TransformComponent, camera::CameraComponent, model::ModelComponent, DeltaTime}, EventQueue, SimpleSystem, Component, Criteria, CriteriaSchedule, BatchedSystem}, math::Transform, input::{KeyCode, InputButtons, MouseMotion, ActionHandler, Layout, Action, ActionKind, LayoutId, ActionMapping, Binding, ActionSource, ActionMappingId, InputActionPlugin, ActionState}, game::Game, plugin::Plugin, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct};
use lyra_engine::{math::{self, Vec3}, ecs::{components::{transform::TransformComponent, camera::CameraComponent, model::ModelComponent, DeltaTime}, EventQueue, SimpleSystem, Criteria, CriteriaSchedule, BatchedSystem, lyra_ecs::{Component, world::World}}, math::Transform, input::{KeyCode, InputButtons, MouseMotion, ActionHandler, Layout, Action, ActionKind, LayoutId, ActionMapping, Binding, ActionSource, ActionMappingId, InputActionPlugin, ActionState}, game::Game, plugin::Plugin, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct};
use lyra_engine::assets::{ResourceManager, Model};
mod free_fly_camera;
@ -39,9 +39,9 @@ impl FixedTimestep {
}
impl Criteria for FixedTimestep {
fn can_run(&mut self, world: &mut edict::World, check_count: u32) -> CriteriaSchedule {
fn can_run(&mut self, world: &mut World, check_count: u32) -> CriteriaSchedule {
if check_count == 0 {
let delta_time = world.get_resource::<DeltaTime>().unwrap();
let delta_time = world.get_resource::<DeltaTime>();
self.accumulator += **delta_time;
}
@ -64,12 +64,12 @@ struct CubeFlag;
async fn main() {
let setup_sys = |world: &mut World| -> anyhow::Result<()> {
{
let mut window_options = world.get_resource_mut::<Ct<WindowOptions>>().unwrap();
let mut window_options = world.get_resource_mut::<Ct<WindowOptions>>();
window_options.cursor_grab = CursorGrabMode::Confined;
window_options.cursor_visible = false;
}
let mut resman = world.get_resource_mut::<ResourceManager>().unwrap();
let mut resman = world.get_resource_mut::<ResourceManager>();
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
//let cube_model = resman.request::<Model>("assets/cube-texture-bin.glb").unwrap();
@ -183,7 +183,7 @@ async fn main() {
};
let fps_system = |world: &mut World| -> anyhow::Result<()> {
let mut counter = world.get_resource_mut::<fps_counter::FPSCounter>().unwrap();
let mut counter = world.get_resource_mut::<fps_counter::FPSCounter>();
let fps = counter.tick();
@ -193,32 +193,32 @@ async fn main() {
};
let fps_plugin = move |game: &mut Game| {
let world = game.world();
world.insert_resource(fps_counter::FPSCounter::new());
world.add_resource(fps_counter::FPSCounter::new());
};
let spin_system = |world: &mut World| -> anyhow::Result<()> {
const SPEED: f32 = 4.0;
let delta_time = **world.get_resource::<DeltaTime>().unwrap();
let delta_time = **world.get_resource::<DeltaTime>();
for (transform, _) in world.query_mut::<(&mut TransformComponent, &CubeFlag)>().iter_mut() {
for (mut transform, _) in world.view_iter::<(&mut TransformComponent, &CubeFlag)>() {
let t = &mut transform.transform;
t.rotate_y(math::Angle::Degrees(SPEED * delta_time));
}
/* for (transform, s) in world.query_mut::<(&mut TransformComponent, &mut SpotLight)>().iter_mut() {
for (mut transform, _s) in world.view_iter::<(&mut TransformComponent, &mut SpotLight)>() {
let t = &mut transform.transform;
t.rotate_x(math::Angle::Degrees(SPEED * delta_time));
} */
}
Ok(())
};
let jiggle_plugin = move |game: &mut Game| {
game.world().insert_resource(TpsAccumulator(0.0));
game.world().add_resource(TpsAccumulator(0.0));
let mut sys = BatchedSystem::new();
sys.with_criteria(FixedTimestep::new(45));
//sys.with_system(spin_system);
sys.with_system(spin_system);
//sys.with_system(fps_system);
game.with_system("fixed", sys, &[]);
@ -243,7 +243,7 @@ async fn main() {
);
let test_system = |world: &mut World| -> anyhow::Result<()> {
let handler = world.get_resource::<ActionHandler>().unwrap();
let handler = world.get_resource::<ActionHandler>();
if let Some(alpha) = handler.get_pressed_modifier("forward_backward") {
debug!("'forward_backward': {alpha}");
@ -256,7 +256,7 @@ async fn main() {
Ok(())
};
game.world().insert_resource(action_handler);
game.world().add_resource(action_handler);
game.with_plugin(InputActionPlugin);
//game.with_system("test_actions", test_system, &[]);
};

View File

@ -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, Deref}};
use crate::{world::{Entity, ArchetypeEntityId}, bundle::Bundle, component_info::ComponentInfo, DynTypeId};
use crate::{world::{Entity, ArchetypeEntityId}, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick};
#[derive(Clone)]
pub struct ComponentColumn {
@ -8,6 +8,8 @@ pub struct ComponentColumn {
pub len: usize,
pub capacity: usize,
pub info: ComponentInfo,
/// The last tick that this component was modified at.
pub entity_ticks: Vec<Tick>,
}
impl Drop for ComponentColumn {
@ -58,6 +60,7 @@ impl ComponentColumn {
capacity,
info,
len: 0,
entity_ticks: Vec::new(),
}
}
@ -66,7 +69,7 @@ impl ComponentColumn {
/// # 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>) {
pub unsafe fn set_at(&mut self, entity_index: usize, comp_src: NonNull<u8>, tick: Tick) {
assert!(entity_index < self.capacity);
let mut data = self.data.borrow_mut();
@ -77,7 +80,15 @@ impl ComponentColumn {
unsafe {
let layout = self.info.layout.into_layout_unchecked();
std::alloc::dealloc(comp_src.as_ptr(), layout);
//std::alloc::dealloc(comp_src.as_ptr(), layout);
}
// check if a component spot is being set twice and that the entity's tick is
// already stored
if self.entity_ticks.len() > entity_index {
self.entity_ticks[entity_index].tick_to(&tick);
} else {
self.entity_ticks.push(tick);
}
}
@ -101,7 +112,9 @@ impl ComponentColumn {
/// # Safety
///
/// This column must have the entity.
pub unsafe fn get_mut<T>(&mut self, entity_index: usize) -> &mut T {
pub unsafe fn get_mut<T>(&mut self, entity_index: usize, tick: &Tick) -> &mut T {
self.entity_ticks[entity_index].tick_to(&tick);
let mut data = self.data.borrow_mut();
let data = data.deref_mut();
@ -116,6 +129,8 @@ impl ComponentColumn {
/// Parameters:
/// * `new_capacity` - The new capacity of components that can fit in this column.
///
/// Note: This does not modify the Tick of this column, since no components were actually modified.
///
/// # Safety
///
/// Will panic if `new_capacity` is less than the current capacity of the column.
@ -123,7 +138,6 @@ impl ComponentColumn {
assert!(new_capacity > self.capacity);
let mut data = self.data.borrow_mut();
//let data = data.deref_mut();
let mut new_ptr = Self::alloc(self.info.layout.into_layout_unchecked(), new_capacity);
@ -148,7 +162,9 @@ impl ComponentColumn {
}
/// Removes a component from the column, freeing it, and returning the old index of the entity that took its place in the column.
pub unsafe fn remove_component(&mut self, entity_index: usize) -> Option<usize> {
pub unsafe fn remove_component(&mut self, entity_index: usize, tick: &Tick) -> Option<usize> {
//self.entity_ticks[entity_index].tick_to(&tick);
let mut data = self.data.borrow_mut();
let data = data.deref_mut();
@ -163,6 +179,11 @@ impl ComponentColumn {
ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), self.info.layout.size);
mem::swap(&mut old_comp_ptr, &mut new_comp_ptr); // new_comp_ptr is now the old ptr
// make sure to keep entity indexes correct in the ticks list as well
self.entity_ticks.swap(moved_index, entity_index);
self.entity_ticks.pop();
Some(moved_index)
} else { None };
@ -231,7 +252,7 @@ impl Archetype {
/// # Safety:
///
/// Archetype must contain all of the components
pub(crate) fn add_entity<B>(&mut self, entity: Entity, bundle: B) -> ArchetypeEntityId
pub(crate) fn add_entity<B>(&mut self, entity: Entity, bundle: B, tick: &Tick) -> ArchetypeEntityId
where
B: Bundle
{
@ -246,7 +267,7 @@ impl Archetype {
bundle.take(|data, type_id, _size| {
let col = self.get_column_mut(type_id).unwrap();
unsafe { col.set_at(entity_index, data); }
unsafe { col.set_at(entity_index, data, tick.clone()); }
col.len += 1;
});
@ -254,20 +275,21 @@ impl Archetype {
}
/// Removes an entity from the Archetype and frees its components. Returns the entity record that took its place in the component column.
pub(crate) fn remove_entity(&mut self, entity: Entity) -> Option<(Entity, ArchetypeEntityId)> {
pub(crate) fn remove_entity(&mut self, entity: Entity, tick: &Tick) -> Option<(Entity, ArchetypeEntityId)> {
let entity_index = *self.entities.get(&entity)
.expect("The entity is not in this Archetype!");
let mut removed_entity: Option<(Entity, ArchetypeEntityId)> = None;
for c in self.columns.iter_mut() {
let moved_entity = unsafe { c.remove_component(entity_index.0 as usize) };
let moved_entity = unsafe { c.remove_component(entity_index.0 as usize, tick) };
// Make sure that the moved entity is the same as what was moved in other columns.
// If this is the first move, find the EntityId that points to the column index.
// If there wasn't a moved entity, make sure no other columns moved something.
if let Some(res) = moved_entity {
if let Some((_, aid)) = removed_entity {
assert!(res as u64 == aid.0); // make sure we removed the same entity
assert!(res as u64 == aid.0); // make sure all columns removed the same entity
} else {
let replaced_entity = self.entities.iter().find(|(_, a)| a.0 == res as u64)
.map(|(e, _a)| *e).expect("Failure to find entity for moved component!");
@ -330,6 +352,9 @@ impl Archetype {
self.columns.iter().find(|c| c.info.type_id == type_id)
}
/// Returns a mutable borrow to a component column for `type_id`.
///
/// Note: This does not modify the tick for the column!
pub fn get_column_mut(&mut self, type_id: DynTypeId) -> Option<&mut ComponentColumn> {
self.columns.iter_mut().find(|c| c.info.type_id == type_id)
}
@ -358,7 +383,7 @@ impl Archetype {
/// The entity IS NOT removed from the old archetype. You must manually call [`Archetype::remove_entity`].
/// It was done this way because I had some borrow check issues when writing [`World::insert`]
/// 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>(&self, into_arch: &mut Archetype, entity: Entity, new_components: B)
where
B: Bundle
{
@ -387,7 +412,7 @@ impl Archetype {
});
//self.remove_entity(entity);
}
} */
pub fn entities(&self) -> &HashMap<Entity, ArchetypeEntityId> {
&self.entities
@ -400,7 +425,7 @@ mod tests {
use rand::Rng;
use crate::{ArchetypeId, tests::{Vec2, Vec3}, world::{Entity, EntityId}, bundle::Bundle, ComponentInfo, MemoryLayout, DynTypeId, DynamicBundle};
use crate::{ArchetypeId, tests::{Vec2, Vec3}, world::{Entity, EntityId}, bundle::Bundle, ComponentInfo, MemoryLayout, DynTypeId, DynamicBundle, Tick};
use super::Archetype;
@ -413,7 +438,7 @@ mod tests {
};
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), bundle.info());
let entity_arch_id = a.add_entity(entity, bundle);
let entity_arch_id = a.add_entity(entity, bundle, &Tick::default());
let col = a.columns.get(0).unwrap();
let vec2: &Vec2 = unsafe { col.get(entity_arch_id.0 as usize) };
@ -429,7 +454,7 @@ mod tests {
};
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), bundle.info());
let entity_arch_id = a.add_entity(entity, bundle);
let entity_arch_id = a.add_entity(entity, bundle, &Tick::default());
let col = a.columns.get(0).unwrap();
let vec2: &Vec2 = unsafe { col.get(entity_arch_id.0 as usize) };
@ -454,8 +479,8 @@ mod tests {
};
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), b1.info());
let earch1 = a.add_entity(e1, b1);
let earch2 = a.add_entity(e2, b2);
let earch1 = a.add_entity(e1, b1, &Tick::default());
let earch2 = a.add_entity(e2, b2, &Tick::default());
let col = a.columns.get(0).unwrap();
let vec2: &Vec2 = unsafe { col.get(earch1.0 as usize) };
@ -478,8 +503,8 @@ mod tests {
};
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), b1.info());
let earch1 = a.add_entity(e1, b1);
let earch2 = a.add_entity(e2, b2);
let earch1 = a.add_entity(e1, b1, &Tick::default());
let earch2 = a.add_entity(e2, b2, &Tick::default());
let col = a.columns.get(0).unwrap();
let vec2: &Vec2 = unsafe { col.get(earch1.0 as usize) };
@ -525,7 +550,8 @@ mod tests {
id: EntityId(i as u64),
generation: 0
},
c
c,
&Tick::default()
);
}
println!("Inserted {} entities", bundle_count);
@ -553,6 +579,7 @@ mod tests {
generation: 0
},
(bundles[i],),
&Tick::default()
);
}
@ -560,8 +587,9 @@ mod tests {
let moved_entity = a.remove_entity(
Entity {
id: EntityId(1u64),
generation: 0
}
generation: 0,
},
&Tick::default()
).expect("No entity was moved");
// The last entity in the column should have been moved
@ -592,7 +620,9 @@ mod tests {
Entity {
id: EntityId(0),
generation: 0
}, dynamic_bundle
},
dynamic_bundle,
&Tick::default()
);
let col = a.columns.iter().next().unwrap();
@ -600,47 +630,4 @@ mod tests {
assert_eq!(unsafe { *ptr.cast::<u32>().as_ref() }, comp);
assert_eq!(col.info, info);
}
#[test]
fn move_archetypes() {
let bundles = vec![Vec2::rand(), ];
let mut info = (bundles[0],).info();
let mut a = Archetype::from_bundle_info(ArchetypeId(0), info.clone());
let entity = Entity {
id: EntityId(0),
generation: 0
};
a.add_entity(entity, (bundles[0],));
info.extend((Vec3::new(0.0, 0.0, 0.0),).info().into_iter());
let mut new_a = Archetype::from_bundle_info(ArchetypeId(1), info);
let inserting_vec3 = Vec3::rand();
let new_bundle = (inserting_vec3,);
a.move_into(&mut new_a, entity, new_bundle);
let new_index = new_a.entities.get(&entity).unwrap();
// Check Vec3
{
let new_col = new_a.get_column(DynTypeId::of::<Vec3>()).unwrap();
let from_col = unsafe { new_col.get::<Vec3>(new_index.0 as _) };
println!("expected {:?}, got {:?}", inserting_vec3, *from_col);
assert_eq!(*from_col, inserting_vec3);
}
// Check Vec2
{
let new_col = new_a.get_column(DynTypeId::of::<Vec2>()).unwrap();
let from_col = unsafe { new_col.get::<Vec2>(new_index.0 as _) };
println!("expected {:?}, got {:?}", bundles[0], *from_col);
assert_eq!(*from_col, bundles[0]);
}
assert!(a.entities.contains_key(&entity));
assert!(a.remove_entity(entity).is_none()); // No entity would take its place
}
}

View File

@ -27,6 +27,9 @@ pub use resource::*;
mod system;
pub use system::*;
mod tick;
pub use tick::*;
pub use lyra_ecs_derive::*;
#[cfg(test)]

View File

@ -1,6 +1,6 @@
use std::{marker::PhantomData, any::TypeId, ptr::NonNull, cell::{Ref, RefCell, RefMut}};
use crate::{world::World, ComponentColumn, DynTypeId};
use crate::{world::World, ComponentColumn, DynTypeId, Tick};
use super::{Fetch, Query, AsQuery};
@ -78,7 +78,7 @@ where
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
}
unsafe fn fetch<'a>(&self, _world: &'a World, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
.expect("You ignored 'can_visit_archetype'!");
@ -100,8 +100,9 @@ impl<T: 'static> AsQuery for &T {
/// A fetcher for mutably borrowing components from archetypes.
pub struct FetchBorrowMut<'a, T> {
col: &'a ComponentColumn,
col: NonNull<ComponentColumn>,
size: usize,
tick: Tick,
_phantom: PhantomData<&'a T>
}
@ -116,7 +117,10 @@ where
}
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
let ptr = self.col.borrow_mut_ptr();
let col = unsafe { self.col.as_mut() };
col.entity_ticks[entity.0 as usize] = self.tick;
let ptr = col.borrow_mut_ptr();
RefMut::map(ptr, |ptr| {
let ptr = NonNull::new_unchecked(ptr.as_ptr()
.add(entity.0 as usize * self.size))
@ -164,6 +168,8 @@ where
type Fetch<'a> = FetchBorrowMut<'a, T>;
const MUTATES: bool = true;
fn new() -> Self {
QueryBorrowMut::<T>::new()
}
@ -172,13 +178,19 @@ where
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
}
unsafe fn fetch<'a>(&self, _world: &'a World, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
.expect("You ignored 'can_visit_archetype'!");
let layout_size = col.info.layout.size;
let mut col = NonNull::from(col);
// TODO: find a way to get the component column mutable with a borrowed archetype so its tick can be updated.
// the fetcher needs to tick the entities tick in the archetype
FetchBorrowMut {
col,
size: col.info.layout.size,
col: NonNull::from(col),
size: layout_size,
tick,
_phantom: PhantomData,
}
}
@ -194,9 +206,9 @@ impl<T: 'static> AsQuery for &mut T {
#[cfg(test)]
mod tests {
use std::{any::TypeId, mem::size_of, marker::PhantomData};
use std::{any::TypeId, mem::size_of, marker::PhantomData, ptr::NonNull};
use crate::{world::{World, Entity, EntityId}, archetype::{Archetype, ArchetypeId}, query::View, tests::Vec2, bundle::Bundle, Fetch, DynTypeId};
use crate::{world::{World, Entity, EntityId}, archetype::{Archetype, ArchetypeId}, query::View, tests::Vec2, bundle::Bundle, Fetch, DynTypeId, Tick};
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
@ -270,13 +282,14 @@ mod tests {
a.add_entity(Entity {
id: EntityId(i),
generation: 0,
}, (Vec2::rand(),));
}, (Vec2::rand(),), &Tick::default());
}
let col = a.columns.iter().find(|c| c.info.type_id == DynTypeId::of::<Vec2>()).unwrap();
let mut bmut = FetchBorrowMut::<Vec2> {
col,
col: NonNull::from(col),
tick: Tick::default(),
size: size_of::<Vec2>(),
_phantom: PhantomData,
};

View File

@ -1,4 +1,4 @@
use crate::{world::{Entity, World}, archetype::{Archetype, ArchetypeId}};
use crate::{world::{Entity, World}, archetype::{Archetype, ArchetypeId}, AsQuery};
use super::{Fetch, Query};
@ -38,10 +38,14 @@ impl Query for Entities {
true
}
unsafe fn fetch<'a>(&self, _world: &'a World, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a> {
let _ = arch_id; // ignore unused warnings
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let _ = tick; // ignore unused warnings
EntitiesFetch {
entities: archetype.entities.keys().cloned().collect::<Vec<Entity>>(),
}
}
}
impl AsQuery for Entities {
type Query = Self;
}

View File

@ -1,4 +1,4 @@
use crate::{archetype::{Archetype, ArchetypeId}, world::{ArchetypeEntityId, World}};
use crate::{archetype::{Archetype, ArchetypeId}, world::{ArchetypeEntityId, World}, Tick};
pub mod view;
pub use view::*;
@ -19,6 +19,10 @@ pub mod resource;
#[allow(unused_imports)]
pub use resource::*;
pub mod tick;
#[allow(unused_imports)]
pub use tick::*;
pub mod dynamic;
/// A [`Fetch`]er implementation gets data out of an archetype.
@ -52,12 +56,15 @@ pub trait Query: Copy {
/// [`View`] uses this to determine if it should continue to iterate this Query.
const ALWAYS_FETCHES: bool = false;
/// A constant that signifies if this Query will mutate data.
const MUTATES: bool = false;
fn new() -> Self;
/// Returns true if the archetype should be visited or skipped.
fn can_visit_archetype(&self, archetype: &Archetype) -> bool;
unsafe fn fetch<'a>(&self, world: &'a World, arch_id: ArchetypeId, archetype: &'a Archetype) -> Self::Fetch<'a>;
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a Archetype, tick: Tick) -> Self::Fetch<'a>;
/// Attempt to fetch only from the world.
///

View File

@ -61,7 +61,7 @@ impl<T: ResourceObject + 'static> Query for QueryResource<T> {
true
}
unsafe fn fetch<'a>(&self, world: &'a World, _arch_id: crate::archetype::ArchetypeId, _archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
unsafe fn fetch<'a>(&self, world: &'a World, _archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
self.fetch_world(world).unwrap()
}
@ -135,7 +135,7 @@ impl<T: ResourceObject + 'static> Query for QueryResourceMut<T> {
true
}
unsafe fn fetch<'a>(&self, world: &'a World, _arch_id: crate::archetype::ArchetypeId, _archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
unsafe fn fetch<'a>(&self, world: &'a World, _archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
self.fetch_world(world).unwrap()
}

105
lyra-ecs/src/query/tick.rs Normal file
View File

@ -0,0 +1,105 @@
use std::marker::PhantomData;
use crate::{ComponentColumn, Fetch, Tick, DynTypeId, Query, world::World, AsQuery};
#[derive(Clone, Copy, Debug, Default)]
pub struct TickOf<T> {
tick: Tick,
_phantom: PhantomData<T>,
}
impl<T> std::ops::Deref for TickOf<T> {
type Target = Tick;
fn deref(&self) -> &Self::Target {
&self.tick
}
}
/// Fetcher for borrowing components from archetypes.
pub struct FetchTickOf<'a, T> {
col: &'a ComponentColumn,
_phantom: PhantomData<&'a T>
}
impl<'a, T> Fetch<'a> for FetchTickOf<'a, T>
where
T: 'a,
{
type Item = Tick;
fn dangling() -> Self {
unreachable!()
}
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
self.col.entity_ticks[entity.0 as usize]
}
}
/// A Query for borrowing components from archetypes.
///
/// Since [`AsQuery`] is implemented for `&T`, you can use this query like this:
/// ```nobuild
/// for ts in world.view::<&T>() {
/// println!("Got a &T!");
/// }
/// ```
pub struct QueryTickOf<T> {
type_id: DynTypeId,
_phantom: PhantomData<T>
}
// manually implemented to avoid a Copy bound on T
impl<T> Copy for QueryTickOf<T> {}
// manually implemented to avoid a Clone bound on T
impl<T> Clone for QueryTickOf<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> QueryTickOf<T> {
pub fn new() -> Self {
Self {
type_id: DynTypeId::of::<T>(),
_phantom: PhantomData,
}
}
}
impl<T> Query for QueryTickOf<T>
where
T: 'static
{
type Item<'a> = Tick;
type Fetch<'a> = FetchTickOf<'a, T>;
fn new() -> Self {
QueryTickOf::<T>::new()
}
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, _world: &'a World, archetype: &'a crate::archetype::Archetype, _tick: crate::Tick) -> Self::Fetch<'a> {
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
.expect("You ignored 'can_visit_archetype'!");
FetchTickOf {
col,
_phantom: PhantomData,
}
}
}
impl<T: 'static> AsQuery for QueryTickOf<T> {
type Query = Self;
}
impl<T: 'static> AsQuery for TickOf<T> {
type Query = QueryTickOf<T>;
}

View File

@ -46,9 +46,9 @@ where
q1.can_visit_archetype(archetype) && q2.can_visit_archetype(archetype)
}
unsafe fn fetch<'a>(&self, world: &'a World, arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let (q1, q2) = self;
( q1.fetch(world, arch_id, archetype), q2.fetch(world, arch_id, archetype) )
( q1.fetch(world, archetype, tick), q2.fetch(world, archetype, tick) )
}
}
@ -101,9 +101,9 @@ macro_rules! impl_bundle_tuple {
bools.iter().all(|b| *b)
}
unsafe fn fetch<'a>(&self, world: &'a World, arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> {
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let ( $($name,)+ ) = self;
( $($name.fetch(world, arch_id, archetype),)+ )
( $($name.fetch(world, archetype, tick),)+ )
}
}

View File

@ -1,6 +1,6 @@
use std::ops::Range;
use crate::{archetype::{Archetype, ArchetypeId}, world::{ArchetypeEntityId, World}, EntityId};
use crate::{archetype::{Archetype, ArchetypeId}, world::{ArchetypeEntityId, World}, EntityId, TickTracker, Tick};
use super::{Query, Fetch};
@ -32,8 +32,11 @@ where
type IntoIter = ViewIter<'a, Q>;
fn into_iter(self) -> Self::IntoIter {
let tick = self.world.tick_tracker().tick_when(Q::MUTATES);
ViewIter {
world: self.world,
tick,
query: self.query,
fetcher: None,
archetypes: self.archetypes,
@ -45,6 +48,7 @@ where
pub struct ViewIter<'a, Q: Query> {
world: &'a World,
tick: Tick,
query: Q,
fetcher: Option<Q::Fetch<'a>>,
archetypes: Vec<&'a Archetype>,
@ -100,7 +104,7 @@ where
continue;
}
self.fetcher = unsafe { Some(self.query.fetch(self.world, ArchetypeId(arch_id as u64), arch)) };
self.fetcher = unsafe { Some(self.query.fetch(self.world, arch, self.tick)) };
self.component_indices = 0..arch.entities.len() as u64;
}
}
@ -109,14 +113,18 @@ where
pub struct ViewOne<'a, Q: Query> {
world: &'a World,
tick: Tick,
entity: EntityId,
query: Q,
}
impl<'a, Q: Query> ViewOne<'a, Q> {
pub fn new(world: &'a World, entity: EntityId, query: Q) -> Self {
let tick = world.tick_tracker().tick_when(Q::MUTATES);
Self {
world,
tick,
entity,
query
}
@ -128,7 +136,7 @@ impl<'a, Q: Query> ViewOne<'a, Q> {
.expect("An invalid record was specified for an entity");
if self.query.can_visit_archetype(arch) {
let mut fetch = unsafe { self.query.fetch(self.world, arch.id, arch) };
let mut fetch = unsafe { self.query.fetch(self.world, arch, self.tick) };
if fetch.can_visit_item(record.index) {
return Some(unsafe { fetch.get_item(record.index) });
}

88
lyra-ecs/src/tick.rs Normal file
View File

@ -0,0 +1,88 @@
use std::sync::atomic::{AtomicU64, Ordering};
/// TickTracker is used for tracking changes of [`Component`]s and entities.
///
/// TickTracker stores an [`AtomicU64`], making all operations on `TickTracker`, atomic as well.
/// Note that [`Tick::Clone`] only clones the inner value of atomic, and not the atomic itself.
#[derive(Debug, Default)]
pub struct TickTracker {
tick: AtomicU64,
}
impl Clone for TickTracker {
fn clone(&self) -> Self {
Self {
tick: AtomicU64::from(self.tick.load(Ordering::Acquire)),
}
}
}
impl From<u64> for TickTracker {
fn from(value: u64) -> Self {
Self {
tick: AtomicU64::from(value),
}
}
}
impl TickTracker {
/// Create a new tracker starting at zero ticks.
pub fn new() -> Self {
Self::default()
}
/// Increment the tick counter, and return the new Tick.
pub fn tick(&self) -> Tick {
let old = self.tick.fetch_add(1, Ordering::Relaxed);
Tick(old + 1)
}
/// If `when` is true, increment the tick counter.
pub fn tick_when(&self, when: bool) -> Tick {
if when {
self.tick()
} else {
self.current()
}
}
/// Sync this TickTracker with the provided `Tick`.
///
/// This will only update the tracker if `Tick` is larger than self.
pub fn tick_to(&self, other: &Tick) -> Tick {
self.tick.fetch_max(other.0, Ordering::AcqRel);
Tick(self.tick.load(Ordering::Acquire))
}
/// Sync this TickTracker with the other tracker.
///
/// This will only update the tracker if other is larger than self.
pub fn sync_tracker(&self, other: &TickTracker) -> Tick {
let other = other.tick.load(Ordering::Acquire);
self.tick_to(&Tick(other))
}
/// Gets the current Tick that the tracker is at.
pub fn current(&self) -> Tick {
Tick(self.tick.load(Ordering::Acquire))
}
}
/// A specific Tick that was resulted from ticking a [`TickTracker`].
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Default, PartialOrd, Ord)]
pub struct Tick(u64);
impl std::ops::Deref for Tick {
type Target = u64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Tick {
/// Increment this Tick up to `other`
pub fn tick_to(&mut self, other: &Tick) {
self.0 = self.0.max(other.0);
}
}

View File

@ -1,6 +1,6 @@
use std::{collections::{HashMap, VecDeque}, any::TypeId, cell::{Ref, RefMut}, ptr::NonNull};
use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, component::Component, query::{ViewIter, View}, resource::ResourceData, Query, AsQuery, dynamic::{DynamicViewIter, QueryDynamicType, DynamicView}, ViewOne, ComponentInfo, DynTypeId};
use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, component::Component, query::{ViewIter, View}, resource::ResourceData, Query, AsQuery, dynamic::{DynamicViewIter, QueryDynamicType, DynamicView}, ViewOne, ComponentInfo, DynTypeId, TickTracker, Tick};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct EntityId(pub u64);
@ -29,6 +29,7 @@ pub struct World {
dead_entities: VecDeque<Entity>,
next_entity_id: EntityId,
resources: HashMap<TypeId, ResourceData>,
tracker: TickTracker,
}
impl Default for World {
@ -40,6 +41,7 @@ impl Default for World {
dead_entities: VecDeque::new(),
next_entity_id: EntityId(0),
resources: HashMap::new(),
tracker: TickTracker::new(),
}
}
}
@ -49,9 +51,13 @@ impl World {
Self::default()
}
/// Gets a new Entity, will recycle dead entities and increment their generation.
fn get_new_entity(&mut self) -> Entity {
match self.dead_entities.pop_front() {
Some(e) => e,
Some(mut e) => {
e.generation += 1;
e
},
None => {
let new_id = self.next_entity_id;
self.next_entity_id.0 += 1;
@ -64,6 +70,7 @@ impl World {
}
}
/// Spawns a new entity and inserts the component `bundle` into it.
pub fn spawn<B>(&mut self, bundle: B) -> Entity
where
B: Bundle
@ -71,13 +78,15 @@ impl World {
let bundle_types = bundle.type_ids();
let new_entity = self.get_new_entity();
let tick = self.tick();
// try to find an archetype
let archetype = self.archetypes
.values_mut()
.find(|a| a.is_archetype_for(&bundle_types));
if let Some(archetype) = archetype {
let arche_idx = archetype.add_entity(new_entity, bundle);
let arche_idx = archetype.add_entity(new_entity, bundle, &tick);
// Create entity record and store it
let record = Record {
@ -92,7 +101,7 @@ impl World {
// create archetype
let new_arch_id = self.next_archetype_id.increment();
let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info());
let entity_arch_id = archetype.add_entity(new_entity, bundle);
let entity_arch_id = archetype.add_entity(new_entity, bundle, &tick);
// store archetype
self.archetypes.insert(new_arch_id, archetype);
@ -112,22 +121,36 @@ impl World {
/// Despawn an entity from the World
pub fn despawn(&mut self, entity: Entity) {
// Tick the tracker if the entity is spawned. This is done here instead of the `if let`
// below due to the borrow checker complaining about multiple mutable borrows to self.
let tick = if self.entity_index.contains_key(&entity.id) {
Some(self.tick())
} else { None };
if let Some(record) = self.entity_index.get_mut(&entity.id) {
let tick = tick.unwrap();
let arch = self.archetypes.get_mut(&record.id).unwrap();
if let Some((moved, new_index)) = arch.remove_entity(entity) {
if let Some((moved, new_index)) = arch.remove_entity(entity, &tick) {
// replace the archetype index of the moved index with its new index.
self.entity_index.get_mut(&moved.id).unwrap().index = new_index;
}
}
}
/// Insert a bundle into an existing entity. If the components are already existing on the
/// entity, they will be updated, else the entity will be moved to a different Archetype
/// that can store the entity. That may involve creating a new Archetype.
pub fn insert<B>(&mut self, entity: Entity, bundle: B)
where
B: Bundle
{
// TODO: If the archetype has a single entity, add a component column for the new component instead of
// moving the entity to a brand new archetype.
// TODO: If the archetype has a single entity, add a component column for the new
// component instead of moving the entity to a brand new archetype.
// TODO: If the entity already has the components in `bundle`, update the values of the
// components with the bundle.
let tick = self.tick();
let record = *self.entity_index.get(&entity.id).unwrap();
let current_arch = self.archetypes.get(&record.id).unwrap();
@ -140,7 +163,7 @@ impl World {
col_infos.extend(bundle.info().into_iter());
let col_ptrs: Vec<(NonNull<u8>, ComponentInfo)> = current_arch.columns.iter().map(|c| unsafe { (NonNull::new_unchecked(c.borrow_ptr().as_ptr()), c.info) }).collect();
if let Some(arch) = self.archetypes.values_mut().find(|a| a.is_archetype_for(&col_types)) {
let res_index = arch.reserve_one(entity);
@ -149,13 +172,13 @@ impl World {
let ptr = NonNull::new_unchecked(col_ptr.as_ptr()
.add(res_index.0 as usize * col_info.layout.size));
let col = arch.get_column_mut(col_type).unwrap();
col.set_at(res_index.0 as _, ptr);
col.set_at(res_index.0 as _, ptr, tick);
}
}
bundle.take(|data, type_id, _size| {
let col = arch.get_column_mut(type_id).unwrap();
unsafe { col.set_at(res_index.0 as _, data); }
unsafe { col.set_at(res_index.0 as _, data, tick); }
col.len += 1;
});
@ -169,7 +192,7 @@ impl World {
} else {
let new_arch_id = self.next_archetype_id.increment();
let mut archetype = Archetype::from_bundle_info(new_arch_id, col_infos);
let entity_arch_id = archetype.add_entity(entity, bundle);
let entity_arch_id = archetype.add_entity(entity, bundle, &tick);
self.archetypes.insert(new_arch_id, archetype);
@ -183,7 +206,7 @@ impl World {
}
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
current_arch.remove_entity(entity);
current_arch.remove_entity(entity, &tick);
}
/// View into the world for a set of entities that satisfy the queries.
@ -253,11 +276,31 @@ impl World {
.map(|r| r.try_get_mut())
.flatten()
}
/// Increments the TickTracker which is used for tracking changes to components.
///
/// Most users wont need to call this manually, its done for you through queries and views.
pub fn tick(&self) -> Tick {
self.tracker.tick()
}
/// Gets the current tick that the world is at.
///
/// See [`TickTracker`]
pub fn current_tick(&self) -> Tick {
self.tracker.current()
}
pub fn tick_tracker(&self) -> &TickTracker {
&self.tracker
}
}
#[cfg(test)]
mod tests {
use crate::tests::{Vec2, Vec3};
use std::ops::Deref;
use crate::{tests::{Vec2, Vec3}, TickOf};
use super::World;
@ -390,4 +433,32 @@ mod tests {
let view = world.view_one::<&Vec2>(e);
assert_eq!(*view.get().unwrap(), v);
}
#[test]
fn view_change_tracking() {
let mut world = World::new();
/* let v = Vec2::rand();
world.spawn((v,));
let v = Vec2::rand();
world.spawn((v,)); */
println!("spawning");
world.spawn((Vec2::new(10.0, 10.0),));
world.spawn((Vec2::new(5.0, 5.0),));
println!("spawned");
for mut v in world.view_iter::<&mut Vec2>() {
v.y += 50.0;
println!("Moved v to {:?}", v);
}
let world_tick = world.current_tick();
println!("The world tick is {}", *world_tick);
for (v, tick) in world.view_iter::<(&Vec2, TickOf<Vec2>)>() {
println!("Is at {:?}, it was changed at {}", v, *tick);
assert!(v.y > 50.0);
assert!(tick >= world_tick);
}
}
}

View File

@ -1,4 +1,4 @@
use edict::Component;
use lyra_ecs::Component;
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};

View File

@ -1,7 +1,7 @@
use std::borrow::BorrowMut;
use edict::{Component, World};
use instant::Instant;
use lyra_ecs::{Component, world::World};
use crate::plugin::Plugin;
@ -24,12 +24,10 @@ impl std::ops::DerefMut for DeltaTime {
fn delta_time_system(world: &mut World) -> anyhow::Result<()> {
let now = Instant::now();
let mut delta = world.get_resource_mut::<DeltaTime>().unwrap();
let mut delta = world.get_resource_mut::<DeltaTime>();
delta.0 = delta.1.unwrap_or(now).elapsed().as_secs_f32();
delta.1 = Some(now);
//println!("delta: {}", delta.0);
Ok(())
}
@ -37,7 +35,7 @@ pub struct DeltaTimePlugin;
impl Plugin for DeltaTimePlugin {
fn setup(&self, game: &mut crate::game::Game) {
game.world().insert_resource(DeltaTime(0.0, None));
game.world().add_resource(DeltaTime(0.0, None));
game.with_system("delta_time", delta_time_system, &[]);
}
}

View File

@ -1,4 +1,4 @@
use edict::Component;
use lyra_ecs::Component;
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};

View File

@ -1,5 +1,4 @@
use edict::Component;
use lyra_ecs::Component;
use lyra_resource::Mesh;
#[derive(Clone, Component)]

View File

@ -1,8 +1,9 @@
use lyra_ecs::Component;
use lyra_resource::ResHandle;
use crate::assets::Model;
#[derive(Clone, edict::Component)]
#[derive(Clone, Component)]
pub struct ModelComponent(pub ResHandle<Model>);
impl From<ResHandle<Model>> for ModelComponent {

View File

@ -1,4 +1,4 @@
use edict::Component;
use lyra_ecs::Component;
use crate::math::Transform;

View File

@ -78,6 +78,6 @@ pub struct EventsPlugin;
impl Plugin for EventsPlugin {
fn setup(&self, game: &mut crate::game::Game) {
game.world().insert_resource(EventQueue::new());
game.world().add_resource(EventQueue::new());
}
}

View File

@ -1,4 +1,4 @@
pub use edict::*;
pub use lyra_ecs;
pub mod components;

View File

@ -1,5 +1,6 @@
use lyra_ecs::world::World;
use super::{SimpleSystem, Criteria};
use edict::World;
/// A system that executes a batch of systems in order that they were given.
/// You can optionally add criteria that must pass before the systems are

View File

@ -1,3 +1,5 @@
use lyra_ecs::world::World;
pub enum CriteriaSchedule {
Yes,
No,
@ -11,13 +13,13 @@ pub trait Criteria {
/// Parameters:
/// * `world` - The ecs world.
/// * `check_count` - The amount of times the Criteria has been checked this tick.
fn can_run(&mut self, world: &mut edict::World, check_count: u32) -> CriteriaSchedule;
fn can_run(&mut self, world: &mut World, check_count: u32) -> CriteriaSchedule;
}
impl<F> Criteria for F
where F: FnMut(&mut edict::World, u32) -> CriteriaSchedule
where F: FnMut(&mut World, u32) -> CriteriaSchedule
{
fn can_run(&mut self, world: &mut edict::World, check_count: u32) -> CriteriaSchedule {
fn can_run(&mut self, world: &mut World, check_count: u32) -> CriteriaSchedule {
self(world, check_count)
}
}

View File

@ -3,9 +3,9 @@ pub use batched::*;
pub mod criteria;
pub use criteria::*;
use lyra_ecs::world::World;
use std::collections::HashMap;
use edict::World;
use petgraph::{stable_graph::{StableDiGraph, NodeIndex}, visit::Topo};
use tracing::warn;
@ -20,7 +20,7 @@ pub trait SimpleSystem {
}
impl<S> SimpleSystem for S
where S: FnMut(&mut edict::World) -> anyhow::Result<()>
where S: FnMut(&mut World) -> anyhow::Result<()>
{
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
self(world)

View File

@ -2,6 +2,7 @@ use std::{sync::Arc, collections::VecDeque};
use async_std::task::block_on;
use lyra_ecs::world::World;
use tracing::{info, error, Level, debug};
use tracing_appender::non_blocking;
use tracing_subscriber::{
@ -15,7 +16,7 @@ use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, Keyboar
use crate::{render::{renderer::{Renderer, BasicRenderer}, window::WindowOptions}, input::InputEvent, ecs::{SimpleSystem, SystemDispatcher, EventQueue, Events}, plugin::Plugin, change_tracker::Ct};
pub struct Controls<'a> {
pub world: &'a mut edict::World,
pub world: &'a mut World,
}
#[derive(Clone, Default)]
@ -34,14 +35,14 @@ struct GameLoop {
window: Arc<Window>,
renderer: Box<dyn Renderer>,
world: edict::World,
world: World,
/// higher priority systems
engine_sys_dispatcher: SystemDispatcher,
user_sys_dispatcher: SystemDispatcher,
}
impl GameLoop {
pub async fn new(window: Arc<Window>, world: edict::World, user_systems: SystemDispatcher) -> GameLoop {
pub async fn new(window: Arc<Window>, world: World, user_systems: SystemDispatcher) -> GameLoop {
Self {
window: Arc::clone(&window),
renderer: Box::new(BasicRenderer::create_with_window(window).await),
@ -58,7 +59,7 @@ impl GameLoop {
pub async fn on_init(&mut self) {
// Create the EventQueue resource in the world
self.world.insert_resource(self.window.clone());
self.world.add_resource(self.window.clone());
}
pub fn run_sync(&mut self, event: Event<()>, control_flow: &mut ControlFlow) {
@ -113,11 +114,11 @@ impl GameLoop {
/* let trigger = matches!(self.world.get_resource::<WindowState>(), Some(window_state)
if window_state.is_focused && window_state.is_cursor_inside_window); */
let trigger = matches!(self.world.get_resource::<Ct<WindowOptions>>(), Some(window)
let trigger = matches!(self.world.try_get_resource::<Ct<WindowOptions>>(), Some(window)
if window.focused && window.cursor_inside_window);
if trigger {
let mut event_queue = self.world.get_resource_mut::<EventQueue>().unwrap();
let mut event_queue = self.world.try_get_resource_mut::<EventQueue>().unwrap();
let input_event = InputEvent::MouseMotion { device_id, delta, };
event_queue.trigger_event(input_event);
@ -134,7 +135,7 @@ impl GameLoop {
// Its possible to receive multiple input events before the update event for
// the InputSystem is called, so we must use a queue for the events.
{
if let Some(mut event_queue) = self.world.get_resource_mut::<EventQueue>() {
if let Some(mut event_queue) = self.world.try_get_resource_mut::<EventQueue>() {
event_queue.trigger_event(input_event.clone());
};
}
@ -159,7 +160,7 @@ impl GameLoop {
},
WindowEvent::Focused(is_focused) => {
let state = self.world.with_resource(WindowState::new);
let mut state = self.world.get_resource_or_else(WindowState::new);
state.is_focused = *is_focused;
},
@ -178,7 +179,7 @@ impl GameLoop {
} */
self.renderer.as_mut().prepare(&mut self.world);
if let Some(mut event_queue) = self.world.get_resource_mut::<EventQueue>() {
if let Some(mut event_queue) = self.world.try_get_resource_mut::<EventQueue>() {
event_queue.update_events();
}
@ -202,7 +203,7 @@ impl GameLoop {
}
pub struct Game {
world: Option<edict::World>,
world: Option<World>,
plugins: VecDeque<Box<dyn Plugin>>,
system_dispatcher: Option<SystemDispatcher>,
startup_systems: VecDeque<Box<dyn SimpleSystem>>,
@ -211,7 +212,7 @@ pub struct Game {
impl Default for Game {
fn default() -> Self {
Self {
world: Some(edict::World::new()),
world: Some(World::new()),
plugins: VecDeque::new(),
system_dispatcher: Some(SystemDispatcher::new()),
startup_systems: VecDeque::new(),
@ -225,7 +226,7 @@ impl Game {
}
/// Get the world of this game
pub fn world(&mut self) -> &mut edict::World {
pub fn world(&mut self) -> &mut World {
// world is always `Some`, so unwrapping is safe
self.world.as_mut().unwrap()
}
@ -265,7 +266,7 @@ impl Game {
/// Override the default (empty) world
///
/// This isn't recommended, you should create a startup system and add it to `with_startup_system`
pub fn with_world(&mut self, world: edict::World) -> &mut Self {
pub fn with_world(&mut self, world: World) -> &mut Self {
self.world = Some(world);
self

View File

@ -1,6 +1,6 @@
use std::{collections::HashMap, cell::RefCell, borrow::BorrowMut, ops::Deref};
use edict::{action, World};
use lyra_ecs::world::World;
use crate::{castable_any::CastableAny, plugin::Plugin};
@ -369,11 +369,11 @@ impl ActionHandler {
}
fn actions_system(world: &mut World) -> anyhow::Result<()> {
let keys = world.get_resource::<InputButtons<KeyCode>>()
let keys = world.try_get_resource::<InputButtons<KeyCode>>()
.map(|r| r.deref().clone());
if let Some(keys) = keys {
let mut handler = world.get_resource_mut::<ActionHandler>()
let mut handler = world.try_get_resource_mut::<ActionHandler>()
.expect("No Input Action handler was created in the world!");
//handler

View File

@ -1,4 +1,5 @@
use glam::Vec2;
use lyra_ecs::world::World;
use winit::event::MouseScrollDelta;
use crate::{ecs::{SimpleSystem, EventQueue}, plugin::Plugin,};
@ -11,8 +12,8 @@ pub type KeyCode = winit::event::VirtualKeyCode;
pub struct InputSystem;
impl InputSystem {
pub fn process_event(&mut self, world: &mut edict::World, event: &InputEvent) -> bool {
let event_queue = world.get_resource_mut::<EventQueue>();
pub fn process_event(&mut self, world: &mut World, event: &InputEvent) -> bool {
let event_queue = world.try_get_resource_mut::<EventQueue>();
if event_queue.is_none() {
return false;
}
@ -22,7 +23,7 @@ impl InputSystem {
InputEvent::KeyboardInput { input, .. } => {
if let Some(code) = input.virtual_keycode {
drop(event_queue);
let e = world.with_resource(InputButtons::<winit::event::VirtualKeyCode>::new);
let mut e = world.get_resource_or_else(InputButtons::<winit::event::VirtualKeyCode>::new);
//let mut e = with_resource_mut(world, || InputButtons::<KeyCode>::new());
e.add_input_from_winit(code, input.state);
}
@ -72,7 +73,7 @@ impl InputSystem {
event_queue.trigger_event(button_event);
drop(event_queue);
let e = world.with_resource(InputButtons::<MouseButton>::new);
let mut e = world.get_resource_or_else(InputButtons::<MouseButton>::new);
e.add_input_from_winit(button_event, *state);
},
InputEvent::Touch(t) => {
@ -85,7 +86,7 @@ impl InputSystem {
finger_id: t.id,
};
let touches = world.with_resource(Touches::new);
let mut touches = world.get_resource_or_else(Touches::new);
touches.touches.push(touch);
},
_ => {},
@ -96,8 +97,8 @@ impl InputSystem {
}
impl SimpleSystem for InputSystem {
fn execute_mut(&mut self, world: &mut edict::World) -> anyhow::Result<()> {
let queue = world.get_resource_mut::<EventQueue>()
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
let queue = world.try_get_resource_mut::<EventQueue>()
.and_then(|q| q.read_events::<InputEvent>());
if queue.is_none() {

View File

@ -98,7 +98,7 @@ pub struct ResourceManagerPlugin;
impl Plugin for ResourceManagerPlugin {
fn setup(&self, game: &mut Game) {
game.world().insert_resource(ResourceManager::new());
game.world().add_resource(ResourceManager::new());
}
}

View File

@ -1,4 +1,6 @@
#[derive(Default, Debug, Clone, edict::Component)]
use lyra_ecs::Component;
#[derive(Default, Debug, Clone, Component)]
pub struct DirectionalLight {
//pub direction: glam::Quat,
pub color: glam::Vec3,

View File

@ -2,13 +2,13 @@ pub mod point;
pub mod directional;
pub mod spotlight;
use lyra_ecs::{Entity, Tick, world::World, Entities, TickOf};
pub use point::*;
pub use directional::*;
pub use spotlight::*;
use std::{collections::{VecDeque, HashMap}, num::{NonZeroU64, NonZeroU32}, marker::PhantomData};
use edict::query::EpochOf;
pub use point::*;
use tracing::{debug, Instrument};
use wgpu::util::DeviceExt;
@ -33,7 +33,7 @@ pub struct LightBuffer<U: Default + bytemuck::Pod + bytemuck::Zeroable> {
/// to recreate the array and remove the gaps.
pub buffer_count: usize,
/// The buffer index for a specific entity/caster.
used_indexes: HashMap<edict::EntityId, usize>,
used_indexes: HashMap<Entity, usize>,
/// Indexes that were being used but are no longer needed.
dead_indexes: VecDeque<usize>,
}
@ -49,12 +49,12 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
}
}
pub fn has_light(&self, entity: edict::EntityId) -> bool {
pub fn has_light(&self, entity: Entity) -> bool {
self.used_indexes.contains_key(&entity)
}
/// Update an existing light in the light buffer.
pub fn update_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) {
pub fn update_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: Entity, light: U) {
let buffer_idx = *self.used_indexes.get(&entity)
.expect("Entity for Light is not in buffer!");
@ -62,7 +62,7 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
}
/// Add a new light to the light buffer.
pub fn add_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) {
pub fn add_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: Entity, light: U) {
let buffer_idx = match self.dead_indexes.pop_front() {
Some(i) => i,
None => {
@ -82,7 +82,7 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
}
/// Update, or add a new caster, to the light buffer.
pub fn update_or_add(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) {
pub fn update_or_add(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: Entity, light: U) {
if self.used_indexes.contains_key(&entity) {
self.update_light(lights_buffer, entity, light);
} else {
@ -91,7 +91,7 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
}
/// Remove a caster from the buffer, returns true if it was removed.
pub fn remove_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId) -> bool {
pub fn remove_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: Entity) -> bool {
if let Some(removed_idx) = self.used_indexes.remove(&entity) {
self.dead_indexes.push_back(removed_idx);
//self.current_count -= 1;
@ -170,31 +170,31 @@ impl LightUniformBuffers {
}
}
pub fn update_lights(&mut self, queue: &wgpu::Queue, world_epoch: edict::epoch::EpochId, world: &edict::World) {
pub fn update_lights(&mut self, queue: &wgpu::Queue, world_tick: Tick, world: &World) {
for (entity, point_light, transform, light_epoch, transform_epoch)
in world.query::<(edict::Entities, &PointLight, &TransformComponent, EpochOf<PointLight>, EpochOf<TransformComponent>)>().iter() {
in world.view_iter::<(Entities, &PointLight, &TransformComponent, TickOf<PointLight>, TickOf<TransformComponent>)>() {
if !self.point_lights.has_light(entity) || light_epoch == world_epoch || transform_epoch == world_epoch {
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
if !self.point_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick {
let uniform = PointLightUniform::from_bundle(&point_light, &transform.transform);
self.point_lights.update_or_add(&mut self.lights_uniform.point_lights, entity, uniform);
debug!("Updated point light");
}
}
for (entity, spot_light, transform, light_epoch, transform_epoch)
in world.query::<(edict::Entities, &SpotLight, &TransformComponent, EpochOf<SpotLight>, EpochOf<TransformComponent>)>().iter() {
in world.view_iter::<(Entities, &SpotLight, &TransformComponent, TickOf<SpotLight>, TickOf<TransformComponent>)>() {
if !self.spot_lights.has_light(entity) || light_epoch == world_epoch || transform_epoch == world_epoch {
let uniform = SpotLightUniform::from_bundle(spot_light, &transform.transform);
if !self.spot_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick {
let uniform = SpotLightUniform::from_bundle(&spot_light, &transform.transform);
self.spot_lights.update_or_add(&mut self.lights_uniform.spot_lights, entity, uniform);
//debug!("Updated spot light");
}
}
if let Some((dir_light, transform)) =
world.query::<(&DirectionalLight, &TransformComponent)>().iter().next() {
world.view_iter::<(&DirectionalLight, &TransformComponent)>().next() {
let uniform = DirectionalLightUniform::from_bundle(dir_light, &transform.transform);
let uniform = DirectionalLightUniform::from_bundle(&dir_light, &transform.transform);
self.lights_uniform.directional_light = uniform;
}

View File

@ -1,4 +1,6 @@
#[derive(Default, Debug, Clone, edict::Component)]
use lyra_ecs::Component;
#[derive(Default, Debug, Clone, Component)]
pub struct PointLight {
pub color: glam::Vec3,
pub intensity: f32,

View File

@ -1,6 +1,8 @@
use lyra_ecs::Component;
use crate::math;
#[derive(Debug, Clone, edict::Component)]
#[derive(Debug, Clone, Component)]
pub struct SpotLight {
pub color: glam::Vec3,
pub cutoff: math::Angle,

View File

@ -1,18 +1,19 @@
use edict::EntityId;
use lyra_ecs::Entity;
use crate::math::Transform;
use super::material::Material;
pub struct RenderJob {
pub entity: EntityId,
pub entity: Entity,
pub shader_id: u64,
pub mesh_buffer_id: uuid::Uuid,
pub transform: Transform,
}
impl RenderJob {
pub fn new(entity: EntityId, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform) -> Self {
pub fn new(entity: Entity, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform) -> Self {
Self {
entity,
shader_id,

View File

@ -4,11 +4,11 @@ use std::num::NonZeroU64;
use std::sync::Arc;
use std::borrow::Cow;
use edict::query::EpochOf;
use edict::{EntityId, Entities};
use glam::Vec3;
use instant::Instant;
use itertools::izip;
use lyra_ecs::{Entity, Entities, TickOf};
use lyra_ecs::world::World;
use tracing::{debug, warn};
use wgpu::{BindGroup, BindGroupLayout, Limits};
use wgpu::util::DeviceExt;
@ -35,7 +35,7 @@ use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, r
use lyra_resource::Mesh;
pub trait Renderer {
fn prepare(&mut self, main_world: &mut edict::World);
fn prepare(&mut self, main_world: &mut World);
fn render(&mut self) -> Result<(), wgpu::SurfaceError>;
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
@ -79,8 +79,8 @@ pub struct BasicRenderer {
pub render_jobs: VecDeque<RenderJob>,
mesh_buffers: HashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
entity_meshes: HashMap<EntityId, uuid::Uuid>,
entity_last_transforms: HashMap<EntityId, CachedTransform>,
entity_meshes: HashMap<Entity, uuid::Uuid>,
entity_last_transforms: HashMap<Entity, CachedTransform>,
transform_buffers: TransformBuffers,
@ -269,7 +269,7 @@ impl BasicRenderer {
s
}
fn update_mesh_buffers(&mut self, _entity: EntityId, mesh: &Mesh) {
fn update_mesh_buffers(&mut self, _entity: Entity, mesh: &Mesh) {
if let Some(buffers) = self.mesh_buffers.get_mut(&mesh.uuid) {
// check if the buffer sizes dont match. If they dont, completely remake the buffers
let vertices = mesh.position().unwrap();
@ -372,7 +372,7 @@ impl BasicRenderer {
}
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
fn process_mesh(&mut self, entity: EntityId, transform: Transform, mesh: &Mesh) -> bool {
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh) -> bool {
if self.transform_buffers.should_expand() {
self.transform_buffers.expand_buffers(&self.device);
}
@ -393,14 +393,14 @@ impl BasicRenderer {
}
impl Renderer for BasicRenderer {
fn prepare(&mut self, main_world: &mut edict::World) {
let last_epoch = main_world.epoch();
fn prepare(&mut self, main_world: &mut World) {
let last_epoch = main_world.current_tick();
let mut alive_entities = HashSet::new();
let now_inst = Instant::now();
for (entity, model, model_epoch, transform, transform_epoch) in main_world.query::<(Entities, &ModelComponent, EpochOf<ModelComponent>, &TransformComponent, EpochOf<TransformComponent>)>().iter() {
for (entity, model, model_epoch, transform, transform_epoch) in main_world.view_iter::<(Entities, &ModelComponent, TickOf<ModelComponent>, &TransformComponent, TickOf<TransformComponent>)>() {
alive_entities.insert(entity);
let cached = match self.entity_last_transforms.get_mut(&entity) {
@ -464,8 +464,8 @@ impl Renderer for BasicRenderer {
self.mesh_buffers.retain(|u, _| !removed_entities.contains(u));
}
if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
let view_proj = self.inuse_camera.update_view_projection(camera);
if let Some(camera) = main_world.view_iter::<&mut CameraComponent>().next() {
let view_proj = self.inuse_camera.update_view_projection(&camera);
let pos = camera.transform.translation;
let uniform = CameraUniform {
view_proj: *view_proj,

View File

@ -1,6 +1,6 @@
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
use edict::EntityId;
use lyra_ecs::Entity;
use tracing::debug;
use wgpu::Limits;
@ -23,8 +23,8 @@ pub(crate) struct TransformBufferEntry {
pub(crate) struct TransformBuffers {
pub bindgroup_layout: wgpu::BindGroupLayout,
/// A vector storing the EntityId and
pub just_updated: HashMap<EntityId, TransformBufferIndices>,
pub not_updated: HashMap<EntityId, TransformBufferIndices>,
pub just_updated: HashMap<Entity, TransformBufferIndices>,
pub not_updated: HashMap<Entity, TransformBufferIndices>,
pub dead_indices: VecDeque<TransformBufferIndices>,
pub next_indices: TransformBufferIndices,
/// (transform count, buffer, bindgroup)
@ -80,7 +80,7 @@ impl TransformBuffers {
}
/// Update an entity's buffer with the new transform. Will panic if the entity isn't stored
pub fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices {
pub fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: Entity, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices {
let indices = self.not_updated.remove(&entity)
.or_else(|| self.just_updated.remove(&entity))
.expect("Use 'insert_entity' for new entities");
@ -96,7 +96,7 @@ impl TransformBuffers {
}
/// Insert a new entity into the buffer, returns where it was stored.
pub fn insert_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices {
pub fn insert_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: Entity, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformBufferIndices {
let indices = match self.dead_indices.pop_front() {
Some(indices) => indices,
None => {
@ -119,7 +119,7 @@ impl TransformBuffers {
}
/// Update or insert an entities transform
pub fn update_or_insert<TFn>(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform_fn: TFn) -> TransformBufferIndices
pub fn update_or_insert<TFn>(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: Entity, transform_fn: TFn) -> TransformBufferIndices
where TFn: Fn() -> (glam::Mat4, glam::Mat3)
{
let (tran, norm) = transform_fn();
@ -131,7 +131,7 @@ impl TransformBuffers {
}
/// Returns true if the entity's transform is stored (does not mean its up-to-date).
pub fn contains(&self, entity: EntityId) -> bool {
pub fn contains(&self, entity: Entity) -> bool {
self.not_updated.contains_key(&entity) || self.just_updated.contains_key(&entity)
}
@ -165,7 +165,7 @@ impl TransformBuffers {
.map(|entry| &entry.bindgroup)
}
pub fn entity_bind_group(&self, entity: EntityId) -> Option<&wgpu::BindGroup> {
pub fn entity_bind_group(&self, entity: Entity) -> Option<&wgpu::BindGroup> {
self.entity_indices(entity).and_then(|i| self.bind_group(*i))
}
@ -233,7 +233,7 @@ impl TransformBuffers {
self.buffer_bindgroups.push(entry);
}
pub fn entity_indices(&self, entity: EntityId) -> Option<&TransformBufferIndices> {
pub fn entity_indices(&self, entity: Entity) -> Option<&TransformBufferIndices> {
self.just_updated.get(&entity).or_else(|| self.not_updated.get(&entity))
}
}

View File

@ -1,6 +1,7 @@
use std::{sync::Arc, collections::VecDeque};
use glam::{Vec2, IVec2};
use lyra_ecs::world::World;
use tracing::{warn, error};
use winit::{window::{Window, Fullscreen}, dpi::{LogicalPosition, LogicalSize, PhysicalPosition}, error::ExternalError};
@ -274,14 +275,14 @@ fn center_mouse(window: &Window, options: &WindowOptions) {
}
}
fn window_updater_system(world: &mut edict::World) -> anyhow::Result<()> {
if let (Some(window), Some(opts)) = (world.get_resource::<Arc<Window>>(), world.get_resource::<Ct<WindowOptions>>()) {
fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
if let (Some(window), Some(opts)) = (world.try_get_resource::<Arc<Window>>(), world.try_get_resource::<Ct<WindowOptions>>()) {
// if the options changed, update the window
if opts.peek_changed() {
drop(opts); // drop the Ref, we're about to get a RefMut
// now we can get it mutable, this will trigger the ChangeTracker, so it will be reset at the end of this scope.
let mut opts = world.get_resource_mut::<Ct<WindowOptions>>().unwrap();
let mut opts = world.get_resource_mut::<Ct<WindowOptions>>();
if opts.focused {
window.focus_window();
@ -337,9 +338,9 @@ fn window_updater_system(world: &mut edict::World) -> anyhow::Result<()> {
center_mouse(&window, &opts);
} else {
drop(opts); // drop the Ref, we're about to get a RefMut
let mut opts = world.get_resource_mut::<Ct<WindowOptions>>().unwrap();
let mut opts = world.get_resource_mut::<Ct<WindowOptions>>();
if let Some(event_queue) = world.get_resource_mut::<EventQueue>() {
if let Some(event_queue) = world.try_get_resource_mut::<EventQueue>() {
if let Some(mut events) = event_queue.read_events::<InputEvent>() {
while let Some(ev) = events.pop_front() {
match ev {
@ -372,7 +373,7 @@ impl Plugin for WindowPlugin {
fn setup(&self, game: &mut crate::game::Game) {
let window_options = WindowOptions::default();
game.world().insert_resource(Ct::new(window_options));
game.world().add_resource(Ct::new(window_options));
game.with_system("window_updater", window_updater_system, &[]);
}
}