From a17c035c05238c696c127d1d0976150721309891 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 31 Mar 2024 00:32:31 -0400 Subject: [PATCH] resource: use a SceneGraph for loading gltf nodes, make resources Send + Sync --- Cargo.lock | 15 ++++--- lyra-ecs/src/world.rs | 4 +- lyra-resource/Cargo.toml | 1 + lyra-resource/src/gltf/loader.rs | 60 +++++++++++++++++++++------ lyra-resource/src/gltf/mod.rs | 13 ++---- lyra-resource/src/gltf/scene.rs | 19 ++++++++- lyra-resource/src/lib.rs | 2 +- lyra-resource/src/resource.rs | 10 ++--- lyra-resource/src/resource_manager.rs | 10 ++--- lyra-resource/src/world_ext.rs | 4 +- lyra-scene/src/lib.rs | 12 ++++++ 11 files changed, 106 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 642fd0c..a87856e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,6 +330,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomic_refcell" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" + [[package]] name = "autocfg" version = "1.1.0" @@ -708,12 +714,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -1749,6 +1752,7 @@ name = "lyra-ecs" version = "0.1.0" dependencies = [ "anyhow", + "atomic_refcell", "lyra-ecs-derive", "lyra-math", "paste", @@ -1844,6 +1848,7 @@ dependencies = [ "lyra-ecs", "lyra-math", "lyra-reflect", + "lyra-scene", "mime", "notify", "notify-debouncer-full", diff --git a/lyra-ecs/src/world.rs b/lyra-ecs/src/world.rs index eb4593f..9919a65 100644 --- a/lyra-ecs/src/world.rs +++ b/lyra-ecs/src/world.rs @@ -367,8 +367,8 @@ impl World { } // TODO: Ensure that all non-send resources are only accessible on the main thread. -/* unsafe impl Send for World {} -unsafe impl Sync for World {} */ +unsafe impl Send for World {} +unsafe impl Sync for World {} #[cfg(test)] mod tests { diff --git a/lyra-resource/Cargo.toml b/lyra-resource/Cargo.toml index 77833fc..9e5e149 100644 --- a/lyra-resource/Cargo.toml +++ b/lyra-resource/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-reflect = { path = "../lyra-reflect" } lyra-math = { path = "../lyra-math" } +lyra-scene = { path = "../lyra-scene" } anyhow = "1.0.75" base64 = "0.21.4" crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] } diff --git a/lyra-resource/src/gltf/loader.rs b/lyra-resource/src/gltf/loader.rs index 665d101..a131df9 100644 --- a/lyra-resource/src/gltf/loader.rs +++ b/lyra-resource/src/gltf/loader.rs @@ -3,9 +3,10 @@ use std::{ffi::OsStr, path::{Path, PathBuf}, sync::Arc}; use glam::{Quat, Vec3}; use instant::Instant; use lyra_math::Transform; +use lyra_scene::{SceneGraph, SceneNode}; use thiserror::Error; -use crate::{gltf::GltfScene, loader::{LoaderError, PinedBoxLoaderFuture, ResourceLoader}, util, ResHandle, ResourceData, ResourceManager, ResourceStorage}; +use crate::{loader::{LoaderError, PinedBoxLoaderFuture, ResourceLoader}, util, ResHandle, ResourceData, ResourceManager, ResourceStorage}; use super::{Gltf, GltfNode, Material, Mesh, MeshIndices, MeshVertexAttribute, VertexAttributeData}; use tracing::debug; @@ -64,7 +65,7 @@ impl ModelLoader { } } */ - fn process_node(ctx: &mut GltfLoadContext, materials: &Vec>, gnode: gltf::Node<'_>) -> GltfNode { + fn process_node(ctx: &mut GltfLoadContext, materials: &Vec>, scene: &mut SceneGraph, scene_parent: &SceneNode, gnode: gltf::Node<'_>) -> GltfNode { let mut node = GltfNode::default(); node.transform = { @@ -75,6 +76,8 @@ impl ModelLoader { }; node.name = gnode.name().map(str::to_string); + let scene_node = scene.add_node_under(scene_parent, node.transform, ()); + if let Some(mesh) = gnode.mesh() { let mut new_mesh = Mesh::default(); @@ -127,11 +130,12 @@ impl ModelLoader { let handle = ResHandle::new_ready(None, new_mesh); ctx.resource_manager.store_uuid(handle.clone()); - node.mesh = Some(handle); + node.mesh = Some(handle.clone()); + scene.insert(&scene_node, handle); } for child in gnode.children() { - let cmesh = ModelLoader::process_node(ctx, materials, child); + let cmesh = ModelLoader::process_node(ctx, materials, scene, &scene_node, child); node.children.push(cmesh); } @@ -210,7 +214,21 @@ impl ResourceLoader for ModelLoader { debug!("Loaded {} materials in {}s", materials.len(), mat_time.as_secs_f32()); for (_idx, scene) in gltf.scenes().enumerate() { - let start_inst = Instant::now(); + let mut graph = SceneGraph::new(); + let root_node = graph.root_node(); + + for node in scene.nodes() { + let n = ModelLoader::process_node(&mut context, &materials, &mut graph, &root_node, node); + + if let Some(mesh) = n.mesh { + gltf_out.meshes.push(mesh.clone()); + } + } + + let graph = ResHandle::new_ready(Some(path.as_str()), graph); + gltf_out.scenes.push(graph); + + /* let start_inst = Instant::now(); let nodes: Vec = scene.nodes() .map(|node| ModelLoader::process_node(&mut context, &materials, node)) .collect(); @@ -228,7 +246,7 @@ impl ResourceLoader for ModelLoader { nodes, }; let scene = ResHandle::new_ready(Some(path.as_str()), scene); - gltf_out.scenes.push(scene); + gltf_out.scenes.push(scene); */ } gltf_out.materials = materials; @@ -249,6 +267,8 @@ impl ResourceLoader for ModelLoader { #[cfg(test)] mod tests { + use lyra_ecs::{query::Entities, relation::ChildOf}; + use crate::tests::busy_wait_resource; use super::*; @@ -272,14 +292,30 @@ mod tests { let scene = &gltf.scenes[0] .data_ref().unwrap(); - assert_eq!(scene.nodes.len(), 1); - let mnode = &scene.nodes[0]; + let mut node = None; + scene.traverse_down(|no, _tran| { + node = Some(no.clone()); + }); - assert!(mnode.mesh.is_some()); - assert_eq!(mnode.transform, Transform::from_xyz(0.0, 0.0, 0.0)); - assert_eq!(mnode.children.len(), 0); + let world = scene.world(); + let node = node.unwrap(); + + let data = world.view_one::<(&ResHandle, &Transform)>(node.entity()).get(); + debug_assert!(data.is_some(), "The mesh was not loaded"); // transform will always be there + let data = data.unwrap(); - let mesh = mnode.mesh.as_ref().unwrap(); + // ensure there are no children of the node + assert_eq!( + world.view::() + .relates_to::(node.entity()) + .into_iter() + .count(), + 0 + ); + + assert_eq!(*data.1, Transform::from_xyz(0.0, 0.0, 0.0)); + + let mesh = data.0; let mesh = mesh.data_ref().unwrap(); assert!(mesh.position().unwrap().len() > 0); assert!(mesh.normals().unwrap().len() > 0); diff --git a/lyra-resource/src/gltf/mod.rs b/lyra-resource/src/gltf/mod.rs index 5eef200..0b14924 100644 --- a/lyra-resource/src/gltf/mod.rs +++ b/lyra-resource/src/gltf/mod.rs @@ -3,6 +3,7 @@ pub use loader::*; pub mod material; use lyra_math::Transform; +use lyra_scene::SceneGraph; use crate::ResourceData; pub use material::*; @@ -17,7 +18,7 @@ use crate::ResHandle; /// A loaded Gltf file #[derive(Clone, Default)] pub struct Gltf { - pub scenes: Vec>, + pub scenes: Vec>, pub materials: Vec>, pub meshes: Vec>, } @@ -55,14 +56,6 @@ impl ResourceData for Gltf { impl Gltf { /// Collects all Gltf meshes and gets their world Transform. pub fn collect_world_meshes(&self) -> Vec<(ResHandle, Transform)> { - let mut v = vec![]; - - for scene in self.scenes.iter() { - let mut tmp = scene.data_ref() - .unwrap().collect_world_meshes(); - v.append(&mut tmp); - } - - v + todo!() } } \ No newline at end of file diff --git a/lyra-resource/src/gltf/scene.rs b/lyra-resource/src/gltf/scene.rs index 28ee149..9c3a47e 100644 --- a/lyra-resource/src/gltf/scene.rs +++ b/lyra-resource/src/gltf/scene.rs @@ -1,4 +1,5 @@ use lyra_math::Transform; +use lyra_scene::SceneGraph; use crate::{optionally_add_to_dep, ResourceData, UntypedResHandle}; use super::Mesh; @@ -33,8 +34,8 @@ impl ResourceData for GltfNode { } } -/// A Scene in a Gltf file -#[derive(Clone)] +// A Scene in a Gltf file +/* #[derive(Clone)] pub struct GltfScene { pub nodes: Vec, } @@ -92,4 +93,18 @@ impl GltfScene { v } +} */ + +impl ResourceData for SceneGraph { + fn dependencies(&self) -> Vec { + todo!() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } } \ No newline at end of file diff --git a/lyra-resource/src/lib.rs b/lyra-resource/src/lib.rs index 0d6cbcd..a68d47e 100644 --- a/lyra-resource/src/lib.rs +++ b/lyra-resource/src/lib.rs @@ -31,4 +31,4 @@ pub(crate) mod lyra_engine { pub(crate) mod reflect { pub use lyra_reflect::*; } -} +} \ No newline at end of file diff --git a/lyra-resource/src/resource.rs b/lyra-resource/src/resource.rs index 7604f38..7a95a6f 100644 --- a/lyra-resource/src/resource.rs +++ b/lyra-resource/src/resource.rs @@ -214,12 +214,12 @@ impl UntypedResHandle { /// However, the only times it will be blocking is if another thread is reloading the resource /// and has a write lock on the data. This means that most of the time, it is not blocking. #[derive(Component)] -pub struct ResHandle { +pub struct ResHandle { pub(crate) handle: UntypedResHandle, _marker: PhantomData, } -impl Clone for ResHandle { +impl Clone for ResHandle { fn clone(&self) -> Self { Self { handle: self.handle.clone(), @@ -228,7 +228,7 @@ impl Clone for ResHandle { } } -impl Deref for ResHandle { +impl Deref for ResHandle { type Target = UntypedResHandle; fn deref(&self) -> &Self::Target { @@ -236,7 +236,7 @@ impl Deref for ResHandle { } } -impl DerefMut for ResHandle { +impl DerefMut for ResHandle { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.handle } @@ -285,7 +285,7 @@ impl ResHandle { } } -impl ResourceStorage for ResHandle { +impl ResourceStorage for ResHandle { fn as_any(&self) -> &dyn Any { self } diff --git a/lyra-resource/src/resource_manager.rs b/lyra-resource/src/resource_manager.rs index 1781434..d8326de 100644 --- a/lyra-resource/src/resource_manager.rs +++ b/lyra-resource/src/resource_manager.rs @@ -173,7 +173,7 @@ impl ResourceManager { /// Request a resource without downcasting to a `ResHandle`. /// Whenever you're ready to downcast, you can do so like this: - /// ```compile_fail + /// ```nobuild /// let arc_any = res_arc.as_arc_any(); /// let res: Arc> = res.downcast::>().expect("Failure to downcast resource"); /// ``` @@ -221,7 +221,7 @@ impl ResourceManager { /// /// The resource cannot be requested with [`ResourceManager::request`], it can only be /// retrieved with [`ResourceManager::request_uuid`]. - pub fn store_uuid(&self, res: ResHandle) { + pub fn store_uuid(&self, res: ResHandle) { let mut state = self.state_mut(); state.resources.insert(res.uuid().to_string(), Arc::new(res)); } @@ -230,7 +230,7 @@ impl ResourceManager { /// /// Returns `None` if the resource was not found. The resource must of had been /// stored with [`ResourceManager::request`] to return `Some`. - pub fn request_uuid(&self, uuid: &Uuid) -> Option> { + pub fn request_uuid(&self, uuid: &Uuid) -> Option> { let state = self.state(); match state.resources.get(&uuid.to_string()) .or_else(|| state.uuid_resources.get(&uuid)) @@ -291,7 +291,7 @@ impl ResourceManager { /// Requests bytes from the manager. pub fn request_loaded_bytes(&self, ident: &str) -> Result>, RequestError> where - T: Send + Sync + Any + 'static + T: ResourceData { let state = self.state(); match state.resources.get(&ident.to_string()) { @@ -366,7 +366,7 @@ impl ResourceManager { /// the handle. pub fn reload(&self, resource: ResHandle) -> Result<(), RequestError> where - T: Send + Sync + Any + 'static + T: ResourceData { let state = self.state(); diff --git a/lyra-resource/src/world_ext.rs b/lyra-resource/src/world_ext.rs index c885ca4..a010cc4 100644 --- a/lyra-resource/src/world_ext.rs +++ b/lyra-resource/src/world_ext.rs @@ -30,7 +30,7 @@ pub trait WorldAssetExt { /// automatically triggered if the resource is being watched. fn reload_res(&mut self, resource: ResHandle) -> Result<(), RequestError> where - T: Send + Sync + Any + 'static; + T: ResourceData; } impl WorldAssetExt for World { @@ -67,7 +67,7 @@ impl WorldAssetExt for World { fn reload_res(&mut self, resource: ResHandle) -> Result<(), RequestError> where - T: Send + Sync + Any + 'static + T: ResourceData { let man = self.get_resource_or_default::(); man.reload(resource) diff --git a/lyra-scene/src/lib.rs b/lyra-scene/src/lib.rs index d5dc820..e2ce2c0 100644 --- a/lyra-scene/src/lib.rs +++ b/lyra-scene/src/lib.rs @@ -116,6 +116,13 @@ impl SceneGraph { world_add_child_node(&mut self.world, parent, local_transform, bundle) } + /// Insert a component bundle to a SceneNode. + /// + /// See [`lyra_ecs::World::insert`]. + pub fn insert(&mut self, node: &SceneNode, bundle: B) { + self.world.insert(node.entity(), bundle); + } + pub fn add_empty_node_under(&mut self, parent: &SceneNode, local_transform: Transform) -> SceneNode { let e = self.world.spawn((SceneNodeFlag, local_transform)); self.world.add_relation(e, ChildOf, parent.entity()); @@ -155,6 +162,11 @@ impl SceneGraph { pub fn root_node(&self) -> SceneNode { self.root_node.clone() } + + /// Retrieve a borrow of the world that backs the Scene + pub fn world(&self) -> &World { + &self.world + } } /// Add a node under a parent node.