From 254dd8048a4c8ea124e7edf727d9bad0a7b33d14 Mon Sep 17 00:00:00 2001 From: HampusM Date: Tue, 27 Oct 2026 08:57:36 +0100 Subject: refactor(engine): rename rendering::main_render_pass mod to main_pipeline --- engine/src/rendering.rs | 4 +- engine/src/rendering/main_pipeline.rs | 1000 ++++++++++++++++++++++++++++++ engine/src/rendering/main_render_pass.rs | 1000 ------------------------------ 3 files changed, 1002 insertions(+), 1002 deletions(-) create mode 100644 engine/src/rendering/main_pipeline.rs delete mode 100644 engine/src/rendering/main_render_pass.rs (limited to 'engine/src') diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs index 747fdfa..bbfee72 100644 --- a/engine/src/rendering.rs +++ b/engine/src/rendering.rs @@ -32,7 +32,7 @@ use crate::windowing::window::Window; pub mod backend; pub mod blending; -pub mod main_render_pass; +pub mod main_pipeline; pub mod object; pub mod shader; @@ -80,7 +80,7 @@ impl crate::ecs::extension::Extension for Extension collector.add_system( *PRE_RENDER_PHASE, - main_render_pass::add_main_render_pass + main_pipeline::add_passes .into_system() .initialize(Default::default()), ); diff --git a/engine/src/rendering/main_pipeline.rs b/engine/src/rendering/main_pipeline.rs new file mode 100644 index 0000000..66030ab --- /dev/null +++ b/engine/src/rendering/main_pipeline.rs @@ -0,0 +1,1000 @@ +use std::path::Path; +use std::sync::LazyLock; + +use ecs::actions::Actions; +use ecs::component::local::Local; +use ecs::error::Context as _; +use ecs::pair::ChildOf; +use ecs::query::term::{Traverse, TraverseUp}; +use ecs::uid::Uid; +use ecs::Component; + +use crate::asset::{Assets, Label as AssetLabel}; +use crate::camera::{Active as ActiveCamera, Camera}; +use crate::color::Rgba; +use crate::data_types::dimens::{Dimens, Dimens3}; +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::error; +use crate::image::Image; +use crate::lighting::{ + DirectionalLight, + Environmental as EnvironmentalLighting, + PointLight, +}; +use crate::material::{Flags as MaterialFlags, Material}; +use crate::mesh::Mesh; +use crate::model::{ModelMaterial, ModelMesh}; +use crate::projection::{ClipVolume as ProjectionClipVolume, Perspective, Projection}; +use crate::rendering::object::{Id as ObjectId, Store as ObjectStore}; +use crate::rendering::shader::cursor::{ + BindingTextureKind as ShaderBindingTextureKind, + BindingValue as ShaderBindingValue, + Cursor as ShaderCursor, +}; +use crate::rendering::shader::{ + Context as ShaderContext, + EntrypointFlags as ShaderEntrypointFlags, + ModuleSource as ShaderModuleSource, + Program as ShaderProgram, +}; +use crate::rendering::{ + AssetOrValue, + BufferClearMask, + Command, + DepthFunction, + DrawMeshOptions, + DrawProperties, + DrawPropertiesUpdateFlags, + MeshUsage, + RenderPass, + RenderPasses, + RgbaTextureDataType, + Surface, + TargetWindow, + TextureCreation, + TexturePixelDataFormat, +}; +use crate::scene::{Active as ActiveScene, Scene}; +use crate::sky_box::SkyBox; +use crate::texture::{ + Filtering as TextureFiltering, + Properties as TextureProperties, + Texture, + Wrapping as TextureWrapping, +}; +use crate::transform::Transform; +use crate::vector::Vec3; +use crate::windowing::window::Window; + +type RenderableEntity<'a> = ( + &'a ModelMesh, + Option<&'a ModelMaterial>, + Traverse<(With, With), TraverseUp, ChildOf>, + Option<&'a MaterialFlags>, + Option<&'a Transform>, + Option<&'a DrawFlags>, + Without, +); + +pub static SKY_BOX_SHADER_ASSET_LABEL: LazyLock = + LazyLock::new(|| AssetLabel { + path: Path::new("").into(), + name: Some("sky_box_shader".into()), + }); + +pub static MAIN_3D_SHADER_ASSET_LABEL: LazyLock = + LazyLock::new(|| AssetLabel { + path: Path::new("").into(), + name: Some("main_3d_shader".into()), + }); + +struct SkyBoxIds +{ + texture_object: ObjectId, + mesh_object: ObjectId, +} + +#[derive(Debug, Default, Component)] +pub struct MiscObjectIds +{ + white_1x1_tex_obj_id: Option, +} + +#[tracing::instrument(skip_all)] +pub fn add_passes( + renderable_query: Query>, + camera_query: Query<( + &Camera, + &Transform, + &ActiveCamera, + Traverse<(With, With), TraverseUp, ChildOf>, + )>, + window_query: Query<(&Window, &Surface, With)>, + scene_query: Query<( + Option<&EnvironmentalLighting>, + Option<&SkyBox>, + Option<&SkyBoxState>, + With, + With, + )>, + point_light_query: Query<( + &PointLight, + &Transform, + Traverse<(With, With), TraverseUp, ChildOf>, + )>, + directional_light_query: Query<( + &DirectionalLight, + Traverse<(With, With), TraverseUp, ChildOf>, + )>, + shader_context: Single, + mut assets: Single, + mut render_passes: Single, + mut object_store: Single, + mut actions: Actions, + mut misc_object_ids: Local, +) -> Result<(), crate::Error> +{ + let assets = assets.get_mut()?; + let shader_context = shader_context.get()?; + let render_passes = render_passes.get_mut()?; + let object_store = object_store.get_mut()?; + + let Some((scene_ent_id, (scene_env_lighting, scene_skybox, scene_skybox_state))) = + scene_query.iter_with_euids().next() + else { + return Ok(()); + }; + + let Some((camera, camera_transform, _)) = camera_query.iter().next() else { + tracing::trace!("No active camera"); + return Ok(()); + }; + + let render_pass = render_passes.passes.push_front_mut(RenderPass { + commands: Vec::with_capacity(30), + draw_properties: DrawProperties::default(), + }); + + for (mesh, material, ..) in &renderable_query { + add_renderable_creation_commands( + assets, + object_store, + render_pass, + &mesh, + material.as_deref(), + ); + } + + let shaders = + match get_or_create_shaders(assets, shader_context, object_store, render_pass) { + Ok(ShadersCreationStatus::Ready(ready_shaders)) => ready_shaders, + Ok(ShadersCreationStatus::ProcessingRequired) => return Ok(()), + Err(err) => { + tracing::error!("Failed to create shaders: {err}"); + return Ok(()); + } + }; + + let white_1x1_tex_obj_id = + *misc_object_ids.white_1x1_tex_obj_id.get_or_insert_with(|| { + create_white_1x1_texture_object(object_store, render_pass) + }); + + let main_3d_shader_cursor = ShaderCursor::new( + shaders + .main_3d_shader_program + .reflection(0) + .context("Unable to get reflection for main 3D shader")? + .global_params_var_layout() + .ok_or_else(|| { + crate::Error::message( + "Unable to get reflection for main 3D shader's global parameters", + ) + })?, + ); + + add_set_3d_shader_point_light_bindings( + render_pass, + &point_light_query, + shaders.main_3d_shader_obj_id, + &main_3d_shader_cursor, + )?; + + add_set_3d_shader_dir_lights_bindings( + render_pass, + &directional_light_query, + shaders.main_3d_shader_obj_id, + &main_3d_shader_cursor, + )?; + + for (window, window_surface) in &window_query { + render_pass + .commands + .push(Command::MakeCurrent(window_surface.id)); + + add_set_3d_shader_camera_bindings( + render_pass, + (&camera, &camera_transform), + &window, + shaders.main_3d_shader_obj_id, + &main_3d_shader_cursor, + )?; + + let mut buf_clear_mask = BufferClearMask::DEPTH; + + buf_clear_mask.set(BufferClearMask::COLOR, scene_skybox.is_none()); + + render_pass + .commands + .push(Command::ClearBuffers(buf_clear_mask)); + + for (mesh, material, material_flags, transform, draw_flags) in &renderable_query { + if assets.get(&mesh.asset).is_none() { + continue; + } + + let model_material = match material { + Some(material) + if let Some(material_desc) = assets.get(&material.asset) => + { + match assets.get(&material_desc.asset) { + Some(material) => material, + None => continue, + } + } + Some(_) => { + continue; + } + None => &Material::default(), + }; + + if model_material + .textures() + .any(|texture_asset| !assets.is_loaded_and_has_type(texture_asset)) + { + continue; + } + + add_set_3d_shader_renderable_bindings( + render_pass, + ( + model_material, + material_flags.as_deref(), + transform.as_deref(), + ), + scene_env_lighting.as_deref(), + shaders.main_3d_shader_obj_id, + &main_3d_shader_cursor, + white_1x1_tex_obj_id, + )?; + + render_pass + .commands + .push(Command::ActivateShader(shaders.main_3d_shader_obj_id)); + + if let Some(draw_flags) = draw_flags.as_deref().filter(|&draw_flags| { + draw_flags.polygon_mode_config != PolygonModeConfig::default() + }) { + render_pass.commands.push(Command::UpdateDrawProperties( + DrawProperties { + polygon_mode_config: draw_flags.polygon_mode_config.clone(), + ..Default::default() + }, + DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG, + )); + } + + 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, + )); + } + } + + if let Some(scene_skybox) = &scene_skybox { + let Some(sky_box_ids) = load_sky_box( + scene_ent_id, + scene_skybox, + scene_skybox_state.as_deref(), + assets, + object_store, + render_pass, + &mut actions, + )? + else { + continue; + }; + + add_sky_box_commands( + render_pass, + &sky_box_ids, + &camera, + &camera_transform, + &window, + shaders.sky_box_shader_obj_id, + shaders.sky_box_shader_program, + )?; + } + } + + Ok(()) +} + +fn create_white_1x1_texture_object( + object_store: &mut ObjectStore, + render_pass: &mut RenderPass, +) -> ObjectId +{ + let white_1x1_tex_obj_id = ObjectId::new_sequential(); + + object_store.insert_pending(white_1x1_tex_obj_id); + + render_pass.commands.push(Command::CreateTexture { + obj_id: white_1x1_tex_obj_id, + pixel_data_format: TexturePixelDataFormat::Rgba( + RgbaTextureDataType::UnsignedByte, + ), + creation: TextureCreation::Texture2D { + size: Dimens { width: 1, height: 1 }, + image: Some( + Image::from_color(Rgba::::white(), Dimens { width: 1, height: 1 }) + .into_bytes() + .into_boxed_slice(), + ), + }, + properties: TextureProperties::default(), + }); + + white_1x1_tex_obj_id +} + +#[derive(Debug)] +enum ShadersCreationStatus<'shader_ctx> +{ + ProcessingRequired, + Ready(ReadyShaders<'shader_ctx>), +} + +#[derive(Debug)] +struct ReadyShaders<'shader_ctx> +{ + main_3d_shader_obj_id: ObjectId, + main_3d_shader_program: &'shader_ctx ShaderProgram, + sky_box_shader_obj_id: ObjectId, + sky_box_shader_program: &'shader_ctx ShaderProgram, +} + +fn get_or_create_shaders<'shader_ctx>( + assets: &mut Assets, + shader_context: &'shader_ctx ShaderContext, + object_store: &mut ObjectStore, + render_pass: &mut RenderPass, +) -> Result, crate::Error> +{ + let main_3d_shader_asset = assets + .get_handle_to_loaded::(MAIN_3D_SHADER_ASSET_LABEL.clone()) + .or_else(|| { + assets.store_with_label( + MAIN_3D_SHADER_ASSET_LABEL.clone(), + ShaderModuleSource { + name: "main_3d_shader.slang".into(), + file_path: Path::new("@engine/main_3d_shader").into(), + source: include_str!("../../res/main_3d_shader.slang").into(), + link_entrypoints: ShaderEntrypointFlags::VERTEX + | ShaderEntrypointFlags::FRAGMENT, + }, + ); + + None + }); + + let sky_box_shader_asset = assets + .get_handle_to_loaded::(SKY_BOX_SHADER_ASSET_LABEL.clone()) + .or_else(|| { + assets.store_with_label( + SKY_BOX_SHADER_ASSET_LABEL.clone(), + ShaderModuleSource { + name: "sky_box_shader.slang".into(), + file_path: Path::new("@engine/sky_box_shader").into(), + source: include_str!("../../res/sky_box_shader.slang").into(), + link_entrypoints: ShaderEntrypointFlags::VERTEX + | ShaderEntrypointFlags::FRAGMENT, + }, + ); + None + }); + + let (Some(main_3d_shader_asset), Some(sky_box_shader_asset)) = + (main_3d_shader_asset, sky_box_shader_asset) + else { + return Ok(ShadersCreationStatus::ProcessingRequired); + }; + + let main_3d_shader_program = shader_context.get_program(&main_3d_shader_asset.id()); + + let main_3d_shader_program = main_3d_shader_program.ok_or_else(|| { + error!("Shader context doesn't have a program for main 3D shader") + })?; + + let main_3d_shader_obj_id = ObjectId::Asset(main_3d_shader_asset.id()); + + if !object_store.contains_maybe_pending_with_id(&main_3d_shader_obj_id) { + object_store.insert_pending(main_3d_shader_obj_id); + + render_pass.commands.push(Command::CreateShaderProgram( + main_3d_shader_obj_id, + main_3d_shader_program.clone(), + )); + } + + let sky_box_shader_program = shader_context.get_program(&sky_box_shader_asset.id()); + + let sky_box_shader_program = sky_box_shader_program.ok_or_else(|| { + error!("Shader context doesn't have a program for sky box shader") + })?; + + let sky_box_shader_obj_id = ObjectId::Asset(sky_box_shader_asset.id()); + + if !object_store.contains_maybe_pending_with_id(&sky_box_shader_obj_id) { + object_store.insert_pending(sky_box_shader_obj_id); + + render_pass.commands.push(Command::CreateShaderProgram( + sky_box_shader_obj_id, + sky_box_shader_program.clone(), + )); + } + + Ok(ShadersCreationStatus::Ready(ReadyShaders { + main_3d_shader_obj_id, + main_3d_shader_program, + sky_box_shader_obj_id, + sky_box_shader_program, + })) +} + +fn add_renderable_creation_commands( + assets: &Assets, + object_store: &mut ObjectStore, + render_pass: &mut RenderPass, + mesh: &ModelMesh, + material: Option<&ModelMaterial>, +) +{ + if assets.get(&mesh.asset).is_none() { + return; + } + + let model_material = match material { + Some(material) if let Some(material_desc) = assets.get(&material.asset) => { + match assets.get(&material_desc.asset) { + Some(material) => material, + None => return, + } + } + Some(_) => { + return; + } + None => &Material::default(), + }; + + // debug_assert!(model_spec.materials.len() <= 1); + + for texture_asset in model_material.textures() { + let Some(texture) = assets.get(texture_asset) else { + return; + }; + + let Texture::Texture2D(texture) = texture else { + tracing::error!( + texture_asset_id = ?texture_asset.id(), + texture_asset_label = + assets.get_label(texture_asset).map(tracing::field::display), + "Material texture map is not 2D" + ); + return; + }; + + let texture_object_id = ObjectId::Asset(texture_asset.id()); + + if object_store.contains_maybe_pending_with_id(&texture_object_id) { + return; + } + + let Some(tex_pixel_data_format) = + TexturePixelDataFormat::for_image(&texture.image) + else { + tracing::error!("No texture pixel data format is available for image"); + return; + }; + + object_store.insert_pending(texture_object_id); + + render_pass.commands.push(Command::CreateTexture { + obj_id: texture_object_id, + pixel_data_format: tex_pixel_data_format, + creation: TextureCreation::Texture2D { + size: texture.image.dimensions(), + image: Some(texture.image.as_bytes().to_vec().into_boxed_slice()), + }, + properties: texture.properties.clone(), + }); + } + + let mesh_obj_id = ObjectId::Asset(mesh.asset.id()); + + if !object_store.contains_maybe_pending_with_id(&mesh_obj_id) { + object_store.insert_pending(mesh_obj_id); + + render_pass.commands.push(Command::CreateMesh { + obj_id: mesh_obj_id, + mesh: AssetOrValue::Asset(mesh.asset.clone()), + usage: MeshUsage::Static, + }); + } +} + +fn add_set_3d_shader_point_light_bindings( + render_pass: &mut RenderPass, + point_light_query: &Query<( + &PointLight, + &Transform, + Traverse<(With, With), TraverseUp, ChildOf>, + )>, + main_3d_shader_obj_id: ObjectId, + main_3d_shader_cursor: &ShaderCursor<'_>, +) -> Result<(), crate::Error> +{ + let lighting_shader_cursor = + main_3d_shader_cursor.field("Uniforms").field("lighting"); + + render_pass + .commands + .reserve(1 + point_light_query.iter().count() * 6); + + render_pass.commands.push(Command::SetShaderBinding( + main_3d_shader_obj_id, + lighting_shader_cursor.field("point_light_cnt").binding( + u32::try_from(point_light_query.iter().count()) + .expect("Point light count does not fit in 32-bit unsigned integer") + .into(), + )?, + )); + + for (point_light_index, (point_light, point_light_transform)) in + point_light_query.iter().enumerate() + { + let point_light_shader_cursor = lighting_shader_cursor + .field("point_lights") + .element(point_light_index); + + let phong_shader_cursor = point_light_shader_cursor.field("phong"); + + let attenuation_props_shader_cursor = + point_light_shader_cursor.field("attenuation_props"); + + render_pass.commands.extend( + [ + phong_shader_cursor + .field("diffuse") + .binding(point_light.diffuse.to_rgb_lossy().into())?, + phong_shader_cursor + .field("specular") + .binding(point_light.specular.to_rgb_lossy().into())?, + point_light_shader_cursor.field("position").binding( + (point_light_transform.position + point_light.local_position).into(), + )?, + attenuation_props_shader_cursor + .field("constant") + .binding(point_light.attenuation_params.constant.into())?, + attenuation_props_shader_cursor + .field("linear") + .binding(point_light.attenuation_params.linear.into())?, + attenuation_props_shader_cursor + .field("quadratic") + .binding(point_light.attenuation_params.quadratic.into())?, + ] + .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)), + ); + } + + Ok(()) +} + +fn add_set_3d_shader_dir_lights_bindings( + render_pass: &mut RenderPass, + directional_light_query: &Query<( + &DirectionalLight, + Traverse<(With, With), TraverseUp, ChildOf>, + )>, + main_3d_shader_obj_id: ObjectId, + main_3d_shader_cursor: &ShaderCursor<'_>, +) -> Result<(), crate::Error> +{ + let lighting_shader_cursor = + main_3d_shader_cursor.field("Uniforms").field("lighting"); + + render_pass + .commands + .reserve(1 + directional_light_query.iter().count() * 3); + + render_pass.commands.push(Command::SetShaderBinding( + main_3d_shader_obj_id, + lighting_shader_cursor + .field("directional_light_cnt") + .binding( + u32::try_from(directional_light_query.iter().count()) + .expect( + "Directional light count does not fit in 32-bit unsigned integer", + ) + .into(), + )?, + )); + + for (directional_light_index, (directional_light,)) in + directional_light_query.iter().enumerate() + { + let directional_light_shader_cursor = lighting_shader_cursor + .field("directional_lights") + .element(directional_light_index); + + let phong_shader_cursor = directional_light_shader_cursor.field("phong"); + + render_pass.commands.extend( + [ + phong_shader_cursor + .field("diffuse") + .binding(directional_light.diffuse.to_rgb_lossy().into())?, + phong_shader_cursor + .field("specular") + .binding(directional_light.specular.to_rgb_lossy().into())?, + directional_light_shader_cursor + .field("direction") + .binding(directional_light.direction.into())?, + ] + .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)), + ); + } + + Ok(()) +} + +fn add_set_3d_shader_camera_bindings( + render_pass: &mut RenderPass, + (camera, camera_transform): (&Camera, &Transform), + window: &Window, + main_3d_shader_obj_id: ObjectId, + main_3d_shader_cursor: &ShaderCursor<'_>, +) -> Result<(), crate::Error> +{ + let model_3d_shader_cursor = + main_3d_shader_cursor.field("Uniforms").field("model_3d"); + + let lighting_shader_cursor = + main_3d_shader_cursor.field("Uniforms").field("lighting"); + + render_pass.commands.extend( + [ + model_3d_shader_cursor + .field("view") + .binding(camera.to_view_matrix(camera_transform.position).into())?, + model_3d_shader_cursor.field("projection").binding( + camera + .projection + .to_matrix_rh(window.inner_size, ProjectionClipVolume::NegOneToOne) + .into(), + )?, + lighting_shader_cursor + .field("view_pos") + .binding(camera_transform.position.into())?, + ] + .into_iter() + .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)), + ); + + Ok(()) +} + +fn add_set_3d_shader_renderable_bindings( + render_pass: &mut RenderPass, + renderable: (&Material, Option<&MaterialFlags>, Option<&Transform>), + scene_env_lighting: Option<&EnvironmentalLighting>, + main_3d_shader_obj_id: ObjectId, + main_3d_shader_cursor: &ShaderCursor<'_>, + white_1x1_tex_obj_id: ObjectId, +) -> Result<(), crate::Error> +{ + let (material, material_flags, transform) = renderable; + + let transform = match transform { + Some(transform) => transform, + None => &Transform::default(), + }; + + let model_matrix = transform.to_matrix(); + let inverted_model_matrix = model_matrix.inverse(); + + let material_flags = + material_flags.unwrap_or(&const { MaterialFlags::builder().build() }); + + let env_lighting = match &scene_env_lighting { + Some(env_lighting) => env_lighting, + None => &EnvironmentalLighting::default(), + }; + + let model_3d_shader_cursor = + main_3d_shader_cursor.field("Uniforms").field("model_3d"); + + let material_shader_cursor = main_3d_shader_cursor + .field("Uniforms") + .field("lighting") + .field("material"); + + let diffuse_map_obj_id = material + .diffuse_map + .as_ref() + .map_or(white_1x1_tex_obj_id, |diffuse_map_asset| { + ObjectId::Asset(diffuse_map_asset.id()) + }); + + render_pass.commands.extend( + [ + model_3d_shader_cursor + .field("model") + .binding(model_matrix.into())?, + model_3d_shader_cursor + .field("model_inverted") + .binding(inverted_model_matrix.into())?, + material_shader_cursor.field("ambient").binding( + if material_flags.use_ambient_color { + &material.ambient + } else { + &env_lighting.ambient_color + } + .to_rgb_lossy() + .into(), + )?, + material_shader_cursor + .field("diffuse") + .binding(material.diffuse.to_rgb_lossy().into())?, + material_shader_cursor + .field("specular") + .binding(material.specular.to_rgb_lossy().into())?, + main_3d_shader_cursor.field("ambient_map").binding( + ShaderBindingValue::Texture( + material + .ambient_map + .as_ref() + .map_or(diffuse_map_obj_id, |ambient_map_asset| { + ObjectId::Asset(ambient_map_asset.id()) + }), + ShaderBindingTextureKind::Texture2D, + ), + )?, + main_3d_shader_cursor.field("diffuse_map").binding( + ShaderBindingValue::Texture( + diffuse_map_obj_id, + ShaderBindingTextureKind::Texture2D, + ), + )?, + main_3d_shader_cursor.field("specular_map").binding( + ShaderBindingValue::Texture( + material + .specular_map + .as_ref() + .map_or(white_1x1_tex_obj_id, |specular_map_asset| { + ObjectId::Asset(specular_map_asset.id()) + }), + ShaderBindingTextureKind::Texture2D, + ), + )?, + material_shader_cursor + .field("shininess") + .binding(material.shininess.into())?, + ] + .into_iter() + .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)), + ); + + Ok(()) +} + +fn add_sky_box_commands( + render_pass: &mut RenderPass, + sky_box_ids: &SkyBoxIds, + camera: &Camera, + camera_transform: &Transform, + window: &Window, + sky_box_shader_obj_id: ObjectId, + sky_box_shader_program: &ShaderProgram, +) -> Result<(), crate::Error> +{ + render_pass + .commands + .push(Command::ActivateShader(sky_box_shader_obj_id)); + + let sky_box_shader_cursor = ShaderCursor::new( + sky_box_shader_program + .reflection(0) + .unwrap() + .global_params_var_layout() + .unwrap(), + ); + + let mut view = camera.to_view_matrix(camera_transform.position); + + view.translate(&Vec3 { x: 0.0, y: 0.0, z: 0.0 }); + + let sky_box_shader_bindings = [ + sky_box_shader_cursor + .field("Uniforms") + .field("projection") + .binding( + (match camera.projection { + Projection::Perspective(_) => camera.projection.to_matrix_rh( + window.inner_size, + ProjectionClipVolume::NegOneToOne, + ), + Projection::Orthographic(_) => { + // A orthographic projection cannot be used for a sky box + Perspective::default().to_matrix_rh( + window.inner_size.width as f32 + / window.inner_size.height as f32, + ProjectionClipVolume::NegOneToOne, + ) + } + }) + .into(), + )?, + sky_box_shader_cursor + .field("Uniforms") + .field("view") + .binding(view.into())?, + sky_box_shader_cursor.field("cube_texture").binding( + ShaderBindingValue::Texture( + sky_box_ids.texture_object, + ShaderBindingTextureKind::Cube, + ), + )?, + ]; + + for binding in sky_box_shader_bindings { + render_pass + .commands + .push(Command::SetShaderBinding(sky_box_shader_obj_id, binding)); + } + + render_pass.commands.push(Command::UpdateDrawProperties( + DrawProperties { + depth_function: DepthFunction::LessOrEqual, + ..Default::default() + }, + DrawPropertiesUpdateFlags::DEPTH_FUNCTION, + )); + + render_pass.commands.push(Command::DrawMesh( + sky_box_ids.mesh_object, + DrawMeshOptions::default(), + )); + + Ok(()) +} + +fn load_sky_box( + scene_ent_id: Uid, + sky_box: &SkyBox, + sky_box_state: Option<&SkyBoxState>, + assets: &mut Assets, + object_store: &mut ObjectStore, + render_pass: &mut RenderPass, + actions: &mut Actions, +) -> Result, crate::Error> +{ + let mesh_object_id = if let Some(sky_box_state) = &sky_box_state { + sky_box_state.mesh_object_id + } else { + let sky_box_mesh = Mesh::cube(Dimens3 { width: 1.0, height: 1.0, depth: 1.0 }); + + let mesh_object_id = ObjectId::new_sequential(); + + object_store.insert_pending(mesh_object_id); + + render_pass.commands.push(Command::CreateMesh { + obj_id: mesh_object_id, + mesh: AssetOrValue::Value(sky_box_mesh), + usage: MeshUsage::Static, + }); + + mesh_object_id + }; + + let texture_object_id = match &sky_box { + SkyBox::AssetPerCubeMapFace(cube_map_part_assets) => { + if let Some(sky_box_state) = sky_box_state { + sky_box_state.texture_object_id + } else { + let Some(part_images) = cube_map_part_assets + .iter() + .map_while(|(face, part_texture_asset)| { + let part_texture = assets.get(part_texture_asset)?; + + match part_texture { + Texture::Texture2D(part_texture) => { + Some((*face, &part_texture.image)) + } + Texture::CubeMap(_) => { + tracing::warn!( + "Cube map texture cannot be used as cube map part" + ); + + None + } + } + }) + .collect::>() + .as_array::<6>() + .copied() + else { + return Ok(None); + }; + + let texture_object_id = ObjectId::new_sequential(); + + object_store.insert_pending(texture_object_id); + + render_pass.commands.push(Command::CreateTexture { + obj_id: texture_object_id, + pixel_data_format: TexturePixelDataFormat::for_image( + part_images[0].1, + ) + .expect("No texture pixel data format is available for image"), + creation: TextureCreation::CubeMap { + size: part_images[0].1.dimensions(), + images: Some(Box::new(part_images.map(|(face, image)| { + (face, image.as_bytes().to_vec().into_boxed_slice()) + }))), + }, + properties: TextureProperties::builder() + .minifying_filter(TextureFiltering::Linear) + .magnifying_filter(TextureFiltering::Linear) + .wrap(TextureWrapping::ClampToEdge) + .build(), + }); + + texture_object_id + } + } + }; + + if sky_box_state.is_none() { + actions.add_components( + scene_ent_id, + (SkyBoxState { texture_object_id, mesh_object_id },), + ); + } + + Ok(Some(SkyBoxIds { + texture_object: texture_object_id, + mesh_object: mesh_object_id, + })) +} + +#[derive(Debug, Component)] +pub struct SkyBoxState +{ + texture_object_id: ObjectId, + mesh_object_id: ObjectId, +} diff --git a/engine/src/rendering/main_render_pass.rs b/engine/src/rendering/main_render_pass.rs deleted file mode 100644 index 99d2909..0000000 --- a/engine/src/rendering/main_render_pass.rs +++ /dev/null @@ -1,1000 +0,0 @@ -use std::path::Path; -use std::sync::LazyLock; - -use ecs::actions::Actions; -use ecs::component::local::Local; -use ecs::error::Context as _; -use ecs::pair::ChildOf; -use ecs::query::term::{Traverse, TraverseUp}; -use ecs::uid::Uid; -use ecs::Component; - -use crate::asset::{Assets, Label as AssetLabel}; -use crate::camera::{Active as ActiveCamera, Camera}; -use crate::color::Rgba; -use crate::data_types::dimens::{Dimens, Dimens3}; -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::error; -use crate::image::Image; -use crate::lighting::{ - DirectionalLight, - Environmental as EnvironmentalLighting, - PointLight, -}; -use crate::material::{Flags as MaterialFlags, Material}; -use crate::mesh::Mesh; -use crate::model::{ModelMaterial, ModelMesh}; -use crate::projection::{ClipVolume as ProjectionClipVolume, Perspective, Projection}; -use crate::rendering::object::{Id as ObjectId, Store as ObjectStore}; -use crate::rendering::shader::cursor::{ - BindingTextureKind as ShaderBindingTextureKind, - BindingValue as ShaderBindingValue, - Cursor as ShaderCursor, -}; -use crate::rendering::shader::{ - Context as ShaderContext, - EntrypointFlags as ShaderEntrypointFlags, - ModuleSource as ShaderModuleSource, - Program as ShaderProgram, -}; -use crate::rendering::{ - AssetOrValue, - BufferClearMask, - Command, - DepthFunction, - DrawMeshOptions, - DrawProperties, - DrawPropertiesUpdateFlags, - MeshUsage, - RenderPass, - RenderPasses, - RgbaTextureDataType, - Surface, - TargetWindow, - TextureCreation, - TexturePixelDataFormat, -}; -use crate::scene::{Active as ActiveScene, Scene}; -use crate::sky_box::SkyBox; -use crate::texture::{ - Filtering as TextureFiltering, - Properties as TextureProperties, - Texture, - Wrapping as TextureWrapping, -}; -use crate::transform::Transform; -use crate::vector::Vec3; -use crate::windowing::window::Window; - -type RenderableEntity<'a> = ( - &'a ModelMesh, - Option<&'a ModelMaterial>, - Traverse<(With, With), TraverseUp, ChildOf>, - Option<&'a MaterialFlags>, - Option<&'a Transform>, - Option<&'a DrawFlags>, - Without, -); - -pub static SKY_BOX_SHADER_ASSET_LABEL: LazyLock = - LazyLock::new(|| AssetLabel { - path: Path::new("").into(), - name: Some("sky_box_shader".into()), - }); - -pub static MAIN_3D_SHADER_ASSET_LABEL: LazyLock = - LazyLock::new(|| AssetLabel { - path: Path::new("").into(), - name: Some("main_3d_shader".into()), - }); - -struct SkyBoxIds -{ - texture_object: ObjectId, - mesh_object: ObjectId, -} - -#[derive(Debug, Default, Component)] -pub struct MiscObjectIds -{ - white_1x1_tex_obj_id: Option, -} - -#[tracing::instrument(skip_all)] -pub fn add_main_render_pass( - renderable_query: Query>, - camera_query: Query<( - &Camera, - &Transform, - &ActiveCamera, - Traverse<(With, With), TraverseUp, ChildOf>, - )>, - window_query: Query<(&Window, &Surface, With)>, - scene_query: Query<( - Option<&EnvironmentalLighting>, - Option<&SkyBox>, - Option<&SkyBoxState>, - With, - With, - )>, - point_light_query: Query<( - &PointLight, - &Transform, - Traverse<(With, With), TraverseUp, ChildOf>, - )>, - directional_light_query: Query<( - &DirectionalLight, - Traverse<(With, With), TraverseUp, ChildOf>, - )>, - shader_context: Single, - mut assets: Single, - mut render_passes: Single, - mut object_store: Single, - mut actions: Actions, - mut misc_object_ids: Local, -) -> Result<(), crate::Error> -{ - let assets = assets.get_mut()?; - let shader_context = shader_context.get()?; - let render_passes = render_passes.get_mut()?; - let object_store = object_store.get_mut()?; - - let Some((scene_ent_id, (scene_env_lighting, scene_skybox, scene_skybox_state))) = - scene_query.iter_with_euids().next() - else { - return Ok(()); - }; - - let Some((camera, camera_transform, _)) = camera_query.iter().next() else { - tracing::trace!("No active camera"); - return Ok(()); - }; - - let render_pass = render_passes.passes.push_front_mut(RenderPass { - commands: Vec::with_capacity(30), - draw_properties: DrawProperties::default(), - }); - - for (mesh, material, ..) in &renderable_query { - add_renderable_creation_commands( - assets, - object_store, - render_pass, - &mesh, - material.as_deref(), - ); - } - - let shaders = - match get_or_create_shaders(assets, shader_context, object_store, render_pass) { - Ok(ShadersCreationStatus::Ready(ready_shaders)) => ready_shaders, - Ok(ShadersCreationStatus::ProcessingRequired) => return Ok(()), - Err(err) => { - tracing::error!("Failed to create shaders: {err}"); - return Ok(()); - } - }; - - let white_1x1_tex_obj_id = - *misc_object_ids.white_1x1_tex_obj_id.get_or_insert_with(|| { - create_white_1x1_texture_object(object_store, render_pass) - }); - - let main_3d_shader_cursor = ShaderCursor::new( - shaders - .main_3d_shader_program - .reflection(0) - .context("Unable to get reflection for main 3D shader")? - .global_params_var_layout() - .ok_or_else(|| { - crate::Error::message( - "Unable to get reflection for main 3D shader's global parameters", - ) - })?, - ); - - add_set_3d_shader_point_light_bindings( - render_pass, - &point_light_query, - shaders.main_3d_shader_obj_id, - &main_3d_shader_cursor, - )?; - - add_set_3d_shader_dir_lights_bindings( - render_pass, - &directional_light_query, - shaders.main_3d_shader_obj_id, - &main_3d_shader_cursor, - )?; - - for (window, window_surface) in &window_query { - render_pass - .commands - .push(Command::MakeCurrent(window_surface.id)); - - add_set_3d_shader_camera_bindings( - render_pass, - (&camera, &camera_transform), - &window, - shaders.main_3d_shader_obj_id, - &main_3d_shader_cursor, - )?; - - let mut buf_clear_mask = BufferClearMask::DEPTH; - - buf_clear_mask.set(BufferClearMask::COLOR, scene_skybox.is_none()); - - render_pass - .commands - .push(Command::ClearBuffers(buf_clear_mask)); - - for (mesh, material, material_flags, transform, draw_flags) in &renderable_query { - if assets.get(&mesh.asset).is_none() { - continue; - } - - let model_material = match material { - Some(material) - if let Some(material_desc) = assets.get(&material.asset) => - { - match assets.get(&material_desc.asset) { - Some(material) => material, - None => continue, - } - } - Some(_) => { - continue; - } - None => &Material::default(), - }; - - if model_material - .textures() - .any(|texture_asset| !assets.is_loaded_and_has_type(texture_asset)) - { - continue; - } - - add_set_3d_shader_renderable_bindings( - render_pass, - ( - model_material, - material_flags.as_deref(), - transform.as_deref(), - ), - scene_env_lighting.as_deref(), - shaders.main_3d_shader_obj_id, - &main_3d_shader_cursor, - white_1x1_tex_obj_id, - )?; - - render_pass - .commands - .push(Command::ActivateShader(shaders.main_3d_shader_obj_id)); - - if let Some(draw_flags) = draw_flags.as_deref().filter(|&draw_flags| { - draw_flags.polygon_mode_config != PolygonModeConfig::default() - }) { - render_pass.commands.push(Command::UpdateDrawProperties( - DrawProperties { - polygon_mode_config: draw_flags.polygon_mode_config.clone(), - ..Default::default() - }, - DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG, - )); - } - - 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, - )); - } - } - - if let Some(scene_skybox) = &scene_skybox { - let Some(sky_box_ids) = load_sky_box( - scene_ent_id, - scene_skybox, - scene_skybox_state.as_deref(), - assets, - object_store, - render_pass, - &mut actions, - )? - else { - continue; - }; - - add_sky_box_commands( - render_pass, - &sky_box_ids, - &camera, - &camera_transform, - &window, - shaders.sky_box_shader_obj_id, - shaders.sky_box_shader_program, - )?; - } - } - - Ok(()) -} - -fn create_white_1x1_texture_object( - object_store: &mut ObjectStore, - render_pass: &mut RenderPass, -) -> ObjectId -{ - let white_1x1_tex_obj_id = ObjectId::new_sequential(); - - object_store.insert_pending(white_1x1_tex_obj_id); - - render_pass.commands.push(Command::CreateTexture { - obj_id: white_1x1_tex_obj_id, - pixel_data_format: TexturePixelDataFormat::Rgba( - RgbaTextureDataType::UnsignedByte, - ), - creation: TextureCreation::Texture2D { - size: Dimens { width: 1, height: 1 }, - image: Some( - Image::from_color(Rgba::::white(), Dimens { width: 1, height: 1 }) - .into_bytes() - .into_boxed_slice(), - ), - }, - properties: TextureProperties::default(), - }); - - white_1x1_tex_obj_id -} - -#[derive(Debug)] -enum ShadersCreationStatus<'shader_ctx> -{ - ProcessingRequired, - Ready(ReadyShaders<'shader_ctx>), -} - -#[derive(Debug)] -struct ReadyShaders<'shader_ctx> -{ - main_3d_shader_obj_id: ObjectId, - main_3d_shader_program: &'shader_ctx ShaderProgram, - sky_box_shader_obj_id: ObjectId, - sky_box_shader_program: &'shader_ctx ShaderProgram, -} - -fn get_or_create_shaders<'shader_ctx>( - assets: &mut Assets, - shader_context: &'shader_ctx ShaderContext, - object_store: &mut ObjectStore, - render_pass: &mut RenderPass, -) -> Result, crate::Error> -{ - let main_3d_shader_asset = assets - .get_handle_to_loaded::(MAIN_3D_SHADER_ASSET_LABEL.clone()) - .or_else(|| { - assets.store_with_label( - MAIN_3D_SHADER_ASSET_LABEL.clone(), - ShaderModuleSource { - name: "main_3d_shader.slang".into(), - file_path: Path::new("@engine/main_3d_shader").into(), - source: include_str!("../../res/main_3d_shader.slang").into(), - link_entrypoints: ShaderEntrypointFlags::VERTEX - | ShaderEntrypointFlags::FRAGMENT, - }, - ); - - None - }); - - let sky_box_shader_asset = assets - .get_handle_to_loaded::(SKY_BOX_SHADER_ASSET_LABEL.clone()) - .or_else(|| { - assets.store_with_label( - SKY_BOX_SHADER_ASSET_LABEL.clone(), - ShaderModuleSource { - name: "sky_box_shader.slang".into(), - file_path: Path::new("@engine/sky_box_shader").into(), - source: include_str!("../../res/sky_box_shader.slang").into(), - link_entrypoints: ShaderEntrypointFlags::VERTEX - | ShaderEntrypointFlags::FRAGMENT, - }, - ); - None - }); - - let (Some(main_3d_shader_asset), Some(sky_box_shader_asset)) = - (main_3d_shader_asset, sky_box_shader_asset) - else { - return Ok(ShadersCreationStatus::ProcessingRequired); - }; - - let main_3d_shader_program = shader_context.get_program(&main_3d_shader_asset.id()); - - let main_3d_shader_program = main_3d_shader_program.ok_or_else(|| { - error!("Shader context doesn't have a program for main 3D shader") - })?; - - let main_3d_shader_obj_id = ObjectId::Asset(main_3d_shader_asset.id()); - - if !object_store.contains_maybe_pending_with_id(&main_3d_shader_obj_id) { - object_store.insert_pending(main_3d_shader_obj_id); - - render_pass.commands.push(Command::CreateShaderProgram( - main_3d_shader_obj_id, - main_3d_shader_program.clone(), - )); - } - - let sky_box_shader_program = shader_context.get_program(&sky_box_shader_asset.id()); - - let sky_box_shader_program = sky_box_shader_program.ok_or_else(|| { - error!("Shader context doesn't have a program for sky box shader") - })?; - - let sky_box_shader_obj_id = ObjectId::Asset(sky_box_shader_asset.id()); - - if !object_store.contains_maybe_pending_with_id(&sky_box_shader_obj_id) { - object_store.insert_pending(sky_box_shader_obj_id); - - render_pass.commands.push(Command::CreateShaderProgram( - sky_box_shader_obj_id, - sky_box_shader_program.clone(), - )); - } - - Ok(ShadersCreationStatus::Ready(ReadyShaders { - main_3d_shader_obj_id, - main_3d_shader_program, - sky_box_shader_obj_id, - sky_box_shader_program, - })) -} - -fn add_renderable_creation_commands( - assets: &Assets, - object_store: &mut ObjectStore, - render_pass: &mut RenderPass, - mesh: &ModelMesh, - material: Option<&ModelMaterial>, -) -{ - if assets.get(&mesh.asset).is_none() { - return; - } - - let model_material = match material { - Some(material) if let Some(material_desc) = assets.get(&material.asset) => { - match assets.get(&material_desc.asset) { - Some(material) => material, - None => return, - } - } - Some(_) => { - return; - } - None => &Material::default(), - }; - - // debug_assert!(model_spec.materials.len() <= 1); - - for texture_asset in model_material.textures() { - let Some(texture) = assets.get(texture_asset) else { - return; - }; - - let Texture::Texture2D(texture) = texture else { - tracing::error!( - texture_asset_id = ?texture_asset.id(), - texture_asset_label = - assets.get_label(texture_asset).map(tracing::field::display), - "Material texture map is not 2D" - ); - return; - }; - - let texture_object_id = ObjectId::Asset(texture_asset.id()); - - if object_store.contains_maybe_pending_with_id(&texture_object_id) { - return; - } - - let Some(tex_pixel_data_format) = - TexturePixelDataFormat::for_image(&texture.image) - else { - tracing::error!("No texture pixel data format is available for image"); - return; - }; - - object_store.insert_pending(texture_object_id); - - render_pass.commands.push(Command::CreateTexture { - obj_id: texture_object_id, - pixel_data_format: tex_pixel_data_format, - creation: TextureCreation::Texture2D { - size: texture.image.dimensions(), - image: Some(texture.image.as_bytes().to_vec().into_boxed_slice()), - }, - properties: texture.properties.clone(), - }); - } - - let mesh_obj_id = ObjectId::Asset(mesh.asset.id()); - - if !object_store.contains_maybe_pending_with_id(&mesh_obj_id) { - object_store.insert_pending(mesh_obj_id); - - render_pass.commands.push(Command::CreateMesh { - obj_id: mesh_obj_id, - mesh: AssetOrValue::Asset(mesh.asset.clone()), - usage: MeshUsage::Static, - }); - } -} - -fn add_set_3d_shader_point_light_bindings( - render_pass: &mut RenderPass, - point_light_query: &Query<( - &PointLight, - &Transform, - Traverse<(With, With), TraverseUp, ChildOf>, - )>, - main_3d_shader_obj_id: ObjectId, - main_3d_shader_cursor: &ShaderCursor<'_>, -) -> Result<(), crate::Error> -{ - let lighting_shader_cursor = - main_3d_shader_cursor.field("Uniforms").field("lighting"); - - render_pass - .commands - .reserve(1 + point_light_query.iter().count() * 6); - - render_pass.commands.push(Command::SetShaderBinding( - main_3d_shader_obj_id, - lighting_shader_cursor.field("point_light_cnt").binding( - u32::try_from(point_light_query.iter().count()) - .expect("Point light count does not fit in 32-bit unsigned integer") - .into(), - )?, - )); - - for (point_light_index, (point_light, point_light_transform)) in - point_light_query.iter().enumerate() - { - let point_light_shader_cursor = lighting_shader_cursor - .field("point_lights") - .element(point_light_index); - - let phong_shader_cursor = point_light_shader_cursor.field("phong"); - - let attenuation_props_shader_cursor = - point_light_shader_cursor.field("attenuation_props"); - - render_pass.commands.extend( - [ - phong_shader_cursor - .field("diffuse") - .binding(point_light.diffuse.to_rgb_lossy().into())?, - phong_shader_cursor - .field("specular") - .binding(point_light.specular.to_rgb_lossy().into())?, - point_light_shader_cursor.field("position").binding( - (point_light_transform.position + point_light.local_position).into(), - )?, - attenuation_props_shader_cursor - .field("constant") - .binding(point_light.attenuation_params.constant.into())?, - attenuation_props_shader_cursor - .field("linear") - .binding(point_light.attenuation_params.linear.into())?, - attenuation_props_shader_cursor - .field("quadratic") - .binding(point_light.attenuation_params.quadratic.into())?, - ] - .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)), - ); - } - - Ok(()) -} - -fn add_set_3d_shader_dir_lights_bindings( - render_pass: &mut RenderPass, - directional_light_query: &Query<( - &DirectionalLight, - Traverse<(With, With), TraverseUp, ChildOf>, - )>, - main_3d_shader_obj_id: ObjectId, - main_3d_shader_cursor: &ShaderCursor<'_>, -) -> Result<(), crate::Error> -{ - let lighting_shader_cursor = - main_3d_shader_cursor.field("Uniforms").field("lighting"); - - render_pass - .commands - .reserve(1 + directional_light_query.iter().count() * 3); - - render_pass.commands.push(Command::SetShaderBinding( - main_3d_shader_obj_id, - lighting_shader_cursor - .field("directional_light_cnt") - .binding( - u32::try_from(directional_light_query.iter().count()) - .expect( - "Directional light count does not fit in 32-bit unsigned integer", - ) - .into(), - )?, - )); - - for (directional_light_index, (directional_light,)) in - directional_light_query.iter().enumerate() - { - let directional_light_shader_cursor = lighting_shader_cursor - .field("directional_lights") - .element(directional_light_index); - - let phong_shader_cursor = directional_light_shader_cursor.field("phong"); - - render_pass.commands.extend( - [ - phong_shader_cursor - .field("diffuse") - .binding(directional_light.diffuse.to_rgb_lossy().into())?, - phong_shader_cursor - .field("specular") - .binding(directional_light.specular.to_rgb_lossy().into())?, - directional_light_shader_cursor - .field("direction") - .binding(directional_light.direction.into())?, - ] - .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)), - ); - } - - Ok(()) -} - -fn add_set_3d_shader_camera_bindings( - render_pass: &mut RenderPass, - (camera, camera_transform): (&Camera, &Transform), - window: &Window, - main_3d_shader_obj_id: ObjectId, - main_3d_shader_cursor: &ShaderCursor<'_>, -) -> Result<(), crate::Error> -{ - let model_3d_shader_cursor = - main_3d_shader_cursor.field("Uniforms").field("model_3d"); - - let lighting_shader_cursor = - main_3d_shader_cursor.field("Uniforms").field("lighting"); - - render_pass.commands.extend( - [ - model_3d_shader_cursor - .field("view") - .binding(camera.to_view_matrix(camera_transform.position).into())?, - model_3d_shader_cursor.field("projection").binding( - camera - .projection - .to_matrix_rh(window.inner_size, ProjectionClipVolume::NegOneToOne) - .into(), - )?, - lighting_shader_cursor - .field("view_pos") - .binding(camera_transform.position.into())?, - ] - .into_iter() - .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)), - ); - - Ok(()) -} - -fn add_set_3d_shader_renderable_bindings( - render_pass: &mut RenderPass, - renderable: (&Material, Option<&MaterialFlags>, Option<&Transform>), - scene_env_lighting: Option<&EnvironmentalLighting>, - main_3d_shader_obj_id: ObjectId, - main_3d_shader_cursor: &ShaderCursor<'_>, - white_1x1_tex_obj_id: ObjectId, -) -> Result<(), crate::Error> -{ - let (material, material_flags, transform) = renderable; - - let transform = match transform { - Some(transform) => transform, - None => &Transform::default(), - }; - - let model_matrix = transform.to_matrix(); - let inverted_model_matrix = model_matrix.inverse(); - - let material_flags = - material_flags.unwrap_or(&const { MaterialFlags::builder().build() }); - - let env_lighting = match &scene_env_lighting { - Some(env_lighting) => env_lighting, - None => &EnvironmentalLighting::default(), - }; - - let model_3d_shader_cursor = - main_3d_shader_cursor.field("Uniforms").field("model_3d"); - - let material_shader_cursor = main_3d_shader_cursor - .field("Uniforms") - .field("lighting") - .field("material"); - - let diffuse_map_obj_id = material - .diffuse_map - .as_ref() - .map_or(white_1x1_tex_obj_id, |diffuse_map_asset| { - ObjectId::Asset(diffuse_map_asset.id()) - }); - - render_pass.commands.extend( - [ - model_3d_shader_cursor - .field("model") - .binding(model_matrix.into())?, - model_3d_shader_cursor - .field("model_inverted") - .binding(inverted_model_matrix.into())?, - material_shader_cursor.field("ambient").binding( - if material_flags.use_ambient_color { - &material.ambient - } else { - &env_lighting.ambient_color - } - .to_rgb_lossy() - .into(), - )?, - material_shader_cursor - .field("diffuse") - .binding(material.diffuse.to_rgb_lossy().into())?, - material_shader_cursor - .field("specular") - .binding(material.specular.to_rgb_lossy().into())?, - main_3d_shader_cursor.field("ambient_map").binding( - ShaderBindingValue::Texture( - material - .ambient_map - .as_ref() - .map_or(diffuse_map_obj_id, |ambient_map_asset| { - ObjectId::Asset(ambient_map_asset.id()) - }), - ShaderBindingTextureKind::Texture2D, - ), - )?, - main_3d_shader_cursor.field("diffuse_map").binding( - ShaderBindingValue::Texture( - diffuse_map_obj_id, - ShaderBindingTextureKind::Texture2D, - ), - )?, - main_3d_shader_cursor.field("specular_map").binding( - ShaderBindingValue::Texture( - material - .specular_map - .as_ref() - .map_or(white_1x1_tex_obj_id, |specular_map_asset| { - ObjectId::Asset(specular_map_asset.id()) - }), - ShaderBindingTextureKind::Texture2D, - ), - )?, - material_shader_cursor - .field("shininess") - .binding(material.shininess.into())?, - ] - .into_iter() - .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)), - ); - - Ok(()) -} - -fn add_sky_box_commands( - render_pass: &mut RenderPass, - sky_box_ids: &SkyBoxIds, - camera: &Camera, - camera_transform: &Transform, - window: &Window, - sky_box_shader_obj_id: ObjectId, - sky_box_shader_program: &ShaderProgram, -) -> Result<(), crate::Error> -{ - render_pass - .commands - .push(Command::ActivateShader(sky_box_shader_obj_id)); - - let sky_box_shader_cursor = ShaderCursor::new( - sky_box_shader_program - .reflection(0) - .unwrap() - .global_params_var_layout() - .unwrap(), - ); - - let mut view = camera.to_view_matrix(camera_transform.position); - - view.translate(&Vec3 { x: 0.0, y: 0.0, z: 0.0 }); - - let sky_box_shader_bindings = [ - sky_box_shader_cursor - .field("Uniforms") - .field("projection") - .binding( - (match camera.projection { - Projection::Perspective(_) => camera.projection.to_matrix_rh( - window.inner_size, - ProjectionClipVolume::NegOneToOne, - ), - Projection::Orthographic(_) => { - // A orthographic projection cannot be used for a sky box - Perspective::default().to_matrix_rh( - window.inner_size.width as f32 - / window.inner_size.height as f32, - ProjectionClipVolume::NegOneToOne, - ) - } - }) - .into(), - )?, - sky_box_shader_cursor - .field("Uniforms") - .field("view") - .binding(view.into())?, - sky_box_shader_cursor.field("cube_texture").binding( - ShaderBindingValue::Texture( - sky_box_ids.texture_object, - ShaderBindingTextureKind::Cube, - ), - )?, - ]; - - for binding in sky_box_shader_bindings { - render_pass - .commands - .push(Command::SetShaderBinding(sky_box_shader_obj_id, binding)); - } - - render_pass.commands.push(Command::UpdateDrawProperties( - DrawProperties { - depth_function: DepthFunction::LessOrEqual, - ..Default::default() - }, - DrawPropertiesUpdateFlags::DEPTH_FUNCTION, - )); - - render_pass.commands.push(Command::DrawMesh( - sky_box_ids.mesh_object, - DrawMeshOptions::default(), - )); - - Ok(()) -} - -fn load_sky_box( - scene_ent_id: Uid, - sky_box: &SkyBox, - sky_box_state: Option<&SkyBoxState>, - assets: &mut Assets, - object_store: &mut ObjectStore, - render_pass: &mut RenderPass, - actions: &mut Actions, -) -> Result, crate::Error> -{ - let mesh_object_id = if let Some(sky_box_state) = &sky_box_state { - sky_box_state.mesh_object_id - } else { - let sky_box_mesh = Mesh::cube(Dimens3 { width: 1.0, height: 1.0, depth: 1.0 }); - - let mesh_object_id = ObjectId::new_sequential(); - - object_store.insert_pending(mesh_object_id); - - render_pass.commands.push(Command::CreateMesh { - obj_id: mesh_object_id, - mesh: AssetOrValue::Value(sky_box_mesh), - usage: MeshUsage::Static, - }); - - mesh_object_id - }; - - let texture_object_id = match &sky_box { - SkyBox::AssetPerCubeMapFace(cube_map_part_assets) => { - if let Some(sky_box_state) = sky_box_state { - sky_box_state.texture_object_id - } else { - let Some(part_images) = cube_map_part_assets - .iter() - .map_while(|(face, part_texture_asset)| { - let part_texture = assets.get(part_texture_asset)?; - - match part_texture { - Texture::Texture2D(part_texture) => { - Some((*face, &part_texture.image)) - } - Texture::CubeMap(_) => { - tracing::warn!( - "Cube map texture cannot be used as cube map part" - ); - - None - } - } - }) - .collect::>() - .as_array::<6>() - .copied() - else { - return Ok(None); - }; - - let texture_object_id = ObjectId::new_sequential(); - - object_store.insert_pending(texture_object_id); - - render_pass.commands.push(Command::CreateTexture { - obj_id: texture_object_id, - pixel_data_format: TexturePixelDataFormat::for_image( - part_images[0].1, - ) - .expect("No texture pixel data format is available for image"), - creation: TextureCreation::CubeMap { - size: part_images[0].1.dimensions(), - images: Some(Box::new(part_images.map(|(face, image)| { - (face, image.as_bytes().to_vec().into_boxed_slice()) - }))), - }, - properties: TextureProperties::builder() - .minifying_filter(TextureFiltering::Linear) - .magnifying_filter(TextureFiltering::Linear) - .wrap(TextureWrapping::ClampToEdge) - .build(), - }); - - texture_object_id - } - } - }; - - if sky_box_state.is_none() { - actions.add_components( - scene_ent_id, - (SkyBoxState { texture_object_id, mesh_object_id },), - ); - } - - Ok(Some(SkyBoxIds { - texture_object: texture_object_id, - mesh_object: mesh_object_id, - })) -} - -#[derive(Debug, Component)] -pub struct SkyBoxState -{ - texture_object_id: ObjectId, - mesh_object_id: ObjectId, -} -- cgit v1.2.3-18-g5258