summaryrefslogtreecommitdiff
path: root/engine/src/renderer
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/renderer')
-rw-r--r--engine/src/renderer/opengl.rs525
-rw-r--r--engine/src/renderer/opengl/glsl/fragment.glsl73
-rw-r--r--engine/src/renderer/opengl/glsl/light.glsl133
-rw-r--r--engine/src/renderer/opengl/glsl/vertex.glsl24
-rw-r--r--engine/src/renderer/opengl/glsl/vertex_data.glsl11
-rw-r--r--engine/src/renderer/opengl/vertex.rs64
6 files changed, 630 insertions, 200 deletions
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs
index a353c6a..cfd046f 100644
--- a/engine/src/renderer/opengl.rs
+++ b/engine/src/renderer/opengl.rs
@@ -2,75 +2,109 @@
use std::collections::HashMap;
use std::ffi::{c_void, CString};
-use std::ops::Deref;
+use std::io::{Error as IoError, ErrorKind as IoErrorKind};
+use std::path::Path;
use std::process::abort;
use ecs::actions::Actions;
use ecs::component::local::Local;
-use ecs::query::options::{Not, With};
+use ecs::component::Handle as ComponentHandle;
+use ecs::phase::START as START_PHASE;
+use ecs::query::term::Without;
use ecs::sole::Single;
use ecs::system::{Into as _, System};
use ecs::{Component, Query};
+use crate::asset::{Assets, Id as AssetId};
use crate::camera::{Active as ActiveCamera, Camera};
use crate::color::Color;
use crate::data_types::dimens::Dimens;
use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig};
-use crate::event::{Present as PresentEvent, Start as StartEvent};
+use crate::image::{ColorType as ImageColorType, Image};
use crate::lighting::{DirectionalLight, GlobalLight, PointLight};
use crate::material::{Flags as MaterialFlags, Material};
use crate::matrix::Matrix;
use crate::mesh::Mesh;
+use crate::model::Model;
use crate::opengl::buffer::{Buffer, Usage as BufferUsage};
-#[cfg(feature = "debug")]
-use crate::opengl::debug::{MessageSeverity, MessageSource, MessageType};
+use crate::opengl::debug::{
+ enable_debug_output,
+ set_debug_message_callback,
+ set_debug_message_control,
+ MessageIdsAction,
+ MessageSeverity,
+ MessageSource,
+ MessageType,
+};
+use crate::opengl::glsl::{
+ preprocess as glsl_preprocess,
+ PreprocessingError as GlslPreprocessingError,
+};
use crate::opengl::shader::{
Error as GlShaderError,
+ Kind as ShaderKind,
Program as GlShaderProgram,
Shader as GlShader,
};
use crate::opengl::texture::{
- set_active_texture_unit,
+ Filtering as GlTextureFiltering,
+ PixelDataFormat as GlTexturePixelDataFormat,
Texture as GlTexture,
- TextureUnit,
+ Wrapping as GlTextureWrapping,
};
use crate::opengl::vertex_array::{
DataType as VertexArrayDataType,
PrimitiveKind,
VertexArray,
};
-use crate::opengl::{clear_buffers, enable, BufferClearMask, Capability};
-use crate::projection::{new_perspective_matrix, Projection};
-use crate::shader::Program as ShaderProgram;
-use crate::texture::{Id as TextureId, Texture};
-use crate::transform::{Position, Scale};
-use crate::util::NeverDrop;
+use crate::opengl::{
+ clear_buffers,
+ enable,
+ get_context_flags as get_opengl_context_flags,
+ BufferClearMask,
+ Capability,
+ ContextFlags,
+};
+use crate::projection::{ClipVolume, Projection};
+use crate::renderer::opengl::vertex::{AttributeComponentType, Vertex};
+use crate::renderer::RENDER_PHASE;
+use crate::texture::{
+ Filtering as TextureFiltering,
+ Properties as TextureProperties,
+ Wrapping as TextureWrapping,
+};
+use crate::transform::{Scale, WorldPosition};
+use crate::util::{defer, Defer, RefOrValue};
use crate::vector::{Vec2, Vec3};
-use crate::vertex::{AttributeComponentType, Vertex};
use crate::window::Window;
-type RenderableEntity = (
- Mesh,
- ShaderProgram,
- Material,
- Option<MaterialFlags>,
- Option<Position>,
- Option<Scale>,
- Option<DrawFlags>,
- Option<GlObjects>,
+mod vertex;
+
+const AMBIENT_MAP_TEXTURE_UNIT: u32 = 0;
+const DIFFUSE_MAP_TEXTURE_UNIT: u32 = 1;
+const SPECULAR_MAP_TEXTURE_UNIT: u32 = 2;
+
+type RenderableEntity<'a> = (
+ &'a Model,
+ Option<&'a MaterialFlags>,
+ Option<&'a WorldPosition>,
+ Option<&'a Scale>,
+ Option<&'a DrawFlags>,
+ Option<&'a GlObjects>,
);
#[derive(Debug, Default)]
+#[non_exhaustive]
pub struct Extension {}
impl ecs::extension::Extension for Extension
{
fn collect(self, mut collector: ecs::extension::Collector<'_>)
{
- collector.add_system(StartEvent, initialize);
+ collector.add_system(*START_PHASE, initialize);
collector.add_system(
- PresentEvent,
+ *RENDER_PHASE,
render
.into_system()
.initialize((GlobalGlObjects::default(),)),
@@ -95,8 +129,9 @@ fn initialize(window: Single<Window>)
}
});
- #[cfg(feature = "debug")]
- initialize_debug();
+ if get_opengl_context_flags().contains(ContextFlags::DEBUG) {
+ initialize_debug();
+ }
let window_size = window.size().expect("Failed to get window size");
@@ -110,78 +145,63 @@ fn initialize(window: Single<Window>)
enable(Capability::MultiSample);
}
+#[tracing::instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
fn render(
- query: Query<RenderableEntity, Not<With<NoDraw>>>,
- point_light_query: Query<(PointLight,)>,
- directional_lights: Query<(DirectionalLight,)>,
- camera_query: Query<(Camera, Position, ActiveCamera)>,
+ query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>,
+ point_light_query: Query<(&PointLight, &WorldPosition)>,
+ directional_lights: Query<(&DirectionalLight,)>,
+ camera_query: Query<(&Camera, &WorldPosition, &ActiveCamera)>,
window: Single<Window>,
global_light: Single<GlobalLight>,
+ assets: Single<Assets>,
mut gl_objects: Local<GlobalGlObjects>,
mut actions: Actions,
)
{
- let Some((camera, camera_pos, _)) = camera_query.iter().next() else {
- #[cfg(feature = "debug")]
+ let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else {
tracing::warn!("No current camera. Nothing will be rendered");
return;
};
- let point_lights = point_light_query
- .iter()
- .map(|(point_light,)| point_light)
- .collect::<Vec<_>>();
-
let directional_lights = directional_lights.iter().collect::<Vec<_>>();
let GlobalGlObjects {
- shader_programs: gl_shader_programs,
+ shader_program,
textures: gl_textures,
+ default_1x1_texture: default_1x1_gl_texture,
} = &mut *gl_objects;
+ let shader_program =
+ shader_program.get_or_insert_with(|| create_default_shader_program().unwrap());
+
clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH);
- for (
- entity_index,
- (
- mesh,
- shader_program,
- material,
- material_flags,
- position,
- scale,
- draw_flags,
- gl_objects,
- ),
- ) in query.iter().enumerate()
+ 'subject_loop: for (
+ euid,
+ (model, material_flags, position, scale, draw_flags, gl_objects),
+ ) in query.iter_with_euids()
{
+ let Some(model_data) = assets.get(&model.asset_handle) else {
+ tracing::trace!("Missing model asset");
+ continue;
+ };
+
let material_flags = material_flags
.map(|material_flags| material_flags.clone())
.unwrap_or_default();
- let shader_program = gl_shader_programs
- .entry(shader_program.u64_hash())
- .or_insert_with(|| create_gl_shader_program(&shader_program).unwrap());
-
- let new_gl_objects;
-
- let gl_objects = if let Some(gl_objects) = gl_objects.as_deref() {
- gl_objects
- } else {
- // TODO: Account for when meshes are changed
- let gl_objects = GlObjects::new(&mesh);
-
- new_gl_objects = Some(gl_objects.clone());
-
- actions.add_components(
- query.get_entity_uid(entity_index).unwrap(),
- (gl_objects,),
- );
-
- &*new_gl_objects.unwrap()
+ let gl_objs = match gl_objects.as_deref() {
+ Some(gl_objs) => RefOrValue::Ref(gl_objs),
+ None => RefOrValue::Value(Some(GlObjects::new(&model_data.mesh))),
};
+ defer!(|gl_objs| {
+ if let RefOrValue::Value(opt_gl_objs) = gl_objs {
+ actions.add_components(euid, (opt_gl_objs.take().unwrap(),));
+ };
+ });
+
apply_transformation_matrices(
Transformation {
position: position.map(|pos| *pos).unwrap_or_default().position,
@@ -189,37 +209,82 @@ fn render(
},
shader_program,
&camera,
- &camera_pos,
+ &camera_world_pos,
window.size().expect("Failed to get window size"),
);
+ if model_data.materials.len() > 1 {
+ tracing::warn!(concat!(
+ "Multiple model materials are not supported ",
+ "so only the first material will be used"
+ ));
+ }
+
+ let material = match model_data.materials.values().next() {
+ Some(material) => material,
+ None => {
+ tracing::warn!("Model has no materials. Using default material");
+
+ &Material::default()
+ }
+ };
+
apply_light(
&material,
&material_flags,
&global_light,
shader_program,
- point_lights.as_slice(),
+ (point_light_query.iter(), point_light_query.iter().count()),
directional_lights
.iter()
.map(|(dir_light,)| &**dir_light)
.collect::<Vec<_>>()
.as_slice(),
- &camera_pos,
+ &camera_world_pos,
);
- for texture in &material.textures {
- let gl_texture = gl_textures
- .entry(texture.id())
- .or_insert_with(|| create_gl_texture(texture));
-
- let texture_unit =
- TextureUnit::from_texture_id(texture.id()).unwrap_or_else(|| {
- panic!("Texture id {} is a invalid texture unit", texture.id());
+ let material_texture_maps = [
+ (&material.ambient_map, AMBIENT_MAP_TEXTURE_UNIT),
+ (&material.diffuse_map, DIFFUSE_MAP_TEXTURE_UNIT),
+ (&material.specular_map, SPECULAR_MAP_TEXTURE_UNIT),
+ ];
+
+ for (texture, texture_unit) in material_texture_maps {
+ let Some(texture) = texture else {
+ let gl_texture = default_1x1_gl_texture.get_or_insert_with(|| {
+ create_gl_texture(
+ &Image::from_color(1, Color::WHITE_U8),
+ &TextureProperties::default(),
+ )
});
- set_active_texture_unit(texture_unit);
+ gl_texture.bind_to_texture_unit(texture_unit);
- gl_texture.bind();
+ continue;
+ };
+
+ let texture_image_asset_id = texture.asset_handle.id();
+
+ let gl_texture = match gl_textures.get(&texture_image_asset_id) {
+ Some(gl_texture) => gl_texture,
+ None => {
+ let Some(image) = assets.get::<Image>(&texture.asset_handle) else {
+ tracing::trace!("Missing texture asset");
+ continue 'subject_loop;
+ };
+
+ gl_textures.insert(
+ texture_image_asset_id,
+ create_gl_texture(image, &texture.properties),
+ );
+
+ gl_textures
+ .get(&texture.asset_handle.id())
+ .expect("Not possible")
+ }
+ };
+
+ gl_texture.bind_to_texture_unit(texture_unit);
}
shader_program.activate();
@@ -231,7 +296,7 @@ fn render(
);
}
- draw_mesh(gl_objects);
+ draw_mesh(gl_objs.get().unwrap());
if draw_flags.is_some() {
let default_polygon_mode_config = PolygonModeConfig::default();
@@ -247,8 +312,9 @@ fn render(
#[derive(Debug, Default, Component)]
struct GlobalGlObjects
{
- shader_programs: HashMap<u64, GlShaderProgram>,
- textures: HashMap<TextureId, GlTexture>,
+ shader_program: Option<GlShaderProgram>,
+ textures: HashMap<AssetId, GlTexture>,
+ default_1x1_texture: Option<GlTexture>,
}
fn set_viewport(position: Vec2<u32>, size: Dimens<u32>)
@@ -256,20 +322,11 @@ fn set_viewport(position: Vec2<u32>, size: Dimens<u32>)
crate::opengl::set_viewport(position, size);
}
-#[cfg(feature = "debug")]
fn initialize_debug()
{
- use crate::opengl::debug::{
- enable_debug_output,
- set_debug_message_callback,
- set_debug_message_control,
- MessageIdsAction,
- };
-
enable_debug_output();
set_debug_message_callback(opengl_debug_message_cb);
-
set_debug_message_control(None, None, None, &[], MessageIdsAction::Disable);
}
@@ -278,72 +335,119 @@ fn draw_mesh(gl_objects: &GlObjects)
gl_objects.vertex_arr.bind();
if gl_objects.index_buffer.is_some() {
- VertexArray::draw_elements(PrimitiveKind::Triangles, 0, gl_objects.index_cnt);
+ VertexArray::draw_elements(PrimitiveKind::Triangles, 0, gl_objects.element_cnt);
} else {
- VertexArray::draw_arrays(PrimitiveKind::Triangles, 0, 3);
+ VertexArray::draw_arrays(PrimitiveKind::Triangles, 0, gl_objects.element_cnt);
}
}
-fn create_gl_texture(texture: &Texture) -> GlTexture
+fn create_gl_texture(image: &Image, texture_properties: &TextureProperties) -> GlTexture
{
let mut gl_texture = GlTexture::new();
gl_texture.generate(
- *texture.dimensions(),
- texture.image().as_bytes(),
- texture.pixel_data_format(),
+ image.dimensions(),
+ image.as_bytes(),
+ match image.color_type() {
+ ImageColorType::Rgb8 => GlTexturePixelDataFormat::Rgb8,
+ ImageColorType::Rgba8 => GlTexturePixelDataFormat::Rgba8,
+ _ => {
+ unimplemented!();
+ }
+ },
);
- gl_texture.apply_properties(texture.properties());
+ gl_texture.set_wrap(texture_wrapping_to_gl(texture_properties.wrap));
+
+ gl_texture.set_magnifying_filter(texture_filtering_to_gl(
+ texture_properties.magnifying_filter,
+ ));
+
+ gl_texture.set_minifying_filter(texture_filtering_to_gl(
+ texture_properties.minifying_filter,
+ ));
gl_texture
}
-fn create_gl_shader_program(
- shader_program: &ShaderProgram,
-) -> Result<GlShaderProgram, GlShaderError>
+const VERTEX_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex.glsl");
+const FRAGMENT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/fragment.glsl");
+
+const VERTEX_DATA_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex_data.glsl");
+const LIGHT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/light.glsl");
+
+fn create_default_shader_program() -> Result<GlShaderProgram, CreateShaderError>
{
- let gl_shaders = shader_program
- .shaders()
- .iter()
- .map(|shader| {
- let gl_shader = GlShader::new(shader.kind());
+ let mut vertex_shader = GlShader::new(ShaderKind::Vertex);
- gl_shader.set_source(shader.source())?;
- gl_shader.compile()?;
+ vertex_shader.set_source(&*glsl_preprocess(
+ VERTEX_GLSL_SHADER_SRC,
+ &get_glsl_shader_content,
+ )?)?;
- Ok(gl_shader)
- })
- .collect::<Result<Vec<_>, _>>()?;
+ vertex_shader.compile()?;
- let gl_shader_program = GlShaderProgram::new();
+ let mut fragment_shader = GlShader::new(ShaderKind::Fragment);
- for gl_shader in &gl_shaders {
- gl_shader_program.attach(gl_shader);
- }
+ fragment_shader.set_source(&*glsl_preprocess(
+ FRAGMENT_GLSL_SHADER_SRC,
+ &get_glsl_shader_content,
+ )?)?;
+
+ fragment_shader.compile()?;
+
+ let mut gl_shader_program = GlShaderProgram::new();
+
+ gl_shader_program.attach(&vertex_shader);
+ gl_shader_program.attach(&fragment_shader);
gl_shader_program.link()?;
Ok(gl_shader_program)
}
+#[derive(Debug, thiserror::Error)]
+enum CreateShaderError
+{
+ #[error(transparent)]
+ ShaderError(#[from] GlShaderError),
+
+ #[error(transparent)]
+ PreprocessingError(#[from] GlslPreprocessingError),
+}
+
+fn get_glsl_shader_content(path: &Path) -> Result<Vec<u8>, std::io::Error>
+{
+ if path == Path::new("vertex_data.glsl") {
+ return Ok(VERTEX_DATA_GLSL_SHADER_SRC.as_bytes().to_vec());
+ }
+
+ if path == Path::new("light.glsl") {
+ return Ok(LIGHT_GLSL_SHADER_SRC.as_bytes().to_vec());
+ }
+
+ Err(IoError::new(
+ IoErrorKind::NotFound,
+ format!("Content for shader file {} not found", path.display()),
+ ))
+}
+
#[derive(Debug, Component)]
struct GlObjects
{
/// Vertex and index buffer has to live as long as the vertex array
- vertex_buffer: Buffer<Vertex>,
+ _vertex_buffer: Buffer<Vertex>,
index_buffer: Option<Buffer<u32>>,
- index_cnt: u32,
+ element_cnt: u32,
vertex_arr: VertexArray,
}
impl GlObjects
{
- #[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
+ #[tracing::instrument(skip_all)]
fn new(mesh: &Mesh) -> Self
{
- #[cfg(feature = "debug")]
tracing::trace!(
"Creating vertex array, vertex buffer{}",
if mesh.indices().is_some() {
@@ -356,7 +460,13 @@ impl GlObjects
let mut vertex_arr = VertexArray::new();
let mut vertex_buffer = Buffer::new();
- vertex_buffer.store(mesh.vertices(), BufferUsage::Static);
+ vertex_buffer.store_mapped(mesh.vertices(), BufferUsage::Static, |vertex| {
+ Vertex {
+ pos: vertex.pos,
+ texture_coords: vertex.texture_coords,
+ normal: vertex.normal,
+ }
+ });
vertex_arr.bind_vertex_buffer(0, &vertex_buffer, 0);
@@ -387,78 +497,77 @@ impl GlObjects
vertex_arr.bind_element_buffer(&index_buffer);
return Self {
- vertex_buffer,
+ _vertex_buffer: vertex_buffer,
index_buffer: Some(index_buffer),
- index_cnt: indices.len().try_into().unwrap(),
+ element_cnt: indices
+ .len()
+ .try_into()
+ .expect("Mesh index count does not fit into a 32-bit unsigned int"),
vertex_arr,
};
}
Self {
- vertex_buffer,
+ _vertex_buffer: vertex_buffer,
index_buffer: None,
- index_cnt: 0,
+ element_cnt: mesh
+ .vertices()
+ .len()
+ .try_into()
+ .expect("Mesh vertex count does not fit into a 32-bit unsigned int"),
vertex_arr,
}
}
-
- pub fn clone(&self) -> NeverDrop<Self>
- {
- NeverDrop::new(Self {
- // SAFETY: The vertex buffer will never become dropped (NeverDrop ensures it)
- vertex_buffer: unsafe { self.vertex_buffer.clone_weak() },
- index_buffer: self
- .index_buffer
- .as_ref()
- // SAFETY: The index buffer will never become dropped (NeverDrop ensures
- // it)
- .map(|index_buffer| unsafe { index_buffer.clone_weak() }),
- index_cnt: self.index_cnt,
- // SAFETY: The vertex array will never become dropped (NeverDrop ensures it)
- vertex_arr: unsafe { self.vertex_arr.clone_unsafe() },
- })
- }
}
fn apply_transformation_matrices(
transformation: Transformation,
gl_shader_program: &mut GlShaderProgram,
camera: &Camera,
- camera_pos: &Position,
+ camera_world_pos: &WorldPosition,
window_size: Dimens<u32>,
)
{
gl_shader_program
- .set_uniform_matrix_4fv(c"model", &create_transformation_matrix(transformation));
+ .set_uniform(c"model", &create_transformation_matrix(transformation));
- let view = create_view(camera, camera_pos);
+ let view_matrix = create_view_matrix(camera, &camera_world_pos.position);
- gl_shader_program.set_uniform_matrix_4fv(c"view", &view);
+ gl_shader_program.set_uniform(c"view", &view_matrix);
#[allow(clippy::cast_precision_loss)]
- let projection = match &camera.projection {
- Projection::Perspective(perspective) => new_perspective_matrix(
- perspective,
+ let proj_matrix = match &camera.projection {
+ Projection::Perspective(perspective_proj) => perspective_proj.to_matrix_rh(
window_size.width as f32 / window_size.height as f32,
+ ClipVolume::NegOneToOne,
),
+ Projection::Orthographic(orthographic_proj) => orthographic_proj
+ .to_matrix_rh(&camera_world_pos.position, ClipVolume::NegOneToOne),
};
- gl_shader_program.set_uniform_matrix_4fv(c"projection", &projection);
+ gl_shader_program.set_uniform(c"projection", &proj_matrix);
}
-fn apply_light<PointLightHolder>(
+fn apply_light<'point_light>(
material: &Material,
material_flags: &MaterialFlags,
global_light: &GlobalLight,
gl_shader_program: &mut GlShaderProgram,
- point_lights: &[PointLightHolder],
+ (point_light_iter, point_light_cnt): (
+ impl Iterator<
+ Item = (
+ ComponentHandle<'point_light, PointLight>,
+ ComponentHandle<'point_light, WorldPosition>,
+ ),
+ >,
+ usize,
+ ),
directional_lights: &[&DirectionalLight],
- camera_pos: &Position,
-) where
- PointLightHolder: Deref<Target = PointLight>,
+ camera_world_pos: &WorldPosition,
+)
{
debug_assert!(
- point_lights.len() < 64,
+ point_light_cnt < 64,
"Shader cannot handle more than 64 point lights"
);
@@ -468,7 +577,7 @@ fn apply_light<PointLightHolder>(
);
for (dir_light_index, dir_light) in directional_lights.iter().enumerate() {
- gl_shader_program.set_uniform_vec_3fv(
+ gl_shader_program.set_uniform(
&create_light_uniform_name(
"directional_lights",
dir_light_index,
@@ -488,71 +597,68 @@ fn apply_light<PointLightHolder>(
// There probably won't be more than 2147483648 directional lights
#[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
gl_shader_program
- .set_uniform_1i(c"directional_light_cnt", directional_lights.len() as i32);
+ .set_uniform(c"directional_light_cnt", &(directional_lights.len() as i32));
- for (point_light_index, point_light) in point_lights.iter().enumerate() {
- gl_shader_program.set_uniform_vec_3fv(
+ for (point_light_index, (point_light, point_light_world_pos)) in
+ point_light_iter.enumerate()
+ {
+ gl_shader_program.set_uniform(
&create_light_uniform_name("point_lights", point_light_index, "position"),
- &point_light.position,
+ &(point_light_world_pos.position + point_light.local_position),
);
set_light_phong_uniforms(
gl_shader_program,
"point_lights",
point_light_index,
- &**point_light,
+ &*point_light,
);
set_light_attenuation_uniforms(
gl_shader_program,
"point_lights",
point_light_index,
- point_light,
+ &*point_light,
);
}
// There probably won't be more than 2147483648 point lights
#[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl_shader_program.set_uniform_1i(c"point_light_cnt", point_lights.len() as i32);
+ gl_shader_program.set_uniform(c"point_light_cnt", &(point_light_cnt as i32));
- gl_shader_program.set_uniform_vec_3fv(
+ gl_shader_program.set_uniform(
c"material.ambient",
- &if material_flags.use_ambient_color {
+ &Vec3::from(if material_flags.use_ambient_color {
material.ambient.clone()
} else {
global_light.ambient.clone()
- }
- .into(),
+ }),
);
gl_shader_program
- .set_uniform_vec_3fv(c"material.diffuse", &material.diffuse.clone().into());
+ .set_uniform(c"material.diffuse", &Vec3::from(material.diffuse.clone()));
#[allow(clippy::cast_possible_wrap)]
gl_shader_program
- .set_uniform_vec_3fv(c"material.specular", &material.specular.clone().into());
+ .set_uniform(c"material.specular", &Vec3::from(material.specular.clone()));
#[allow(clippy::cast_possible_wrap)]
- gl_shader_program.set_uniform_1i(
- c"material.ambient_map",
- material.ambient_map.into_inner() as i32,
- );
+ gl_shader_program
+ .set_uniform(c"material.ambient_map", &(AMBIENT_MAP_TEXTURE_UNIT as i32));
#[allow(clippy::cast_possible_wrap)]
- gl_shader_program.set_uniform_1i(
- c"material.diffuse_map",
- material.diffuse_map.into_inner() as i32,
- );
+ gl_shader_program
+ .set_uniform(c"material.diffuse_map", &(DIFFUSE_MAP_TEXTURE_UNIT as i32));
#[allow(clippy::cast_possible_wrap)]
- gl_shader_program.set_uniform_1i(
+ gl_shader_program.set_uniform(
c"material.specular_map",
- material.specular_map.into_inner() as i32,
+ &(SPECULAR_MAP_TEXTURE_UNIT as i32),
);
- gl_shader_program.set_uniform_1fv(c"material.shininess", material.shininess);
+ gl_shader_program.set_uniform(c"material.shininess", &material.shininess);
- gl_shader_program.set_uniform_vec_3fv(c"view_pos", &camera_pos.position);
+ gl_shader_program.set_uniform(c"view_pos", &camera_world_pos.position);
}
fn set_light_attenuation_uniforms(
@@ -562,27 +668,27 @@ fn set_light_attenuation_uniforms(
light: &PointLight,
)
{
- gl_shader_program.set_uniform_1fv(
+ gl_shader_program.set_uniform(
&create_light_uniform_name(
light_array,
light_index,
"attenuation_props.constant",
),
- light.attenuation_params.constant,
+ &light.attenuation_params.constant,
);
- gl_shader_program.set_uniform_1fv(
+ gl_shader_program.set_uniform(
&create_light_uniform_name(light_array, light_index, "attenuation_props.linear"),
- light.attenuation_params.linear,
+ &light.attenuation_params.linear,
);
- gl_shader_program.set_uniform_1fv(
+ gl_shader_program.set_uniform(
&create_light_uniform_name(
light_array,
light_index,
"attenuation_props.quadratic",
),
- light.attenuation_params.quadratic,
+ &light.attenuation_params.quadratic,
);
}
@@ -593,14 +699,14 @@ fn set_light_phong_uniforms(
light: &impl Light,
)
{
- gl_shader_program.set_uniform_vec_3fv(
+ gl_shader_program.set_uniform(
&create_light_uniform_name(light_array, light_index, "phong.diffuse"),
- &light.diffuse().clone().into(),
+ &Vec3::from(light.diffuse().clone()),
);
- gl_shader_program.set_uniform_vec_3fv(
+ gl_shader_program.set_uniform(
&create_light_uniform_name(light_array, light_index, "phong.specular"),
- &light.specular().clone().into(),
+ &Vec3::from(light.specular().clone()),
);
}
@@ -649,16 +755,15 @@ fn create_light_uniform_name(
}
}
-fn create_view(camera: &Camera, camera_pos: &Position) -> Matrix<f32, 4, 4>
+fn create_view_matrix(camera: &Camera, camera_pos: &Vec3<f32>) -> Matrix<f32, 4, 4>
{
let mut view = Matrix::new();
- view.look_at(&camera_pos.position, &camera.target, &camera.global_up);
+ view.look_at(&camera_pos, &camera.target, &camera.global_up);
view
}
-#[cfg(feature = "debug")]
#[tracing::instrument(skip_all)]
fn opengl_debug_message_cb(
source: MessageSource,
@@ -717,3 +822,23 @@ fn create_transformation_matrix(transformation: Transformation) -> Matrix<f32, 4
matrix
}
+
+#[inline]
+fn texture_wrapping_to_gl(texture_wrapping: TextureWrapping) -> GlTextureWrapping
+{
+ match texture_wrapping {
+ TextureWrapping::Repeat => GlTextureWrapping::Repeat,
+ TextureWrapping::MirroredRepeat => GlTextureWrapping::MirroredRepeat,
+ TextureWrapping::ClampToEdge => GlTextureWrapping::ClampToEdge,
+ TextureWrapping::ClampToBorder => GlTextureWrapping::ClampToBorder,
+ }
+}
+
+#[inline]
+fn texture_filtering_to_gl(texture_filtering: TextureFiltering) -> GlTextureFiltering
+{
+ match texture_filtering {
+ TextureFiltering::Linear => GlTextureFiltering::Linear,
+ TextureFiltering::Nearest => GlTextureFiltering::Nearest,
+ }
+}
diff --git a/engine/src/renderer/opengl/glsl/fragment.glsl b/engine/src/renderer/opengl/glsl/fragment.glsl
new file mode 100644
index 0000000..5bf5ff2
--- /dev/null
+++ b/engine/src/renderer/opengl/glsl/fragment.glsl
@@ -0,0 +1,73 @@
+#version 330 core
+
+#preinclude "light.glsl"
+#preinclude "vertex_data.glsl"
+
+#define MAX_LIGHT_CNT 64
+
+out vec4 FragColor;
+
+in VertexData vertex_data;
+
+uniform vec3 view_pos;
+uniform sampler2D input_texture;
+uniform Material material;
+
+uniform PointLight point_lights[MAX_LIGHT_CNT];
+uniform int point_light_cnt;
+
+uniform DirectionalLight directional_lights[MAX_LIGHT_CNT];
+uniform int directional_light_cnt;
+
+void main()
+{
+ vec3 ambient_light = calc_ambient_light(material, vertex_data.texture_coords);
+
+ vec3 directional_light_sum = vec3(0.0, 0.0, 0.0);
+
+ for (int dl_index = 0; dl_index < directional_light_cnt; dl_index++) {
+ CalculatedLight calculated_dir_light;
+
+ calc_light(
+ // Negated since we want the light to point from the light direction
+ normalize(-directional_lights[dl_index].direction),
+ directional_lights[dl_index].phong,
+ vertex_data,
+ view_pos,
+ material,
+ calculated_dir_light
+ );
+
+ directional_light_sum +=
+ calculated_dir_light.diffuse + calculated_dir_light.specular;
+ }
+
+ vec3 point_light_sum = vec3(0.0, 0.0, 0.0);
+
+ for (int pl_index = 0; pl_index < point_light_cnt; pl_index++) {
+ vec3 light_direction =
+ normalize(point_lights[pl_index].position - vertex_data.world_space_pos);
+
+ CalculatedLight calculated_point_light;
+
+ calc_light(
+ light_direction,
+ point_lights[pl_index].phong,
+ vertex_data,
+ view_pos,
+ material,
+ calculated_point_light
+ );
+
+ float attenuation =
+ calc_attenuation(point_lights[pl_index], vertex_data.world_space_pos);
+
+ calculated_point_light.diffuse *= attenuation;
+ calculated_point_light.specular *= attenuation;
+
+ point_light_sum +=
+ calculated_point_light.diffuse + calculated_point_light.specular;
+ }
+
+ FragColor = vec4((ambient_light + directional_light_sum + point_light_sum), 1.0);
+}
diff --git a/engine/src/renderer/opengl/glsl/light.glsl b/engine/src/renderer/opengl/glsl/light.glsl
new file mode 100644
index 0000000..f12b5fe
--- /dev/null
+++ b/engine/src/renderer/opengl/glsl/light.glsl
@@ -0,0 +1,133 @@
+#version 330 core
+
+#ifndef LIGHT_GLSL
+#define LIGHT_GLSL
+
+#preinclude "vertex_data.glsl"
+
+struct Material
+{
+ vec3 ambient;
+ vec3 diffuse;
+ vec3 specular;
+ sampler2D ambient_map;
+ sampler2D diffuse_map;
+ sampler2D specular_map;
+ float shininess;
+};
+
+struct LightPhong
+{
+ vec3 diffuse;
+ vec3 specular;
+};
+
+struct AttenuationProperties
+{
+ float constant;
+ float linear;
+ float quadratic;
+};
+
+struct PointLight
+{
+ LightPhong phong;
+ vec3 position;
+ AttenuationProperties attenuation_props;
+};
+
+struct DirectionalLight
+{
+ LightPhong phong;
+ vec3 direction;
+};
+
+struct CalculatedLight
+{
+ vec3 diffuse;
+ vec3 specular;
+};
+
+vec3 calc_ambient_light(in Material material, in vec2 texture_coords)
+{
+ return vec3(texture(material.ambient_map, texture_coords)) * material.ambient;
+}
+
+vec3 calc_diffuse_light(
+ in Material material,
+ in LightPhong light_phong,
+ in vec3 light_dir,
+ in vec3 norm,
+ in vec2 texture_coords
+)
+{
+ float diff = max(dot(norm, light_dir), 0.0);
+
+ return light_phong.diffuse * (
+ diff * (vec3(texture(material.diffuse_map, texture_coords)) * material.diffuse)
+ );
+}
+
+vec3 calc_specular_light(
+ in Material material,
+ in LightPhong light_phong,
+ in vec3 light_dir,
+ in vec3 norm,
+ in vec3 view_pos,
+ in vec3 frag_pos,
+ in vec2 texture_coords
+)
+{
+ vec3 view_direction = normalize(view_pos - frag_pos);
+
+ vec3 halfway_direction = normalize(light_dir + view_direction);
+
+ float spec =
+ pow(max(dot(norm, halfway_direction), 0.0), material.shininess);
+
+ return light_phong.specular * (
+ spec * (vec3(texture(material.specular_map, texture_coords)) * material.specular)
+ );
+}
+
+float calc_attenuation(in PointLight point_light, in vec3 position)
+{
+ float light_distance = length(point_light.position - position);
+
+ return 1.0 / (point_light.attenuation_props.constant
+ + point_light.attenuation_props.linear
+ * light_distance + point_light.attenuation_props.quadratic
+ * pow(light_distance, 2));
+}
+
+void calc_light(
+ in vec3 light_direction,
+ in LightPhong light_phong,
+ in VertexData vertex_data,
+ in vec3 view_pos,
+ in Material material,
+ out CalculatedLight calculated_light
+)
+{
+ vec3 norm = normalize(vertex_data.world_space_normal);
+
+ calculated_light.diffuse = calc_diffuse_light(
+ material,
+ light_phong,
+ light_direction,
+ norm,
+ vertex_data.texture_coords
+ );
+
+ calculated_light.specular = calc_specular_light(
+ material,
+ light_phong,
+ light_direction,
+ norm,
+ view_pos,
+ vertex_data.world_space_pos,
+ vertex_data.texture_coords
+ );
+}
+
+#endif
diff --git a/engine/src/renderer/opengl/glsl/vertex.glsl b/engine/src/renderer/opengl/glsl/vertex.glsl
new file mode 100644
index 0000000..b57caa6
--- /dev/null
+++ b/engine/src/renderer/opengl/glsl/vertex.glsl
@@ -0,0 +1,24 @@
+#version 330 core
+
+#preinclude "vertex_data.glsl"
+
+layout (location = 0) in vec3 pos;
+layout (location = 1) in vec2 texture_coords;
+layout (location = 2) in vec3 normal;
+
+out VertexData vertex_data;
+
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
+
+void main()
+{
+ gl_Position = projection * view * model * vec4(pos, 1.0);
+
+ vertex_data.world_space_pos = vec3(model * vec4(pos, 1.0));
+ vertex_data.texture_coords = texture_coords;
+
+ // TODO: Do this using CPU for performance increase
+ vertex_data.world_space_normal = mat3(transpose(inverse(model))) * normal;
+}
diff --git a/engine/src/renderer/opengl/glsl/vertex_data.glsl b/engine/src/renderer/opengl/glsl/vertex_data.glsl
new file mode 100644
index 0000000..486d445
--- /dev/null
+++ b/engine/src/renderer/opengl/glsl/vertex_data.glsl
@@ -0,0 +1,11 @@
+#ifndef VERTEX_DATA_GLSL
+#define VERTEX_DATA_GLSL
+
+struct VertexData
+{
+ vec2 texture_coords;
+ vec3 world_space_pos;
+ vec3 world_space_normal;
+};
+
+#endif
diff --git a/engine/src/renderer/opengl/vertex.rs b/engine/src/renderer/opengl/vertex.rs
new file mode 100644
index 0000000..499b94b
--- /dev/null
+++ b/engine/src/renderer/opengl/vertex.rs
@@ -0,0 +1,64 @@
+use crate::vector::{Vec2, Vec3};
+
+#[derive(Debug, Clone)]
+#[repr(C)]
+pub struct Vertex
+{
+ pub pos: Vec3<f32>,
+ pub texture_coords: Vec2<f32>,
+ pub normal: Vec3<f32>,
+}
+
+impl Vertex
+{
+ pub fn attrs() -> &'static [Attribute]
+ {
+ #[allow(clippy::cast_possible_truncation)]
+ &[
+ Attribute {
+ index: 0,
+ component_type: AttributeComponentType::Float,
+ component_cnt: AttributeComponentCnt::Three,
+ component_size: size_of::<f32>() as u32,
+ },
+ Attribute {
+ index: 1,
+ component_type: AttributeComponentType::Float,
+ component_cnt: AttributeComponentCnt::Two,
+ component_size: size_of::<f32>() as u32,
+ },
+ Attribute {
+ index: 2,
+ component_type: AttributeComponentType::Float,
+ component_cnt: AttributeComponentCnt::Three,
+ component_size: size_of::<f32>() as u32,
+ },
+ ]
+ }
+}
+
+#[derive(Debug)]
+pub struct Attribute
+{
+ pub index: u32,
+ pub component_type: AttributeComponentType,
+ pub component_cnt: AttributeComponentCnt,
+ pub component_size: u32,
+}
+
+#[derive(Debug)]
+pub enum AttributeComponentType
+{
+ Float,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[repr(u32)]
+#[allow(dead_code)]
+pub enum AttributeComponentCnt
+{
+ One = 1,
+ Two = 2,
+ Three = 3,
+ Four = 4,
+}