use ecs::Component; use crate::color::Color; use crate::data_types::dimens::Dimens; use crate::texture::{Id as TextureId, Texture}; use crate::util::builder; #[derive(Debug, Clone, Component)] #[non_exhaustive] pub struct Material { pub ambient: Color, pub diffuse: Color, pub specular: Color, pub ambient_map: TextureId, pub diffuse_map: TextureId, pub specular_map: TextureId, pub textures: Vec, pub shininess: f32, } impl Material { pub fn builder() -> Builder { Builder::default() } } /// [`Material`] builder. #[derive(Debug, Clone)] pub struct Builder { ambient: Option>, diffuse: Option>, specular: Option>, ambient_map: Option, diffuse_map: Option, specular_map: Option, textures: Vec, shininess: f32, } impl Builder { #[must_use] pub fn new() -> Self { Self { ambient: None, diffuse: None, specular: None, ambient_map: None, diffuse_map: None, specular_map: None, textures: Vec::new(), shininess: 32.0, } } #[must_use] pub fn ambient(mut self, ambient: Color) -> Self { self.ambient = Some(ambient); self } #[must_use] pub fn diffuse(mut self, diffuse: Color) -> Self { self.diffuse = Some(diffuse); self } #[must_use] pub fn specular(mut self, specular: Color) -> Self { self.specular = Some(specular); self } #[must_use] pub fn ambient_map(mut self, ambient_map: TextureId) -> Self { self.ambient_map = Some(ambient_map); self } #[must_use] pub fn diffuse_map(mut self, diffuse_map: TextureId) -> Self { self.diffuse_map = Some(diffuse_map); self } #[must_use] pub fn specular_map(mut self, specular_map: TextureId) -> Self { self.specular_map = Some(specular_map); self } #[must_use] pub fn textures(mut self, textures: impl IntoIterator) -> Self { self.textures = textures.into_iter().collect(); self } #[must_use] pub fn texture(mut self, texture: Texture) -> Self { self.textures.push(texture); self } #[must_use] pub fn shininess(mut self, shininess: f32) -> Self { self.shininess = shininess; self } /// Builds a new [`Material`]. /// /// # Panics /// Will panic if no ambient map, diffuse map or specular map is set. #[must_use] pub fn build(mut self) -> Material { let ambient_map = self.ambient_map.unwrap_or_else(|| { let texture = create_1x1_white_texture(); let texture_id = texture.id(); self.textures.push(texture); texture_id }); let diffuse_map = self.diffuse_map.unwrap_or_else(|| { let texture = create_1x1_white_texture(); let texture_id = texture.id(); self.textures.push(texture); texture_id }); let specular_map = self.specular_map.unwrap_or_else(|| { let texture = create_1x1_white_texture(); let texture_id = texture.id(); self.textures.push(texture); texture_id }); Material { ambient: self.ambient.unwrap_or(Color::WHITE_F32), diffuse: self.diffuse.unwrap_or(Color::WHITE_F32), specular: self.specular.unwrap_or(Color::WHITE_F32), ambient_map, diffuse_map, specular_map, textures: self.textures, shininess: self.shininess, } } } impl Default for Builder { fn default() -> Self { Self::new() } } builder! { /// Material flags. #[builder(name = FlagsBuilder, derives = (Debug, Default, Clone))] #[derive(Debug, Default, Clone, Component)] #[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 fn builder() -> FlagsBuilder { FlagsBuilder::default() } } fn create_1x1_white_texture() -> Texture { Texture::new_from_color(&Dimens { width: 1, height: 1 }, &Color::WHITE_U8) }