use std::collections::HashMap; use std::path::Path; use crate::material::Material; use crate::mesh::Mesh; use crate::shader::{ Error as ShaderError, Kind as ShaderKind, Program as ShaderProgram, Shader, }; use crate::texture::{Id as TextureId, Texture}; use crate::transform::Transform; use crate::vector::Vec3; use crate::vertex::Vertex; const VERTEX_SHADER_FILE: &str = "vertex.glsl"; const FRAGMENT_SHADER_FILE: &str = "fragment.glsl"; const SHADER_DIR: &str = "engine"; #[derive(Debug)] pub struct Object { id: Id, mesh: Mesh, shader: ShaderProgram, transform: Transform, textures: HashMap, material: Material, } impl Object { /// Returns the object ID. #[must_use] pub fn id(&self) -> Id { self.id } #[must_use] pub fn position(&self) -> &Vec3 { self.transform.position() } pub fn translate(&mut self, translation: Vec3) { self.transform .set_position(self.transform.position().clone() + translation); } pub fn scale(&mut self, scaling: Vec3) { self.transform.set_scale(scaling); } pub fn textures(&self) -> impl Iterator { self.textures.iter() } #[must_use] pub fn material(&self) -> &Material { &self.material } #[must_use] pub fn mesh(&self) -> &Mesh { &self.mesh } #[must_use] pub fn shader(&self) -> &ShaderProgram { &self.shader } pub(crate) fn transform(&self) -> &Transform { &self.transform } } /// Object builder. #[derive(Debug)] pub struct Builder { vertices: Vec, indices: Option>, textures: HashMap, material: Option, } impl Builder { /// Returns a new [`Object`] builder. #[must_use] pub fn new() -> Self { Self { vertices: Vec::new(), indices: None, textures: HashMap::new(), material: None, } } #[must_use] pub fn vertices(mut self, vertices: impl IntoIterator) -> Self { self.vertices = vertices.into_iter().collect(); self } #[must_use] pub fn indices(mut self, indices: impl IntoIterator) -> Self { self.indices = Some(indices.into_iter().collect()); self } #[must_use] pub fn texture(mut self, id: TextureId, texture: Texture) -> Self { self.textures.insert(id, texture); self } #[must_use] pub fn textures(mut self, textures: Textures) -> Self where Textures: IntoIterator, { self.textures.extend(textures); self } #[must_use] pub fn material(mut self, material: Material) -> Self { self.material = Some(material); self } /// Builds a new [`Object`]. /// /// # Errors /// Will return `Err` if: /// - Creating shaders fails /// - Linking the shader program fails pub fn build(self, id: Id) -> Result { let mut shader_program = ShaderProgram::new(); shader_program.push_shader( Shader::read_shader_file( ShaderKind::Vertex, &Path::new(SHADER_DIR).join(VERTEX_SHADER_FILE), )? .preprocess()?, ); shader_program.push_shader( Shader::read_shader_file( ShaderKind::Fragment, &Path::new(SHADER_DIR).join(FRAGMENT_SHADER_FILE), )? .preprocess()?, ); let mesh = Mesh::new(self.vertices, self.indices); let transform = Transform::new(); Ok(Object { id, mesh, shader: shader_program, transform, textures: self.textures, material: self.material.ok_or(Error::NoMaterial)?, }) } } impl Default for Builder { fn default() -> Self { Self::new() } } /// Object error #[derive(Debug, thiserror::Error)] pub enum Error { #[error(transparent)] ShaderError(#[from] ShaderError), #[error("Object builder has no material")] NoMaterial, } /// Object ID. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Id { id: u16, } impl Id { /// Returns a new object ID. #[must_use] pub fn new(id: u16) -> Self { Self { id } } }