use crate::asset::Assets; use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig}; use crate::ecs::query::term::{With, Without}; use crate::ecs::sole::Single; use crate::ecs::Query; use crate::model::{MaterialSearchResult, Model}; use crate::rendering::object::{Id as ObjectId, Store as ObjectStore}; use crate::rendering::{ BufferClearMask, Command, DrawMeshOptions, DrawProperties, DrawPropertiesUpdateFlags, MeshUsage, PendingShaderBindings, RenderPass, RenderPasses, 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; }; let render_pass = render_passes.passes.push_front_mut(RenderPass { commands: Vec::with_capacity(30), draw_properties: DrawProperties::default(), }); for (surface_spec,) in &window_surface_spec_query { render_pass .commands .push(Command::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(&ObjectId::Asset(default_texture_asset.id())) { object_store.insert_pending(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, }; let Some(pending_shader_bindings) = pending_shader_bindings.as_mut() else { continue; }; if pending_shader_bindings.bindings.is_empty() && pending_shader_bindings.surface_specific_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(&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; }; object_store.insert_pending(ObjectId::Asset(shader_asset.id())); 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_maybe_pending_with_id(&ObjectId::Asset(texture_asset.id())) { object_store.insert_pending(ObjectId::Asset(texture_asset.id())); render_pass .commands .push(Command::CreateTexture(texture_asset.clone())); } } for (shader_binding_loc, shader_binding_val) in &pending_shader_bindings.bindings { render_pass.commands.push(Command::SetShaderBinding( shader_binding_loc.clone(), shader_binding_val.clone(), )); } for (shader_binding_surface_id, shader_binding_loc, shader_binding_val) in &pending_shader_bindings.surface_specific_bindings { if *shader_binding_surface_id != surface_spec.id { continue; } render_pass.commands.push(Command::SetShaderBinding( shader_binding_loc.clone(), shader_binding_val.clone(), )); } if let Some(draw_flags) = draw_flags.as_deref().and_then(|draw_flags| { if draw_flags.polygon_mode_config != PolygonModeConfig::default() { Some(draw_flags) } else { None } }) { 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_maybe_pending_with_id(&ObjectId::Asset(mesh_asset.id())) { object_store.insert_pending(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 draw_flags.as_deref().is_some_and(|draw_flags| { 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, )); } } } }