summaryrefslogtreecommitdiff
path: root/engine/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-11-27 20:02:08 +0100
committerHampusM <hampus@hampusmat.com>2023-11-27 20:02:08 +0100
commitc230f5aaea3df46ae9a4d7c1c9761e55ef827b82 (patch)
tree9e429a33df6e12f4b2f9adf87d08dad2a0127756 /engine/src
parent935f35455ac2e3547cdd21cd4596538958a7217e (diff)
feat(engine): add lighting maps
Diffstat (limited to 'engine/src')
-rw-r--r--engine/src/lib.rs15
-rw-r--r--engine/src/material.rs53
-rw-r--r--engine/src/object.rs92
-rw-r--r--engine/src/opengl/shader.rs14
-rw-r--r--engine/src/opengl/texture.rs42
-rw-r--r--engine/src/renderer/mod.rs42
-rw-r--r--engine/src/texture.rs58
7 files changed, 216 insertions, 100 deletions
diff --git a/engine/src/lib.rs b/engine/src/lib.rs
index 30346ed..c4711f2 100644
--- a/engine/src/lib.rs
+++ b/engine/src/lib.rs
@@ -135,11 +135,13 @@ impl Engine
let window_size = self.window.size().map_err(Error::GetWindowSizeFailed)?;
- self.renderer.render(
- self.objects.values(),
- self.light_source.as_ref(),
- &window_size,
- );
+ self.renderer
+ .render(
+ self.objects.values(),
+ self.light_source.as_ref(),
+ &window_size,
+ )
+ .map_err(Error::RenderingFailed)?;
self.window
.swap_buffers()
@@ -234,4 +236,7 @@ pub enum Error
#[error("Failed to get cursor position")]
GetCursorPosFailed(#[source] glfw::Error),
+
+ #[error("Rendering failed")]
+ RenderingFailed(#[source] renderer::Error),
}
diff --git a/engine/src/material.rs b/engine/src/material.rs
index 240d7e2..5af3282 100644
--- a/engine/src/material.rs
+++ b/engine/src/material.rs
@@ -1,32 +1,32 @@
-use crate::color::Color;
+use crate::texture::Id as TextureId;
#[derive(Debug, Clone)]
pub struct Material
{
- ambient: Color<f32>,
- diffuse: Color<f32>,
- specular: Color<f32>,
+ ambient_map: TextureId,
+ diffuse_map: TextureId,
+ specular_map: TextureId,
shininess: f32,
}
impl Material
{
#[must_use]
- pub fn ambient(&self) -> &Color<f32>
+ pub fn ambient_map(&self) -> &TextureId
{
- &self.ambient
+ &self.ambient_map
}
#[must_use]
- pub fn diffuse(&self) -> &Color<f32>
+ pub fn diffuse_map(&self) -> &TextureId
{
- &self.diffuse
+ &self.diffuse_map
}
#[must_use]
- pub fn specular(&self) -> &Color<f32>
+ pub fn specular_map(&self) -> &TextureId
{
- &self.specular
+ &self.specular_map
}
#[must_use]
@@ -40,9 +40,9 @@ impl Material
#[derive(Debug, Clone)]
pub struct Builder
{
- ambient: Color<f32>,
- diffuse: Color<f32>,
- specular: Color<f32>,
+ ambient_map: Option<TextureId>,
+ diffuse_map: Option<TextureId>,
+ specular_map: Option<TextureId>,
shininess: f32,
}
@@ -52,33 +52,33 @@ impl Builder
pub fn new() -> Self
{
Self {
- ambient: 0.2.into(),
- diffuse: 0.5.into(),
- specular: 1.0.into(),
+ ambient_map: None,
+ diffuse_map: None,
+ specular_map: None,
shininess: 32.0,
}
}
#[must_use]
- pub fn ambient(mut self, ambient: Color<f32>) -> Self
+ pub fn ambient_map(mut self, ambient_map: TextureId) -> Self
{
- self.ambient = ambient;
+ self.ambient_map = Some(ambient_map);
self
}
#[must_use]
- pub fn diffuse(mut self, diffuse: Color<f32>) -> Self
+ pub fn diffuse_map(mut self, diffuse_map: TextureId) -> Self
{
- self.diffuse = diffuse;
+ self.diffuse_map = Some(diffuse_map);
self
}
#[must_use]
- pub fn specular(mut self, specular: Color<f32>) -> Self
+ pub fn specular_map(mut self, specular_map: TextureId) -> Self
{
- self.specular = specular;
+ self.specular_map = Some(specular_map);
self
}
@@ -92,13 +92,16 @@ impl Builder
}
/// Builds a new [`Material`].
+ ///
+ /// # Panics
+ /// Will panic if no ambient map, diffuse map or specular map is set.
#[must_use]
pub fn build(&self) -> Material
{
Material {
- ambient: self.ambient.clone(),
- diffuse: self.diffuse.clone(),
- specular: self.specular.clone(),
+ ambient_map: self.ambient_map.unwrap(),
+ diffuse_map: self.diffuse_map.unwrap(),
+ specular_map: self.specular_map.unwrap(),
shininess: self.shininess,
}
}
diff --git a/engine/src/object.rs b/engine/src/object.rs
index 6749124..4e143a0 100644
--- a/engine/src/object.rs
+++ b/engine/src/object.rs
@@ -1,8 +1,9 @@
+use std::collections::HashMap;
use std::fs::read_to_string;
use std::io::Error as IoError;
use std::path::{Path, PathBuf};
-use crate::material::{Builder as MaterialBuilder, Material};
+use crate::material::Material;
use crate::opengl::shader::{
Error as ShaderError,
Kind as ShaderKind,
@@ -11,7 +12,7 @@ use crate::opengl::shader::{
};
use crate::renderer::Renderable;
use crate::shader_preprocessor::{Error as ShaderPreprocessorError, ShaderPreprocessor};
-use crate::texture::Texture;
+use crate::texture::{Id as TextureId, Texture};
use crate::transform::Transform;
use crate::vector::Vec3;
use crate::vertex::Vertex;
@@ -19,8 +20,7 @@ use crate::vertex::Vertex;
const SHADER_DIR: &str = "engine";
const VERTEX_SHADER_FILE: &str = "vertex.glsl";
-const FRAG_SHADER_COLOR_FILE: &str = "fragment-color.glsl";
-const FRAG_SHADER_TEXTURE_FILE: &str = "fragment-texture.glsl";
+const FRAGMENT_SHADER_FILE: &str = "fragment.glsl";
#[derive(Debug)]
pub struct Object
@@ -28,7 +28,7 @@ pub struct Object
id: Id,
renderable: Renderable,
transform: Transform,
- texture: Option<Texture>,
+ textures: HashMap<TextureId, Texture>,
material: Material,
}
@@ -58,10 +58,9 @@ impl Object
self.transform.set_scale(scaling);
}
- #[must_use]
- pub fn texture(&self) -> Option<&Texture>
+ pub fn textures(&self) -> impl Iterator<Item = (&TextureId, &Texture)>
{
- self.texture.as_ref()
+ self.textures.iter()
}
#[must_use]
@@ -87,8 +86,8 @@ pub struct Builder
{
vertices: Vec<Vertex>,
indices: Option<Vec<u32>>,
- texture: Option<Texture>,
- material: Material,
+ textures: HashMap<TextureId, Texture>,
+ material: Option<Material>,
}
impl Builder
@@ -100,8 +99,8 @@ impl Builder
Self {
vertices: Vec::new(),
indices: None,
- texture: None,
- material: MaterialBuilder::new().build(),
+ textures: HashMap::new(),
+ material: None,
}
}
@@ -122,9 +121,19 @@ impl Builder
}
#[must_use]
- pub fn texture(mut self, texture: Texture) -> Self
+ pub fn texture(mut self, id: TextureId, texture: Texture) -> Self
+ {
+ self.textures.insert(id, texture);
+
+ self
+ }
+
+ #[must_use]
+ pub fn textures<Textures>(mut self, textures: Textures) -> Self
+ where
+ Textures: IntoIterator<Item = (TextureId, Texture)>,
{
- self.texture = Some(texture);
+ self.textures.extend(textures);
self
}
@@ -132,7 +141,7 @@ impl Builder
#[must_use]
pub fn material(mut self, material: Material) -> Self
{
- self.material = material;
+ self.material = Some(material);
self
}
@@ -147,11 +156,7 @@ impl Builder
{
let vertex_shader_file = Path::new(SHADER_DIR).join(VERTEX_SHADER_FILE);
- let fragment_shader_color_file =
- Path::new(SHADER_DIR).join(FRAG_SHADER_COLOR_FILE);
-
- let fragment_shader_texture_file =
- Path::new(SHADER_DIR).join(FRAG_SHADER_TEXTURE_FILE);
+ let fragment_shader_file = Path::new(SHADER_DIR).join(FRAGMENT_SHADER_FILE);
let vertex_shader_content =
read_to_string(&vertex_shader_file).map_err(|err| {
@@ -161,17 +166,11 @@ impl Builder
}
})?;
- let fragment_shader_color_content = read_to_string(&fragment_shader_color_file)
- .map_err(|err| Error::ReadShaderFailed {
- source: err,
- shader_file: fragment_shader_color_file.clone(),
- })?;
-
- let fragment_shader_texture_content =
- read_to_string(&fragment_shader_texture_file).map_err(|err| {
+ let fragment_shader_content =
+ read_to_string(&fragment_shader_file).map_err(|err| {
Error::ReadShaderFailed {
source: err,
- shader_file: fragment_shader_texture_file.clone(),
+ shader_file: fragment_shader_file.clone(),
}
})?;
@@ -184,36 +183,20 @@ impl Builder
shader_file: vertex_shader_file,
})?;
- let frag_shader_color_content_preprocessed = shader_preprocessor
- .preprocess(fragment_shader_color_content, &fragment_shader_color_file)
+ let frag_shader_content_preprocessed = shader_preprocessor
+ .preprocess(fragment_shader_content, &fragment_shader_file)
.map_err(|err| Error::PreprocessShaderFailed {
source: err,
- shader_file: fragment_shader_color_file,
- })?;
-
- let frag_shader_texture_content_preprocessed = shader_preprocessor
- .preprocess(
- fragment_shader_texture_content,
- &fragment_shader_texture_file,
- )
- .map_err(|err| Error::PreprocessShaderFailed {
- source: err,
- shader_file: fragment_shader_texture_file,
+ shader_file: fragment_shader_file,
})?;
let vertex_shader =
Self::create_shader(ShaderKind::Vertex, &vertex_shader_content_preprocessed)
.map_err(Error::CreateVertexShaderFailed)?;
- let fragment_shader = Self::create_shader(
- ShaderKind::Fragment,
- if self.texture.is_some() {
- &frag_shader_texture_content_preprocessed
- } else {
- &frag_shader_color_content_preprocessed
- },
- )
- .map_err(Error::CreateFragmentShaderFailed)?;
+ let fragment_shader =
+ Self::create_shader(ShaderKind::Fragment, &frag_shader_content_preprocessed)
+ .map_err(Error::CreateFragmentShaderFailed)?;
let shader_program = ShaderProgram::new();
@@ -233,8 +216,8 @@ impl Builder
id,
renderable,
transform,
- texture: self.texture,
- material: self.material,
+ textures: self.textures,
+ material: self.material.ok_or(Error::NoMaterial)?,
})
}
@@ -285,6 +268,9 @@ pub enum Error
source: ShaderPreprocessorError,
shader_file: PathBuf,
},
+
+ #[error("Object builder has no material")]
+ NoMaterial,
}
/// Object ID.
diff --git a/engine/src/opengl/shader.rs b/engine/src/opengl/shader.rs
index 9bed09b..224a9bc 100644
--- a/engine/src/opengl/shader.rs
+++ b/engine/src/opengl/shader.rs
@@ -153,7 +153,7 @@ impl Program
Ok(())
}
- pub fn activate(&self, cb: impl FnOnce(CurrentlyBound<'_, Self>))
+ pub fn activate<Ret>(&self, cb: impl FnOnce(CurrentlyBound<'_, Self>) -> Ret) -> Ret
{
unsafe {
gl::UseProgram(self.program);
@@ -162,7 +162,7 @@ impl Program
// SAFETY: The shader program object is bound above
let currently_bound = unsafe { CurrentlyBound::new() };
- cb(currently_bound);
+ cb(currently_bound)
}
pub fn set_uniform_matrix_4fv(
@@ -205,6 +205,16 @@ impl Program
}
}
+ pub fn set_uniform_1i(&self, _: &CurrentlyBound<Self>, name: &CStr, num: i32)
+ {
+ let uniform_location =
+ unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
+
+ unsafe {
+ gl::Uniform1i(uniform_location, num);
+ }
+ }
+
fn get_info_log(&self) -> String
{
let mut buf = vec![gl::types::GLchar::default(); 512];
diff --git a/engine/src/opengl/texture.rs b/engine/src/opengl/texture.rs
index 4186479..56eb118 100644
--- a/engine/src/opengl/texture.rs
+++ b/engine/src/opengl/texture.rs
@@ -1,6 +1,7 @@
use std::ptr::null;
use crate::opengl::currently_bound::CurrentlyBound;
+use crate::texture::Id;
use crate::vector::Vec2;
#[derive(Debug)]
@@ -234,3 +235,44 @@ impl PixelDataFormat
}
}
}
+
+pub fn set_active_texture_unit(texture_unit: TextureUnit)
+{
+ unsafe {
+ gl::ActiveTexture(texture_unit.into_gl());
+ }
+}
+
+macro_rules! texture_unit_enum {
+ (cnt=$cnt: literal) => {
+ seq_macro::seq!(N in 0..$cnt {
+ #[derive(Debug, Clone, Copy)]
+ pub enum TextureUnit {
+ #(
+ No~N,
+ )*
+ }
+
+ impl TextureUnit {
+ fn into_gl(self) -> gl::types::GLenum {
+ match self {
+ #(
+ Self::No~N => gl::TEXTURE~N,
+ )*
+ }
+ }
+
+ pub fn from_texture_id(texture_id: Id) -> Option<Self> {
+ match texture_id.into_inner() {
+ #(
+ N => Some(Self::No~N),
+ )*
+ _ => None
+ }
+ }
+ }
+ });
+ };
+}
+
+texture_unit_enum!(cnt = 31);
diff --git a/engine/src/renderer/mod.rs b/engine/src/renderer/mod.rs
index 6fa692c..593b55f 100644
--- a/engine/src/renderer/mod.rs
+++ b/engine/src/renderer/mod.rs
@@ -18,6 +18,7 @@ use crate::opengl::currently_bound::CurrentlyBound;
#[cfg(feature = "debug")]
use crate::opengl::debug::{MessageSeverity, MessageSource, MessageType};
use crate::opengl::shader::Program as ShaderProgram;
+use crate::opengl::texture::{set_active_texture_unit, TextureUnit};
use crate::opengl::vertex_array::{PrimitiveKind, VertexArray};
use crate::opengl::{clear_buffers, enable, BufferClearMask, Capability};
use crate::projection::new_perspective;
@@ -69,7 +70,7 @@ impl Renderer
objects: impl IntoIterator<Item = &'obj Object>,
light_source: Option<&LightSource>,
window_size: &WindowSize,
- )
+ ) -> Result<(), Error>
{
clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH);
@@ -91,15 +92,22 @@ impl Renderer
&shader_program_curr_bound,
);
- if let Some(texture) = obj.texture() {
- texture.inner().bind(|_| {
- Self::draw_object(obj);
- });
- } else {
- Self::draw_object(obj);
+ for (texture_id, texture) in obj.textures() {
+ let texture_unit = TextureUnit::from_texture_id(*texture_id)
+ .ok_or(Error::TextureIdIsInvalidTextureUnit)?;
+
+ set_active_texture_unit(texture_unit);
+
+ texture.inner().bind(|_| {});
}
- });
+
+ Self::draw_object(obj);
+
+ Ok(())
+ })?;
}
+
+ Ok(())
}
pub fn set_viewport(position: &Vec2<u32>, size: &WindowSize)
@@ -217,6 +225,9 @@ pub enum Error
{
#[error("Failed to get window size")]
GetWindowSizeFailed(#[source] glfw::Error),
+
+ #[error("Texture ID is a invalid texture unit")]
+ TextureIdIsInvalidTextureUnit,
}
fn apply_transformation_matrices(
@@ -300,22 +311,25 @@ fn apply_light(
.into(),
);
- obj.renderable().shader_program.set_uniform_vec_3fv(
+ #[allow(clippy::cast_possible_wrap)]
+ obj.renderable().shader_program.set_uniform_1i(
shader_program_curr_bound,
cstr!("material.ambient"),
- &obj.material().ambient().clone().into(),
+ obj.material().ambient_map().into_inner() as i32,
);
- obj.renderable().shader_program.set_uniform_vec_3fv(
+ #[allow(clippy::cast_possible_wrap)]
+ obj.renderable().shader_program.set_uniform_1i(
shader_program_curr_bound,
cstr!("material.diffuse"),
- &obj.material().diffuse().clone().into(),
+ obj.material().diffuse_map().into_inner() as i32,
);
- obj.renderable().shader_program.set_uniform_vec_3fv(
+ #[allow(clippy::cast_possible_wrap)]
+ obj.renderable().shader_program.set_uniform_1i(
shader_program_curr_bound,
cstr!("material.specular"),
- &obj.material().specular().clone().into(),
+ obj.material().specular_map().into_inner() as i32,
);
obj.renderable().shader_program.set_uniform_1fv(
diff --git a/engine/src/texture.rs b/engine/src/texture.rs
index f32bc0a..f644b58 100644
--- a/engine/src/texture.rs
+++ b/engine/src/texture.rs
@@ -1,8 +1,9 @@
use std::path::Path;
use image::io::Reader as ImageReader;
-use image::{DynamicImage, ImageError};
+use image::{DynamicImage, EncodableLayout, ImageError, Rgb, RgbImage};
+use crate::color::Color;
use crate::opengl::texture::{PixelDataFormat, Texture as InnerTexture};
use crate::vector::Vec2;
@@ -67,6 +68,39 @@ impl Texture
Ok(me)
}
+ #[must_use]
+ pub fn new_from_color(dimensions: &Vec2<u32>, color: &Color<u8>) -> Self
+ {
+ let image = RgbImage::from_pixel(
+ dimensions.x,
+ dimensions.y,
+ Rgb([color.red, color.green, color.blue]),
+ );
+
+ let inner = InnerTexture::new();
+
+ inner.bind(|texture_curr_bound| {
+ InnerTexture::generate(
+ &texture_curr_bound,
+ dimensions,
+ image.as_bytes(),
+ PixelDataFormat::Rgb,
+ );
+ });
+
+ let me = Self {
+ inner,
+ pixel_data_format: PixelDataFormat::Rgb,
+ dimensions: dimensions.clone(),
+ };
+
+ me.set_wrap(Wrapping::Repeat);
+ me.set_magnifying_filter(Filtering::Linear);
+ me.set_minifying_filter(Filtering::Nearest);
+
+ me
+ }
+
pub fn set_wrap(&self, wrapping: Wrapping)
{
self.inner.bind(|texture_curr_bound| {
@@ -126,3 +160,25 @@ pub enum Error
#[error("Unsupported image data kind")]
UnsupportedImageDataKind,
}
+
+/// Texture ID.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Id
+{
+ id: u32,
+}
+
+impl Id
+{
+ /// Returns a new texture ID.
+ #[must_use]
+ pub fn new(id: u32) -> Self
+ {
+ Self { id }
+ }
+
+ pub(crate) fn into_inner(self) -> u32
+ {
+ self.id
+ }
+}