use ecs::Query; 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, DrawMeshOptions as RendererDrawMeshOptions, DrawProperties as RendererDrawProperties, DrawPropertiesUpdateFlags as RendererDrawPropertiesUpdateFlags, MeshUsage as RendererMeshUsage, PendingShaderBindings, RenderPass, RenderPasses as RendererRenderPasses, SurfaceSpec, }; 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( renderable_query: Query, (Without,)>, window_surface_spec_query: Query<(&SurfaceSpec,), (With,)>, assets: Single, shader_context: Single, mut render_passes: Single, mut object_store: Single, ) { 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 (surface_spec,) in &window_surface_spec_query { let render_pass = render_passes.passes.push_front_mut(RenderPass { surface_id: surface_spec.id, commands: Vec::with_capacity(30), draw_properties: RendererDrawProperties::default(), }); 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_maybe_pending_with_id(&RendererObjectId::Asset( default_texture_asset.id(), )) { object_store .insert_pending(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_maybe_pending_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; }; object_store.insert_pending(RendererObjectId::Asset(shader_asset.id())); 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_maybe_pending_with_id(&RendererObjectId::Asset( texture_asset.id(), )) { object_store .insert_pending(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_maybe_pending_with_id(&RendererObjectId::Asset(mesh_asset.id())) { object_store.insert_pending(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, )); } } } }