Fix #6: Rendering Shared 3D Models #10

Merged
SeanOMik merged 11 commits from bug/6-rendering-shared-models into main 2024-04-01 15:03:02 +00:00
12 changed files with 169 additions and 48 deletions
Showing only changes of commit aa8d94851c - Show all commits

1
Cargo.lock generated
View File

@ -1795,6 +1795,7 @@ dependencies = [
"lyra-math", "lyra-math",
"lyra-reflect", "lyra-reflect",
"lyra-resource", "lyra-resource",
"lyra-scene",
"quote", "quote",
"syn 2.0.51", "syn 2.0.51",
"thiserror", "thiserror",

View File

@ -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; use lyra_engine::assets::ResourceManager;
struct FixedTimestep { struct FixedTimestep {
@ -79,7 +79,8 @@ async fn main() {
let separate_gltf = resman.request::<Gltf>("assets/pos-testing/child-node-cubes.glb").unwrap(); */ let separate_gltf = resman.request::<Gltf>("assets/pos-testing/child-node-cubes.glb").unwrap(); */
//drop(resman); //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() let cube_mesh = &cube_gltf.data_ref()
.unwrap().meshes[0]; .unwrap().meshes[0];
/* let crate_mesh = &crate_gltf.data_ref() /* let crate_mesh = &crate_gltf.data_ref()
@ -91,7 +92,8 @@ async fn main() {
let sponza_model = resman.request::<Gltf>("../assets/sponza/Sponza.gltf").unwrap(); let sponza_model = resman.request::<Gltf>("../assets/sponza/Sponza.gltf").unwrap();
drop(resman); 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() let sponza_scene = &sponza_model.data_ref()
.unwrap().scenes[0]; .unwrap().scenes[0];

View File

@ -8,6 +8,7 @@ lyra-resource = { path = "../lyra-resource" }
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] } lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
lyra-math = { path = "../lyra-math" } lyra-math = { path = "../lyra-math" }
lyra-scene = { path = "../lyra-scene" }
winit = "0.28.1" winit = "0.28.1"
tracing = "0.1.37" tracing = "0.1.37"

View File

@ -1,12 +1,13 @@
use std::any::Any; 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(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any;
} }
/// Implements this trait for anything that fits the type bounds /// Implements this trait for anything that fits the type bounds
impl<T: Send + Sync + 'static> CastableAny for T { impl<T: Send + Sync + 'static> AsSyncAny for T {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }

View File

@ -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 {} pub trait Event: Clone + Send + Sync + 'static {}
impl<T: Clone + Send + Sync + 'static> Event for T {} impl<T: Clone + Send + Sync + 'static> Event for T {}
pub type Events<T> = VecDeque<T>; pub type Events<T> = VecDeque<T>;
#[derive(Clone)]
struct TypeErasedBuffer {
type_id: TypeId,
item_size: usize,
data: Vec<MaybeUninit<u8>>,
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<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
item_size: mem::size_of::<T>(),
data: Default::default(),
// dropped immediately after the read
fn_drop_item: |item| unsafe { item.cast::<T>().drop_in_place() },
}
}
pub fn push<T: 'static>(&mut self, value: T) {
debug_assert!(TypeId::of::<T>() == 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::<T>());
// 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::<T>()
// 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::<T>());
}
}
pub fn len(&self) -> usize {
self.data.len() / self.item_size
}
}
pub struct EventQueue { pub struct EventQueue {
events: HashMap<TypeId, RefCell<Box<dyn CastableAny>>>, events: HashMap<TypeId, TypeErasedBuffer>,
event_write_queue: HashMap<TypeId, RefCell<Box<dyn CastableAny>>> event_write_queue: HashMap<TypeId, TypeErasedBuffer>,
} }
impl Default for EventQueue { impl Default for EventQueue {
@ -32,7 +99,7 @@ impl EventQueue {
E: Event E: Event
{ {
// the compiler wants me to explicit right here for some reason // the compiler wants me to explicit right here for some reason
let default = || RefCell::new(Box::<Events::<E>>::default() as Box<dyn CastableAny>); /* let default = || RefCell::new(Box::<Events::<E>>::default() as Box<dyn AsSyncAny>);
// Get, or create, a list of events of this type // Get, or create, a list of events of this type
let type_id = event.type_id(); let type_id = event.type_id();
@ -42,7 +109,13 @@ impl EventQueue {
// downcast resource as an events storage // downcast resource as an events storage
let e: &mut Events<E> = events.as_mut().as_any_mut().downcast_mut().unwrap(); let e: &mut Events<E> = 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::<E>());
events.push(event);
} }
// Clear events, this should happen at the start of every tick since events are cloned // 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<E>(&self) -> Option<Events<E>> pub fn read_events<E>(&self) -> Option<EventReader<E>>
where where
E: Event E: Event
{ {
if let Some(event ) = self.events.get(&TypeId::of::<E>()) { self.events.get(&TypeId::of::<E>())
let eref = event.borrow(); .map(|event| EventReader::new(event.clone()))
Some(eref.as_ref().as_any().downcast_ref::<Events<E>>().unwrap().clone()) }
}
pub struct EventReader<E: Event> {
buf: TypeErasedBuffer,
range: Option<Range<*mut MaybeUninit<u8>>>,
_marker: PhantomData<E>,
}
impl<E: Event> EventReader<E> {
fn new(buffer: TypeErasedBuffer) -> Self {
Self {
buf: buffer,
range: None,
_marker: PhantomData,
}
}
pub fn len(&self) -> usize {
self.buf.len()
}
}
impl<E: Event> Iterator for EventReader<E> {
type Item = E;
fn next(&mut self) -> Option<Self::Item> {
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::<E>().read_unaligned() };
// Advance the pointer to be after this event.
range.start = unsafe { range.start.add(mem::size_of::<E>()) };
Some(event)
} else { } else {
None None
} }

View File

@ -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 count = mouse_events.len();
let mut sum = Vec2::ZERO; let mut sum = Vec2::ZERO;
while let Some(mm) = mouse_events.pop_front() { for mm in mouse_events {
sum += mm.delta; sum += mm.delta;
} }
Some(sum / count as f32) Some(sum / count as f32)

View File

@ -110,8 +110,8 @@ impl crate::ecs::system::System for InputSystem {
return Ok(()); return Ok(());
} }
let mut events = queue.unwrap(); let events = queue.unwrap();
while let Some(event) = events.pop_front() { for event in events {
self.process_event(world, &event); self.process_event(world, &event);
} }

View File

@ -8,7 +8,7 @@ pub mod game;
pub mod render; pub mod render;
pub mod resources; pub mod resources;
pub mod input; pub mod input;
pub mod castable_any; pub mod as_any;
pub mod plugin; pub mod plugin;
pub mod change_tracker; pub mod change_tracker;

View File

@ -10,7 +10,7 @@ use lyra_ecs::query::filter::{Has, Or};
use lyra_ecs::{Entity, Tick}; use lyra_ecs::{Entity, Tick};
use lyra_ecs::query::{Entities, TickOf}; use lyra_ecs::query::{Entities, TickOf};
use lyra_ecs::World; use lyra_ecs::World;
use lyra_resource::gltf::GltfScene; use lyra_scene::SceneGraph;
use tracing::{debug, warn}; use tracing::{debug, warn};
use uuid::Uuid; use uuid::Uuid;
use wgpu::{BindGroupLayout, Limits}; use wgpu::{BindGroupLayout, Limits};
@ -36,7 +36,7 @@ use super::{render_pipeline::FullRenderPipeline, render_buffer::BufferStorage, r
use lyra_resource::{gltf::Mesh, ResHandle}; use lyra_resource::{gltf::Mesh, ResHandle};
type MeshHandle = ResHandle<Mesh>; type MeshHandle = ResHandle<Mesh>;
type SceneHandle = ResHandle<GltfScene>; type SceneHandle = ResHandle<SceneGraph>;
pub trait Renderer { pub trait Renderer {
fn prepare(&mut self, main_world: &mut World); fn prepare(&mut self, main_world: &mut World);
@ -465,11 +465,11 @@ impl Renderer for BasicRenderer {
if let Some(scene) = scene_han.data_ref() { if let Some(scene) = scene_han.data_ref() {
let interpo_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch); 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() { 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() { if let Some(mesh) = mesh_han.data_ref() {
let mesh_interpo = interpo_pos + mesh_pos; let mesh_interpo = interpo_pos + pos;
// if process mesh did not just create a new mesh, and the epoch // if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers // shows that the scene has changed, verify that the mesh buffers
@ -486,6 +486,7 @@ impl Renderer for BasicRenderer {
self.render_jobs.push_back(job); self.render_jobs.push_back(job);
} }
} }
});
} }
} }
} }

View File

@ -341,8 +341,8 @@ fn window_updater_system(world: &mut World) -> anyhow::Result<()> {
let mut opts = world.get_resource_mut::<Ct<WindowOptions>>(); let mut opts = world.get_resource_mut::<Ct<WindowOptions>>();
if let Some(event_queue) = world.try_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(events) = event_queue.read_events::<InputEvent>() {
while let Some(ev) = events.pop_front() { for ev in events {
match ev { match ev {
InputEvent::CursorEntered { .. } => { InputEvent::CursorEntered { .. } => {
opts.cursor_inside_window = true; opts.cursor_inside_window = true;

View File

@ -293,7 +293,7 @@ mod tests {
.data_ref().unwrap(); .data_ref().unwrap();
let mut node = None; let mut node = None;
scene.traverse_down(|no, _tran| { scene.traverse_down(|_, no, _tran| {
node = Some(no.clone()); node = Some(no.clone());
}); });

View File

@ -135,7 +135,7 @@ impl SceneGraph {
/// The traversal does not include the root scene node. /// The traversal does not include the root scene node.
pub fn traverse_down<F>(&self, mut callback: F) pub fn traverse_down<F>(&self, mut callback: F)
where where
F: FnMut(&SceneNode, Transform), F: FnMut(&World, &SceneNode, Transform),
{ {
self.traverse_down_from(self.root_node.clone(), &mut callback); self.traverse_down_from(self.root_node.clone(), &mut callback);
} }
@ -144,7 +144,7 @@ impl SceneGraph {
/// SceneNode and its world transform. /// SceneNode and its world transform.
fn traverse_down_from<F>(&self, start: SceneNode, callback: &mut F) fn traverse_down_from<F>(&self, start: SceneNode, callback: &mut F)
where where
F: FnMut(&SceneNode, Transform), F: FnMut(&World, &SceneNode, Transform),
{ {
let v = self.world let v = self.world
.view::<(Entities, &Transform)>() .view::<(Entities, &Transform)>()
@ -153,7 +153,7 @@ impl SceneGraph {
for ((e, _), _rel) in v.iter() { for ((e, _), _rel) in v.iter() {
let node = SceneNode::new(Some(start.entity()), e); let node = SceneNode::new(Some(start.entity()), e);
let world_pos = node.world_transform(self); let world_pos = node.world_transform(self);
callback(&node, world_pos); callback(&self.world, &node, world_pos);
self.traverse_down_from(node, callback); self.traverse_down_from(node, callback);
} }
@ -222,7 +222,7 @@ pub mod tests {
assert!(b.parent(&scene).unwrap() == a); assert!(b.parent(&scene).unwrap() == a);
let mut idx = 0; let mut idx = 0;
scene.traverse_down(|_e, pos| { scene.traverse_down(|_, _, pos| {
if idx == 0 { if idx == 0 {
assert_eq!(pos, Transform::from_translation(v2s[idx])); assert_eq!(pos, Transform::from_translation(v2s[idx]));
} else if idx == 1 { } else if idx == 1 {