use crate::{ camera::{self as cam, FirstPersonCamera}, engine::World, graphics_util::draw_cuboid, }; use macroquad::prelude as mq; use macroquad::ui::{hash, Ui}; use rapier3d::prelude::*; use strum::IntoEnumIterator; mod ui; const DEPTH_MAP_SIZE: u32 = 2048; const LIGHT_PROJECTION_SIZE: f32 = 40.0; const LIGHT_NEAR: f32 = 50.0; const LIGHT_FAR: f32 = 400.0; pub struct Light { projection_matrix: mq::Mat4, view_matrix: mq::Mat4, pub position: mq::Vec3, pub front: mq::Vec3, } impl Light { pub fn new() -> Light { let mut l = Light { projection_matrix: mq::Mat4::orthographic_rh( -LIGHT_PROJECTION_SIZE, LIGHT_PROJECTION_SIZE, -LIGHT_PROJECTION_SIZE, LIGHT_PROJECTION_SIZE, LIGHT_NEAR, LIGHT_FAR, ), view_matrix: mq::Mat4::NAN, position: mq::Vec3::NAN, front: mq::Vec3::NAN, }; l.set_location(mq::vec3(5.0, 10.0, 0.0), mq::Vec3::NEG_Y); return l; } pub fn set_location(&mut self, position: mq::Vec3, front: mq::Vec3) { self.position = position; self.front = front; self.view_matrix = mq::Mat4::look_at_rh(self.position, self.position + self.front, mq::Vec3::Y); } pub fn get_space_matrix(&self) -> mq::Mat4 { return self.projection_matrix * self.view_matrix; } } pub struct Renderer { base_material: mq::Material, depth_material: mq::Material, depth_target: mq::RenderTarget, pub light: Light, pub camera: cam::FirstPersonCamera, ui_state: ui::MaterialUi, pub enable_shadows: bool, } #[derive(strum::EnumIter, Debug)] pub enum MaterialUni { RenderNormalsBool, RenderShadowsBool, ShadowStepCount, ShadowStepSize, ShadowMinBias, ShadowFacBias, ShadowMaxBias, LightPosition, LightSpaceMatrix, } impl MaterialUni { pub fn as_str(&self) -> &'static str { use MaterialUni::*; match self { RenderNormalsBool => "render_normals_bool", RenderShadowsBool => "render_shadows_bool", ShadowStepCount => "shadow_step_count", ShadowStepSize => "shadow_step_size", ShadowMinBias => "shadow_min_bias", ShadowFacBias => "shadow_fac_bias", ShadowMaxBias => "shadow_max_bias", LightPosition => "light_position", LightSpaceMatrix => "light_space_matrix", } } pub fn as_type(&self) -> mq::UniformType { use mq::UniformType::*; use MaterialUni::*; match self { RenderNormalsBool => Int1, RenderShadowsBool => Int1, ShadowStepCount => Int1, ShadowStepSize => Int1, ShadowMinBias => Float1, ShadowFacBias => Float1, ShadowMaxBias => Float1, LightPosition => Float3, LightSpaceMatrix => Mat4, } } pub fn uniform_list() -> Vec { let mut ret: Vec = Default::default(); for member in MaterialUni::iter() { ret.push(mq::UniformDesc::new(member.as_str(), member.as_type())); } return ret; } } impl Renderer { pub fn new() -> Renderer { let material = mq::load_material( mq::ShaderSource::Glsl { vertex: include_str!("shaders/shader.vert"), fragment: include_str!("shaders/shader.frag"), }, mq::MaterialParams { pipeline_params: mq::PipelineParams { cull_face: macroquad::miniquad::CullFace::Front, depth_test: mq::Comparison::LessOrEqual, depth_write: true, ..Default::default() }, uniforms: MaterialUni::uniform_list(), textures: vec![String::from("shadow_map")], ..Default::default() }, ) .unwrap(); material.set_uniform(MaterialUni::RenderNormalsBool.as_str(), 0); material.set_uniform(MaterialUni::RenderShadowsBool.as_str(), true as i32); let depth_material = mq::load_material( mq::ShaderSource::Glsl { vertex: include_str!("shaders/depth.vert"), fragment: include_str!("shaders/depth.frag"), }, mq::MaterialParams { pipeline_params: mq::PipelineParams { cull_face: macroquad::miniquad::CullFace::Back, depth_test: mq::Comparison::LessOrEqual, depth_write: true, ..Default::default() }, uniforms: vec![mq::UniformDesc::new( "light_space_matrix", mq::UniformType::Mat4, )], ..Default::default() }, ) .unwrap(); let depth_map_texture = mq::render_target_ex( DEPTH_MAP_SIZE, DEPTH_MAP_SIZE, mq::RenderTargetParams { sample_count: 1, depth: true, }, ); depth_map_texture .texture .set_filter(mq::FilterMode::Nearest); let light = Light::new(); let camera = FirstPersonCamera::new(mq::vec3(10.0, 50.0, 10.0)); let mut r = Renderer { base_material: material, depth_material, depth_target: depth_map_texture, light, camera, ui_state: ui::MaterialUi::new(), enable_shadows: true, }; r.light .set_location(mq::vec3(10.0, 40.0, 10.0), mq::Vec3::NEG_Y); r.update_light(); r.apply_config(); return r; } pub fn apply_config(&mut self) { self.ui_state.apply(&self.base_material); self.enable_shadows = self.ui_state.render_shadows_bool; } pub fn update_camera(&mut self) { self.camera.update(mq::get_frame_time()); } pub fn update_light(&mut self) { let light_space_matrix = self.light.get_space_matrix(); self.base_material .set_uniform(MaterialUni::LightPosition.as_str(), self.light.position); self.base_material .set_uniform(MaterialUni::LightSpaceMatrix.as_str(), light_space_matrix); self.depth_material .set_uniform(MaterialUni::LightSpaceMatrix.as_str(), light_space_matrix); } fn render_depth(&mut self, mut world: &mut World) { mq::set_camera(&mq::Camera3D { position: self.light.position, target: self.light.position + self.light.front, up: mq::Vec3::Y, render_target: Some(self.depth_target.clone()), ..Default::default() }); mq::gl_use_material(&self.depth_material); mq::clear_background(mq::DARKGRAY); draw_world(&mut world, None); mq::gl_use_default_material(); mq::set_default_camera(); self.base_material .set_texture("shadow_map", self.depth_target.texture.clone()); } fn render_default(&mut self, mut world: &mut World) { mq::clear_background(mq::LIGHTGRAY); self.camera.apply(); draw_world(&mut world, Some(&self.base_material)); mq::draw_sphere(self.light.position, 0.25, None, mq::WHITE); mq::draw_grid(20, 1., mq::BLACK, mq::GRAY); mq::gl_use_default_material(); mq::set_default_camera(); } fn render_ui(&mut self) { mq::set_default_camera(); // mq::draw_texture_ex( // &self.depth_target.texture, // 0., // 0., // mq::WHITE, // mq::DrawTextureParams { // flip_y: true, // dest_size: Some(mq::vec2(200.0, 200.0)), // ..Default::default() // }, // ); macroquad::ui::root_ui().window( hash!("MaterialWindow"), mq::vec2(20.0, 20.0), mq::vec2(300.0, 300.0), |ui| { self.ui_state.ui(ui); }, ); } pub fn draw(&mut self, mut world: &mut World) { if self.enable_shadows { self.render_depth(&mut world); } self.render_default(&mut world); self.render_ui(); } } fn draw_world(world: &mut World, material: Option<&mq::Material>) { for (handle, coll) in world.colliders.iter() { let position: glam::Vec3 = world.position_of_collider(handle).unwrap().into(); let rotation = *coll.rotation(); unsafe { let context = mq::get_internal_gl().quad_gl; context.push_model_matrix(mq::Mat4::from_rotation_translation( rotation.into(), position, )); } let color = world.collider_data.get(&handle).unwrap().color; match coll.shape().shape_type() { ShapeType::Ball => { mq::draw_sphere( mq::vec3(0.0, 0.0, 0.0), coll.shape().as_ball().unwrap().radius, None, color, ); } ShapeType::Cuboid => { match material { Some(mat) => { mq::gl_use_material(&mat); } None => {} } draw_cuboid( mq::vec3(0.0, 0.0, 0.0), (coll.shape().as_cuboid().unwrap().half_extents * 2.0).into(), color, ); match material { Some(_) => { mq::gl_use_default_material(); } None => {} } } _ => println!("Not implemented.. Skipping"), } unsafe { let context = mq::get_internal_gl().quad_gl; context.pop_model_matrix(); } } }