diff options
| author | HampusM <hampus@hampusmat.com> | 2026-03-20 14:22:19 +0100 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-03-20 14:22:19 +0100 |
| commit | f285f82072b491b1f3cc92db8e08485f26779d5a (patch) | |
| tree | bf6c6c61cdfb3a12550e55966c8552957ade9e71 /engine/src/renderer.rs | |
| parent | 0546d575c11d3668d0f95933697ae4f670fe2a55 (diff) | |
Diffstat (limited to 'engine/src/renderer.rs')
| -rw-r--r-- | engine/src/renderer.rs | 299 |
1 files changed, 147 insertions, 152 deletions
diff --git a/engine/src/renderer.rs b/engine/src/renderer.rs index 2a66a68..6c20102 100644 --- a/engine/src/renderer.rs +++ b/engine/src/renderer.rs @@ -1,5 +1,7 @@ use std::any::type_name; use std::collections::VecDeque; +use std::path::Path; +use std::sync::LazyLock; use std::sync::atomic::{AtomicU64, Ordering}; use bitflags::bitflags; @@ -10,18 +12,28 @@ use ecs::query::term::Without; use ecs::sole::Single; use ecs::{Component, Query, declare_entity}; -use crate::asset::{Assets, Handle as AssetHandle}; +use crate::asset::{Assets, Handle as AssetHandle, Label as AssetLabel}; use crate::builder; -use crate::camera::{Active as ActiveCamera, Camera}; +use crate::color::Color; use crate::data_types::dimens::Dimens; use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig}; -use crate::lighting::{DirectionalLight, GlobalLight, PointLight}; -use crate::material::{Flags as MaterialFlags, Material}; +use crate::image::Image; use crate::mesh::Mesh; -use crate::model::{Materials as ModelMaterials, Model, Spec as ModelSpec}; +use crate::model::{MaterialSearchResult, Model}; use crate::renderer::object::{Id as ObjectId, Store as ObjectStore}; -use crate::texture::Texture; -use crate::transform::{Scale, Transform, WorldPosition}; +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::{Properties as TextureProperties, Texture}; use crate::windowing::window::Window; pub mod object; @@ -29,8 +41,14 @@ pub mod opengl; static NEXT_SURFACE_ID: AtomicU64 = AtomicU64::new(0); +pub static DEFAULT_TEXTURE_ASSET_LABEL: LazyLock<AssetLabel> = + LazyLock::new(|| AssetLabel { + path: Path::new("").into(), + name: Some("default_texture".into()), + }); + declare_entity!( - pub RENDER_PHASE, + pub PRE_RENDER_PHASE, ( Phase, Pair::builder() @@ -40,6 +58,17 @@ declare_entity!( ) ); +declare_entity!( + pub RENDER_PHASE, + ( + Phase, + Pair::builder() + .relation::<ChildOf>() + .target_id(*PRE_RENDER_PHASE) + .build() + ) +); + builder! { /// Window graphics properties. #[builder(name=GraphicsPropertiesBuilder, derives=(Debug, Clone))] @@ -131,23 +160,10 @@ 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)>), + CreateShaderProgram(ObjectId, ShaderProgram), + ActivateShader(ObjectId), + SetShaderBinding(ShaderBindingLocation, ShaderBindingValue), CreateTexture(Texture), - UseMaterial - { - material_asset: Option<AssetHandle<Material>>, - material_flags: MaterialFlags, - global_light: GlobalLight, - }, DrawMesh { mesh_asset: AssetHandle<Mesh>, @@ -202,12 +218,19 @@ pub struct CtxUsedByWindow; type RenderableEntity<'a> = ( &'a Model, - Option<&'a MaterialFlags>, - Option<&'a WorldPosition>, - Option<&'a Scale>, Option<&'a DrawFlags>, + Option<&'a Shader>, + Option<&'a mut PendingShaderBindings>, ); +pub fn init(mut assets: Single<Assets>) +{ + assets.store_with_label( + DEFAULT_TEXTURE_ASSET_LABEL.clone(), + Image::from_color(Dimens { width: 1, height: 1 }, Color::WHITE_U8), + ); +} + #[tracing::instrument(skip_all)] pub fn enqueue_commands( renderer_ctx_query: Query<( @@ -216,16 +239,15 @@ pub fn enqueue_commands( &[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>, + shader_context: Single<ShaderContext>, mut actions: Actions, ) { - let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else { - tracing::warn!("No current camera. Nothing will be rendered"); + 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; }; @@ -240,7 +262,7 @@ pub fn enqueue_commands( continue; }; - let Some(window) = window_ent.get::<Window>() else { + if window_ent.get::<Window>().is_none() { tracing::debug!( window_entity_id=%window_ent_id, "Window entity does not have a {} component", @@ -270,15 +292,38 @@ pub fn enqueue_commands( command_queue.push(Command::MakeCurrent(surface_spec.id)); - command_queue - .push(Command::UseCamera(camera.clone(), camera_world_pos.clone())); + let default_texture_asset = assets + .get_handle_to_loaded::<Image>(DEFAULT_TEXTURE_ASSET_LABEL.clone()) + .expect("Not possible"); + + if !object_store + .contains_with_id(&ObjectId::Asset(default_texture_asset.id())) + { + command_queue.push(Command::CreateTexture(Texture { + asset_handle: default_texture_asset, + properties: TextureProperties::default(), + })); + } command_queue.push(Command::ClearBuffers( BufferClearMask::COLOR | BufferClearMask::DEPTH, )); - for (model, material_flags, world_pos, scale, draw_flags) in &renderable_query + 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; }; @@ -289,103 +334,69 @@ pub fn enqueue_commands( 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, - }; + 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 let Some(model_material_asset) = &model_material_asset { - let Some(model_material) = assets.get(model_material_asset) else { - unreachable!(); + 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; }; - if let Some(ambient_map) = &model_material.ambient_map { - if assets.get(&ambient_map.asset_handle).is_none() { - continue; - } - } + command_queue.push(Command::CreateShaderProgram( + ObjectId::Asset(shader_asset.id()), + shader_program.clone(), + )); + } - if let Some(diffuse_map) = &model_material.diffuse_map { - if assets.get(&diffuse_map.asset_handle).is_none() { - continue; - } - } + command_queue + .push(Command::ActivateShader(ObjectId::Asset(shader_asset.id()))); - if let Some(specular_map) = &model_material.specular_map { - if assets.get(&specular_map.asset_handle).is_none() { - continue; - } + let Some(model_material) = assets.get(&model_material_asset) else { + // TODO: Handle this case since it may occur + unreachable!(); + }; + + for texture 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_handle.id())) + { + command_queue.push(Command::CreateTexture(texture.clone())); } } - 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 let Some(pending_shader_bindings) = &mut pending_shader_bindings { + for (shader_binding_loc, shader_binding_val) in + pending_shader_bindings.bindings.drain(..) { - if !object_store - .contains_with_id(&ObjectId::Asset(texture.asset_handle.id())) - { - command_queue.push(Command::CreateTexture(texture.clone())); - } + command_queue.push(Command::SetShaderBinding( + shader_binding_loc, + shader_binding_val, + )); } } - 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() { @@ -410,39 +421,23 @@ pub fn enqueue_commands( } } -enum MaterialSearchResult<'a> +#[derive(Default, Clone, Component)] +pub struct PendingShaderBindings { - Found(&'a AssetHandle<Material>), - NotFound, - NoMaterials, + pub bindings: Vec<(ShaderBindingLocation, ShaderBindingValue)>, } -fn find_first_model_material<'assets>( - model_spec: &'assets ModelSpec, - assets: &'assets Assets, -) -> MaterialSearchResult<'assets> +impl<'a> Extend<(ShaderCursor<'a>, ShaderBindingValue)> for PendingShaderBindings { - 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; + fn extend<Iter: IntoIterator<Item = (ShaderCursor<'a>, ShaderBindingValue)>>( + &mut self, + iter: Iter, + ) + { + self.bindings.extend(iter.into_iter().map( + |(shader_cursor, shader_binding_val)| { + (shader_cursor.into_binding_location(), shader_binding_val) + }, + )) } - - MaterialSearchResult::Found(material_asset) } |
