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; use crate::reflection::Reflection; use crate::texture::Texture; pub mod asset; #[derive(Debug, Clone)] #[non_exhaustive] pub struct Material { pub ambient: Color, pub diffuse: Color, pub specular: Color, pub ambient_map: Option>, pub diffuse_map: Option>, pub specular_map: Option>, pub shininess: f32, pub loading_textures: Vec<(LoadingTextureName, AssetLabel<'static>)>, } impl Material { #[must_use] pub fn builder() -> Builder { Builder::new() } pub fn textures(&self) -> impl Iterator> { [&self.ambient_map, &self.diffuse_map, &self.specular_map] .into_iter() .flatten() } } impl Default for Material { fn default() -> Self { Self::builder().build() } } impl Asset for Material { fn dependencies(&self) -> impl Iterator> { self.loading_textures.iter().map(|(_, texture_label)| { AssetDependency::new::(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 { ambient: Color, diffuse: Color, specular: Color, ambient_map: Option>, diffuse_map: Option>, specular_map: Option>, shininess: f32, loading_textures: Vec<(LoadingTextureName, AssetLabel<'static>)>, } impl Builder { #[must_use] pub fn new() -> Self { Self { ambient: Color::Rgb(Rgb::::white()), diffuse: Color::Rgb(Rgb::::white()), specular: Color::Rgb(Rgb::::white()), ambient_map: None, diffuse_map: None, specular_map: None, shininess: 32.0, loading_textures: Vec::with_capacity(3), } } #[must_use] pub fn ambient(mut self, ambient: Color) -> Self { self.ambient = ambient; self } #[must_use] pub fn diffuse(mut self, diffuse: Color) -> Self { self.diffuse = diffuse; self } #[must_use] pub fn specular(mut self, specular: Color) -> Self { self.specular = specular; self } #[must_use] pub fn ambient_map(mut self, ambient_map: AssetHandle) -> Self { self.ambient_map = Some(ambient_map); self } #[must_use] pub fn diffuse_map(mut self, diffuse_map: AssetHandle) -> Self { self.diffuse_map = Some(diffuse_map); self } #[must_use] pub fn specular_map(mut self, specular_map: AssetHandle) -> Self { self.specular_map = Some(specular_map); self } #[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; self } /// Builds a new [`Material`]. #[must_use] pub fn build(self) -> Material { Material { ambient: self.ambient, diffuse: self.diffuse, specular: self.specular, ambient_map: self.ambient_map, diffuse_map: self.diffuse_map, specular_map: self.specular_map, shininess: self.shininess, loading_textures: self.loading_textures, } } } impl Default for Builder { fn default() -> Self { Self::new() } } builder! { /// Material flags. #[builder(name = FlagsBuilder, derives = (Debug, Clone))] #[derive(Debug, Clone, Component, Reflection)] #[non_exhaustive] pub struct Flags { /// Whether to use material's ambient color instead of the global ambient color. /// Default is `false` pub use_ambient_color: bool, } } impl Flags { #[must_use] pub const fn builder() -> FlagsBuilder { FlagsBuilder::new() } } impl Default for Flags { fn default() -> Self { Self::builder().build() } } impl FlagsBuilder { #[must_use] pub const fn new() -> Self { Self { use_ambient_color: false } } } impl Default for FlagsBuilder { fn default() -> Self { Self::new() } } pub(crate) fn set_stored_materials_texture_handles( mut assets: Single, asset_events: Single, ) { 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::::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::(tex_label.clone()), ) }) .collect::>() }) 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); } } }