diff options
Diffstat (limited to 'engine/src/rendering/main_render_pass.rs')
| -rw-r--r-- | engine/src/rendering/main_render_pass.rs | 313 |
1 files changed, 303 insertions, 10 deletions
diff --git a/engine/src/rendering/main_render_pass.rs b/engine/src/rendering/main_render_pass.rs index 60bed03..622a087 100644 --- a/engine/src/rendering/main_render_pass.rs +++ b/engine/src/rendering/main_render_pass.rs @@ -1,17 +1,33 @@ +use std::path::Path; +use std::sync::LazyLock; + +use ecs::actions::Actions; use ecs::pair::ChildOf; use ecs::query::term::{Traverse, TraverseUp}; +use ecs::uid::Uid; +use ecs::Component; -use crate::asset::Assets; +use crate::asset::{Assets, Id as AssetId, Label as AssetLabel}; +use crate::camera::{Active as ActiveCamera, Camera}; +use crate::data_types::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::mesh::Mesh; use crate::model::{MaterialSearchResult, Model}; +use crate::projection::ClipVolume as ProjectionClipVolume; 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::default::ASSET_LABEL as DEFAULT_SHADER_ASSET_LABEL; use crate::rendering::shader::{ Context as ShaderContext, + EntrypointFlags as ShaderEntrypointFlags, ModuleSource as ShaderModuleSource, Shader, }; @@ -19,6 +35,7 @@ use crate::rendering::{ AssetOrValue, BufferClearMask, Command, + DepthFunction, DrawMeshOptions, DrawProperties, DrawPropertiesUpdateFlags, @@ -30,7 +47,17 @@ use crate::rendering::{ TargetWindow, }; use crate::scene::{Active as ActiveScene, Scene}; -use crate::texture::{Texture, WHITE_1X1_ASSET_LABEL as TEXTURE_WHITE_1X1_ASSET_LABEL}; +use crate::sky_box::SkyBox; +use crate::texture::{ + Filtering as TextureFiltering, + Properties as TextureProperties, + TexCubeMap, + Texture, + Wrapping as TextureWrapping, + WHITE_1X1_ASSET_LABEL as TEXTURE_WHITE_1X1_ASSET_LABEL, +}; +use crate::transform::WorldPosition; +use crate::vector::Vec3; use crate::windowing::window::Window; type RenderableEntity<'a> = ( @@ -42,21 +69,59 @@ type RenderableEntity<'a> = ( Without<NoDraw>, ); +pub static SKY_BOX_SHADER_ASSET_LABEL: LazyLock<AssetLabel> = + LazyLock::new(|| AssetLabel { + path: Path::new("").into(), + name: Some("sky_box_shader".into()), + }); + +struct SkyBoxIds +{ + texture_object: ObjectId, + mesh_object: ObjectId, + shader_object: ObjectId, + shader_asset: AssetId, +} + #[tracing::instrument(skip_all)] -pub fn add_main_render_passes( +pub fn add_main_render_pass( renderable_query: Query<RenderableEntity<'_>>, - window_surface_spec_query: Query<(&SurfaceSpec, With<Window>, With<TargetWindow>)>, - assets: Single<Assets>, + camera_query: Query<( + &Camera, + &WorldPosition, + &ActiveCamera, + Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>, + )>, + window_query: Query<(&Window, &SurfaceSpec, With<TargetWindow>)>, + scene_query: Query<( + Option<&SkyBox>, + Option<&SkyBoxState>, + With<Scene>, + With<ActiveScene>, + )>, shader_context: Single<ShaderContext>, + mut assets: Single<Assets>, mut render_passes: Single<RenderPasses>, mut object_store: Single<ObjectStore>, + mut actions: Actions, ) -> Result<(), crate::Error> { - let assets = assets.get()?; + 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_skybox, scene_skybox_stuff))) = + scene_query.iter_with_euids().next() + else { + return Ok(()); + }; + + let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else { + tracing::trace!("No active camera"); + return Ok(()); + }; + let Some(default_shader_asset) = assets .get_handle_to_loaded::<ShaderModuleSource>(DEFAULT_SHADER_ASSET_LABEL.clone()) else { @@ -68,7 +133,7 @@ pub fn add_main_render_passes( draw_properties: DrawProperties::default(), }); - for (surface_spec,) in &window_surface_spec_query { + for (window, surface_spec) in &window_query { render_pass .commands .push(Command::MakeCurrent(surface_spec.id)); @@ -88,9 +153,28 @@ pub fn add_main_render_passes( )); } - render_pass.commands.push(Command::ClearBuffers( - BufferClearMask::COLOR | BufferClearMask::DEPTH, - )); + let sky_box_ids = if let Some(scene_skybox) = &scene_skybox { + load_sky_box( + scene_ent_id, + &scene_skybox, + scene_skybox_stuff.as_deref(), + assets, + object_store, + shader_context, + render_pass, + &mut actions, + )? + } else { + None + }; + + let mut buf_clear_mask = BufferClearMask::DEPTH; + + buf_clear_mask.set(BufferClearMask::COLOR, sky_box_ids.is_none()); + + render_pass + .commands + .push(Command::ClearBuffers(buf_clear_mask)); for (model, draw_flags, shader, mut pending_shader_bindings) in &renderable_query { @@ -246,7 +330,216 @@ pub fn add_main_render_passes( )); } } + + if let Some(sky_box_ids) = &sky_box_ids { + render_pass + .commands + .push(Command::ActivateShader(sky_box_ids.shader_object)); + + let Some(sky_box_shader_program) = + shader_context.get_program(&sky_box_ids.shader_asset) + else { + unreachable!(); + }; + + 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_world_pos.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( + camera + .projection + .to_matrix_rh( + window.inner_size, + 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(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, + shader_context: &ShaderContext, + render_pass: &mut RenderPass, + actions: &mut Actions, +) -> Result<Option<SkyBoxIds>, crate::Error> +{ + let Some(sky_box_shader_asset) = assets + .get_handle_to_loaded::<ShaderModuleSource>(SKY_BOX_SHADER_ASSET_LABEL.clone()) + 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, + }, + ); + + // shader has to be processed + return Ok(None); + }; + + if !object_store + .contains_maybe_pending_with_id(&ObjectId::Asset(sky_box_shader_asset.id())) + { + let Some(shader_program) = shader_context.get_program(&sky_box_shader_asset.id()) + else { + tracing::error!( + "Shader context doesn't have a program for sky box shader asset", + ); + return Ok(None); + }; + + object_store.insert_pending(ObjectId::Asset(sky_box_shader_asset.id())); + + render_pass.commands.push(Command::CreateShaderProgram( + ObjectId::Asset(sky_box_shader_asset.id()), + shader_program.clone(), + )); + } + + let mesh_object_id = match &sky_box_state { + Some(sky_box_state) => sky_box_state.mesh_object_id, + None => { + 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::<Vec<_>>() + .as_array::<6>() + .cloned() + else { + return Ok(None); + }; + + let texture_object_id = ObjectId::new_sequential(); + + object_store.insert_pending(texture_object_id); + + render_pass.commands.push(Command::CreateTexture( + texture_object_id, + AssetOrValue::Value(Texture::CubeMap(TexCubeMap { + images: part_images.map(|(face, image)| (face, image.clone())), + 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, + shader_object: ObjectId::Asset(sky_box_shader_asset.id()), + shader_asset: sky_box_shader_asset.id(), + })) +} + +#[derive(Debug, Component)] +pub struct SkyBoxState +{ + texture_object_id: ObjectId, + mesh_object_id: ObjectId, +} |
