Switch to use lyra-ecs
This commit is contained in:
parent
64519b2b4f
commit
2805399fe4
|
@ -40,6 +40,28 @@
|
||||||
},
|
},
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}"
|
"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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -261,7 +261,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -399,7 +399,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -631,7 +631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c94d80dc0f05250a9082bb9455bbf3d6c6c51db388b060df914aebcfb4a9b9f1"
|
checksum = "c94d80dc0f05250a9082bb9455bbf3d6c6c51db388b060df914aebcfb4a9b9f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"edict-proc-lib",
|
"edict-proc-lib",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -643,7 +643,7 @@ dependencies = [
|
||||||
"proc-easy",
|
"proc-easy",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -931,7 +931,7 @@ dependencies = [
|
||||||
"inflections",
|
"inflections",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1283,6 +1283,25 @@ dependencies = [
|
||||||
"value-bag",
|
"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]]
|
[[package]]
|
||||||
name = "lyra-engine"
|
name = "lyra-engine"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
@ -1294,17 +1313,17 @@ dependencies = [
|
||||||
"atomicell",
|
"atomicell",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"edict",
|
|
||||||
"gilrs-core",
|
"gilrs-core",
|
||||||
"glam",
|
"glam",
|
||||||
"image",
|
"image",
|
||||||
"instant",
|
"instant",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"lyra-ecs",
|
||||||
"lyra-resource",
|
"lyra-resource",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"quote",
|
"quote",
|
||||||
"stopwatch",
|
"stopwatch",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
"tobj",
|
"tobj",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
|
@ -1685,7 +1704,7 @@ dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1834,7 +1853,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1898,7 +1917,7 @@ checksum = "ea59c637cd0e6b71ae18e589854e9de9b7cb17fefdbf2047e42bd38e24285b19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1913,9 +1932,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.64"
|
version = "1.0.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
|
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1937,9 +1956,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.29"
|
version = "1.0.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -2137,7 +2156,7 @@ checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2291,9 +2310,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.26"
|
version = "2.0.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
|
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2315,30 +2334,30 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-std",
|
"async-std",
|
||||||
"edict",
|
|
||||||
"fps_counter",
|
"fps_counter",
|
||||||
|
"lyra-ecs",
|
||||||
"lyra-engine",
|
"lyra-engine",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.48"
|
version = "1.0.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
|
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.48"
|
version = "1.0.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
|
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2478,7 +2497,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2613,7 +2632,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2647,7 +2666,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.26",
|
"syn 2.0.42",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,11 +6,13 @@ edition = "2021"
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"lyra-resource",
|
"lyra-resource",
|
||||||
|
"lyra-ecs",
|
||||||
"examples/testbed"
|
"examples/testbed"
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lyra-resource = { path = "lyra-resource", version = "0.0.1" }
|
lyra-resource = { path = "lyra-resource", version = "0.0.1" }
|
||||||
|
lyra-ecs = { path = "lyra-ecs" }
|
||||||
|
|
||||||
winit = "0.28.1"
|
winit = "0.28.1"
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
|
@ -31,7 +33,7 @@ glam = { version = "0.24.0", features = ["bytemuck", "debug-glam-assert"] }
|
||||||
gilrs-core = "0.5.6"
|
gilrs-core = "0.5.6"
|
||||||
syn = "2.0.26"
|
syn = "2.0.26"
|
||||||
quote = "1.0.29"
|
quote = "1.0.29"
|
||||||
edict = "0.5.0"
|
#edict = "0.5.0"
|
||||||
atomicell = "0.1.9"
|
atomicell = "0.1.9"
|
||||||
aligned-vec = "0.5.0"
|
aligned-vec = "0.5.0"
|
||||||
tracing-appender = "0.2.2"
|
tracing-appender = "0.2.2"
|
||||||
|
|
|
@ -7,8 +7,8 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lyra-engine = { path = "../../", version = "0.0.1" }
|
lyra-engine = { path = "../../", version = "0.0.1" }
|
||||||
|
lyra-ecs = { path = "../../lyra-ecs"}
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
async-std = "1.12.0"
|
async-std = "1.12.0"
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
fps_counter = "2.0.0"
|
fps_counter = "2.0.0"
|
||||||
edict = "0.5.0"
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use edict::{Component, World};
|
use lyra_ecs::{Component, world::World};
|
||||||
use lyra_engine::{
|
use lyra_engine::{
|
||||||
ecs::{components::{camera::CameraComponent, DeltaTime}, EventQueue, SimpleSystem},
|
ecs::{components::{camera::CameraComponent, DeltaTime}, EventQueue, SimpleSystem},
|
||||||
game::Game,
|
game::Game,
|
||||||
|
@ -48,14 +48,14 @@ impl SimpleSystem for FreeFlyCameraPlugin {
|
||||||
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||||
let mut camera_rot = Vec3::default();
|
let mut camera_rot = Vec3::default();
|
||||||
|
|
||||||
let delta_time = **world.get_resource::<DeltaTime>().unwrap();
|
let delta_time = **world.get_resource::<DeltaTime>();
|
||||||
|
|
||||||
let events = world
|
let events = world
|
||||||
.get_resource_mut::<EventQueue>()
|
.try_get_resource_mut::<EventQueue>()
|
||||||
.and_then(|q| q.read_events::<MouseMotion>());
|
.and_then(|q| q.read_events::<MouseMotion>());
|
||||||
|
|
||||||
let keys = world
|
let keys = world
|
||||||
.get_resource::<InputButtons<KeyCode>>()
|
.try_get_resource::<InputButtons<KeyCode>>()
|
||||||
.map(|r| r.deref().clone());
|
.map(|r| r.deref().clone());
|
||||||
|
|
||||||
if let Some(keys) = keys.as_ref() {
|
if let Some(keys) = keys.as_ref() {
|
||||||
|
@ -84,9 +84,8 @@ impl SimpleSystem for FreeFlyCameraPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (cam, fly) in world
|
for (mut cam, mut fly) in world
|
||||||
.query_mut::<(&mut CameraComponent, &mut FreeFlyCamera)>()
|
.view_iter::<(&mut CameraComponent, &mut FreeFlyCamera)>()
|
||||||
.iter_mut()
|
|
||||||
{
|
{
|
||||||
let forward = cam.transform.forward();
|
let forward = cam.transform.forward();
|
||||||
let left = cam.transform.left();
|
let left = cam.transform.left();
|
||||||
|
|
|
@ -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};
|
use lyra_engine::assets::{ResourceManager, Model};
|
||||||
|
|
||||||
mod free_fly_camera;
|
mod free_fly_camera;
|
||||||
|
@ -39,9 +39,9 @@ impl FixedTimestep {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Criteria for 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 {
|
if check_count == 0 {
|
||||||
let delta_time = world.get_resource::<DeltaTime>().unwrap();
|
let delta_time = world.get_resource::<DeltaTime>();
|
||||||
self.accumulator += **delta_time;
|
self.accumulator += **delta_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,12 +64,12 @@ struct CubeFlag;
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let setup_sys = |world: &mut World| -> anyhow::Result<()> {
|
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_grab = CursorGrabMode::Confined;
|
||||||
window_options.cursor_visible = false;
|
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 diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
|
||||||
let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
|
let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
|
||||||
//let cube_model = resman.request::<Model>("assets/cube-texture-bin.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 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();
|
let fps = counter.tick();
|
||||||
|
|
||||||
|
@ -193,32 +193,32 @@ async fn main() {
|
||||||
};
|
};
|
||||||
let fps_plugin = move |game: &mut Game| {
|
let fps_plugin = move |game: &mut Game| {
|
||||||
let world = game.world();
|
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<()> {
|
let spin_system = |world: &mut World| -> anyhow::Result<()> {
|
||||||
const SPEED: f32 = 4.0;
|
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;
|
let t = &mut transform.transform;
|
||||||
t.rotate_y(math::Angle::Degrees(SPEED * delta_time));
|
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;
|
let t = &mut transform.transform;
|
||||||
t.rotate_x(math::Angle::Degrees(SPEED * delta_time));
|
t.rotate_x(math::Angle::Degrees(SPEED * delta_time));
|
||||||
} */
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
let jiggle_plugin = move |game: &mut Game| {
|
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();
|
let mut sys = BatchedSystem::new();
|
||||||
sys.with_criteria(FixedTimestep::new(45));
|
sys.with_criteria(FixedTimestep::new(45));
|
||||||
//sys.with_system(spin_system);
|
sys.with_system(spin_system);
|
||||||
//sys.with_system(fps_system);
|
//sys.with_system(fps_system);
|
||||||
|
|
||||||
game.with_system("fixed", sys, &[]);
|
game.with_system("fixed", sys, &[]);
|
||||||
|
@ -243,7 +243,7 @@ async fn main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
let test_system = |world: &mut World| -> anyhow::Result<()> {
|
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") {
|
if let Some(alpha) = handler.get_pressed_modifier("forward_backward") {
|
||||||
debug!("'forward_backward': {alpha}");
|
debug!("'forward_backward': {alpha}");
|
||||||
|
@ -256,7 +256,7 @@ async fn main() {
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
game.world().insert_resource(action_handler);
|
game.world().add_resource(action_handler);
|
||||||
game.with_plugin(InputActionPlugin);
|
game.with_plugin(InputActionPlugin);
|
||||||
//game.with_system("test_actions", test_system, &[]);
|
//game.with_system("test_actions", test_system, &[]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 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)]
|
#[derive(Clone)]
|
||||||
pub struct ComponentColumn {
|
pub struct ComponentColumn {
|
||||||
|
@ -8,6 +8,8 @@ pub struct ComponentColumn {
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
pub capacity: usize,
|
pub capacity: usize,
|
||||||
pub info: ComponentInfo,
|
pub info: ComponentInfo,
|
||||||
|
/// The last tick that this component was modified at.
|
||||||
|
pub entity_ticks: Vec<Tick>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ComponentColumn {
|
impl Drop for ComponentColumn {
|
||||||
|
@ -58,6 +60,7 @@ impl ComponentColumn {
|
||||||
capacity,
|
capacity,
|
||||||
info,
|
info,
|
||||||
len: 0,
|
len: 0,
|
||||||
|
entity_ticks: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +69,7 @@ impl ComponentColumn {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This column must have space to fit the component, if it does not have room it will panic.
|
/// 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);
|
assert!(entity_index < self.capacity);
|
||||||
|
|
||||||
let mut data = self.data.borrow_mut();
|
let mut data = self.data.borrow_mut();
|
||||||
|
@ -77,7 +80,15 @@ impl ComponentColumn {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let layout = self.info.layout.into_layout_unchecked();
|
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
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This column must have the entity.
|
/// 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 mut data = self.data.borrow_mut();
|
||||||
let data = data.deref_mut();
|
let data = data.deref_mut();
|
||||||
|
|
||||||
|
@ -116,6 +129,8 @@ impl ComponentColumn {
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
/// * `new_capacity` - The new capacity of components that can fit in this column.
|
/// * `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
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Will panic if `new_capacity` is less than the current capacity of the column.
|
/// 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);
|
assert!(new_capacity > self.capacity);
|
||||||
|
|
||||||
let mut data = self.data.borrow_mut();
|
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);
|
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.
|
/// 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 mut data = self.data.borrow_mut();
|
||||||
let data = data.deref_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);
|
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
|
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)
|
Some(moved_index)
|
||||||
} else { None };
|
} else { None };
|
||||||
|
|
||||||
|
@ -231,7 +252,7 @@ impl Archetype {
|
||||||
/// # Safety:
|
/// # Safety:
|
||||||
///
|
///
|
||||||
/// Archetype must contain all of the components
|
/// 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
|
where
|
||||||
B: Bundle
|
B: Bundle
|
||||||
{
|
{
|
||||||
|
@ -246,7 +267,7 @@ impl Archetype {
|
||||||
|
|
||||||
bundle.take(|data, type_id, _size| {
|
bundle.take(|data, type_id, _size| {
|
||||||
let col = self.get_column_mut(type_id).unwrap();
|
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;
|
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.
|
/// 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)
|
let entity_index = *self.entities.get(&entity)
|
||||||
.expect("The entity is not in this Archetype!");
|
.expect("The entity is not in this Archetype!");
|
||||||
let mut removed_entity: Option<(Entity, ArchetypeEntityId)> = None;
|
let mut removed_entity: Option<(Entity, ArchetypeEntityId)> = None;
|
||||||
|
|
||||||
for c in self.columns.iter_mut() {
|
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.
|
// 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 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 there wasn't a moved entity, make sure no other columns moved something.
|
||||||
if let Some(res) = moved_entity {
|
if let Some(res) = moved_entity {
|
||||||
|
|
||||||
if let Some((_, aid)) = removed_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 {
|
} else {
|
||||||
let replaced_entity = self.entities.iter().find(|(_, a)| a.0 == res as u64)
|
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!");
|
.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)
|
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> {
|
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)
|
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`].
|
/// 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`]
|
/// 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.
|
/// 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
|
where
|
||||||
B: Bundle
|
B: Bundle
|
||||||
{
|
{
|
||||||
|
@ -387,7 +412,7 @@ impl Archetype {
|
||||||
});
|
});
|
||||||
|
|
||||||
//self.remove_entity(entity);
|
//self.remove_entity(entity);
|
||||||
}
|
} */
|
||||||
|
|
||||||
pub fn entities(&self) -> &HashMap<Entity, ArchetypeEntityId> {
|
pub fn entities(&self) -> &HashMap<Entity, ArchetypeEntityId> {
|
||||||
&self.entities
|
&self.entities
|
||||||
|
@ -400,7 +425,7 @@ mod tests {
|
||||||
|
|
||||||
use rand::Rng;
|
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;
|
use super::Archetype;
|
||||||
|
|
||||||
|
@ -413,7 +438,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), bundle.info());
|
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 col = a.columns.get(0).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(entity_arch_id.0 as usize) };
|
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 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 col = a.columns.get(0).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(entity_arch_id.0 as usize) };
|
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 mut a = Archetype::from_bundle_info(super::ArchetypeId(0), b1.info());
|
||||||
let earch1 = a.add_entity(e1, b1);
|
let earch1 = a.add_entity(e1, b1, &Tick::default());
|
||||||
let earch2 = a.add_entity(e2, b2);
|
let earch2 = a.add_entity(e2, b2, &Tick::default());
|
||||||
|
|
||||||
let col = a.columns.get(0).unwrap();
|
let col = a.columns.get(0).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(earch1.0 as usize) };
|
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 mut a = Archetype::from_bundle_info(super::ArchetypeId(0), b1.info());
|
||||||
let earch1 = a.add_entity(e1, b1);
|
let earch1 = a.add_entity(e1, b1, &Tick::default());
|
||||||
let earch2 = a.add_entity(e2, b2);
|
let earch2 = a.add_entity(e2, b2, &Tick::default());
|
||||||
|
|
||||||
let col = a.columns.get(0).unwrap();
|
let col = a.columns.get(0).unwrap();
|
||||||
let vec2: &Vec2 = unsafe { col.get(earch1.0 as usize) };
|
let vec2: &Vec2 = unsafe { col.get(earch1.0 as usize) };
|
||||||
|
@ -525,7 +550,8 @@ mod tests {
|
||||||
id: EntityId(i as u64),
|
id: EntityId(i as u64),
|
||||||
generation: 0
|
generation: 0
|
||||||
},
|
},
|
||||||
c
|
c,
|
||||||
|
&Tick::default()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
println!("Inserted {} entities", bundle_count);
|
println!("Inserted {} entities", bundle_count);
|
||||||
|
@ -553,6 +579,7 @@ mod tests {
|
||||||
generation: 0
|
generation: 0
|
||||||
},
|
},
|
||||||
(bundles[i],),
|
(bundles[i],),
|
||||||
|
&Tick::default()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,8 +587,9 @@ mod tests {
|
||||||
let moved_entity = a.remove_entity(
|
let moved_entity = a.remove_entity(
|
||||||
Entity {
|
Entity {
|
||||||
id: EntityId(1u64),
|
id: EntityId(1u64),
|
||||||
generation: 0
|
generation: 0,
|
||||||
}
|
},
|
||||||
|
&Tick::default()
|
||||||
).expect("No entity was moved");
|
).expect("No entity was moved");
|
||||||
|
|
||||||
// The last entity in the column should have been moved
|
// The last entity in the column should have been moved
|
||||||
|
@ -592,7 +620,9 @@ mod tests {
|
||||||
Entity {
|
Entity {
|
||||||
id: EntityId(0),
|
id: EntityId(0),
|
||||||
generation: 0
|
generation: 0
|
||||||
}, dynamic_bundle
|
},
|
||||||
|
dynamic_bundle,
|
||||||
|
&Tick::default()
|
||||||
);
|
);
|
||||||
|
|
||||||
let col = a.columns.iter().next().unwrap();
|
let col = a.columns.iter().next().unwrap();
|
||||||
|
@ -600,47 +630,4 @@ mod tests {
|
||||||
assert_eq!(unsafe { *ptr.cast::<u32>().as_ref() }, comp);
|
assert_eq!(unsafe { *ptr.cast::<u32>().as_ref() }, comp);
|
||||||
assert_eq!(col.info, info);
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -27,6 +27,9 @@ pub use resource::*;
|
||||||
mod system;
|
mod system;
|
||||||
pub use system::*;
|
pub use system::*;
|
||||||
|
|
||||||
|
mod tick;
|
||||||
|
pub use tick::*;
|
||||||
|
|
||||||
pub use lyra_ecs_derive::*;
|
pub use lyra_ecs_derive::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{marker::PhantomData, any::TypeId, ptr::NonNull, cell::{Ref, RefCell, RefMut}};
|
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};
|
use super::{Fetch, Query, AsQuery};
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ where
|
||||||
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
|
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)
|
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
|
||||||
.expect("You ignored 'can_visit_archetype'!");
|
.expect("You ignored 'can_visit_archetype'!");
|
||||||
|
|
||||||
|
@ -100,8 +100,9 @@ impl<T: 'static> AsQuery for &T {
|
||||||
|
|
||||||
/// A fetcher for mutably borrowing components from archetypes.
|
/// A fetcher for mutably borrowing components from archetypes.
|
||||||
pub struct FetchBorrowMut<'a, T> {
|
pub struct FetchBorrowMut<'a, T> {
|
||||||
col: &'a ComponentColumn,
|
col: NonNull<ComponentColumn>,
|
||||||
size: usize,
|
size: usize,
|
||||||
|
tick: Tick,
|
||||||
_phantom: PhantomData<&'a T>
|
_phantom: PhantomData<&'a T>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +117,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||||
let 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| {
|
RefMut::map(ptr, |ptr| {
|
||||||
let ptr = NonNull::new_unchecked(ptr.as_ptr()
|
let ptr = NonNull::new_unchecked(ptr.as_ptr()
|
||||||
.add(entity.0 as usize * self.size))
|
.add(entity.0 as usize * self.size))
|
||||||
|
@ -164,6 +168,8 @@ where
|
||||||
|
|
||||||
type Fetch<'a> = FetchBorrowMut<'a, T>;
|
type Fetch<'a> = FetchBorrowMut<'a, T>;
|
||||||
|
|
||||||
|
const MUTATES: bool = true;
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
QueryBorrowMut::<T>::new()
|
QueryBorrowMut::<T>::new()
|
||||||
}
|
}
|
||||||
|
@ -172,13 +178,19 @@ where
|
||||||
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
|
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)
|
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
|
||||||
.expect("You ignored 'can_visit_archetype'!");
|
.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 {
|
FetchBorrowMut {
|
||||||
col,
|
col: NonNull::from(col),
|
||||||
size: col.info.layout.size,
|
size: layout_size,
|
||||||
|
tick,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,9 +206,9 @@ impl<T: 'static> AsQuery for &mut T {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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};
|
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
|
||||||
|
|
||||||
|
@ -270,13 +282,14 @@ mod tests {
|
||||||
a.add_entity(Entity {
|
a.add_entity(Entity {
|
||||||
id: EntityId(i),
|
id: EntityId(i),
|
||||||
generation: 0,
|
generation: 0,
|
||||||
}, (Vec2::rand(),));
|
}, (Vec2::rand(),), &Tick::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
let col = a.columns.iter().find(|c| c.info.type_id == DynTypeId::of::<Vec2>()).unwrap();
|
let col = a.columns.iter().find(|c| c.info.type_id == DynTypeId::of::<Vec2>()).unwrap();
|
||||||
|
|
||||||
let mut bmut = FetchBorrowMut::<Vec2> {
|
let mut bmut = FetchBorrowMut::<Vec2> {
|
||||||
col,
|
col: NonNull::from(col),
|
||||||
|
tick: Tick::default(),
|
||||||
size: size_of::<Vec2>(),
|
size: size_of::<Vec2>(),
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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};
|
use super::{Fetch, Query};
|
||||||
|
|
||||||
|
@ -38,10 +38,14 @@ impl Query for Entities {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
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: crate::Tick) -> Self::Fetch<'a> {
|
||||||
let _ = arch_id; // ignore unused warnings
|
let _ = tick; // ignore unused warnings
|
||||||
EntitiesFetch {
|
EntitiesFetch {
|
||||||
entities: archetype.entities.keys().cloned().collect::<Vec<Entity>>(),
|
entities: archetype.entities.keys().cloned().collect::<Vec<Entity>>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsQuery for Entities {
|
||||||
|
type Query = Self;
|
||||||
}
|
}
|
|
@ -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 mod view;
|
||||||
pub use view::*;
|
pub use view::*;
|
||||||
|
@ -19,6 +19,10 @@ pub mod resource;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use resource::*;
|
pub use resource::*;
|
||||||
|
|
||||||
|
pub mod tick;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use tick::*;
|
||||||
|
|
||||||
pub mod dynamic;
|
pub mod dynamic;
|
||||||
|
|
||||||
/// A [`Fetch`]er implementation gets data out of an archetype.
|
/// 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.
|
/// [`View`] uses this to determine if it should continue to iterate this Query.
|
||||||
const ALWAYS_FETCHES: bool = false;
|
const ALWAYS_FETCHES: bool = false;
|
||||||
|
|
||||||
|
/// A constant that signifies if this Query will mutate data.
|
||||||
|
const MUTATES: bool = false;
|
||||||
|
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
|
|
||||||
/// Returns true if the archetype should be visited or skipped.
|
/// Returns true if the archetype should be visited or skipped.
|
||||||
fn can_visit_archetype(&self, archetype: &Archetype) -> bool;
|
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.
|
/// Attempt to fetch only from the world.
|
||||||
///
|
///
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl<T: ResourceObject + 'static> Query for QueryResource<T> {
|
||||||
true
|
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()
|
self.fetch_world(world).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ impl<T: ResourceObject + 'static> Query for QueryResourceMut<T> {
|
||||||
true
|
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()
|
self.fetch_world(world).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
|
}
|
|
@ -46,9 +46,9 @@ where
|
||||||
q1.can_visit_archetype(archetype) && q2.can_visit_archetype(archetype)
|
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;
|
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)
|
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;
|
let ( $($name,)+ ) = self;
|
||||||
( $($name.fetch(world, arch_id, archetype),)+ )
|
( $($name.fetch(world, archetype, tick),)+ )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::Range;
|
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};
|
use super::{Query, Fetch};
|
||||||
|
|
||||||
|
@ -32,8 +32,11 @@ where
|
||||||
type IntoIter = ViewIter<'a, Q>;
|
type IntoIter = ViewIter<'a, Q>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
let tick = self.world.tick_tracker().tick_when(Q::MUTATES);
|
||||||
|
|
||||||
ViewIter {
|
ViewIter {
|
||||||
world: self.world,
|
world: self.world,
|
||||||
|
tick,
|
||||||
query: self.query,
|
query: self.query,
|
||||||
fetcher: None,
|
fetcher: None,
|
||||||
archetypes: self.archetypes,
|
archetypes: self.archetypes,
|
||||||
|
@ -45,6 +48,7 @@ where
|
||||||
|
|
||||||
pub struct ViewIter<'a, Q: Query> {
|
pub struct ViewIter<'a, Q: Query> {
|
||||||
world: &'a World,
|
world: &'a World,
|
||||||
|
tick: Tick,
|
||||||
query: Q,
|
query: Q,
|
||||||
fetcher: Option<Q::Fetch<'a>>,
|
fetcher: Option<Q::Fetch<'a>>,
|
||||||
archetypes: Vec<&'a Archetype>,
|
archetypes: Vec<&'a Archetype>,
|
||||||
|
@ -100,7 +104,7 @@ where
|
||||||
continue;
|
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;
|
self.component_indices = 0..arch.entities.len() as u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,14 +113,18 @@ where
|
||||||
|
|
||||||
pub struct ViewOne<'a, Q: Query> {
|
pub struct ViewOne<'a, Q: Query> {
|
||||||
world: &'a World,
|
world: &'a World,
|
||||||
|
tick: Tick,
|
||||||
entity: EntityId,
|
entity: EntityId,
|
||||||
query: Q,
|
query: Q,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Q: Query> ViewOne<'a, Q> {
|
impl<'a, Q: Query> ViewOne<'a, Q> {
|
||||||
pub fn new(world: &'a World, entity: EntityId, query: Q) -> Self {
|
pub fn new(world: &'a World, entity: EntityId, query: Q) -> Self {
|
||||||
|
let tick = world.tick_tracker().tick_when(Q::MUTATES);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
world,
|
world,
|
||||||
|
tick,
|
||||||
entity,
|
entity,
|
||||||
query
|
query
|
||||||
}
|
}
|
||||||
|
@ -128,7 +136,7 @@ impl<'a, Q: Query> ViewOne<'a, Q> {
|
||||||
.expect("An invalid record was specified for an entity");
|
.expect("An invalid record was specified for an entity");
|
||||||
|
|
||||||
if self.query.can_visit_archetype(arch) {
|
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) {
|
if fetch.can_visit_item(record.index) {
|
||||||
return Some(unsafe { fetch.get_item(record.index) });
|
return Some(unsafe { fetch.get_item(record.index) });
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::{HashMap, VecDeque}, any::TypeId, cell::{Ref, RefMut}, ptr::NonNull};
|
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)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct EntityId(pub u64);
|
pub struct EntityId(pub u64);
|
||||||
|
@ -29,6 +29,7 @@ pub struct World {
|
||||||
dead_entities: VecDeque<Entity>,
|
dead_entities: VecDeque<Entity>,
|
||||||
next_entity_id: EntityId,
|
next_entity_id: EntityId,
|
||||||
resources: HashMap<TypeId, ResourceData>,
|
resources: HashMap<TypeId, ResourceData>,
|
||||||
|
tracker: TickTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for World {
|
impl Default for World {
|
||||||
|
@ -40,6 +41,7 @@ impl Default for World {
|
||||||
dead_entities: VecDeque::new(),
|
dead_entities: VecDeque::new(),
|
||||||
next_entity_id: EntityId(0),
|
next_entity_id: EntityId(0),
|
||||||
resources: HashMap::new(),
|
resources: HashMap::new(),
|
||||||
|
tracker: TickTracker::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,9 +51,13 @@ impl World {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a new Entity, will recycle dead entities and increment their generation.
|
||||||
fn get_new_entity(&mut self) -> Entity {
|
fn get_new_entity(&mut self) -> Entity {
|
||||||
match self.dead_entities.pop_front() {
|
match self.dead_entities.pop_front() {
|
||||||
Some(e) => e,
|
Some(mut e) => {
|
||||||
|
e.generation += 1;
|
||||||
|
e
|
||||||
|
},
|
||||||
None => {
|
None => {
|
||||||
let new_id = self.next_entity_id;
|
let new_id = self.next_entity_id;
|
||||||
self.next_entity_id.0 += 1;
|
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
|
pub fn spawn<B>(&mut self, bundle: B) -> Entity
|
||||||
where
|
where
|
||||||
B: Bundle
|
B: Bundle
|
||||||
|
@ -71,13 +78,15 @@ impl World {
|
||||||
let bundle_types = bundle.type_ids();
|
let bundle_types = bundle.type_ids();
|
||||||
let new_entity = self.get_new_entity();
|
let new_entity = self.get_new_entity();
|
||||||
|
|
||||||
|
let tick = self.tick();
|
||||||
|
|
||||||
// try to find an archetype
|
// try to find an archetype
|
||||||
let archetype = self.archetypes
|
let archetype = self.archetypes
|
||||||
.values_mut()
|
.values_mut()
|
||||||
.find(|a| a.is_archetype_for(&bundle_types));
|
.find(|a| a.is_archetype_for(&bundle_types));
|
||||||
|
|
||||||
if let Some(archetype) = archetype {
|
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
|
// Create entity record and store it
|
||||||
let record = Record {
|
let record = Record {
|
||||||
|
@ -92,7 +101,7 @@ impl World {
|
||||||
// create archetype
|
// create archetype
|
||||||
let new_arch_id = self.next_archetype_id.increment();
|
let new_arch_id = self.next_archetype_id.increment();
|
||||||
let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info());
|
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
|
// store archetype
|
||||||
self.archetypes.insert(new_arch_id, archetype);
|
self.archetypes.insert(new_arch_id, archetype);
|
||||||
|
@ -112,22 +121,36 @@ impl World {
|
||||||
|
|
||||||
/// Despawn an entity from the World
|
/// Despawn an entity from the World
|
||||||
pub fn despawn(&mut self, entity: Entity) {
|
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) {
|
if let Some(record) = self.entity_index.get_mut(&entity.id) {
|
||||||
|
let tick = tick.unwrap();
|
||||||
let arch = self.archetypes.get_mut(&record.id).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.
|
// replace the archetype index of the moved index with its new index.
|
||||||
self.entity_index.get_mut(&moved.id).unwrap().index = 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)
|
pub fn insert<B>(&mut self, entity: Entity, bundle: B)
|
||||||
where
|
where
|
||||||
B: Bundle
|
B: Bundle
|
||||||
{
|
{
|
||||||
// TODO: If the archetype has a single entity, add a component column for the new component instead of
|
// TODO: If the archetype has a single entity, add a component column for the new
|
||||||
// moving the entity to a brand new archetype.
|
// 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 record = *self.entity_index.get(&entity.id).unwrap();
|
||||||
let current_arch = self.archetypes.get(&record.id).unwrap();
|
let current_arch = self.archetypes.get(&record.id).unwrap();
|
||||||
|
@ -140,7 +163,7 @@ impl World {
|
||||||
col_infos.extend(bundle.info().into_iter());
|
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();
|
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)) {
|
if let Some(arch) = self.archetypes.values_mut().find(|a| a.is_archetype_for(&col_types)) {
|
||||||
let res_index = arch.reserve_one(entity);
|
let res_index = arch.reserve_one(entity);
|
||||||
|
|
||||||
|
@ -149,13 +172,13 @@ impl World {
|
||||||
let ptr = NonNull::new_unchecked(col_ptr.as_ptr()
|
let ptr = NonNull::new_unchecked(col_ptr.as_ptr()
|
||||||
.add(res_index.0 as usize * col_info.layout.size));
|
.add(res_index.0 as usize * col_info.layout.size));
|
||||||
let col = arch.get_column_mut(col_type).unwrap();
|
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| {
|
bundle.take(|data, type_id, _size| {
|
||||||
let col = arch.get_column_mut(type_id).unwrap();
|
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;
|
col.len += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -169,7 +192,7 @@ impl World {
|
||||||
} else {
|
} else {
|
||||||
let new_arch_id = self.next_archetype_id.increment();
|
let new_arch_id = self.next_archetype_id.increment();
|
||||||
let mut archetype = Archetype::from_bundle_info(new_arch_id, col_infos);
|
let mut archetype = Archetype::from_bundle_info(new_arch_id, 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);
|
self.archetypes.insert(new_arch_id, archetype);
|
||||||
|
|
||||||
|
@ -183,7 +206,7 @@ impl World {
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
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.
|
/// 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())
|
.map(|r| r.try_get_mut())
|
||||||
.flatten()
|
.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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{Vec2, Vec3};
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use crate::{tests::{Vec2, Vec3}, TickOf};
|
||||||
|
|
||||||
use super::World;
|
use super::World;
|
||||||
|
|
||||||
|
@ -390,4 +433,32 @@ mod tests {
|
||||||
let view = world.view_one::<&Vec2>(e);
|
let view = world.view_one::<&Vec2>(e);
|
||||||
assert_eq!(*view.get().unwrap(), v);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use edict::Component;
|
use lyra_ecs::Component;
|
||||||
|
|
||||||
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};
|
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::BorrowMut;
|
use std::borrow::BorrowMut;
|
||||||
|
|
||||||
use edict::{Component, World};
|
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
|
use lyra_ecs::{Component, world::World};
|
||||||
|
|
||||||
use crate::plugin::Plugin;
|
use crate::plugin::Plugin;
|
||||||
|
|
||||||
|
@ -24,12 +24,10 @@ impl std::ops::DerefMut for DeltaTime {
|
||||||
|
|
||||||
fn delta_time_system(world: &mut World) -> anyhow::Result<()> {
|
fn delta_time_system(world: &mut World) -> anyhow::Result<()> {
|
||||||
let now = Instant::now();
|
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.0 = delta.1.unwrap_or(now).elapsed().as_secs_f32();
|
||||||
delta.1 = Some(now);
|
delta.1 = Some(now);
|
||||||
|
|
||||||
//println!("delta: {}", delta.0);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +35,7 @@ pub struct DeltaTimePlugin;
|
||||||
|
|
||||||
impl Plugin for DeltaTimePlugin {
|
impl Plugin for DeltaTimePlugin {
|
||||||
fn setup(&self, game: &mut crate::game::Game) {
|
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, &[]);
|
game.with_system("delta_time", delta_time_system, &[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use edict::Component;
|
use lyra_ecs::Component;
|
||||||
|
|
||||||
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};
|
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use edict::Component;
|
use lyra_ecs::Component;
|
||||||
|
|
||||||
use lyra_resource::Mesh;
|
use lyra_resource::Mesh;
|
||||||
|
|
||||||
#[derive(Clone, Component)]
|
#[derive(Clone, Component)]
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
use lyra_ecs::Component;
|
||||||
use lyra_resource::ResHandle;
|
use lyra_resource::ResHandle;
|
||||||
|
|
||||||
use crate::assets::Model;
|
use crate::assets::Model;
|
||||||
|
|
||||||
#[derive(Clone, edict::Component)]
|
#[derive(Clone, Component)]
|
||||||
pub struct ModelComponent(pub ResHandle<Model>);
|
pub struct ModelComponent(pub ResHandle<Model>);
|
||||||
|
|
||||||
impl From<ResHandle<Model>> for ModelComponent {
|
impl From<ResHandle<Model>> for ModelComponent {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use edict::Component;
|
use lyra_ecs::Component;
|
||||||
|
|
||||||
use crate::math::Transform;
|
use crate::math::Transform;
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,6 @@ pub struct EventsPlugin;
|
||||||
|
|
||||||
impl Plugin for EventsPlugin {
|
impl Plugin for EventsPlugin {
|
||||||
fn setup(&self, game: &mut crate::game::Game) {
|
fn setup(&self, game: &mut crate::game::Game) {
|
||||||
game.world().insert_resource(EventQueue::new());
|
game.world().add_resource(EventQueue::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
pub use edict::*;
|
pub use lyra_ecs;
|
||||||
|
|
||||||
pub mod components;
|
pub mod components;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use lyra_ecs::world::World;
|
||||||
|
|
||||||
use super::{SimpleSystem, Criteria};
|
use super::{SimpleSystem, Criteria};
|
||||||
use edict::World;
|
|
||||||
|
|
||||||
/// A system that executes a batch of systems in order that they were given.
|
/// 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
|
/// You can optionally add criteria that must pass before the systems are
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use lyra_ecs::world::World;
|
||||||
|
|
||||||
pub enum CriteriaSchedule {
|
pub enum CriteriaSchedule {
|
||||||
Yes,
|
Yes,
|
||||||
No,
|
No,
|
||||||
|
@ -11,13 +13,13 @@ pub trait Criteria {
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
/// * `world` - The ecs world.
|
/// * `world` - The ecs world.
|
||||||
/// * `check_count` - The amount of times the Criteria has been checked this tick.
|
/// * `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
|
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)
|
self(world, check_count)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,9 +3,9 @@ pub use batched::*;
|
||||||
|
|
||||||
pub mod criteria;
|
pub mod criteria;
|
||||||
pub use criteria::*;
|
pub use criteria::*;
|
||||||
|
use lyra_ecs::world::World;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use edict::World;
|
|
||||||
use petgraph::{stable_graph::{StableDiGraph, NodeIndex}, visit::Topo};
|
use petgraph::{stable_graph::{StableDiGraph, NodeIndex}, visit::Topo};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ pub trait SimpleSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> SimpleSystem for S
|
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<()> {
|
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||||
self(world)
|
self(world)
|
||||||
|
|
27
src/game.rs
27
src/game.rs
|
@ -2,6 +2,7 @@ use std::{sync::Arc, collections::VecDeque};
|
||||||
|
|
||||||
use async_std::task::block_on;
|
use async_std::task::block_on;
|
||||||
|
|
||||||
|
use lyra_ecs::world::World;
|
||||||
use tracing::{info, error, Level, debug};
|
use tracing::{info, error, Level, debug};
|
||||||
use tracing_appender::non_blocking;
|
use tracing_appender::non_blocking;
|
||||||
use tracing_subscriber::{
|
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};
|
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 struct Controls<'a> {
|
||||||
pub world: &'a mut edict::World,
|
pub world: &'a mut World,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -34,14 +35,14 @@ struct GameLoop {
|
||||||
window: Arc<Window>,
|
window: Arc<Window>,
|
||||||
renderer: Box<dyn Renderer>,
|
renderer: Box<dyn Renderer>,
|
||||||
|
|
||||||
world: edict::World,
|
world: World,
|
||||||
/// higher priority systems
|
/// higher priority systems
|
||||||
engine_sys_dispatcher: SystemDispatcher,
|
engine_sys_dispatcher: SystemDispatcher,
|
||||||
user_sys_dispatcher: SystemDispatcher,
|
user_sys_dispatcher: SystemDispatcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameLoop {
|
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 {
|
Self {
|
||||||
window: Arc::clone(&window),
|
window: Arc::clone(&window),
|
||||||
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
||||||
|
@ -58,7 +59,7 @@ impl GameLoop {
|
||||||
|
|
||||||
pub async fn on_init(&mut self) {
|
pub async fn on_init(&mut self) {
|
||||||
// Create the EventQueue resource in the world
|
// 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) {
|
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)
|
/* let trigger = matches!(self.world.get_resource::<WindowState>(), Some(window_state)
|
||||||
if window_state.is_focused && window_state.is_cursor_inside_window); */
|
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 window.focused && window.cursor_inside_window);
|
||||||
|
|
||||||
if trigger {
|
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, };
|
let input_event = InputEvent::MouseMotion { device_id, delta, };
|
||||||
event_queue.trigger_event(input_event);
|
event_queue.trigger_event(input_event);
|
||||||
|
@ -134,7 +135,7 @@ impl GameLoop {
|
||||||
// Its possible to receive multiple input events before the update event for
|
// 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.
|
// 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());
|
event_queue.trigger_event(input_event.clone());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -159,7 +160,7 @@ impl GameLoop {
|
||||||
},
|
},
|
||||||
|
|
||||||
WindowEvent::Focused(is_focused) => {
|
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;
|
state.is_focused = *is_focused;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ impl GameLoop {
|
||||||
} */
|
} */
|
||||||
|
|
||||||
self.renderer.as_mut().prepare(&mut self.world);
|
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();
|
event_queue.update_events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +203,7 @@ impl GameLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
world: Option<edict::World>,
|
world: Option<World>,
|
||||||
plugins: VecDeque<Box<dyn Plugin>>,
|
plugins: VecDeque<Box<dyn Plugin>>,
|
||||||
system_dispatcher: Option<SystemDispatcher>,
|
system_dispatcher: Option<SystemDispatcher>,
|
||||||
startup_systems: VecDeque<Box<dyn SimpleSystem>>,
|
startup_systems: VecDeque<Box<dyn SimpleSystem>>,
|
||||||
|
@ -211,7 +212,7 @@ pub struct Game {
|
||||||
impl Default for Game {
|
impl Default for Game {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
world: Some(edict::World::new()),
|
world: Some(World::new()),
|
||||||
plugins: VecDeque::new(),
|
plugins: VecDeque::new(),
|
||||||
system_dispatcher: Some(SystemDispatcher::new()),
|
system_dispatcher: Some(SystemDispatcher::new()),
|
||||||
startup_systems: VecDeque::new(),
|
startup_systems: VecDeque::new(),
|
||||||
|
@ -225,7 +226,7 @@ impl Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the world of this 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
|
// world is always `Some`, so unwrapping is safe
|
||||||
self.world.as_mut().unwrap()
|
self.world.as_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -265,7 +266,7 @@ impl Game {
|
||||||
/// Override the default (empty) world
|
/// Override the default (empty) world
|
||||||
///
|
///
|
||||||
/// This isn't recommended, you should create a startup system and add it to `with_startup_system`
|
/// 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.world = Some(world);
|
||||||
|
|
||||||
self
|
self
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::HashMap, cell::RefCell, borrow::BorrowMut, ops::Deref};
|
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};
|
use crate::{castable_any::CastableAny, plugin::Plugin};
|
||||||
|
|
||||||
|
@ -369,11 +369,11 @@ impl ActionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions_system(world: &mut World) -> anyhow::Result<()> {
|
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());
|
.map(|r| r.deref().clone());
|
||||||
|
|
||||||
if let Some(keys) = keys {
|
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!");
|
.expect("No Input Action handler was created in the world!");
|
||||||
|
|
||||||
//handler
|
//handler
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
|
use lyra_ecs::world::World;
|
||||||
use winit::event::MouseScrollDelta;
|
use winit::event::MouseScrollDelta;
|
||||||
|
|
||||||
use crate::{ecs::{SimpleSystem, EventQueue}, plugin::Plugin,};
|
use crate::{ecs::{SimpleSystem, EventQueue}, plugin::Plugin,};
|
||||||
|
@ -11,8 +12,8 @@ pub type KeyCode = winit::event::VirtualKeyCode;
|
||||||
pub struct InputSystem;
|
pub struct InputSystem;
|
||||||
|
|
||||||
impl InputSystem {
|
impl InputSystem {
|
||||||
pub fn process_event(&mut self, world: &mut edict::World, event: &InputEvent) -> bool {
|
pub fn process_event(&mut self, world: &mut World, event: &InputEvent) -> bool {
|
||||||
let event_queue = world.get_resource_mut::<EventQueue>();
|
let event_queue = world.try_get_resource_mut::<EventQueue>();
|
||||||
if event_queue.is_none() {
|
if event_queue.is_none() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +23,7 @@ impl InputSystem {
|
||||||
InputEvent::KeyboardInput { input, .. } => {
|
InputEvent::KeyboardInput { input, .. } => {
|
||||||
if let Some(code) = input.virtual_keycode {
|
if let Some(code) = input.virtual_keycode {
|
||||||
drop(event_queue);
|
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());
|
//let mut e = with_resource_mut(world, || InputButtons::<KeyCode>::new());
|
||||||
e.add_input_from_winit(code, input.state);
|
e.add_input_from_winit(code, input.state);
|
||||||
}
|
}
|
||||||
|
@ -72,7 +73,7 @@ impl InputSystem {
|
||||||
event_queue.trigger_event(button_event);
|
event_queue.trigger_event(button_event);
|
||||||
drop(event_queue);
|
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);
|
e.add_input_from_winit(button_event, *state);
|
||||||
},
|
},
|
||||||
InputEvent::Touch(t) => {
|
InputEvent::Touch(t) => {
|
||||||
|
@ -85,7 +86,7 @@ impl InputSystem {
|
||||||
finger_id: t.id,
|
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);
|
touches.touches.push(touch);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
|
@ -96,8 +97,8 @@ impl InputSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleSystem for InputSystem {
|
impl SimpleSystem for InputSystem {
|
||||||
fn execute_mut(&mut self, world: &mut edict::World) -> anyhow::Result<()> {
|
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
|
||||||
let queue = world.get_resource_mut::<EventQueue>()
|
let queue = world.try_get_resource_mut::<EventQueue>()
|
||||||
.and_then(|q| q.read_events::<InputEvent>());
|
.and_then(|q| q.read_events::<InputEvent>());
|
||||||
|
|
||||||
if queue.is_none() {
|
if queue.is_none() {
|
||||||
|
|
|
@ -98,7 +98,7 @@ pub struct ResourceManagerPlugin;
|
||||||
|
|
||||||
impl Plugin for ResourceManagerPlugin {
|
impl Plugin for ResourceManagerPlugin {
|
||||||
fn setup(&self, game: &mut Game) {
|
fn setup(&self, game: &mut Game) {
|
||||||
game.world().insert_resource(ResourceManager::new());
|
game.world().add_resource(ResourceManager::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#[derive(Default, Debug, Clone, edict::Component)]
|
use lyra_ecs::Component;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Component)]
|
||||||
pub struct DirectionalLight {
|
pub struct DirectionalLight {
|
||||||
//pub direction: glam::Quat,
|
//pub direction: glam::Quat,
|
||||||
pub color: glam::Vec3,
|
pub color: glam::Vec3,
|
||||||
|
|
|
@ -2,13 +2,13 @@ pub mod point;
|
||||||
pub mod directional;
|
pub mod directional;
|
||||||
pub mod spotlight;
|
pub mod spotlight;
|
||||||
|
|
||||||
|
use lyra_ecs::{Entity, Tick, world::World, Entities, TickOf};
|
||||||
pub use point::*;
|
pub use point::*;
|
||||||
pub use directional::*;
|
pub use directional::*;
|
||||||
pub use spotlight::*;
|
pub use spotlight::*;
|
||||||
|
|
||||||
use std::{collections::{VecDeque, HashMap}, num::{NonZeroU64, NonZeroU32}, marker::PhantomData};
|
use std::{collections::{VecDeque, HashMap}, num::{NonZeroU64, NonZeroU32}, marker::PhantomData};
|
||||||
|
|
||||||
use edict::query::EpochOf;
|
|
||||||
pub use point::*;
|
pub use point::*;
|
||||||
use tracing::{debug, Instrument};
|
use tracing::{debug, Instrument};
|
||||||
use wgpu::util::DeviceExt;
|
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.
|
/// to recreate the array and remove the gaps.
|
||||||
pub buffer_count: usize,
|
pub buffer_count: usize,
|
||||||
/// The buffer index for a specific entity/caster.
|
/// 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.
|
/// Indexes that were being used but are no longer needed.
|
||||||
dead_indexes: VecDeque<usize>,
|
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)
|
self.used_indexes.contains_key(&entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update an existing light in the light buffer.
|
/// 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)
|
let buffer_idx = *self.used_indexes.get(&entity)
|
||||||
.expect("Entity for Light is not in buffer!");
|
.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.
|
/// 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() {
|
let buffer_idx = match self.dead_indexes.pop_front() {
|
||||||
Some(i) => i,
|
Some(i) => i,
|
||||||
None => {
|
None => {
|
||||||
|
@ -82,7 +82,7 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update, or add a new caster, to the light buffer.
|
/// 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) {
|
if self.used_indexes.contains_key(&entity) {
|
||||||
self.update_light(lights_buffer, entity, light);
|
self.update_light(lights_buffer, entity, light);
|
||||||
} else {
|
} 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.
|
/// 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) {
|
if let Some(removed_idx) = self.used_indexes.remove(&entity) {
|
||||||
self.dead_indexes.push_back(removed_idx);
|
self.dead_indexes.push_back(removed_idx);
|
||||||
//self.current_count -= 1;
|
//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)
|
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 {
|
if !self.point_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick {
|
||||||
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
|
let uniform = PointLightUniform::from_bundle(&point_light, &transform.transform);
|
||||||
self.point_lights.update_or_add(&mut self.lights_uniform.point_lights, entity, uniform);
|
self.point_lights.update_or_add(&mut self.lights_uniform.point_lights, entity, uniform);
|
||||||
debug!("Updated point light");
|
debug!("Updated point light");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entity, spot_light, transform, light_epoch, transform_epoch)
|
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 {
|
if !self.spot_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick {
|
||||||
let uniform = SpotLightUniform::from_bundle(spot_light, &transform.transform);
|
let uniform = SpotLightUniform::from_bundle(&spot_light, &transform.transform);
|
||||||
self.spot_lights.update_or_add(&mut self.lights_uniform.spot_lights, entity, uniform);
|
self.spot_lights.update_or_add(&mut self.lights_uniform.spot_lights, entity, uniform);
|
||||||
//debug!("Updated spot light");
|
//debug!("Updated spot light");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((dir_light, transform)) =
|
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;
|
self.lights_uniform.directional_light = uniform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#[derive(Default, Debug, Clone, edict::Component)]
|
use lyra_ecs::Component;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Component)]
|
||||||
pub struct PointLight {
|
pub struct PointLight {
|
||||||
pub color: glam::Vec3,
|
pub color: glam::Vec3,
|
||||||
pub intensity: f32,
|
pub intensity: f32,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
use lyra_ecs::Component;
|
||||||
|
|
||||||
use crate::math;
|
use crate::math;
|
||||||
|
|
||||||
#[derive(Debug, Clone, edict::Component)]
|
#[derive(Debug, Clone, Component)]
|
||||||
pub struct SpotLight {
|
pub struct SpotLight {
|
||||||
pub color: glam::Vec3,
|
pub color: glam::Vec3,
|
||||||
pub cutoff: math::Angle,
|
pub cutoff: math::Angle,
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
use edict::EntityId;
|
|
||||||
|
use lyra_ecs::Entity;
|
||||||
|
|
||||||
use crate::math::Transform;
|
use crate::math::Transform;
|
||||||
|
|
||||||
use super::material::Material;
|
use super::material::Material;
|
||||||
|
|
||||||
pub struct RenderJob {
|
pub struct RenderJob {
|
||||||
pub entity: EntityId,
|
pub entity: Entity,
|
||||||
pub shader_id: u64,
|
pub shader_id: u64,
|
||||||
pub mesh_buffer_id: uuid::Uuid,
|
pub mesh_buffer_id: uuid::Uuid,
|
||||||
pub transform: Transform,
|
pub transform: Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderJob {
|
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 {
|
Self {
|
||||||
entity,
|
entity,
|
||||||
shader_id,
|
shader_id,
|
||||||
|
|
|
@ -4,11 +4,11 @@ use std::num::NonZeroU64;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use edict::query::EpochOf;
|
|
||||||
use edict::{EntityId, Entities};
|
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
|
use lyra_ecs::{Entity, Entities, TickOf};
|
||||||
|
use lyra_ecs::world::World;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
use wgpu::{BindGroup, BindGroupLayout, Limits};
|
use wgpu::{BindGroup, BindGroupLayout, Limits};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
@ -35,7 +35,7 @@ use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, r
|
||||||
use lyra_resource::Mesh;
|
use lyra_resource::Mesh;
|
||||||
|
|
||||||
pub trait Renderer {
|
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 render(&mut self) -> Result<(), wgpu::SurfaceError>;
|
||||||
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
|
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
|
||||||
|
|
||||||
|
@ -79,8 +79,8 @@ pub struct BasicRenderer {
|
||||||
pub render_jobs: VecDeque<RenderJob>,
|
pub render_jobs: VecDeque<RenderJob>,
|
||||||
|
|
||||||
mesh_buffers: HashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
mesh_buffers: HashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
||||||
entity_meshes: HashMap<EntityId, uuid::Uuid>,
|
entity_meshes: HashMap<Entity, uuid::Uuid>,
|
||||||
entity_last_transforms: HashMap<EntityId, CachedTransform>,
|
entity_last_transforms: HashMap<Entity, CachedTransform>,
|
||||||
|
|
||||||
transform_buffers: TransformBuffers,
|
transform_buffers: TransformBuffers,
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ impl BasicRenderer {
|
||||||
s
|
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) {
|
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
|
// check if the buffer sizes dont match. If they dont, completely remake the buffers
|
||||||
let vertices = mesh.position().unwrap();
|
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.
|
/// 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() {
|
if self.transform_buffers.should_expand() {
|
||||||
self.transform_buffers.expand_buffers(&self.device);
|
self.transform_buffers.expand_buffers(&self.device);
|
||||||
}
|
}
|
||||||
|
@ -393,14 +393,14 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer for BasicRenderer {
|
impl Renderer for BasicRenderer {
|
||||||
fn prepare(&mut self, main_world: &mut edict::World) {
|
fn prepare(&mut self, main_world: &mut World) {
|
||||||
let last_epoch = main_world.epoch();
|
let last_epoch = main_world.current_tick();
|
||||||
|
|
||||||
let mut alive_entities = HashSet::new();
|
let mut alive_entities = HashSet::new();
|
||||||
|
|
||||||
let now_inst = Instant::now();
|
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);
|
alive_entities.insert(entity);
|
||||||
|
|
||||||
let cached = match self.entity_last_transforms.get_mut(&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));
|
self.mesh_buffers.retain(|u, _| !removed_entities.contains(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
|
if let Some(camera) = main_world.view_iter::<&mut CameraComponent>().next() {
|
||||||
let view_proj = self.inuse_camera.update_view_projection(camera);
|
let view_proj = self.inuse_camera.update_view_projection(&camera);
|
||||||
let pos = camera.transform.translation;
|
let pos = camera.transform.translation;
|
||||||
let uniform = CameraUniform {
|
let uniform = CameraUniform {
|
||||||
view_proj: *view_proj,
|
view_proj: *view_proj,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
|
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64};
|
||||||
|
|
||||||
use edict::EntityId;
|
use lyra_ecs::Entity;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use wgpu::Limits;
|
use wgpu::Limits;
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ pub(crate) struct TransformBufferEntry {
|
||||||
pub(crate) struct TransformBuffers {
|
pub(crate) struct TransformBuffers {
|
||||||
pub bindgroup_layout: wgpu::BindGroupLayout,
|
pub bindgroup_layout: wgpu::BindGroupLayout,
|
||||||
/// A vector storing the EntityId and
|
/// A vector storing the EntityId and
|
||||||
pub just_updated: HashMap<EntityId, TransformBufferIndices>,
|
pub just_updated: HashMap<Entity, TransformBufferIndices>,
|
||||||
pub not_updated: HashMap<EntityId, TransformBufferIndices>,
|
pub not_updated: HashMap<Entity, TransformBufferIndices>,
|
||||||
pub dead_indices: VecDeque<TransformBufferIndices>,
|
pub dead_indices: VecDeque<TransformBufferIndices>,
|
||||||
pub next_indices: TransformBufferIndices,
|
pub next_indices: TransformBufferIndices,
|
||||||
/// (transform count, buffer, bindgroup)
|
/// (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
|
/// 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)
|
let indices = self.not_updated.remove(&entity)
|
||||||
.or_else(|| self.just_updated.remove(&entity))
|
.or_else(|| self.just_updated.remove(&entity))
|
||||||
.expect("Use 'insert_entity' for new entities");
|
.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.
|
/// 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() {
|
let indices = match self.dead_indices.pop_front() {
|
||||||
Some(indices) => indices,
|
Some(indices) => indices,
|
||||||
None => {
|
None => {
|
||||||
|
@ -119,7 +119,7 @@ impl TransformBuffers {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update or insert an entities transform
|
/// 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)
|
where TFn: Fn() -> (glam::Mat4, glam::Mat3)
|
||||||
{
|
{
|
||||||
let (tran, norm) = transform_fn();
|
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).
|
/// 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)
|
self.not_updated.contains_key(&entity) || self.just_updated.contains_key(&entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ impl TransformBuffers {
|
||||||
.map(|entry| &entry.bindgroup)
|
.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))
|
self.entity_indices(entity).and_then(|i| self.bind_group(*i))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ impl TransformBuffers {
|
||||||
self.buffer_bindgroups.push(entry);
|
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))
|
self.just_updated.get(&entity).or_else(|| self.not_updated.get(&entity))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{sync::Arc, collections::VecDeque};
|
use std::{sync::Arc, collections::VecDeque};
|
||||||
|
|
||||||
use glam::{Vec2, IVec2};
|
use glam::{Vec2, IVec2};
|
||||||
|
use lyra_ecs::world::World;
|
||||||
use tracing::{warn, error};
|
use tracing::{warn, error};
|
||||||
use winit::{window::{Window, Fullscreen}, dpi::{LogicalPosition, LogicalSize, PhysicalPosition}, error::ExternalError};
|
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<()> {
|
fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
|
||||||
if let (Some(window), Some(opts)) = (world.get_resource::<Arc<Window>>(), world.get_resource::<Ct<WindowOptions>>()) {
|
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 the options changed, update the window
|
||||||
if opts.peek_changed() {
|
if opts.peek_changed() {
|
||||||
drop(opts); // drop the Ref, we're about to get a RefMut
|
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.
|
// 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 {
|
if opts.focused {
|
||||||
window.focus_window();
|
window.focus_window();
|
||||||
|
@ -337,9 +338,9 @@ fn window_updater_system(world: &mut edict::World) -> anyhow::Result<()> {
|
||||||
center_mouse(&window, &opts);
|
center_mouse(&window, &opts);
|
||||||
} else {
|
} else {
|
||||||
drop(opts); // drop the Ref, we're about to get a RefMut
|
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>() {
|
if let Some(mut events) = event_queue.read_events::<InputEvent>() {
|
||||||
while let Some(ev) = events.pop_front() {
|
while let Some(ev) = events.pop_front() {
|
||||||
match ev {
|
match ev {
|
||||||
|
@ -372,7 +373,7 @@ impl Plugin for WindowPlugin {
|
||||||
fn setup(&self, game: &mut crate::game::Game) {
|
fn setup(&self, game: &mut crate::game::Game) {
|
||||||
let window_options = WindowOptions::default();
|
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, &[]);
|
game.with_system("window_updater", window_updater_system, &[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue