Switch to lyra-ecs systems, move ecs mod to scene mod, reexport lyra-ecs as ecs

This commit is contained in:
SeanOMik 2023-12-26 23:48:46 -05:00
parent 0a0ac0ae6f
commit 09bba5b3b3
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
37 changed files with 208 additions and 236 deletions

1
Cargo.lock generated
View File

@ -2335,7 +2335,6 @@ dependencies = [
"anyhow", "anyhow",
"async-std", "async-std",
"fps_counter", "fps_counter",
"lyra-ecs",
"lyra-engine", "lyra-engine",
"tracing", "tracing",
] ]

View File

@ -7,7 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
lyra-engine = { path = "../../", version = "0.0.1" } lyra-engine = { path = "../../", version = "0.0.1" }
lyra-ecs = { path = "../../lyra-ecs"} #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"

View File

@ -1,12 +1,10 @@
use std::ops::Deref; use std::{ops::Deref, ptr::NonNull};
use lyra_ecs::{Component, world::World};
use lyra_engine::{ use lyra_engine::{
ecs::{components::{camera::CameraComponent, DeltaTime}, EventQueue, SimpleSystem},
game::Game, game::Game,
input::{InputButtons, KeyCode, MouseMotion}, input::{InputButtons, KeyCode, MouseMotion},
math::{Quat, Vec3, EulerRot}, math::{Quat, Vec3, EulerRot},
plugin::Plugin, plugin::Plugin, ecs::{system::System, world::World, Access, Component}, DeltaTime, EventQueue, scene::CameraComponent,
}; };
#[derive(Clone, Component)] #[derive(Clone, Component)]
@ -44,14 +42,15 @@ impl FreeFlyCamera {
pub struct FreeFlyCameraPlugin; pub struct FreeFlyCameraPlugin;
impl SimpleSystem for FreeFlyCameraPlugin { impl System for FreeFlyCameraPlugin {
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { fn execute(&mut self, mut world: NonNull<World>) -> anyhow::Result<()> {
let world = unsafe { world.as_mut() };
let mut camera_rot = Vec3::default(); let mut camera_rot = Vec3::default();
let delta_time = **world.get_resource::<DeltaTime>(); let delta_time = **world.get_resource::<DeltaTime>();
let events = world let events = world
.try_get_resource_mut::<EventQueue>() .try_get_resource::<EventQueue>()
.and_then(|q| q.read_events::<MouseMotion>()); .and_then(|q| q.read_events::<MouseMotion>());
let keys = world let keys = world
@ -153,6 +152,10 @@ impl SimpleSystem for FreeFlyCameraPlugin {
Ok(()) Ok(())
} }
fn world_access(&self) -> lyra_engine::ecs::Access {
Access::Write
}
} }
impl Plugin for FreeFlyCameraPlugin { impl Plugin for FreeFlyCameraPlugin {

View File

@ -1,4 +1,6 @@
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 std::ptr::NonNull;
use lyra_engine::{math::{self, Vec3}, 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, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{TransformComponent, ModelComponent, CameraComponent}};
use lyra_engine::assets::{ResourceManager, Model}; use lyra_engine::assets::{ResourceManager, Model};
mod free_fly_camera; mod free_fly_camera;
@ -39,7 +41,8 @@ impl FixedTimestep {
} }
impl Criteria for FixedTimestep { impl Criteria for FixedTimestep {
fn can_run(&mut self, world: &mut World, check_count: u32) -> CriteriaSchedule { fn can_run(&mut self, mut world: NonNull<World>, check_count: u32) -> CriteriaSchedule {
let world = unsafe { world.as_mut() };
if check_count == 0 { if check_count == 0 {
let delta_time = world.get_resource::<DeltaTime>(); let delta_time = world.get_resource::<DeltaTime>();
self.accumulator += **delta_time; self.accumulator += **delta_time;
@ -218,7 +221,7 @@ async fn main() {
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.into_system());
//sys.with_system(fps_system); //sys.with_system(fps_system);
game.with_system("fixed", sys, &[]); game.with_system("fixed", sys, &[]);
@ -263,7 +266,7 @@ async fn main() {
Game::initialize().await Game::initialize().await
.with_plugin(lyra_engine::DefaultPlugins) .with_plugin(lyra_engine::DefaultPlugins)
.with_startup_system(setup_sys) .with_startup_system(setup_sys.into_system())
.with_plugin(action_handler_plugin) .with_plugin(action_handler_plugin)
//.with_plugin(fps_plugin) //.with_plugin(fps_plugin)
.with_plugin(jiggle_plugin) .with_plugin(jiggle_plugin)

View File

@ -1,21 +1,16 @@
use quote::quote; use quote::quote;
use syn::{parse_macro_input, DeriveInput}; use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(Component)] #[proc_macro_derive(Component)]
pub fn derive_component(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn derive_component(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
//let generics = input.generics.clone()
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let type_ident = &input.ident; let type_ident = &input.ident;
//type_ident.span().
let type_name = type_ident.to_string(); let type_name = type_ident.to_string();
proc_macro::TokenStream::from(quote! { proc_macro::TokenStream::from(quote! {
impl #impl_generics ::lyra_ecs::Component for #type_ident #ty_generics #where_clause { impl #impl_generics lyra_engine::ecs::Component for #type_ident #ty_generics #where_clause {
fn name() -> &'static str { fn name() -> &'static str {
#type_name #type_name
} }

View File

@ -3,31 +3,37 @@ use crate::world::World;
extern crate self as lyra_ecs; extern crate self as lyra_ecs;
//mod lyra_ecs { pub use super::*; } //mod lyra_ecs { pub use super::*; }
mod archetype; pub(crate) mod lyra_engine {
pub(crate) mod ecs {
pub use super::super::*;
}
}
pub mod archetype;
pub use archetype::*; pub use archetype::*;
pub mod world; pub mod world;
pub use world::*; pub use world::*;
mod bundle; pub mod bundle;
pub use bundle::*; pub use bundle::*;
mod component; pub mod component;
pub use component::*; pub use component::*;
mod query; pub mod query;
pub use query::*; //pub use query::*;
mod component_info; pub mod component_info;
pub use component_info::*; pub use component_info::*;
mod resource; pub mod resource;
pub use resource::*; pub use resource::*;
mod system; pub mod system;
pub use system::*; //pub use system::*;
mod tick; pub mod tick;
pub use tick::*; pub use tick::*;
pub use lyra_ecs_derive::*; pub use lyra_ecs_derive::*;

View File

@ -208,7 +208,7 @@ impl<T: 'static> AsQuery for &mut T {
mod tests { mod tests {
use std::{any::TypeId, mem::size_of, marker::PhantomData, ptr::NonNull}; 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, Tick}; use crate::{world::{World, Entity, EntityId}, archetype::{Archetype, ArchetypeId}, query::{View, Fetch}, tests::Vec2, bundle::Bundle, DynTypeId, Tick};
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut}; use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};

View File

@ -1,10 +1,12 @@
use std::{ptr::NonNull, cell::Ref}; use std::{ptr::NonNull, cell::Ref};
use crate::{Fetch, world::World, Query, ComponentColumn, ComponentInfo}; use crate::{world::World, ComponentColumn, ComponentInfo};
pub mod view; pub mod view;
pub use view::*; pub use view::*;
use super::Fetch;
/// Data that rust does not know the type of /// Data that rust does not know the type of
pub struct DynamicType { pub struct DynamicType {
pub info: ComponentInfo, pub info: ComponentInfo,

View File

@ -1,6 +1,6 @@
use std::ops::Range; use std::ops::Range;
use crate::{world::World, Archetype, ArchetypeEntityId, Fetch, ArchetypeId}; use crate::{world::World, Archetype, ArchetypeEntityId, ArchetypeId, query::Fetch};
use super::{QueryDynamicType, FetchDynamicType, DynamicType}; use super::{QueryDynamicType, FetchDynamicType, DynamicType};
@ -104,7 +104,7 @@ impl<'a> Iterator for DynamicViewIter<'a> {
mod tests { mod tests {
use std::{alloc::Layout, ptr::NonNull}; use std::{alloc::Layout, ptr::NonNull};
use crate::{world::World, MemoryLayout, ComponentInfo, DynTypeId, DynamicBundle, dynamic::QueryDynamicType}; use crate::{world::World, MemoryLayout, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType};
use super::DynamicView; use super::DynamicView;

View File

@ -1,6 +1,6 @@
use crate::{world::{Entity, World}, archetype::{Archetype, ArchetypeId}, AsQuery}; use crate::{world::{Entity, World}, archetype::{Archetype, ArchetypeId}};
use super::{Fetch, Query}; use super::{Fetch, Query, AsQuery};
pub struct EntitiesFetch { pub struct EntitiesFetch {
entities: Vec<Entity>, entities: Vec<Entity>,

View File

@ -152,7 +152,7 @@ impl<R: ResourceObject> AsQuery for QueryResourceMut<R> {
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{world::World, tests::{Vec2, Vec3}, QueryResourceMut}; use crate::{world::World, tests::{Vec2, Vec3}, query::QueryResourceMut};
use super::QueryResource; use super::QueryResource;

View File

@ -1,6 +1,8 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::{ComponentColumn, Fetch, Tick, DynTypeId, Query, world::World, AsQuery}; use crate::{ComponentColumn, Tick, DynTypeId, world::World};
use super::{Query, Fetch, AsQuery};
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct TickOf<T> { pub struct TickOf<T> {

View File

@ -1,12 +1,14 @@
use lyra_ecs::world::World; use lyra_ecs::world::World;
use super::{SimpleSystem, Criteria}; use crate::Access;
use super::{System, Criteria};
/// 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
/// executed. /// executed.
pub struct BatchedSystem { pub struct BatchedSystem {
systems: Vec<Box<dyn SimpleSystem>>, systems: Vec<Box<dyn System>>,
criteria: Vec<Box<dyn Criteria>>, criteria: Vec<Box<dyn Criteria>>,
criteria_checks: u32, criteria_checks: u32,
} }
@ -23,7 +25,7 @@ impl BatchedSystem {
pub fn with_system<S>(&mut self, system: S) -> &mut Self pub fn with_system<S>(&mut self, system: S) -> &mut Self
where where
S: SimpleSystem + 'static S: System + 'static
{ {
self.systems.push(Box::new(system)); self.systems.push(Box::new(system));
self self
@ -38,8 +40,16 @@ impl BatchedSystem {
} }
} }
impl SimpleSystem for BatchedSystem { impl System for BatchedSystem {
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { fn world_access(&self) -> crate::Access {
if self.systems.iter().any(|s| s.world_access() == Access::Write) {
Access::Write
} else {
Access::Read
}
}
fn execute(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
let mut can_run = true; let mut can_run = true;
let mut check_again = false; let mut check_again = false;
@ -60,13 +70,13 @@ impl SimpleSystem for BatchedSystem {
if can_run { if can_run {
for system in self.systems.iter_mut() { for system in self.systems.iter_mut() {
system.execute_mut(world)?; system.execute(world)?;
} }
} }
if check_again { if check_again {
self.criteria_checks += 1; self.criteria_checks += 1;
self.execute_mut(world)?; self.execute(world)?;
} }
self.criteria_checks = 0; self.criteria_checks = 0;

View File

@ -1,5 +1,8 @@
use std::ptr::NonNull;
use lyra_ecs::world::World; use lyra_ecs::world::World;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum CriteriaSchedule { pub enum CriteriaSchedule {
Yes, Yes,
No, No,
@ -13,13 +16,14 @@ 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 World, check_count: u32) -> CriteriaSchedule; fn can_run(&mut self, world: NonNull<World>, check_count: u32) -> CriteriaSchedule;
} }
impl<F> Criteria for F impl<F> Criteria for F
where F: FnMut(&mut World, u32) -> CriteriaSchedule where F: FnMut(&mut World, u32) -> CriteriaSchedule
{ {
fn can_run(&mut self, world: &mut World, check_count: u32) -> CriteriaSchedule { fn can_run(&mut self, mut world: NonNull<World>, check_count: u32) -> CriteriaSchedule {
self(world, check_count) let world_mut = unsafe { world.as_mut() };
self(world_mut, check_count)
} }
} }

View File

@ -1,6 +1,8 @@
use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull}; use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull};
use crate::{System, world::World}; use super::System;
use crate::world::World;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum GraphExecutorError { pub enum GraphExecutorError {
@ -55,8 +57,7 @@ impl GraphExecutor {
} }
/// Executes the systems in the graph /// Executes the systems in the graph
pub fn execute(&mut self, world: &World, stop_on_error: bool) -> Result<Vec<GraphExecutorError>, GraphExecutorError> { pub fn execute(&mut self, world: NonNull<World>, stop_on_error: bool) -> Result<Vec<GraphExecutorError>, GraphExecutorError> {
let world = NonNull::from(world);
let mut stack = VecDeque::new(); let mut stack = VecDeque::new();
let mut visited = HashSet::new(); let mut visited = HashSet::new();
@ -106,7 +107,9 @@ impl GraphExecutor {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{View, QueryBorrow, tests::Vec2, IntoSystem, world::World, Resource, ResourceMut}; use std::ptr::NonNull;
use crate::{world::World, query::{ResourceMut, View}, system::IntoSystem};
use super::GraphExecutor; use super::GraphExecutor;
@ -150,7 +153,7 @@ mod tests {
exec.insert_system("a", a_system.into_system(), &[]); exec.insert_system("a", a_system.into_system(), &[]);
exec.insert_system("b", b_system.into_system(), &["a"]); exec.insert_system("b", b_system.into_system(), &["a"]);
exec.execute(&world, true).unwrap(); exec.execute(NonNull::from(&world), true).unwrap();
println!("Executed systems"); println!("Executed systems");
let order = world.get_resource::<Vec<String>>(); let order = world.get_resource::<Vec<String>>();

View File

@ -1,16 +1,30 @@
use std::{ptr::NonNull, marker::PhantomData, cell::{Ref, RefMut}}; use std::{ptr::NonNull, marker::PhantomData, cell::{Ref, RefMut}};
use crate::{world::World, View, Query, Access, ResourceObject}; use crate::{world::World, Access, ResourceObject, query::{Query, View}};
pub mod graph; pub mod graph;
pub use graph::*;
pub mod criteria;
pub use criteria::*;
pub mod batched;
pub use batched::*;
/// A system that does not mutate the world /// A system that does not mutate the world
pub trait System { pub trait System {
/// A method that indicates the type of access of the world the system requires.
fn world_access(&self) -> Access; fn world_access(&self) -> Access;
/// The actual execution of the system.
fn execute(&mut self, world: NonNull<World>) -> anyhow::Result<()>; fn execute(&mut self, world: NonNull<World>) -> anyhow::Result<()>;
/// A setup step of the System, called before `execute` ever runs.
fn setup(&self, world: NonNull<World>) -> anyhow::Result<()> {
Ok(())
}
} }
/// A trait for converting something into a system.
pub trait IntoSystem<T> { pub trait IntoSystem<T> {
type System: System; type System: System;
@ -61,6 +75,21 @@ macro_rules! impl_fn_system_tuple {
} }
} }
/* impl<F, $($name: FnArg,)+> IntoSystem for F
where
F: FnMut($($name,)+) -> anyhow::Result<()>,
F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>,
{
type System = FnSystem<F, ($($name::Fetcher,)+)>;
fn into_system(self) -> Self::System {
FnSystem {
args: ($($name::Fetcher::new(),)+),
inner: self
}
}
} */
impl<F, $($name: FnArg,)+> IntoSystem<($($name,)+)> for F impl<F, $($name: FnArg,)+> IntoSystem<($($name,)+)> for F
where where
F: FnMut($($name,)+) -> anyhow::Result<()>, F: FnMut($($name,)+) -> anyhow::Result<()>,
@ -223,7 +252,7 @@ impl<R: ResourceObject> FnArgFetcher for ResourceMutArgFetcher<R> {
mod tests { mod tests {
use std::{ptr::NonNull, cell::RefMut}; use std::{ptr::NonNull, cell::RefMut};
use crate::{tests::{Vec2, Vec3}, View, QueryBorrow, world::World}; use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, View}};
use super::{System, IntoSystem}; use super::{System, IntoSystem};
struct SomeCounter(u32); struct SomeCounter(u32);
@ -311,6 +340,35 @@ mod tests {
assert_eq!(counter.0, 20); assert_eq!(counter.0, 20);
} }
#[test]
fn function_system() {
let mut world = World::new();
world.spawn((Vec2::rand(), Vec3::rand()));
world.spawn((Vec2::rand(), Vec3::rand()));
world.add_resource(SomeCounter(0));
let test_system = |world: &World| -> anyhow::Result<()> {
let mut counter = world.get_resource_mut::<SomeCounter>();
counter.0 += 10;
Ok(())
};
test_system.into_system().execute(NonNull::from(&world)).unwrap();
fn test_system(world: &mut World) -> anyhow::Result<()> {
let mut counter = world.get_resource_mut::<SomeCounter>();
counter.0 += 10;
Ok(())
}
test_system.into_system().execute(NonNull::from(&world)).unwrap();
let counter = world.get_resource::<SomeCounter>();
assert_eq!(counter.0, 20);
}
#[test] #[test]
fn resource_system() { fn resource_system() {
let mut world = World::new(); let mut world = World::new();

View File

@ -1,15 +1,23 @@
use lyra_ecs_derive::Component;
use rand::Rng; use rand::Rng;
use crate::Component;
use crate::lyra_engine;
//use crate::lyra_ecs; //use crate::lyra_ecs;
/// This source file includes some common things that tests are using. /// This source file includes some common things that tests are using.
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd, Component)] #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
pub struct Vec2 { pub struct Vec2 {
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,
} }
impl Component for Vec2 {
fn name() -> &'static str {
"Vec2"
}
}
impl Vec2 { impl Vec2 {
pub fn new(x: f32, y: f32) -> Self { pub fn new(x: f32, y: f32) -> Self {
Self { Self {

View File

@ -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, TickTracker, Tick}; use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, component::Component, query::{Query, ViewIter, View, AsQuery}, resource::ResourceData, query::{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);
@ -300,7 +300,7 @@ impl World {
mod tests { mod tests {
use std::ops::Deref; use std::ops::Deref;
use crate::{tests::{Vec2, Vec3}, TickOf}; use crate::{tests::{Vec2, Vec3}, query::TickOf};
use super::World; use super::World;

View File

@ -1,7 +1,7 @@
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
use instant::Instant; use instant::Instant;
use lyra_ecs::{Component, world::World}; use lyra_ecs::{Component, world::World, system::IntoSystem};
use crate::plugin::Plugin; use crate::plugin::Plugin;
@ -36,6 +36,6 @@ 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().add_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.into_system(), &[]);
} }
} }

View File

@ -1,9 +0,0 @@
pub use lyra_ecs;
pub mod components;
pub mod events;
pub use events::*;
pub mod system;
pub use system::*;

View File

@ -1,127 +0,0 @@
pub mod batched;
pub use batched::*;
pub mod criteria;
pub use criteria::*;
use lyra_ecs::world::World;
use std::collections::HashMap;
use petgraph::{stable_graph::{StableDiGraph, NodeIndex}, visit::Topo};
use tracing::warn;
/// A trait that represents a simple system
pub trait SimpleSystem {
fn setup(&mut self, _world: &mut World) -> anyhow::Result<()> {
Ok(())
}
// todo: make async?
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()>;
}
impl<S> SimpleSystem for S
where S: FnMut(&mut World) -> anyhow::Result<()>
{
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
self(world)
}
}
struct SystemGraphNode {
name: String,
#[allow(dead_code)]
dependent_on: Vec<String>, // TODO
system: Box<dyn SimpleSystem>,
}
struct SystemGraphEdge {
}
/// Dispatcher for multiple systems.
///
/// This struct uses a graph for finding executing systems with dependencies.
/// At some point this will be multithreaded.
pub struct SystemDispatcher {
graph: StableDiGraph<SystemGraphNode, SystemGraphEdge>,
node_refs: HashMap<String, NodeIndex>,
}
impl Default for SystemDispatcher {
fn default() -> Self {
Self {
graph: StableDiGraph::new(),
node_refs: HashMap::new(),
}
}
}
impl SystemDispatcher {
pub fn new() -> Self {
Self::default()
}
/// Adds a SimpleSystem to the SystemGraph.
///
/// WARN: Ensure that no cycles are created, this will cause the systems graph to not be sortable by topological sort,
/// and will cause a panic later on in the engine.
///
/// TODO: Return false if a cycle was created, making it impossible to know when to dispatch them. This will mean that the System would not of had been added.
pub fn add_system<S>(&mut self, name: &str, system: S, dependencies: &[&str]) -> bool
where
S: SimpleSystem + 'static
{
let name = name.to_string();
let dependencies: Vec<String> = dependencies.iter().map(|s| s.to_string()).collect();
let system = Box::new(system) as Box<dyn SimpleSystem>;
let added_index =
self.graph.add_node(SystemGraphNode {
name: name.to_string(),
dependent_on: dependencies.clone(),
system,
});
// store name ref to the graph node
self.node_refs.insert(name, added_index);
// find the dependencies in node_refs and add edges directed towards the new node
for depend in dependencies.into_iter() {
let idx = self.node_refs
.get(&depend)
.expect("Dependency not found!");
self.graph.add_edge(*idx, added_index, SystemGraphEdge { });
}
true
}
pub(crate) fn execute_systems(&mut self, world: &mut World) {
let mut topo = Topo::new(&self.graph);
while let Some(nx) = topo.next(&self.graph) {
let node = self.graph.node_weight_mut(nx).unwrap();
match node.system.execute_mut(world) {
Ok(()) => {},
Err(e) => {
warn!("System execution of {} resulted in an error! '{}'.", node.name, e);
// TODO: Find some way to stop traversing down a topopath in the graph executor and continue down a different one.
// This might require a custom topopath implementation (preferably iterative). It would have to ensure that it
// doesn't run other systems that are dependent on that failed system.
return;
}
}
}
}
}
impl SimpleSystem for SystemDispatcher {
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> {
self.execute_systems(world);
Ok(())
}
}

View File

@ -1,8 +1,8 @@
use std::{sync::Arc, collections::VecDeque}; use std::{sync::Arc, collections::VecDeque, ptr::NonNull};
use async_std::task::block_on; use async_std::task::block_on;
use lyra_ecs::world::World; use lyra_ecs::{world::World, system::{GraphExecutor, System}};
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::{
@ -13,7 +13,7 @@ use tracing_subscriber::{
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, DeviceEvent}, event_loop::{EventLoop, ControlFlow}}; use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, DeviceEvent}, event_loop::{EventLoop, ControlFlow}};
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, plugin::Plugin, change_tracker::Ct, EventQueue};
pub struct Controls<'a> { pub struct Controls<'a> {
pub world: &'a mut World, pub world: &'a mut World,
@ -37,18 +37,18 @@ struct GameLoop {
world: World, world: World,
/// higher priority systems /// higher priority systems
engine_sys_dispatcher: SystemDispatcher, engine_sys_dispatcher: GraphExecutor,
user_sys_dispatcher: SystemDispatcher, user_sys_dispatcher: GraphExecutor,
} }
impl GameLoop { impl GameLoop {
pub async fn new(window: Arc<Window>, world: World, user_systems: SystemDispatcher) -> GameLoop { pub async fn new(window: Arc<Window>, world: World, user_systems: GraphExecutor) -> 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),
world, world,
engine_sys_dispatcher: SystemDispatcher::new(), engine_sys_dispatcher: GraphExecutor::new(),
user_sys_dispatcher: user_systems, user_sys_dispatcher: user_systems,
} }
} }
@ -67,11 +67,12 @@ impl GameLoop {
} }
async fn update(&mut self) { async fn update(&mut self) {
if let Err(e) = self.engine_sys_dispatcher.execute_mut(&mut self.world) { let world_ptr = NonNull::from(&self.world);
if let Err(e) = self.engine_sys_dispatcher.execute(world_ptr, true) {
error!("Error when executing engine ecs systems: '{}'", e); error!("Error when executing engine ecs systems: '{}'", e);
} }
if let Err(e) = self.user_sys_dispatcher.execute_mut(&mut self.world) { if let Err(e) = self.user_sys_dispatcher.execute(world_ptr, true) {
error!("Error when executing user ecs systems: '{}'", e); error!("Error when executing user ecs systems: '{}'", e);
} }
} }
@ -205,8 +206,8 @@ impl GameLoop {
pub struct Game { pub struct Game {
world: Option<World>, world: Option<World>,
plugins: VecDeque<Box<dyn Plugin>>, plugins: VecDeque<Box<dyn Plugin>>,
system_dispatcher: Option<SystemDispatcher>, system_dispatcher: Option<GraphExecutor>,
startup_systems: VecDeque<Box<dyn SimpleSystem>>, startup_systems: VecDeque<Box<dyn System>>,
} }
impl Default for Game { impl Default for Game {
@ -214,7 +215,7 @@ impl Default for Game {
Self { Self {
world: Some(World::new()), world: Some(World::new()),
plugins: VecDeque::new(), plugins: VecDeque::new(),
system_dispatcher: Some(SystemDispatcher::new()), system_dispatcher: Some(GraphExecutor::new()),
startup_systems: VecDeque::new(), startup_systems: VecDeque::new(),
} }
} }
@ -234,10 +235,10 @@ impl Game {
/// Add a system to the ecs world /// Add a system to the ecs world
pub fn with_system<S>(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self pub fn with_system<S>(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self
where where
S: SimpleSystem + 'static S: System + 'static
{ {
let system_dispatcher = self.system_dispatcher.as_mut().unwrap(); let system_dispatcher = self.system_dispatcher.as_mut().unwrap();
system_dispatcher.add_system(name, system, depends); system_dispatcher.insert_system(name, system, depends);
self self
} }
@ -246,7 +247,7 @@ impl Game {
/// They will only be ran once /// They will only be ran once
pub fn with_startup_system<S>(&mut self, system: S) -> &mut Self pub fn with_startup_system<S>(&mut self, system: S) -> &mut Self
where where
S: SimpleSystem + 'static S: System + 'static
{ {
self.startup_systems.push_back(Box::new(system)); self.startup_systems.push_back(Box::new(system));
@ -294,8 +295,9 @@ impl Game {
// run startup systems // run startup systems
while let Some(mut startup) = self.startup_systems.pop_front() { while let Some(mut startup) = self.startup_systems.pop_front() {
let startup = startup.as_mut(); let startup = startup.as_mut();
startup.setup(&mut world).expect("World returned an error!"); let world_ptr = NonNull::from(&world);
startup.execute_mut(&mut world).expect("World returned an error!"); startup.setup(world_ptr).expect("World returned an error!");
startup.execute(world_ptr).expect("World returned an error!");
} }
// start winit event loops // start winit event loops

View File

@ -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 lyra_ecs::world::World; use lyra_ecs::{world::World, system::IntoSystem};
use crate::{castable_any::CastableAny, plugin::Plugin}; use crate::{castable_any::CastableAny, plugin::Plugin};
@ -424,6 +424,6 @@ pub struct InputActionPlugin;
impl Plugin for InputActionPlugin { impl Plugin for InputActionPlugin {
fn setup(&self, game: &mut crate::game::Game) { fn setup(&self, game: &mut crate::game::Game) {
game.with_system("input_actions", actions_system, &[]); game.with_system("input_actions", actions_system.into_system(), &[]);
} }
} }

View File

@ -1,8 +1,10 @@
use std::ptr::NonNull;
use glam::Vec2; use glam::Vec2;
use lyra_ecs::world::World; use lyra_ecs::world::World;
use winit::event::MouseScrollDelta; use winit::event::MouseScrollDelta;
use crate::{ecs::{SimpleSystem, EventQueue}, plugin::Plugin,}; use crate::{EventQueue, plugin::Plugin};
use super::{events::*, InputButtons, InputEvent}; use super::{events::*, InputButtons, InputEvent};
@ -96,8 +98,9 @@ impl InputSystem {
} }
} }
impl SimpleSystem for InputSystem { impl crate::ecs::system::System for InputSystem {
fn execute_mut(&mut self, world: &mut World) -> anyhow::Result<()> { fn execute(&mut self, mut world: NonNull<World>) -> anyhow::Result<()> {
let world = unsafe { world.as_mut() };
let queue = world.try_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>());
@ -113,6 +116,10 @@ impl SimpleSystem for InputSystem {
Ok(()) Ok(())
} }
fn world_access(&self) -> lyra_ecs::Access {
lyra_ecs::Access::Write
}
} }
/// Plugin that runs InputSystem /// Plugin that runs InputSystem

View File

@ -2,16 +2,26 @@
#![feature(lint_reasons)] #![feature(lint_reasons)]
#![feature(trait_alias)] #![feature(trait_alias)]
extern crate self as lyra_engine;
pub mod game; pub mod game;
pub mod render; pub mod render;
pub mod resources; pub mod resources;
pub mod ecs;
pub mod math; pub mod math;
pub mod input; pub mod input;
pub mod castable_any; pub mod castable_any;
pub mod plugin; pub mod plugin;
pub mod change_tracker; pub mod change_tracker;
pub mod events;
pub use events::*;
pub mod delta_time;
pub use delta_time::*;
pub mod scene;
pub use lyra_resource as assets; pub use lyra_resource as assets;
pub use lyra_ecs as ecs;
pub use plugin::DefaultPlugins; pub use plugin::DefaultPlugins;

View File

@ -1,7 +1,7 @@
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
use crate::ecs::EventsPlugin; use crate::EventsPlugin;
use crate::ecs::components::DeltaTimePlugin; use crate::DeltaTimePlugin;
use crate::game::Game; use crate::game::Game;
use crate::input::InputPlugin; use crate::input::InputPlugin;
use crate::render::window::WindowPlugin; use crate::render::window::WindowPlugin;

View File

@ -1,6 +1,6 @@
use winit::dpi::PhysicalSize; use winit::dpi::PhysicalSize;
use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX}, ecs::components::camera::CameraComponent}; use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX}, scene::CameraComponent};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum CameraProjectionMode { pub enum CameraProjectionMode {

View File

@ -2,7 +2,7 @@ pub mod point;
pub mod directional; pub mod directional;
pub mod spotlight; pub mod spotlight;
use lyra_ecs::{Entity, Tick, world::World, Entities, TickOf}; use lyra_ecs::{Entity, Tick, world::World, query::{Entities, TickOf}};
pub use point::*; pub use point::*;
pub use directional::*; pub use directional::*;
pub use spotlight::*; pub use spotlight::*;
@ -15,7 +15,7 @@ use wgpu::util::DeviceExt;
use std::mem; use std::mem;
use crate::{math::Transform, ecs::components::TransformComponent}; use crate::{math::Transform, scene::TransformComponent};
use self::directional::DirectionalLight; use self::directional::DirectionalLight;

View File

@ -7,20 +7,19 @@ use std::borrow::Cow;
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::Entity;
use lyra_ecs::query::{Entities, TickOf};
use lyra_ecs::world::World; 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;
use winit::window::Window; use winit::window::Window;
use crate::ecs::components::camera::CameraComponent;
use crate::ecs::components::model::ModelComponent;
use crate::ecs::components::transform::TransformComponent;
use crate::math::{Transform, self}; use crate::math::{Transform, self};
use crate::render::light::PointLightUniform; use crate::render::light::PointLightUniform;
use crate::render::material::MaterialUniform; use crate::render::material::MaterialUniform;
use crate::render::render_buffer::{BufferWrapperBuilder, BindGroupPair}; use crate::render::render_buffer::{BufferWrapperBuilder, BindGroupPair};
use crate::scene::{ModelComponent, TransformComponent, CameraComponent};
use super::camera::{RenderCamera, CameraUniform}; use super::camera::{RenderCamera, CameraUniform};
use super::desc_buf_lay::DescVertexBufferLayout; use super::desc_buf_lay::DescVertexBufferLayout;

View File

@ -1,13 +1,13 @@
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 lyra_ecs::{world::World, system::IntoSystem};
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};
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel}; pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
use crate::{plugin::Plugin, change_tracker::Ct, ecs::EventQueue, input::InputEvent}; use crate::{plugin::Plugin, change_tracker::Ct, input::InputEvent, EventQueue};
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub enum WindowMode { pub enum WindowMode {
@ -374,6 +374,6 @@ impl Plugin for WindowPlugin {
let window_options = WindowOptions::default(); let window_options = WindowOptions::default();
game.world().add_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.into_system(), &[]);
} }
} }

5
src/ecs/components/mod.rs → src/scene/mod.rs Executable file → Normal file
View File

@ -11,7 +11,4 @@ pub mod camera;
pub use camera::*; pub use camera::*;
pub mod free_fly_camera; pub mod free_fly_camera;
pub use free_fly_camera::*; pub use free_fly_camera::*;
pub mod delta_time;
pub use delta_time::*;