diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/Cargo.toml | 2 | ||||
| -rw-r--r-- | engine/src/camera.rs | 2 | ||||
| -rw-r--r-- | engine/src/draw_flags.rs | 2 | ||||
| -rw-r--r-- | engine/src/projection.rs | 6 | ||||
| -rw-r--r-- | engine/src/renderer.rs | 374 | ||||
| -rw-r--r-- | engine/src/renderer/object.rs | 112 | ||||
| -rw-r--r-- | engine/src/renderer/opengl.rs | 1023 | ||||
| -rw-r--r-- | engine/src/renderer/opengl/graphics_mesh.rs | 18 | ||||
| -rw-r--r-- | engine/src/transform.rs | 39 | ||||
| -rw-r--r-- | engine/src/windowing.rs | 30 | ||||
| -rw-r--r-- | engine/src/windowing/window.rs | 16 |
11 files changed, 1091 insertions, 533 deletions
diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 6ddcf12..8e46bef 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "engine" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] glutin = "0.32.3" diff --git a/engine/src/camera.rs b/engine/src/camera.rs index 66150af..1ecb4f3 100644 --- a/engine/src/camera.rs +++ b/engine/src/camera.rs @@ -5,7 +5,7 @@ use crate::vector::Vec3; pub mod fly; -#[derive(Debug, Component)] +#[derive(Debug, Clone, Component)] pub struct Camera { pub target: Vec3<f32>, diff --git a/engine/src/draw_flags.rs b/engine/src/draw_flags.rs index 426f865..8328669 100644 --- a/engine/src/draw_flags.rs +++ b/engine/src/draw_flags.rs @@ -22,7 +22,7 @@ impl DrawFlags } } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PolygonModeConfig { pub face: PolygonModeFace, diff --git a/engine/src/projection.rs b/engine/src/projection.rs index 115ca39..29636e7 100644 --- a/engine/src/projection.rs +++ b/engine/src/projection.rs @@ -1,9 +1,9 @@ +use crate::builder; use crate::data_types::dimens::Dimens3; use crate::matrix::Matrix; -use crate::builder; use crate::vector::Vec3; -#[derive(Debug)] +#[derive(Debug, Clone)] #[non_exhaustive] pub enum Projection { @@ -12,7 +12,7 @@ pub enum Projection } /// Perspective projection parameters. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Perspective { pub fov_radians: f32, diff --git a/engine/src/renderer.rs b/engine/src/renderer.rs index 6d25f6b..2a66a68 100644 --- a/engine/src/renderer.rs +++ b/engine/src/renderer.rs @@ -1,11 +1,34 @@ -use ecs::pair::{ChildOf, Pair}; -use ecs::phase::{Phase, POST_UPDATE as POST_UPDATE_PHASE}; -use ecs::{declare_entity, Component}; +use std::any::type_name; +use std::collections::VecDeque; +use std::sync::atomic::{AtomicU64, Ordering}; +use bitflags::bitflags; +use ecs::actions::Actions; +use ecs::pair::{ChildOf, Pair, Wildcard}; +use ecs::phase::{POST_UPDATE as POST_UPDATE_PHASE, Phase}; +use ecs::query::term::Without; +use ecs::sole::Single; +use ecs::{Component, Query, declare_entity}; + +use crate::asset::{Assets, Handle as AssetHandle}; use crate::builder; +use crate::camera::{Active as ActiveCamera, Camera}; +use crate::data_types::dimens::Dimens; +use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig}; +use crate::lighting::{DirectionalLight, GlobalLight, PointLight}; +use crate::material::{Flags as MaterialFlags, Material}; +use crate::mesh::Mesh; +use crate::model::{Materials as ModelMaterials, Model, Spec as ModelSpec}; +use crate::renderer::object::{Id as ObjectId, Store as ObjectStore}; +use crate::texture::Texture; +use crate::transform::{Scale, Transform, WorldPosition}; +use crate::windowing::window::Window; +pub mod object; pub mod opengl; +static NEXT_SURFACE_ID: AtomicU64 = AtomicU64::new(0); + declare_entity!( pub RENDER_PHASE, ( @@ -78,3 +101,348 @@ impl Default for GraphicsPropertiesBuilder } } } + +#[derive(Debug, Component)] +pub struct SurfaceSpec +{ + pub id: SurfaceId, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SurfaceId +{ + inner: u64, +} + +impl SurfaceId +{ + pub fn new_unique() -> Self + { + Self { + inner: NEXT_SURFACE_ID.fetch_add(1, Ordering::Relaxed), + } + } +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum Command +{ + MakeCurrent(SurfaceId), + ClearBuffers(BufferClearMask), + SwapBuffers(SurfaceId), + UseShader, // TODO: Add ability to specify shader + ActivateShader, + UseCamera(Camera, WorldPosition), + ApplyTransform + { + transform: Transform, + window_size: Dimens<u32>, + }, + SetShaderDirectionalLights(Vec<DirectionalLight>), + SetShaderPointLights(Vec<(PointLight, WorldPosition)>), + CreateTexture(Texture), + UseMaterial + { + material_asset: Option<AssetHandle<Material>>, + material_flags: MaterialFlags, + global_light: GlobalLight, + }, + DrawMesh + { + mesh_asset: AssetHandle<Mesh>, + }, + SetPolygonModeConfig(PolygonModeConfig), +} + +bitflags! { + #[derive(Debug)] + pub struct BufferClearMask: u8 { + const COLOR = 1; + const DEPTH = 2; + const STENCIL = 3; + } +} + +/// Renderer command FIFO queue. +/// +/// This component is present in renderer context entities. +#[derive(Debug, Component)] +pub struct CommandQueue +{ + queue: VecDeque<Command>, +} + +impl CommandQueue +{ + pub fn push(&mut self, command: Command) + { + self.queue.push_back(command); + } + + pub fn drain(&mut self) -> impl Iterator<Item = Command> + use<'_> + { + self.queue.drain(..) + } +} + +impl Default for CommandQueue +{ + fn default() -> Self + { + CommandQueue { queue: VecDeque::with_capacity(100) } + } +} + +#[derive(Debug, Component)] +pub struct WindowUsingRendererCtx; + +#[derive(Debug, Component)] +pub struct CtxUsedByWindow; + +type RenderableEntity<'a> = ( + &'a Model, + Option<&'a MaterialFlags>, + Option<&'a WorldPosition>, + Option<&'a Scale>, + Option<&'a DrawFlags>, +); + +#[tracing::instrument(skip_all)] +pub fn enqueue_commands( + renderer_ctx_query: Query<( + &mut CommandQueue, + &ObjectStore, + &[Pair<CtxUsedByWindow, Wildcard>], + )>, + renderable_query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>, + point_light_query: Query<(&PointLight, &WorldPosition)>, + directional_light_query: Query<(&DirectionalLight,)>, + camera_query: Query<(&Camera, &WorldPosition, &ActiveCamera)>, + global_light: Single<GlobalLight>, + assets: Single<Assets>, + mut actions: Actions, +) +{ + let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else { + tracing::warn!("No current camera. Nothing will be rendered"); + return; + }; + + for (renderer_ctx_ent_id, (mut command_queue, 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; + }; + + let Some(window) = window_ent.get::<Window>() else { + 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; + }; + + command_queue.push(Command::MakeCurrent(surface_spec.id)); + + command_queue + .push(Command::UseCamera(camera.clone(), camera_world_pos.clone())); + + command_queue.push(Command::ClearBuffers( + BufferClearMask::COLOR | BufferClearMask::DEPTH, + )); + + for (model, material_flags, world_pos, scale, draw_flags) in &renderable_query + { + let Some(model_spec) = assets.get(&model.spec_asset) else { + continue; + }; + + let Some(mesh_asset) = &model_spec.mesh_asset else { + continue; + }; + + debug_assert!(model_spec.material_names.len() <= 1); + + let model_material_asset = + match find_first_model_material(model_spec, &assets) { + MaterialSearchResult::Found(model_material_asset) => { + Some(model_material_asset.clone()) + } + MaterialSearchResult::NotFound => { + continue; + } + MaterialSearchResult::NoMaterials => None, + }; + + if let Some(model_material_asset) = &model_material_asset { + let Some(model_material) = assets.get(model_material_asset) else { + unreachable!(); + }; + + if let Some(ambient_map) = &model_material.ambient_map { + if assets.get(&ambient_map.asset_handle).is_none() { + continue; + } + } + + if let Some(diffuse_map) = &model_material.diffuse_map { + if assets.get(&diffuse_map.asset_handle).is_none() { + continue; + } + } + + if let Some(specular_map) = &model_material.specular_map { + if assets.get(&specular_map.asset_handle).is_none() { + continue; + } + } + } + + command_queue.push(Command::UseShader); + + command_queue.push(Command::ApplyTransform { + transform: Transform { + position: world_pos + .as_deref() + .cloned() + .unwrap_or_default() + .position, + scale: scale.as_deref().cloned().unwrap_or_default().scale, + }, + window_size: *window.inner_size(), + }); + + command_queue.push(Command::SetShaderDirectionalLights( + directional_light_query + .iter() + .map(|(dir_light,)| dir_light.clone()) + .collect::<Vec<_>>(), + )); + + command_queue.push(Command::SetShaderPointLights( + point_light_query + .iter() + .map(|(point_light, point_light_world_pos)| { + (point_light.clone(), point_light_world_pos.clone()) + }) + .collect::<Vec<_>>(), + )); + + if let Some(model_material_asset) = &model_material_asset { + let Some(model_material) = assets.get(model_material_asset) else { + unreachable!(); + }; + + for texture in [ + &model_material.specular_map, + &model_material.diffuse_map, + &model_material.specular_map, + ] + .into_iter() + .flatten() + { + if !object_store + .contains_with_id(&ObjectId::Asset(texture.asset_handle.id())) + { + command_queue.push(Command::CreateTexture(texture.clone())); + } + } + } + + command_queue.push(Command::UseMaterial { + material_asset: model_material_asset, + material_flags: material_flags + .as_deref() + .cloned() + .unwrap_or_default(), + global_light: global_light.clone(), + }); + + command_queue.push(Command::ActivateShader); + + if let Some(draw_flags) = draw_flags.as_deref() + && draw_flags.polygon_mode_config != PolygonModeConfig::default() + { + command_queue.push(Command::SetPolygonModeConfig( + draw_flags.polygon_mode_config.clone(), + )); + } + + command_queue.push(Command::DrawMesh { mesh_asset: mesh_asset.clone() }); + + if let Some(draw_flags) = draw_flags.as_deref() + && draw_flags.polygon_mode_config != PolygonModeConfig::default() + { + command_queue.push(Command::SetPolygonModeConfig( + PolygonModeConfig::default(), + )); + } + } + + command_queue.push(Command::SwapBuffers(surface_spec.id)); + } + } +} + +enum MaterialSearchResult<'a> +{ + Found(&'a AssetHandle<Material>), + NotFound, + NoMaterials, +} + +fn find_first_model_material<'assets>( + model_spec: &'assets ModelSpec, + assets: &'assets Assets, +) -> MaterialSearchResult<'assets> +{ + let Some(material_name) = model_spec.material_names.first() else { + return MaterialSearchResult::NoMaterials; + }; + + let Some(material_asset) = (match &model_spec.materials { + ModelMaterials::Maps(material_asset_map_assets) => material_asset_map_assets + .iter() + .find_map(|mat_asset_map_asset| { + let mat_asset_map = assets.get(mat_asset_map_asset)?; + + mat_asset_map.assets.get(material_name) + }), + ModelMaterials::Direct(material_assets) => material_assets.get(material_name), + }) else { + return MaterialSearchResult::NotFound; + }; + + if assets.get(material_asset).is_none() { + tracing::trace!("Missing material asset"); + return MaterialSearchResult::NotFound; + } + + MaterialSearchResult::Found(material_asset) +} diff --git a/engine/src/renderer/object.rs b/engine/src/renderer/object.rs new file mode 100644 index 0000000..d8bb2e3 --- /dev/null +++ b/engine/src/renderer/object.rs @@ -0,0 +1,112 @@ +use std::collections::HashMap; +use std::collections::hash_map::Entry as HashMapEntry; + +use ecs::Component; + +use crate::asset::Id as AssetId; + +/// Renderer object ID. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Id +{ + Asset(AssetId), + Other(u64), +} + +/// Renderer object store. +#[derive(Debug, Default, Component)] +pub struct Store +{ + objects: HashMap<Id, Object>, +} + +impl Store +{ + pub fn get_obj(&self, id: &Id) -> Option<&Object> + { + self.objects.get(id) + } + + pub fn get_texture_obj(&self, id: &Id) -> Option<&Object> + { + let obj = self.get_obj(id)?; + + if !matches!(obj.kind(), Kind::Texture) { + return None; + } + + Some(obj) + } + + pub fn contains_with_id(&self, id: &Id) -> bool + { + self.objects.contains_key(id) + } + + pub fn insert(&mut self, id: Id, object: Object) + { + self.objects.insert(id, object); + } + + pub fn entry(&mut self, id: Id) -> StoreEntry<'_> + { + StoreEntry { inner: self.objects.entry(id) } + } +} + +#[derive(Debug)] +pub struct StoreEntry<'store> +{ + inner: HashMapEntry<'store, Id, Object>, +} + +impl<'store> StoreEntry<'store> +{ + pub fn or_insert(self, default_obj: Object) -> &'store mut Object + { + self.inner.or_insert(default_obj) + } + + pub fn or_insert_with( + self, + default_func: impl FnOnce() -> Object, + ) -> &'store mut Object + { + self.inner.or_insert_with(default_func) + } +} + +/// Renderer object. +#[derive(Debug, Clone)] +pub struct Object +{ + raw: u32, + kind: Kind, +} + +impl Object +{ + pub fn from_raw(raw: u32, kind: Kind) -> Self + { + Self { raw, kind } + } + + pub fn as_raw(&self) -> u32 + { + self.raw + } + + pub fn kind(&self) -> Kind + { + self.kind + } +} + +/// Renderer object kind. +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum Kind +{ + Texture, + Mesh, +} diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs index 8f10215..4bd67a4 100644 --- a/engine/src/renderer/opengl.rs +++ b/engine/src/renderer/opengl.rs @@ -7,7 +7,6 @@ use std::io::{Error as IoError, ErrorKind as IoErrorKind}; use std::path::Path; use ecs::actions::Actions; -use ecs::component::Handle as ComponentHandle; use ecs::entity::obtainer::Obtainer as EntityObtainer; use ecs::event::component::{Changed, Removed}; use ecs::pair::{ChildOf, Pair, Wildcard}; @@ -18,7 +17,11 @@ use ecs::system::observer::Observe; use ecs::{Component, Query, declare_entity}; use glutin::display::GetGlDisplay; use glutin::prelude::GlDisplay; -use glutin::surface::GlSurface; +use glutin::surface::{ + GlSurface, + Surface as GlutinSurface, + WindowSurface as GlutinWindowSurface, +}; use opengl_bindings::debug::{ MessageIdsAction, MessageSeverity, @@ -29,7 +32,7 @@ use opengl_bindings::debug::{ set_debug_message_control, }; use opengl_bindings::misc::{ - BufferClearMask, + BufferClearMask as GlBufferClearMask, Capability, SetViewportError as GlSetViewportError, clear_buffers, @@ -58,33 +61,47 @@ use opengl_bindings::{ContextWithFns, CurrentContextWithFns}; use safer_ffi::layout::ReprC; use crate::asset::{Assets, Id as AssetId}; -use crate::camera::{Active as ActiveCamera, Camera}; +use crate::camera::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::model::{Materials as ModelMaterials, Model, Spec as ModelSpec}; +use crate::model::Model; use crate::opengl::glsl::{ PreprocessingError as GlslPreprocessingError, preprocess as glsl_preprocess, }; use crate::projection::{ClipVolume, Projection}; +use crate::renderer::object::{ + Id as RendererObjectId, + Kind as RendererObjectKind, + Object as RendererObject, + Store as RendererObjectStore, +}; use crate::renderer::opengl::glutin_compat::{ DisplayBuilder, Error as GlutinCompatError, }; use crate::renderer::opengl::graphics_mesh::GraphicsMesh; -use crate::renderer::{GraphicsProperties, RENDER_PHASE}; +use crate::renderer::{ + BufferClearMask, + Command as RendererCommand, + CommandQueue as RendererCommandQueue, + CtxUsedByWindow as RendererCtxUsedByWindow, + GraphicsProperties, + RENDER_PHASE, + SurfaceId, + SurfaceSpec, + WindowUsingRendererCtx, +}; use crate::texture::{ Filtering as TextureFiltering, Properties as TextureProperties, Wrapping as TextureWrapping, }; -use crate::transform::{Scale, WorldPosition}; -use crate::util::MapVec; +use crate::transform::WorldPosition; use crate::vector::{Vec2, Vec3}; use crate::windowing::Context as WindowingContext; use crate::windowing::window::{ @@ -102,14 +119,7 @@ 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>, - &'a [Pair<DataInGraphicsContext, Wildcard>], -); +const DEFAULT_TEXTURE_OBJECT_ID: RendererObjectId = RendererObjectId::Other(0xaa); declare_entity!( pub POST_RENDER_PHASE, @@ -117,55 +127,24 @@ declare_entity!( ); #[derive(Debug, Component)] -struct WithGraphicsContext; - -#[derive(Debug, Component)] struct WindowGlConfig { gl_config: glutin::config::Config, } -#[derive(Debug, Component)] -struct WindowGraphicsSurface -{ - surface: glutin::surface::Surface<glutin::surface::WindowSurface>, -} - #[derive(Component)] struct GraphicsContext { - context: ContextWithFns, + gl_context: ContextWithFns, shader_program: Option<GlShaderProgram>, - textures_objs: HashMap<AssetId, GlTexture>, - default_1x1_texture_obj: Option<GlTexture>, graphics_mesh_store: GraphicsMeshStore, + surfaces: HashMap<SurfaceId, GlutinSurface<GlutinWindowSurface>>, } #[derive(Debug, Default)] struct GraphicsMeshStore { - graphics_meshes: MapVec<GraphicsMeshId, GraphicsMesh>, - next_id: GraphicsMeshId, -} - -impl GraphicsMeshStore -{ - fn insert(&mut self, graphics_mesh: GraphicsMesh) -> GraphicsMeshId - { - let id = self.next_id; - - self.graphics_meshes.insert(id, graphics_mesh); - - self.next_id.inner += 1; - - id - } -} - -#[derive(Debug, Component)] -struct DataInGraphicsContext -{ - graphics_mesh_id: GraphicsMeshId, + graphics_meshes: HashMap<AssetId, GraphicsMesh>, } #[derive(Debug, Default)] @@ -179,7 +158,8 @@ impl ecs::extension::Extension for Extension collector.add_declared_entity(&RENDER_PHASE); collector.add_declared_entity(&POST_RENDER_PHASE); - collector.add_system(*RENDER_PHASE, render); + collector.add_system(*RENDER_PHASE, super::enqueue_commands); + collector.add_system(*RENDER_PHASE, handle_commands); collector.add_system(*POST_RENDER_PHASE, prepare_windows); collector.add_system(*POST_RENDER_PHASE, init_window_graphics); @@ -192,55 +172,96 @@ impl ecs::extension::Extension for Extension } #[tracing::instrument(skip_all)] -fn handle_model_removed(observe: Observe<Pair<Removed, Model>>, mut actions: Actions) +fn handle_model_removed( + observe: Observe<Pair<Removed, Model>>, + renderer_ctx_query: Query<( + &mut GraphicsContext, + Pair<RendererCtxUsedByWindow, Wildcard>, + )>, + assets: Single<Assets>, +) { for evt_match in &observe { - let ent_id = evt_match.id(); + let model_ent_id = evt_match.id(); - tracing::debug!(entity_id=%ent_id, "Cleaning up after model"); + for (renderer_ctx_ent_id, (mut graphics_ctx, renderer_ctx_used_by_window)) in + renderer_ctx_query.iter_with_euids() + { + let GraphicsContext { + ref gl_context, + ref mut graphics_mesh_store, + ref surfaces, + .. + } = *graphics_ctx; - let ent = evt_match.get_ent_infallible(); + let model = evt_match.get_removed_comp(); - for data_in_graphics_ctx_pair in - ent.get_wildcard_pair_matches::<DataInGraphicsContext, Wildcard>() - { - actions.remove_components(ent_id, [data_in_graphics_ctx_pair.id()]); + let Some(model_spec) = assets.get(&model.spec_asset) else { + continue; + }; - let Some(graphics_context_ent) = data_in_graphics_ctx_pair.get_target_ent() - else { - tracing::trace!( - concat!( - "Graphics context referenced by pair ({}, {}) does not exist. ", - "Skipping cleanup of this model" - ), - type_name::<DataInGraphicsContext>(), - data_in_graphics_ctx_pair.id().target_entity() + let Some(mesh_asset) = &model_spec.mesh_asset else { + continue; + }; + + if !graphics_mesh_store + .graphics_meshes + .contains_key(&mesh_asset.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(data_in_graphics_ctx) = - data_in_graphics_ctx_pair.get_data_as_relation() - else { - unreachable!(); + 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 Some(mut graphics_context) = - graphics_context_ent.get_mut::<GraphicsContext>() + 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(mut graphics_mesh) = + graphics_mesh_store.graphics_meshes.remove(&mesh_asset.id()) else { - tracing::trace!( - "Graphics context entity {} does not have a {} component", - graphics_context_ent.uid(), - type_name::<GraphicsContext>() + tracing::warn!( + model_entity_id=%model_ent_id, + "No mesh exists for model" ); continue; }; - graphics_context - .graphics_mesh_store - .graphics_meshes - .remove(data_in_graphics_ctx.graphics_mesh_id); + graphics_mesh.destroy(&curr_gl_ctx); } } } @@ -259,15 +280,14 @@ fn handle_window_changed( "Handling window change" ); - let Some(window_graphics_surface) = window_ent.get::<WindowGraphicsSurface>() - else { + let Some(surface_spec) = window_ent.get::<SurfaceSpec>() else { continue; }; - let Some(graphics_context_ent_id) = window_ent + let Some(renderer_ctx_ent_id) = window_ent .get_matching_components( Pair::builder() - .relation::<WithGraphicsContext>() + .relation::<WindowUsingRendererCtx>() .target_id(Wildcard::uid()) .build() .id(), @@ -278,23 +298,30 @@ fn handle_window_changed( continue; }; - let Some(graphics_context_ent) = - entity_obtainer.get_entity(graphics_context_ent_id) + let Some(renderer_ctx_ent) = entity_obtainer.get_entity(renderer_ctx_ent_id) else { - tracing::error!("Graphics context entity does not exist"); + tracing::error!("Renderer context entity does not exist"); continue; }; - let Some(graphics_context) = graphics_context_ent.get::<GraphicsContext>() else { + let Some(graphics_context) = renderer_ctx_ent.get::<GraphicsContext>() else { tracing::error!( - "Graphics context entity does not have a GraphicsContext component" + "Renderer context entity does not have a GraphicsContext component" ); continue; }; - let Ok(current_graphics_context) = graphics_context - .context - .make_current(&window_graphics_surface.surface) + 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; @@ -324,42 +351,40 @@ fn handle_window_removed(observe: Observe<Pair<Removed, Window>>, mut actions: A "Handling removal of window" ); - actions.remove_comps::<(WindowGraphicsSurface, WindowGlConfig)>(window_ent_id); + actions.remove_comps::<(SurfaceSpec, WindowGlConfig)>(window_ent_id); - let Some(with_graphics_ctx_pair_handle) = - window_ent.get_first_wildcard_pair_match::<WithGraphicsContext, Wildcard>() + let Some(with_renderer_ctx_pair) = window_ent + .get_first_wildcard_pair_match::<WindowUsingRendererCtx, Wildcard>() else { - tracing::warn!("Window entity is missing a (WithGraphicsContext, *) pair"); + tracing::warn!( + "Window entity is missing a ({}, *) pair", + type_name::<WindowUsingRendererCtx>() + ); continue; }; - let graphics_context_ent_id = with_graphics_ctx_pair_handle.id().target_entity(); + let renderer_context_ent_id = with_renderer_ctx_pair.id().target_entity(); - actions.remove_comps::<(GraphicsContext,)>(graphics_context_ent_id); + actions.remove_comps::<(GraphicsContext, RendererObjectStore)>( + renderer_context_ent_id, + ); + + actions.remove_components( + renderer_context_ent_id, + [Pair::builder() + .relation::<RendererCtxUsedByWindow>() + .target_id(window_ent_id) + .build() + .id()], + ); - actions.remove_components(window_ent_id, [with_graphics_ctx_pair_handle.id()]); + actions.remove_components(window_ent_id, [with_renderer_ctx_pair.id()]); } } #[derive(Debug, Component)] struct SetupFailed; -// fn on_window_creation_attrs_added( -// observe: Observe<Pair<Added, WindowCreationAttributes>>, -// windowing: Single<Windowing>, -// window_store: Single<WindowStore>, -// mut actions: Actions, -// ) -// { -// for evt_match in &observe { -// let Some(ent) = evt_match.get_entity() else { -// unreachable!(); -// }; -// -// if ent.has_component(WindowGlConfig::id()) || -// ent.has_component(WindowClosed::id()) || ent.has_component() {} } -// } - fn prepare_windows( window_query: Query< ( @@ -444,38 +469,6 @@ fn prepare_windows( *window_creation_attrs = new_window_creation_attrs; - // let gl_config_template = glutin_config_template_builder.build(); - // - // let display = match glutin_winit_compat::create_display( - // unsafe { engine_display.as_display_handle() }, - // glutin_winit_compat::ApiPreference::default(), - // None, - // ) { - // Ok(gl_display) => gl_display, - // Err(err) => { - // tracing::error!("Failed to create graphics platform display: {err}"); - // continue; - // } - // }; - // - // let mut gl_configs = match unsafe { display.find_configs(gl_config_template) } - // { Ok(gl_configs) => gl_configs, - // Err(err) => { - // tracing::error!("Failed to find GL configs: {err:?}"); - // continue; - // } - // }; - // - // let Some(first_gl_config) = gl_configs.next() else { - // tracing::error!("No matching GL configuration exists"); - // continue; - // }; - // - // *window_creation_attrs = finalize_window_creation_attrs( - // window_creation_attrs.clone(), - // &first_gl_config, - // ); - actions.add_components(window_ent_id, (WindowGlConfig { gl_config },)); if window.is_none() { @@ -488,7 +481,7 @@ fn prepare_windows( fn init_window_graphics( window_query: Query< (&Window, &WindowGlConfig, &GraphicsProperties), - (Without<WindowGraphicsSurface>, Without<SetupFailed>), + (Without<SurfaceSpec>, Without<SetupFailed>), >, mut actions: Actions, windowing_context: Single<WindowingContext>, @@ -564,7 +557,7 @@ fn init_window_graphics( } }; - let context = match ContextWithFns::new(context, &surface) { + let gl_context = match ContextWithFns::new(context, &surface) { Ok(context) => context, Err(err) => { tracing::error!("Failed to create graphics context: {err}"); @@ -572,45 +565,37 @@ fn init_window_graphics( } }; - let Ok(current_graphics_context) = context.make_current(&surface) else { + let Ok(curr_gl_context) = gl_context.make_current(&surface) else { tracing::error!("Failed to make graphics context current"); continue; }; - if let Err(err) = set_viewport( - ¤t_graphics_context, - Vec2 { x: 0, y: 0 }, - window.inner_size(), - ) { + if let Err(err) = + set_viewport(&curr_gl_context, Vec2 { x: 0, y: 0 }, window.inner_size()) + { tracing::error!("Failed to set viewport: {err}"); } set_enabled( - ¤t_graphics_context, + &curr_gl_context, Capability::DepthTest, graphics_props.depth_test, ); set_enabled( - ¤t_graphics_context, + &curr_gl_context, Capability::MultiSample, graphics_props.multisampling_sample_cnt.is_some(), ); if graphics_props.debug { - enable(¤t_graphics_context, Capability::DebugOutput); - enable( - ¤t_graphics_context, - Capability::DebugOutputSynchronous, - ); + enable(&curr_gl_context, Capability::DebugOutput); + enable(&curr_gl_context, Capability::DebugOutputSynchronous); - set_debug_message_callback( - ¤t_graphics_context, - opengl_debug_message_cb, - ); + set_debug_message_callback(&curr_gl_context, opengl_debug_message_cb); match set_debug_message_control( - ¤t_graphics_context, + &curr_gl_context, None, None, None, @@ -627,326 +612,320 @@ fn init_window_graphics( } } - let graphics_context_ent_id = actions.spawn((GraphicsContext { - context, - shader_program: None, - textures_objs: HashMap::new(), - default_1x1_texture_obj: None, - graphics_mesh_store: GraphicsMeshStore::default(), - },)); + let surface_id = SurfaceId::new_unique(); + + let renderer_ctx_ent_id = actions.spawn(( + GraphicsContext { + gl_context, + shader_program: None, + graphics_mesh_store: GraphicsMeshStore::default(), + surfaces: HashMap::from([(surface_id, surface)]), + }, + RendererObjectStore::default(), + RendererCommandQueue::default(), + Pair::builder() + .relation::<RendererCtxUsedByWindow>() + .target_id(window_ent_id) + .build(), + )); actions.add_components( window_ent_id, ( - WindowGraphicsSurface { surface }, + SurfaceSpec { id: surface_id }, Pair::builder() - .relation::<WithGraphicsContext>() - .target_id(graphics_context_ent_id) + .relation::<WindowUsingRendererCtx>() + .target_id(renderer_ctx_ent_id) .build(), ), ); } } -enum MaterialSearchResult<'a> -{ - Found(&'a Material), - NotFound, - NoMaterials, -} - -fn find_first_model_material<'assets>( - model_spec: &'assets ModelSpec, - assets: &'assets Assets, -) -> MaterialSearchResult<'assets> -{ - let Some(material_name) = model_spec.material_names.first() else { - return MaterialSearchResult::NoMaterials; - }; - - let Some(material_asset) = (match &model_spec.materials { - ModelMaterials::Maps(material_asset_map_assets) => material_asset_map_assets - .iter() - .find_map(|mat_asset_map_asset| { - let mat_asset_map = assets.get(mat_asset_map_asset)?; - - mat_asset_map.assets.get(material_name) - }), - ModelMaterials::Direct(material_assets) => material_assets.get(material_name), - }) else { - return MaterialSearchResult::NotFound; - }; - - let Some(material) = assets.get(material_asset) else { - tracing::trace!("Missing material asset"); - return MaterialSearchResult::NotFound; - }; - - MaterialSearchResult::Found(material) -} - #[tracing::instrument(skip_all)] -#[allow(clippy::too_many_arguments)] -fn render( - query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>, - point_light_query: Query<(&PointLight, &WorldPosition)>, - directional_lights: Query<(&DirectionalLight,)>, - camera_query: Query<(&Camera, &WorldPosition, &ActiveCamera)>, - window_query: Query<( - &Window, - &WindowGraphicsSurface, - &GraphicsProperties, - Pair<WithGraphicsContext, Wildcard>, +fn handle_commands( + renderer_ctx_query: Query<( + &mut GraphicsContext, + &mut RendererObjectStore, + &mut RendererCommandQueue, )>, - global_light: Single<GlobalLight>, assets: Single<Assets>, - mut actions: Actions, ) { - for ( - window_ent_id, - (window, window_graphics_surface, window_graphics_props, graphics_context_pair), - ) in window_query.iter_with_euids() + for (mut graphics_ctx, mut renderer_object_store, mut command_queue) in + &renderer_ctx_query { - let Some(graphics_context_ent) = graphics_context_pair.get_target_ent() else { - tracing::error!("Window's associated graphics context entity does not exist"); - actions.remove_components(window_ent_id, [graphics_context_pair.id()]); - continue; - }; - - let Some(mut graphics_context) = - graphics_context_ent.get_mut::<GraphicsContext>() - else { - tracing::error!( - "Graphics context entity does not have a GraphicsContext component" - ); - return; - }; - let GraphicsContext { - ref context, + ref gl_context, ref mut shader_program, - ref mut textures_objs, - ref mut default_1x1_texture_obj, ref mut graphics_mesh_store, - } = *graphics_context; + ref surfaces, + } = *graphics_ctx; - let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else { - tracing::warn!("No current camera. Nothing will be rendered"); - return; - }; + let mut opt_curr_gl_ctx: Option<CurrentContextWithFns> = None; - let Ok(current_graphics_context) = - context.make_current(&window_graphics_surface.surface) - else { - tracing::error!("Failed to make graphics context current"); - continue; - }; + let mut opt_curr_camera: Option<(Camera, WorldPosition)> = None; - let directional_lights = directional_lights.iter().collect::<Vec<_>>(); + for command in command_queue.drain() { + let tracing_span = tracing::info_span!("handle_cmd", command = ?command); + let _tracing_span_enter = tracing_span.enter(); - let shader_program = shader_program.get_or_insert_with(|| { - create_default_shader_program(¤t_graphics_context).unwrap() - }); + match command { + RendererCommand::MakeCurrent(surface_id) => { + let Some(surface) = surfaces.get(&surface_id) else { + tracing::error!(surface_id=?surface_id, "Surface does not exist"); + continue; + }; - let mut clear_mask = BufferClearMask::COLOR; + let curr_gl_ctx = match gl_context.make_current(surface) { + Ok(current_graphics_context) => current_graphics_context, + Err(err) => { + tracing::error!( + "Failed to make graphics context current: {err}" + ); + continue; + } + }; - clear_mask.set(BufferClearMask::DEPTH, window_graphics_props.depth_test); + opt_curr_gl_ctx = Some(curr_gl_ctx); + } + RendererCommand::ClearBuffers(buffer_clear_mask) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - clear_buffers(¤t_graphics_context, clear_mask); + let mut clear_mask = GlBufferClearMask::empty(); - for ( - euid, - ( - model, - material_flags, - position, - scale, - draw_flags, - data_in_graphics_ctx_pairs, - ), - ) in query.iter_with_euids() - { - let Some(model_spec) = assets.get(&model.spec_asset) else { - tracing::trace!("Missing model spec asset"); - continue; - }; + clear_mask.set( + GlBufferClearMask::COLOR, + buffer_clear_mask.contains(BufferClearMask::COLOR), + ); - let Some(mesh_asset) = &model_spec.mesh_asset else { - tracing::debug!("Model spec mesh asset is None"); - continue; - }; + clear_mask.set( + GlBufferClearMask::DEPTH, + buffer_clear_mask.contains(BufferClearMask::DEPTH), + ); - let Some(model_mesh) = assets.get(&mesh_asset) else { - tracing::trace!("Missing mesh asset"); - continue; - }; + clear_mask.set( + GlBufferClearMask::STENCIL, + buffer_clear_mask.contains(BufferClearMask::STENCIL), + ); - debug_assert!(model_spec.material_names.len() <= 1); + clear_buffers(&curr_gl_ctx, clear_mask); + } + RendererCommand::SwapBuffers(surface_id) => { + let Some(surface) = surfaces.get(&surface_id) else { + tracing::error!(surface_id=?surface_id, "Surface does not exist"); + continue; + }; - let model_material = match find_first_model_material(model_spec, &assets) { - MaterialSearchResult::Found(model_material) => model_material, - MaterialSearchResult::NotFound => { - continue; + if let Err(err) = surface.swap_buffers(gl_context.context()) { + tracing::error!("Failed to swap buffers: {err}"); + } } - MaterialSearchResult::NoMaterials => &Material::default(), - }; + RendererCommand::UseShader => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - let material_flags = material_flags - .map(|material_flags| material_flags.clone()) - .unwrap_or_default(); + let _shader_program = shader_program.get_or_insert_with(|| { + create_default_shader_program(&curr_gl_ctx).unwrap() + }); + } + RendererCommand::ActivateShader => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - let graphics_mesh_id = match data_in_graphics_ctx_pairs - .get_with_target_id(graphics_context_ent.uid()) - { - Some(data_in_graphics_ctx_pair) => { - let Some(data_in_graphics_ctx) = - data_in_graphics_ctx_pair.get_data::<DataInGraphicsContext>() - else { - tracing::warn!( - concat!( - "Pair with relation {} ({}) has no data or data with a ", - "wrong type. This pair will be removed" - ), - type_name::<DataInGraphicsContext>(), - data_in_graphics_ctx_pair.id() - ); - - actions.remove_components(euid, [data_in_graphics_ctx_pair.id()]); + let Some(shader_program) = shader_program else { + tracing::error!("Shader does not exist"); continue; }; - data_in_graphics_ctx.graphics_mesh_id + shader_program.activate(&curr_gl_ctx); } - None => { - let graphics_mesh = - match GraphicsMesh::new(¤t_graphics_context, model_mesh) { - Ok(graphics_mesh) => graphics_mesh, - Err(err) => { - tracing::error!( - "Failed to create {}: {err}", - type_name::<GraphicsMesh>() - ); - - // This system should not try again - actions.add_components(euid, (NoDraw,)); + RendererCommand::UseCamera(camera, camera_world_pos) => { + opt_curr_camera = Some((camera, camera_world_pos)); + } + RendererCommand::ApplyTransform { transform, window_size } => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - continue; - } - }; + let Some(shader_program) = shader_program else { + tracing::error!("Shader does not exist"); + continue; + }; - let graphics_mesh_id = graphics_mesh_store.insert(graphics_mesh); + let Some((camera, camera_world_pos)) = &opt_curr_camera else { + tracing::error!("No current camera"); + continue; + }; - actions.add_components( - euid, - (Pair::builder() - .relation_as_data(DataInGraphicsContext { graphics_mesh_id }) - .target_id(graphics_context_ent.uid()) - .build(),), + apply_transformation_matrices( + &curr_gl_ctx, + Transformation { + position: transform.position, + scale: transform.scale, + }, + shader_program, + &camera, + &camera_world_pos, + &window_size, ); - - graphics_mesh_id } - }; + RendererCommand::SetShaderDirectionalLights(directional_lights) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - let Some(graphics_mesh) = - graphics_mesh_store.graphics_meshes.get(&graphics_mesh_id) - else { - tracing::error!("Graphics mesh with ID: {graphics_mesh_id:?} not found"); - continue; - }; + let Some(shader_program) = shader_program else { + tracing::error!("Shader does not exist"); + continue; + }; - apply_transformation_matrices( - ¤t_graphics_context, - Transformation { - position: position.map(|pos| *pos).unwrap_or_default().position, - scale: scale.map(|scale| *scale).unwrap_or_default().scale, - }, - shader_program, - &camera, - &camera_world_pos, - window.inner_size(), - ); + set_shader_directional_lights( + curr_gl_ctx, + shader_program, + &directional_lights, + ); + } + RendererCommand::SetShaderPointLights(point_lights) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - apply_light( - ¤t_graphics_context, - model_material, - &material_flags, - &global_light, - shader_program, - (point_light_query.iter(), point_light_query.iter().count()), - directional_lights - .iter() - .map(|(dir_light,)| &**dir_light) - .collect::<Vec<_>>() - .as_slice(), - &camera_world_pos, - ); + let Some(shader_program) = shader_program else { + tracing::error!("Shader does not exist"); + continue; + }; - match create_bind_material_textures( - ¤t_graphics_context, - model_material, - &assets, - textures_objs, - default_1x1_texture_obj, - ) { - Ok(()) => {} - Err(CreateBindMaterialTexturesError::MissingTextureAsset) => { - continue; + set_shader_point_lights(curr_gl_ctx, shader_program, &point_lights); } - Err( - err @ CreateBindMaterialTexturesError::CreateTextureFailed { .. }, - ) => { - tracing::error!( - "Creating &/ binding material textures failed: {err}" - ); + RendererCommand::CreateTexture(texture) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - // This system should not try again - actions.add_components(euid, (NoDraw,)); + let Some(texture_image) = assets.get(&texture.asset_handle) else { + continue; + }; - continue; + if let Err(err) = create_texture_object( + curr_gl_ctx, + &mut renderer_object_store, + texture.asset_handle.id(), + texture_image, + &texture.properties, + ) { + tracing::error!("Failed to create texture object: {err}"); + } } - } + RendererCommand::UseMaterial { + material_asset, + material_flags, + global_light, + } => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - shader_program.activate(¤t_graphics_context); + let Some(shader_program) = shader_program else { + tracing::error!("Shader does not exist"); + continue; + }; - if let Some(draw_flags) = &draw_flags { - opengl_bindings::misc::set_polygon_mode( - ¤t_graphics_context, - draw_flags.polygon_mode_config.face, - draw_flags.polygon_mode_config.mode, - ); - } + let Some((_, camera_world_pos)) = &opt_curr_camera else { + tracing::error!("No current camera"); + continue; + }; - if let Err(err) = draw_mesh(¤t_graphics_context, &graphics_mesh) { - tracing::error!( - entity_id = %euid, - graphics_context_entity_id = %graphics_context_ent.uid(), - "Failed to draw mesh: {err}", - ); + let material = match material_asset.as_ref() { + Some(material_asset) => { + let Some(material) = assets.get(&material_asset) else { + continue; + }; - // This system should not try again - actions.add_components(euid, (NoDraw,)); + material + } + None => &Material::default(), + }; - continue; - }; + set_shader_material( + curr_gl_ctx, + material, + &material_flags, + &global_light, + shader_program, + camera_world_pos, + ); - if draw_flags.is_some() { - let default_polygon_mode_config = PolygonModeConfig::default(); + bind_material_textures( + curr_gl_ctx, + material, + &mut renderer_object_store, + ); + } + RendererCommand::DrawMesh { mesh_asset } => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; - opengl_bindings::misc::set_polygon_mode( - ¤t_graphics_context, - default_polygon_mode_config.face, - default_polygon_mode_config.mode, - ); - } - } + let graphics_mesh = + match graphics_mesh_store.graphics_meshes.get(&mesh_asset.id()) { + Some(graphics_mesh) => graphics_mesh, + None => { + let Some(mesh) = assets.get(&mesh_asset) else { + tracing::trace!("Missing model asset"); + continue; + }; + + let graphics_mesh = + match GraphicsMesh::new(&curr_gl_ctx, mesh) { + Ok(graphics_mesh) => graphics_mesh, + Err(err) => { + tracing::error!( + "Failed to create {}: {err}", + type_name::<GraphicsMesh>() + ); + + continue; + } + }; + + graphics_mesh_store + .graphics_meshes + .entry(mesh_asset.id()) + .or_insert(graphics_mesh) + } + }; - if let Err(err) = window_graphics_surface - .surface - .swap_buffers(context.context()) - { - tracing::error!("Failed to swap buffers: {err}"); + if let Err(err) = draw_mesh(&curr_gl_ctx, graphics_mesh) { + tracing::error!("Failed to draw mesh: {err}"); + }; + } + RendererCommand::SetPolygonModeConfig(polygon_mode_config) => { + let Some(curr_gl_ctx) = &opt_curr_gl_ctx else { + tracing::error!("No GL context is current"); + continue; + }; + + opengl_bindings::misc::set_polygon_mode( + &curr_gl_ctx, + polygon_mode_config.face, + polygon_mode_config.mode, + ); + } + } } } } @@ -966,13 +945,41 @@ fn create_default_texture(current_context: &CurrentContextWithFns<'_>) -> GlText } } -fn create_bind_material_textures( +#[tracing::instrument(skip_all)] +fn create_texture_object( + curr_gl_ctx: &CurrentContextWithFns<'_>, + renderer_object_store: &mut RendererObjectStore, + texture_image_asset_id: AssetId, + image: &Image, + texture_properties: &TextureProperties, +) -> Result<(), GlTextureGenerateError> +{ + let object_id = RendererObjectId::Asset(texture_image_asset_id); + + if renderer_object_store.contains_with_id(&object_id) { + tracing::error!( + texture_object_id=?object_id, + "Renderer object store already contains object with this ID" + ); + return Ok(()); + } + + renderer_object_store.insert( + object_id, + RendererObject::from_raw( + create_gl_texture(curr_gl_ctx, image, texture_properties)?.into_raw(), + RendererObjectKind::Texture, + ), + ); + + Ok(()) +} + +fn bind_material_textures( current_context: &CurrentContextWithFns<'_>, material: &Material, - assets: &Assets, - texture_objs: &mut HashMap<AssetId, GlTexture>, - default_1x1_texture_obj: &mut Option<GlTexture>, -) -> Result<(), CreateBindMaterialTexturesError> + renderer_object_store: &mut RendererObjectStore, +) { let material_texture_maps = [ (&material.ambient_map, AMBIENT_MAP_TEXTURE_UNIT), @@ -982,55 +989,37 @@ fn create_bind_material_textures( for (texture, texture_unit) in material_texture_maps { let Some(texture) = texture else { - let gl_texture = default_1x1_texture_obj - .get_or_insert_with(|| create_default_texture(current_context)); + let default_texture_obj = renderer_object_store + .entry(DEFAULT_TEXTURE_OBJECT_ID) + .or_insert_with(|| { + RendererObject::from_raw( + create_default_texture(current_context).into_raw(), + RendererObjectKind::Texture, + ) + }); + + let gl_texture = GlTexture::from_raw(default_texture_obj.as_raw()); gl_texture.bind_to_texture_unit(current_context, texture_unit); continue; }; - let texture_image_asset_id = texture.asset_handle.id(); + let texture_object_id = RendererObjectId::Asset(texture.asset_handle.id()); - let gl_texture = match texture_objs.get(&texture_image_asset_id) { - Some(gl_texture) => gl_texture, - None => { - let Some(image) = assets.get::<Image>(&texture.asset_handle) else { - tracing::trace!(handle=?texture.asset_handle, "Missing texture asset"); - return Err(CreateBindMaterialTexturesError::MissingTextureAsset); - }; - - texture_objs.entry(texture_image_asset_id).or_insert( - create_gl_texture(current_context, image, &texture.properties) - .map_err(|err| { - CreateBindMaterialTexturesError::CreateTextureFailed { - err, - image_asset_id: texture_image_asset_id, - } - })?, - ) - } + let Some(texture_obj) = renderer_object_store.get_texture_obj(&texture_object_id) + else { + tracing::error!( + texture_object_id=?texture_object_id, + "Texture object does not exist" + ); + continue; }; + let gl_texture = GlTexture::from_raw(texture_obj.as_raw()); + gl_texture.bind_to_texture_unit(current_context, texture_unit); } - - Ok(()) -} - -#[derive(Debug, thiserror::Error)] -enum CreateBindMaterialTexturesError -{ - #[error("Missing texture asset")] - MissingTextureAsset, - - #[error("Failed to create texture from image asset with ID {image_asset_id:?}")] - CreateTextureFailed - { - #[source] - err: GlTextureGenerateError, - image_asset_id: AssetId, - }, } fn set_viewport( @@ -1179,12 +1168,6 @@ fn get_glsl_shader_content(path: &Path) -> Result<Vec<u8>, std::io::Error> )) } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -struct GraphicsMeshId -{ - inner: usize, -} - fn apply_transformation_matrices( current_context: &CurrentContextWithFns<'_>, transformation: Transformation, @@ -1227,31 +1210,13 @@ fn apply_transformation_matrices( ); } -fn apply_light<'point_light>( - current_context: &CurrentContextWithFns<'_>, - material: &Material, - material_flags: &MaterialFlags, - global_light: &GlobalLight, +fn set_shader_directional_lights( + curr_gl_ctx: &CurrentContextWithFns<'_>, gl_shader_program: &mut GlShaderProgram, - (point_light_iter, point_light_cnt): ( - impl Iterator< - Item = ( - ComponentHandle<'point_light, PointLight>, - ComponentHandle<'point_light, WorldPosition>, - ), - >, - usize, - ), - directional_lights: &[&DirectionalLight], - camera_world_pos: &WorldPosition, + directional_lights: &[DirectionalLight], ) { debug_assert!( - point_light_cnt < 64, - "Shader cannot handle more than 64 point lights" - ); - - debug_assert!( directional_lights.len() < 64, "Shader cannot handle more than 64 directional lights" ); @@ -1260,7 +1225,7 @@ fn apply_light<'point_light>( let direction: opengl_bindings::data_types::Vec3<_> = dir_light.direction.into(); gl_shader_program.set_uniform( - current_context, + curr_gl_ctx, &create_light_uniform_name( "directional_lights", dir_light_index, @@ -1270,36 +1235,48 @@ fn apply_light<'point_light>( ); set_light_phong_uniforms( - current_context, + curr_gl_ctx, gl_shader_program, "directional_lights", dir_light_index, - *dir_light, + dir_light, ); } // There probably won't be more than 2147483648 directional lights #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] gl_shader_program.set_uniform( - current_context, + curr_gl_ctx, c"directional_light_cnt", &(directional_lights.len() as i32), ); +} + +fn set_shader_point_lights( + curr_gl_ctx: &CurrentContextWithFns<'_>, + gl_shader_program: &mut GlShaderProgram, + point_lights: &[(PointLight, WorldPosition)], +) +{ + debug_assert!( + point_lights.len() < 64, + "Shader cannot handle more than 64 point lights" + ); for (point_light_index, (point_light, point_light_world_pos)) in - point_light_iter.enumerate() + point_lights.iter().enumerate() { let pos: opengl_bindings::data_types::Vec3<_> = (point_light_world_pos.position + point_light.local_position).into(); gl_shader_program.set_uniform( - current_context, + curr_gl_ctx, &create_light_uniform_name("point_lights", point_light_index, "position"), &pos, ); set_light_phong_uniforms( - current_context, + curr_gl_ctx, gl_shader_program, "point_lights", point_light_index, @@ -1307,7 +1284,7 @@ fn apply_light<'point_light>( ); set_light_attenuation_uniforms( - current_context, + curr_gl_ctx, gl_shader_program, "point_lights", point_light_index, @@ -1318,11 +1295,21 @@ fn apply_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( - current_context, + curr_gl_ctx, c"point_light_cnt", - &(point_light_cnt as i32), + &(point_lights.len() as i32), ); +} +fn set_shader_material( + curr_gl_ctx: &CurrentContextWithFns<'_>, + material: &Material, + material_flags: &MaterialFlags, + global_light: &GlobalLight, + gl_shader_program: &mut GlShaderProgram, + camera_world_pos: &WorldPosition, +) +{ let ambient: opengl_bindings::data_types::Vec3<_> = Vec3::from(if material_flags.use_ambient_color { material.ambient.clone() @@ -1331,49 +1318,49 @@ fn apply_light<'point_light>( }) .into(); - gl_shader_program.set_uniform(current_context, c"material.ambient", &ambient); + gl_shader_program.set_uniform(curr_gl_ctx, c"material.ambient", &ambient); let diffuse: opengl_bindings::data_types::Vec3<_> = Vec3::from(material.diffuse.clone()).into(); - gl_shader_program.set_uniform(current_context, c"material.diffuse", &diffuse); + gl_shader_program.set_uniform(curr_gl_ctx, c"material.diffuse", &diffuse); let specular: opengl_bindings::data_types::Vec3<_> = Vec3::from(material.specular.clone()).into(); #[allow(clippy::cast_possible_wrap)] - gl_shader_program.set_uniform(current_context, c"material.specular", &specular); + gl_shader_program.set_uniform(curr_gl_ctx, c"material.specular", &specular); #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform( - current_context, + curr_gl_ctx, c"material.ambient_map", &(AMBIENT_MAP_TEXTURE_UNIT as i32), ); #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform( - current_context, + curr_gl_ctx, c"material.diffuse_map", &(DIFFUSE_MAP_TEXTURE_UNIT as i32), ); #[allow(clippy::cast_possible_wrap)] gl_shader_program.set_uniform( - current_context, + curr_gl_ctx, c"material.specular_map", &(SPECULAR_MAP_TEXTURE_UNIT as i32), ); gl_shader_program.set_uniform( - current_context, + curr_gl_ctx, c"material.shininess", &material.shininess, ); let view_pos: opengl_bindings::data_types::Vec3<_> = camera_world_pos.position.into(); - gl_shader_program.set_uniform(current_context, c"view_pos", &view_pos); + gl_shader_program.set_uniform(curr_gl_ctx, c"view_pos", &view_pos); } fn set_light_attenuation_uniforms( diff --git a/engine/src/renderer/opengl/graphics_mesh.rs b/engine/src/renderer/opengl/graphics_mesh.rs index 9b929c0..dc839a8 100644 --- a/engine/src/renderer/opengl/graphics_mesh.rs +++ b/engine/src/renderer/opengl/graphics_mesh.rs @@ -1,9 +1,9 @@ +use opengl_bindings::CurrentContextWithFns as GlCurrentContextWithFns; use opengl_bindings::buffer::{Buffer as GlBuffer, Usage as GlBufferUsage}; use opengl_bindings::vertex_array::{ DataType as GlVertexArrayDataType, VertexArray as GlVertexArray, }; -use opengl_bindings::CurrentContextWithFns as GlCurrentContextWithFns; use crate::mesh::Mesh; use crate::renderer::opengl::vertex::{ @@ -15,7 +15,7 @@ use crate::renderer::opengl::vertex::{ pub struct GraphicsMesh { /// Vertex and index buffer has to live as long as the vertex array - _vertex_buffer: GlBuffer<RendererVertex>, + vertex_buffer: GlBuffer<RendererVertex>, pub index_buffer: Option<GlBuffer<u32>>, pub element_cnt: u32, pub vertex_arr: GlVertexArray, @@ -86,7 +86,7 @@ impl GraphicsMesh vertex_arr.bind_element_buffer(current_context, &index_buffer); return Ok(Self { - _vertex_buffer: vertex_buffer, + vertex_buffer: vertex_buffer, index_buffer: Some(index_buffer), element_cnt: indices .len() @@ -97,7 +97,7 @@ impl GraphicsMesh } Ok(Self { - _vertex_buffer: vertex_buffer, + vertex_buffer: vertex_buffer, index_buffer: None, element_cnt: mesh .vertices() @@ -107,6 +107,16 @@ impl GraphicsMesh vertex_arr, }) } + + pub fn destroy(&mut self, curr_gl_ctx: &GlCurrentContextWithFns<'_>) + { + self.vertex_arr.delete(curr_gl_ctx); + self.vertex_buffer.delete(curr_gl_ctx); + + if let Some(index_buffer) = &self.index_buffer { + index_buffer.delete(curr_gl_ctx); + } + } } #[derive(Debug, thiserror::Error)] diff --git a/engine/src/transform.rs b/engine/src/transform.rs index 7c0c941..05819bc 100644 --- a/engine/src/transform.rs +++ b/engine/src/transform.rs @@ -1,7 +1,46 @@ use ecs::Component; +use crate::builder; use crate::vector::Vec3; +builder!( + #[builder(name = Builder, derives=(Debug))] + #[derive(Debug)] + #[non_exhaustive] + pub struct Transform + { + pub position: Vec3<f32>, + pub scale: Vec3<f32>, + } +); + +impl Transform +{ + pub fn builder() -> Builder + { + Builder::default() + } +} + +impl Default for Transform +{ + fn default() -> Self + { + Self::builder().build() + } +} + +impl Default for Builder +{ + fn default() -> Self + { + Self { + position: Vec3::from(0.0), + scale: Vec3::from(1.0), + } + } +} + /// A position in world space. #[derive(Debug, Default, Clone, Copy, Component)] pub struct WorldPosition diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs index 69adae9..2bfdb31 100644 --- a/engine/src/windowing.rs +++ b/engine/src/windowing.rs @@ -195,6 +195,29 @@ fn update_stuff( actions.remove_comps::<(Window,)>(*window_ent_id); } + MessageFromApp::WindowScaleFactorChanged(window_id, scale_factor) => { + let Some(window_ent_id) = + windows.get(&window_id).map(|(_, ent_id)| ent_id) + else { + tracing::error!( + wid = ?window_id, + "Window does not exist in windowing context" + ); + continue; + }; + + let Some(window_ent) = entity_obtainer.get_entity(*window_ent_id) else { + continue; + }; + + let Some(mut window) = window_ent.get_mut::<Window>() else { + continue; + }; + + window.set_scale_factor(scale_factor); + + window.set_changed(); + } MessageFromApp::KeyboardKeyStateChanged(key, key_state) => { keyboard.set_key_state(key, key_state); } @@ -405,6 +428,7 @@ enum MessageFromApp WindowCreated(Uid, Arc<WinitWindow>, WindowCreationAttributes), WindowResized(WindowId, Dimens<u32>), WindowCloseRequested(WindowId), + WindowScaleFactorChanged(WindowId, f64), KeyboardKeyStateChanged(Key, KeyState), MouseMoved { @@ -605,6 +629,12 @@ impl ApplicationHandler for App self.focused_window_id = None; } } + WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer: _ } => { + self.send_message(MessageFromApp::WindowScaleFactorChanged( + WindowId::from_inner(window_id), + scale_factor, + )); + } _ => {} } } diff --git a/engine/src/windowing/window.rs b/engine/src/windowing/window.rs index 79b2102..627bdec 100644 --- a/engine/src/windowing/window.rs +++ b/engine/src/windowing/window.rs @@ -87,11 +87,12 @@ pub struct CreationReady; #[non_exhaustive] pub struct Window { - wid: Id, pub title: Cow<'static, str>, pub cursor_visible: bool, pub cursor_grab_mode: CursorGrabMode, + wid: Id, inner_size: Dimens<u32>, + scale_factor: f64, } impl Window @@ -106,17 +107,23 @@ impl Window &self.inner_size } + pub fn scale_factor(&self) -> f64 + { + self.scale_factor + } + pub(crate) fn new( winit_window: &winit::window::Window, creation_attrs: &CreationAttributes, ) -> Self { Self { - wid: Id::from_inner(winit_window.id()), title: creation_attrs.title().to_string().into(), cursor_visible: true, cursor_grab_mode: CursorGrabMode::None, + wid: Id::from_inner(winit_window.id()), inner_size: winit_window.inner_size().into(), + scale_factor: winit_window.scale_factor(), } } @@ -130,6 +137,11 @@ impl Window { self.inner_size = inner_size; } + + pub(crate) fn set_scale_factor(&mut self, scale_factor: f64) + { + self.scale_factor = scale_factor; + } } #[derive(Debug, Component)] |
