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"
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",

View File

@ -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 {

View File

@ -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" ] }

View File

@ -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<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();
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<GltfNode> = 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<Mesh>, &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::<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();
assert!(mesh.position().unwrap().len() > 0);
assert!(mesh.normals().unwrap().len() > 0);

View File

@ -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<ResHandle<GltfScene>>,
pub scenes: Vec<ResHandle<SceneGraph>>,
pub materials: Vec<ResHandle<Material>>,
pub meshes: Vec<ResHandle<Mesh>>,
}
@ -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<Mesh>, 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!()
}
}

View File

@ -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<GltfNode>,
}
@ -92,4 +93,18 @@ impl GltfScene {
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

@ -31,4 +31,4 @@ pub(crate) mod lyra_engine {
pub(crate) mod reflect {
pub use lyra_reflect::*;
}
}
}

View File

@ -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<T: 'static> {
pub struct ResHandle<T: ResourceData> {
pub(crate) handle: UntypedResHandle,
_marker: PhantomData<T>,
}
impl<T> Clone for ResHandle<T> {
impl<T: ResourceData> Clone for ResHandle<T> {
fn clone(&self) -> Self {
Self {
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;
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 {
&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 {
self
}

View File

@ -173,7 +173,7 @@ impl ResourceManager {
/// Request a resource without downcasting to a `ResHandle<T>`.
/// 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<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
/// 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();
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<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();
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<T>(&self, ident: &str) -> Result<Arc<ResHandle<T>>, 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<T>(&self, resource: ResHandle<T>) -> Result<(), RequestError>
where
T: Send + Sync + Any + 'static
T: ResourceData
{
let state = self.state();

View File

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

View File

@ -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<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 {
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.