summaryrefslogtreecommitdiff
path: root/engine/src/shader/default.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/shader/default.rs')
-rw-r--r--engine/src/shader/default.rs363
1 files changed, 363 insertions, 0 deletions
diff --git a/engine/src/shader/default.rs b/engine/src/shader/default.rs
new file mode 100644
index 0000000..28bbdc9
--- /dev/null
+++ b/engine/src/shader/default.rs
@@ -0,0 +1,363 @@
+use std::path::Path;
+use std::sync::LazyLock;
+
+use ecs::Query;
+use ecs::actions::Actions;
+use ecs::query::term::Without;
+use ecs::sole::Single;
+
+use crate::asset::{Assets, Label as AssetLabel};
+use crate::camera::{Active as ActiveCamera, Camera};
+use crate::data_types::dimens::Dimens;
+use crate::draw_flags::NoDraw;
+use crate::lighting::{DirectionalLight, GlobalLight, PointLight};
+use crate::material::{Flags as MaterialFlags, Material};
+use crate::matrix::Matrix;
+use crate::model::{MaterialSearchResult, Model};
+use crate::projection::{ClipVolume as ProjectionClipVolume, Projection};
+use crate::renderer::{DEFAULT_TEXTURE_ASSET_LABEL, PendingShaderBindings};
+use crate::shader::cursor::{BindingValue as ShaderBindingValue, Cursor as ShaderCursor};
+use crate::shader::{
+ Context as ShaderContext,
+ ModuleSource as ShaderModuleSource,
+ Shader,
+};
+use crate::transform::{Scale, Transform, WorldPosition};
+use crate::vector::Vec3;
+use crate::windowing::window::Window;
+
+pub static ASSET_LABEL: LazyLock<AssetLabel> = LazyLock::new(|| AssetLabel {
+ path: Path::new("").into(),
+ name: Some("default_shader".into()),
+});
+
+pub fn enqueue_set_shader_bindings(
+ renderable_query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>,
+ camera_query: Query<(&Camera, &WorldPosition, &ActiveCamera)>,
+ window_query: Query<(&Window,)>,
+ point_light_query: Query<(&PointLight, &WorldPosition)>,
+ directional_light_query: Query<(&DirectionalLight,)>,
+ assets: Single<Assets>,
+ shader_context: Single<ShaderContext>,
+ global_light: Single<GlobalLight>,
+ mut actions: Actions,
+)
+{
+ let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else {
+ tracing::warn!("No current camera");
+ return;
+ };
+
+ let Some((window,)) = window_query.iter().next() else {
+ // tracing::warn!("No window");
+ return;
+ };
+
+ let default_shader_asset = assets
+ .get_handle_to_loaded::<ShaderModuleSource>(ASSET_LABEL.clone())
+ .unwrap();
+
+ for (
+ entity_id,
+ (model, material_flags, world_pos, scale, shader, mut pending_shader_bindings),
+ ) in renderable_query.iter_with_euids()
+ {
+ let shader_asset = match &shader {
+ Some(shader) => &shader.asset_handle,
+ None => &default_shader_asset,
+ };
+
+ if shader_asset.id() != default_shader_asset.id() {
+ continue;
+ }
+
+ let Some(shader_program) = shader_context.get_program(&shader_asset.id()) else {
+ continue;
+ };
+
+ let Some(model_spec) = assets.get(&model.spec_asset) else {
+ continue;
+ };
+
+ let has_pending_shader_bindings_comp = pending_shader_bindings.is_some();
+
+ let shader_bindings = match pending_shader_bindings.as_deref_mut() {
+ Some(pending_shader_bindings) => pending_shader_bindings,
+ None => &mut PendingShaderBindings::default(),
+ };
+
+ let shader_cursor = ShaderCursor::new(
+ shader_program
+ .reflection(0)
+ .unwrap()
+ .global_params_var_layout()
+ .unwrap(),
+ );
+
+ let model_matrix = create_model_matrix(Transform {
+ position: world_pos.as_deref().cloned().unwrap_or_default().position,
+ scale: scale.as_deref().cloned().unwrap_or_default().scale,
+ });
+
+ let inverted_model_matrix = model_matrix.inverse();
+
+ let model_material = match model_spec.find_first_material(&assets) {
+ MaterialSearchResult::Found(model_material_asset) => {
+ let Some(model_material) = assets.get(&model_material_asset) else {
+ continue;
+ };
+
+ model_material
+ }
+ MaterialSearchResult::NotFound => {
+ continue;
+ }
+ MaterialSearchResult::NoMaterials => &const { Material::builder().build() },
+ };
+
+ if [
+ &model_material.ambient_map,
+ &model_material.diffuse_map,
+ &model_material.specular_map,
+ ]
+ .into_iter()
+ .flatten()
+ .any(|texture| !assets.is_loaded_and_has_type(&texture.asset_handle))
+ {
+ continue;
+ }
+
+ let material_flags = material_flags
+ .as_deref()
+ .unwrap_or(&const { MaterialFlags::builder().build() });
+
+ let model_3d_shader_cursor = shader_cursor.field("Uniforms").field("model_3d");
+ let lighting_shader_cursor = shader_cursor.field("Uniforms").field("lighting");
+ let material_shader_cursor = lighting_shader_cursor.field("material");
+
+ shader_bindings.extend([
+ (model_3d_shader_cursor.field("model"), model_matrix.into()),
+ (
+ model_3d_shader_cursor.field("model_inverted"),
+ inverted_model_matrix.into(),
+ ),
+ (
+ model_3d_shader_cursor.field("view"),
+ create_view_matrix(&camera, &camera_world_pos.position).into(),
+ ),
+ (
+ model_3d_shader_cursor.field("projection"),
+ create_projection_matrix(
+ &camera,
+ &camera_world_pos.position,
+ window.inner_size(),
+ )
+ .into(),
+ ),
+ (
+ lighting_shader_cursor.field("view_pos"),
+ camera_world_pos.position.into(),
+ ),
+ (
+ material_shader_cursor.field("ambient"),
+ Vec3::from(
+ if material_flags.use_ambient_color {
+ &model_material.ambient
+ } else {
+ &global_light.ambient
+ }
+ .clone(),
+ )
+ .into(),
+ ),
+ (
+ material_shader_cursor.field("diffuse"),
+ Vec3::from(model_material.diffuse.clone()).into(),
+ ),
+ (
+ material_shader_cursor.field("specular"),
+ Vec3::from(model_material.specular.clone()).into(),
+ ),
+ (
+ material_shader_cursor.field("shininess"),
+ model_material.shininess.into(),
+ ),
+ (
+ lighting_shader_cursor.field("directional_light_cnt"),
+ u32::try_from(directional_light_query.iter().count())
+ .expect(
+ "Directional light count does not fit in 32-bit unsigned integer",
+ )
+ .into(),
+ ),
+ (
+ lighting_shader_cursor.field("point_light_cnt"),
+ u32::try_from(point_light_query.iter().count())
+ .expect("Point light count does not fit in 32-bit unsigned integer")
+ .into(),
+ ),
+ ]);
+
+ shader_bindings.extend(point_light_query.iter().enumerate().flat_map(
+ |(point_light_index, (point_light, point_light_world_pos))| {
+ 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");
+
+ [
+ (
+ phong_shader_cursor.field("diffuse"),
+ Vec3::from(point_light.diffuse.clone()).into(),
+ ),
+ (
+ phong_shader_cursor.field("specular"),
+ Vec3::from(point_light.specular.clone()).into(),
+ ),
+ (
+ point_light_shader_cursor.field("position"),
+ (point_light_world_pos.position + point_light.local_position)
+ .into(),
+ ),
+ (
+ attenuation_props_shader_cursor.field("constant"),
+ point_light.attenuation_params.constant.into(),
+ ),
+ (
+ attenuation_props_shader_cursor.field("linear"),
+ point_light.attenuation_params.linear.into(),
+ ),
+ (
+ attenuation_props_shader_cursor.field("quadratic"),
+ point_light.attenuation_params.quadratic.into(),
+ ),
+ ]
+ },
+ ));
+
+ shader_bindings.extend(directional_light_query.iter().enumerate().flat_map(
+ |(directional_light_index, (directional_light,))| {
+ 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");
+
+ [
+ (
+ phong_shader_cursor.field("diffuse"),
+ Vec3::from(directional_light.diffuse.clone()).into(),
+ ),
+ (
+ phong_shader_cursor.field("specular"),
+ Vec3::from(directional_light.specular.clone()).into(),
+ ),
+ (
+ directional_light_shader_cursor.field("direction"),
+ directional_light.direction.into(),
+ ),
+ ]
+ },
+ ));
+
+ shader_bindings.bindings.push((
+ shader_cursor.field("ambient_map").into_binding_location(),
+ ShaderBindingValue::Texture(
+ model_material
+ .ambient_map
+ .as_ref()
+ .map(|ambient_map| ambient_map.asset_handle.clone())
+ .unwrap_or_else(|| {
+ assets
+ .get_handle_to_loaded(DEFAULT_TEXTURE_ASSET_LABEL.clone())
+ .expect("Not possible")
+ }),
+ ),
+ ));
+
+ shader_bindings.bindings.push((
+ shader_cursor.field("diffuse_map").into_binding_location(),
+ ShaderBindingValue::Texture(
+ model_material
+ .diffuse_map
+ .as_ref()
+ .map(|diffuse_map| diffuse_map.asset_handle.clone())
+ .unwrap_or_else(|| {
+ assets
+ .get_handle_to_loaded(DEFAULT_TEXTURE_ASSET_LABEL.clone())
+ .expect("Not possible")
+ }),
+ ),
+ ));
+
+ shader_bindings.bindings.push((
+ shader_cursor.field("specular_map").into_binding_location(),
+ ShaderBindingValue::Texture(
+ model_material
+ .specular_map
+ .as_ref()
+ .map(|specular_map| specular_map.asset_handle.clone())
+ .unwrap_or_else(|| {
+ assets
+ .get_handle_to_loaded(DEFAULT_TEXTURE_ASSET_LABEL.clone())
+ .expect("Not possible")
+ }),
+ ),
+ ));
+
+ if !has_pending_shader_bindings_comp {
+ actions.add_components(entity_id, (shader_bindings.clone(),));
+ }
+ }
+}
+
+fn create_model_matrix(transform: Transform) -> Matrix<f32, 4, 4>
+{
+ let mut matrix = Matrix::new_identity();
+
+ matrix.translate(&transform.position);
+ matrix.scale(&transform.scale);
+
+ matrix
+}
+
+fn create_view_matrix(camera: &Camera, camera_world_pos: &Vec3<f32>)
+-> Matrix<f32, 4, 4>
+{
+ let mut view = Matrix::new();
+
+ // tracing::debug!("Camera target: {:?}", camera.target);
+
+ view.look_at(camera_world_pos, &camera.target, &camera.global_up);
+
+ view
+}
+
+fn create_projection_matrix(
+ camera: &Camera,
+ camera_world_pos: &Vec3<f32>,
+ window_size: &Dimens<u32>,
+) -> Matrix<f32, 4, 4>
+{
+ match &camera.projection {
+ Projection::Perspective(perspective_proj) => perspective_proj.to_matrix_rh(
+ window_size.width as f32 / window_size.height as f32,
+ ProjectionClipVolume::NegOneToOne,
+ ),
+ Projection::Orthographic(orthographic_proj) => orthographic_proj
+ .to_matrix_rh(camera_world_pos, ProjectionClipVolume::NegOneToOne),
+ }
+}
+
+type RenderableEntity<'a> = (
+ &'a Model,
+ Option<&'a MaterialFlags>,
+ Option<&'a WorldPosition>,
+ Option<&'a Scale>,
+ Option<&'a Shader>,
+ Option<&'a mut PendingShaderBindings>,
+);