Compare commits

..

2 Commits

23 changed files with 917 additions and 118 deletions

2
Cargo.lock generated
View File

@ -1849,10 +1849,12 @@ dependencies = [
"instant", "instant",
"lyra-ecs", "lyra-ecs",
"lyra-math", "lyra-math",
"lyra-reflect",
"mime", "mime",
"notify", "notify",
"notify-debouncer-full", "notify-debouncer-full",
"percent-encoding", "percent-encoding",
"rand 0.8.5",
"thiserror", "thiserror",
"tracing", "tracing",
"uuid", "uuid",

View File

@ -110,6 +110,7 @@ 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();
let sponza_scene = &sponza_model.data_ref() let sponza_scene = &sponza_model.data_ref()
.unwrap().scenes[0]; .unwrap().scenes[0];

View File

@ -441,9 +441,9 @@ fn gen_enum_variant_type(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_
let vty = VariantType::from(var); let vty = VariantType::from(var);
match vty { match vty {
VariantType::Struct => quote! { #variant_ident => EnumType::Struct }, VariantType::Struct => quote! { #variant_ident => lyra_engine::reflect::EnumType::Struct },
VariantType::Tuple => quote! { #variant_ident => EnumType::Tuple }, VariantType::Tuple => quote! { #variant_ident => lyra_engine::reflect::EnumType::Tuple },
VariantType::Unit => quote! { #variant_ident => EnumType::Unit }, VariantType::Unit => quote! { #variant_ident => lyra_engine::reflect::EnumType::Unit },
} }
}); });
@ -496,6 +496,10 @@ pub fn derive_reflect_enum(input: &DeriveInput, data_enum: &DataEnum) -> proc_ma
self self
} }
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) { fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) {
let val = val.as_any().downcast_ref::<Self>() let val = val.as_any().downcast_ref::<Self>()
.expect("The type of `val` is not the same as `self`"); .expect("The type of `val` is not the same as `self`");

View File

@ -1,7 +1,7 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::Ident; use proc_macro2::Ident;
use quote::quote; use quote::quote;
use syn::{Generics, Path, Attribute, GenericParam, parse_macro_input, DeriveInput, TypeParamBound}; use syn::{parse_macro_input, Attribute, DeriveInput, GenericParam, Generics, Path, TypeParamBound};
mod enum_derive; mod enum_derive;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -71,6 +71,8 @@ impl syn::parse::Parse for ReflectDef {
let type_path = Path::parse_mod_style(input)?; let type_path = Path::parse_mod_style(input)?;
//let ident = type_path. //type_path.require_ident()?; //let ident = type_path. //type_path.require_ident()?;
let mut generics = input.parse::<Generics>()?; let mut generics = input.parse::<Generics>()?;
/* let f: Punctuated<GenericParam, Token![,]> = input.parse_terminated(GenericParam::parse, Token![,])?;
generics.params = f; */
generics.where_clause = input.parse()?; generics.where_clause = input.parse()?;
Ok(Self { Ok(Self {
@ -135,6 +137,10 @@ pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream {
self self
} }
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) { fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) {
let val = val.as_any().downcast_ref::<Self>() let val = val.as_any().downcast_ref::<Self>()
.expect("The type of `val` is not the same as `self`"); .expect("The type of `val` is not the same as `self`");

View File

@ -404,6 +404,10 @@ pub fn derive_reflect_struct(input: &DeriveInput, data_struct: &DataStruct) -> p
self self
} }
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) { fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) {
let val = val.as_any().downcast_ref::<Self>() let val = val.as_any().downcast_ref::<Self>()
.expect(&format!("`self` was not {}", #name)); .expect(&format!("`self` was not {}", #name));

View File

@ -178,6 +178,10 @@ pub(crate) fn impl_reflect_simple_struct(input: proc_macro::TokenStream) -> proc
self self
} }
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) { fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) {
let val = val.as_any().downcast_ref::<Self>() let val = val.as_any().downcast_ref::<Self>()
.expect("The type of `val` is not the same as `self`"); .expect("The type of `val` is not the same as `self`");

View File

@ -43,6 +43,10 @@ impl Reflect for DynamicTupleRef {
self self
} }
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn Reflect) { fn apply(&mut self, val: &dyn Reflect) {
if let ReflectRef::Tuple(t) = val.reflect_ref() { if let ReflectRef::Tuple(t) = val.reflect_ref() {
assert_eq!(t.items_len(), self.items.len(), "The Tuple lengths do not match"); assert_eq!(t.items_len(), self.items.len(), "The Tuple lengths do not match");

View File

@ -1,9 +1,14 @@
use std::any::TypeId;
use std::sync::Arc;
use std::sync::RwLock;
use lyra_reflect_derive::impl_reflect_trait_value; use lyra_reflect_derive::impl_reflect_trait_value;
use crate::List; use crate::List;
use crate::lyra_engine; use crate::lyra_engine;
use crate::Struct;
use crate::{Reflect, ReflectRef, ReflectMut, util}; use crate::{Reflect, ReflectRef, ReflectMut, util};
impl_reflect_trait_value!(bool); impl_reflect_trait_value!(bool);
@ -26,11 +31,18 @@ impl_reflect_trait_value!(isize);
impl_reflect_trait_value!(f32); impl_reflect_trait_value!(f32);
impl_reflect_trait_value!(f64); impl_reflect_trait_value!(f64);
impl_reflect_trait_value!(TypeId);
impl_reflect_trait_value!(String); impl_reflect_trait_value!(String);
impl_reflect_trait_value!(::core::option::Option<T: Clone + Reflect>); impl_reflect_trait_value!(::core::option::Option<T: Clone + Reflect>);
impl_reflect_trait_value!(::core::result::Result<T: Clone + Reflect, E: Clone + Reflect>); impl_reflect_trait_value!(::core::result::Result<T: Clone + Reflect, E: Clone + Reflect>);
impl_reflect_trait_value!(::core::marker::PhantomData<T: 'static>);
impl_reflect_trait_value!(::std::sync::Arc<T: Reflect>);
//impl_reflect_trait_value!(::std::sync::Arc<std::sync::Mutex<T: Reflect>>);
//impl_reflect_trait_value!(::std::sync::RwLock<T: Reflect>);
impl_reflect_trait_value!(::core::ptr::NonNull<T: Reflect>); impl_reflect_trait_value!(::core::ptr::NonNull<T: Reflect>);
impl<T: Clone + Reflect, const N: usize> Reflect for [T; N] { impl<T: Clone + Reflect, const N: usize> Reflect for [T; N] {
@ -50,6 +62,10 @@ impl<T: Clone + Reflect, const N: usize> Reflect for [T; N] {
self self
} }
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn Reflect) { fn apply(&mut self, val: &dyn Reflect) {
if let ReflectRef::Array(arr) = val.reflect_ref() { if let ReflectRef::Array(arr) = val.reflect_ref() {
util::apply_array(self, arr); util::apply_array(self, arr);
@ -96,6 +112,10 @@ impl<T: Clone + Reflect> Reflect for Vec<T> {
self self
} }
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn Reflect) { fn apply(&mut self, val: &dyn Reflect) {
if let ReflectRef::List(list) = val.reflect_ref() { if let ReflectRef::List(list) = val.reflect_ref() {
self.apply_list(list); self.apply_list(list);
@ -128,3 +148,52 @@ impl<T: Clone + Reflect> Reflect for Vec<T> {
self self
} }
} }
impl<T: Reflect + Struct> Reflect for Arc<RwLock<T>> {
fn name(&self) -> String {
let t = self.read().unwrap();
format!("Arc<RwLock<{}>>", t.name())
}
fn type_id(&self) -> std::any::TypeId {
TypeId::of::<Self>()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn Reflect) {
let val = val.as_any().downcast_ref::<Self>()
.expect(&format!("Cannot reflect assign {} to {}", self.name(), val.name()));
*self = val.clone();
}
fn clone_inner(&self) -> Box<dyn Reflect> {
todo!()
}
fn reflect_ref(&self) -> ReflectRef {
todo!()
}
fn reflect_mut(&mut self) -> ReflectMut {
todo!()
}
fn reflect_val(&self) -> &dyn Reflect {
todo!()
}
fn reflect_val_mut(&mut self) -> &mut dyn Reflect {
todo!()
}
}

View File

@ -33,6 +33,9 @@ pub use reflected_list::*;
pub mod dynamic_tuple; pub mod dynamic_tuple;
pub use dynamic_tuple::*; pub use dynamic_tuple::*;
pub mod reflected_hashmap;
pub use reflected_hashmap::*;
pub mod component; pub mod component;
pub use component::*; pub use component::*;
@ -55,6 +58,7 @@ pub trait Reflect: Any {
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;
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
/// Apply a value to this object. /// Apply a value to this object.
/// ///
@ -62,6 +66,7 @@ pub trait Reflect: Any {
/// ///
/// The method panics if /// The method panics if
/// * the type of `val` is not the same type as `self` /// * the type of `val` is not the same type as `self`
// TODO: instead of taking as reference, it would make more sense to take a Box<dyn Reflect>.
fn apply(&mut self, val: &dyn Reflect); fn apply(&mut self, val: &dyn Reflect);
/// Clone self into a [`Box<dyn Reflect>`] /// Clone self into a [`Box<dyn Reflect>`]
@ -92,6 +97,7 @@ pub enum ReflectRef<'a> {
Array(&'a dyn Array), Array(&'a dyn Array),
List(&'a dyn List), List(&'a dyn List),
Value(&'a dyn Reflect), Value(&'a dyn Reflect),
Map(&'a dyn ReflectedMap),
} }
impl<'a> ReflectRef<'a> { impl<'a> ReflectRef<'a> {
@ -114,6 +120,7 @@ pub enum ReflectMut<'a> {
Array(&'a mut dyn Array), Array(&'a mut dyn Array),
List(&'a mut dyn List), List(&'a mut dyn List),
Value(&'a mut dyn Reflect), Value(&'a mut dyn Reflect),
Map(&'a dyn ReflectedMap),
} }
impl<'a> ReflectMut<'a> { impl<'a> ReflectMut<'a> {
@ -126,6 +133,7 @@ impl<'a> ReflectMut<'a> {
ReflectMut::Array(v) => ReflectRef::Array(*v), ReflectMut::Array(v) => ReflectRef::Array(*v),
ReflectMut::List(v) => ReflectRef::List(*v), ReflectMut::List(v) => ReflectRef::List(*v),
ReflectMut::Value(v) => ReflectRef::Value(*v), ReflectMut::Value(v) => ReflectRef::Value(*v),
ReflectMut::Map(v) => ReflectRef::Map(*v),
} }
} }

View File

@ -0,0 +1,129 @@
use std::{any::TypeId, collections::HashMap, hash::Hash};
use crate::Reflect;
pub trait ReflectedMap: Reflect {
/// Get the value at the provided `key` in the map.
///
/// `key` must be the same type as the key in the map.
fn reflect_get(&self, key: &dyn Reflect) -> Option<&dyn Reflect>;
/// Get the nth value in the map.
fn reflect_get_nth_value(&self, n: usize) -> Option<&dyn Reflect>;
/// Get a mutable borrow to the nth value in the map.
fn reflect_get_nth_value_mut(&mut self, n: usize) -> Option<&mut dyn Reflect>;
/// Insert a value at the provided `key` in the map.
///
/// If there is already a value at `key`, the old value will be returned.
fn reflect_insert(&mut self, key: Box<dyn Reflect>, val: Box<dyn Reflect>) -> Option<Box<dyn Reflect>>;
/// Returns a boolean indicating if the map contains a key as `key`.
///
/// `key` must be the same type as the key in the map.
fn reflect_contains_key(&self, key: &dyn Reflect) -> bool;
/// Returns the length of the map
fn reflect_len(&self) -> usize;
fn reflect_capacity(&self) -> usize;
}
impl<K: PartialEq + Eq + Hash + Clone + Reflect, V: Clone + Reflect> Reflect for HashMap<K, V> {
fn name(&self) -> String {
format!("HashMap<?, ?>") // TODO: get types of the generics
}
fn type_id(&self) -> std::any::TypeId {
TypeId::of::<Self>()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn Reflect) {
let val = val.as_any().downcast_ref::<Self>()
.expect("The provided value is not the same type the HashMap");
for (k, v) in val.iter() {
let (k, v) = (k.clone(), v.clone());
self.insert(k, v);
}
}
fn clone_inner(&self) -> Box<dyn Reflect> {
Box::new(self.clone())
}
fn reflect_ref(&self) -> crate::ReflectRef {
crate::ReflectRef::Map(self)
}
fn reflect_mut(&mut self) -> crate::ReflectMut {
crate::ReflectMut::Map(self)
}
fn reflect_val(&self) -> &dyn Reflect {
self
}
fn reflect_val_mut(&mut self) -> &mut dyn Reflect {
self
}
}
impl<K: PartialEq + Eq + Hash + Clone + Reflect, V: Clone + Reflect> ReflectedMap for HashMap<K, V> {
fn reflect_get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
let key = key.as_any().downcast_ref::<K>()
.expect("The provided key is not the same type as the HashMap's key");
self.get(key)
.map(|v| v.reflect_val())
}
fn reflect_get_nth_value(&self, n: usize) -> Option<&dyn Reflect> {
self.values().nth(n)
.map(|v| v.reflect_val())
}
fn reflect_get_nth_value_mut(&mut self, n: usize) -> Option<&mut dyn Reflect> {
self.values_mut().nth(n)
.map(|v| v.reflect_val_mut())
}
fn reflect_insert(&mut self, key: Box<dyn Reflect>, val: Box<dyn Reflect>) -> Option<Box<dyn Reflect>> {
let key = key.as_boxed_any();
let key = *key.downcast::<K>().expect("The provided key is not the same type as the HashMap's key");
let val = val.as_boxed_any();
let val = *val.downcast::<V>().expect("The provided value is not the same type as the HashMap's value");
self.insert(key, val)
.map(|v| Box::new(v) as Box<dyn Reflect>)
}
fn reflect_contains_key(&self, key: &dyn Reflect) -> bool {
let key = key.as_any().downcast_ref::<K>()
.expect("The provided key is not the same type as the HashMap's key");
self.contains_key(key)
}
fn reflect_len(&self) -> usize {
self.len()
}
fn reflect_capacity(&self) -> usize {
self.capacity()
}
}

View File

@ -25,6 +25,10 @@ macro_rules! impl_reflect_tuple {
self self
} }
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn Reflect) { fn apply(&mut self, val: &dyn Reflect) {
let val = val.as_any().downcast_ref::<Self>() let val = val.as_any().downcast_ref::<Self>()
.expect("The type of `val` is not the same as `self`"); .expect("The type of `val` is not the same as `self`");

View File

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
lyra-reflect = { path = "../lyra-reflect" }
lyra-math = { path = "../lyra-math" } lyra-math = { path = "../lyra-math" }
anyhow = "1.0.75" anyhow = "1.0.75"
base64 = "0.21.4" base64 = "0.21.4"
@ -26,3 +27,6 @@ tracing = "0.1.37"
uuid = { version = "1.4.1", features = ["v4"] } uuid = { version = "1.4.1", features = ["v4"] }
instant = "0.1" instant = "0.1"
async-std = "1.12.0" async-std = "1.12.0"
[dev-dependencies]
rand = "0.8.5"

View File

@ -0,0 +1,109 @@
use std::sync::Arc;
use crate::{loader::LoaderError, ResourceState, UntypedResHandle};
#[derive(Clone)]
pub enum DependencyState {
Loading,
Error {
/// The resource that had the error.
handle: UntypedResHandle,
/// The error that the resource ran into when loading.
error: Arc<LoaderError>,
},
Ready,
}
impl DependencyState {
/// Creates a DependencyState from a resource by retrieving its state. Does not include
/// the states of the dependencies.
pub fn shallow_from_res(handle: &UntypedResHandle) -> DependencyState {
let res = handle.read();
match &res.state {
ResourceState::Loading => DependencyState::Loading,
ResourceState::Error(er) => DependencyState::Error {
handle: handle.clone(),
error: er.clone(),
},
ResourceState::Ready(_) => DependencyState::Ready,
}
}
/// Retrieve the state of the handle and its dependencies, does not recursively retrieve.
pub fn from_res(handle: &UntypedResHandle) -> DependencyState {
let res = handle.read();
match &res.state {
ResourceState::Loading => DependencyState::Loading,
ResourceState::Error(er) => DependencyState::Error {
handle: handle.clone(),
error: er.clone(),
},
ResourceState::Ready(res) => {
let mut lowest_state = DependencyState::Ready;
for dep in res.dependencies() {
let state = DependencyState::shallow_from_res(&dep);
// try to find the "lowest" dependency. Meaning the state of a dependency
// that would stop the parent from being ready.
if state.is_loading() {
lowest_state = state;
break;
} else if state.is_error() {
lowest_state = state;
break;
}
// anything else would be loaded, so no need to update `lowest_state`
}
lowest_state
},
}
}
/// Retrieve the state of the handle and its dependencies, does not recursively retrieve.
pub fn from_res_recurse(handle: &UntypedResHandle) -> DependencyState {
let res = handle.read();
match &res.state {
ResourceState::Loading => DependencyState::Loading,
ResourceState::Error(er) => DependencyState::Error {
handle: handle.clone(),
error: er.clone(),
},
ResourceState::Ready(res) => {
let mut lowest_state = DependencyState::Ready;
for dep in res.dependencies() {
let state = DependencyState::from_res_recurse(&dep);
// try to find the "lowest" dependency. Meaning the state of a dependency
// that would stop the parent from being ready.
if state.is_loading() {
lowest_state = state;
break;
} else if state.is_error() {
lowest_state = state;
break;
}
// anything else would be loaded, so no need to update `lowest_state`
}
lowest_state
},
}
}
pub fn is_ready(&self) -> bool {
matches!(self, DependencyState::Ready)
}
pub fn is_error(&self) -> bool {
matches!(self, DependencyState::Error { handle: _, error: _ })
}
pub fn is_loading(&self) -> bool {
matches!(self, DependencyState::Loading)
}
}

View File

@ -1,12 +1,14 @@
use std::{collections::hash_map::DefaultHasher, hash::{Hash, Hasher}}; use std::{collections::hash_map::DefaultHasher, hash::{Hash, Hasher}};
use gltf::texture::{MagFilter, MinFilter}; use gltf::texture::{MagFilter, MinFilter};
use lyra_reflect::Reflect;
use crate::{lyra_engine, optionally_add_to_dep, ResourceData};
use crate::{util, FilterMode, Image, ResHandle, Texture, TextureSampler, WrappingMode}; use crate::{util, FilterMode, Image, ResHandle, Texture, TextureSampler, WrappingMode};
use super::loader::GltfLoadContext; use super::loader::GltfLoadContext;
/// PBR metallic roughness /// PBR metallic roughness
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, Reflect)]
pub struct PbrRoughness { pub struct PbrRoughness {
/// The rgba base color of the PBR material /// The rgba base color of the PBR material
pub base_color: [f32; 4], pub base_color: [f32; 4],
@ -54,7 +56,7 @@ impl From<gltf::material::PbrSpecularGlossiness<'_>> for PbrGlossiness {
/// The alpha rendering mode of a material. /// The alpha rendering mode of a material.
/// This is essentially a re-export of gltf::material::AlphaMode /// This is essentially a re-export of gltf::material::AlphaMode
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] #[derive(Clone, Copy, Eq, PartialEq, Debug, Default, Reflect)]
pub enum AlphaMode { pub enum AlphaMode {
/// The alpha value is ignored and the rendered output is fully opaque. /// The alpha value is ignored and the rendered output is fully opaque.
#[default] #[default]
@ -181,6 +183,33 @@ pub struct Material {
pub specular: Option<Specular>, pub specular: Option<Specular>,
} }
impl ResourceData for Material {
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
let mut deps = vec![];
optionally_add_to_dep(&mut deps, &self.metallic_roughness_texture);
optionally_add_to_dep(&mut deps, &self.base_color_texture);
optionally_add_to_dep(&mut deps, &self.metallic_roughness_texture);
if let Some(spec) = &self.specular {
optionally_add_to_dep(&mut deps, &spec.texture);
optionally_add_to_dep(&mut deps, &spec.color_texture);
}
deps
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
#[allow(dead_code)] #[allow(dead_code)]
impl Material { impl Material {
/// Get a uri's identifier /// Get a uri's identifier

View File

@ -1,11 +1,15 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{lyra_engine, ResHandle}; use lyra_reflect::Reflect;
use crate::lyra_engine;
use crate::ResHandle;
use crate::ResourceData;
use super::Material; use super::Material;
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq, Reflect)]
pub enum MeshIndices { pub enum MeshIndices {
//U8(Vec<u8>), //U8(Vec<u8>),
U16(Vec<u16>), U16(Vec<u16>),
@ -68,7 +72,7 @@ impl VertexAttributeData {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Reflect)]
pub enum MeshVertexAttribute { pub enum MeshVertexAttribute {
Position, Position,
Normals, Normals,
@ -90,6 +94,23 @@ pub struct Mesh {
pub material: Option<ResHandle<Material>>, pub material: Option<ResHandle<Material>>,
} }
impl ResourceData for Mesh {
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
match &self.material {
Some(m) => vec![m.untyped_clone()],
None => vec![]
}
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
impl Mesh { impl Mesh {
pub fn add_attribute(&mut self, attribute: MeshVertexAttribute, data: VertexAttributeData) { pub fn add_attribute(&mut self, attribute: MeshVertexAttribute, data: VertexAttributeData) {
self.attributes.insert(attribute, data); self.attributes.insert(attribute, data);

View File

@ -3,6 +3,7 @@ pub use loader::*;
pub mod material; pub mod material;
use lyra_math::Transform; use lyra_math::Transform;
use crate::ResourceData;
pub use material::*; pub use material::*;
pub mod mesh; pub mod mesh;
@ -21,6 +22,36 @@ pub struct Gltf {
pub meshes: Vec<ResHandle<Mesh>>, pub meshes: Vec<ResHandle<Mesh>>,
} }
impl ResourceData for Gltf {
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
let mut deps = vec![];
for scene in self.scenes.iter() {
deps.push(scene.untyped_clone())
}
for mat in self.materials.iter() {
deps.push(mat.untyped_clone())
}
for mesh in self.meshes.iter() {
deps.push(mesh.untyped_clone())
}
deps
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
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)> {

View File

@ -1,4 +1,5 @@
use lyra_math::Transform; use lyra_math::Transform;
use crate::{optionally_add_to_dep, ResourceData, UntypedResHandle};
use super::Mesh; use super::Mesh;
use crate::ResHandle; use crate::ResHandle;
@ -12,12 +13,51 @@ pub struct GltfNode {
pub children: Vec<GltfNode>, pub children: Vec<GltfNode>,
} }
impl ResourceData for GltfNode {
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
let mut deps: Vec<UntypedResHandle> = self.children.iter()
.flat_map(|c| c.mesh.as_ref().map(|h| h.untyped_clone()))
.collect();
optionally_add_to_dep(&mut deps, &self.mesh);
deps
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
/// 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>,
} }
impl ResourceData for GltfScene {
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
let deps: Vec<UntypedResHandle> = self.nodes.iter()
.map(|n| n.dependencies())
.flatten()
.collect();
deps
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
impl GltfScene { impl GltfScene {
fn collect_node(&self, parent_node: &GltfNode, node: &GltfNode) -> Vec<(ResHandle<Mesh>, Transform)> { fn collect_node(&self, parent_node: &GltfNode, node: &GltfNode) -> Vec<(ResHandle<Mesh>, Transform)> {
let mut v = vec![]; let mut v = vec![];

View File

@ -7,6 +7,9 @@ pub use resource::*;
mod texture; mod texture;
pub use texture::*; pub use texture::*;
mod dep_state;
pub use dep_state::*;
pub mod loader; pub mod loader;
pub mod gltf; pub mod gltf;
@ -24,4 +27,8 @@ pub(crate) mod lyra_engine {
pub(crate) mod ecs { pub(crate) mod ecs {
pub use lyra_ecs::*; pub use lyra_ecs::*;
} }
pub(crate) mod reflect {
pub use lyra_reflect::*;
}
} }

View File

@ -2,7 +2,7 @@ use std::{ffi::OsStr, path::Path, sync::Arc};
use async_std::io::ReadExt; use async_std::io::ReadExt;
use image::ImageError; use image::ImageError;
use tracing::trace; use tracing::debug;
use crate::{Image, ResHandle, ResourceData, ResourceManager}; use crate::{Image, ResHandle, ResourceData, ResourceManager};
@ -87,14 +87,14 @@ impl ResourceLoader for ImageLoader {
fn load_bytes(&self, _resource_manager: ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> PinedBoxLoaderFuture { fn load_bytes(&self, _resource_manager: ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> PinedBoxLoaderFuture {
Box::pin(async move { Box::pin(async move {
trace!("Loading {} bytes as an image", length);
let image = image::load_from_memory(&bytes[offset..(length-offset)]) let image = image::load_from_memory(&bytes[offset..(length-offset)])
.map_err(|e| match e { .map_err(|e| match e {
ImageError::IoError(e) => LoaderError::IoError(e), ImageError::IoError(e) => LoaderError::IoError(e),
_ => LoaderError::DecodingError(e.into()), _ => LoaderError::DecodingError(e.into()),
})?; })?;
let image = Image::from(image); let image = Image::from(image);
debug!("Finished loading image ({} bytes)", length);
Ok(Box::new(image) as Box<dyn ResourceData>) Ok(Box::new(image) as Box<dyn ResourceData>)
}) })
} }

View File

@ -1,40 +1,74 @@
use std::{any::{Any, TypeId}, marker::PhantomData, sync::{Arc, RwLock}}; use std::{any::{Any, TypeId}, marker::PhantomData, ops::{Deref, DerefMut}, sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}};
use lyra_ecs::Component; use lyra_ecs::Component;
use crate::{loader::LoaderError, lyra_engine}; use crate::{loader::LoaderError, lyra_engine, DependencyState};
use uuid::Uuid; use uuid::Uuid;
use crate::ResourceStorage; use crate::ResourceStorage;
pub fn optionally_add_to_dep<R: ResourceData>(deps: &mut Vec<UntypedResHandle>, handle: &Option<ResHandle<R>>) {
if let Some(h) = handle {
deps.push(h.untyped_clone());
}
}
/// A trait that that each resource type should implement. /// A trait that that each resource type should implement.
pub trait ResourceData: Send + Sync + Any + 'static { pub trait ResourceData: Send + Sync + Any + '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;
fn type_id(&self) -> TypeId;
}
impl<T: Send + Sync + Any + 'static> ResourceData for T { /// Collect the dependencies of the Resource.
fn as_any(&self) -> &dyn Any { fn dependencies(&self) -> Vec<UntypedResHandle>;
self
}
fn as_any_mut(&mut self) -> &mut dyn Any { /// Recursively collect the dependencies of the Resource.
self ///
} /// If a dependency has a child dependency, it will not show up in this list until its
/// parent is loaded.
fn recur_dependencies(&self) -> Vec<UntypedResHandle> {
let deps = self.dependencies();
let mut all_deps = deps.clone();
fn type_id(&self) -> TypeId { for dep in deps.into_iter() {
TypeId::of::<T>() let dep = dep.read();
match &dep.state {
ResourceState::Ready(data) => {
let mut deps_dep = data.dependencies();
all_deps.append(&mut deps_dep);
},
_ => {}
}
}
all_deps
} }
} }
//impl<T: Send + Sync + Reflect> ResourceData for T { }
pub enum ResourceState { pub enum ResourceState {
Loading, Loading,
Error(Arc<LoaderError>), Error(Arc<LoaderError>),
Ready(Box<dyn ResourceData>), Ready(Box<dyn ResourceData>),
} }
impl ResourceState {
/// Returns a boolean indicating if the state of still loading
pub fn is_loading(&self) -> bool {
matches!(self, ResourceState::Loading)
}
pub fn is_error(&self) -> bool {
matches!(self, ResourceState::Error(_))
}
pub fn is_ready(&self) -> bool {
matches!(self, ResourceState::Ready(_))
}
}
pub struct ResourceDataRef<'a, T> { pub struct ResourceDataRef<'a, T> {
guard: std::sync::RwLockReadGuard<'a, Resource<T>>, guard: std::sync::RwLockReadGuard<'a, UntypedResource>,
_marker: PhantomData<T>,
} }
impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> { impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> {
@ -52,13 +86,125 @@ impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> {
} }
} }
pub struct Resource<T> { pub struct UntypedResource {
pub(crate) version: usize, pub(crate) version: usize,
pub(crate) state: ResourceState, pub(crate) state: ResourceState,
uuid: Uuid, uuid: Uuid,
path: Option<String>, path: Option<String>,
pub(crate) is_watched: bool, pub(crate) is_watched: bool,
_marker: PhantomData<T>, /// can be used to wait for the resource to load.
pub(crate) condvar: Arc<(Mutex<bool>, Condvar)>,
}
#[derive(Clone)]
pub struct UntypedResHandle{
pub(crate) res: Arc<RwLock<UntypedResource>>,
#[allow(dead_code)]
tyid: TypeId,
}
impl UntypedResHandle {
pub fn new(res: UntypedResource, tyid: TypeId) -> Self {
Self {
res: Arc::new(RwLock::new(res)),
tyid
}
}
pub fn read(&self) -> RwLockReadGuard<UntypedResource> {
self.res.read().unwrap()
}
pub fn write(&self) -> RwLockWriteGuard<UntypedResource> {
self.res.write().unwrap()
}
/// Returns a boolean indicating if this resource's path is being watched.
pub fn is_watched(&self) -> bool {
let d = self.read();
d.is_watched
}
/// Returns a boolean indicating if this resource is loaded
pub fn is_loaded(&self) -> bool {
let d = self.read();
matches!(d.state, ResourceState::Ready(_))
}
/// Returns the uuid of the resource.
pub fn uuid(&self) -> Uuid {
let d = self.read();
d.uuid
}
pub fn path(&self) -> Option<String> {
let d = self.read();
d.path.clone()
}
/// Retrieves the current version of the resource. This gets incremented when the resource
/// is reloaded.
pub fn version(&self) -> usize {
let d = self.read();
d.version
}
/// Wait for the resource to be loaded, not including its dependencies
/// (see[`UntypedResHandle::wait_recurse_dependencies_load`]).
///
/// This blocks the thread without consuming CPU time; its backed by a
/// [`Condvar`](std::sync::Condvar).
pub fn wait_for_load(&self) {
let d = self.read();
if matches!(d.state, ResourceState::Ready(_)) {
return;
}
let cv = d.condvar.clone();
drop(d);
let l = cv.0.lock().unwrap();
let _unused = cv.1.wait(l).unwrap();
}
/// Wait for the entire resource, including its dependencies to be loaded.
///
/// This blocks the thread without consuming CPU time; its backed by a
/// [`Condvar`](std::sync::Condvar).
pub fn wait_recurse_dependencies_load(&self) {
self.wait_for_load();
let res = self.read();
match &res.state {
ResourceState::Ready(data) => {
// `recur_dependencies` wont return resources that are not loaded in yet
// if we did not check if the resource was finished loading, we could miss
// waiting for some resources and finish early.
while self.recurse_dependency_state().is_loading() {
for dep in data.recur_dependencies() {
dep.wait_for_load();
}
}
},
_ => unreachable!() // the self.wait_for_load ensures that the state is ready
}
}
/// Recursively get the state of the dependencies.
///
/// This doesn't return any resource data, it can be used to check if the resource and its
/// dependencies are loaded.
pub fn recurse_dependency_state(&self) -> DependencyState {
DependencyState::from_res_recurse(self)
}
pub fn as_typed<T: ResourceData>(&self) -> ResHandle<T> {
ResHandle {
handle: self.clone(),
_marker: PhantomData::<T>,
}
}
} }
/// A handle to a resource. /// A handle to a resource.
@ -69,89 +215,69 @@ pub struct Resource<T> {
/// 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: 'static> {
pub(crate) data: Arc<RwLock<Resource<T>>>, pub(crate) handle: UntypedResHandle,
_marker: PhantomData<T>,
} }
impl<T> Clone for ResHandle<T> { impl<T> Clone for ResHandle<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { data: self.data.clone() } Self {
handle: self.handle.clone(),
_marker: PhantomData::<T>
}
}
}
impl<T> Deref for ResHandle<T> {
type Target = UntypedResHandle;
fn deref(&self) -> &Self::Target {
&self.handle
}
}
impl<T> DerefMut for ResHandle<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.handle
} }
} }
impl<T: ResourceData> ResHandle<T> { impl<T: ResourceData> ResHandle<T> {
pub fn new_loading(path: Option<&str>) -> Self { pub fn new_loading(path: Option<&str>) -> Self {
let res_version = Resource { let res_version = UntypedResource {
version: 0, version: 0,
path: path.map(str::to_string), path: path.map(str::to_string),
state: ResourceState::Loading, state: ResourceState::Loading,
uuid: Uuid::new_v4(), uuid: Uuid::new_v4(),
is_watched: false, is_watched: false,
_marker: PhantomData::<T> condvar: Arc::new((Mutex::new(false), Condvar::new())),
}; };
Self { Self {
data: Arc::new(RwLock::new(res_version)), handle: UntypedResHandle::new(res_version, TypeId::of::<T>()),
_marker: PhantomData::<T>,
} }
} }
/// Create the resource with data, its assumed the state is `Ready` /// Create the resource with data, its assumed the state is `Ready`
pub fn new_ready(path: Option<&str>, data: T) -> Self { pub fn new_ready(path: Option<&str>, data: T) -> Self {
let res_version = Resource { let han = Self::new_loading(path);
version: 0, han.set_state(ResourceState::Ready(Box::new(data)));
path: path.map(str::to_string), han
state: ResourceState::Ready(Box::new(data)),
uuid: Uuid::new_v4(),
is_watched: false,
_marker: PhantomData::<T>
};
Self {
data: Arc::new(RwLock::new(res_version)),
}
} }
/// Returns a boolean indicating if this resource's path is being watched. /// Retrieve an untyped clone of the handle
pub fn is_watched(&self) -> bool { pub fn untyped_clone(&self) -> UntypedResHandle {
let d = self.data.read().expect("Resource mutex was poisoned!"); self.handle.clone()
d.is_watched
}
/// Returns a boolean indicating if this resource is loaded
pub fn is_loaded(&self) -> bool {
let d = self.data.read().expect("Resource mutex was poisoned!");
matches!(d.state, ResourceState::Ready(_))
}
/// Returns the current state of the resource.
/* pub fn state(&self) -> &ResourceState {
let d = self.data.read().expect("Resource mutex was poisoned!");
&d.state
} */
/// Returns the uuid of the resource.
pub fn uuid(&self) -> Uuid {
let d = self.data.read().expect("Resource mutex was poisoned!");
d.uuid
}
pub fn path(&self) -> Option<String> {
let d = self.data.read().expect("Resource mutex was poisoned!");
d.path.clone()
}
/// Retrieves the current version of the resource. This gets incremented when the resource
/// is reloaded.
pub fn version(&self) -> usize {
let d = self.data.read().expect("Resource mutex was poisoned!");
d.version
} }
/// Get a reference to the data in the resource /// Get a reference to the data in the resource
pub fn data_ref<'a>(&'a self) -> Option<ResourceDataRef<'a, T>> { pub fn data_ref<'a>(&'a self) -> Option<ResourceDataRef<'a, T>> {
if self.is_loaded() { if self.is_loaded() {
let d = self.data.read().expect("Resource mutex was poisoned!"); let d = self.handle.read();
Some(ResourceDataRef { Some(ResourceDataRef {
guard: d guard: d,
_marker: PhantomData::<T>
}) })
} else { } else {
None None
@ -177,34 +303,185 @@ impl<T: Send + Sync + 'static> ResourceStorage for ResHandle<T> {
} }
fn set_watched(&self, watched: bool) { fn set_watched(&self, watched: bool) {
let mut w = self.data.write().unwrap(); let mut d = self.handle.write();
w.is_watched = watched; d.is_watched = watched;
} }
fn version(&self) -> usize { fn version(&self) -> usize {
self.version() self.handle.version()
} }
fn uuid(&self) -> Uuid { fn uuid(&self) -> Uuid {
self.uuid() self.handle.uuid()
} }
fn path(&self) -> Option<String> { fn path(&self) -> Option<String> {
self.path() self.handle.path()
} }
fn is_watched(&self) -> bool { fn is_watched(&self) -> bool {
self.is_watched() self.handle.is_watched()
} }
fn is_loaded(&self) -> bool { fn is_loaded(&self) -> bool {
self.is_loaded() self.handle.is_loaded()
} }
fn set_state(&self, new: ResourceState) { fn set_state(&self, new: ResourceState) {
let mut d = self.data.write().expect("Resource mutex was poisoned!"); let mut d = self.handle.write();
d.state = new; d.state = new;
} }
}
#[cfg(test)]
mod tests {
use std::{path::PathBuf, str::FromStr, sync::Arc};
use async_std::task;
use instant::Duration;
use rand::Rng;
use crate::{loader::ResourceLoader, ResHandle, ResourceData, ResourceManager};
#[allow(dead_code)]
struct SimpleDepend {
file_name: String,
ext: String,
}
impl ResourceData for SimpleDepend {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
vec![]
}
}
#[derive(Default)]
struct SlowSimpleDependLoader;
impl ResourceLoader for SlowSimpleDependLoader {
fn extensions(&self) -> &[&str] {
&["txt", "buf"]
}
fn mime_types(&self) -> &[&str] {
&[]
}
fn load(&self, _: crate::ResourceManager, path: &str) -> crate::loader::PinedBoxLoaderFuture {
let path = path.to_string();
Box::pin(async move {
let path = PathBuf::from_str(&path).unwrap();
let file_name = path.file_name()
.and_then(|os| os.to_str())
.unwrap();
let path_ext = path.extension()
.and_then(|os| os.to_str())
.unwrap();
let res = rand::thread_rng().gen_range(500..1000);
task::sleep(Duration::from_millis(res)).await;
let simple = SimpleDepend {
file_name: file_name.to_string(),
ext: path_ext.to_string(),
};
Ok(Box::new(simple) as Box<dyn ResourceData>)
})
}
fn load_bytes(&self, _: crate::ResourceManager, _: Vec<u8>, _: usize, _: usize) -> crate::loader::PinedBoxLoaderFuture {
unreachable!()
}
fn create_erased_handle(&self) -> std::sync::Arc<dyn crate::ResourceStorage> {
Arc::from(ResHandle::<SimpleDepend>::new_loading(None))
}
}
struct SimpleResource {
depend_a: ResHandle<SimpleDepend>,
}
impl ResourceData for SimpleResource {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
vec![self.depend_a.untyped_clone()]
}
}
#[derive(Default)]
struct SlowSimpleResourceLoader;
impl ResourceLoader for SlowSimpleResourceLoader {
fn extensions(&self) -> &[&str] {
&["res", "large"]
}
fn mime_types(&self) -> &[&str] {
&[]
}
fn load(&self, res_man: crate::ResourceManager, _: &str) -> crate::loader::PinedBoxLoaderFuture {
Box::pin(async move {
let res = rand::thread_rng().gen_range(500..1000);
task::sleep(Duration::from_millis(res)).await;
// load dummy dependency that will take a bit
let depend_path = "depend.txt";
let depend_han = res_man.request::<SimpleDepend>(depend_path).unwrap();
let simple = SimpleResource {
depend_a: depend_han,
};
Ok(Box::new(simple) as Box<dyn ResourceData>)
})
}
fn load_bytes(&self, _: crate::ResourceManager, _: Vec<u8>, _: usize, _: usize) -> crate::loader::PinedBoxLoaderFuture {
unreachable!()
}
fn create_erased_handle(&self) -> std::sync::Arc<dyn crate::ResourceStorage> {
Arc::from(ResHandle::<SimpleResource>::new_loading(None))
}
}
#[test]
fn recursive() {
let man = ResourceManager::new();
man.register_loader::<SlowSimpleDependLoader>();
man.register_loader::<SlowSimpleResourceLoader>();
let res = man.request::<SimpleResource>("massive_asset.res").unwrap();
let state = res.recurse_dependency_state();
assert!(state.is_loading());
// this will take a bit
res.wait_recurse_dependencies_load();
let state = res.recurse_dependency_state();
assert!(!state.is_loading());
}
} }

View File

@ -7,7 +7,7 @@ use notify_debouncer_full::{DebouncedEvent, FileIdMap};
use thiserror::Error; use thiserror::Error;
use uuid::Uuid; use uuid::Uuid;
use crate::{gltf::ModelLoader, loader::{ImageLoader, LoaderError, ResourceLoader}, resource::ResHandle, ResourceState}; use crate::{gltf::ModelLoader, loader::{ImageLoader, LoaderError, ResourceLoader}, resource::ResHandle, ResourceData, ResourceState};
/// A trait for type erased storage of a resource. /// A trait for type erased storage of a resource.
/// Implemented for [`ResHandle<T>`] /// Implemented for [`ResHandle<T>`]
@ -61,6 +61,7 @@ pub struct ResourceWatcher {
/// The state of the ResourceManager /// The state of the ResourceManager
pub struct ResourceManagerState { pub struct ResourceManagerState {
resources: HashMap<String, Arc<dyn ResourceStorage>>, resources: HashMap<String, Arc<dyn ResourceStorage>>,
uuid_resources: HashMap<Uuid, Arc<dyn ResourceStorage>>,
loaders: Vec<Arc<dyn ResourceLoader>>, loaders: Vec<Arc<dyn ResourceLoader>>,
watchers: HashMap<String, ResourceWatcher>, watchers: HashMap<String, ResourceWatcher>,
} }
@ -79,6 +80,7 @@ impl Default for ResourceManager {
inner: Arc::new(RwLock::new( inner: Arc::new(RwLock::new(
ResourceManagerState { ResourceManagerState {
resources: HashMap::new(), resources: HashMap::new(),
uuid_resources: HashMap::new(),
loaders: vec![ Arc::new(ImageLoader), Arc::new(ModelLoader) ], loaders: vec![ Arc::new(ImageLoader), Arc::new(ModelLoader) ],
watchers: HashMap::new(), watchers: HashMap::new(),
} }
@ -121,7 +123,7 @@ impl ResourceManager {
/// handle to check if the resource is loaded. /// handle to check if the resource is loaded.
pub fn request<T>(&self, path: &str) -> Result<ResHandle<T>, RequestError> pub fn request<T>(&self, path: &str) -> Result<ResHandle<T>, RequestError>
where where
T: Send + Sync + Any + 'static T: ResourceData
{ {
let mut state = self.state_mut(); let mut state = self.state_mut();
match state.resources.get(&path.to_string()) { match state.resources.get(&path.to_string()) {
@ -146,18 +148,20 @@ impl ResourceManager {
task::spawn(async move { task::spawn(async move {
match res.await { match res.await {
Ok(data) => { Ok(data) => {
let mut d = thand.data.write().unwrap(); let mut d = thand.write();
d.state = ResourceState::Ready(data); d.state = ResourceState::Ready(data);
d.condvar.1.notify_all();
} }
Err(err) => { Err(err) => {
let mut d = thand.data.write().unwrap(); let mut d = thand.write();
d.state = ResourceState::Error(Arc::new(err)); d.state = ResourceState::Error(Arc::new(err));
} }
} }
}); });
let res: Arc<dyn ResourceStorage> = Arc::from(handle.clone()); let res: Arc<dyn ResourceStorage> = Arc::from(handle.clone());
state.resources.insert(path.to_string(), res); state.resources.insert(path.to_string(), res.clone());
state.uuid_resources.insert(res.uuid(), res);
Ok(handle) Ok(handle)
} else { } else {
@ -174,13 +178,13 @@ impl ResourceManager {
/// 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");
/// ``` /// ```
pub fn request_raw(&self, path: &str) -> Result<Arc<dyn ResourceStorage>, RequestError> { pub fn request_raw(&self, path: &str) -> Result<Arc<dyn ResourceStorage>, RequestError> {
let inner = self.inner.write().unwrap(); let mut state = self.state_mut();
match inner.resources.get(&path.to_string()) { match state.resources.get(&path.to_string()) {
Some(res) => { Some(res) => {
Ok(res.clone()) Ok(res.clone())
}, },
None => { None => {
if let Some(loader) = inner.loaders.iter() if let Some(loader) = state.loaders.iter()
.find(|l| l.does_support_file(path)) { .find(|l| l.does_support_file(path)) {
// Load the resource and store it // Load the resource and store it
@ -202,6 +206,9 @@ impl ResourceManager {
} }
}); });
let res: Arc<dyn ResourceStorage> = Arc::from(handle.clone());
state.uuid_resources.insert(res.uuid(), res);
Ok(handle) Ok(handle)
} else { } else {
Err(RequestError::UnsupportedFileExtension(path.to_string())) Err(RequestError::UnsupportedFileExtension(path.to_string()))
@ -225,7 +232,9 @@ impl ResourceManager {
/// 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: Send + Sync + 'static>(&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))
{
Some(res) => { Some(res) => {
let res = res.clone().as_arc_any(); let res = res.clone().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");
@ -246,7 +255,7 @@ impl ResourceManager {
/// Returns: The `Arc` to the now stored resource /// Returns: The `Arc` to the now stored resource
pub fn load_bytes<T>(&self, ident: &str, mime_type: &str, bytes: Vec<u8>, offset: usize, length: usize) -> Result<ResHandle<T>, RequestError> pub fn load_bytes<T>(&self, ident: &str, mime_type: &str, bytes: Vec<u8>, offset: usize, length: usize) -> Result<ResHandle<T>, RequestError>
where where
T: Send + Sync + Any + 'static T: ResourceData
{ {
let mut state = self.state_mut(); let mut state = self.state_mut();
if let Some(loader) = state.loaders.iter() if let Some(loader) = state.loaders.iter()
@ -259,18 +268,19 @@ impl ResourceManager {
task::spawn(async move { task::spawn(async move {
match res.await { match res.await {
Ok(data) => { Ok(data) => {
let mut d = thand.data.write().unwrap(); let mut d = thand.write();
d.state = ResourceState::Ready(data); d.state = ResourceState::Ready(data);
} }
Err(err) => { Err(err) => {
let mut d = thand.data.write().unwrap(); let mut d = thand.write();
d.state = ResourceState::Error(Arc::new(err)); d.state = ResourceState::Error(Arc::new(err));
} }
} }
}); });
let res: Arc<dyn ResourceStorage> = Arc::from(handle.clone()); let res: Arc<dyn ResourceStorage> = Arc::from(handle.clone());
state.resources.insert(ident.to_string(), res); state.resources.insert(ident.to_string(), res.clone());
state.uuid_resources.insert(res.uuid(), res);
Ok(handle) Ok(handle)
} else { } else {
@ -376,12 +386,12 @@ impl ResourceManager {
task::spawn(async move { task::spawn(async move {
match res.await { match res.await {
Ok(data) => { Ok(data) => {
let mut d = thand.data.write().unwrap(); let mut d = thand.write();
d.state = ResourceState::Ready(data); d.state = ResourceState::Ready(data);
d.version += 1; d.version += 1;
} }
Err(err) => { Err(err) => {
let mut d = thand.data.write().unwrap(); let mut d = thand.write();
d.state = ResourceState::Error(Arc::new(err)); d.state = ResourceState::Error(Arc::new(err));
} }
} }
@ -444,7 +454,8 @@ pub(crate) mod tests {
let res = man.request::<Image>(&get_image("squiggles.png")).unwrap(); let res = man.request::<Image>(&get_image("squiggles.png")).unwrap();
assert!(!res.is_loaded()); assert!(!res.is_loaded());
busy_wait_resource(&res, 10.0); res.wait_for_load();
//busy_wait_resource(&res, 10.0);
// shouldn't panic because of the loop // shouldn't panic because of the loop
res.data_ref().unwrap(); res.data_ref().unwrap();
@ -455,10 +466,10 @@ pub(crate) mod tests {
fn ensure_single() { fn ensure_single() {
let man = ResourceManager::new(); let man = ResourceManager::new();
let res = man.request::<Texture>(&get_image("squiggles.png")).unwrap(); let res = man.request::<Texture>(&get_image("squiggles.png")).unwrap();
assert_eq!(Arc::strong_count(&res.data), 3); assert_eq!(Arc::strong_count(&res.handle.res), 3);
let resagain = man.request::<Texture>(&get_image("squiggles.png")).unwrap(); let resagain = man.request::<Texture>(&get_image("squiggles.png")).unwrap();
assert_eq!(Arc::strong_count(&resagain.data), 4); assert_eq!(Arc::strong_count(&resagain.handle.res), 4);
} }
/// Ensures that an error is returned when a file that doesn't exist is requested /// Ensures that an error is returned when a file that doesn't exist is requested
@ -471,7 +482,7 @@ pub(crate) mod tests {
// 1 second should be enough to run into an error // 1 second should be enough to run into an error
std::thread::sleep(Duration::from_secs(1)); std::thread::sleep(Duration::from_secs(1));
//busy_wait_resource(&res, 10.0); //busy_wait_resource(&res, 10.0);
let state = &res.data.read().unwrap().state; let state = &res.read().state;
assert!( assert!(
match state { match state {

View File

@ -2,20 +2,23 @@ use std::ops::{Deref, DerefMut};
//pub use gltf::texture::{MagFilter, MinFilter, WrappingMode}; //pub use gltf::texture::{MagFilter, MinFilter, WrappingMode};
use image::DynamicImage; use image::DynamicImage;
use lyra_reflect::Reflect;
use crate::lyra_engine;
use crate::ResHandle; use crate::ResHandle;
use crate::ResourceData;
/// The filter mode of the sampler. /// The filter mode of the sampler.
/// ///
/// This is used for minification, magnification, and mipmap filters /// This is used for minification, magnification, and mipmap filters
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq, Reflect)]
pub enum FilterMode { pub enum FilterMode {
Nearest, Nearest,
Linear, Linear,
} }
/// The wrapping mode of the Texture coordinates /// The wrapping mode of the Texture coordinates
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq, Reflect)]
pub enum WrappingMode { pub enum WrappingMode {
ClampToEdge, ClampToEdge,
MirroredRepeat, MirroredRepeat,
@ -23,7 +26,7 @@ pub enum WrappingMode {
} }
/// The descriptor of the sampler for a Texture. /// The descriptor of the sampler for a Texture.
#[derive(Clone)] #[derive(Clone, Reflect)]
pub struct TextureSampler { pub struct TextureSampler {
pub mag_filter: Option<FilterMode>, pub mag_filter: Option<FilterMode>,
pub min_filter: Option<FilterMode>, pub min_filter: Option<FilterMode>,
@ -33,8 +36,24 @@ pub struct TextureSampler {
pub wrap_w: WrappingMode, pub wrap_w: WrappingMode,
} }
#[derive(Clone)] #[derive(Clone, Reflect)]
pub struct Image(DynamicImage); pub struct Image(#[reflect(skip)] DynamicImage);
impl ResourceData for Image {
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
vec![]
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
impl Deref for Image { impl Deref for Image {
type Target = DynamicImage; type Target = DynamicImage;
@ -62,6 +81,22 @@ pub struct Texture {
pub sampler: Option<TextureSampler>, pub sampler: Option<TextureSampler>,
} }
impl ResourceData for Texture {
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
vec![self.image.untyped_clone()]
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
impl Texture { impl Texture {
/// Create a texture from an image. /// Create a texture from an image.
pub fn from_image(image: ResHandle<Image>) -> Self { pub fn from_image(image: ResHandle<Image>) -> Self {

View File

@ -4,7 +4,7 @@ use crossbeam::channel::Receiver;
use lyra_ecs::World; use lyra_ecs::World;
use notify_debouncer_full::DebouncedEvent; use notify_debouncer_full::DebouncedEvent;
use crate::{loader::ResourceLoader, RequestError, ResHandle, ResourceManager}; use crate::{loader::ResourceLoader, RequestError, ResHandle, ResourceData, ResourceManager};
pub trait WorldAssetExt { pub trait WorldAssetExt {
/// Register a resource loader with the resource manager. /// Register a resource loader with the resource manager.
@ -15,7 +15,7 @@ pub trait WorldAssetExt {
/// Request a resource from the resource manager. /// Request a resource from the resource manager.
fn request_res<T>(&mut self, path: &str) -> Result<ResHandle<T>, RequestError> fn request_res<T>(&mut self, path: &str) -> Result<ResHandle<T>, RequestError>
where where
T: Send + Sync + Any + 'static; T: ResourceData;
/// Start watching a resource for changes. Returns a crossbeam channel that can be used to listen for events. /// Start watching a resource for changes. Returns a crossbeam channel that can be used to listen for events.
fn watch_res(&mut self, path: &str, recursive: bool) -> notify::Result<Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>>; fn watch_res(&mut self, path: &str, recursive: bool) -> notify::Result<Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>>;
@ -44,7 +44,7 @@ impl WorldAssetExt for World {
fn request_res<T>(&mut self, path: &str) -> Result<ResHandle<T>, RequestError> fn request_res<T>(&mut self, path: &str) -> Result<ResHandle<T>, 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.request(path) man.request(path)