mirror of
https://github.com/Noratrieb/game-wip-dontplay.git
synced 2026-01-16 12:25:02 +01:00
Switch to lightmap texture based lighting
This commit is contained in:
parent
907a5da105
commit
f9a99fc0ae
6 changed files with 56 additions and 128 deletions
BIN
res/lightcircle.png
Normal file
BIN
res/lightcircle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
|
|
@ -1,46 +0,0 @@
|
||||||
// Based on https://github.com/romrz/Basic-2D-lighting
|
|
||||||
|
|
||||||
uniform sampler2D texture;
|
|
||||||
uniform vec2 resolution;
|
|
||||||
uniform vec2 mouse;
|
|
||||||
uniform float time;
|
|
||||||
uniform bool has_texture;
|
|
||||||
|
|
||||||
// The RGB values are the ambient light color
|
|
||||||
// and the alpha is the ambient intensity
|
|
||||||
uniform vec4 ambientData;
|
|
||||||
// The RGB values are the light color
|
|
||||||
// and the alpha is the light intensity
|
|
||||||
uniform vec4 lightData;
|
|
||||||
// Maximum radius of the light
|
|
||||||
uniform float lightSize;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// Light's position
|
|
||||||
vec2 position = mouse/resolution.xx;
|
|
||||||
|
|
||||||
// Makes the light change its size slightly to make a fire effect
|
|
||||||
float maxDistance = lightSize + 0.015*sin(time);
|
|
||||||
// Gets the distance from the light's position and the fragment coord
|
|
||||||
float distance = distance(gl_FragCoord.xy/resolution.xx, position);
|
|
||||||
// Calculates the amount of light for the fragment
|
|
||||||
float value = 1.0 - smoothstep(-0.2, maxDistance, distance);
|
|
||||||
|
|
||||||
// Gets the original color from the texture
|
|
||||||
vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);
|
|
||||||
if (!has_texture) {
|
|
||||||
pixel = gl_Color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Applies the ambient light to the original pixel color
|
|
||||||
vec3 ambient = pixel.rgb * ambientData.rgb * ambientData.a;
|
|
||||||
|
|
||||||
// Calculates the light color for the pixel
|
|
||||||
vec3 light = lightData.rgb * lightData.a * clamp(value, 0.0, 1.0);
|
|
||||||
|
|
||||||
// Applies the light to the pixel
|
|
||||||
vec3 intensity = ambient + light;
|
|
||||||
vec4 final = pixel * vec4(intensity.r, intensity.g, intensity.b, 1.0);
|
|
||||||
|
|
||||||
gl_FragColor = final;
|
|
||||||
}
|
|
||||||
22
src/app.rs
22
src/app.rs
|
|
@ -7,7 +7,8 @@ use gamedebug_core::{imm, imm_dbg};
|
||||||
use sfml::{
|
use sfml::{
|
||||||
audio::SoundSource,
|
audio::SoundSource,
|
||||||
graphics::{
|
graphics::{
|
||||||
Color, Rect, RenderTarget, RenderTexture, RenderWindow, Sprite, Transformable, View,
|
BlendMode, Color, Rect, RenderStates, RenderTarget, RenderTexture, RenderWindow, Sprite,
|
||||||
|
Transformable, View,
|
||||||
},
|
},
|
||||||
system::Vector2u,
|
system::Vector2u,
|
||||||
window::{Event, Key},
|
window::{Event, Key},
|
||||||
|
|
@ -15,7 +16,7 @@ use sfml::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::DebugState,
|
debug::DebugState,
|
||||||
game::{for_each_tile_on_screen, Biome, GameState},
|
game::{for_each_tile_on_screen, Biome, GameState, LightSource},
|
||||||
graphics::{self, ScreenPos, ScreenPosScalar},
|
graphics::{self, ScreenPos, ScreenPosScalar},
|
||||||
input::Input,
|
input::Input,
|
||||||
math::{center_offset, px_per_frame_to_km_h, WorldPos, M_PER_PX, TILE_SIZE},
|
math::{center_offset, px_per_frame_to_km_h, WorldPos, M_PER_PX, TILE_SIZE},
|
||||||
|
|
@ -36,6 +37,8 @@ pub struct App {
|
||||||
pub scale: u8,
|
pub scale: u8,
|
||||||
/// RenderTexture for rendering the game at its native resolution
|
/// RenderTexture for rendering the game at its native resolution
|
||||||
pub rt: RenderTexture,
|
pub rt: RenderTexture,
|
||||||
|
/// Light map overlay, blended together with the non-lighted version of the scene
|
||||||
|
pub light_map: RenderTexture,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
@ -49,6 +52,8 @@ impl App {
|
||||||
let rw_size = rw.size();
|
let rw_size = rw.size();
|
||||||
let rt =
|
let rt =
|
||||||
RenderTexture::new(rw_size.x, rw_size.y).context("Failed to create render texture")?;
|
RenderTexture::new(rw_size.x, rw_size.y).context("Failed to create render texture")?;
|
||||||
|
let light_map = RenderTexture::new(rw_size.x, rw_size.y)
|
||||||
|
.context("Failed to create lightmap texture")?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
rw,
|
rw,
|
||||||
should_quit: false,
|
should_quit: false,
|
||||||
|
|
@ -59,6 +64,7 @@ impl App {
|
||||||
debug: DebugState::default(),
|
debug: DebugState::default(),
|
||||||
scale: 1,
|
scale: 1,
|
||||||
rt,
|
rt,
|
||||||
|
light_map,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,6 +204,8 @@ impl App {
|
||||||
} else {
|
} else {
|
||||||
t.bg = self.game.tile_to_place;
|
t.bg = self.game.tile_to_place;
|
||||||
}
|
}
|
||||||
|
} else if self.input.mid_pressed {
|
||||||
|
self.game.light_sources.push(LightSource { pos: wpos });
|
||||||
}
|
}
|
||||||
if self.game.camera_offset.y > 643_000 {
|
if self.game.camera_offset.y > 643_000 {
|
||||||
self.game.current_biome = Biome::Underground;
|
self.game.current_biome = Biome::Underground;
|
||||||
|
|
@ -244,10 +252,10 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_rendering(&mut self) {
|
fn do_rendering(&mut self) {
|
||||||
|
self.game.light_pass(&mut self.light_map, &self.res);
|
||||||
self.rt.clear(Color::rgb(55, 221, 231));
|
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_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();
|
self.rt.display();
|
||||||
let mut spr = Sprite::with_texture(self.rt.texture());
|
let mut spr = Sprite::with_texture(self.rt.texture());
|
||||||
spr.set_scale((self.scale as f32, self.scale as f32));
|
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));
|
spr.set_position((vco.x as f32, vco.y as f32));
|
||||||
self.rw.clear(Color::rgb(40, 10, 70));
|
self.rw.clear(Color::rgb(40, 10, 70));
|
||||||
self.rw.draw(&spr);
|
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
|
self.sf_egui
|
||||||
.do_frame(|ctx| {
|
.do_frame(|ctx| {
|
||||||
if self.debug.panel {
|
if self.debug.panel {
|
||||||
|
|
|
||||||
98
src/game.rs
98
src/game.rs
|
|
@ -1,12 +1,10 @@
|
||||||
mod player;
|
mod player;
|
||||||
|
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use egui_inspect::{derive::Inspect, inspect};
|
use egui_inspect::derive::Inspect;
|
||||||
use sfml::{
|
use sfml::{
|
||||||
graphics::{
|
graphics::{
|
||||||
glsl::{Vec2, Vec4},
|
Color, Rect, RectangleShape, RenderTarget, RenderTexture, Shape, Sprite, Transformable,
|
||||||
Color, Rect, RectangleShape, RenderStates, RenderTarget, RenderTexture, Shape, Sprite,
|
|
||||||
Transformable,
|
|
||||||
},
|
},
|
||||||
system::{Clock, Vector2u},
|
system::{Clock, Vector2u},
|
||||||
SfBox,
|
SfBox,
|
||||||
|
|
@ -38,7 +36,12 @@ pub struct GameState {
|
||||||
pub ambient_light: f32,
|
pub ambient_light: f32,
|
||||||
#[opaque]
|
#[opaque]
|
||||||
pub clock: SfBox<Clock>,
|
pub clock: SfBox<Clock>,
|
||||||
lighting_params: LightingData,
|
pub light_sources: Vec<LightSource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Inspect)]
|
||||||
|
pub struct LightSource {
|
||||||
|
pub pos: WorldPos,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Inspect)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug, Inspect)]
|
||||||
|
|
@ -48,32 +51,26 @@ pub enum Biome {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameState {
|
impl GameState {
|
||||||
pub(crate) fn draw_world(&mut self, rw: &mut RenderTexture, res: &mut Res) {
|
pub(crate) fn draw_world(&mut self, rt: &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));
|
|
||||||
let mut s = Sprite::with_texture(&res.tile_atlas);
|
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);
|
let tile = self.world.tile_at_mut(tp, &self.worldgen);
|
||||||
s.set_position(sp.to_sf_vec());
|
s.set_position(sp.to_sf_vec());
|
||||||
if tile.bg != Tile::EMPTY {
|
if tile.bg != Tile::EMPTY {
|
||||||
s.set_texture_rect(Rect::new((tile.bg - 1) as i32 * 32, 0, 32, 32));
|
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 {
|
if tile.mid != Tile::EMPTY {
|
||||||
s.set_texture_rect(Rect::new((tile.mid - 1) as i32 * 32, 0, 32, 32));
|
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 {
|
if tile.fg != Tile::EMPTY {
|
||||||
s.set_texture_rect(Rect::new((tile.fg - 1) as i32 * 32, 0, 32, 32));
|
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) {
|
pub fn draw_entities(&mut self, rw: &mut RenderTexture) {
|
||||||
let mut rend_st = RenderStates::default();
|
|
||||||
res.lighting_shader.set_uniform_bool("has_texture", false);
|
|
||||||
rend_st.set_shader(Some(&res.lighting_shader));
|
|
||||||
let (x, y, w, h) = self.player.col_en.en.xywh();
|
let (x, y, w, h) = self.player.col_en.en.xywh();
|
||||||
let mut rect_sh = RectangleShape::new();
|
let mut rect_sh = RectangleShape::new();
|
||||||
rect_sh.set_position((
|
rect_sh.set_position((
|
||||||
|
|
@ -81,7 +78,7 @@ impl GameState {
|
||||||
(y - self.camera_offset.y as i32) as f32,
|
(y - self.camera_offset.y as i32) as f32,
|
||||||
));
|
));
|
||||||
rect_sh.set_size((w as f32, h 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_size((2., 2.));
|
||||||
rect_sh.set_fill_color(Color::RED);
|
rect_sh.set_fill_color(Color::RED);
|
||||||
rect_sh.set_position((
|
rect_sh.set_position((
|
||||||
|
|
@ -90,56 +87,21 @@ impl GameState {
|
||||||
));
|
));
|
||||||
rw.draw(&rect_sh);
|
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)]
|
pub(crate) fn light_pass(&mut self, lightmap: &mut RenderTexture, res: &Res) {
|
||||||
struct LightingData {
|
// Clear light map
|
||||||
#[inspect_with(inspect_vec4)]
|
// You can clear to a brighter color to increase ambient light level
|
||||||
light: Vec4,
|
lightmap.clear(Color::rgba(0, 0, 0, 255));
|
||||||
#[inspect_with(inspect_vec4)]
|
for ls in &self.light_sources {
|
||||||
ambient: Vec4,
|
let (x, y) = (
|
||||||
size: f32,
|
ls.pos.x as i32 - self.camera_offset.x as i32,
|
||||||
}
|
ls.pos.y as i32 - self.camera_offset.y as i32,
|
||||||
|
);
|
||||||
fn inspect_vec4(val: &mut Vec4, ui: &mut egui::Ui, _id: u64) {
|
let mut s = Sprite::with_texture(&res.light_texture);
|
||||||
inspect! {
|
s.set_scale((3.0, 3.0));
|
||||||
ui, val.x, val.y, val.z, val.w
|
s.set_origin((32., 32.));
|
||||||
}
|
s.set_position((x as f32, y as f32));
|
||||||
}
|
lightmap.draw(&s);
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +142,7 @@ impl Default for GameState {
|
||||||
worldgen: Worldgen::default(),
|
worldgen: Worldgen::default(),
|
||||||
ambient_light: 1.0,
|
ambient_light: 1.0,
|
||||||
clock: Clock::start(),
|
clock: Clock::start(),
|
||||||
lighting_params: Default::default(),
|
light_sources: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ pub struct Input {
|
||||||
pub lmb_down: bool,
|
pub lmb_down: bool,
|
||||||
pub rmb_down: bool,
|
pub rmb_down: bool,
|
||||||
pub mouse_down_loc: ScreenPos,
|
pub mouse_down_loc: ScreenPos,
|
||||||
|
pub mid_pressed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
|
|
@ -33,6 +34,9 @@ impl Input {
|
||||||
if button == mouse::Button::Right {
|
if button == mouse::Button::Right {
|
||||||
self.rmb_down = true;
|
self.rmb_down = true;
|
||||||
}
|
}
|
||||||
|
if button == mouse::Button::Middle {
|
||||||
|
self.mid_pressed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&Event::MouseButtonReleased { button, .. } => {
|
&Event::MouseButtonReleased { button, .. } => {
|
||||||
if button == mouse::Button::Left {
|
if button == mouse::Button::Left {
|
||||||
|
|
@ -51,6 +55,7 @@ impl Input {
|
||||||
}
|
}
|
||||||
/// Pressed event should be cleared every frame
|
/// Pressed event should be cleared every frame
|
||||||
pub fn clear_pressed(&mut self) {
|
pub fn clear_pressed(&mut self) {
|
||||||
|
self.mid_pressed = false;
|
||||||
self.pressed.clear();
|
self.pressed.clear();
|
||||||
}
|
}
|
||||||
pub fn down(&self, key: Key) -> bool {
|
pub fn down(&self, key: Key) -> bool {
|
||||||
|
|
|
||||||
13
src/res.rs
13
src/res.rs
|
|
@ -1,27 +1,20 @@
|
||||||
use sfml::{
|
use sfml::{audio::Music, graphics::Texture, SfBox};
|
||||||
audio::Music,
|
|
||||||
graphics::{Shader, ShaderType, Texture},
|
|
||||||
SfBox,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Res {
|
pub struct Res {
|
||||||
pub tile_atlas: SfBox<Texture>,
|
pub tile_atlas: SfBox<Texture>,
|
||||||
|
pub light_texture: SfBox<Texture>,
|
||||||
pub surf_music: Music<'static>,
|
pub surf_music: Music<'static>,
|
||||||
pub und_music: Music<'static>,
|
pub und_music: Music<'static>,
|
||||||
pub lighting_shader: Shader<'static>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Res {
|
impl Res {
|
||||||
pub fn load() -> anyhow::Result<Self> {
|
pub fn load() -> anyhow::Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
tile_atlas: Texture::from_file("res/tiles.png")?,
|
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(),
|
surf_music: Music::from_file("res/music.ogg").unwrap(),
|
||||||
und_music: Music::from_file("res/cave2.ogg").unwrap(),
|
und_music: Music::from_file("res/cave2.ogg").unwrap(),
|
||||||
lighting_shader: Shader::from_memory(
|
|
||||||
include_str!("../shaders/lighting.glsl"),
|
|
||||||
ShaderType::Fragment,
|
|
||||||
)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue