resource: use a SceneGraph for loading gltf nodes, make resources Send + Sync

This commit is contained in:
SeanOMik 2024-03-31 00:32:31 -04:00
parent a2aac25249
commit a17c035c05
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
11 changed files with 106 additions and 44 deletions

15
Cargo.lock generated
View File

@ -330,6 +330,12 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "atomic_refcell"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -708,12 +714,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.18" version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crunchy" name = "crunchy"
@ -1749,6 +1752,7 @@ name = "lyra-ecs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"atomic_refcell",
"lyra-ecs-derive", "lyra-ecs-derive",
"lyra-math", "lyra-math",
"paste", "paste",
@ -1844,6 +1848,7 @@ dependencies = [
"lyra-ecs", "lyra-ecs",
"lyra-math", "lyra-math",
"lyra-reflect", "lyra-reflect",
"lyra-scene",
"mime", "mime",
"notify", "notify",
"notify-debouncer-full", "notify-debouncer-full",

View File

@ -367,8 +367,8 @@ impl World {
} }
// TODO: Ensure that all non-send resources are only accessible on the main thread. // TODO: Ensure that all non-send resources are only accessible on the main thread.
/* unsafe impl Send for World {} unsafe impl Send for World {}
unsafe impl Sync for World {} */ unsafe impl Sync for World {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -9,6 +9,7 @@ edition = "2021"
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
lyra-reflect = { path = "../lyra-reflect" } lyra-reflect = { path = "../lyra-reflect" }
lyra-math = { path = "../lyra-math" } lyra-math = { path = "../lyra-math" }
lyra-scene = { path = "../lyra-scene" }
anyhow = "1.0.75" anyhow = "1.0.75"
base64 = "0.21.4" base64 = "0.21.4"
crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] } crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] }

View File

@ -3,9 +3,10 @@ use std::{ffi::OsStr, path::{Path, PathBuf}, sync::Arc};
use glam::{Quat, Vec3}; use glam::{Quat, Vec3};
use instant::Instant; use instant::Instant;
use lyra_math::Transform; use lyra_math::Transform;
use lyra_scene::{SceneGraph, SceneNode};
use thiserror::Error; 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 super::{Gltf, GltfNode, Material, Mesh, MeshIndices, MeshVertexAttribute, VertexAttributeData};
use tracing::debug; use tracing::debug;
@ -64,7 +65,7 @@ impl ModelLoader {
} }
} */ } */
fn process_node(ctx: &mut GltfLoadContext, materials: &Vec<ResHandle<Material>>, gnode: gltf::Node<'_>) -> GltfNode { fn process_node(ctx: &mut GltfLoadContext, materials: &Vec<ResHandle<Material>>, scene: &mut SceneGraph, scene_parent: &SceneNode, gnode: gltf::Node<'_>) -> GltfNode {
let mut node = GltfNode::default(); let mut node = GltfNode::default();
node.transform = { node.transform = {
@ -75,6 +76,8 @@ impl ModelLoader {
}; };
node.name = gnode.name().map(str::to_string); 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() { if let Some(mesh) = gnode.mesh() {
let mut new_mesh = Mesh::default(); let mut new_mesh = Mesh::default();
@ -127,11 +130,12 @@ impl ModelLoader {
let handle = ResHandle::new_ready(None, new_mesh); let handle = ResHandle::new_ready(None, new_mesh);
ctx.resource_manager.store_uuid(handle.clone()); 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() { 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); node.children.push(cmesh);
} }
@ -210,7 +214,21 @@ impl ResourceLoader for ModelLoader {
debug!("Loaded {} materials in {}s", materials.len(), mat_time.as_secs_f32()); debug!("Loaded {} materials in {}s", materials.len(), mat_time.as_secs_f32());
for (_idx, scene) in gltf.scenes().enumerate() { 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<GltfNode> = scene.nodes() let nodes: Vec<GltfNode> = scene.nodes()
.map(|node| ModelLoader::process_node(&mut context, &materials, node)) .map(|node| ModelLoader::process_node(&mut context, &materials, node))
.collect(); .collect();
@ -228,7 +246,7 @@ impl ResourceLoader for ModelLoader {
nodes, nodes,
}; };
let scene = ResHandle::new_ready(Some(path.as_str()), scene); let scene = ResHandle::new_ready(Some(path.as_str()), scene);
gltf_out.scenes.push(scene); gltf_out.scenes.push(scene); */
} }
gltf_out.materials = materials; gltf_out.materials = materials;
@ -249,6 +267,8 @@ impl ResourceLoader for ModelLoader {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use lyra_ecs::{query::Entities, relation::ChildOf};
use crate::tests::busy_wait_resource; use crate::tests::busy_wait_resource;
use super::*; use super::*;
@ -272,14 +292,30 @@ mod tests {
let scene = &gltf.scenes[0] let scene = &gltf.scenes[0]
.data_ref().unwrap(); .data_ref().unwrap();
assert_eq!(scene.nodes.len(), 1); let mut node = None;
let mnode = &scene.nodes[0]; scene.traverse_down(|no, _tran| {
node = Some(no.clone());
});
assert!(mnode.mesh.is_some()); let world = scene.world();
assert_eq!(mnode.transform, Transform::from_xyz(0.0, 0.0, 0.0)); let node = node.unwrap();
assert_eq!(mnode.children.len(), 0);
let mesh = mnode.mesh.as_ref().unwrap(); let data = world.view_one::<(&ResHandle<Mesh>, &Transform)>(node.entity()).get();
debug_assert!(data.is_some(), "The mesh was not loaded"); // transform will always be there
let data = data.unwrap();
// ensure there are no children of the node
assert_eq!(
world.view::<Entities>()
.relates_to::<ChildOf>(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(); let mesh = mesh.data_ref().unwrap();
assert!(mesh.position().unwrap().len() > 0); assert!(mesh.position().unwrap().len() > 0);
assert!(mesh.normals().unwrap().len() > 0); assert!(mesh.normals().unwrap().len() > 0);

View File

@ -3,6 +3,7 @@ pub use loader::*;
pub mod material; pub mod material;
use lyra_math::Transform; use lyra_math::Transform;
use lyra_scene::SceneGraph;
use crate::ResourceData; use crate::ResourceData;
pub use material::*; pub use material::*;
@ -17,7 +18,7 @@ use crate::ResHandle;
/// A loaded Gltf file /// A loaded Gltf file
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Gltf { pub struct Gltf {
pub scenes: Vec<ResHandle<GltfScene>>, pub scenes: Vec<ResHandle<SceneGraph>>,
pub materials: Vec<ResHandle<Material>>, pub materials: Vec<ResHandle<Material>>,
pub meshes: Vec<ResHandle<Mesh>>, pub meshes: Vec<ResHandle<Mesh>>,
} }
@ -55,14 +56,6 @@ impl ResourceData for Gltf {
impl Gltf { impl Gltf {
/// Collects all Gltf meshes and gets their world Transform. /// Collects all Gltf meshes and gets their world Transform.
pub fn collect_world_meshes(&self) -> Vec<(ResHandle<Mesh>, Transform)> { pub fn collect_world_meshes(&self) -> Vec<(ResHandle<Mesh>, Transform)> {
let mut v = vec![]; todo!()
for scene in self.scenes.iter() {
let mut tmp = scene.data_ref()
.unwrap().collect_world_meshes();
v.append(&mut tmp);
}
v
} }
} }

View File

@ -1,4 +1,5 @@
use lyra_math::Transform; use lyra_math::Transform;
use lyra_scene::SceneGraph;
use crate::{optionally_add_to_dep, ResourceData, UntypedResHandle}; use crate::{optionally_add_to_dep, ResourceData, UntypedResHandle};
use super::Mesh; use super::Mesh;
@ -33,8 +34,8 @@ impl ResourceData for GltfNode {
} }
} }
/// A Scene in a Gltf file // A Scene in a Gltf file
#[derive(Clone)] /* #[derive(Clone)]
pub struct GltfScene { pub struct GltfScene {
pub nodes: Vec<GltfNode>, pub nodes: Vec<GltfNode>,
} }
@ -92,4 +93,18 @@ impl GltfScene {
v v
} }
} */
impl ResourceData for SceneGraph {
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
todo!()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
} }

View File

@ -214,12 +214,12 @@ impl UntypedResHandle {
/// However, the only times it will be blocking is if another thread is reloading the resource /// 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. /// and has a write lock on the data. This means that most of the time, it is not blocking.
#[derive(Component)] #[derive(Component)]
pub struct ResHandle<T: 'static> { pub struct ResHandle<T: ResourceData> {
pub(crate) handle: UntypedResHandle, pub(crate) handle: UntypedResHandle,
_marker: PhantomData<T>, _marker: PhantomData<T>,
} }
impl<T> Clone for ResHandle<T> { impl<T: ResourceData> Clone for ResHandle<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
handle: self.handle.clone(), handle: self.handle.clone(),
@ -228,7 +228,7 @@ impl<T> Clone for ResHandle<T> {
} }
} }
impl<T> Deref for ResHandle<T> { impl<T: ResourceData> Deref for ResHandle<T> {
type Target = UntypedResHandle; type Target = UntypedResHandle;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -236,7 +236,7 @@ impl<T> Deref for ResHandle<T> {
} }
} }
impl<T> DerefMut for ResHandle<T> { impl<T: ResourceData> DerefMut for ResHandle<T> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.handle &mut self.handle
} }
@ -285,7 +285,7 @@ impl<T: ResourceData> ResHandle<T> {
} }
} }
impl<T: Send + Sync + 'static> ResourceStorage for ResHandle<T> { impl<T: ResourceData> ResourceStorage for ResHandle<T> {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }

View File

@ -173,7 +173,7 @@ impl ResourceManager {
/// Request a resource without downcasting to a `ResHandle<T>`. /// Request a resource without downcasting to a `ResHandle<T>`.
/// Whenever you're ready to downcast, you can do so like this: /// Whenever you're ready to downcast, you can do so like this:
/// ```compile_fail /// ```nobuild
/// let arc_any = res_arc.as_arc_any(); /// let arc_any = res_arc.as_arc_any();
/// let res: Arc<ResHandle<T>> = res.downcast::<ResHandle<T>>().expect("Failure to downcast resource"); /// let res: Arc<ResHandle<T>> = res.downcast::<ResHandle<T>>().expect("Failure to downcast resource");
/// ``` /// ```
@ -221,7 +221,7 @@ impl ResourceManager {
/// ///
/// The resource cannot be requested with [`ResourceManager::request`], it can only be /// The resource cannot be requested with [`ResourceManager::request`], it can only be
/// retrieved with [`ResourceManager::request_uuid`]. /// retrieved with [`ResourceManager::request_uuid`].
pub fn store_uuid<T: Send + Sync + 'static>(&self, res: ResHandle<T>) { pub fn store_uuid<T: ResourceData>(&self, res: ResHandle<T>) {
let mut state = self.state_mut(); let mut state = self.state_mut();
state.resources.insert(res.uuid().to_string(), Arc::new(res)); 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 /// Returns `None` if the resource was not found. The resource must of had been
/// stored with [`ResourceManager::request`] to return `Some`. /// stored with [`ResourceManager::request`] to return `Some`.
pub fn request_uuid<T: Send + Sync + 'static>(&self, uuid: &Uuid) -> Option<ResHandle<T>> { pub fn request_uuid<T: ResourceData>(&self, uuid: &Uuid) -> Option<ResHandle<T>> {
let state = self.state(); let state = self.state();
match state.resources.get(&uuid.to_string()) match state.resources.get(&uuid.to_string())
.or_else(|| state.uuid_resources.get(&uuid)) .or_else(|| state.uuid_resources.get(&uuid))
@ -291,7 +291,7 @@ impl ResourceManager {
/// Requests bytes from the manager. /// Requests bytes from the manager.
pub fn request_loaded_bytes<T>(&self, ident: &str) -> Result<Arc<ResHandle<T>>, RequestError> pub fn request_loaded_bytes<T>(&self, ident: &str) -> Result<Arc<ResHandle<T>>, RequestError>
where where
T: Send + Sync + Any + 'static T: ResourceData
{ {
let state = self.state(); let state = self.state();
match state.resources.get(&ident.to_string()) { match state.resources.get(&ident.to_string()) {
@ -366,7 +366,7 @@ impl ResourceManager {
/// the handle. /// the handle.
pub fn reload<T>(&self, resource: ResHandle<T>) -> Result<(), RequestError> pub fn reload<T>(&self, resource: ResHandle<T>) -> Result<(), RequestError>
where where
T: Send + Sync + Any + 'static T: ResourceData
{ {
let state = self.state(); let state = self.state();

View File

@ -30,7 +30,7 @@ pub trait WorldAssetExt {
/// automatically triggered if the resource is being watched. /// automatically triggered if the resource is being watched.
fn reload_res<T>(&mut self, resource: ResHandle<T>) -> Result<(), RequestError> fn reload_res<T>(&mut self, resource: ResHandle<T>) -> Result<(), RequestError>
where where
T: Send + Sync + Any + 'static; T: ResourceData;
} }
impl WorldAssetExt for World { impl WorldAssetExt for World {
@ -67,7 +67,7 @@ impl WorldAssetExt for World {
fn reload_res<T>(&mut self, resource: ResHandle<T>) -> Result<(), RequestError> fn reload_res<T>(&mut self, resource: ResHandle<T>) -> Result<(), RequestError>
where where
T: Send + Sync + Any + 'static T: ResourceData
{ {
let man = self.get_resource_or_default::<ResourceManager>(); let man = self.get_resource_or_default::<ResourceManager>();
man.reload(resource) man.reload(resource)

View File

@ -116,6 +116,13 @@ impl SceneGraph {
world_add_child_node(&mut self.world, parent, local_transform, bundle) 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<B: Bundle>(&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 { pub fn add_empty_node_under(&mut self, parent: &SceneNode, local_transform: Transform) -> SceneNode {
let e = self.world.spawn((SceneNodeFlag, local_transform)); let e = self.world.spawn((SceneNodeFlag, local_transform));
self.world.add_relation(e, ChildOf, parent.entity()); self.world.add_relation(e, ChildOf, parent.entity());
@ -155,6 +162,11 @@ impl SceneGraph {
pub fn root_node(&self) -> SceneNode { pub fn root_node(&self) -> SceneNode {
self.root_node.clone() 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. /// Add a node under a parent node.