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], ), (With,), >, renderable_query: Query, (Without,)>, assets: Single, shader_context: Single, mut render_passes: Single, mut actions: Actions, ) { let Some(default_shader_asset) = assets .get_handle_to_loaded::(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::().is_none() { tracing::debug!( window_entity_id=%window_ent_id, "Window entity does not have a {} component", type_name::() ); actions.remove_components( renderer_ctx_ent_id, [Pair::builder() .relation::() .target_id(window_ent_id) .build() .id()], ); continue; }; let Some(surface_spec) = window_ent.get::() else { tracing::debug!( window_entity_id=%window_ent_id, "Window entity does not have a {} component", type_name::() ); 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_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, )); } } } } }