summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engine')
-rw-r--r--engine/src/image.rs97
-rw-r--r--engine/src/renderer.rs317
-rw-r--r--engine/src/renderer/main_render_pass.rs280
-rw-r--r--engine/src/renderer/object.rs5
-rw-r--r--engine/src/renderer/opengl.rs297
5 files changed, 528 insertions, 468 deletions
diff --git a/engine/src/image.rs b/engine/src/image.rs
index ac95f22..b6d8013 100644
--- a/engine/src/image.rs
+++ b/engine/src/image.rs
@@ -1,8 +1,10 @@
+use std::any::type_name;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use image_rs::GenericImageView as _;
+use zerocopy::{FromBytes, Immutable};
use crate::color::Color;
use crate::data_types::dimens::Dimens;
@@ -33,6 +35,32 @@ impl Image
})
}
+ pub fn try_from_bytes(
+ bytes: &[u8],
+ dimens: Dimens<u32>,
+ color_type: ColorType,
+ ) -> Result<Self, FromBytesError>
+ {
+ use image_rs::{Luma, LumaA, Rgb, Rgba};
+
+ let inner = match color_type {
+ ColorType::L8 => image_buf_from_bytes::<Luma<u8>>(dimens, bytes)?.into(),
+ ColorType::La8 => image_buf_from_bytes::<LumaA<u8>>(dimens, bytes)?.into(),
+ ColorType::Rgb8 => image_buf_from_bytes::<Rgb<u8>>(dimens, bytes)?.into(),
+ ColorType::Rgba8 => image_buf_from_bytes::<Rgba<u8>>(dimens, bytes)?.into(),
+ ColorType::L16 => image_buf_from_bytes::<Luma<u16>>(dimens, bytes)?.into(),
+ ColorType::La16 => image_buf_from_bytes::<LumaA<u16>>(dimens, bytes)?.into(),
+ ColorType::Rgb16 => image_buf_from_bytes::<Rgb<u16>>(dimens, bytes)?.into(),
+ ColorType::Rgba16 => image_buf_from_bytes::<Rgba<u16>>(dimens, bytes)?.into(),
+ ColorType::Rgb32F => image_buf_from_bytes::<Rgb<f32>>(dimens, bytes)?.into(),
+ ColorType::Rgba32F => {
+ image_buf_from_bytes::<Rgba<f32>>(dimens, bytes)?.into()
+ }
+ };
+
+ Ok(Self { inner })
+ }
+
pub fn from_color(dimens: impl Into<Dimens<u32>>, color: impl Into<Color<u8>>)
-> Self
{
@@ -173,3 +201,72 @@ pub enum Error
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct DecodeError(image_rs::ImageError);
+
+#[derive(Debug, thiserror::Error)]
+pub enum FromBytesError
+{
+ #[error(
+ "The number of bytes provided is not enough to fit a image with the given size"
+ )]
+ NotEnoughBytesForDimensions,
+
+ #[error("Failed to cast bytes to subpixels")]
+ CastToSubPixelsFailed(#[from] CastToSubPixelsError),
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum CastToSubPixelsError
+{
+ #[error(
+ "Source address {:?} ({}) isn't a multiple of the alignment of the type {} ({})",
+ src,
+ src_type_name,
+ dst_type_name,
+ dst_align
+ )]
+ Alignment
+ {
+ src_type_name: &'static str,
+ dst_type_name: &'static str,
+ src: *const u8,
+ dst_align: usize,
+ },
+
+ #[error(
+ "Source size {} ({}) is incorrect size for type {}",
+ src_size,
+ src_type_name,
+ dst_type_name
+ )]
+ Size
+ {
+ src_type_name: &'static str,
+ dst_type_name: &'static str,
+ src_size: usize,
+ },
+}
+
+fn image_buf_from_bytes<Pixel>(
+ dimens: Dimens<u32>,
+ bytes: &[u8],
+) -> Result<image_rs::ImageBuffer<Pixel, Vec<Pixel::Subpixel>>, FromBytesError>
+where
+ Pixel: image_rs::Pixel<Subpixel: FromBytes + Immutable>,
+{
+ let buf = <[Pixel::Subpixel]>::ref_from_bytes(bytes).map_err(|err| match err {
+ zerocopy::CastError::Alignment(_) => CastToSubPixelsError::Alignment {
+ src_type_name: type_name::<&[u8]>(),
+ dst_type_name: type_name::<&[Pixel]>(),
+ src: bytes.as_ptr(),
+ dst_align: align_of_val::<[Pixel]>(&[]),
+ },
+ zerocopy::ConvertError::Size(_) => CastToSubPixelsError::Size {
+ src_type_name: type_name::<&[u8]>(),
+ dst_type_name: type_name::<&[Pixel]>(),
+ src_size: bytes.len(),
+ },
+ })?;
+
+ image_rs::ImageBuffer::from_raw(dimens.width, dimens.height, buf.to_vec())
+ .ok_or(FromBytesError::NotEnoughBytesForDimensions)
+}
diff --git a/engine/src/renderer.rs b/engine/src/renderer.rs
index 68e8aed..bdbbe0c 100644
--- a/engine/src/renderer.rs
+++ b/engine/src/renderer.rs
@@ -3,40 +3,34 @@ use std::collections::VecDeque;
use std::sync::atomic::{AtomicU64, Ordering};
use bitflags::bitflags;
-use ecs::actions::Actions;
use ecs::entity::obtainer::Obtainer as EntityObtainer;
+use ecs::event::component::Changed;
use ecs::pair::{ChildOf, Pair, Wildcard};
use ecs::phase::{POST_UPDATE as POST_UPDATE_PHASE, Phase};
-use ecs::query::term::{With, Without};
use ecs::sole::Single;
+use ecs::system::observer::Observe;
use ecs::uid::Uid;
use ecs::{Component, Query, Sole, declare_entity};
-use crate::asset::{Assets, Handle as AssetHandle};
+use crate::asset::Handle as AssetHandle;
use crate::builder;
use crate::data_types::dimens::Dimens;
-use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig};
+use crate::draw_flags::PolygonModeConfig;
use crate::mesh::Mesh;
-use crate::model::{MaterialSearchResult, Model};
use crate::renderer::blending::Config as BlendingConfig;
-use crate::renderer::object::{Id as ObjectId, Store as ObjectStore};
+use crate::renderer::object::Id as ObjectId;
+use crate::shader::Program as ShaderProgram;
use crate::shader::cursor::{
BindingLocation as ShaderBindingLocation,
BindingValue as ShaderBindingValue,
Cursor as ShaderCursor,
};
-use crate::shader::default::ASSET_LABEL as DEFAULT_SHADER_ASSET_LABEL;
-use crate::shader::{
- Context as ShaderContext,
- ModuleSource as ShaderModuleSource,
- Program as ShaderProgram,
- Shader,
-};
-use crate::texture::{Texture, WHITE_1X1_ASSET_LABEL as TEXTURE_WHITE_1X1_ASSET_LABEL};
+use crate::texture::Texture;
use crate::vector::Vec2;
use crate::windowing::window::Window;
pub mod blending;
+pub mod main_render_pass;
pub mod object;
pub mod opengl;
@@ -64,6 +58,11 @@ declare_entity!(
)
);
+declare_entity!(
+ pub POST_RENDER_PHASE,
+ (Phase, Pair::builder().relation::<ChildOf>().target_id(*RENDER_PHASE).build())
+);
+
builder! {
/// Window graphics properties.
#[builder(name=GraphicsPropertiesBuilder, derives=(Debug, Clone))]
@@ -191,8 +190,14 @@ pub enum Command
mesh: Mesh,
usage: MeshUsage,
},
+ RemoveMesh(ObjectId),
DrawMesh(ObjectId, DrawMeshOptions),
UpdateDrawProperties(DrawProperties, DrawPropertiesUpdateFlags),
+ SetViewport
+ {
+ size: Dimens<u32>,
+ position: Vec2<u32>,
+ },
}
builder! {
@@ -354,237 +359,6 @@ pub struct WindowUsingRendererCtx;
#[derive(Debug, Component)]
pub struct CtxUsedByWindow;
-type RenderableEntity<'a> = (
- &'a Model,
- Option<&'a DrawFlags>,
- Option<&'a Shader>,
- Option<&'a mut PendingShaderBindings>,
-);
-
-#[tracing::instrument(skip_all)]
-pub fn add_main_render_passes(
- renderer_ctx_query: Query<
- (&ObjectStore, &[Pair<CtxUsedByWindow, Wildcard>]),
- (With<CommandQueue>,),
- >,
- renderable_query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>,
- assets: Single<Assets>,
- shader_context: Single<ShaderContext>,
- mut render_passes: Single<RenderPasses>,
- mut actions: Actions,
-)
-{
- let Some(default_shader_asset) = assets
- .get_handle_to_loaded::<ShaderModuleSource>(DEFAULT_SHADER_ASSET_LABEL.clone())
- else {
- tracing::error!("Default shader asset is not loaded");
- return;
- };
-
- for (renderer_ctx_ent_id, (object_store, used_by_windows)) in
- renderer_ctx_query.iter_with_euids()
- {
- for ctx_used_by_window in used_by_windows {
- let window_ent_id = ctx_used_by_window.id().target_entity();
-
- let Some(window_ent) = ctx_used_by_window.get_target_ent() else {
- tracing::error!("Window entity does not exist");
- continue;
- };
-
- if window_ent.get::<Window>().is_none() {
- tracing::debug!(
- window_entity_id=%window_ent_id,
- "Window entity does not have a {} component",
- type_name::<Window>()
- );
-
- actions.remove_components(
- renderer_ctx_ent_id,
- [Pair::builder()
- .relation::<CtxUsedByWindow>()
- .target_id(window_ent_id)
- .build()
- .id()],
- );
-
- continue;
- };
-
- let Some(surface_spec) = window_ent.get::<SurfaceSpec>() else {
- tracing::debug!(
- window_entity_id=%window_ent_id,
- "Window entity does not have a {} component",
- type_name::<SurfaceSpec>()
- );
- continue;
- };
-
- render_passes.passes.push_front(RenderPass {
- renderer_ctx_ent_id,
- surface_id: surface_spec.id,
- commands: Vec::with_capacity(30),
- draw_properties: DrawProperties::default(),
- });
-
- let render_pass = render_passes.passes.front_mut().expect("Not possible");
-
- render_pass
- .commands
- .push(Command::MakeCurrent(surface_spec.id));
-
- let default_texture_asset = assets
- .get_handle_to_loaded::<Texture>(TEXTURE_WHITE_1X1_ASSET_LABEL.clone())
- .expect("Not possible");
-
- if !object_store
- .contains_with_id(&ObjectId::Asset(default_texture_asset.id()))
- {
- render_pass
- .commands
- .push(Command::CreateTexture(default_texture_asset));
- }
-
- render_pass.commands.push(Command::ClearBuffers(
- BufferClearMask::COLOR | BufferClearMask::DEPTH,
- ));
-
- for (model, draw_flags, shader, mut pending_shader_bindings) in
- &renderable_query
- {
- let shader_asset = match &shader {
- Some(shader) => &shader.asset_handle,
- None => &default_shader_asset,
- };
-
- if pending_shader_bindings.as_ref().map_or_else(
- || true,
- |pending_shader_bindings| pending_shader_bindings.bindings.is_empty(),
- ) {
- continue;
- }
-
- let Some(model_spec) = assets.get(&model.spec_asset) else {
- continue;
- };
-
- let Some(mesh_asset) = &model_spec.mesh_asset else {
- continue;
- };
-
- if assets.get(mesh_asset).is_none() {
- continue;
- }
-
- debug_assert!(model_spec.material_names.len() <= 1);
-
- let model_material_asset = match model_spec.find_first_material(&assets) {
- MaterialSearchResult::Found(model_material_asset) => {
- model_material_asset.clone()
- // Some(model_material_asset.clone())
- }
- MaterialSearchResult::NotFound
- | MaterialSearchResult::NoMaterials => {
- // MaterialSearchResult::NotFound => {
- continue;
- } // MaterialSearchResult::NoMaterials => None,
- };
-
- if !object_store.contains_with_id(&ObjectId::Asset(shader_asset.id())) {
- let Some(shader_program) =
- shader_context.get_program(&shader_asset.id())
- else {
- tracing::error!(
- "Shader context doesn't have a program for shader asset {:?}",
- assets.get_label(&shader_asset)
- );
- continue;
- };
-
- render_pass.commands.push(Command::CreateShaderProgram(
- ObjectId::Asset(shader_asset.id()),
- shader_program.clone(),
- ));
- }
-
- render_pass
- .commands
- .push(Command::ActivateShader(ObjectId::Asset(shader_asset.id())));
-
- let Some(model_material) = assets.get(&model_material_asset) else {
- // TODO: Handle this case since it may occur
- unreachable!();
- };
-
- for texture_asset in [
- &model_material.ambient_map,
- &model_material.diffuse_map,
- &model_material.specular_map,
- ]
- .into_iter()
- .flatten()
- {
- if !object_store
- .contains_with_id(&ObjectId::Asset(texture_asset.id()))
- {
- render_pass
- .commands
- .push(Command::CreateTexture(texture_asset.clone()));
- }
- }
-
- if let Some(pending_shader_bindings) = &mut pending_shader_bindings {
- for (shader_binding_loc, shader_binding_val) in
- pending_shader_bindings.bindings.drain(..)
- {
- render_pass.commands.push(Command::SetShaderBinding(
- shader_binding_loc,
- shader_binding_val,
- ));
- }
- }
-
- if let Some(draw_flags) = draw_flags.as_deref()
- && draw_flags.polygon_mode_config != PolygonModeConfig::default()
- {
- render_pass.commands.push(Command::UpdateDrawProperties(
- DrawProperties {
- polygon_mode_config: draw_flags.polygon_mode_config.clone(),
- ..Default::default()
- },
- DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG,
- ));
- }
-
- if !object_store.contains_with_id(&ObjectId::Asset(mesh_asset.id())) {
- render_pass.commands.push(Command::CreateMesh {
- obj_id: ObjectId::Asset(mesh_asset.id()),
- mesh: None,
- usage: MeshUsage::Static,
- });
- }
-
- render_pass.commands.push(Command::DrawMesh(
- ObjectId::Asset(mesh_asset.id()),
- DrawMeshOptions::default(),
- ));
-
- if let Some(draw_flags) = draw_flags.as_deref()
- && draw_flags.polygon_mode_config != PolygonModeConfig::default()
- {
- render_pass.commands.push(Command::UpdateDrawProperties(
- DrawProperties {
- polygon_mode_config: PolygonModeConfig::default(),
- ..Default::default()
- },
- DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG,
- ));
- }
- }
- }
- }
-}
-
#[tracing::instrument(skip_all)]
pub fn enqueue_commands_from_render_passes(
renderer_ctx_query: Query<(
@@ -678,6 +452,59 @@ pub fn enqueue_commands_from_render_passes(
}
}
+#[tracing::instrument(skip_all)]
+fn handle_window_changed(
+ observe: Observe<Pair<Changed, Window>>,
+ entity_obtainer: EntityObtainer,
+)
+{
+ for evt_match in &observe {
+ let window_ent = evt_match.get_ent_infallible();
+
+ let Some(window_surface_spec) = window_ent.get::<SurfaceSpec>() else {
+ continue;
+ };
+
+ let Some(renderer_ctx_ent_id) = window_ent
+ .get_matching_components(
+ Pair::builder()
+ .relation::<WindowUsingRendererCtx>()
+ .target_id(Wildcard::uid())
+ .build()
+ .id(),
+ )
+ .next()
+ .map(|comp_ref| comp_ref.id().target_entity())
+ else {
+ continue;
+ };
+
+ let Some(renderer_ctx_ent) = entity_obtainer.get_entity(renderer_ctx_ent_id)
+ else {
+ tracing::error!("Renderer context entity does not exist");
+ continue;
+ };
+
+ let Some(mut command_queue) = renderer_ctx_ent.get_mut::<CommandQueue>() else {
+ tracing::error!(
+ "Renderer context entity does not have a {} component",
+ type_name::<CommandQueue>()
+ );
+ continue;
+ };
+
+ command_queue.queue.push_front(Command::SetViewport {
+ position: Vec2 { x: 0, y: 0 },
+ size: *evt_match.get_changed_comp().inner_size(),
+ });
+
+ command_queue
+ .queue
+ .push_front(Command::MakeCurrent(window_surface_spec.id));
+ }
+}
+
+// TODO: Maybe move this struct to somewhere more appropriate
#[derive(Default, Clone, Component)]
pub struct PendingShaderBindings
{
diff --git a/engine/src/renderer/main_render_pass.rs b/engine/src/renderer/main_render_pass.rs
new file mode 100644
index 0000000..ec490e4
--- /dev/null
+++ b/engine/src/renderer/main_render_pass.rs
@@ -0,0 +1,280 @@
+use std::any::type_name;
+
+use ecs::Query;
+use ecs::actions::Actions;
+use ecs::pair::{Pair, Wildcard};
+use ecs::query::term::{With, Without};
+use ecs::sole::Single;
+
+use crate::asset::Assets;
+use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig};
+use crate::model::{MaterialSearchResult, Model};
+use crate::renderer::object::{Id as RendererObjectId, Store as RendererObjectStore};
+use crate::renderer::{
+ BufferClearMask as RendererBufferClearMask,
+ Command as RendererCommand,
+ CommandQueue as RendererCommandQueue,
+ CtxUsedByWindow as RendererCtxUsedByWindow,
+ DrawMeshOptions as RendererDrawMeshOptions,
+ DrawProperties as RendererDrawProperties,
+ DrawPropertiesUpdateFlags as RendererDrawPropertiesUpdateFlags,
+ MeshUsage as RendererMeshUsage,
+ PendingShaderBindings,
+ RenderPass,
+ RenderPasses as RendererRenderPasses,
+ SurfaceSpec as RendererSurfaceSpec,
+};
+use crate::shader::default::ASSET_LABEL as DEFAULT_SHADER_ASSET_LABEL;
+use crate::shader::{
+ Context as ShaderContext,
+ ModuleSource as ShaderModuleSource,
+ Shader,
+};
+use crate::texture::{Texture, WHITE_1X1_ASSET_LABEL as TEXTURE_WHITE_1X1_ASSET_LABEL};
+use crate::windowing::window::Window;
+
+type RenderableEntity<'a> = (
+ &'a Model,
+ Option<&'a DrawFlags>,
+ Option<&'a Shader>,
+ Option<&'a mut PendingShaderBindings>,
+);
+
+#[tracing::instrument(skip_all)]
+pub fn add_main_render_passes(
+ renderer_ctx_query: Query<
+ (
+ &RendererObjectStore,
+ &[Pair<RendererCtxUsedByWindow, Wildcard>],
+ ),
+ (With<RendererCommandQueue>,),
+ >,
+ renderable_query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>,
+ assets: Single<Assets>,
+ shader_context: Single<ShaderContext>,
+ mut render_passes: Single<RendererRenderPasses>,
+ mut actions: Actions,
+)
+{
+ let Some(default_shader_asset) = assets
+ .get_handle_to_loaded::<ShaderModuleSource>(DEFAULT_SHADER_ASSET_LABEL.clone())
+ else {
+ tracing::error!("Default shader asset is not loaded");
+ return;
+ };
+
+ for (renderer_ctx_ent_id, (object_store, used_by_windows)) in
+ renderer_ctx_query.iter_with_euids()
+ {
+ for ctx_used_by_window in used_by_windows {
+ let window_ent_id = ctx_used_by_window.id().target_entity();
+
+ let Some(window_ent) = ctx_used_by_window.get_target_ent() else {
+ tracing::error!("Window entity does not exist");
+ continue;
+ };
+
+ if window_ent.get::<Window>().is_none() {
+ tracing::debug!(
+ window_entity_id=%window_ent_id,
+ "Window entity does not have a {} component",
+ type_name::<Window>()
+ );
+
+ actions.remove_components(
+ renderer_ctx_ent_id,
+ [Pair::builder()
+ .relation::<RendererCtxUsedByWindow>()
+ .target_id(window_ent_id)
+ .build()
+ .id()],
+ );
+
+ continue;
+ };
+
+ let Some(surface_spec) = window_ent.get::<RendererSurfaceSpec>() else {
+ tracing::debug!(
+ window_entity_id=%window_ent_id,
+ "Window entity does not have a {} component",
+ type_name::<RendererSurfaceSpec>()
+ );
+ continue;
+ };
+
+ render_passes.passes.push_front(RenderPass {
+ renderer_ctx_ent_id,
+ surface_id: surface_spec.id,
+ commands: Vec::with_capacity(30),
+ draw_properties: RendererDrawProperties::default(),
+ });
+
+ let render_pass = render_passes.passes.front_mut().expect("Not possible");
+
+ render_pass
+ .commands
+ .push(RendererCommand::MakeCurrent(surface_spec.id));
+
+ let default_texture_asset = assets
+ .get_handle_to_loaded::<Texture>(TEXTURE_WHITE_1X1_ASSET_LABEL.clone())
+ .expect("Not possible");
+
+ if !object_store
+ .contains_with_id(&RendererObjectId::Asset(default_texture_asset.id()))
+ {
+ render_pass
+ .commands
+ .push(RendererCommand::CreateTexture(default_texture_asset));
+ }
+
+ render_pass.commands.push(RendererCommand::ClearBuffers(
+ RendererBufferClearMask::COLOR | RendererBufferClearMask::DEPTH,
+ ));
+
+ for (model, draw_flags, shader, mut pending_shader_bindings) in
+ &renderable_query
+ {
+ let shader_asset = match &shader {
+ Some(shader) => &shader.asset_handle,
+ None => &default_shader_asset,
+ };
+
+ if pending_shader_bindings.as_ref().map_or_else(
+ || true,
+ |pending_shader_bindings| pending_shader_bindings.bindings.is_empty(),
+ ) {
+ continue;
+ }
+
+ let Some(model_spec) = assets.get(&model.spec_asset) else {
+ continue;
+ };
+
+ let Some(mesh_asset) = &model_spec.mesh_asset else {
+ continue;
+ };
+
+ if assets.get(mesh_asset).is_none() {
+ continue;
+ }
+
+ debug_assert!(model_spec.material_names.len() <= 1);
+
+ let model_material_asset = match model_spec.find_first_material(&assets) {
+ MaterialSearchResult::Found(model_material_asset) => {
+ model_material_asset.clone()
+ // Some(model_material_asset.clone())
+ }
+ MaterialSearchResult::NotFound
+ | MaterialSearchResult::NoMaterials => {
+ // MaterialSearchResult::NotFound => {
+ continue;
+ } // MaterialSearchResult::NoMaterials => None,
+ };
+
+ if !object_store
+ .contains_with_id(&RendererObjectId::Asset(shader_asset.id()))
+ {
+ let Some(shader_program) =
+ shader_context.get_program(&shader_asset.id())
+ else {
+ tracing::error!(
+ "Shader context doesn't have a program for shader asset {:?}",
+ assets.get_label(&shader_asset)
+ );
+ continue;
+ };
+
+ render_pass
+ .commands
+ .push(RendererCommand::CreateShaderProgram(
+ RendererObjectId::Asset(shader_asset.id()),
+ shader_program.clone(),
+ ));
+ }
+
+ render_pass.commands.push(RendererCommand::ActivateShader(
+ RendererObjectId::Asset(shader_asset.id()),
+ ));
+
+ let Some(model_material) = assets.get(&model_material_asset) else {
+ // TODO: Handle this case since it may occur
+ unreachable!();
+ };
+
+ for texture_asset in [
+ &model_material.ambient_map,
+ &model_material.diffuse_map,
+ &model_material.specular_map,
+ ]
+ .into_iter()
+ .flatten()
+ {
+ if !object_store
+ .contains_with_id(&RendererObjectId::Asset(texture_asset.id()))
+ {
+ render_pass
+ .commands
+ .push(RendererCommand::CreateTexture(texture_asset.clone()));
+ }
+ }
+
+ if let Some(pending_shader_bindings) = &mut pending_shader_bindings {
+ for (shader_binding_loc, shader_binding_val) in
+ pending_shader_bindings.bindings.drain(..)
+ {
+ render_pass.commands.push(RendererCommand::SetShaderBinding(
+ shader_binding_loc,
+ shader_binding_val,
+ ));
+ }
+ }
+
+ if let Some(draw_flags) = draw_flags.as_deref()
+ && draw_flags.polygon_mode_config != PolygonModeConfig::default()
+ {
+ render_pass
+ .commands
+ .push(RendererCommand::UpdateDrawProperties(
+ RendererDrawProperties {
+ polygon_mode_config: draw_flags
+ .polygon_mode_config
+ .clone(),
+ ..Default::default()
+ },
+ RendererDrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG,
+ ));
+ }
+
+ if !object_store
+ .contains_with_id(&RendererObjectId::Asset(mesh_asset.id()))
+ {
+ render_pass.commands.push(RendererCommand::CreateMesh {
+ obj_id: RendererObjectId::Asset(mesh_asset.id()),
+ mesh: None,
+ usage: RendererMeshUsage::Static,
+ });
+ }
+
+ render_pass.commands.push(RendererCommand::DrawMesh(
+ RendererObjectId::Asset(mesh_asset.id()),
+ RendererDrawMeshOptions::default(),
+ ));
+
+ if let Some(draw_flags) = draw_flags.as_deref()
+ && draw_flags.polygon_mode_config != PolygonModeConfig::default()
+ {
+ render_pass
+ .commands
+ .push(RendererCommand::UpdateDrawProperties(
+ RendererDrawProperties {
+ polygon_mode_config: PolygonModeConfig::default(),
+ ..Default::default()
+ },
+ RendererDrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG,
+ ));
+ }
+ }
+ }
+ }
+}
diff --git a/engine/src/renderer/object.rs b/engine/src/renderer/object.rs
index 8e2254f..13be4a4 100644
--- a/engine/src/renderer/object.rs
+++ b/engine/src/renderer/object.rs
@@ -77,6 +77,11 @@ impl Store
self.objects.insert(id, object);
}
+ pub fn remove(&mut self, id: &Id) -> Option<Object>
+ {
+ self.objects.remove(id)
+ }
+
pub fn entry(&mut self, id: Id) -> StoreEntry<'_>
{
StoreEntry { inner: self.objects.entry(id) }
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs
index a713d41..1cd46b2 100644
--- a/engine/src/renderer/opengl.rs
+++ b/engine/src/renderer/opengl.rs
@@ -5,14 +5,12 @@ use std::borrow::Cow;
use std::collections::HashMap;
use ecs::actions::Actions;
-use ecs::entity::obtainer::Obtainer as EntityObtainer;
-use ecs::event::component::{Changed, Removed};
-use ecs::pair::{ChildOf, Pair, Wildcard};
-use ecs::phase::Phase;
+use ecs::event::component::Removed;
+use ecs::pair::{Pair, Wildcard};
use ecs::query::term::Without;
use ecs::sole::Single;
use ecs::system::observer::Observe;
-use ecs::{Component, Query, declare_entity};
+use ecs::{Component, Query};
use glutin::display::GetGlDisplay;
use glutin::prelude::GlDisplay;
use glutin::surface::{
@@ -38,12 +36,12 @@ use opengl_bindings::debug::{
use opengl_bindings::misc::{
BufferClearMask as GlBufferClearMask,
Capability,
- SetViewportError as GlSetViewportError,
clear_buffers,
define_scissor_box as gl_define_scissor_box,
enable,
get_viewport as gl_get_viewport,
set_enabled,
+ set_viewport as gl_set_viewport,
};
use opengl_bindings::shader::{
Error as GlShaderError,
@@ -73,7 +71,6 @@ use crate::asset::{Assets, Handle as AssetHandle};
use crate::data_types::dimens::Dimens;
use crate::image::{ColorType as ImageColorType, Image};
use crate::matrix::Matrix;
-use crate::model::Model;
use crate::renderer::blending::{Equation as BlendingEquation, Factor as BlendingFactor};
use crate::renderer::object::{
Id as RendererObjectId,
@@ -96,6 +93,7 @@ use crate::renderer::{
DrawMeshOptions,
DrawPropertiesUpdateFlags,
GraphicsProperties,
+ POST_RENDER_PHASE,
PRE_RENDER_PHASE,
RENDER_PHASE,
RenderPasses,
@@ -128,11 +126,6 @@ use crate::windowing::window::{
mod glutin_compat;
mod graphics_mesh;
-declare_entity!(
- pub POST_RENDER_PHASE,
- (Phase, Pair::builder().relation::<ChildOf>().target_id(*RENDER_PHASE).build())
-);
-
#[derive(Debug, Component)]
struct WindowGlConfig
{
@@ -174,7 +167,10 @@ impl ecs::extension::Extension for Extension
let _ = collector.add_sole(RenderPasses::default());
- collector.add_system(*PRE_RENDER_PHASE, super::add_main_render_passes);
+ collector.add_system(
+ *PRE_RENDER_PHASE,
+ crate::renderer::main_render_pass::add_main_render_passes,
+ );
collector.add_system(*RENDER_PHASE, super::enqueue_commands_from_render_passes);
collector.add_system(*RENDER_PHASE, handle_commands);
@@ -182,201 +178,12 @@ impl ecs::extension::Extension for Extension
collector.add_system(*POST_RENDER_PHASE, prepare_windows);
collector.add_system(*POST_RENDER_PHASE, init_window_graphics);
- collector.add_observer(handle_model_removed);
-
- collector.add_observer(handle_window_changed);
+ collector.add_observer(super::handle_window_changed);
collector.add_observer(handle_window_removed);
}
}
#[tracing::instrument(skip_all)]
-fn handle_model_removed(
- observe: Observe<Pair<Removed, Model>>,
- renderer_ctx_query: Query<(
- &mut GraphicsContext,
- &RendererObjectStore,
- Pair<RendererCtxUsedByWindow, Wildcard>,
- )>,
- assets: Single<Assets>,
-)
-{
- for evt_match in &observe {
- let model_ent_id = evt_match.id();
-
- for (
- renderer_ctx_ent_id,
- (mut graphics_ctx, renderer_object_store, renderer_ctx_used_by_window),
- ) in renderer_ctx_query.iter_with_euids()
- {
- let GraphicsContext {
- ref gl_context,
- ref surfaces,
- objects: ref mut graphics_ctx_objects,
- ..
- } = *graphics_ctx;
-
- let model = evt_match.get_removed_comp();
-
- let Some(model_spec) = assets.get(&model.spec_asset) else {
- continue;
- };
-
- let Some(mesh_asset) = &model_spec.mesh_asset else {
- continue;
- };
-
- let Some(graphics_ctx_object_id) = renderer_object_store
- .get_obj(&RendererObjectId::Asset(mesh_asset.id()))
- .map(|obj| obj.as_raw())
- else {
- continue;
- };
-
- if !graphics_ctx_objects.contains_key(&graphics_ctx_object_id) {
- continue;
- }
-
- let Some(window_ent) = renderer_ctx_used_by_window.get_target_ent() else {
- tracing::error!(
- window_entity_id = %renderer_ctx_used_by_window.id().target_entity(),
- "Window entity does not exist"
- );
- continue;
- };
-
- let Some(surface_spec) = window_ent.get::<SurfaceSpec>() else {
- tracing::error!(
- window_entity_id = %window_ent.uid(),
- "Window entity does not have a {} component",
- type_name::<SurfaceSpec>()
- );
- continue;
- };
-
- let Some(surface) = surfaces.get(&surface_spec.id) else {
- tracing::error!(
- window_entity_id = %window_ent.uid(),
- "Surface specified by window entity's {} component does not exist",
- type_name::<SurfaceSpec>()
- );
- continue;
- };
-
- let curr_gl_ctx = match gl_context.make_current(surface) {
- Ok(curr_gl_ctx) => curr_gl_ctx,
- Err(err) => {
- tracing::error!("{err}");
- continue;
- }
- };
-
- tracing::debug!(
- model_entity_id=%model_ent_id,
- renderer_ctx_entity_id=%renderer_ctx_ent_id,
- "Cleaning up after model in renderer context"
- );
-
- let Some(removed_graphics_ctx_obj) =
- graphics_ctx_objects.remove(&graphics_ctx_object_id)
- else {
- tracing::warn!(
- model_entity_id=%model_ent_id,
- "No graphics context object exists for model"
- );
- continue;
- };
-
- #[allow(irrefutable_let_patterns)]
- let GraphicsContextObject::Mesh {
- mesh: mut graphics_mesh,
- compatible_shader_program_obj_id: _,
- } = removed_graphics_ctx_obj
- else {
- tracing::error!(
- model_entity_id=%model_ent_id,
- "Graphics context object for model is not a mesh"
- );
-
- continue;
- };
-
- graphics_mesh.destroy(&curr_gl_ctx);
- }
- }
-}
-
-#[tracing::instrument(skip_all)]
-fn handle_window_changed(
- observe: Observe<Pair<Changed, Window>>,
- entity_obtainer: EntityObtainer,
-)
-{
- for evt_match in &observe {
- let window_ent = evt_match.get_ent_infallible();
-
- tracing::trace!(
- new_state = ?evt_match.get_changed_comp(),
- "Handling window change"
- );
-
- let Some(surface_spec) = window_ent.get::<SurfaceSpec>() else {
- continue;
- };
-
- let Some(renderer_ctx_ent_id) = window_ent
- .get_matching_components(
- Pair::builder()
- .relation::<WindowUsingRendererCtx>()
- .target_id(Wildcard::uid())
- .build()
- .id(),
- )
- .next()
- .map(|comp_ref| comp_ref.id().target_entity())
- else {
- continue;
- };
-
- let Some(renderer_ctx_ent) = entity_obtainer.get_entity(renderer_ctx_ent_id)
- else {
- tracing::error!("Renderer context entity does not exist");
- continue;
- };
-
- let Some(graphics_context) = renderer_ctx_ent.get::<GraphicsContext>() else {
- tracing::error!(
- "Renderer context entity does not have a GraphicsContext component"
- );
- continue;
- };
-
- let Some(surface) = graphics_context.surfaces.get(&surface_spec.id) else {
- tracing::error!(
- window_entity_id = %window_ent.uid(),
- "Surface specified by window entity's {} component does not exist",
- type_name::<SurfaceSpec>()
- );
- continue;
- };
-
- let Ok(current_graphics_context) =
- graphics_context.gl_context.make_current(surface)
- else {
- tracing::error!("Failed to make graphics context current");
- continue;
- };
-
- if let Err(err) = set_viewport(
- &current_graphics_context,
- Vec2::default(),
- evt_match.get_changed_comp().inner_size(),
- ) {
- tracing::error!("Failed to set viewport: {err}");
- }
- }
-}
-
-#[tracing::instrument(skip_all)]
fn handle_window_removed(observe: Observe<Pair<Removed, Window>>, mut actions: Actions)
{
for evt_match in &observe {
@@ -609,9 +416,11 @@ fn init_window_graphics(
continue;
};
- if let Err(err) =
- set_viewport(&curr_gl_context, Vec2 { x: 0, y: 0 }, window.inner_size())
- {
+ if let Err(err) = gl_set_viewport(
+ &curr_gl_context,
+ &Vec2 { x: 0, y: 0 }.into(),
+ &window.inner_size().clone().into(),
+ ) {
tracing::error!("Failed to set viewport: {err}");
}
@@ -1090,6 +899,48 @@ fn handle_commands(
tracing::error!("Failed to update mesh: {err}");
}
}
+ RendererCommand::RemoveMesh(mesh_object_id) => {
+ let Some(curr_gl_ctx) = &opt_curr_gl_ctx else {
+ tracing::error!("No GL context is current");
+ continue;
+ };
+
+ let Some(mesh_graphics_ctx_obj_key) = renderer_object_store
+ .remove(&mesh_object_id)
+ .map(|obj| obj.as_raw())
+ else {
+ tracing::error!(
+ object_id=?mesh_object_id,
+ "Object store does not contain a mesh object with this ID"
+ );
+ continue;
+ };
+
+ let Some(mesh_graphics_ctx_obj) =
+ graphics_ctx_objects.remove(&mesh_graphics_ctx_obj_key)
+ else {
+ tracing::error!(
+ object_id=?mesh_object_id,
+ key=mesh_graphics_ctx_obj_key,
+ "Graphics context does not contain a mesh object with this key"
+ );
+ continue;
+ };
+
+ #[allow(irrefutable_let_patterns)]
+ let GraphicsContextObject::Mesh { mesh: mut graphics_mesh, .. } =
+ mesh_graphics_ctx_obj
+ else {
+ tracing::error!(
+ object_id=?mesh_object_id,
+ key=mesh_graphics_ctx_obj_key,
+ "Graphics context object with this key is not a mesh"
+ );
+ continue;
+ };
+
+ graphics_mesh.destroy(curr_gl_ctx);
+ }
RendererCommand::DrawMesh(mesh_object_id, draw_mesh_opts) => {
let Some(curr_gl_ctx) = &opt_curr_gl_ctx else {
tracing::error!("No GL context is current");
@@ -1256,6 +1107,23 @@ fn handle_commands(
);
}
}
+ RendererCommand::SetViewport {
+ size: viewport_size,
+ position: viewport_position,
+ } => {
+ let Some(curr_gl_ctx) = &opt_curr_gl_ctx else {
+ tracing::error!("No GL context is current");
+ continue;
+ };
+
+ if let Err(err) = gl_set_viewport(
+ curr_gl_ctx,
+ &viewport_position.into(),
+ &viewport_size.into(),
+ ) {
+ tracing::error!("Failed to set viewport: {err}");
+ }
+ }
}
}
}
@@ -1323,23 +1191,6 @@ fn create_texture_object(
Ok(())
}
-fn set_viewport(
- current_context: &CurrentContextWithFns<'_>,
- position: Vec2<u32>,
- size: &Dimens<u32>,
-) -> Result<(), GlSetViewportError>
-{
- let position =
- opengl_bindings::data_types::Vec2::<u32> { x: position.x, y: position.y };
-
- let size = opengl_bindings::data_types::Dimens::<u32> {
- width: size.width,
- height: size.height,
- };
-
- opengl_bindings::misc::set_viewport(current_context, &position, &size)
-}
-
fn draw_mesh(
current_context: &CurrentContextWithFns<'_>,
graphics_mesh: &GraphicsMesh,