summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-06-22 23:00:37 +0200
committerHampusM <hampus@hampusmat.com>2024-06-22 23:00:37 +0200
commit4793e4411d98d97f879023dc072f3847201d49da (patch)
tree063683aaa6204e72788ec6217d3c57e281b61101
parent7af802e1dfcdbd4d863fec9a8f88229694926860 (diff)
feat(engine): add support for drawing objects as a bundle
-rw-r--r--engine/src/draw_flags.rs35
-rw-r--r--engine/src/renderer/opengl.rs195
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<Window>)
enable(Capability::MultiSample);
}
+type EntityToRender = (
+ Mesh,
+ ShaderProgram,
+ Material,
+ Option<MaterialFlags>,
+ Option<Position>,
+ Option<Scale>,
+ Option<DrawFlags>,
+);
+
fn render(
- query: Query<(
- Mesh,
- ShaderProgram,
- Material,
- Option<MaterialFlags>,
- Option<Position>,
- Option<Scale>,
- Option<DrawFlags>,
- )>,
+ query: Query<EntityToRender, Not<WithComponent<DrawingBundled>>>,
+ query_bundled: Query<<EntityToRender as With<DrawingBundled>>::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<(usize, _)>> = Vec::new();
- apply_light(
- &material,
- &material_flags,
- &global_light,
- shader_program,
- point_lights.as_slice(),
- directional_lights
- .iter()
- .map(|(dir_light,)| &**dir_light)
- .collect::<Vec<_>>()
- .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: <EntityToRender as ComponentSequence>::Refs<'_>,
+ camera: &Camera,
+ point_lights: &[ComponentRefMut<PointLight>],
+ gl_shader_programs: &mut HashMap<u64, GlShaderProgram>,
+ gl_textures: &mut HashMap<TextureId, GlTexture>,
+ window: &Window,
+ global_light: &GlobalLight,
+ directional_lights: &[(ComponentRefMut<DirectionalLight>,)],
+)
+{
+ 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::<Vec<_>>()
+ .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,
+ )
}
}