diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/src/lib.rs | 10 | ||||
| -rw-r--r-- | engine/src/material.rs | 99 | ||||
| -rw-r--r-- | engine/src/material/asset.rs | 33 | ||||
| -rw-r--r-- | engine/src/model.rs | 167 | ||||
| -rw-r--r-- | engine/src/model/asset.rs | 69 | ||||
| -rw-r--r-- | engine/src/rendering/main_render_pass.rs | 129 |
6 files changed, 311 insertions, 196 deletions
diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 57aa125..fd0c43a 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -69,6 +69,16 @@ impl Engine world.add_extension(AssetExtension { assets }); + world.register_system( + *crate::asset::HANDLE_ASSETS_PHASE, + crate::material::set_stored_materials_texture_handles, + ); + + world.register_system( + *crate::asset::HANDLE_ASSETS_PHASE, + crate::model::set_stored_mat_descs_handles, + ); + Self { world } } diff --git a/engine/src/material.rs b/engine/src/material.rs index 5f77950..ae0751b 100644 --- a/engine/src/material.rs +++ b/engine/src/material.rs @@ -1,4 +1,14 @@ -use crate::asset::{Asset, Handle as AssetHandle}; +use ecs::sole::Single; + +use crate::asset::{ + Asset, + Assets, + Dependency as AssetDependency, + Event as AssetEvent, + Events as AssetEvents, + Handle as AssetHandle, + Label as AssetLabel, +}; use crate::builder; use crate::color::{Color, Rgb}; use crate::ecs::Component; @@ -18,6 +28,7 @@ pub struct Material pub diffuse_map: Option<AssetHandle<Texture>>, pub specular_map: Option<AssetHandle<Texture>>, pub shininess: f32, + pub loading_textures: Vec<(LoadingTextureName, AssetLabel<'static>)>, } impl Material @@ -46,12 +57,26 @@ impl Default for Material impl Asset for Material { - fn dependencies(&self) -> impl Iterator<Item = crate::asset::Dependency<'_>> + fn dependencies(&self) -> impl Iterator<Item = AssetDependency<'_>> { - [].into_iter() + self.loading_textures.iter().map(|(_, texture_label)| { + AssetDependency::new::<Texture>(AssetLabel { + path: (&*texture_label.path).into(), + name: texture_label.name.as_deref().map(Into::into), + }) + }) } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum LoadingTextureName +{ + AmbientMap, + DiffuseMap, + SpecularMap, +} + /// [`Material`] builder. #[derive(Debug, Clone)] pub struct Builder @@ -63,6 +88,7 @@ pub struct Builder diffuse_map: Option<AssetHandle<Texture>>, specular_map: Option<AssetHandle<Texture>>, shininess: f32, + loading_textures: Vec<(LoadingTextureName, AssetLabel<'static>)>, } impl Builder @@ -78,6 +104,7 @@ impl Builder diffuse_map: None, specular_map: None, shininess: 32.0, + loading_textures: Vec::with_capacity(3), } } @@ -130,6 +157,17 @@ impl Builder } #[must_use] + pub fn loading_texture( + mut self, + name: LoadingTextureName, + asset_label: AssetLabel<'static>, + ) -> Self + { + self.loading_textures.push((name, asset_label)); + self + } + + #[must_use] pub fn shininess(mut self, shininess: f32) -> Self { self.shininess = shininess; @@ -138,11 +176,8 @@ impl Builder } /// Builds a new [`Material`]. - /// - /// # Panics - /// Will panic if no ambient map, diffuse map or specular map is set. #[must_use] - pub const fn build(self) -> Material + pub fn build(self) -> Material { Material { ambient: self.ambient, @@ -152,6 +187,7 @@ impl Builder diffuse_map: self.diffuse_map, specular_map: self.specular_map, shininess: self.shininess, + loading_textures: self.loading_textures, } } } @@ -210,3 +246,52 @@ impl Default for FlagsBuilder Self::new() } } + +pub(crate) fn set_stored_materials_texture_handles( + mut assets: Single<Assets>, + asset_events: Single<AssetEvents>, +) +{ + let assets = assets.get_mut().unwrap(); + let asset_events = asset_events.get().unwrap(); + + for AssetEvent::Stored(asset_id, ..) in asset_events.iter_last_tick() { + let asset_handle = AssetHandle::<Material>::from_id(*asset_id); + + if !assets.is_loaded_and_has_type(&asset_handle) { + continue; + } + + let Some(material_textures) = assets.get(&asset_handle).map(|mat| { + mat.loading_textures + .iter() + .map(|(tex_name, tex_label)| { + ( + *tex_name, + assets.get_handle_to_loaded::<Texture>(tex_label.clone()), + ) + }) + .collect::<Vec<_>>() + }) else { + unreachable!(); + }; + + let Some(material) = assets.get_mut(&asset_handle) else { + unreachable!(); + }; + + for (tex_name, tex_asset) in material_textures { + let Some(tex_asset) = tex_asset else { + unreachable!(); + }; + + let dst = match tex_name { + LoadingTextureName::AmbientMap => &mut material.ambient_map, + LoadingTextureName::DiffuseMap => &mut material.diffuse_map, + LoadingTextureName::SpecularMap => &mut material.specular_map, + }; + + *dst = Some(tex_asset); + } + } +} diff --git a/engine/src/material/asset.rs b/engine/src/material/asset.rs index f452f54..d48126e 100644 --- a/engine/src/material/asset.rs +++ b/engine/src/material/asset.rs @@ -1,8 +1,13 @@ use std::fs::read_to_string; use std::path::{Path, PathBuf}; -use crate::asset::{Assets, Submitter as AssetSubmitter}; -use crate::material::Material; +use crate::asset::{ + Assets, + Handle as AssetHandle, + Label as AssetLabel, + Submitter as AssetSubmitter, +}; +use crate::material::{LoadingTextureName, Material}; /// Material asset import settings. #[derive(Debug)] @@ -41,21 +46,27 @@ fn import( .shininess(material.shininess); if let Some(ambient_map) = material.ambient_map { - material_builder = material_builder.ambient_map( - asset_submitter.submit_load_other(parent_path.join(&ambient_map.path)), - ); + let asset_label = AssetLabel::from(parent_path.join(&ambient_map.path)); + + material_builder = material_builder + .ambient_map(AssetHandle::invalid()) + .loading_texture(LoadingTextureName::AmbientMap, asset_label); } if let Some(diffuse_map) = material.diffuse_map { - material_builder = material_builder.diffuse_map( - asset_submitter.submit_load_other(parent_path.join(&diffuse_map.path)), - ); + let asset_label = AssetLabel::from(parent_path.join(&diffuse_map.path)); + + material_builder = material_builder + .diffuse_map(AssetHandle::invalid()) + .loading_texture(LoadingTextureName::DiffuseMap, asset_label); } if let Some(specular_map) = material.specular_map { - material_builder = material_builder.specular_map( - asset_submitter.submit_load_other(parent_path.join(&specular_map.path)), - ); + let asset_label = AssetLabel::from(parent_path.join(&specular_map.path)); + + material_builder = material_builder + .specular_map(AssetHandle::invalid()) + .loading_texture(LoadingTextureName::SpecularMap, asset_label); } asset_submitter.submit_store_named(material.name, material_builder.build()); diff --git a/engine/src/model.rs b/engine/src/model.rs index 220e4f6..c231a9f 100644 --- a/engine/src/model.rs +++ b/engine/src/model.rs @@ -1,125 +1,124 @@ -use crate::asset::{Asset, Assets, Handle as AssetHandle}; +use crate::asset::{ + Asset, + Assets, + Dependency as AssetDependency, + Event as AssetEvent, + Events as AssetEvents, + Handle as AssetHandle, + Label as AssetLabel, +}; +use crate::ecs::sole::Single; use crate::ecs::Component; use crate::material::Material; use crate::mesh::Mesh; pub mod asset; -#[derive(Debug, Clone, Component)] +#[derive(Debug, Default)] #[non_exhaustive] -pub struct Model -{ - pub spec_asset: AssetHandle<Spec>, -} +pub struct Model {} -impl Model +impl Asset for Model { - #[must_use] - pub fn new(asset_handle: AssetHandle<Spec>) -> Self + fn dependencies(&self) -> impl Iterator<Item = AssetDependency<'_>> { - Self { spec_asset: asset_handle } + [].into_iter() } } +#[derive(Debug, Clone, Component)] +#[allow(clippy::module_name_repetitions)] +pub struct ModelMesh +{ + pub asset: AssetHandle<Mesh>, +} + +#[derive(Debug, Clone, Component)] +#[allow(clippy::module_name_repetitions)] +pub struct ModelMaterial +{ + pub asset: AssetHandle<MaterialDescription>, +} + #[derive(Debug, Clone)] #[non_exhaustive] -pub struct Spec +pub struct MaterialDescription { - pub mesh_asset: Option<AssetHandle<Mesh>>, - pub materials: Vec<MaterialDescription>, + pub asset: AssetHandle<Material>, + pub loading_material: Option<AssetLabel<'static>>, } -impl Spec +impl MaterialDescription { #[must_use] - pub fn builder() -> SpecBuilder + pub fn new(asset: AssetHandle<Material>) -> Self { - SpecBuilder::default() + Self { asset, loading_material: None } } - pub fn find_first_material<'assets>( - &'assets self, - assets: &'assets Assets, - ) -> MaterialSearchResult<'assets> + #[must_use] + pub fn with_loading_material(mut self, loading_material: AssetLabel<'static>) + -> Self { - let Some(material_desc) = self.materials.first() else { - return MaterialSearchResult::NoMaterials; - }; - - if assets.get(&material_desc.asset).is_none() { - tracing::trace!("Missing material asset"); - return MaterialSearchResult::NotFound; - } - - MaterialSearchResult::Found(&material_desc.asset) + self.loading_material = Some(loading_material); + self } } -impl Asset for Spec +impl Asset for MaterialDescription { - fn dependencies(&self) -> impl Iterator<Item = crate::asset::Dependency<'_>> + fn dependencies(&self) -> impl Iterator<Item = AssetDependency<'_>> { - [].into_iter() + self.loading_material.iter().map(|material_label| { + AssetDependency::new::<Material>(AssetLabel { + path: (&*material_label.path).into(), + name: material_label.name.as_deref().map(Into::into), + }) + }) } } -#[derive(Debug, Default, Clone)] -pub struct SpecBuilder +pub(crate) fn set_stored_mat_descs_handles( + mut assets: Single<Assets>, + asset_events: Single<AssetEvents>, +) { - mesh_asset: Option<AssetHandle<Mesh>>, - materials: Vec<MaterialDescription>, -} + let assets = assets.get_mut().unwrap(); + let asset_events = asset_events.get().unwrap(); -impl SpecBuilder -{ - #[must_use] - pub fn mesh(mut self, asset: AssetHandle<Mesh>) -> Self - { - self.mesh_asset = Some(asset); + for AssetEvent::Stored(asset_id, ..) in asset_events.iter_last_tick() { + let asset_handle = AssetHandle::<MaterialDescription>::from_id(*asset_id); - self - } - - #[must_use] - pub fn materials( - mut self, - materials: impl IntoIterator<Item = MaterialDescription>, - ) -> Self - { - self.materials = materials.into_iter().collect(); + if !assets.is_loaded_and_has_type(&asset_handle) { + continue; + } - self - } + let material_asset = { + let Some(material_desc) = assets.get(&asset_handle) else { + unreachable!(); + }; + + if let Some(material_label) = &material_desc.loading_material { + let Some(material_asset) = + assets.get_handle_to_loaded::<Material>(material_label.clone()) + else { + unreachable!(); + }; + + Some(material_asset) + } else { + None + } + }; - #[tracing::instrument(skip_all)] - pub fn build(self) -> Spec - { - Spec { - mesh_asset: self.mesh_asset, - materials: self.materials, - } - } -} + let Some(material_asset) = material_asset else { + continue; + }; -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct MaterialDescription -{ - pub asset: AssetHandle<Material>, -} + let Some(material_desc) = assets.get_mut(&asset_handle) else { + unreachable!(); + }; -impl MaterialDescription -{ - #[must_use] - pub fn new(asset: AssetHandle<Material>) -> Self - { - Self { asset } + material_desc.asset = material_asset; } } - -pub enum MaterialSearchResult<'a> -{ - Found(&'a AssetHandle<Material>), - NotFound, - NoMaterials, -} diff --git a/engine/src/model/asset.rs b/engine/src/model/asset.rs index af3e1b2..d9de6ce 100644 --- a/engine/src/model/asset.rs +++ b/engine/src/model/asset.rs @@ -1,11 +1,14 @@ use std::fs::read_to_string; use std::path::{Path, PathBuf}; -use ecs::util::Either; - -use crate::asset::{Assets, Label as AssetLabel, Submitter as AssetSubmitter}; -use crate::material::Material; -use crate::model::{MaterialDescription, Spec}; +use crate::asset::{ + Assets, + Handle as AssetHandle, + Label as AssetLabel, + Submitter as AssetSubmitter, +}; +use crate::mesh::Mesh; +use crate::model::{MaterialDescription, Model}; #[derive(Debug, Clone)] #[non_exhaustive] @@ -42,7 +45,8 @@ impl Default for Settings pub fn add_importers(assets: &mut Assets) { - assets.set_importer::<Spec, Settings, Error>(import); + assets.set_importer::<Model, Settings, Error>(import); + assets.set_importer::<Mesh, Settings, Error>(import); } fn import( @@ -69,41 +73,38 @@ fn import( .map_err(|err| Error::ReadFailed(err, path.to_path_buf()))?, )?; + if obj.mtl_libs.len() > 1 { + return Err(Error::MoreThanOneMaterialLibrary); + } + let mesh = obj.to_mesh( crate::file_format::wavefront::obj::ToMeshOptions::default() .y_flip_uvs(settings.y_flip_uvs), )?; - let mesh_asset = asset_submitter.submit_store_named("mesh", mesh); - - if obj.mtl_libs.len() > 1 { - return Err(Error::MoreThanOneMaterialLibrary); + asset_submitter.submit_store_named("mesh", mesh); + + let materials = obj + .unique_used_material_names + .into_iter() + .zip(std::iter::repeat(obj.mtl_libs.iter()).flatten()) + .map(|(material_name, mtl_lib)| { + (material_name.into_string(), parent_path.join(mtl_lib)) + }); + + for (material_name, material_path) in materials { + asset_submitter.submit_store_named( + format!("material_{material_name}"), + MaterialDescription::new(AssetHandle::invalid()).with_loading_material( + AssetLabel { + path: material_path.into(), + name: Some(material_name.into()), + }, + ), + ); } - asset_submitter.submit_store( - Spec::builder() - .mesh(mesh_asset) - .materials(if obj.mtl_libs.is_empty() { - Either::A([].into_iter()) - } else { - Either::B( - obj.unique_used_material_names - .iter() - .zip(std::iter::repeat(obj.mtl_libs.iter()).flatten()) - .map(|(material_name, mtl_lib)| { - MaterialDescription::new( - asset_submitter.submit_load_other::<Material>( - AssetLabel { - path: parent_path.join(mtl_lib).into(), - name: Some(material_name.as_ref().into()), - }, - ), - ) - }), - ) - }) - .build(), - ); + asset_submitter.submit_store(Model::default()); Ok(()) } diff --git a/engine/src/rendering/main_render_pass.rs b/engine/src/rendering/main_render_pass.rs index cb38071..ca19a0f 100644 --- a/engine/src/rendering/main_render_pass.rs +++ b/engine/src/rendering/main_render_pass.rs @@ -26,7 +26,7 @@ use crate::lighting::{ }; use crate::material::{Flags as MaterialFlags, Material}; use crate::mesh::Mesh; -use crate::model::{MaterialSearchResult, Model}; +use crate::model::{ModelMaterial, ModelMesh}; use crate::projection::{ClipVolume as ProjectionClipVolume, Perspective, Projection}; use crate::rendering::object::{Id as ObjectId, Store as ObjectStore}; use crate::rendering::shader::cursor::{ @@ -70,7 +70,8 @@ use crate::vector::Vec3; use crate::windowing::window::Window; type RenderableEntity<'a> = ( - &'a Model, + &'a ModelMesh, + Option<&'a ModelMaterial>, Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>, Option<&'a MaterialFlags>, Option<&'a Transform>, @@ -157,8 +158,14 @@ pub fn add_main_render_pass( draw_properties: DrawProperties::default(), }); - for (model, ..) in &renderable_query { - add_renderable_creation_commands(assets, object_store, render_pass, &model); + for (mesh, material, ..) in &renderable_query { + add_renderable_creation_commands( + assets, + object_store, + render_pass, + &mesh, + material.as_deref(), + ); } let shaders = @@ -224,29 +231,24 @@ pub fn add_main_render_pass( .commands .push(Command::ClearBuffers(buf_clear_mask)); - for (model, material_flags, transform, draw_flags) in &renderable_query { - let Some(model_spec) = assets.get(&model.spec_asset) else { - continue; - }; - - let Some(mesh_asset) = &model_spec.mesh_asset else { - continue; - }; - - if assets.get(mesh_asset).is_none() { + for (mesh, material, material_flags, transform, draw_flags) in &renderable_query { + if assets.get(&mesh.asset).is_none() { continue; } - let model_material = match model_spec.find_first_material(assets) { - MaterialSearchResult::Found(model_material_asset) - if let Some(model_material) = assets.get(model_material_asset) => + let model_material = match material { + Some(material) + if let Some(material_desc) = assets.get(&material.asset) => { - model_material + match assets.get(&material_desc.asset) { + Some(material) => material, + None => continue, + } } - MaterialSearchResult::Found(_) | MaterialSearchResult::NotFound => { + Some(_) => { continue; } - MaterialSearchResult::NoMaterials => &Material::builder().build(), + None => &Material::default(), }; if model_material @@ -273,7 +275,9 @@ pub fn add_main_render_pass( .commands .push(Command::ActivateShader(shaders.main_3d_shader_obj_id)); - if let Some(draw_flags) = draw_flags.as_deref().filter(|&draw_flags| draw_flags.polygon_mode_config != PolygonModeConfig::default()) { + if let Some(draw_flags) = draw_flags.as_deref().filter(|&draw_flags| { + draw_flags.polygon_mode_config != PolygonModeConfig::default() + }) { render_pass.commands.push(Command::UpdateDrawProperties( DrawProperties { polygon_mode_config: draw_flags.polygon_mode_config.clone(), @@ -284,7 +288,7 @@ pub fn add_main_render_pass( } render_pass.commands.push(Command::DrawMesh( - ObjectId::Asset(mesh_asset.id()), + ObjectId::Asset(mesh.asset.id()), DrawMeshOptions::default(), )); @@ -466,37 +470,29 @@ fn add_renderable_creation_commands( assets: &Assets, object_store: &mut ObjectStore, render_pass: &mut RenderPass, - model: &Model, + mesh: &ModelMesh, + material: Option<&ModelMaterial>, ) { - let Some(model_spec) = assets.get(&model.spec_asset) else { - return; - }; - - let Some(mesh_asset) = &model_spec.mesh_asset else { - return; - }; - - if assets.get(mesh_asset).is_none() { + if assets.get(&mesh.asset).is_none() { return; } - debug_assert!(model_spec.materials.len() <= 1); - - let model_material = match model_spec.find_first_material(assets) { - MaterialSearchResult::Found(model_material_asset) => { - let Some(model_material) = assets.get(model_material_asset) else { - return; - }; - - model_material + let model_material = match material { + Some(material) if let Some(material_desc) = assets.get(&material.asset) => { + match assets.get(&material_desc.asset) { + Some(material) => material, + None => return, + } } - MaterialSearchResult::NotFound => { + Some(_) => { return; } - MaterialSearchResult::NoMaterials => &Material::builder().build(), + None => &Material::default(), }; + // debug_assert!(model_spec.materials.len() <= 1); + for texture_asset in model_material.textures() { let Some(texture) = assets.get(texture_asset) else { return; @@ -505,7 +501,8 @@ fn add_renderable_creation_commands( let Texture::Texture2D(texture) = texture else { tracing::error!( texture_asset_id = ?texture_asset.id(), - texture_asset_label = ?assets.get_label(texture_asset), + texture_asset_label = + assets.get_label(texture_asset).map(tracing::field::display), "Material texture map is not 2D" ); return; @@ -537,12 +534,14 @@ fn add_renderable_creation_commands( }); } - if !object_store.contains_maybe_pending_with_id(&ObjectId::Asset(mesh_asset.id())) { - object_store.insert_pending(ObjectId::Asset(mesh_asset.id())); + let mesh_obj_id = ObjectId::Asset(mesh.asset.id()); + + if !object_store.contains_maybe_pending_with_id(&mesh_obj_id) { + object_store.insert_pending(mesh_obj_id); render_pass.commands.push(Command::CreateMesh { - obj_id: ObjectId::Asset(mesh_asset.id()), - mesh: AssetOrValue::Asset(mesh_asset.clone()), + obj_id: mesh_obj_id, + mesh: AssetOrValue::Asset(mesh.asset.clone()), usage: MeshUsage::Static, }); } @@ -728,8 +727,8 @@ fn add_set_3d_shader_renderable_bindings( let model_matrix = transform.to_matrix(); let inverted_model_matrix = model_matrix.inverse(); - let material_flags = material_flags - .unwrap_or(&const { MaterialFlags::builder().build() }); + let material_flags = + material_flags.unwrap_or(&const { MaterialFlags::builder().build() }); let env_lighting = match &scene_env_lighting { Some(env_lighting) => env_lighting, @@ -747,7 +746,9 @@ fn add_set_3d_shader_renderable_bindings( let diffuse_map_obj_id = material .diffuse_map .as_ref() - .map_or(white_1x1_tex_obj_id, |diffuse_map| ObjectId::Asset(diffuse_map.id())); + .map_or(white_1x1_tex_obj_id, |diffuse_map_asset| { + ObjectId::Asset(diffuse_map_asset.id()) + }); render_pass.commands.extend( [ @@ -758,10 +759,13 @@ fn add_set_3d_shader_renderable_bindings( .field("model_inverted") .binding(inverted_model_matrix.into())?, material_shader_cursor.field("ambient").binding( - if material_flags - .use_ambient_color { &material.ambient } else { &env_lighting.ambient_color } - .to_rgb_lossy() - .into(), + if material_flags.use_ambient_color { + &material.ambient + } else { + &env_lighting.ambient_color + } + .to_rgb_lossy() + .into(), )?, material_shader_cursor .field("diffuse") @@ -774,7 +778,9 @@ fn add_set_3d_shader_renderable_bindings( material .ambient_map .as_ref() - .map_or(diffuse_map_obj_id, |ambient_map| ObjectId::Asset(ambient_map.id())), + .map_or(diffuse_map_obj_id, |ambient_map_asset| { + ObjectId::Asset(ambient_map_asset.id()) + }), ShaderBindingTextureKind::Texture2D, ), )?, @@ -789,7 +795,9 @@ fn add_set_3d_shader_renderable_bindings( material .specular_map .as_ref() - .map_or(white_1x1_tex_obj_id, |specular_map| ObjectId::Asset(specular_map.id())), + .map_or(white_1x1_tex_obj_id, |specular_map_asset| { + ObjectId::Asset(specular_map_asset.id()) + }), ShaderBindingTextureKind::Texture2D, ), )?, @@ -895,9 +903,10 @@ fn load_sky_box( actions: &mut Actions, ) -> Result<Option<SkyBoxIds>, crate::Error> { - let mesh_object_id = if let Some(sky_box_state) = &sky_box_state { sky_box_state.mesh_object_id } else { - let sky_box_mesh = - Mesh::cube(Dimens3 { width: 1.0, height: 1.0, depth: 1.0 }); + let mesh_object_id = if let Some(sky_box_state) = &sky_box_state { + sky_box_state.mesh_object_id + } else { + let sky_box_mesh = Mesh::cube(Dimens3 { width: 1.0, height: 1.0, depth: 1.0 }); let mesh_object_id = ObjectId::new_sequential(); |
