diff --git a/assets.pkg b/assets.pkg index 2b824bc..55286e4 100644 Binary files a/assets.pkg and b/assets.pkg differ diff --git a/assets/birds.mp3 b/assets/audio/birds.mp3 similarity index 100% rename from assets/birds.mp3 rename to assets/audio/birds.mp3 diff --git a/assets/wind.wav b/assets/audio/wind.wav similarity index 100% rename from assets/wind.wav rename to assets/audio/wind.wav diff --git a/assets/model.obj b/assets/models/model.obj similarity index 100% rename from assets/model.obj rename to assets/models/model.obj diff --git a/assets/model_flat.obj b/assets/models/model_flat.obj similarity index 100% rename from assets/model_flat.obj rename to assets/models/model_flat.obj diff --git a/assets/skybox.obj b/assets/models/skybox.obj similarity index 100% rename from assets/skybox.obj rename to assets/models/skybox.obj diff --git a/assets/packages/my_scene.tar.lz4 b/assets/packages/my_scene.tar.lz4 new file mode 100644 index 0000000..edf4ede Binary files /dev/null and b/assets/packages/my_scene.tar.lz4 differ diff --git a/assets/my_scene.ron b/assets/scenes/my_scene.ron similarity index 100% rename from assets/my_scene.ron rename to assets/scenes/my_scene.ron diff --git a/assets/StandardCubeMap.png b/assets/textures/StandardCubeMap.png similarity index 100% rename from assets/StandardCubeMap.png rename to assets/textures/StandardCubeMap.png diff --git a/assets/pbr_test/ao.jpg b/assets/textures/pbr_test/ao.jpg similarity index 100% rename from assets/pbr_test/ao.jpg rename to assets/textures/pbr_test/ao.jpg diff --git a/assets/pbr_test/diffuse.jpg b/assets/textures/pbr_test/diffuse.jpg similarity index 100% rename from assets/pbr_test/diffuse.jpg rename to assets/textures/pbr_test/diffuse.jpg diff --git a/assets/pbr_test/disp.jpg b/assets/textures/pbr_test/disp.jpg similarity index 100% rename from assets/pbr_test/disp.jpg rename to assets/textures/pbr_test/disp.jpg diff --git a/assets/pbr_test/nrm.jpg b/assets/textures/pbr_test/nrm.jpg similarity index 100% rename from assets/pbr_test/nrm.jpg rename to assets/textures/pbr_test/nrm.jpg diff --git a/assets/pbr_test/rgh.jpg b/assets/textures/pbr_test/rgh.jpg similarity index 100% rename from assets/pbr_test/rgh.jpg rename to assets/textures/pbr_test/rgh.jpg diff --git a/assets/uv.jpg b/assets/textures/uv.jpg similarity index 100% rename from assets/uv.jpg rename to assets/textures/uv.jpg diff --git a/examples/packaged_scene.rs b/examples/packaged_scene.rs new file mode 100644 index 0000000..28ef8b3 --- /dev/null +++ b/examples/packaged_scene.rs @@ -0,0 +1,20 @@ +use despero::prelude::*; + +fn main() { + Despero::init(WindowBuilder::default()) + + .default_systems() + .add_setup_system(load_scene) + .run(); +} + +fn load_scene( + mut cmd: Write, + mut asset_manager: Write, +) -> DesperoResult<()> { + let scene = Scene::load_packaged("assets/packages/my_scene.tar.lz4")?; + + cmd.spawn_scene(scene, &mut asset_manager); + + Ok(()) +} \ No newline at end of file diff --git a/examples/pbr.rs b/examples/pbr.rs index db25556..5fcc0d2 100644 --- a/examples/pbr.rs +++ b/examples/pbr.rs @@ -37,7 +37,7 @@ fn create_scene( mut cmd: Write, mut asset_manager: Write, ){ - let diffuse = asset_manager.create_texture("assets/pbr_test/diffuse.jpg", Filter::Linear); + let diffuse = asset_manager.create_texture("assets/textures/pbr_test/diffuse.jpg", Filter::Linear); cmd.spawn( ModelBundle::builder() @@ -72,11 +72,11 @@ fn create_scene( }, )); - let sky_tex = asset_manager.create_texture("assets/StandardCubeMap.png", Filter::Linear); + let sky_tex = asset_manager.create_texture("assets/textures/StandardCubeMap.png", Filter::Linear); cmd.spawn( ModelBundle::builder() - .mesh(Mesh::load_obj("assets/skybox.obj").swap_remove(0)) + .mesh(Mesh::load_obj("assets/models/skybox.obj").swap_remove(0)) .material( asset_manager.create_material( DefaultMat::builder() diff --git a/scene.ron b/scene.ron new file mode 100644 index 0000000..f3a09b6 --- /dev/null +++ b/scene.ron @@ -0,0 +1,132 @@ +Scene( + assets: AssetManager( + audio: AudioManager( + sounds: [], + cast_count: 128, + listener_count: 8, + ), + textures: [ + Texture( + load_type: Path("assets/textures/uv.jpg"), + filter: Linear, + ), + ], + materials: [ + { + "material": "DefaultMat", + "color": (1.0, 1.0, 1.0), + "albedo": 0, + "metallic": 0.5, + "metallic_map": 0, + "roughness": 0.5, + "roughness_map": 0, + "normal": 1.0, + "normal_map": 0, + }, + ], + ), + entities: [ + Entity( + components: [ + { + "component": "Mesh", + "vertexdata": [ + Vertex( + position: (-1.0, 1.0, 0.0), + normal: (0.0, 0.0, -4.0), + texcoord: (0.0, 1.0), + ), + Vertex( + position: (-1.0, -1.0, 0.0), + normal: (0.0, 0.0, -4.0), + texcoord: (0.0, 0.0), + ), + Vertex( + position: (1.0, 1.0, 0.0), + normal: (0.0, 0.0, -4.0), + texcoord: (1.0, 1.0), + ), + Vertex( + position: (1.0, -1.0, 0.0), + normal: (0.0, 0.0, -4.0), + texcoord: (1.0, 0.0), + ), + ], + "indexdata": [ + 0, + 2, + 1, + 1, + 2, + 3, + ], + }, + { + "component": "AssetHandle", + "value": 0, + }, + { + "component": "Transform", + "translation": [ + 0.0, + 0.0, + 0.0, + ], + "rotation": [ + 0.0, + 0.0, + 0.0, + 1.0, + ], + "scale": 1.0, + }, + ], + ), + Entity( + components: [ + { + "component": "Camera", + "camera_type": FirstPerson, + "projectionmatrix": [ + 1.2990379, + 0.0, + 0.0, + 0.0, + 0.0, + 1.7320507, + 0.0, + 0.0, + 0.0, + 0.0, + 1.001001, + 1.0, + 0.0, + 0.0, + -0.1001001, + 0.0, + ], + "fovy": 1.0471976, + "aspect": 1.3333334, + "near": 0.1, + "far": 100.0, + "is_active": true, + }, + { + "component": "Transform", + "translation": [ + 0.0, + 0.0, + 5.0, + ], + "rotation": [ + 0.0, + 0.0, + 0.0, + 1.0, + ], + "scale": 1.0, + }, + ], + ), + ], +) \ No newline at end of file diff --git a/src/assets/asset_manager.rs b/src/assets/asset_manager.rs index 7a9d9be..d70d55e 100644 --- a/src/assets/asset_manager.rs +++ b/src/assets/asset_manager.rs @@ -7,8 +7,10 @@ use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; #[cfg(feature = "render")] use ash::vk; -use super::AssetHandle; use crate::audio::AudioManager; + +#[cfg(feature = "render")] +use super::AssetHandle; #[cfg(feature = "render")] use crate::render::*; diff --git a/src/assets/mod.rs b/src/assets/mod.rs index fd8c145..93cd811 100644 --- a/src/assets/mod.rs +++ b/src/assets/mod.rs @@ -10,18 +10,18 @@ pub use ser_component::*; pub use settings::*; pub use world_serializer::*; +use std::path::PathBuf; use serde::{Serialize, Deserialize}; -#[derive(Default, Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum AssetLoadType { - #[default] - Resource, - Path(String), + Resource(String), + Path(PathBuf), } -impl From for AssetLoadType { - fn from(value: T) -> Self { - AssetLoadType::Path(value.to_string()) +impl Default for AssetLoadType { + fn default() -> Self { + AssetLoadType::Resource("".into()) } } diff --git a/src/assets/scene.rs b/src/assets/scene.rs index 2108e5c..226d284 100644 --- a/src/assets/scene.rs +++ b/src/assets/scene.rs @@ -1,7 +1,11 @@ -use std::path::Path; -use std::fs::read_to_string; use std::sync::Arc; -use std::fs::File; +use std::io::{Read, Cursor}; +use std::path::Path; +use std::fs::{File, read_to_string}; + +#[cfg(feature = "render")] +use image::ImageFormat; +use kira::sound::static_sound::{StaticSoundData, StaticSoundSettings}; use tar::EntryType; use ron::ser::{Serializer, PrettyConfig}; @@ -10,6 +14,7 @@ use serde::{ Deserialize }; +use crate::audio::AudioError; use crate::ecs::*; use crate::error::*; use crate::assets::{ @@ -17,6 +22,8 @@ use crate::assets::{ ser_component::*, }; +use super::AssetLoadType; + #[derive(Default, Serialize, Deserialize)] #[serde(rename = "Entity")] pub struct SerializableEntity { @@ -40,8 +47,8 @@ impl Scene { )?) } - /// Load scene with assets from compressed `.lvl` package. - /// It is `.tar.lz4` package which can be created manually or with + /// Load scene with assets from compressed `.tar.lz4` package. + /// It is ordinary package which can be created manually or with /// [Metio Editor](https://konceptosociala.eu.org/softvaro/metio). /// /// It has the following structure: @@ -56,40 +63,80 @@ impl Scene { /// │ ├─ sound1.mp3 /// ``` pub fn load_packaged>(path: P) -> DesperoResult { - // TODO: Packaged scene - // - let mut asset_manager = AssetManager::new(); + let mut scene = Scene::new(); - let package = File::open("assets.pkg")?; + let package = File::open(path)?; let decoded = lz4::Decoder::new(package)?; let mut archive = tar::Archive::new(decoded); + let mut entries = vec![]; // Vec<(Header, Vec)> + for file in archive.entries().unwrap() { - let file = file.unwrap(); - let header = file.header(); - match header.entry_type() { - EntryType::Regular => { - if header.path().unwrap() == Path::new("manager.ron") { - asset_manager = ron::de::from_reader(file)?; + let mut file = file.unwrap(); + let header = file.header().clone(); + + let mut bytes = vec![]; + file.read_to_end(&mut bytes)?; + + entries.push((header, bytes)); + } + + for (header, file) in &mut entries { + let path = header.path().unwrap(); + if path == Path::new("scene.ron") { + log::debug!("Deserializing scene `{}`...", path.display()); + scene = ron::de::from_reader(&**file)?; + } + } + + for (header, file) in entries { + let filepath = header.path().unwrap(); + + if header.entry_type() == EntryType::Regular { + let name = filepath + .file_stem() + .unwrap() + .to_os_string() + .into_string() + .unwrap(); + + #[cfg(feature = "render")] + let ext = filepath.extension().unwrap().to_owned(); + + if filepath.starts_with("textures") { + #[cfg(feature = "render")] + for texture in &mut scene.assets.textures { + if texture.load_type == AssetLoadType::Resource(name.clone()) { + let cursor = Cursor::new(file.clone()); + + let image = image::load( + cursor, + ImageFormat::from_extension(ext.clone()).expect("Wrong image extension!") + ) + .map(|img| img.to_rgba8()) + .expect("Unable to open image"); + + texture.image = Some(image); + } } - }, - EntryType::Directory => { - // if `sounds`: - // - // - - // if `textures`: - // for texture in asset_manager.textures { - // let reader = BufReader::new(file); - // let image = image::load(reader, ImageFormat::from_path(path)); - // texture.generate_from(image); - // } - // - }, - _ => {}, + } else if filepath.starts_with("audio") { + for sound in &mut scene.assets.audio.sounds { + if sound.load_type == AssetLoadType::Resource(name.clone()) { + let cursor = Cursor::new(file.clone()); + + let static_data = StaticSoundData::from_media_source( + cursor, + StaticSoundSettings::default(), + ).map_err(|e| AudioError::from(e))?; + + sound.static_data = Some(static_data); + } + } + } } } - Ok(Self::new()) + + Ok(scene) } pub fn save>(&self, path: P) -> DesperoResult<()> { @@ -111,9 +158,10 @@ pub trait SpawnSceneExt { impl SpawnSceneExt for CommandBuffer { fn spawn_scene(&mut self, scene: Scene, asset_manager: &mut AssetManager) { - self.write(|world| { - world.clear(); - }); + // self.write(|world| { + // world.clear(); + // }); + // TODO: Clear world for entity in scene.entities { let mut entity_builder = EntityBuilder::new(); @@ -131,7 +179,7 @@ impl SpawnSceneExt for CommandBuffer { impl SpawnSceneExt for World { fn spawn_scene(&mut self, scene: Scene, asset_manager: &mut AssetManager) { - self.clear(); + // self.clear(); for entity in scene.entities { let mut entity_builder = EntityBuilder::new(); diff --git a/src/audio/mod.rs b/src/audio/mod.rs index b173122..1fe0700 100644 --- a/src/audio/mod.rs +++ b/src/audio/mod.rs @@ -41,10 +41,12 @@ pub use sound::*; type KiraAudioManager = kira::manager::AudioManager; +// TODO: Link casts to sounds in packaged scene + /// Main audio managment struct. It's actually a part of [`AssetManager`] #[derive(Serialize)] pub struct AudioManager { - sounds: Vec, + pub(crate) sounds: Vec, cast_count: usize, listener_count: usize, diff --git a/src/audio/sound.rs b/src/audio/sound.rs index 1f4f2e3..0fb59ec 100644 --- a/src/audio/sound.rs +++ b/src/audio/sound.rs @@ -17,7 +17,7 @@ use super::{AudioError, cast::AudioCast}; #[derive(Debug, Clone, Serialize)] pub struct Sound { - loader_type: AssetLoadType, + pub(crate) load_type: AssetLoadType, #[serde(skip_serializing)] pub(crate) static_data: Option, @@ -33,7 +33,7 @@ impl Sound { ); Ok(Sound { - loader_type: path.into(), + load_type: AssetLoadType::Path(path.into()), static_data, }) } @@ -52,7 +52,7 @@ impl<'de> Deserialize<'de> for Sound { { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "snake_case")] - enum SoundField { LoaderType } + enum SoundField { LoadType } struct SoundVisitor; @@ -67,9 +67,9 @@ impl<'de> Deserialize<'de> for Sound { where V: SeqAccess<'de>, { - let loader_type: AssetLoadType = seq.next_element()?.ok_or_else(|| DeError::invalid_length(0, &self))?; + let load_type: AssetLoadType = seq.next_element()?.ok_or_else(|| DeError::invalid_length(0, &self))?; - let static_data = match loader_type.clone() { + let static_data = match load_type.clone() { AssetLoadType::Path(path) => { Some( StaticSoundData::from_file( @@ -82,7 +82,7 @@ impl<'de> Deserialize<'de> for Sound { }; Ok(Sound { - loader_type, + load_type, static_data, }) } @@ -91,20 +91,20 @@ impl<'de> Deserialize<'de> for Sound { where V: MapAccess<'de>, { - let mut loader_type: Option = None; + let mut load_type: Option = None; while let Some(key) = map.next_key()? { match key { - SoundField::LoaderType => { - if loader_type.is_some() { - return Err(DeError::duplicate_field("loader_type")); + SoundField::LoadType => { + if load_type.is_some() { + return Err(DeError::duplicate_field("load_type")); } - loader_type = Some(map.next_value()?); + load_type = Some(map.next_value()?); } } } - let loader_type = loader_type.ok_or_else(|| DeError::missing_field("loader_type"))?; + let load_type = load_type.ok_or_else(|| DeError::missing_field("load_type"))?; - let static_data = match loader_type.clone() { + let static_data = match load_type.clone() { AssetLoadType::Path(path) => { Some( StaticSoundData::from_file( @@ -117,13 +117,13 @@ impl<'de> Deserialize<'de> for Sound { }; Ok(Sound { - loader_type, + load_type, static_data, }) } } - const FIELDS: &'static [&'static str] = &["loader_type"]; + const FIELDS: &'static [&'static str] = &["load_type"]; deserializer.deserialize_struct("Sound", FIELDS, SoundVisitor) } } \ No newline at end of file diff --git a/src/ecs/runners.rs b/src/ecs/runners.rs index eb12ccd..1ea959a 100644 --- a/src/ecs/runners.rs +++ b/src/ecs/runners.rs @@ -99,7 +99,7 @@ pub fn default_runner(despero: &mut Despero) { &mut despero.asset_manager, )).expect("Cannot execute loop schedule"); - if let Some(handler) = despero.events.get_handler::>() { + if let Some(handler) = despero.events.get_handler::() { if let Some(_) = handler.read() { return (); } diff --git a/src/ecs/systems.rs b/src/ecs/systems.rs index 3fbb999..6a11fe4 100644 --- a/src/ecs/systems.rs +++ b/src/ecs/systems.rs @@ -122,8 +122,21 @@ pub fn generate_textures( mut renderer: Write, ) -> DesperoResult<()> { for texture in &mut asset_manager.textures { - if texture.sampler == None { - texture.generate(&mut renderer)?; + match texture.load_type { + AssetLoadType::Path(_) => { + log::debug!("Path texture found"); + if texture.vk_image.is_none() { + texture.generate(&mut renderer)?; + log::debug!("Path texture generated"); + } + }, + AssetLoadType::Resource(_) => { + log::debug!("Resource texture found"); + if let Some(ref image) = texture.image { + log::debug!("Resource texture found"); + texture.generate_from(&mut renderer, image.clone())?; + } + }, } } diff --git a/src/render/pbr/model.rs b/src/render/pbr/model.rs index 753e463..f308237 100644 --- a/src/render/pbr/model.rs +++ b/src/render/pbr/model.rs @@ -58,6 +58,14 @@ impl Vertex { } } +// TODO: Mesh to Model: +// +// Model { +// load_type: AssetLoadType, +// mesh: Option, +// } +// + /// Model mesh struct pub struct Mesh { pub vertexdata: Vec, diff --git a/src/render/pbr/texture.rs b/src/render/pbr/texture.rs index bc60475..71e5363 100644 --- a/src/render/pbr/texture.rs +++ b/src/render/pbr/texture.rs @@ -72,7 +72,7 @@ impl Texture { filter: Filter, ) -> Self { Texture { - load_type: path.into(), + load_type: AssetLoadType::Path(path.into()), filter, image: None, vk_image: None, @@ -81,22 +81,6 @@ impl Texture { sampler: None, } } - - pub(crate) fn new_resource( - renderer: &mut Renderer, - filter: Filter, - image: image::RgbaImage, - ) -> DesperoResult { - let mut texture = Texture { - load_type: AssetLoadType::Resource, - filter, - ..Default::default() - }; - - texture.generate_from(renderer, image)?; - - Ok(texture) - } /// Create texture from file and load it to memory pub fn new_from_file( @@ -117,7 +101,7 @@ impl Texture { _ => return Ok(()), }; - let image = image::open(path.as_str()) + let image = image::open(path) .map(|img| img.to_rgba8()) .expect("unable to open image"); @@ -126,7 +110,7 @@ impl Texture { Ok(()) } - fn generate_from( + pub(crate) fn generate_from( &mut self, renderer: &mut Renderer, image: image::RgbaImage,