diff --git a/editor/src/inspector/editors/mod.rs b/editor/src/inspector/editors/mod.rs index b0abb44e3..876e3184f 100644 --- a/editor/src/inspector/editors/mod.rs +++ b/editor/src/inspector/editors/mod.rs @@ -38,15 +38,15 @@ use fyrox::{ inspectable::InspectablePropertyEditorDefinition, PropertyEditorDefinitionContainer, }, material::{ - shader::{Shader, ShaderError, ShaderState}, + shader::{Shader, ShaderError, ShaderResource}, SharedMaterial, }, resource::{ curve::{CurveResource, CurveResourceError, CurveResourceState}, - model::{MaterialSearchOptions, Model, ModelData, ModelLoadError}, + model::{MaterialSearchOptions, Model, ModelLoadError, ModelResource}, texture::{ - CompressionOptions, Texture, TextureMagnificationFilter, TextureMinificationFilter, - TextureWrapMode, + CompressionOptions, TextureMagnificationFilter, TextureMinificationFilter, + TextureResource, TextureWrapMode, }, }, scene::{ @@ -89,8 +89,8 @@ use fyrox::{ HighShelfFilterEffect, LowPassFilterEffect, LowShelfFilterEffect, }, reverb::Reverb, - Attenuate, AudioBus, Biquad, DistanceModel, Effect, EffectWrapper, SoundBufferResource, - SoundBufferResourceLoadError, SoundBufferState, Status, + Attenuate, AudioBus, Biquad, DistanceModel, Effect, EffectWrapper, SoundBuffer, + SoundBufferResource, SoundBufferResourceLoadError, Status, }, terrain::Layer, transform::Transform, @@ -132,7 +132,7 @@ pub fn make_property_editors_container( let container = PropertyEditorDefinitionContainer::new(); container.insert(TexturePropertyEditorDefinition); - container.insert(InheritablePropertyEditorDefinition::>::new()); + container.insert(InheritablePropertyEditorDefinition::>::new()); container.insert(InheritablePropertyEditorDefinition::>::new()); container.insert(MaterialPropertyEditorDefinition { @@ -174,17 +174,17 @@ pub fn make_property_editors_container( .register_inheritable_vec_collection::(); container.insert(ResourceFieldPropertyEditorDefinition::< + ModelResource, Model, - ModelData, ModelLoadError, >::new(Rc::new(|resource_manager, path| { block_on(resource_manager.request_model(path)) }))); - container.insert(InheritablePropertyEditorDefinition::>::new()); + container.insert(InheritablePropertyEditorDefinition::>::new()); container.insert(ResourceFieldPropertyEditorDefinition::< SoundBufferResource, - SoundBufferState, + SoundBuffer, SoundBufferResourceLoadError, >::new(Rc::new(|resource_manager, path| { block_on(resource_manager.request_sound_buffer(path)) @@ -203,13 +203,13 @@ pub fn make_property_editors_container( container.insert(InheritablePropertyEditorDefinition::>::new()); container.insert(ResourceFieldPropertyEditorDefinition::< + ShaderResource, Shader, - ShaderState, ShaderError, >::new(Rc::new(|resource_manager, path| { block_on(resource_manager.request_shader(path)) }))); - container.insert(InheritablePropertyEditorDefinition::>::new()); + container.insert(InheritablePropertyEditorDefinition::>::new()); container.register_inheritable_inspectable::(); container.register_inheritable_inspectable::(); diff --git a/editor/src/inspector/editors/texture.rs b/editor/src/inspector/editors/texture.rs index 3c249e80f..6ed74b531 100644 --- a/editor/src/inspector/editors/texture.rs +++ b/editor/src/inspector/editors/texture.rs @@ -16,7 +16,7 @@ use fyrox::{ widget::{Widget, WidgetBuilder, WidgetMessage}, BuildContext, Control, Thickness, UiNode, UserInterface, }, - resource::texture::Texture, + resource::texture::TextureResource, utils::into_gui_texture, }; use std::{ @@ -30,7 +30,7 @@ pub struct TextureEditor { widget: Widget, image: Handle, resource_manager: ResourceManager, - texture: Option, + texture: Option, } impl Debug for TextureEditor { @@ -55,11 +55,11 @@ impl DerefMut for TextureEditor { #[derive(Debug, PartialEq, Clone, Eq)] pub enum TextureEditorMessage { - Texture(Option), + Texture(Option), } impl TextureEditorMessage { - define_constructor!(TextureEditorMessage:Texture => fn texture(Option), layout: false); + define_constructor!(TextureEditorMessage:Texture => fn texture(Option), layout: false); } impl Control for TextureEditor { @@ -106,7 +106,7 @@ impl Control for TextureEditor { pub struct TextureEditorBuilder { widget_builder: WidgetBuilder, - texture: Option, + texture: Option, } impl TextureEditorBuilder { @@ -117,7 +117,7 @@ impl TextureEditorBuilder { } } - pub fn with_texture(mut self, texture: Option) -> Self { + pub fn with_texture(mut self, texture: Option) -> Self { self.texture = texture; self } @@ -158,14 +158,14 @@ pub struct TexturePropertyEditorDefinition; impl PropertyEditorDefinition for TexturePropertyEditorDefinition { fn value_type_id(&self) -> TypeId { - TypeId::of::>() + TypeId::of::>() } fn create_instance( &self, ctx: PropertyEditorBuildContext, ) -> Result { - let value = ctx.property_info.cast_value::>()?; + let value = ctx.property_info.cast_value::>()?; Ok(PropertyEditorInstance::Simple { editor: TextureEditorBuilder::new( @@ -189,7 +189,7 @@ impl PropertyEditorDefinition for TexturePropertyEditorDefinition { &self, ctx: PropertyEditorMessageContext, ) -> Result, InspectorError> { - let value = ctx.property_info.cast_value::>()?; + let value = ctx.property_info.cast_value::>()?; Ok(Some(TextureEditorMessage::texture( ctx.instance, diff --git a/editor/src/lib.rs b/editor/src/lib.rs index eebf40327..6ef897ab1 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -109,9 +109,9 @@ use fyrox::{ window::{WindowBuilder, WindowMessage, WindowTitle}, BuildContext, UiNode, UserInterface, VerticalAlignment, }, - material::{shader::Shader, Material, PropertyValue, SharedMaterial}, + material::{shader::ShaderResource, Material, PropertyValue, SharedMaterial}, plugin::PluginConstructor, - resource::texture::{CompressionOptions, Texture, TextureKind}, + resource::texture::{CompressionOptions, TextureKind, TextureResource}, scene::{ camera::{Camera, Projection}, mesh::Mesh, @@ -152,13 +152,13 @@ type GameEngine = fyrox::engine::Engine; pub fn load_image(data: &[u8]) -> Option { Some(into_gui_texture( - Texture::load_from_memory(data, CompressionOptions::NoCompression, false).ok()?, + TextureResource::load_from_memory(data, CompressionOptions::NoCompression, false).ok()?, )) } lazy_static! { - static ref GIZMO_SHADER: Shader = { - Shader::from_str( + static ref GIZMO_SHADER: ShaderResource = { + ShaderResource::from_str( include_str!("../resources/embed/shaders/gizmo.shader",), PathBuf::default(), ) @@ -536,7 +536,7 @@ impl Editor { let graphics_context = engine.graphics_context.as_initialized_mut(); - if let Ok(icon_img) = Texture::load_from_memory( + if let Ok(icon_img) = TextureResource::load_from_memory( include_bytes!("../resources/embed/icon.png"), CompressionOptions::NoCompression, false, @@ -891,7 +891,7 @@ impl Editor { } // Setup new one. - scene.render_target = Some(Texture::new_render_target(0, 0)); + scene.render_target = Some(TextureResource::new_render_target(0, 0)); self.scene_viewer .set_render_target(&self.engine.user_interface, scene.render_target.clone()); @@ -1408,7 +1408,7 @@ impl Editor { { let frame_size = self.scene_viewer.frame_bounds(&engine.user_interface).size; if width != frame_size.x as u32 || height != frame_size.y as u32 { - scene.render_target = Some(Texture::new_render_target( + scene.render_target = Some(TextureResource::new_render_target( frame_size.x as u32, frame_size.y as u32, )); diff --git a/editor/src/material.rs b/editor/src/material.rs index aec4cc1f6..0988224f1 100644 --- a/editor/src/material.rs +++ b/editor/src/material.rs @@ -39,7 +39,7 @@ use fyrox::{ window::{WindowBuilder, WindowTitle}, BuildContext, RcUiNodeHandle, Thickness, UiNode, UserInterface, VerticalAlignment, }, - material::{shader::Shader, Material, PropertyValue, SharedMaterial}, + material::{shader::ShaderResource, Material, PropertyValue, SharedMaterial}, resource::texture::TextureState, scene::{ base::BaseBuilder, @@ -89,7 +89,7 @@ pub struct MaterialEditor { preview: PreviewPanel, material: Option, available_shaders: Handle, - shaders_list: Vec, + shaders_list: Vec, texture_context_menu: TextureContextMenu, } @@ -328,7 +328,7 @@ impl MaterialEditor { self.shaders_list.clear(); self.shaders_list - .extend_from_slice(&Shader::standard_shaders()); + .extend_from_slice(&ShaderResource::standard_shaders()); for dir in fyrox::walkdir::WalkDir::new(".").into_iter().flatten() { let path = dir.path(); diff --git a/editor/src/overlay.rs b/editor/src/overlay.rs index 1281ddd19..35feeef1a 100644 --- a/editor/src/overlay.rs +++ b/editor/src/overlay.rs @@ -11,7 +11,7 @@ use fyrox::{ }, RenderPassStatistics, SceneRenderPass, SceneRenderPassContext, }, - resource::texture::{CompressionOptions, Texture}, + resource::texture::{CompressionOptions, TextureResource}, scene::mesh::surface::SurfaceData, }; use std::{cell::RefCell, rc::Rc}; @@ -51,8 +51,8 @@ impl OverlayShader { pub struct OverlayRenderPass { quad: GeometryBuffer, shader: OverlayShader, - sound_icon: Texture, - light_icon: Texture, + sound_icon: TextureResource, + light_icon: TextureResource, pub pictogram_size: f32, } @@ -65,13 +65,13 @@ impl OverlayRenderPass { state, ), shader: OverlayShader::new(state).unwrap(), - sound_icon: Texture::load_from_memory( + sound_icon: TextureResource::load_from_memory( include_bytes!("../resources/embed/sound_source.png"), CompressionOptions::NoCompression, false, ) .unwrap(), - light_icon: Texture::load_from_memory( + light_icon: TextureResource::load_from_memory( include_bytes!("../resources/embed/light_source.png"), CompressionOptions::NoCompression, false, diff --git a/editor/src/preview.rs b/editor/src/preview.rs index 23a0db51a..13d6afa6e 100644 --- a/editor/src/preview.rs +++ b/editor/src/preview.rs @@ -16,7 +16,7 @@ use fyrox::{ widget::{WidgetBuilder, WidgetMessage}, HorizontalAlignment, Orientation, Thickness, UiNode, VerticalAlignment, }, - resource::texture::{Texture, TextureKind}, + resource::texture::{TextureKind, TextureResource}, scene::{ base::BaseBuilder, camera::{CameraBuilder, Projection}, @@ -157,7 +157,7 @@ impl PreviewPanel { scene.ambient_lighting_color = Color::opaque(80, 80, 80); - let render_target = Texture::new_render_target(width, height); + let render_target = TextureResource::new_render_target(width, height); scene.render_target = Some(render_target.clone()); let scene = engine.scenes.add(scene); @@ -371,7 +371,8 @@ impl PreviewPanel { if let Some(frame) = engine.user_interface.node(self.frame).cast::() { let frame_size = frame.actual_local_size(); if rt_width != frame_size.x as u32 || rt_height != frame_size.y as u32 { - let rt = Texture::new_render_target(frame_size.x as u32, frame_size.y as u32); + let rt = + TextureResource::new_render_target(frame_size.x as u32, frame_size.y as u32); scene.render_target = Some(rt.clone()); engine.user_interface.send_message(ImageMessage::texture( self.frame, diff --git a/editor/src/scene/commands/material.rs b/editor/src/scene/commands/material.rs index fe46577af..b65d70e98 100644 --- a/editor/src/scene/commands/material.rs +++ b/editor/src/scene/commands/material.rs @@ -1,7 +1,7 @@ use crate::{command::Command, scene::commands::SceneContext}; use fyrox::{ core::sstorage::ImmutableString, - material::{shader::Shader, Material, PropertyValue, SharedMaterial}, + material::{shader::ShaderResource, Material, PropertyValue, SharedMaterial}, }; #[derive(Debug)] @@ -48,7 +48,7 @@ impl Command for SetMaterialPropertyValueCommand { #[derive(Debug)] enum SetMaterialShaderCommandState { Undefined, - NonExecuted { new_shader: Shader }, + NonExecuted { new_shader: ShaderResource }, Executed { old_material: Material }, Reverted { new_material: Material }, } @@ -60,7 +60,7 @@ pub struct SetMaterialShaderCommand { } impl SetMaterialShaderCommand { - pub fn new(material: SharedMaterial, shader: Shader) -> Self { + pub fn new(material: SharedMaterial, shader: ShaderResource) -> Self { Self { material, state: SetMaterialShaderCommandState::NonExecuted { new_shader: shader }, diff --git a/editor/src/scene/commands/mesh.rs b/editor/src/scene/commands/mesh.rs index aa07e5bdb..c42736168 100644 --- a/editor/src/scene/commands/mesh.rs +++ b/editor/src/scene/commands/mesh.rs @@ -2,14 +2,14 @@ use crate::{command::Command, scene::commands::SceneContext}; use fyrox::{ core::{pool::Handle, sstorage::ImmutableString}, material::{shader::SamplerFallback, PropertyValue}, - resource::texture::Texture, + resource::texture::TextureResource, scene::{mesh::Mesh, node::Node}, }; #[derive(Debug)] enum TextureSet { - Single(Texture), - Multiple(Vec>), + Single(TextureResource), + Multiple(Vec>), } #[derive(Debug)] @@ -19,7 +19,7 @@ pub struct SetMeshTextureCommand { } impl SetMeshTextureCommand { - pub fn new(node: Handle, texture: Texture) -> Self { + pub fn new(node: Handle, texture: TextureResource) -> Self { Self { node, set: TextureSet::Single(texture), diff --git a/editor/src/scene/commands/terrain.rs b/editor/src/scene/commands/terrain.rs index 1287946de..55895386e 100644 --- a/editor/src/scene/commands/terrain.rs +++ b/editor/src/scene/commands/terrain.rs @@ -2,7 +2,7 @@ use crate::{command::Command, create_terrain_layer_material, scene::commands::Sc use fyrox::resource::texture::{TextureKind, TexturePixelKind}; use fyrox::{ core::pool::Handle, - resource::texture::Texture, + resource::texture::TextureResource, scene::{node::Node, terrain::Layer}, utils, }; @@ -11,7 +11,7 @@ use fyrox::{ pub struct AddTerrainLayerCommand { terrain: Handle, layer: Option, - masks: Vec, + masks: Vec, } impl AddTerrainLayerCommand { @@ -50,7 +50,7 @@ pub struct DeleteTerrainLayerCommand { terrain: Handle, layer: Option, index: usize, - masks: Vec, + masks: Vec, } impl DeleteTerrainLayerCommand { @@ -121,7 +121,7 @@ impl ModifyTerrainHeightCommand { ) { chunk .set_heightmap( - Texture::from_bytes( + TextureResource::from_bytes( TextureKind::Rectangle { width: heigth_map_size.x, height: heigth_map_size.y, diff --git a/editor/src/scene_viewer.rs b/editor/src/scene_viewer.rs index c7509ed17..417552590 100644 --- a/editor/src/scene_viewer.rs +++ b/editor/src/scene_viewer.rs @@ -37,7 +37,7 @@ use fyrox::{ VerticalAlignment, BRUSH_BRIGHT_BLUE, BRUSH_DARKER, BRUSH_DARKEST, BRUSH_LIGHT, BRUSH_LIGHTER, BRUSH_LIGHTEST, }, - resource::texture::{Texture, TextureState}, + resource::texture::{TextureResource, TextureState}, scene::{ camera::{Camera, Projection}, node::Node, @@ -764,7 +764,7 @@ impl SceneViewer { )); } - pub fn set_render_target(&self, ui: &UserInterface, render_target: Option) { + pub fn set_render_target(&self, ui: &UserInterface, render_target: Option) { ui.send_message(ImageMessage::texture( self.frame, MessageDirection::ToWidget, diff --git a/editor/src/utils/mod.rs b/editor/src/utils/mod.rs index f34784306..79a083e1f 100644 --- a/editor/src/utils/mod.rs +++ b/editor/src/utils/mod.rs @@ -7,7 +7,7 @@ use fyrox::{ window::{Window, WindowBuilder}, BuildContext, UiNode, UserInterface, }, - resource::texture::{CompressionOptions, Texture}, + resource::texture::{CompressionOptions, TextureResource}, scene::camera::{SkyBox, SkyBoxBuilder}, }; @@ -86,8 +86,8 @@ pub fn fetch_node_screen_center_ui(handle: Handle, ui: &UserInterface) - .unwrap_or_default() } -fn load_texture(data: &[u8]) -> Texture { - Texture::load_from_memory(data, CompressionOptions::NoCompression, false) +fn load_texture(data: &[u8]) -> TextureResource { + TextureResource::load_from_memory(data, CompressionOptions::NoCompression, false) .ok() .unwrap() } diff --git a/editor/src/utils/path_fixer.rs b/editor/src/utils/path_fixer.rs index c468e5ab3..8c4b049ee 100644 --- a/editor/src/utils/path_fixer.rs +++ b/editor/src/utils/path_fixer.rs @@ -26,7 +26,7 @@ use fyrox::{ VerticalAlignment, }, material::PropertyValue, - resource::{model::Model, texture::Texture}, + resource::{model::ModelResource, texture::TextureResource}, scene::{ camera::Camera, decal::Decal, dim2::rectangle::Rectangle, light::spot::SpotLight, mesh::Mesh, particle_system::ParticleSystem, sprite::Sprite, terrain::Terrain, Scene, @@ -60,8 +60,8 @@ pub struct PathFixer { #[derive(Clone)] enum SceneResource { - Model(Model), - Texture(Texture), + Model(ModelResource), + Texture(TextureResource), // TODO: Add sound buffers. } diff --git a/examples/custom_loader.rs b/examples/custom_loader.rs index bb92fb605..fe66d91ab 100644 --- a/examples/custom_loader.rs +++ b/examples/custom_loader.rs @@ -32,8 +32,8 @@ use fyrox::{ event_loop::{ControlFlow, EventLoop}, material::{shader::SamplerFallback, Material, PropertyValue, SharedMaterial}, resource::{ - model::{Model, ModelImportOptions}, - texture::{Texture, TextureImportOptions}, + model::{ModelImportOptions, ModelResource}, + texture::{TextureImportOptions, TextureResource}, }, scene::{ base::BaseBuilder, @@ -55,12 +55,12 @@ use std::sync::Arc; struct CustomModelLoader(Arc); -impl ResourceLoader for CustomModelLoader { +impl ResourceLoader for CustomModelLoader { fn load( &self, - resource: Model, + resource: ModelResource, default_import_options: ModelImportOptions, - event_broadcaster: ResourceEventBroadcaster, + event_broadcaster: ResourceEventBroadcaster, reload: bool, ) -> BoxedLoaderFuture { // Arc is required as BoxedLoaderFuture has a static lifetime and hence self cannot be @@ -79,12 +79,12 @@ impl ResourceLoader for CustomModelLoader { /// For simplicity we just simply wrap the default loader and log the invocation to the console. struct CustomTextureLoader(Arc); -impl ResourceLoader for CustomTextureLoader { +impl ResourceLoader for CustomTextureLoader { fn load( &self, - resource: Texture, + resource: TextureResource, default_import_options: TextureImportOptions, - event_broadcaster: ResourceEventBroadcaster, + event_broadcaster: ResourceEventBroadcaster, reload: bool, ) -> BoxedLoaderFuture { // Arc is required as BoxedLoaderFuture has a static lifetime and hence self cannot be diff --git a/examples/render_to_texture.rs b/examples/render_to_texture.rs index 0b1ff0b44..107671b0c 100644 --- a/examples/render_to_texture.rs +++ b/examples/render_to_texture.rs @@ -11,14 +11,14 @@ use fyrox::{ UiNode, VerticalAlignment, }, plugin::{Plugin, PluginConstructor, PluginContext}, - resource::texture::{Texture, TextureKind}, + resource::texture::{TextureKind, TextureResource}, scene::{Scene, SceneLoader}, utils, window::WindowAttributes, }; struct Game { - render_target: Texture, + render_target: TextureResource, scene_handle: Handle, scene_image: Handle, exit: Handle, @@ -38,7 +38,7 @@ impl Plugin for Game { // Re-create render target with new size. drop(render_target); self.render_target = - Texture::new_render_target(image_size.x as u32, image_size.y as u32); + TextureResource::new_render_target(image_size.x as u32, image_size.y as u32); context.scenes[self.scene_handle].render_target = Some(self.render_target.clone()); context.user_interface.send_message(ImageMessage::texture( self.scene_image, @@ -100,7 +100,7 @@ impl PluginConstructor for GameConstructor { // Create render target and force the scene to render into it. let rt_size = Vector2::new(100.0, 100.0); - let render_target = Texture::new_render_target(rt_size.x as u32, rt_size.y as u32); + let render_target = TextureResource::new_render_target(rt_size.x as u32, rt_size.y as u32); scene.render_target = Some(render_target.clone()); // Add the loaded scene to the engine. diff --git a/fyrox-core/src/lib.rs b/fyrox-core/src/lib.rs index 4c3b97685..20edc8faf 100644 --- a/fyrox-core/src/lib.rs +++ b/fyrox-core/src/lib.rs @@ -53,6 +53,7 @@ pub use notify; #[cfg(target_arch = "wasm32")] pub use js_sys; use std::iter::FromIterator; +use uuid::Uuid; #[cfg(target_arch = "wasm32")] pub use wasm_bindgen; #[cfg(target_arch = "wasm32")] @@ -348,3 +349,9 @@ pub fn make_relative_path>(path: P) -> Result Uuid; +} diff --git a/fyrox-resource/src/container/mod.rs b/fyrox-resource/src/container/mod.rs index 5ec49368a..c67771695 100644 --- a/fyrox-resource/src/container/mod.rs +++ b/fyrox-resource/src/container/mod.rs @@ -6,15 +6,15 @@ use crate::{ entry::{TimedEntry, DEFAULT_RESOURCE_LIFETIME}, event::{ResourceEvent, ResourceEventBroadcaster}, }, - core::{variable::InheritableVariable, VecExtensions}, + core::VecExtensions, loader::ResourceLoader, state::ResourceState, task::TaskPool, UntypedResource, }; use fxhash::FxHashMap; -use std::ffi::OsString; -use std::{path::Path, sync::Arc}; +use fyrox_core::uuid::Uuid; +use std::{ffi::OsString, path::Path, sync::Arc}; pub mod entry; pub mod event; @@ -168,11 +168,14 @@ impl ResourceContainer { } /// Tries to load a resources at a given path. - pub fn request>(&mut self, path: P) -> UntypedResource { + pub fn request

(&mut self, path: P, type_uuid: Uuid) -> UntypedResource + where + P: AsRef, + { match self.find(path.as_ref()) { Some(existing) => existing.clone(), None => { - let resource = UntypedResource::new_pending(path.as_ref().to_owned()); + let resource = UntypedResource::new_pending(path.as_ref().to_owned(), type_uuid); self.push(resource.clone()); @@ -192,7 +195,7 @@ impl ResourceContainer { /// Reloads a single resource. pub fn reload_resource(&mut self, resource: UntypedResource) { - let state = resource.0.lock(); + let mut state = resource.0.lock(); if !state.is_loading() { let path = state.path().to_path_buf(); @@ -218,39 +221,6 @@ impl ResourceContainer { resources } - - /// Tries to restore resource by making an attempt to request resource with path from existing - /// resource instance. This method is used to restore "shallow" resources after scene - /// deserialization. - pub fn try_restore_resource(&mut self, resource: &mut UntypedResource) { - let path = resource.0.lock().path().to_path_buf(); - let new_resource = self.request(path); - *resource = new_resource; - } - - /// Tries to restore resource by making an attempt to request resource with path from existing - /// resource instance. This method is used to restore "shallow" resources after scene - /// deserialization. - pub fn try_restore_optional_resource(&mut self, resource: &mut Option) { - if let Some(shallow_resource) = resource.as_mut() { - let path = shallow_resource.0.lock().path().to_path_buf(); - let new_resource = self.request(path); - *shallow_resource = new_resource; - } - } - - /// Tries to restore resource by making an attempt to request resource with path from existing - /// resource instance. This method is used to restore "shallow" resources after scene - /// deserialization. - pub fn try_restore_inheritable_resource( - &mut self, - inheritable_resource: &mut InheritableVariable>, - ) { - if let Some(shallow_resource) = inheritable_resource.get_value_mut_silent().as_mut() { - let new_resource = self.request(shallow_resource.0.lock().path()); - *shallow_resource = new_resource; - } - } } impl Container for ResourceContainer { diff --git a/fyrox-resource/src/lib.rs b/fyrox-resource/src/lib.rs index 30e17ee82..927a877dd 100644 --- a/fyrox-resource/src/lib.rs +++ b/fyrox-resource/src/lib.rs @@ -1,23 +1,23 @@ //! Resource management -#![warn(missing_docs)] +// #![warn(missing_docs)] TODO use crate::{ core::{ parking_lot::MutexGuard, + reflect::prelude::*, reflect::FieldValue, uuid::{uuid, Uuid}, visitor::prelude::*, }, state::ResourceState, }; -use std::fmt::Display; use std::{ any::Any, borrow::Cow, fmt::{Debug, Formatter}, future::Future, - hash::Hash, + hash::{Hash, Hasher}, marker::PhantomData, ops::{Deref, DerefMut}, path::{Path, PathBuf}, @@ -26,8 +26,10 @@ use std::{ task::{Context, Poll}, }; +use crate::manager::ResourceManager; use crate::untyped::UntypedResource; pub use fyrox_core as core; +use fyrox_core::TypeUuidProvider; pub mod constructor; pub mod container; @@ -39,6 +41,7 @@ pub mod task; pub mod untyped; pub const TEXTURE_RESOURCE_UUID: Uuid = uuid!("02c23a44-55fa-411a-bc39-eb7a5eadf15c"); +pub const MODEL_RESOURCE_UUID: Uuid = uuid!("44cd768f-b4ca-4804-a98c-0adf85577ada"); pub const SOUND_BUFFER_RESOURCE_UUID: Uuid = uuid!("f6a077b7-c8ff-4473-a95b-0289441ea9d8"); pub const SHADER_RESOURCE_UUID: Uuid = uuid!("f1346417-b726-492a-b80f-c02096c6c019"); pub const CURVE_RESOURCE_UUID: Uuid = uuid!("f28b949f-28a2-4b68-9089-59c234f58b6b"); @@ -59,13 +62,13 @@ pub trait ResourceData: 'static + Debug + Visit + Send { } /// A trait for resource load error. -pub trait ResourceLoadError: 'static + Debug + Display + Send + Sync {} +pub trait ResourceLoadError: 'static + Debug + Send + Sync {} -impl ResourceLoadError for T where T: 'static + Display + Debug + Send + Sync {} +impl ResourceLoadError for T where T: 'static + Debug + Send + Sync {} pub struct ResourceStateGuard<'a, T> where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { guard: MutexGuard<'a, ResourceState>, phantom: PhantomData, @@ -73,12 +76,25 @@ where impl<'a, T> ResourceStateGuard<'a, T> where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { pub fn get(&mut self) -> ResourceStateRef<'_, T> { match &*self.guard { - ResourceState::Pending { path, .. } => ResourceStateRef::Pending { path }, - ResourceState::LoadError { path, error } => ResourceStateRef::LoadError { path, error }, + ResourceState::Pending { + path, type_uuid, .. + } => ResourceStateRef::Pending { + path, + type_uuid: *type_uuid, + }, + ResourceState::LoadError { + path, + error, + type_uuid, + } => ResourceStateRef::LoadError { + path, + error, + type_uuid: *type_uuid, + }, ResourceState::Ok(data) => { ResourceStateRef::Ok(data.as_any().downcast_ref().expect("Type mismatch!")) } @@ -87,10 +103,21 @@ where pub fn get_mut(&mut self) -> ResourceStateRefMut<'_, T> { match &mut *self.guard { - ResourceState::Pending { path, .. } => ResourceStateRefMut::Pending { path }, - ResourceState::LoadError { path, error } => { - ResourceStateRefMut::LoadError { path, error } - } + ResourceState::Pending { + path, type_uuid, .. + } => ResourceStateRefMut::Pending { + path, + type_uuid: *type_uuid, + }, + ResourceState::LoadError { + path, + error, + type_uuid, + } => ResourceStateRefMut::LoadError { + path, + error, + type_uuid: *type_uuid, + }, ResourceState::Ok(data) => { ResourceStateRefMut::Ok(data.as_any_mut().downcast_mut().expect("Type mismatch!")) } @@ -107,6 +134,7 @@ where Pending { /// A path to load resource from. path: &'a PathBuf, + type_uuid: Uuid, }, /// An error has occurred during the load. LoadError { @@ -114,6 +142,7 @@ where path: &'a PathBuf, /// An error. error: &'a Option>, + type_uuid: Uuid, }, /// Actual resource data when it is fully loaded. Ok(&'a T), @@ -125,6 +154,7 @@ pub enum ResourceStateRefMut<'a, T> { Pending { /// A path to load resource from. path: &'a mut PathBuf, + type_uuid: Uuid, }, /// An error has occurred during the load. LoadError { @@ -132,6 +162,7 @@ pub enum ResourceStateRefMut<'a, T> { path: &'a mut PathBuf, /// An error. error: &'a mut Option>, + type_uuid: Uuid, }, /// Actual resource data when it is fully loaded. Ok(&'a mut T), @@ -142,27 +173,86 @@ impl Default for ResourceState { Self::LoadError { error: None, path: Default::default(), + type_uuid: Default::default(), } } } -#[derive(Visit, PartialEq, Eq, Hash, Debug)] +#[derive(Debug, Reflect)] +#[reflect(hide_all)] pub struct Resource where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { state: Option, - #[visit(skip)] phantom: PhantomData, } +impl Visit for Resource +where + T: ResourceData + TypeUuidProvider, +{ + fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult { + let mut region = visitor.enter_region(name)?; + + self.state.visit("State", &mut region)?; + + if region.is_reading() { + // Try to restore the shallow handle. + let resource_manager = region + .blackboard + .get::() + .expect("Resource manager must be available when deserializing resources!"); + + let path = self.state.as_ref().unwrap().path(); + + // Procedural resources usually have path empty or use it as an id, in this case we need to + // check if the file actually exists to not mess up procedural resources. + if path.exists() { + self.state = Some( + resource_manager.request_untyped(path, ::type_uuid()), + ); + } + } + + Ok(()) + } +} + +impl PartialEq for Resource +where + T: ResourceData + TypeUuidProvider, +{ + fn eq(&self, other: &Self) -> bool { + match (&self.state, &other.state) { + (Some(a), Some(b)) => a == b, + (None, None) => true, + _ => false, + } + } +} + +impl Eq for Resource where T: ResourceData + TypeUuidProvider {} + +impl Hash for Resource +where + T: ResourceData + TypeUuidProvider, +{ + fn hash(&self, state: &mut H) { + self.state.hash(state) + } +} + impl Resource where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { pub fn new_pending(path: PathBuf) -> Self { Self { - state: Some(UntypedResource::new_pending(path)), + state: Some(UntypedResource::new_pending( + path, + ::type_uuid(), + )), phantom: PhantomData, } } @@ -176,7 +266,11 @@ where pub fn new_load_error(path: PathBuf, error: Option>) -> Self { Self { - state: Some(UntypedResource::new_load_error(path, error)), + state: Some(UntypedResource::new_load_error( + path, + error, + ::type_uuid(), + )), phantom: PhantomData, } } @@ -230,6 +324,10 @@ where self.state.as_ref().unwrap().key() } + pub fn path(&self) -> PathBuf { + self.state.as_ref().unwrap().0.lock().path().to_path_buf() + } + /// Allows you to obtain reference to the resource data. /// /// # Panic @@ -250,7 +348,7 @@ where impl Default for Resource where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { #[inline] fn default() -> Self { @@ -263,7 +361,7 @@ where impl Clone for Resource where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { #[inline] fn clone(&self) -> Self { @@ -276,7 +374,7 @@ where impl From for Resource where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { #[inline] fn from(state: UntypedResource) -> Self { @@ -290,7 +388,7 @@ where #[allow(clippy::from_over_into)] impl Into for Resource where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { #[inline] fn into(self) -> UntypedResource { @@ -300,12 +398,12 @@ where impl Future for Resource where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { type Output = Result>>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let inner = self.state.as_ref().unwrap().clone(); + let mut inner = self.state.as_ref().unwrap().clone(); std::pin::Pin::new(&mut inner) .poll(cx) .map(|r| r.map(|_| self.clone())) @@ -315,7 +413,7 @@ where #[doc(hidden)] pub struct ResourceDataRef<'a, T> where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { guard: MutexGuard<'a, ResourceState>, phantom: PhantomData, @@ -323,7 +421,7 @@ where impl<'a, T> Debug for ResourceDataRef<'a, T> where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match *self.guard { @@ -348,7 +446,7 @@ where impl<'a, T> Deref for ResourceDataRef<'a, T> where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { type Target = T; @@ -373,7 +471,7 @@ where impl<'a, T> DerefMut for ResourceDataRef<'a, T> where - T: ResourceData, + T: ResourceData + TypeUuidProvider, { fn deref_mut(&mut self) -> &mut Self::Target { match *self.guard { @@ -395,53 +493,3 @@ where } } } - -/// Defines a new resource type via new-type wrapper. -#[macro_export] -macro_rules! define_new_resource { - ($(#[$meta:meta])* $name:ident<$state:ty>) => { - $(#[$meta])* - #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] - #[repr(transparent)] - pub struct $name(pub Resource<$state>); - - impl Visit for $name { - fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult { - self.0.visit(name, visitor) - } - } - - impl From> for $name { - fn from(resource: Resource<$state>) -> Self { - $name(resource) - } - } - - impl std::ops::Deref for $name { - type Target = Resource<$state>; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl std::ops::DerefMut for $name { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - - impl std::future::Future for $name { - type Output = Result>>; - - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - std::pin::Pin::new(&mut self.0) - .poll(cx) - .map(|r| r.map(|_| self.clone())) - } - } - }; -} diff --git a/fyrox-resource/src/manager.rs b/fyrox-resource/src/manager.rs index 98750f0a4..d0c5e9c44 100644 --- a/fyrox-resource/src/manager.rs +++ b/fyrox-resource/src/manager.rs @@ -4,17 +4,19 @@ use crate::{ container::{Container, ResourceContainer}, state::ResourceState, task::TaskPool, - Resource, ResourceData, ResourceLoadError, UntypedResource, + Resource, ResourceData, UntypedResource, }; +use fyrox_core::uuid::Uuid; use fyrox_core::{ futures::future::join_all, make_relative_path, notify, parking_lot::{Mutex, MutexGuard}, watcher::FileSystemWatcher, + TypeUuidProvider, }; use std::{ fmt::{Debug, Display, Formatter}, - ops::Deref, + marker::PhantomData, path::Path, sync::Arc, }; @@ -69,34 +71,30 @@ pub struct ResourceManager { /// An error that may occur during texture registration. #[derive(Debug)] pub enum ResourceRegistrationError { - /// Texture saving has failed. - Texture(Arc), - /// Texture was in invalid state (Pending, LoadErr) + /// Resource saving has failed. + UnableToRegister, + /// Resource was in invalid state (Pending, LoadErr) InvalidState, - /// Texture is already registered. + /// Resource is already registered. AlreadyRegistered, } impl Display for ResourceRegistrationError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - ResourceRegistrationError::Texture(v) => Display::fmt(v, f), + ResourceRegistrationError::UnableToRegister => { + write!(f, "Unable to register the resource!") + } ResourceRegistrationError::InvalidState => { - write!(f, "A texture was in invalid state!") + write!(f, "A resource was in invalid state!") } ResourceRegistrationError::AlreadyRegistered => { - write!(f, "A texture is already registered!") + write!(f, "A resource is already registered!") } } } } -impl From> for ResourceRegistrationError { - fn from(e: Arc) -> Self { - Self::Texture(e) - } -} - impl ResourceManager { /// Creates a resource manager with default settings and loaders. pub fn new() -> Self { @@ -118,20 +116,46 @@ impl ResourceManager { self.state.lock() } - pub fn request, T: Deref>, D: ResourceData>( - &self, - path: P, - ) -> T { - self.state().containers_mut().resources.request(path) + pub fn request(&self, path: P) -> Resource + where + P: AsRef, + T: ResourceData + TypeUuidProvider, + { + let untyped = self + .state() + .containers_mut() + .resources + .request(path, ::type_uuid()); + let actual_type_uuid = untyped.type_uuid(); + assert_eq!(actual_type_uuid, ::type_uuid()); + Resource { + state: Some(untyped), + phantom: PhantomData::, + } } - /// Saves given texture in the specified path and registers it in resource manager, so + pub fn request_untyped

(&self, path: P, type_uuid: Uuid) -> UntypedResource + where + P: AsRef, + { + self.state() + .containers_mut() + .resources + .request(path, type_uuid) + } + + /// Saves given resources in the specified path and registers it in resource manager, so /// it will be accessible through it later. - pub fn register>( + pub fn register( &self, resource: UntypedResource, path: P, - ) -> Result<(), ResourceRegistrationError> { + mut on_register: F, + ) -> Result<(), ResourceRegistrationError> + where + P: AsRef, + F: FnMut(&dyn ResourceData, &Path) -> bool, + { let mut state = self.state(); if state.containers().resources.find(path.as_ref()).is_some() { Err(ResourceRegistrationError::AlreadyRegistered) @@ -140,8 +164,8 @@ impl ResourceManager { match &mut *texture_state { ResourceState::Ok(data) => { data.set_path(path.as_ref().to_path_buf()); - if let Err(e) = data.save() { - Err(ResourceRegistrationError::Texture(e)) + if !on_register(&**data, path.as_ref()) { + Err(ResourceRegistrationError::UnableToRegister) } else { std::mem::drop(texture_state); state.containers_mut().resources.push(resource); diff --git a/fyrox-resource/src/state.rs b/fyrox-resource/src/state.rs index ff7d64882..a159ef603 100644 --- a/fyrox-resource/src/state.rs +++ b/fyrox-resource/src/state.rs @@ -57,6 +57,7 @@ pub enum ResourceState { path: PathBuf, /// List of wakers to wake future when resource is fully loaded. wakers: Vec, + type_uuid: Uuid, }, /// An error has occurred during the load. LoadError { @@ -64,6 +65,7 @@ pub enum ResourceState { path: PathBuf, /// An error. This wrapped in Option only to be Default_ed. error: Option>, + type_uuid: Uuid, }, /// Actual resource data when it is fully loaded. Ok(Box), @@ -82,13 +84,21 @@ impl Visit for ResourceState { let mut path = PathBuf::new(); path.visit("Path", &mut region)?; + let mut type_uuid = Uuid::default(); + let _ = type_uuid.visit("TypeUuid", &mut region); + *self = Self::Pending { path, wakers: Default::default(), + type_uuid, }; Ok(()) - } else if let Self::Pending { path, .. } = self { + } else if let Self::Pending { + path, type_uuid, .. + } = self + { + let _ = type_uuid.visit("TypeUuid", &mut region); path.visit("Path", &mut region) } else { Err(VisitError::User("Enum variant mismatch!".to_string())) @@ -99,10 +109,21 @@ impl Visit for ResourceState { let mut path = PathBuf::new(); path.visit("Path", &mut region)?; - *self = Self::LoadError { path, error: None }; + let mut type_uuid = Uuid::default(); + let _ = type_uuid.visit("TypeUuid", &mut region); + + *self = Self::LoadError { + path, + error: None, + type_uuid, + }; Ok(()) - } else if let Self::LoadError { path, .. } = self { + } else if let Self::LoadError { + path, type_uuid, .. + } = self + { + let _ = type_uuid.visit("TypeUuid", &mut region); path.visit("Path", &mut region) } else { Err(VisitError::User("Enum variant mismatch!".to_string())) @@ -151,16 +172,25 @@ impl Visit for ResourceState { impl ResourceState { /// Creates new resource in pending state. #[inline] - pub fn new_pending(path: PathBuf) -> Self { + pub fn new_pending(path: PathBuf, type_uuid: Uuid) -> Self { Self::Pending { path, wakers: Default::default(), + type_uuid, } } #[inline] - pub fn new_load_error(path: PathBuf, error: Option>) -> Self { - Self::LoadError { path, error } + pub fn new_load_error( + path: PathBuf, + error: Option>, + type_uuid: Uuid, + ) -> Self { + Self::LoadError { + path, + error, + type_uuid, + } } #[inline] @@ -175,22 +205,34 @@ impl ResourceState { /// Switches the internal state of the resource to [`ResourceState::Pending`]. pub fn switch_to_pending_state(&mut self) { match self { - ResourceState::LoadError { path, .. } => { + ResourceState::LoadError { + path, type_uuid, .. + } => { *self = ResourceState::Pending { path: std::mem::take(path), wakers: Default::default(), + type_uuid: *type_uuid, } } ResourceState::Ok(data) => { *self = ResourceState::Pending { path: data.path().to_path_buf(), wakers: Default::default(), + type_uuid: data.type_uuid(), } } _ => (), } } + pub fn type_uuid(&self) -> Uuid { + match self { + ResourceState::Pending { type_uuid, .. } => *type_uuid, + ResourceState::LoadError { type_uuid, .. } => *type_uuid, + ResourceState::Ok(data) => data.type_uuid(), + } + } + #[inline] fn id(&self) -> u32 { match self { @@ -234,9 +276,11 @@ impl ResourceState { /// Changes internal state to [`ResourceState::LoadError`]. pub fn commit_error(&mut self, path: PathBuf, error: E) { + let type_uuid = self.type_uuid(); self.commit(ResourceState::LoadError { path, error: Some(Arc::new(error)), + type_uuid, }) } } diff --git a/fyrox-resource/src/untyped.rs b/fyrox-resource/src/untyped.rs index 5b12de6d9..6edf4a13b 100644 --- a/fyrox-resource/src/untyped.rs +++ b/fyrox-resource/src/untyped.rs @@ -1,9 +1,10 @@ -use crate::{state::ResourceState, ResourceData, ResourceLoadError}; -use fyrox_core::{parking_lot::Mutex, visitor::prelude::*}; +use crate::{state::ResourceState, Resource, ResourceData, ResourceLoadError}; +use fyrox_core::{parking_lot::Mutex, uuid::Uuid, visitor::prelude::*, TypeUuidProvider}; use std::{ fmt::{Debug, Formatter}, future::Future, hash::{Hash, Hasher}, + marker::PhantomData, path::PathBuf, pin::Pin, sync::Arc, @@ -24,6 +25,7 @@ impl Default for UntypedResource { fn default() -> Self { Self(Arc::new(Mutex::new(ResourceState::new_pending( Default::default(), + Default::default(), )))) } } @@ -49,20 +51,30 @@ impl Hash for UntypedResource { } impl UntypedResource { - pub fn new_pending(path: PathBuf) -> Self { - Self(Arc::new(Mutex::new(ResourceState::new_pending(path)))) + pub fn new_pending(path: PathBuf, type_uuid: Uuid) -> Self { + Self(Arc::new(Mutex::new(ResourceState::new_pending( + path, type_uuid, + )))) } pub fn new_ok(data: T) -> Self { Self(Arc::new(Mutex::new(ResourceState::new_ok(data)))) } - pub fn new_load_error(path: PathBuf, error: Option>) -> Self { + pub fn new_load_error( + path: PathBuf, + error: Option>, + type_uuid: Uuid, + ) -> Self { Self(Arc::new(Mutex::new(ResourceState::new_load_error( - path, error, + path, error, type_uuid, )))) } + pub fn type_uuid(&self) -> Uuid { + self.0.lock().type_uuid() + } + /// Returns true if the resource is still loading. pub fn is_loading(&self) -> bool { matches!(*self.0.lock(), ResourceState::Pending { .. }) @@ -79,6 +91,28 @@ impl UntypedResource { pub fn key(&self) -> usize { (&*self.0 as *const _) as usize } + + pub fn path(&self) -> PathBuf { + match &*self.0.lock() { + ResourceState::Pending { path, .. } => path.clone(), + ResourceState::LoadError { path, .. } => path.clone(), + ResourceState::Ok(data) => data.path().to_path_buf(), + } + } + + pub fn try_cast(&self) -> Option> + where + T: ResourceData + TypeUuidProvider, + { + if self.type_uuid() == ::type_uuid() { + Some(Resource { + state: Some(self.clone()), + phantom: PhantomData::, + }) + } else { + None + } + } } impl Future for UntypedResource { @@ -86,7 +120,8 @@ impl Future for UntypedResource { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let state = self.0.clone(); - match *state.lock() { + let mut guard = state.lock(); + match *guard { ResourceState::Pending { ref mut wakers, .. } => { // Collect wakers, so we'll be able to wake task when worker thread finish loading. let cx_waker = cx.waker(); diff --git a/fyrox-sound/src/buffer/mod.rs b/fyrox-sound/src/buffer/mod.rs index 99660240b..d7d7fe2bd 100644 --- a/fyrox-sound/src/buffer/mod.rs +++ b/fyrox-sound/src/buffer/mod.rs @@ -12,8 +12,8 @@ use crate::{ buffer::{generic::GenericBuffer, streaming::StreamingBuffer}, error::SoundError, }; -use fyrox_core::{io::FileLoadError, reflect::prelude::*, uuid::Uuid, visitor::prelude::*}; -use fyrox_resource::{define_new_resource, Resource, ResourceData, SOUND_BUFFER_RESOURCE_UUID}; +use fyrox_core::{io::FileLoadError, uuid::Uuid, visitor::prelude::*, TypeUuidProvider}; +use fyrox_resource::{Resource, ResourceData, SOUND_BUFFER_RESOURCE_UUID}; use std::{ any::Any, borrow::Cow, @@ -162,34 +162,9 @@ pub enum SoundBufferResourceLoadError { Io(FileLoadError), } -define_new_resource!( - /// A shared sound buffer resource. - #[derive(Reflect)] - #[reflect(hide_all)] - SoundBufferResource -); - -impl SoundBufferResource { - /// Tries to create new streaming sound buffer from a given data source. Returns sound source - /// wrapped into Arc> that can be directly used with sound sources. - pub fn new_streaming(data_source: DataSource) -> Result { - Ok(Self(Resource::new_ok(SoundBufferState::Streaming( - StreamingBuffer::new(data_source)?, - )))) - } - - /// Tries to create new generic sound buffer from a given data source. Returns sound source - /// wrapped into Arc> that can be directly used with sound sources. - pub fn new_generic(data_source: DataSource) -> Result { - Ok(Self(Resource::new_ok(SoundBufferState::Generic( - GenericBuffer::new(data_source)?, - )))) - } -} - /// Sound buffer is a data source for sound sources. See module documentation for more info. #[derive(Debug, Visit)] -pub enum SoundBufferState { +pub enum SoundBuffer { /// General-purpose buffer, usually contains all the data and allows random /// access to samples. It is also used to make streaming buffer via composition. Generic(GenericBuffer), @@ -201,7 +176,15 @@ pub enum SoundBufferState { Streaming(StreamingBuffer), } -impl SoundBufferState { +pub type SoundBufferResource = Resource; + +impl TypeUuidProvider for SoundBuffer { + fn type_uuid() -> Uuid { + SOUND_BUFFER_RESOURCE_UUID + } +} + +impl SoundBuffer { /// Tries to create new streaming sound buffer from a given data source. It returns raw sound /// buffer that has to be wrapped into Arc> for use with sound sources. pub fn raw_streaming(data_source: DataSource) -> Result { @@ -213,39 +196,59 @@ impl SoundBufferState { pub fn raw_generic(data_source: DataSource) -> Result { Ok(Self::Generic(GenericBuffer::new(data_source)?)) } -} -impl Default for SoundBufferState { - fn default() -> Self { - SoundBufferState::Generic(Default::default()) + /// Tries to create new streaming sound buffer from a given data source. Returns sound source + /// wrapped into Arc> that can be directly used with sound sources. + pub fn new_streaming_resource( + data_source: DataSource, + ) -> Result, DataSource> { + Ok(Resource::new_ok(SoundBuffer::Streaming( + StreamingBuffer::new(data_source)?, + ))) + } + + /// Tries to create new generic sound buffer from a given data source. Returns sound source + /// wrapped into Arc> that can be directly used with sound sources. + pub fn new_generic_resource( + data_source: DataSource, + ) -> Result, DataSource> { + Ok(Resource::new_ok(SoundBuffer::Generic(GenericBuffer::new( + data_source, + )?))) } } -impl Deref for SoundBufferState { +impl Default for SoundBuffer { + fn default() -> Self { + SoundBuffer::Generic(Default::default()) + } +} + +impl Deref for SoundBuffer { type Target = GenericBuffer; /// Returns shared reference to generic buffer for any enum variant. It is possible because /// streaming sound buffers are built on top of generic buffers. fn deref(&self) -> &Self::Target { match self { - SoundBufferState::Generic(v) => v, - SoundBufferState::Streaming(v) => v, + SoundBuffer::Generic(v) => v, + SoundBuffer::Streaming(v) => v, } } } -impl DerefMut for SoundBufferState { +impl DerefMut for SoundBuffer { /// Returns mutable reference to generic buffer for any enum variant. It is possible because /// streaming sound buffers are built on top of generic buffers. fn deref_mut(&mut self) -> &mut Self::Target { match self { - SoundBufferState::Generic(v) => v, - SoundBufferState::Streaming(v) => v, + SoundBuffer::Generic(v) => v, + SoundBuffer::Streaming(v) => v, } } } -impl ResourceData for SoundBufferState { +impl ResourceData for SoundBuffer { fn path(&self) -> Cow { Cow::from(&self.external_source_path) } diff --git a/fyrox-sound/src/source.rs b/fyrox-sound/src/source.rs index d4269f15c..8dc1d06cd 100644 --- a/fyrox-sound/src/source.rs +++ b/fyrox-sound/src/source.rs @@ -29,7 +29,7 @@ #![allow(clippy::float_cmp)] use crate::{ - buffer::{streaming::StreamingBuffer, SoundBufferResource, SoundBufferState}, + buffer::{streaming::StreamingBuffer, SoundBuffer, SoundBufferResource}, bus::AudioBusGraph, context::DistanceModel, error::SoundError, @@ -210,7 +210,7 @@ impl SoundSource { // If we already have streaming buffer assigned make sure to decrease use count // so it can be reused later on if needed. if let Some(buffer) = self.buffer.clone() { - if let SoundBufferState::Streaming(ref mut streaming) = *buffer.data_ref() { + if let SoundBuffer::Streaming(ref mut streaming) = *buffer.data_ref() { streaming.use_count = streaming.use_count.saturating_sub(1); } } @@ -222,7 +222,7 @@ impl SoundSource { } ResourceStateRefMut::Ok(locked_buffer) => { // Check new buffer if streaming - it must not be used by anyone else. - if let SoundBufferState::Streaming(ref mut streaming) = *locked_buffer { + if let SoundBuffer::Streaming(ref mut streaming) = *locked_buffer { if streaming.use_count != 0 { return Err(SoundError::StreamingBufferAlreadyInUse); } @@ -340,7 +340,7 @@ impl SoundSource { if let Some(buffer) = self.buffer.as_ref() { let mut buffer = buffer.data_ref(); - if let SoundBufferState::Streaming(ref mut streaming) = *buffer { + if let SoundBuffer::Streaming(ref mut streaming) = *buffer { streaming.rewind()?; } } @@ -464,7 +464,7 @@ impl SoundSource { pub fn set_playback_time(&mut self, time: Duration) { if let Some(buffer) = self.buffer.as_ref() { let mut buffer = buffer.data_ref(); - if let SoundBufferState::Streaming(ref mut streaming) = *buffer { + if let SoundBuffer::Streaming(ref mut streaming) = *buffer { // Make sure decoder is at right position. streaming.time_seek(time); } @@ -472,14 +472,14 @@ impl SoundSource { self.playback_pos = time.as_secs_f64() * buffer.sample_rate as f64; // Then adjust buffer read position. self.buf_read_pos = match *buffer { - SoundBufferState::Streaming(ref mut streaming) => { + SoundBuffer::Streaming(ref mut streaming) => { // Make sure to load correct data into buffer from decoder. streaming.read_next_block(); // Streaming sources has different buffer read position because // buffer contains only small portion of data. self.playback_pos % (StreamingBuffer::STREAM_SAMPLE_COUNT as f64) } - SoundBufferState::Generic(_) => self.playback_pos, + SoundBuffer::Generic(_) => self.playback_pos, }; assert!( self.buf_read_pos * (buffer.channel_count() as f64) < buffer.samples().len() as f64 @@ -506,7 +506,7 @@ impl SoundSource { self.frame_samples.resize(amount, (0.0, 0.0)); } - fn render_playing(&mut self, buffer: &mut SoundBufferState, amount: usize) { + fn render_playing(&mut self, buffer: &mut SoundBuffer, amount: usize) { let mut count = 0; loop { count += self.render_until_block_end(buffer, amount - count); @@ -517,7 +517,7 @@ impl SoundSource { let channel_count = buffer.channel_count(); let len = buffer.samples().len(); let mut end_reached = true; - if let SoundBufferState::Streaming(streaming) = buffer { + if let SoundBuffer::Streaming(streaming) = buffer { // Means that this is the last available block. if len != channel_count * StreamingBuffer::STREAM_SAMPLE_COUNT { let _ = streaming.rewind(); @@ -542,11 +542,7 @@ impl SoundSource { // Renders until the end of the block or until amount samples is written and returns // the number of written samples. - fn render_until_block_end( - &mut self, - buffer: &mut SoundBufferState, - mut amount: usize, - ) -> usize { + fn render_until_block_end(&mut self, buffer: &mut SoundBuffer, mut amount: usize) -> usize { let step = self.pitch * self.resampling_multiplier; if step == 1.0 { if self.buf_read_pos < 0.0 { @@ -581,7 +577,7 @@ impl SoundSource { // Does linear resampling while rendering until the end of the block. fn render_until_block_end_resample( &mut self, - buffer: &mut SoundBufferState, + buffer: &mut SoundBuffer, amount: usize, step: f64, ) -> usize { @@ -675,7 +671,7 @@ impl Drop for SoundSource { fn drop(&mut self) { if let Some(buffer) = self.buffer.as_ref() { let mut buffer = buffer.data_ref(); - if let SoundBufferState::Streaming(ref mut streaming) = *buffer { + if let SoundBuffer::Streaming(ref mut streaming) = *buffer { streaming.use_count = streaming.use_count.saturating_sub(1); } } diff --git a/src/animation/spritesheet/mod.rs b/src/animation/spritesheet/mod.rs index 25fa9afb0..50b4b38ab 100644 --- a/src/animation/spritesheet/mod.rs +++ b/src/animation/spritesheet/mod.rs @@ -6,7 +6,7 @@ use crate::{ animation::spritesheet::signal::Signal, core::{algebra::Vector2, math::Rect, reflect::prelude::*, visitor::prelude::*}, - resource::texture::Texture, + resource::texture::TextureResource, }; use std::collections::vec_deque::VecDeque; use strum_macros::{AsRefStr, EnumString, EnumVariantNames}; @@ -55,7 +55,7 @@ pub struct SpriteSheetFramesContainer { size: Vector2, frames: Vec>, #[visit(optional)] - texture: Option, + texture: Option, } impl SpriteSheetFramesContainer { @@ -112,7 +112,7 @@ impl SpriteSheetFramesContainer { } /// Returns current texture of the container. To set a texture use sprite sheet animation methods. - pub fn texture(&self) -> Option { + pub fn texture(&self) -> Option { self.texture.clone() } } @@ -140,7 +140,7 @@ pub struct SpriteSheetAnimation { signals: Vec, #[visit(optional)] #[reflect(setter = "set_texture")] - texture: Option, + texture: Option, #[reflect(hidden)] #[visit(skip)] events: VecDeque, @@ -305,13 +305,13 @@ impl SpriteSheetAnimation { } /// Sets new texture for the animation. - pub fn set_texture(&mut self, texture: Option) -> Option { + pub fn set_texture(&mut self, texture: Option) -> Option { self.frames_container.texture = texture.clone(); std::mem::replace(&mut self.texture, texture) } /// Returns current texture of the animation. - pub fn texture(&self) -> Option { + pub fn texture(&self) -> Option { self.texture.clone() } diff --git a/src/engine/executor.rs b/src/engine/executor.rs index f46d5609e..fce4f27d7 100644 --- a/src/engine/executor.rs +++ b/src/engine/executor.rs @@ -1,10 +1,10 @@ //! Executor is a small wrapper that manages plugins and scripts for your game. use crate::{ + asset::manager::ResourceManager, core::instant::Instant, engine::{ - resource_manager::ResourceManager, Engine, EngineInitParams, GraphicsContext, - GraphicsContextParams, SerializationContext, + Engine, EngineInitParams, GraphicsContext, GraphicsContextParams, SerializationContext, }, event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -71,7 +71,7 @@ impl Executor { let serialization_context = Arc::new(SerializationContext::new()); let engine = Engine::new(EngineInitParams { graphics_context_params, - resource_manager: ResourceManager::new(serialization_context.clone()), + resource_manager: ResourceManager::new(), serialization_context, }) .unwrap(); diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 718570ffb..fd622ff31 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -7,18 +7,19 @@ pub mod error; pub mod executor; pub mod resource_loaders; +use crate::resource::model::Model; use crate::{ - core::{algebra::Vector2, futures::executor::block_on, instant, pool::Handle}, - engine::{ - error::EngineError, - resource_manager::{container::event::ResourceEvent, ResourceManager, ResourceWaitContext}, + asset::{ + container::event::ResourceEvent, manager::ResourceManager, manager::ResourceWaitContext, }, + core::{algebra::Vector2, futures::executor::block_on, instant, pool::Handle}, + engine::error::EngineError, event::Event, event_loop::ControlFlow, gui::UserInterface, plugin::{Plugin, PluginConstructor, PluginContext, PluginRegistrationContext}, renderer::{framework::error::FrameworkError, Renderer}, - resource::{model::Model, texture::TextureKind}, + resource::{model::ModelResource, texture::TextureKind}, scene::{ base::NodeScriptMessage, graph::GraphUpdateSwitches, @@ -198,7 +199,7 @@ pub struct Engine { performance_statistics: PerformanceStatistics, - model_events_receiver: Receiver>, + model_events_receiver: Receiver, #[allow(dead_code)] // Keep engine instance alive. sound_engine: SoundEngine, @@ -605,26 +606,28 @@ impl ScriptProcessor { } struct ResourceGraphVertex { - resource: Model, + resource: ModelResource, children: Vec, } impl ResourceGraphVertex { - pub fn new(model: Model, resource_manager: ResourceManager) -> Self { + pub fn new(model: ModelResource, resource_manager: ResourceManager) -> Self { let mut children = Vec::new(); // Look for dependent resources. let mut dependent_resources = HashSet::new(); - for other_model in resource_manager.state().containers().models.iter() { - let state = other_model.state(); - if let ResourceStateRef::Ok(model_data) = state.get() { - if model_data - .get_scene() - .graph - .linear_iter() - .any(|n| n.resource.as_ref().map_or(false, |r| r == &model)) - { - dependent_resources.insert(other_model.clone()); + for resource in resource_manager.state().containers().resources.iter() { + if let Some(other_model) = resource.try_cast::() { + let mut state = other_model.state(); + if let ResourceStateRef::Ok(model_data) = state.get() { + if model_data + .get_scene() + .graph + .linear_iter() + .any(|n| n.resource.as_ref().map_or(false, |r| r == &model)) + { + dependent_resources.insert(other_model.clone()); + } } } } @@ -644,7 +647,7 @@ impl ResourceGraphVertex { pub fn resolve(&self) { Log::info(format!( "Resolving {} resource from dependency graph...", - self.resource.state().path().display() + self.resource.path().display() )); // Wait until resource is fully loaded, then resolve. @@ -663,7 +666,7 @@ struct ResourceDependencyGraph { } impl ResourceDependencyGraph { - pub fn new(model: Model, resource_manager: ResourceManager) -> Self { + pub fn new(model: ModelResource, resource_manager: ResourceManager) -> Self { Self { root: ResourceGraphVertex::new(model, resource_manager), } @@ -828,7 +831,7 @@ impl Engine { resource_manager .state() .containers_mut() - .models + .resources .event_broadcaster .add(rx); @@ -1362,22 +1365,24 @@ impl Engine { /// You should only call this manually if you don't use that method. pub fn handle_model_events(&mut self) { while let Ok(event) = self.model_events_receiver.try_recv() { - if let ResourceEvent::Reloaded(model) = event { - Log::info(format!( - "A model resource {} was reloaded, propagating changes...", - model.state().path().display() - )); + if let ResourceEvent::Reloaded(resource) = event { + if let Some(model) = resource.try_cast::() { + Log::info(format!( + "A model resource {} was reloaded, propagating changes...", + model.path().display() + )); - // Build resource dependency graph and resolve it first. - ResourceDependencyGraph::new(model, self.resource_manager.clone()).resolve(); + // Build resource dependency graph and resolve it first. + ResourceDependencyGraph::new(model, self.resource_manager.clone()).resolve(); - Log::info("Propagating changes to active scenes..."); + Log::info("Propagating changes to active scenes..."); - // Resolve all scenes. - // TODO: This might be inefficient if there is bunch of scenes loaded, - // however this seems to be very rare case so it should be ok. - for scene in self.scenes.iter_mut() { - scene.resolve(); + // Resolve all scenes. + // TODO: This might be inefficient if there is bunch of scenes loaded, + // however this seems to be very rare case so it should be ok. + for scene in self.scenes.iter_mut() { + scene.resolve(); + } } } } diff --git a/src/engine/resource_loaders/curve.rs b/src/engine/resource_loaders/curve.rs index 6210e7aad..f440ece40 100644 --- a/src/engine/resource_loaders/curve.rs +++ b/src/engine/resource_loaders/curve.rs @@ -5,29 +5,29 @@ use crate::{ container::event::ResourceEventBroadcaster, loader::{BoxedLoaderFuture, ResourceLoader}, }, - resource::curve::{CurveImportOptions, CurveResource, CurveResourceState}, + resource::curve::CurveResourceState, utils::log::Log, }; +use fyrox_resource::untyped::UntypedResource; /// Default implementation for curve loading. pub struct CurveLoader; -impl ResourceLoader for CurveLoader { +impl ResourceLoader for CurveLoader { fn load( &self, - curve: CurveResource, - _default_import_options: CurveImportOptions, - event_broadcaster: ResourceEventBroadcaster, + curve: UntypedResource, + event_broadcaster: ResourceEventBroadcaster, reload: bool, ) -> BoxedLoaderFuture { Box::pin(async move { - let path = curve.state().path().to_path_buf(); + let path = curve.0.lock().path().to_path_buf(); match CurveResourceState::from_file(&path).await { Ok(curve_state) => { Log::info(format!("Curve {:?} is loaded!", path)); - curve.state().commit_ok(curve_state); + curve.0.lock().commit_ok(curve_state); event_broadcaster.broadcast_loaded_or_reloaded(curve, reload); } @@ -37,7 +37,7 @@ impl ResourceLoader for CurveLoader { path, error )); - curve.state().commit_error(path, error); + curve.0.lock().commit_error(path, error); } } }) diff --git a/src/engine/resource_loaders/model.rs b/src/engine/resource_loaders/model.rs index 9e3c17bed..a8c481b66 100644 --- a/src/engine/resource_loaders/model.rs +++ b/src/engine/resource_loaders/model.rs @@ -8,9 +8,10 @@ use crate::{ options::try_get_import_settings, }, engine::SerializationContext, - resource::model::{Model, ModelData, ModelImportOptions}, + resource::model::{Model, ModelImportOptions}, utils::log::Log, }; +use fyrox_resource::untyped::UntypedResource; use std::sync::Arc; /// Default implementation for model loading. @@ -20,32 +21,32 @@ pub struct ModelLoader { /// Node constructors contains a set of constructors that allows to build a node using its /// type UUID. pub serialization_context: Arc, + pub default_import_options: ModelImportOptions, } -impl ResourceLoader for ModelLoader { +impl ResourceLoader for ModelLoader { fn load( &self, - model: Model, - default_import_options: ModelImportOptions, - event_broadcaster: ResourceEventBroadcaster, + model: UntypedResource, + event_broadcaster: ResourceEventBroadcaster, reload: bool, ) -> BoxedLoaderFuture { let resource_manager = self.resource_manager.clone(); let node_constructors = self.serialization_context.clone(); + let default_import_options = self.default_import_options.clone(); Box::pin(async move { - let path = model.state().path().to_path_buf(); + let path = model.path().to_path_buf(); let import_options = try_get_import_settings(&path) .await .unwrap_or(default_import_options); - match ModelData::load(&path, node_constructors, resource_manager, import_options).await - { + match Model::load(&path, node_constructors, resource_manager, import_options).await { Ok(raw_model) => { Log::info(format!("Model {:?} is loaded!", path)); - model.state().commit_ok(raw_model); + model.0.lock().commit_ok(raw_model); event_broadcaster.broadcast_loaded_or_reloaded(model, reload); } @@ -55,7 +56,7 @@ impl ResourceLoader for ModelLoader { path, error )); - model.state().commit_error(path, error); + model.0.lock().commit_error(path, error); } } }) diff --git a/src/engine/resource_loaders/shader.rs b/src/engine/resource_loaders/shader.rs index f770a7fa9..def30ce46 100644 --- a/src/engine/resource_loaders/shader.rs +++ b/src/engine/resource_loaders/shader.rs @@ -5,29 +5,29 @@ use crate::{ container::event::ResourceEventBroadcaster, loader::{BoxedLoaderFuture, ResourceLoader}, }, - material::shader::{Shader, ShaderImportOptions, ShaderState}, + material::shader::Shader, utils::log::Log, }; +use fyrox_resource::untyped::UntypedResource; /// Default implementation for shader loading. pub struct ShaderLoader; -impl ResourceLoader for ShaderLoader { +impl ResourceLoader for ShaderLoader { fn load( &self, - shader: Shader, - _default_import_options: ShaderImportOptions, - event_broadcaster: ResourceEventBroadcaster, + shader: UntypedResource, + event_broadcaster: ResourceEventBroadcaster, reload: bool, ) -> BoxedLoaderFuture { Box::pin(async move { - let path = shader.state().path().to_path_buf(); + let path = shader.path().to_path_buf(); - match ShaderState::from_file(&path).await { + match Shader::from_file(&path).await { Ok(shader_state) => { Log::info(format!("Shader {:?} is loaded!", path)); - shader.state().commit_ok(shader_state); + shader.0.lock().commit_ok(shader_state); event_broadcaster.broadcast_loaded_or_reloaded(shader, reload); } @@ -37,7 +37,7 @@ impl ResourceLoader for ShaderLoader { path, error )); - shader.state().commit_error(path, error); + shader.0.lock().commit_error(path, error); } } }) diff --git a/src/engine/resource_loaders/sound.rs b/src/engine/resource_loaders/sound.rs index 9c029c60d..d88c24598 100644 --- a/src/engine/resource_loaders/sound.rs +++ b/src/engine/resource_loaders/sound.rs @@ -9,9 +9,8 @@ use crate::{ core::reflect::prelude::*, utils::log::Log, }; -use fyrox_sound::buffer::{ - DataSource, SoundBufferResource, SoundBufferResourceLoadError, SoundBufferState, -}; +use fyrox_resource::untyped::UntypedResource; +use fyrox_sound::buffer::{DataSource, SoundBuffer, SoundBufferResourceLoadError}; use serde::{Deserialize, Serialize}; /// Defines sound buffer resource import options. @@ -24,18 +23,21 @@ pub struct SoundBufferImportOptions { impl ImportOptions for SoundBufferImportOptions {} /// Default implementation for sound buffer loading. -pub struct SoundBufferLoader; +pub struct SoundBufferLoader { + pub default_import_options: SoundBufferImportOptions, +} -impl ResourceLoader for SoundBufferLoader { +impl ResourceLoader for SoundBufferLoader { fn load( &self, - resource: SoundBufferResource, - default_import_options: SoundBufferImportOptions, - event_broadcaster: ResourceEventBroadcaster, + resource: UntypedResource, + event_broadcaster: ResourceEventBroadcaster, reload: bool, ) -> BoxedLoaderFuture { + let default_import_options = self.default_import_options.clone(); + Box::pin(async move { - let path = resource.state().path().to_path_buf(); + let path = resource.path().to_path_buf(); let import_options = try_get_import_settings(&path) .await @@ -44,20 +46,20 @@ impl ResourceLoader for SoundBuff match DataSource::from_file(&path).await { Ok(source) => { let buffer = if import_options.stream { - SoundBufferState::raw_streaming(source) + SoundBuffer::raw_streaming(source) } else { - SoundBufferState::raw_generic(source) + SoundBuffer::raw_generic(source) }; match buffer { Ok(sound_buffer) => { - resource.state().commit_ok(sound_buffer); + resource.0.lock().commit_ok(sound_buffer); event_broadcaster.broadcast_loaded_or_reloaded(resource, reload); Log::info(format!("Sound buffer {:?} is loaded!", path)); } Err(_) => { - resource.state().commit_error( + resource.0.lock().commit_error( path.clone(), SoundBufferResourceLoadError::UnsupportedFormat, ); @@ -70,7 +72,8 @@ impl ResourceLoader for SoundBuff Log::err(format!("Invalid data source for sound buffer: {:?}", e)); resource - .state() + .0 + .lock() .commit_error(path.clone(), SoundBufferResourceLoadError::Io(e)); } } diff --git a/src/engine/resource_loaders/texture.rs b/src/engine/resource_loaders/texture.rs index 31b804218..71fff7f81 100644 --- a/src/engine/resource_loaders/texture.rs +++ b/src/engine/resource_loaders/texture.rs @@ -7,23 +7,27 @@ use crate::{ options::try_get_import_settings, }, core::instant, - resource::texture::{Texture, TextureData, TextureImportOptions}, + resource::texture::{Texture, TextureImportOptions}, utils::log::Log, }; +use fyrox_resource::untyped::UntypedResource; /// Default implementation for texture loading. -pub struct TextureLoader; +pub struct TextureLoader { + pub default_import_options: TextureImportOptions, +} -impl ResourceLoader for TextureLoader { +impl ResourceLoader for TextureLoader { fn load( &self, - texture: Texture, - default_import_options: TextureImportOptions, - event_broadcaster: ResourceEventBroadcaster, + texture: UntypedResource, + event_broadcaster: ResourceEventBroadcaster, reload: bool, ) -> BoxedLoaderFuture { + let default_import_options = self.default_import_options.clone(); + Box::pin(async move { - let path = texture.state().path().to_path_buf(); + let path = texture.path().to_path_buf(); let import_options = try_get_import_settings(&path) .await @@ -32,8 +36,7 @@ impl ResourceLoader for TextureLoader { let gen_mip_maps = import_options.minification_filter.is_using_mip_mapping(); let time = instant::Instant::now(); - match TextureData::load_from_file(&path, import_options.compression, gen_mip_maps).await - { + match Texture::load_from_file(&path, import_options.compression, gen_mip_maps).await { Ok(mut raw_texture) => { Log::info(format!( "Texture {:?} is loaded in {:?}!", @@ -47,7 +50,7 @@ impl ResourceLoader for TextureLoader { raw_texture.set_s_wrap_mode(import_options.s_wrap_mode); raw_texture.set_t_wrap_mode(import_options.t_wrap_mode); - texture.state().commit_ok(raw_texture); + texture.0.lock().commit_ok(raw_texture); event_broadcaster.broadcast_loaded_or_reloaded(texture, reload); } @@ -57,7 +60,7 @@ impl ResourceLoader for TextureLoader { &path, &error )); - texture.state().commit_error(path, error); + texture.0.lock().commit_error(path, error); } } }) diff --git a/src/material/mod.rs b/src/material/mod.rs index 952d7d79b..9b8ecf6a4 100644 --- a/src/material/mod.rs +++ b/src/material/mod.rs @@ -4,7 +4,10 @@ #![warn(missing_docs)] +use crate::material::shader::ShaderResourceExtension; +use crate::resource::texture::Texture; use crate::{ + asset::manager::ResourceManager, core::{ algebra::{Matrix2, Matrix3, Matrix4, Vector2, Vector3, Vector4}, color::Color, @@ -13,9 +16,8 @@ use crate::{ sstorage::ImmutableString, visitor::prelude::*, }, - engine::resource_manager::ResourceManager, - material::shader::{PropertyKind, SamplerFallback, Shader}, - resource::texture::Texture, + material::shader::{PropertyKind, SamplerFallback, ShaderResource}, + resource::texture::TextureResource, }; use fxhash::FxHashMap; use std::{ @@ -118,7 +120,7 @@ pub enum PropertyValue { /// missing by very specific value in the fallback texture. Sampler { /// Actual value of the sampler. Could be [`None`], in this case `fallback` will be used. - value: Option, + value: Option, /// Sampler fallback value. fallback: SamplerFallback, @@ -234,7 +236,7 @@ impl PropertyValue { ); /// Tries to unwrap property value as texture. - pub fn as_sampler(&self) -> Option { + pub fn as_sampler(&self) -> Option { if let PropertyValue::Sampler { value, .. } = self { value.clone() } else { @@ -286,7 +288,7 @@ impl Default for PropertyValue { /// /// ```no_run /// use fyrox::{ -/// material::shader::{Shader, SamplerFallback}, +/// material::shader::{ShaderResource, SamplerFallback}, /// engine::resource_manager::ResourceManager, /// material::{Material, PropertyValue}, /// core::sstorage::ImmutableString, @@ -345,7 +347,7 @@ impl Default for PropertyValue { /// material instance. Then we populate properties as usual. #[derive(Default, Debug, Visit, Clone, Reflect)] pub struct Material { - shader: Shader, + shader: ShaderResource, properties: FxHashMap, } @@ -398,7 +400,7 @@ impl Material { /// /// ```no_run /// use fyrox::{ - /// material::shader::{Shader, SamplerFallback}, + /// material::shader::{ShaderResource, SamplerFallback}, /// engine::resource_manager::ResourceManager, /// material::{Material, PropertyValue}, /// core::sstorage::ImmutableString @@ -419,12 +421,12 @@ impl Material { /// } /// ``` pub fn standard() -> Self { - Self::from_shader(Shader::standard(), None) + Self::from_shader(ShaderResource::standard(), None) } /// Creates new instance of standard terrain material. pub fn standard_terrain() -> Self { - Self::from_shader(Shader::standard_terrain(), None) + Self::from_shader(ShaderResource::standard_terrain(), None) } /// Creates a new material instance with given shader. Each property will have default values @@ -459,7 +461,7 @@ impl Material { /// material /// } /// ``` - pub fn from_shader(shader: Shader, resource_manager: Option) -> Self { + pub fn from_shader(shader: ShaderResource, resource_manager: Option) -> Self { let data = shader.data_ref(); let mut property_values = FxHashMap::default(); @@ -483,7 +485,9 @@ impl Material { fallback: usage, } => PropertyValue::Sampler { value: default.as_ref().and_then(|path| { - resource_manager.clone().map(|rm| rm.request_texture(path)) + resource_manager + .clone() + .map(|rm| rm.request::(path)) }), fallback: *usage, }, @@ -644,7 +648,7 @@ impl Material { } /// Returns a reference to current shader. - pub fn shader(&self) -> &Shader { + pub fn shader(&self) -> &ShaderResource { &self.shader } diff --git a/src/material/shader.rs b/src/material/shader.rs index c85d75abc..07df03a97 100644 --- a/src/material/shader.rs +++ b/src/material/shader.rs @@ -1,9 +1,232 @@ //! Shader is a script for graphics card. This module contains everything related to shaders. //! -//! For more info see [`Shader`] struct docs. +//! Shader is a script for graphics adapter, it defines how to draw an object. +//! +//! # Structure +//! +//! Shader has rigid structure that could be described in this code snipped: +//! +//! ```ron +//! ( +//! // A set of properties, there could be any amount of properties. +//! properties: [ +//! ( +//! // Each property must have a name. This name must match with respective +//! // uniforms! That's is the whole point of having properties. +//! name: "diffuseTexture", +//! +//! // Value has limited set of possible variants. +//! value: Sampler(default: None, fallback: White) +//! ) +//! ], +//! +//! // A set of render passes (see a section `Render pass` for more info) +//! passes: [ +//! ( +//! // Name must match with the name of either standard render pass (see below) or +//! // one of your passes. +//! name: "Forward", +//! +//! // A set of parameters that regulate renderer pipeline state. +//! // This is mandatory field of each render pass. +//! draw_parameters: DrawParameters( +//! // A face to cull. Either Front or Back. +//! cull_face: Some(Back), +//! +//! // Color mask. Defines which colors should be written to render target. +//! color_write: ColorMask( +//! red: true, +//! green: true, +//! blue: true, +//! alpha: true, +//! ), +//! +//! // Whether to modify depth buffer or not. +//! depth_write: true, +//! +//! // Whether to use stencil test or not. +//! stencil_test: None, +//! +//! // Whether to perform depth test when drawing. +//! depth_test: true, +//! +//! // Blending options. +//! blend: Some(BlendParameters( +//! func: BlendFunc( +//! sfactor: SrcAlpha, +//! dfactor: OneMinusSrcAlpha, +//! alpha_sfactor: SrcAlpha, +//! alpha_dfactor: OneMinusSrcAlpha, +//! ), +//! equation: BlendEquation( +//! rgb: Add, +//! alpha: Add +//! ) +//! )), +//! +//! // Stencil options. +//! stencil_op: StencilOp( +//! fail: Keep, +//! zfail: Keep, +//! zpass: Keep, +//! write_mask: 0xFFFF_FFFF, +//! ), +//! ), +//! +//! // Vertex shader code. +//! vertex_shader: +//! r#" +//! #version 330 core +//! +//! layout(location = 0) in vec3 vertexPosition; +//! layout(location = 1) in vec2 vertexTexCoord; +//! +//! uniform mat4 fyrox_worldViewProjection; +//! +//! out vec2 texCoord; +//! +//! void main() +//! { +//! texCoord = vertexTexCoord; +//! gl_Position = fyrox_worldViewProjection * vertexPosition; +//! } +//! "#; +//! +//! // Pixel shader code. +//! pixel_shader: +//! r#" +//! #version 330 core +//! +//! // Note that the name of this uniform match the name of the property up above. +//! uniform sampler2D diffuseTexture; +//! +//! out vec4 FragColor; +//! +//! in vec2 texCoord; +//! +//! void main() +//! { +//! FragColor = diffuseColor * texture(diffuseTexture, texCoord); +//! } +//! "#; +//! ) +//! ], +//! ) +//! ``` +//! +//! Shader should contain at least one render pass to actually do some job. A shader could not +//! have properties at all. Currently only vertex and fragment programs are supported. Each +//! program mush be written in GLSL. Comprehensive GLSL documentation can be found +//! [here](https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)) +//! +//! # Render pass +//! +//! Modern rendering is a very complex thing that requires drawing an object multiple times +//! with different "scripts". For example to draw an object with shadows you need to draw an +//! object twice: one directly in a render target, and one in a shadow map. Such stages called +//! render passes. +//! +//! Binding of shaders to render passes is done via names, each render pass has unique name. +//! +//! ## Predefined passes +//! +//! There are number of predefined render passes: +//! +//! - GBuffer - A pass that fills a set of render target sized textures with various data +//! about each rendered object. These textures then are used for physically-based lighting. +//! Use this pass when you want the standard lighting to work with your objects. +//! +//! - Forward - A pass that draws an object directly in render target. This pass is very +//! limiting, it does not support lighting, shadows, etc. It should be only used to render +//! translucent objects. +//! +//! - SpotShadow - A pass that emits depth values for an object, later this depth map will be +//! used to render shadows. +//! +//! - PointShadow - A pass that emits distance from a fragment to a point light, later this depth +//! map will be used to render shadows. +//! +//! # Built-in variables +//! +//! There are number of build-in variables that Fyrox pass to each shader automatically: +//! +//! | Name | Type | Description +//! |---------------------------|-----------------|-------------------------------------------- +//! | fyrox_worldMatrix | `Matrix4` | Local-to-world transformation. +//! | fyrox_worldViewProjection | `Matrix4` | Local-to-clip-space transform. +//! | fyrox_boneMatrices | `[Matrix4; 60]` | Array of bone matrices. +//! | fyrox_useSkeletalAnimation | `Vector3` | Whether skinned meshes is rendering or not. +//! | fyrox_cameraPosition | `Vector3` | Position of the camera. +//! | fyrox_usePOM | `bool` | Whether to use parallax mapping or not. +//! | fyrox_lightPosition | `Vector3` | Light position. +//! +//! To use any of the variables, just define a uniform with appropriate name: +//! +//! ```glsl +//! uniform mat4 fyrox_worldMatrix; +//! uniform vec3 fyrox_cameraPosition; +//! ``` +//! +//! This list will be extended in future releases. +//! +//! # Drawing parameters +//! +//! Drawing parameters defines which GPU functions to use and at which state. For example, to render +//! transparent objects you need to enable blending with specific blending rules. Or you need to disable +//! culling to draw objects from both sides. This is when draw parameters comes in handy. +//! +//! There are relatively large list of drawing parameters and it could confuse a person who didn't get +//! used to work with graphics. The following list should help you to use drawing parameters correctly. +//! +//! - cull_face +//! - Defines which side of polygon should be culled. +//! - **Possible values:** `None`, [Some(CullFace::XXX)](crate::renderer::framework::state::CullFace) +//! +//! - color_write: +//! - Defines which components of color should be written to a render target +//! - **Possible values:** [ColorMask](crate::renderer::framework::state::ColorMask)(...) +//! +//! - depth_write: +//! - Whether to modify depth buffer or not. +//! - **Possible values:** `true/false` +//! +//! - stencil_test: +//! - Whether to use stencil test or not. +//! - **Possible values:** +//! - `None` +//! - Some([StencilFunc](crate::renderer::framework::state::StencilFunc)) +//! +//! - depth_test: +//! - Whether to perform depth test when drawing. +//! - **Possible values:** `true/false` +//! +//! - blend: +//! - Blending options. +//! - **Possible values:** +//! - `None` +//! - Some([BlendFunc](crate::renderer::framework::state::BlendFunc)) +//! +//! - stencil_op: +//! - Stencil options. +//! - **Possible values:** [StencilOp](crate::renderer::framework::state::StencilOp) +//! +//! # Standard shader +//! +//! By default Fyrox uses standard material for rendering, it covers 95% of uses cases and it is very +//! flexible. To get standard shader instance, use [`ShaderResource::standard`] +//! +//! ```no_run +//! # use fyrox::material::shader::ShaderResource; +//! +//! let standard_shader = ShaderResource::standard(); +//! ``` +//! +//! Usually you don't need to get this shader manually, using of [Material::standard](super::Material::standard) +//! is enough. use crate::{ - asset::{define_new_resource, Resource, ResourceData}, + asset::options::ImportOptions, + asset::{Resource, ResourceData}, core::{ algebra::{Matrix2, Matrix3, Matrix4, Vector2, Vector3, Vector4}, io::{self, FileLoadError}, @@ -11,11 +234,14 @@ use crate::{ sparse::AtomicIndex, visitor::prelude::*, }, - engine::resource_manager::options::ImportOptions, lazy_static::lazy_static, renderer::framework::framebuffer::DrawParameters, }; +use fyrox_core::uuid::Uuid; +use fyrox_core::TypeUuidProvider; +use fyrox_resource::SHADER_RESOURCE_UUID; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::fmt::{Display, Formatter}; use std::{ borrow::Cow, @@ -55,7 +281,7 @@ pub const STANDARD_SHADER_NAMES: [&str; 3] = [ /// Usually you don't need to access internals of the shader, but there sometimes could be a need to /// read shader definition, to get supported passes and properties. #[derive(Default, Debug)] -pub struct ShaderState { +pub struct Shader { path: PathBuf, /// Shader definition contains description of properties and render passes. @@ -64,7 +290,13 @@ pub struct ShaderState { pub(crate) cache_index: AtomicIndex, } -impl Visit for ShaderState { +impl TypeUuidProvider for Shader { + fn type_uuid() -> Uuid { + SHADER_RESOURCE_UUID + } +} + +impl Visit for Shader { fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult { let mut region = visitor.enter_region(name)?; @@ -224,7 +456,7 @@ pub struct PropertyDefinition { pub kind: PropertyKind, } -/// A render pass definition. See [`Shader`] docs for more info about render passes. +/// A render pass definition. See [`ShaderResource`] docs for more info about render passes. #[derive(Default, Deserialize, Debug, PartialEq, Eq)] pub struct RenderPassDefinition { /// A name of render pass. @@ -258,7 +490,7 @@ impl ShaderDefinition { } } -impl ShaderState { +impl Shader { pub(crate) async fn from_file>(path: P) -> Result { let content = io::load_file(path.as_ref()).await?; Ok(Self { @@ -277,7 +509,7 @@ impl ShaderState { } } -impl ResourceData for ShaderState { +impl ResourceData for Shader { fn path(&self) -> Cow { Cow::from(&self.path) } @@ -285,6 +517,18 @@ impl ResourceData for ShaderState { fn set_path(&mut self, path: PathBuf) { self.path = path; } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn type_uuid(&self) -> Uuid { + ::type_uuid() + } } /// A set of possible error variants that can occur during shader loading. @@ -322,262 +566,50 @@ impl From for ShaderError { } } -define_new_resource!( - /// Shader is a script for graphics adapter, it defines how to draw an object. - /// - /// # Structure - /// - /// Shader has rigid structure that could be described in this code snipped: - /// - /// ```ron - /// ( - /// // A set of properties, there could be any amount of properties. - /// properties: [ - /// ( - /// // Each property must have a name. This name must match with respective - /// // uniforms! That's is the whole point of having properties. - /// name: "diffuseTexture", - /// - /// // Value has limited set of possible variants. - /// value: Sampler(default: None, fallback: White) - /// ) - /// ], - /// - /// // A set of render passes (see a section `Render pass` for more info) - /// passes: [ - /// ( - /// // Name must match with the name of either standard render pass (see below) or - /// // one of your passes. - /// name: "Forward", - /// - /// // A set of parameters that regulate renderer pipeline state. - /// // This is mandatory field of each render pass. - /// draw_parameters: DrawParameters( - /// // A face to cull. Either Front or Back. - /// cull_face: Some(Back), - /// - /// // Color mask. Defines which colors should be written to render target. - /// color_write: ColorMask( - /// red: true, - /// green: true, - /// blue: true, - /// alpha: true, - /// ), - /// - /// // Whether to modify depth buffer or not. - /// depth_write: true, - /// - /// // Whether to use stencil test or not. - /// stencil_test: None, - /// - /// // Whether to perform depth test when drawing. - /// depth_test: true, - /// - /// // Blending options. - /// blend: Some(BlendParameters( - /// func: BlendFunc( - /// sfactor: SrcAlpha, - /// dfactor: OneMinusSrcAlpha, - /// alpha_sfactor: SrcAlpha, - /// alpha_dfactor: OneMinusSrcAlpha, - /// ), - /// equation: BlendEquation( - /// rgb: Add, - /// alpha: Add - /// ) - /// )), - /// - /// // Stencil options. - /// stencil_op: StencilOp( - /// fail: Keep, - /// zfail: Keep, - /// zpass: Keep, - /// write_mask: 0xFFFF_FFFF, - /// ), - /// ), - /// - /// // Vertex shader code. - /// vertex_shader: - /// r#" - /// #version 330 core - /// - /// layout(location = 0) in vec3 vertexPosition; - /// layout(location = 1) in vec2 vertexTexCoord; - /// - /// uniform mat4 fyrox_worldViewProjection; - /// - /// out vec2 texCoord; - /// - /// void main() - /// { - /// texCoord = vertexTexCoord; - /// gl_Position = fyrox_worldViewProjection * vertexPosition; - /// } - /// "#; - /// - /// // Pixel shader code. - /// pixel_shader: - /// r#" - /// #version 330 core - /// - /// // Note that the name of this uniform match the name of the property up above. - /// uniform sampler2D diffuseTexture; - /// - /// out vec4 FragColor; - /// - /// in vec2 texCoord; - /// - /// void main() - /// { - /// FragColor = diffuseColor * texture(diffuseTexture, texCoord); - /// } - /// "#; - /// ) - /// ], - /// ) - /// ``` - /// - /// Shader should contain at least one render pass to actually do some job. A shader could not - /// have properties at all. Currently only vertex and fragment programs are supported. Each - /// program mush be written in GLSL. Comprehensive GLSL documentation can be found - /// [here](https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)) - /// - /// # Render pass - /// - /// Modern rendering is a very complex thing that requires drawing an object multiple times - /// with different "scripts". For example to draw an object with shadows you need to draw an - /// object twice: one directly in a render target, and one in a shadow map. Such stages called - /// render passes. - /// - /// Binding of shaders to render passes is done via names, each render pass has unique name. - /// - /// ## Predefined passes - /// - /// There are number of predefined render passes: - /// - /// - GBuffer - A pass that fills a set of render target sized textures with various data - /// about each rendered object. These textures then are used for physically-based lighting. - /// Use this pass when you want the standard lighting to work with your objects. - /// - /// - Forward - A pass that draws an object directly in render target. This pass is very - /// limiting, it does not support lighting, shadows, etc. It should be only used to render - /// translucent objects. - /// - /// - SpotShadow - A pass that emits depth values for an object, later this depth map will be - /// used to render shadows. - /// - /// - PointShadow - A pass that emits distance from a fragment to a point light, later this depth - /// map will be used to render shadows. - /// - /// # Built-in variables - /// - /// There are number of build-in variables that Fyrox pass to each shader automatically: - /// - /// | Name | Type | Description - /// |---------------------------|-----------------|-------------------------------------------- - /// | fyrox_worldMatrix | `Matrix4` | Local-to-world transformation. - /// | fyrox_worldViewProjection | `Matrix4` | Local-to-clip-space transform. - /// | fyrox_boneMatrices | `[Matrix4; 60]` | Array of bone matrices. - /// | fyrox_useSkeletalAnimation | `Vector3` | Whether skinned meshes is rendering or not. - /// | fyrox_cameraPosition | `Vector3` | Position of the camera. - /// | fyrox_usePOM | `bool` | Whether to use parallax mapping or not. - /// | fyrox_lightPosition | `Vector3` | Light position. - /// - /// To use any of the variables, just define a uniform with appropriate name: - /// - /// ```glsl - /// uniform mat4 fyrox_worldMatrix; - /// uniform vec3 fyrox_cameraPosition; - /// ``` - /// - /// This list will be extended in future releases. - /// - /// # Drawing parameters - /// - /// Drawing parameters defines which GPU functions to use and at which state. For example, to render - /// transparent objects you need to enable blending with specific blending rules. Or you need to disable - /// culling to draw objects from both sides. This is when draw parameters comes in handy. - /// - /// There are relatively large list of drawing parameters and it could confuse a person who didn't get - /// used to work with graphics. The following list should help you to use drawing parameters correctly. - /// - /// - cull_face - /// - Defines which side of polygon should be culled. - /// - **Possible values:** `None`, [Some(CullFace::XXX)](crate::renderer::framework::state::CullFace) - /// - /// - color_write: - /// - Defines which components of color should be written to a render target - /// - **Possible values:** [ColorMask](crate::renderer::framework::state::ColorMask)(...) - /// - /// - depth_write: - /// - Whether to modify depth buffer or not. - /// - **Possible values:** `true/false` - /// - /// - stencil_test: - /// - Whether to use stencil test or not. - /// - **Possible values:** - /// - `None` - /// - Some([StencilFunc](crate::renderer::framework::state::StencilFunc)) - /// - /// - depth_test: - /// - Whether to perform depth test when drawing. - /// - **Possible values:** `true/false` - /// - /// - blend: - /// - Blending options. - /// - **Possible values:** - /// - `None` - /// - Some([BlendFunc](crate::renderer::framework::state::BlendFunc)) - /// - /// - stencil_op: - /// - Stencil options. - /// - **Possible values:** [StencilOp](crate::renderer::framework::state::StencilOp) - /// - /// # Standard shader - /// - /// By default Fyrox uses standard material for rendering, it covers 95% of uses cases and it is very - /// flexible. To get standard shader instance, use [`Shader::standard`] - /// - /// ```no_run - /// # use fyrox::material::shader::Shader; - /// - /// let standard_shader = Shader::standard(); - /// ``` - /// - /// Usually you don't need to get this shader manually, using of [Material::standard](super::Material::standard) - /// is enough. - #[derive(Reflect)] - #[reflect(hide_all)] - Shader -); +pub type ShaderResource = Resource; -impl Shader { +pub trait ShaderResourceExtension: Sized { /// Creates new shader from given string. Input string must have the format defined in - /// examples for [`Shader`]. - pub fn from_str>(str: &str, path: P) -> Result { - Ok(Self(Resource::new_ok(ShaderState::from_str( - str, - path.as_ref(), - )?))) + /// examples for [`ShaderResource`]. + fn from_str>(str: &str, path: P) -> Result; + + /// Returns an instance of standard shader. + fn standard() -> Self; + + /// Returns an instance of standard terrain shader. + fn standard_terrain() -> Self; + + /// Returns an instance of standard two-sides terrain shader. + fn standard_twosides() -> Self; + + /// Returns a list of standard shader. + fn standard_shaders() -> Vec; +} + +impl ShaderResourceExtension for ShaderResource { + /// Creates new shader from given string. Input string must have the format defined in + /// examples for [`ShaderResource`]. + fn from_str>(str: &str, path: P) -> Result { + Ok(Resource::new_ok(Shader::from_str(str, path.as_ref())?)) } /// Returns an instance of standard shader. - pub fn standard() -> Self { + fn standard() -> Self { STANDARD.clone() } /// Returns an instance of standard terrain shader. - pub fn standard_terrain() -> Self { + fn standard_terrain() -> Self { STANDARD_TERRAIN.clone() } /// Returns an instance of standard two-sides terrain shader. - pub fn standard_twosides() -> Self { + fn standard_twosides() -> Self { STANDARD_TWOSIDES.clone() } /// Returns a list of standard shader. - pub fn standard_shaders() -> Vec { + fn standard_shaders() -> Vec { vec![ Self::standard(), Self::standard_terrain(), @@ -593,28 +625,28 @@ pub struct ShaderImportOptions {} impl ImportOptions for ShaderImportOptions {} lazy_static! { - static ref STANDARD: Shader = Shader(Resource::new_ok( - ShaderState::from_str(STANDARD_SHADER_SRC, STANDARD_SHADER_NAME).unwrap(), - )); + static ref STANDARD: ShaderResource = ShaderResource::new_ok( + Shader::from_str(STANDARD_SHADER_SRC, STANDARD_SHADER_NAME).unwrap(), + ); } lazy_static! { - static ref STANDARD_TERRAIN: Shader = Shader(Resource::new_ok( - ShaderState::from_str(STANDARD_TERRAIN_SHADER_SRC, STANDARD_TERRAIN_SHADER_NAME).unwrap(), - )); + static ref STANDARD_TERRAIN: ShaderResource = ShaderResource::new_ok( + Shader::from_str(STANDARD_TERRAIN_SHADER_SRC, STANDARD_TERRAIN_SHADER_NAME).unwrap(), + ); } lazy_static! { - static ref STANDARD_TWOSIDES: Shader = Shader(Resource::new_ok( - ShaderState::from_str(STANDARD_TWOSIDES_SHADER_SRC, STANDARD_TWOSIDES_SHADER_NAME).unwrap(), - )); + static ref STANDARD_TWOSIDES: ShaderResource = ShaderResource::new_ok( + Shader::from_str(STANDARD_TWOSIDES_SHADER_SRC, STANDARD_TWOSIDES_SHADER_NAME).unwrap(), + ); } #[cfg(test)] mod test { use crate::material::shader::{ - PropertyDefinition, PropertyKind, RenderPassDefinition, SamplerFallback, Shader, - ShaderDefinition, + PropertyDefinition, PropertyKind, RenderPassDefinition, SamplerFallback, ShaderDefinition, + ShaderResource, }; #[test] @@ -659,7 +691,7 @@ mod test { ) "##; - let shader = Shader::from_str(code, "test").unwrap(); + let shader = ShaderResource::from_str(code, "test").unwrap(); let data = shader.data_ref(); let reference_definition = ShaderDefinition { diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 6d8e949db..bb209a048 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -3,11 +3,9 @@ #![warn(missing_docs)] use crate::{ + asset::manager::ResourceManager, core::pool::Handle, - engine::{ - resource_manager::ResourceManager, GraphicsContext, PerformanceStatistics, - SerializationContext, - }, + engine::{GraphicsContext, PerformanceStatistics, SerializationContext}, event::Event, event_loop::ControlFlow, gui::{message::UiMessage, UserInterface}, diff --git a/src/renderer/cache/geometry.rs b/src/renderer/cache/geometry.rs index a6cb67e04..3d24a9015 100644 --- a/src/renderer/cache/geometry.rs +++ b/src/renderer/cache/geometry.rs @@ -1,12 +1,11 @@ -use crate::scene::mesh::surface::SurfaceData; use crate::{ + asset::container::entry::DEFAULT_RESOURCE_LIFETIME, core::{scope_profile, sparse::SparseBuffer}, - engine::resource_manager::container::entry::DEFAULT_RESOURCE_LIFETIME, renderer::framework::{ geometry_buffer::{GeometryBuffer, GeometryBufferKind}, state::PipelineState, }, - scene::mesh::surface::SurfaceSharedData, + scene::mesh::surface::{SurfaceData, SurfaceSharedData}, }; use fyrox_core::sparse::AtomicIndex; diff --git a/src/renderer/cache/shader.rs b/src/renderer/cache/shader.rs index 0d5cf4cab..267f629f2 100644 --- a/src/renderer/cache/shader.rs +++ b/src/renderer/cache/shader.rs @@ -1,7 +1,7 @@ use crate::{ + asset::container::entry::DEFAULT_RESOURCE_LIFETIME, core::{scope_profile, sparse::SparseBuffer, sstorage::ImmutableString}, - engine::resource_manager::container::entry::DEFAULT_RESOURCE_LIFETIME, - material::shader::{Shader, ShaderState}, + material::shader::{Shader, ShaderResource}, renderer::{ cache::CacheEntry, framework::{framebuffer::DrawParameters, gpu_program::GpuProgram, state::PipelineState}, @@ -21,7 +21,7 @@ pub struct ShaderSet { } impl ShaderSet { - pub fn new(state: &mut PipelineState, shader: &ShaderState) -> Option { + pub fn new(state: &mut PipelineState, shader: &Shader) -> Option { let mut map = FxHashMap::default(); for render_pass in shader.definition.passes.iter() { let program_name = format!("{}_{}", shader.definition.name, render_pass.name); @@ -63,8 +63,8 @@ pub struct ShaderCache { } impl ShaderCache { - pub fn remove(&mut self, shader: &Shader) { - let state = shader.state(); + pub fn remove(&mut self, shader: &ShaderResource) { + let mut state = shader.state(); if let ResourceStateRef::Ok(shader_state) = state.get() { self.buffer.free(&shader_state.cache_index); } @@ -73,12 +73,12 @@ impl ShaderCache { pub fn get( &mut self, pipeline_state: &mut PipelineState, - shader: &Shader, + shader: &ShaderResource, ) -> Option<&ShaderSet> { scope_profile!(); let key = shader.key(); - let shader_state = shader.state(); + let mut shader_state = shader.state(); if let ResourceStateRef::Ok(shader_state) = shader_state.get() { if self.buffer.is_index_valid(&shader_state.cache_index) { diff --git a/src/renderer/cache/texture.rs b/src/renderer/cache/texture.rs index 2c92b9928..a26d2668d 100644 --- a/src/renderer/cache/texture.rs +++ b/src/renderer/cache/texture.rs @@ -1,7 +1,7 @@ use crate::renderer::framework::error::FrameworkError; use crate::{ + asset::container::entry::DEFAULT_RESOURCE_LIFETIME, core::scope_profile, - engine::resource_manager::container::entry::DEFAULT_RESOURCE_LIFETIME, renderer::{ cache::CacheEntry, framework::{ @@ -9,7 +9,7 @@ use crate::{ state::PipelineState, }, }, - resource::texture::Texture, + resource::texture::TextureResource, utils::log::{Log, MessageKind}, }; use fxhash::FxHashMap; @@ -27,10 +27,10 @@ impl TextureCache { pub fn upload( &mut self, state: &mut PipelineState, - texture: &Texture, + texture: &TextureResource, ) -> Result<(), FrameworkError> { let key = texture.key(); - let texture = texture.state(); + let mut texture = texture.state(); if let ResourceStateRef::Ok(texture) = texture.get() { let gpu_texture = GpuTexture::new( @@ -67,13 +67,13 @@ impl TextureCache { pub fn get( &mut self, state: &mut PipelineState, - texture_resource: &Texture, + texture_resource: &TextureResource, ) -> Option>> { scope_profile!(); let key = texture_resource.key(); - let texture_data_guard = texture_resource.state(); + let mut texture_data_guard = texture_resource.state(); if let ResourceStateRef::Ok(texture) = texture_data_guard.get() { let entry = match self.map.entry(key) { @@ -160,7 +160,7 @@ impl TextureCache { Log::writeln( MessageKind::Error, - format!("Failed to create GPU texture from {:?} engine texture. Reason: {:?}", texture_resource.state().path(), e), + format!("Failed to create GPU texture from {:?} engine texture. Reason: {:?}", texture_resource.path(), e), ); return None; } @@ -194,7 +194,7 @@ impl TextureCache { self.map.clear(); } - pub fn unload(&mut self, texture: Texture) { + pub fn unload(&mut self, texture: TextureResource) { self.map.remove(&texture.key()); } } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 545060641..91cb1e14f 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -34,10 +34,10 @@ mod skybox_shader; mod sprite_renderer; mod ssao; -use crate::renderer::batch::ObserverInfo; -use crate::renderer::framework::geometry_buffer::ElementRange; -use crate::renderer::framework::state::{PolygonFace, PolygonFillMode}; +use crate::material::shader::Shader; +use crate::resource::texture::Texture; use crate::{ + asset::{container::event::ResourceEvent, manager::ResourceManager}, core::{ algebra::{Matrix4, Vector2, Vector3}, color::Color, @@ -47,14 +47,10 @@ use crate::{ reflect::prelude::*, scope_profile, }, - engine::resource_manager::{container::event::ResourceEvent, ResourceManager}, gui::{draw::DrawingContext, UserInterface}, - material::{ - shader::{SamplerFallback, Shader}, - Material, PropertyValue, - }, + material::{shader::SamplerFallback, Material, PropertyValue}, renderer::{ - batch::RenderDataBatchStorage, + batch::{ObserverInfo, RenderDataBatchStorage}, bloom::BloomRenderer, cache::{geometry::GeometryCache, shader::ShaderCache, texture::TextureCache, CacheEntry}, debug_renderer::DebugRenderer, @@ -63,13 +59,15 @@ use crate::{ framework::{ error::FrameworkError, framebuffer::{Attachment, AttachmentKind, DrawParameters, FrameBuffer}, - geometry_buffer::{DrawCallStatistics, GeometryBuffer, GeometryBufferKind}, + geometry_buffer::{ + DrawCallStatistics, ElementRange, GeometryBuffer, GeometryBufferKind, + }, gpu_program::{BuiltInUniform, GpuProgramBinding}, gpu_texture::{ Coordinate, GpuTexture, GpuTextureKind, MagnificationFilter, MinificationFilter, PixelKind, WrapMode, }, - state::{PipelineState, PipelineStatistics}, + state::{PipelineState, PipelineStatistics, PolygonFace, PolygonFillMode}, }, fxaa::FxaaRenderer, gbuffer::{GBuffer, GBufferRenderContext}, @@ -81,7 +79,7 @@ use crate::{ storage::MatrixStorage, ui_renderer::{UiRenderContext, UiRenderer}, }, - resource::texture::{Texture, TextureKind}, + resource::texture::{TextureKind, TextureResource}, scene::{camera::Camera, mesh::surface::SurfaceData, Scene, SceneContainer}, utils::log::{Log, MessageKind}, }; @@ -728,8 +726,8 @@ pub struct Renderer { forward_renderer: ForwardRenderer, fxaa_renderer: FxaaRenderer, renderer2d: Renderer2d, - texture_event_receiver: Receiver>, - shader_event_receiver: Receiver>, + texture_event_receiver: Receiver, + shader_event_receiver: Receiver, matrix_storage: MatrixStorage, // TextureId -> FrameBuffer mapping. This mapping is used for temporal frame buffers // like ones used to render UI instances. @@ -944,7 +942,7 @@ pub(crate) struct MaterialContext<'a, 'b, 'c> { pub camera_position: &'a Vector3, pub use_pom: bool, pub light_position: &'a Vector3, - pub blend_shapes_storage: Option<&'a Texture>, + pub blend_shapes_storage: Option<&'a TextureResource>, pub blend_shapes_weights: &'a [f32], // Fallback samplers. @@ -1107,7 +1105,7 @@ impl Renderer { resource_manager .state() .containers_mut() - .textures + .resources .event_broadcaster .add(texture_event_sender); @@ -1116,7 +1114,7 @@ impl Renderer { resource_manager .state() .containers_mut() - .shaders + .resources .event_broadcaster .add(shader_event_sender); @@ -1275,7 +1273,7 @@ impl Renderer { } /// Unloads texture from GPU memory. - pub fn unload_texture(&mut self, texture: Texture) { + pub fn unload_texture(&mut self, texture: TextureResource) { self.texture_cache.unload(texture) } @@ -1347,7 +1345,7 @@ impl Renderer { /// to have off-screen UIs (like interactive touch-screen in Doom 3, Dead Space, etc). pub fn render_ui_to_texture( &mut self, - render_target: Texture, + render_target: TextureResource, ui: &mut UserInterface, ) -> Result<(), FrameworkError> { let new_width = ui.screen_size().x as usize; @@ -1421,19 +1419,21 @@ impl Renderer { let mut uploaded = 0; while let Ok(event) = self.texture_event_receiver.try_recv() { - if let ResourceEvent::Loaded(texture) | ResourceEvent::Reloaded(texture) = event { - match self.texture_cache.upload(&mut self.state, &texture) { - Ok(_) => { - uploaded += 1; - if uploaded >= THROUGHPUT { - break; + if let ResourceEvent::Loaded(resource) | ResourceEvent::Reloaded(resource) = event { + if let Some(texture) = resource.try_cast::() { + match self.texture_cache.upload(&mut self.state, &texture) { + Ok(_) => { + uploaded += 1; + if uploaded >= THROUGHPUT { + break; + } + } + Err(e) => { + Log::writeln( + MessageKind::Error, + format!("Failed to upload texture to GPU. Reason: {:?}", e), + ); } - } - Err(e) => { - Log::writeln( - MessageKind::Error, - format!("Failed to upload texture to GPU. Reason: {:?}", e), - ); } } } @@ -1444,10 +1444,12 @@ impl Renderer { fn update_shader_cache(&mut self, dt: f32) { while let Ok(event) = self.shader_event_receiver.try_recv() { - if let ResourceEvent::Loaded(shader) | ResourceEvent::Reloaded(shader) = event { - // Remove and immediately "touch" the shader cache to force upload shader. - self.shader_cache.remove(&shader); - let _ = self.shader_cache.get(&mut self.state, &shader); + if let ResourceEvent::Loaded(resource) | ResourceEvent::Reloaded(resource) = event { + if let Some(shader) = resource.try_cast::() { + // Remove and immediately "touch" the shader cache to force upload shader. + self.shader_cache.remove(&shader); + let _ = self.shader_cache.get(&mut self.state, &shader); + } } } diff --git a/src/renderer/renderer2d/cache.rs b/src/renderer/renderer2d/cache.rs index 35726badc..f0bb1e360 100644 --- a/src/renderer/renderer2d/cache.rs +++ b/src/renderer/renderer2d/cache.rs @@ -1,11 +1,11 @@ use crate::{ + asset::container::entry::TimedEntry, core::{ algebra::{Matrix4, Vector2}, color::Color, math::TriangleDefinition, scope_profile, }, - engine::resource_manager::container::entry::TimedEntry, renderer::framework::{ geometry_buffer::{ AttributeDefinition, AttributeKind, BufferBuilder, ElementKind, GeometryBuffer, diff --git a/src/renderer/ui_renderer.rs b/src/renderer/ui_renderer.rs index 0f87fbb10..1b6648db6 100644 --- a/src/renderer/ui_renderer.rs +++ b/src/renderer/ui_renderer.rs @@ -1,9 +1,7 @@ //! See [`UiRenderer`] docs. -use crate::renderer::framework::framebuffer::BlendParameters; -use crate::renderer::framework::geometry_buffer::ElementRange; use crate::{ - asset::Resource, + asset::{state::ResourceState, untyped::UntypedResource}, core::{ algebra::{Matrix4, Vector2, Vector4}, color::Color, @@ -19,10 +17,10 @@ use crate::{ renderer::{ framework::{ error::FrameworkError, - framebuffer::{DrawParameters, FrameBuffer}, + framebuffer::{BlendParameters, DrawParameters, FrameBuffer}, geometry_buffer::{ - AttributeDefinition, AttributeKind, BufferBuilder, ElementKind, GeometryBuffer, - GeometryBufferBuilder, GeometryBufferKind, + AttributeDefinition, AttributeKind, BufferBuilder, ElementKind, ElementRange, + GeometryBuffer, GeometryBufferBuilder, GeometryBufferKind, }, gpu_program::{GpuProgram, UniformLocation}, gpu_texture::GpuTexture, @@ -33,7 +31,7 @@ use crate::{ }, RenderPassStatistics, TextureCache, }, - resource::texture::{Texture, TextureData, TextureKind, TexturePixelKind}, + resource::texture::{Texture, TextureKind, TexturePixelKind}, }; use std::{cell::RefCell, rc::Rc, sync::Arc}; @@ -267,7 +265,7 @@ impl UiRenderer { let mut font = font_arc.0.lock(); if font.texture.is_none() { let size = font.atlas_size() as u32; - if let Some(details) = TextureData::from_bytes( + if let Some(details) = Texture::from_bytes( TextureKind::Rectangle { width: size, height: size, @@ -277,26 +275,29 @@ impl UiRenderer { false, ) { font.texture = Some(SharedTexture(Arc::new(Mutex::new( - TextureState::Ok(details), + ResourceState::new_ok(details), )))); } } - let tex = font - .texture - .clone() - .unwrap() - .0 - .downcast::>() - .unwrap(); - if let Some(texture) = texture_cache.get(state, &Texture(Resource::from(tex))) { + let tex = UntypedResource( + font.texture + .clone() + .unwrap() + .0 + .downcast::>() + .unwrap(), + ); + if let Some(texture) = + texture_cache.get(state, &tex.try_cast::().unwrap()) + { diffuse_texture = texture; } is_font_texture = true; } CommandTexture::Texture(texture) => { - if let Ok(texture) = texture.clone().0.downcast::>() { - let resource = Resource::from(texture); - if let Some(texture) = texture_cache.get(state, &Texture(resource)) { + if let Ok(texture) = texture.clone().0.downcast::>() { + let resource = UntypedResource(texture).try_cast::().unwrap(); + if let Some(texture) = texture_cache.get(state, &resource) { diffuse_texture = texture; } } diff --git a/src/resource/curve.rs b/src/resource/curve.rs index 55bef0e0e..87ddd511c 100644 --- a/src/resource/curve.rs +++ b/src/resource/curve.rs @@ -1,12 +1,15 @@ //! Curve resource holds a [`Curve`] use crate::{ - asset::{define_new_resource, Resource, ResourceData}, - core::reflect::prelude::*, + asset::options::ImportOptions, + asset::{Resource, ResourceData}, core::{curve::Curve, io::FileLoadError, visitor::prelude::*}, - engine::resource_manager::options::ImportOptions, }; +use fyrox_core::uuid::Uuid; +use fyrox_core::TypeUuidProvider; +use fyrox_resource::CURVE_RESOURCE_UUID; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::fmt::{Display, Formatter}; use std::{ borrow::Cow, @@ -67,6 +70,24 @@ impl ResourceData for CurveResourceState { fn set_path(&mut self, path: PathBuf) { self.path = path; } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn type_uuid(&self) -> Uuid { + ::type_uuid() + } +} + +impl TypeUuidProvider for CurveResourceState { + fn type_uuid() -> Uuid { + CURVE_RESOURCE_UUID + } } impl CurveResourceState { @@ -82,12 +103,7 @@ impl CurveResourceState { } } -define_new_resource!( - /// See module docs. - #[derive(Reflect)] - #[reflect(hide_all)] - CurveResource -); +pub type CurveResource = Resource; /// Import options for curve resource. #[derive(Serialize, Deserialize, Default, Clone)] diff --git a/src/resource/fbx/mod.rs b/src/resource/fbx/mod.rs index 6c6d76f87..8e68ed56b 100644 --- a/src/resource/fbx/mod.rs +++ b/src/resource/fbx/mod.rs @@ -10,9 +10,11 @@ mod document; pub mod error; mod scene; +use crate::resource::texture::Texture; use crate::scene::mesh::surface::{BlendShape, InputBlendShapeData}; use crate::{ animation::{track::Track, Animation, AnimationContainer}, + asset::manager::ResourceManager, core::{ algebra::{Matrix4, Point3, UnitQuaternion, Vector2, Vector3, Vector4}, curve::{CurveKey, CurveKeyKind}, @@ -23,7 +25,6 @@ use crate::{ sstorage::ImmutableString, uuid::Uuid, }, - engine::resource_manager::ResourceManager, material::{shader::SamplerFallback, PropertyValue}, resource::{ fbx::{ @@ -351,7 +352,8 @@ async fn create_surfaces( }; if let Some(texture_path) = texture_path { - let texture = resource_manager.request_texture(texture_path.as_path()); + let texture = + resource_manager.request::(texture_path.as_path()); // Make up your mind, Autodesk and Blender. // Handle all possible combinations of links to auto-import materials. diff --git a/src/resource/model.rs b/src/resource/model.rs index 5e50d9b53..c4a279a1f 100644 --- a/src/resource/model.rs +++ b/src/resource/model.rs @@ -20,7 +20,7 @@ use crate::{ animation::Animation, - asset::{define_new_resource, Resource, ResourceData}, + asset::{manager::ResourceManager, options::ImportOptions, Resource, ResourceData}, core::{ algebra::{UnitQuaternion, Vector3}, pool::Handle, @@ -28,10 +28,7 @@ use crate::{ variable::reset_inheritable_properties, visitor::{Visit, VisitError, VisitResult, Visitor}, }, - engine::{ - resource_manager::{options::ImportOptions, ResourceManager}, - SerializationContext, - }, + engine::SerializationContext, resource::fbx::{self, error::FbxError}, scene::{ animation::AnimationPlayer, @@ -41,7 +38,11 @@ use crate::{ }, utils::log::{Log, MessageKind}, }; +use fyrox_core::uuid::Uuid; +use fyrox_core::TypeUuidProvider; +use fyrox_resource::MODEL_RESOURCE_UUID; use serde::{Deserialize, Serialize}; +use std::any::Any; use std::{ borrow::Cow, fmt::{Display, Formatter}, @@ -59,7 +60,7 @@ pub(crate) enum NodeMapping { /// See module docs. #[derive(Debug, Visit)] -pub struct ModelData { +pub struct Model { pub(crate) path: PathBuf, #[visit(skip)] pub(crate) mapping: NodeMapping, @@ -67,17 +68,47 @@ pub struct ModelData { scene: Scene, } -define_new_resource!( - /// See module docs. - #[derive(Reflect)] - #[reflect(hide_all)] - Model -); +impl TypeUuidProvider for Model { + fn type_uuid() -> Uuid { + MODEL_RESOURCE_UUID + } +} -impl Model { - pub(crate) fn instantiate_from( - model: Self, - model_data: &ModelData, +pub type ModelResource = Resource; + +pub trait ModelResourceExtension: Sized { + fn instantiate_from( + model: ModelResource, + model_data: &Model, + handle: Handle, + dest_graph: &mut Graph, + ) -> (Handle, NodeHandleMap); + + fn instantiate(&self, dest_scene: &mut Scene) -> Handle; + + fn instantiate_at( + &self, + scene: &mut Scene, + position: Vector3, + orientation: UnitQuaternion, + ) -> Handle; + + fn retarget_animations_directly(&self, root: Handle, graph: &Graph) -> Vec; + + fn retarget_animations_to_player( + &self, + root: Handle, + dest_animation_player: Handle, + graph: &mut Graph, + ) -> Vec>; + + fn retarget_animations(&self, root: Handle, graph: &mut Graph) -> Vec>; +} + +impl ModelResourceExtension for ModelResource { + fn instantiate_from( + model: ModelResource, + model_data: &Model, handle: Handle, dest_graph: &mut Graph, ) -> (Handle, NodeHandleMap) { @@ -115,7 +146,7 @@ impl Model { } /// Tries to instantiate model from given resource. - pub fn instantiate(&self, dest_scene: &mut Scene) -> Handle { + fn instantiate(&self, dest_scene: &mut Scene) -> Handle { let data = self.data_ref(); let instance_root = Self::instantiate_from( @@ -133,7 +164,7 @@ impl Model { } /// Instantiates a prefab and places it at specified position and orientation in global coordinates. - pub fn instantiate_at( + fn instantiate_at( &self, scene: &mut Scene, position: Vector3, @@ -172,11 +203,7 @@ impl Model { /// /// Most of the 3d model formats can contain only one animation, so in most cases /// this function will return vector with only one animation. - pub fn retarget_animations_directly( - &self, - root: Handle, - graph: &Graph, - ) -> Vec { + fn retarget_animations_directly(&self, root: Handle, graph: &Graph) -> Vec { let mut retargetted_animations = Vec::new(); let data = self.data_ref(); @@ -228,7 +255,7 @@ impl Model { /// /// Panics if `dest_animation_player` is invalid handle, or the node does not have [`AnimationPlayer`] /// component. - pub fn retarget_animations_to_player( + fn retarget_animations_to_player( &self, root: Handle, dest_animation_player: Handle, @@ -256,11 +283,7 @@ impl Model { /// # Panic /// /// Panics if there's no animation player in the given hierarchy (descendant nodes of `root`). - pub fn retarget_animations( - &self, - root: Handle, - graph: &mut Graph, - ) -> Vec> { + fn retarget_animations(&self, root: Handle, graph: &mut Graph) -> Vec> { if let Some((animation_player, _)) = graph.find(root, &mut |n| { n.query_component_ref::().is_some() }) { @@ -271,7 +294,7 @@ impl Model { } } -impl ResourceData for ModelData { +impl ResourceData for Model { fn path(&self) -> Cow { Cow::Borrowed(&self.path) } @@ -279,9 +302,21 @@ impl ResourceData for ModelData { fn set_path(&mut self, path: PathBuf) { self.path = path; } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn type_uuid(&self) -> Uuid { + ::type_uuid() + } } -impl Default for ModelData { +impl Default for Model { fn default() -> Self { Self { path: PathBuf::new(), @@ -426,7 +461,7 @@ impl From for ModelLoadError { } } -impl ModelData { +impl Model { pub(crate) async fn load>( path: P, serialization_context: Arc, diff --git a/src/resource/texture.rs b/src/resource/texture.rs index 65d398376..b8de3fa90 100644 --- a/src/resource/texture.rs +++ b/src/resource/texture.rs @@ -21,23 +21,25 @@ //! access to pixels of render target. use crate::{ - asset::{define_new_resource, Resource, ResourceData}, + asset::{options::ImportOptions, Resource, ResourceData}, core::{ futures::io::Error, io::{self, FileLoadError}, reflect::prelude::*, visitor::{PodVecView, Visit, VisitError, VisitResult, Visitor}, }, - engine::resource_manager::options::ImportOptions, }; use ddsfile::{Caps2, D3DFormat}; use fxhash::FxHasher; +use fyrox_core::uuid::Uuid; +use fyrox_core::TypeUuidProvider; +use fyrox_resource::TEXTURE_RESOURCE_UUID; use image::{imageops::FilterType, ColorType, DynamicImage, ImageError, ImageFormat}; use serde::{Deserialize, Serialize}; -use std::fmt::Display; +use std::any::Any; use std::{ borrow::Cow, - fmt::{Debug, Formatter}, + fmt::{Debug, Display, Formatter}, hash::{Hash, Hasher}, io::Cursor, ops::{Deref, DerefMut}, @@ -186,7 +188,7 @@ impl DerefMut for TextureBytes { /// Actual texture data. #[derive(Debug, Clone)] -pub struct TextureData { +pub struct Texture { path: PathBuf, kind: TextureKind, bytes: TextureBytes, @@ -202,7 +204,13 @@ pub struct TextureData { is_render_target: bool, } -impl ResourceData for TextureData { +impl TypeUuidProvider for Texture { + fn type_uuid() -> Uuid { + TEXTURE_RESOURCE_UUID + } +} + +impl ResourceData for Texture { fn path(&self) -> Cow { Cow::Borrowed(&self.path) } @@ -210,9 +218,21 @@ impl ResourceData for TextureData { fn set_path(&mut self, path: PathBuf) { self.path = path; } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn type_uuid(&self) -> Uuid { + ::type_uuid() + } } -impl Visit for TextureData { +impl Visit for Texture { fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult { let mut region = visitor.enter_region(name)?; @@ -246,7 +266,7 @@ impl Visit for TextureData { } } -impl Default for TextureData { +impl Default for Texture { /// It is very important to mention that defaults may be different for texture when you /// importing them through resource manager, see /// [TextureImportOptions](../engine/resource_manager/struct.TextureImportOptions.html) for more info. @@ -404,19 +424,33 @@ impl TextureImportOptions { } } -define_new_resource!( - /// See module docs. - #[derive(Reflect)] - #[reflect(hide_all)] - Texture -); +pub type TextureResource = Resource; -impl Texture { +pub trait TextureResourceExtension: Sized { + fn new_render_target(width: u32, height: u32) -> Self; + + fn load_from_memory( + data: &[u8], + compression: CompressionOptions, + gen_mip_maps: bool, + ) -> Result; + + fn from_bytes( + kind: TextureKind, + pixel_kind: TexturePixelKind, + bytes: Vec, + serialize_content: bool, + ) -> Option; + + fn deep_clone(&self) -> Self; +} + +impl TextureResourceExtension for TextureResource { /// Creates new render target for a scene. This method automatically configures GPU texture /// to correct settings, after render target was created, it must not be modified, otherwise /// result is undefined. - pub fn new_render_target(width: u32, height: u32) -> Self { - Self(Resource::new_ok(TextureData { + fn new_render_target(width: u32, height: u32) -> Self { + Resource::new_ok(Texture { path: Default::default(), // Render target will automatically set width and height before rendering. kind: TextureKind::Rectangle { width, height }, @@ -431,7 +465,7 @@ impl Texture { serialize_content: false, data_hash: 0, is_render_target: true, - })) + }) } /// Tries to load a texture from given data. Use this method if you want to @@ -451,38 +485,38 @@ impl Texture { /// loading. You should use `ResourceManager::request_texture` in majority of cases! /// /// Main use cases for this method are: procedural textures, icons for GUI. - pub fn load_from_memory( + fn load_from_memory( data: &[u8], compression: CompressionOptions, gen_mip_maps: bool, ) -> Result { - Ok(Self(Resource::new_ok(TextureData::load_from_memory( + Ok(Resource::new_ok(Texture::load_from_memory( data, compression, gen_mip_maps, - )?))) + )?)) } /// Tries to create new texture from given parameters, it may fail only if size of data passed /// in does not match with required. - pub fn from_bytes( + fn from_bytes( kind: TextureKind, pixel_kind: TexturePixelKind, bytes: Vec, serialize_content: bool, ) -> Option { - Some(Self(Resource::new_ok(TextureData::from_bytes( + Some(Resource::new_ok(Texture::from_bytes( kind, pixel_kind, bytes, serialize_content, - )?))) + )?)) } - /// Creates a deep clone of the texture. Unlike [`Texture::clone`], this method clones the actual texture data, + /// Creates a deep clone of the texture. Unlike [`TextureResource::clone`], this method clones the actual texture data, /// which could be slow. - pub fn deep_clone(&self) -> Self { - Self(Resource::new_ok(self.data_ref().clone())) + fn deep_clone(&self) -> Self { + Resource::new_ok(self.data_ref().clone()) } } @@ -1051,7 +1085,7 @@ fn bytes_in_first_mip(kind: TextureKind, pixel_kind: TexturePixelKind) -> u32 { } } -impl TextureData { +impl Texture { /// Tries to load a texture from given data in one of the following formats: PNG, BMP, TGA, JPG, DDS, GIF. Use /// this method if you want to load a texture from embedded data. /// @@ -1468,7 +1502,7 @@ impl TextureData { /// A special reference holder that provides mutable access to content of the /// texture and automatically calculates hash of the data in its destructor. pub struct TextureDataRefMut<'a> { - texture: &'a mut TextureData, + texture: &'a mut Texture, } impl<'a> Drop for TextureDataRefMut<'a> { @@ -1478,7 +1512,7 @@ impl<'a> Drop for TextureDataRefMut<'a> { } impl<'a> Deref for TextureDataRefMut<'a> { - type Target = TextureData; + type Target = Texture; fn deref(&self) -> &Self::Target { self.texture @@ -1515,10 +1549,10 @@ impl<'a> TextureDataRefMut<'a> { #[cfg(test)] pub mod test { - use crate::resource::texture::{Texture, TextureKind, TexturePixelKind}; + use crate::resource::texture::{TextureKind, TexturePixelKind, TextureResource}; - pub fn create_test_texture() -> Texture { - Texture::from_bytes( + pub fn create_test_texture() -> TextureResource { + TextureResource::from_bytes( TextureKind::Rectangle { width: 1, height: 1, diff --git a/src/scene/animation/absm.rs b/src/scene/animation/absm.rs index 20b761e74..f1fbef303 100644 --- a/src/scene/animation/absm.rs +++ b/src/scene/animation/absm.rs @@ -10,12 +10,13 @@ use crate::{ uuid::{uuid, Uuid}, variable::InheritableVariable, visitor::prelude::*, + TypeUuidProvider, }, scene::{ animation::AnimationPlayer, base::{Base, BaseBuilder}, graph::Graph, - node::{Node, NodeTrait, TypeUuidProvider, UpdateContext}, + node::{Node, NodeTrait, UpdateContext}, Scene, }, }; diff --git a/src/scene/animation/mod.rs b/src/scene/animation/mod.rs index 2b1113cd8..5f77e0199 100644 --- a/src/scene/animation/mod.rs +++ b/src/scene/animation/mod.rs @@ -10,11 +10,12 @@ use crate::{ uuid::{uuid, Uuid}, variable::InheritableVariable, visitor::prelude::*, + TypeUuidProvider, }, scene::{ base::{Base, BaseBuilder}, graph::Graph, - node::{Node, NodeTrait, TypeUuidProvider, UpdateContext}, + node::{Node, NodeTrait, UpdateContext}, }, }; use std::ops::{Deref, DerefMut}; diff --git a/src/scene/base.rs b/src/scene/base.rs index 424bd8b0d..33dcb2918 100644 --- a/src/scene/base.rs +++ b/src/scene/base.rs @@ -13,7 +13,7 @@ use crate::{ visitor::{Visit, VisitError, VisitResult, Visitor}, }, engine::SerializationContext, - resource::model::Model, + resource::model::ModelResource, scene::{node::Node, transform::Transform}, script::{Script, ScriptTrait}, utils::log::Log, @@ -382,7 +382,7 @@ pub struct Base { // A resource from which this node was instantiated from, can work in pair // with `original` handle to get corresponding node from resource. #[reflect(read_only)] - pub(crate) resource: Option, + pub(crate) resource: Option, // Handle to node in scene of model resource from which this node // was instantiated from. @@ -540,7 +540,7 @@ impl Base { /// Returns resource from which this node was instantiated from. #[inline] - pub fn resource(&self) -> Option { + pub fn resource(&self) -> Option { self.resource.clone() } @@ -866,10 +866,9 @@ fn visit_opt_script(name: &str, script: &mut Option