summaryrefslogtreecommitdiff
path: root/engine/src/renderer/opengl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/renderer/opengl.rs')
-rw-r--r--engine/src/renderer/opengl.rs302
1 files changed, 202 insertions, 100 deletions
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs
index 5665860..cfd046f 100644
--- a/engine/src/renderer/opengl.rs
+++ b/engine/src/renderer/opengl.rs
@@ -3,26 +3,29 @@
use std::collections::HashMap;
use std::ffi::{c_void, CString};
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
-use std::ops::Deref;
use std::path::Path;
use std::process::abort;
use ecs::actions::Actions;
use ecs::component::local::Local;
-use ecs::phase::{PRESENT as PRESENT_PHASE, START as START_PHASE};
+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::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};
use crate::opengl::debug::{
enable_debug_output,
@@ -44,9 +47,10 @@ use crate::opengl::shader::{
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,
@@ -62,21 +66,31 @@ use crate::opengl::{
ContextFlags,
};
use crate::projection::{ClipVolume, Projection};
-use crate::texture::{Id as TextureId, Texture};
-use crate::transform::{Position, Scale};
+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;
+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 Mesh,
- &'a Material,
- &'a Option<MaterialFlags>,
- &'a Option<Position>,
- &'a Option<Scale>,
- &'a Option<DrawFlags>,
- &'a Option<GlObjects>,
+ &'a Model,
+ Option<&'a MaterialFlags>,
+ Option<&'a WorldPosition>,
+ Option<&'a Scale>,
+ Option<&'a DrawFlags>,
+ Option<&'a GlObjects>,
);
#[derive(Debug, Default)]
@@ -90,7 +104,7 @@ impl ecs::extension::Extension for Extension
collector.add_system(*START_PHASE, initialize);
collector.add_system(
- *PRESENT_PHASE,
+ *RENDER_PHASE,
render
.into_system()
.initialize((GlobalGlObjects::default(),)),
@@ -131,33 +145,31 @@ fn initialize(window: Single<Window>)
enable(Capability::MultiSample);
}
+#[tracing::instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
fn render(
query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>,
- point_light_query: Query<(&PointLight,)>,
+ point_light_query: Query<(&PointLight, &WorldPosition)>,
directional_lights: Query<(&DirectionalLight,)>,
- camera_query: Query<(&Camera, &Position, &ActiveCamera)>,
+ 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 {
+ 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_program,
textures: gl_textures,
+ default_1x1_texture: default_1x1_gl_texture,
} = &mut *gl_objects;
let shader_program =
@@ -165,18 +177,23 @@ fn render(
clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH);
- for (
+ 'subject_loop: for (
euid,
- (mesh, material, material_flags, position, scale, draw_flags, gl_objects),
+ (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 gl_objs = match gl_objects.as_deref() {
Some(gl_objs) => RefOrValue::Ref(gl_objs),
- None => RefOrValue::Value(Some(GlObjects::new(&mesh))),
+ None => RefOrValue::Value(Some(GlObjects::new(&model_data.mesh))),
};
defer!(|gl_objs| {
@@ -192,34 +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 (index, texture) in material.textures.iter().enumerate() {
- let gl_texture = gl_textures
- .entry(texture.id())
- .or_insert_with(|| create_gl_texture(texture));
+ 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(),
+ )
+ });
+
+ gl_texture.bind_to_texture_unit(texture_unit);
- let texture_unit = TextureUnit::from_num(index).expect("Too many textures");
+ continue;
+ };
- set_active_texture_unit(texture_unit);
+ 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();
+ gl_texture.bind_to_texture_unit(texture_unit);
}
shader_program.activate();
@@ -248,7 +313,8 @@ fn render(
struct GlobalGlObjects
{
shader_program: Option<GlShaderProgram>,
- textures: HashMap<TextureId, GlTexture>,
+ textures: HashMap<AssetId, GlTexture>,
+ default_1x1_texture: Option<GlTexture>,
}
fn set_viewport(position: Vec2<u32>, size: Dimens<u32>)
@@ -275,17 +341,31 @@ fn draw_mesh(gl_objects: &GlObjects)
}
}
-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
}
@@ -380,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);
@@ -438,16 +524,16 @@ 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_matrix = create_view_matrix(camera, &camera_pos.position);
+ let view_matrix = create_view_matrix(camera, &camera_world_pos.position);
- gl_shader_program.set_uniform_matrix_4fv(c"view", &view_matrix);
+ gl_shader_program.set_uniform(c"view", &view_matrix);
#[allow(clippy::cast_precision_loss)]
let proj_matrix = match &camera.projection {
@@ -455,27 +541,33 @@ fn apply_transformation_matrices(
window_size.width as f32 / window_size.height as f32,
ClipVolume::NegOneToOne,
),
- Projection::Orthographic(orthographic_proj) => {
- orthographic_proj.to_matrix_rh(&camera_pos.position, 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", &proj_matrix);
+ 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"
);
@@ -485,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,
@@ -505,78 +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());
-
- let texture_map = material
- .textures
- .iter()
- .enumerate()
- .map(|(index, texture)| (texture.id(), index))
- .collect::<HashMap<_, _>>();
+ .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",
- *texture_map.get(&material.ambient_map).unwrap() 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",
- *texture_map.get(&material.diffuse_map).unwrap() 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",
- *texture_map.get(&material.specular_map).unwrap() 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(
@@ -586,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,
);
}
@@ -617,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()),
);
}
@@ -740,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,
+ }
+}