From f9a99fc0ae3ba4e7871e93dcb52592508a67c296 Mon Sep 17 00:00:00 2001 From: crumblingstatue Date: Fri, 7 Apr 2023 22:16:57 +0200 Subject: [PATCH] Switch to lightmap texture based lighting --- res/lightcircle.png | Bin 0 -> 3575 bytes shaders/lighting.glsl | 46 -------------------- src/app.rs | 22 ++++++++-- src/game.rs | 98 +++++++++++++----------------------------- src/input.rs | 5 +++ src/res.rs | 13 ++---- 6 files changed, 56 insertions(+), 128 deletions(-) create mode 100644 res/lightcircle.png delete mode 100644 shaders/lighting.glsl diff --git a/res/lightcircle.png b/res/lightcircle.png new file mode 100644 index 0000000000000000000000000000000000000000..4fcbdf6a5c48eaf97ea1a8a5fe621bd6754d01d9 GIT binary patch literal 3575 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 z℞3>7rF~2g3w}A1kKnYG&wtx&OSZ=FEwif($hgOXkhy6iD0KexpgnQzfbAzzbdh z=>@N=b|ieyRMePt@8knhwnNox7jsRp47beWHeq&AOFZ!RudOTFiOyYJ)sAocxI-LT zw!g}_GXERHuf`9~huw=Uo3=3?UK;8!KZ|{%Y37o38XNiyU0OsIUOp(qV6}D2hqNQs zXN;VA&ECPeEz+`;6i=ae&ZgN<5wO$`Jc`9 z;yTl$s~xXSMf!VgHJ?g>loI;#rI}th|*Q_I&yryWyKmpJ&DY z6$~$~mbdLMElrIy^_Mepn8hC@s&*>=d0F+0vmX@>3ERJ)!o~BL!JP5Hop+x@R_FSB zVCQ+Ut8eMe3#GEWs~oOhdl#{DV`9bYCRvGYmZvL(m1DA{pE-0etmy0BGDASBe>z8B zr~Pa9H;vYlxqVhL_)FXfJlHG& zN;lSXvD)86Oc|11O#QH9_wHn72Kkx~jO~p}EP6$&ven~v9C-23BX50qScY9pUV+^C z4G-2xy{Kf)T>5!F)6w@)wM844GiJ5j-qx%8zENt+-jfX4d>9Ks8bD{A2)~M{m z_rBX|ms+gl+W-IG?=`=37M|{6)p;m^5a+lXw(j+;*SnT2(y%!HIYK2efxAL2O#bvMJ=S8cACn*7STI$h{OlxlhLYIh zE=Lw#J#$HTg2_z@<8Q~8e=3>v^2;SBx3h1*sWAWA_k7p7izdEvw6kWhC9dmGpS@VV z{*U4H(zES{8^8ayE!!>2*UsFo$$ilJA7`gDvXppL zoM;GTIKa+)e)DHW-^)J&!j~`jJ@MX|9-Y$>p}qQnjow^R#Z2};V%F@Lr*`}O_dwBK z`@TP7D&1r*^=5zF-&^Ie$B#b_oY7GJ{@%yD?T0^B^xSj&dqJpnZ`s_vZ9A+5>$cxc z+!(YvrGabbCeo`1yQ(?^ZL;TIZ;>VIsSZb2tkZs)U8c%&&Fx$aphnGUG~( zKfBA{f9Vmpwe73#165Wd&LPNL2tq%+pUA1bG znF;Ujf{IzQjg;=TGfz(XkkS@mm!77-x#^tc|6gCT=dEF%Q+?;|-LK!je_z>hG>QL^ zU%>tEMm`6tG!z*G)pxI(7`4et^x7sKx2|q$x&B)ZT%9v}K3-Cqy_Qq2=F6EJQ;t_< zVy|}bxdtluq!;dpckoaDFiGm}jk(Ir)^3k})Nm9;T-D#Qwcq*j{F+ZEOBZiaUZtS= z_J-T(O+BA%+#{!nuq{_?p0di^f7jAy_7B5s$;u0F;0`qDJ9y3PmvYz~bA>ULjx z8s&U1>J?SI?pe0~hWN#o3@qPk<#U~;{Mxcf_`2#L`_68zvWF>$yg#)(JolRKZN$|u zjkVFG%}UE` 643_000 { self.game.current_biome = Biome::Underground; @@ -244,10 +252,10 @@ impl App { } fn do_rendering(&mut self) { + self.game.light_pass(&mut self.light_map, &self.res); self.rt.clear(Color::rgb(55, 221, 231)); - self.game.render_pre_step(&mut self.res, self.rt.size()); self.game.draw_world(&mut self.rt, &mut self.res); - self.game.draw_entities(&mut self.rt, &mut self.res); + self.game.draw_entities(&mut self.rt); self.rt.display(); let mut spr = Sprite::with_texture(self.rt.texture()); spr.set_scale((self.scale as f32, self.scale as f32)); @@ -255,6 +263,12 @@ impl App { spr.set_position((vco.x as f32, vco.y as f32)); self.rw.clear(Color::rgb(40, 10, 70)); self.rw.draw(&spr); + // Draw light overlay with multiply blending + let mut rst = RenderStates::default(); + rst.blend_mode = BlendMode::MULTIPLY; + self.light_map.display(); + spr.set_texture(self.light_map.texture(), false); + self.rw.draw_with_renderstates(&spr, &rst); self.sf_egui .do_frame(|ctx| { if self.debug.panel { diff --git a/src/game.rs b/src/game.rs index d887eb5..27f9061 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,12 +1,10 @@ mod player; use derivative::Derivative; -use egui_inspect::{derive::Inspect, inspect}; +use egui_inspect::derive::Inspect; use sfml::{ graphics::{ - glsl::{Vec2, Vec4}, - Color, Rect, RectangleShape, RenderStates, RenderTarget, RenderTexture, Shape, Sprite, - Transformable, + Color, Rect, RectangleShape, RenderTarget, RenderTexture, Shape, Sprite, Transformable, }, system::{Clock, Vector2u}, SfBox, @@ -38,7 +36,12 @@ pub struct GameState { pub ambient_light: f32, #[opaque] pub clock: SfBox, - lighting_params: LightingData, + pub light_sources: Vec, +} + +#[derive(Debug, Inspect)] +pub struct LightSource { + pub pos: WorldPos, } #[derive(PartialEq, Eq, Clone, Copy, Debug, Inspect)] @@ -48,32 +51,26 @@ pub enum Biome { } impl GameState { - pub(crate) fn draw_world(&mut self, rw: &mut RenderTexture, res: &mut Res) { - let mut rs = RenderStates::default(); - res.lighting_shader.set_uniform_bool("has_texture", true); - rs.set_shader(Some(&res.lighting_shader)); + pub(crate) fn draw_world(&mut self, rt: &mut RenderTexture, res: &mut Res) { let mut s = Sprite::with_texture(&res.tile_atlas); - for_each_tile_on_screen(self.camera_offset, rw.size(), |tp, sp| { + for_each_tile_on_screen(self.camera_offset, rt.size(), |tp, sp| { let tile = self.world.tile_at_mut(tp, &self.worldgen); s.set_position(sp.to_sf_vec()); if tile.bg != Tile::EMPTY { s.set_texture_rect(Rect::new((tile.bg - 1) as i32 * 32, 0, 32, 32)); - rw.draw_with_renderstates(&s, &rs); + rt.draw(&s); } if tile.mid != Tile::EMPTY { s.set_texture_rect(Rect::new((tile.mid - 1) as i32 * 32, 0, 32, 32)); - rw.draw_with_renderstates(&s, &rs); + rt.draw(&s); } if tile.fg != Tile::EMPTY { s.set_texture_rect(Rect::new((tile.fg - 1) as i32 * 32, 0, 32, 32)); - rw.draw_with_renderstates(&s, &rs); + rt.draw(&s); } }); } - pub fn draw_entities(&mut self, rw: &mut RenderTexture, res: &mut Res) { - let mut rend_st = RenderStates::default(); - res.lighting_shader.set_uniform_bool("has_texture", false); - rend_st.set_shader(Some(&res.lighting_shader)); + pub fn draw_entities(&mut self, rw: &mut RenderTexture) { let (x, y, w, h) = self.player.col_en.en.xywh(); let mut rect_sh = RectangleShape::new(); rect_sh.set_position(( @@ -81,7 +78,7 @@ impl GameState { (y - self.camera_offset.y as i32) as f32, )); rect_sh.set_size((w as f32, h as f32)); - rw.draw_with_renderstates(&rect_sh, &rend_st); + rw.draw(&rect_sh); rect_sh.set_size((2., 2.)); rect_sh.set_fill_color(Color::RED); rect_sh.set_position(( @@ -90,56 +87,21 @@ impl GameState { )); rw.draw(&rect_sh); } - pub fn render_pre_step(&mut self, res: &mut Res, rt_size: Vector2u) { - res.lighting_shader.set_uniform_current_texture("texture"); - res.lighting_shader - .set_uniform_float("time", self.clock.elapsed_time().as_seconds() * 10.0); - res.lighting_shader.set_uniform_vec2( - "mouse", - Vec2::new(rt_size.x as f32 / 2.0, rt_size.y as f32 / 2.0), - ); - res.lighting_shader - .set_uniform_vec2("resolution", Vec2::new(rt_size.x as f32, rt_size.y as f32)); - res.lighting_shader - .set_uniform_vec4("lightData", self.lighting_params.light); - res.lighting_shader - .set_uniform_vec4("ambientData", self.lighting_params.ambient); - res.lighting_shader - .set_uniform_float("lightSize", self.lighting_params.size); - } -} -#[derive(Debug, Inspect)] -struct LightingData { - #[inspect_with(inspect_vec4)] - light: Vec4, - #[inspect_with(inspect_vec4)] - ambient: Vec4, - size: f32, -} - -fn inspect_vec4(val: &mut Vec4, ui: &mut egui::Ui, _id: u64) { - inspect! { - ui, val.x, val.y, val.z, val.w - } -} - -impl Default for LightingData { - fn default() -> Self { - Self { - light: Vec4 { - x: 1.0, - y: 0.8, - z: 0.2, - w: 2.0, - }, - ambient: Vec4 { - x: 0.3, - y: 0.3, - z: 0.8, - w: 0.3, - }, - size: 0.3, + pub(crate) fn light_pass(&mut self, lightmap: &mut RenderTexture, res: &Res) { + // Clear light map + // You can clear to a brighter color to increase ambient light level + lightmap.clear(Color::rgba(0, 0, 0, 255)); + for ls in &self.light_sources { + let (x, y) = ( + ls.pos.x as i32 - self.camera_offset.x as i32, + ls.pos.y as i32 - self.camera_offset.y as i32, + ); + let mut s = Sprite::with_texture(&res.light_texture); + s.set_scale((3.0, 3.0)); + s.set_origin((32., 32.)); + s.set_position((x as f32, y as f32)); + lightmap.draw(&s); } } } @@ -180,7 +142,7 @@ impl Default for GameState { worldgen: Worldgen::default(), ambient_light: 1.0, clock: Clock::start(), - lighting_params: Default::default(), + light_sources: Vec::new(), } } } diff --git a/src/input.rs b/src/input.rs index c362a8b..cb22515 100644 --- a/src/input.rs +++ b/src/input.rs @@ -10,6 +10,7 @@ pub struct Input { pub lmb_down: bool, pub rmb_down: bool, pub mouse_down_loc: ScreenPos, + pub mid_pressed: bool, } impl Input { @@ -33,6 +34,9 @@ impl Input { if button == mouse::Button::Right { self.rmb_down = true; } + if button == mouse::Button::Middle { + self.mid_pressed = true; + } } &Event::MouseButtonReleased { button, .. } => { if button == mouse::Button::Left { @@ -51,6 +55,7 @@ impl Input { } /// Pressed event should be cleared every frame pub fn clear_pressed(&mut self) { + self.mid_pressed = false; self.pressed.clear(); } pub fn down(&self, key: Key) -> bool { diff --git a/src/res.rs b/src/res.rs index 050f0c9..27f99e7 100644 --- a/src/res.rs +++ b/src/res.rs @@ -1,27 +1,20 @@ -use sfml::{ - audio::Music, - graphics::{Shader, ShaderType, Texture}, - SfBox, -}; +use sfml::{audio::Music, graphics::Texture, SfBox}; #[derive(Debug)] pub struct Res { pub tile_atlas: SfBox, + pub light_texture: SfBox, pub surf_music: Music<'static>, pub und_music: Music<'static>, - pub lighting_shader: Shader<'static>, } impl Res { pub fn load() -> anyhow::Result { Ok(Self { tile_atlas: Texture::from_file("res/tiles.png")?, + light_texture: Texture::from_file("res/lightcircle.png")?, surf_music: Music::from_file("res/music.ogg").unwrap(), und_music: Music::from_file("res/cave2.ogg").unwrap(), - lighting_shader: Shader::from_memory( - include_str!("../shaders/lighting.glsl"), - ShaderType::Fragment, - )?, }) } }