diff --git a/Cargo.lock b/Cargo.lock index a87856e..1aeda0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1795,6 +1795,7 @@ dependencies = [ "lyra-math", "lyra-reflect", "lyra-resource", + "lyra-scene", "quote", "syn 2.0.51", "thiserror", diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 372ff32..88548b0 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -1,6 +1,6 @@ -use std::ptr::NonNull; +use std::{ptr::NonNull, thread, time::Duration}; -use lyra_engine::{assets::gltf::Gltf, change_tracker::Ct, ecs::{query::{QueryBorrow, Res, View, ViewState}, system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem}, Component, World}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::{light::{directional::DirectionalLight, PointLight, SpotLight}, window::{CursorGrabMode, WindowOptions}}, scene::{CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime}; +use lyra_engine::{assets::gltf::Gltf, change_tracker::Ct, ecs::{query::{Res, View}, system::{Criteria, CriteriaSchedule, IntoSystem}, Component, World}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::{light::{directional::DirectionalLight, PointLight, SpotLight}, window::{CursorGrabMode, WindowOptions}}, scene::{CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime}; use lyra_engine::assets::ResourceManager; struct FixedTimestep { @@ -79,7 +79,8 @@ async fn main() { let separate_gltf = resman.request::("assets/pos-testing/child-node-cubes.glb").unwrap(); */ //drop(resman); - cube_gltf.wait_recurse_dependencies_load(); + //cube_gltf.wait_recurse_dependencies_load(); + thread::sleep(Duration::from_millis(500)); let cube_mesh = &cube_gltf.data_ref() .unwrap().meshes[0]; /* let crate_mesh = &crate_gltf.data_ref() @@ -91,7 +92,8 @@ async fn main() { let sponza_model = resman.request::("../assets/sponza/Sponza.gltf").unwrap(); drop(resman); - sponza_model.wait_recurse_dependencies_load(); + //sponza_model.wait_recurse_dependencies_load(); + thread::sleep(Duration::from_millis(10000)); let sponza_scene = &sponza_model.data_ref() .unwrap().scenes[0]; diff --git a/lyra-game/Cargo.toml b/lyra-game/Cargo.toml index 070fc85..f270e09 100644 --- a/lyra-game/Cargo.toml +++ b/lyra-game/Cargo.toml @@ -8,6 +8,7 @@ lyra-resource = { path = "../lyra-resource" } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] } lyra-math = { path = "../lyra-math" } +lyra-scene = { path = "../lyra-scene" } winit = "0.28.1" tracing = "0.1.37" diff --git a/lyra-game/src/castable_any.rs b/lyra-game/src/as_any.rs similarity index 62% rename from lyra-game/src/castable_any.rs rename to lyra-game/src/as_any.rs index f1d4727..2043286 100644 --- a/lyra-game/src/castable_any.rs +++ b/lyra-game/src/as_any.rs @@ -1,12 +1,13 @@ use std::any::Any; -pub trait CastableAny: Send + Sync + 'static { +/// A trait that is implemented for types that are `Send + Sync + 'static`. +pub trait AsSyncAny: Send + Sync + 'static { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; } /// Implements this trait for anything that fits the type bounds -impl CastableAny for T { +impl AsSyncAny for T { fn as_any(&self) -> &dyn Any { self } diff --git a/lyra-game/src/events.rs b/lyra-game/src/events.rs index 11f538a..94eea9e 100644 --- a/lyra-game/src/events.rs +++ b/lyra-game/src/events.rs @@ -1,15 +1,82 @@ -use std::{collections::{HashMap, VecDeque}, any::{TypeId, Any}, cell::RefCell}; +use std::{any::{Any, TypeId}, collections::{HashMap, VecDeque}, marker::PhantomData, mem::{self, MaybeUninit}, ops::Range}; -use crate::{castable_any::CastableAny, plugin::Plugin}; +use crate::plugin::Plugin; pub trait Event: Clone + Send + Sync + 'static {} impl Event for T {} pub type Events = VecDeque; +#[derive(Clone)] +struct TypeErasedBuffer { + type_id: TypeId, + item_size: usize, + data: Vec>, + fn_drop_item: fn(item: *mut ()), +} + +impl Drop for TypeErasedBuffer { + fn drop(&mut self) { + let range = self.data.as_mut_ptr_range(); + let mut current = range.start; + let end = range.end; + + while current < end { + // SAFETY: current pointer will either be the start of the buffer, or at the start of + // the next item + (self.fn_drop_item)(current.cast()); + + // SAFETY: the items are placed right after eachother + current = unsafe { current.add(self.item_size) }; + } + + // Safety: all of the events were just dropped + unsafe { self.data.set_len(0) }; + } +} + +impl TypeErasedBuffer { + pub fn new() -> Self { + Self { + type_id: TypeId::of::(), + item_size: mem::size_of::(), + data: Default::default(), + // dropped immediately after the read + fn_drop_item: |item| unsafe { item.cast::().drop_in_place() }, + } + } + + pub fn push(&mut self, value: T) { + debug_assert!(TypeId::of::() == self.type_id, "The type of the buffer does not match the type that was added to it"); + + // reserve enough bytes to store T + let old_len = self.data.len(); + self.data.reserve(mem::size_of::()); + + // Get a pointer to the end of the data. + // SAFETY: we just reserved enough memory to store the data. + let end_ptr = unsafe { self.data.as_mut_ptr().add(old_len) }; + + unsafe { + // write the command and its runner into the buffer + end_ptr.cast::() + // written unaligned to keep everything packed + .write_unaligned(value); + + // we wrote to the vec's buffer without using its api, so we need manually + // set the length of the vec. + self.data.set_len(old_len + mem::size_of::()); + } + } + + pub fn len(&self) -> usize { + self.data.len() / self.item_size + } +} + pub struct EventQueue { - events: HashMap>>, - event_write_queue: HashMap>> + events: HashMap, + event_write_queue: HashMap, } impl Default for EventQueue { @@ -32,7 +99,7 @@ impl EventQueue { E: Event { // the compiler wants me to explicit right here for some reason - let default = || RefCell::new(Box::>::default() as Box); + /* let default = || RefCell::new(Box::>::default() as Box); // Get, or create, a list of events of this type let type_id = event.type_id(); @@ -42,7 +109,13 @@ impl EventQueue { // downcast resource as an events storage let e: &mut Events = events.as_mut().as_any_mut().downcast_mut().unwrap(); - e.push_back(event); + e.push_back(event); */ + + let type_id = event.type_id(); + let events = self.event_write_queue.entry(type_id) + .or_insert_with(|| TypeErasedBuffer::new::()); + + events.push(event); } // Clear events, this should happen at the start of every tick since events are cloned @@ -60,13 +133,55 @@ impl EventQueue { } } - pub fn read_events(&self) -> Option> + pub fn read_events(&self) -> Option> where E: Event { - if let Some(event ) = self.events.get(&TypeId::of::()) { - let eref = event.borrow(); - Some(eref.as_ref().as_any().downcast_ref::>().unwrap().clone()) + self.events.get(&TypeId::of::()) + .map(|event| EventReader::new(event.clone())) + } +} + +pub struct EventReader { + buf: TypeErasedBuffer, + range: Option>>, + _marker: PhantomData, +} + +impl EventReader { + fn new(buffer: TypeErasedBuffer) -> Self { + Self { + buf: buffer, + range: None, + _marker: PhantomData, + } + } + + pub fn len(&self) -> usize { + self.buf.len() + } +} + +impl Iterator for EventReader { + type Item = E; + + fn next(&mut self) -> Option { + if self.range.is_none() { + self.range = Some(self.buf.data.as_mut_ptr_range()); + } + + let range = self.range.as_mut().unwrap(); + + if range.start < range.end { + // Retrieve the event in the buffer. + // SAFETY: current pointer will either be the start of the buffer, or at the start + // of the next event. + let event = unsafe { range.start.cast::().read_unaligned() }; + + // Advance the pointer to be after this event. + range.start = unsafe { range.start.add(mem::size_of::()) }; + + Some(event) } else { None } diff --git a/lyra-game/src/input/action.rs b/lyra-game/src/input/action.rs index a99d1dc..229dcf5 100644 --- a/lyra-game/src/input/action.rs +++ b/lyra-game/src/input/action.rs @@ -521,10 +521,10 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> { } } - let motion_avg = if let Some(mut mouse_events) = mouse_events { + let motion_avg = if let Some(mouse_events) = mouse_events { let count = mouse_events.len(); let mut sum = Vec2::ZERO; - while let Some(mm) = mouse_events.pop_front() { + for mm in mouse_events { sum += mm.delta; } Some(sum / count as f32) diff --git a/lyra-game/src/input/system.rs b/lyra-game/src/input/system.rs index b41a939..1ff55cc 100755 --- a/lyra-game/src/input/system.rs +++ b/lyra-game/src/input/system.rs @@ -110,8 +110,8 @@ impl crate::ecs::system::System for InputSystem { return Ok(()); } - let mut events = queue.unwrap(); - while let Some(event) = events.pop_front() { + let events = queue.unwrap(); + for event in events { self.process_event(world, &event); } diff --git a/lyra-game/src/lib.rs b/lyra-game/src/lib.rs index 85b2922..47de0fb 100644 --- a/lyra-game/src/lib.rs +++ b/lyra-game/src/lib.rs @@ -8,7 +8,7 @@ pub mod game; pub mod render; pub mod resources; pub mod input; -pub mod castable_any; +pub mod as_any; pub mod plugin; pub mod change_tracker; diff --git a/lyra-game/src/render/renderer.rs b/lyra-game/src/render/renderer.rs index c960081..295243c 100755 --- a/lyra-game/src/render/renderer.rs +++ b/lyra-game/src/render/renderer.rs @@ -10,7 +10,7 @@ use lyra_ecs::query::filter::{Has, Or}; use lyra_ecs::{Entity, Tick}; use lyra_ecs::query::{Entities, TickOf}; use lyra_ecs::World; -use lyra_resource::gltf::GltfScene; +use lyra_scene::SceneGraph; use tracing::{debug, warn}; use uuid::Uuid; use wgpu::{BindGroupLayout, Limits}; @@ -36,7 +36,7 @@ use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, r use lyra_resource::{gltf::Mesh, ResHandle}; type MeshHandle = ResHandle; -type SceneHandle = ResHandle; +type SceneHandle = ResHandle; pub trait Renderer { fn prepare(&mut self, main_world: &mut World); @@ -465,27 +465,28 @@ impl Renderer for BasicRenderer { if let Some(scene) = scene_han.data_ref() { let interpo_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch); - let meshes = scene.collect_world_meshes(); - for (mesh_han, mesh_pos) in meshes.into_iter() { - if let Some(mesh) = mesh_han.data_ref() { - let mesh_interpo = interpo_pos + mesh_pos; - - // if process mesh did not just create a new mesh, and the epoch - // shows that the scene has changed, verify that the mesh buffers - // dont need to be resent to the gpu. - if !self.process_mesh(entity, mesh_interpo, &*mesh, mesh_han.uuid()) - && scene_epoch == last_epoch { - self.check_mesh_buffers(entity, &mesh_han); + scene.traverse_down(|sw: &World, mesh_han, pos| { + if let Some(mesh_han) = sw.view_one::<&MeshHandle>(mesh_han.entity()).get() { + if let Some(mesh) = mesh_han.data_ref() { + let mesh_interpo = interpo_pos + pos; + + // if process mesh did not just create a new mesh, and the epoch + // shows that the scene has changed, verify that the mesh buffers + // dont need to be resent to the gpu. + if !self.process_mesh(entity, mesh_interpo, &*mesh, mesh_han.uuid()) + && scene_epoch == last_epoch { + self.check_mesh_buffers(entity, &mesh_han); + } + + let material = mesh.material.as_ref().unwrap() + .data_ref().unwrap(); + let shader = material.shader_uuid.unwrap_or(0); + let job = RenderJob::new(entity, shader, mesh_han.uuid()); + self.render_jobs.push_back(job); } - - let material = mesh.material.as_ref().unwrap() - .data_ref().unwrap(); - let shader = material.shader_uuid.unwrap_or(0); - let job = RenderJob::new(entity, shader, mesh_han.uuid()); - self.render_jobs.push_back(job); } - } + }); } } } diff --git a/lyra-game/src/render/window.rs b/lyra-game/src/render/window.rs index c63c632..d7c9696 100644 --- a/lyra-game/src/render/window.rs +++ b/lyra-game/src/render/window.rs @@ -341,8 +341,8 @@ fn window_updater_system(world: &mut World) -> anyhow::Result<()> { let mut opts = world.get_resource_mut::>(); if let Some(event_queue) = world.try_get_resource_mut::() { - if let Some(mut events) = event_queue.read_events::() { - while let Some(ev) = events.pop_front() { + if let Some(events) = event_queue.read_events::() { + for ev in events { match ev { InputEvent::CursorEntered { .. } => { opts.cursor_inside_window = true; diff --git a/lyra-resource/src/gltf/loader.rs b/lyra-resource/src/gltf/loader.rs index a131df9..4fb05ba 100644 --- a/lyra-resource/src/gltf/loader.rs +++ b/lyra-resource/src/gltf/loader.rs @@ -293,7 +293,7 @@ mod tests { .data_ref().unwrap(); let mut node = None; - scene.traverse_down(|no, _tran| { + scene.traverse_down(|_, no, _tran| { node = Some(no.clone()); }); diff --git a/lyra-scene/src/lib.rs b/lyra-scene/src/lib.rs index e2ce2c0..56d1976 100644 --- a/lyra-scene/src/lib.rs +++ b/lyra-scene/src/lib.rs @@ -135,7 +135,7 @@ impl SceneGraph { /// The traversal does not include the root scene node. pub fn traverse_down(&self, mut callback: F) where - F: FnMut(&SceneNode, Transform), + F: FnMut(&World, &SceneNode, Transform), { self.traverse_down_from(self.root_node.clone(), &mut callback); } @@ -144,7 +144,7 @@ impl SceneGraph { /// SceneNode and its world transform. fn traverse_down_from(&self, start: SceneNode, callback: &mut F) where - F: FnMut(&SceneNode, Transform), + F: FnMut(&World, &SceneNode, Transform), { let v = self.world .view::<(Entities, &Transform)>() @@ -153,7 +153,7 @@ impl SceneGraph { for ((e, _), _rel) in v.iter() { let node = SceneNode::new(Some(start.entity()), e); let world_pos = node.world_transform(self); - callback(&node, world_pos); + callback(&self.world, &node, world_pos); self.traverse_down_from(node, callback); } @@ -222,7 +222,7 @@ pub mod tests { assert!(b.parent(&scene).unwrap() == a); let mut idx = 0; - scene.traverse_down(|_e, pos| { + scene.traverse_down(|_, _, pos| { if idx == 0 { assert_eq!(pos, Transform::from_translation(v2s[idx])); } else if idx == 1 {