resource: use a SceneGraph for loading gltf nodes, make resources Send + Sync
This commit is contained in:
parent
a2aac25249
commit
a17c035c05
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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" ] }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -31,4 +31,4 @@ pub(crate) mod lyra_engine {
|
|||
pub(crate) mod reflect {
|
||||
pub use lyra_reflect::*;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue