From 4793e4411d98d97f879023dc072f3847201d49da Mon Sep 17 00:00:00 2001 From: HampusM Date: Sat, 22 Jun 2024 23:00:37 +0200 Subject: feat(engine): add support for drawing objects as a bundle --- engine/src/draw_flags.rs | 35 ++++++++ engine/src/renderer/opengl.rs | 195 +++++++++++++++++++++++++++--------------- 2 files changed, 162 insertions(+), 68 deletions(-) diff --git a/engine/src/draw_flags.rs b/engine/src/draw_flags.rs index c8c11c9..1352fe2 100644 --- a/engine/src/draw_flags.rs +++ b/engine/src/draw_flags.rs @@ -1,7 +1,11 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; + use ecs::Component; use crate::util::builder; +static CURRENT_BUNDLE_ID: AtomicUsize = AtomicUsize::new(0); + builder! { /// Flags for how a object should be drawn. #[builder(name = Builder, derives = (Debug, Default, Clone))] @@ -47,3 +51,34 @@ pub enum PolygonModeFace #[default] FrontAndBack, } + +/// Metadata for how a object should be drawn together with other objects. +#[derive(Debug, Clone, Component)] +pub struct DrawingBundled +{ + pub bundle_id: DrawingBundleId, + pub order: usize, +} + +/// The ID of a object drawing bundle. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DrawingBundleId +{ + inner: usize, +} + +impl DrawingBundleId +{ + /// Creates a new unique bundle ID. + pub fn new() -> Self + { + Self { + inner: CURRENT_BUNDLE_ID.fetch_add(1, Ordering::Relaxed), + } + } + + pub fn inner(&self) -> usize + { + self.inner + } +} diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs index 108c504..53e2a4b 100644 --- a/engine/src/renderer/opengl.rs +++ b/engine/src/renderer/opengl.rs @@ -7,14 +7,17 @@ use std::process::abort; use cstr::cstr; use ecs::component::local::Local; +use ecs::component::Sequence as ComponentSequence; +use ecs::query::options::{Not, With as WithComponent}; use ecs::sole::Single; -use ecs::system::{Into as _, System}; +use ecs::system::{ComponentRefMut, Into as _, System}; +use ecs::tuple::{Pop, With}; use ecs::{Component, Query}; use crate::camera::Camera; use crate::color::Color; use crate::data_types::dimens::Dimens; -use crate::draw_flags::{DrawFlags, PolygonModeConfig}; +use crate::draw_flags::{DrawFlags, DrawingBundled, PolygonModeConfig}; use crate::event::{Present as PresentEvent, Start as StartEvent}; use crate::lighting::{DirectionalLight, GlobalLight, PointLight}; use crate::material::{Flags as MaterialFlags, Material}; @@ -95,16 +98,19 @@ fn initialize(window: Single) enable(Capability::MultiSample); } +type EntityToRender = ( + Mesh, + ShaderProgram, + Material, + Option, + Option, + Option, + Option, +); + fn render( - query: Query<( - Mesh, - ShaderProgram, - Material, - Option, - Option, - Option, - Option, - )>, + query: Query>>, + query_bundled: Query<>::With>, point_light_query: Query<(PointLight,)>, directional_lights: Query<(DirectionalLight,)>, camera_query: Query<(Camera,)>, @@ -139,75 +145,128 @@ fn render( clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH); - for (mesh, shader_program, material, material_flags, position, scale, draw_flags) in - &query - { - 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()); - - apply_transformation_matrices( - Transformation { - position: position.map(|pos| *pos).unwrap_or_default().position, - scale: scale.map(|scale| *scale).unwrap_or_default().scale, - }, - shader_program, - &camera, - window.size().expect("Failed to get window size"), - ); + let mut bundles: Vec> = Vec::new(); - apply_light( - &material, - &material_flags, - &global_light, - shader_program, - point_lights.as_slice(), - directional_lights - .iter() - .map(|(dir_light,)| &**dir_light) - .collect::>() - .as_slice(), + bundles.resize_with( + query_bundled + .iter() + .map(|(.., bundled)| bundled.bundle_id.inner() as usize + 1) + .max() + .unwrap_or_default(), + || Vec::new(), + ); + + for entity_to_render in &query_bundled { + let (entity_to_render, bundled) = entity_to_render.pop(); + + bundles + .get_mut(bundled.bundle_id.inner()) + .unwrap() + .push((bundled.order, entity_to_render)); + } + + for bundle in &mut bundles { + bundle.sort_by(|(order_a, _), (order_b, _)| order_a.cmp(order_b)); + } + + for entity_to_render in query.iter().chain( + bundles + .into_iter() + .flatten() + .map(|(_, entity_to_render)| entity_to_render), + ) { + draw_single( + entity_to_render, &camera, + &point_lights, + gl_shader_programs, + gl_textures, + &window, + &global_light, + &directional_lights, ); + } +} - for texture in &material.textures { - let gl_texture = gl_textures - .entry(texture.id()) - .or_insert_with(|| create_gl_texture(texture)); +fn draw_single( + entity_to_render: ::Refs<'_>, + camera: &Camera, + point_lights: &[ComponentRefMut], + gl_shader_programs: &mut HashMap, + gl_textures: &mut HashMap, + window: &Window, + global_light: &GlobalLight, + directional_lights: &[(ComponentRefMut,)], +) +{ + let (mesh, shader_program, material, material_flags, position, scale, draw_flags) = + entity_to_render; + + 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()); + + apply_transformation_matrices( + Transformation { + position: position.map(|pos| *pos).unwrap_or_default().position, + scale: scale.map(|scale| *scale).unwrap_or_default().scale, + }, + shader_program, + &camera, + window.size().expect("Failed to get window size"), + ); - let texture_unit = - TextureUnit::from_texture_id(texture.id()).unwrap_or_else(|| { - panic!("Texture id {} is a invalid texture unit", texture.id()); - }); + apply_light( + &material, + &material_flags, + &global_light, + shader_program, + point_lights, + directional_lights + .iter() + .map(|(dir_light,)| &**dir_light) + .collect::>() + .as_slice(), + &camera, + ); - set_active_texture_unit(texture_unit); + for texture in &material.textures { + let gl_texture = gl_textures + .entry(texture.id()) + .or_insert_with(|| create_gl_texture(texture)); - gl_texture.bind(); - } + let texture_unit = + TextureUnit::from_texture_id(texture.id()).unwrap_or_else(|| { + panic!("Texture id {} is a invalid texture unit", texture.id()); + }); - shader_program.activate(); + set_active_texture_unit(texture_unit); - if let Some(draw_flags) = &draw_flags { - crate::opengl::set_polygon_mode( - draw_flags.polygon_mode_config.face, - draw_flags.polygon_mode_config.mode, - ) - } + gl_texture.bind(); + } - draw_mesh(&mesh); + shader_program.activate(); - if draw_flags.is_some() { - let default_polygon_mode_config = PolygonModeConfig::default(); + if let Some(draw_flags) = &draw_flags { + crate::opengl::set_polygon_mode( + draw_flags.polygon_mode_config.face, + draw_flags.polygon_mode_config.mode, + ) + } - crate::opengl::set_polygon_mode( - default_polygon_mode_config.face, - default_polygon_mode_config.mode, - ) - } + draw_mesh(&mesh); + + if draw_flags.is_some() { + let default_polygon_mode_config = PolygonModeConfig::default(); + + crate::opengl::set_polygon_mode( + default_polygon_mode_config.face, + default_polygon_mode_config.mode, + ) } } -- cgit v1.2.3-18-g5258