1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-05-06 06:14:29 +00:00

Water shader and motion interpolation

This commit is contained in:
Alula 2020-11-07 18:17:01 +01:00
parent 23602bde73
commit 7db891f5a1
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
15 changed files with 303 additions and 58 deletions

View file

@ -55,7 +55,7 @@ directories = "2"
gfx = "0.18"
gfx_core = "0.9"
gfx_device_gl = {git = "https://github.com/doukutsu-rs/gfx.git", branch = "pre-ll"}
ggez = {git = "https://github.com/doukutsu-rs/ggez.git", branch = "d-rs"}
ggez = {git = "https://github.com/doukutsu-rs/ggez.git", rev = "aad56b0d173ca9f4aeb28599075b5af49ab9214e"}
glutin = {git = "https://github.com/doukutsu-rs/glutin.git", branch = "android-support"}
imgui = {git = "https://github.com/JMS55/imgui-rs.git"}
imgui-gfx-renderer = {git = "https://github.com/JMS55/imgui-rs.git"}

View file

@ -0,0 +1,27 @@
#version 150 core
in vec2 a_Pos;
in vec2 a_Uv;
in vec4 a_Src;
in vec4 a_TCol1;
in vec4 a_TCol2;
in vec4 a_TCol3;
in vec4 a_TCol4;
in vec4 a_Color;
layout (std140) uniform Globals {
mat4 u_MVP;
};
out vec2 v_Uv;
out vec4 v_Color;
void main() {
v_Uv = a_Uv * a_Src.zw + a_Src.xy;
v_Color = a_Color;
mat4 instance_transform = mat4(a_TCol1, a_TCol2, a_TCol3, a_TCol4);
vec4 position = instance_transform * vec4(a_Pos, 0.0, 1.0);
gl_Position = u_MVP * position;
}

View file

@ -0,0 +1,26 @@
#version 150 core
uniform sampler2D t_Texture;
in vec2 v_Uv;
in vec4 v_Color;
out vec4 Target0;
layout (std140) uniform Globals {
mat4 u_MVP;
};
layout (std140) uniform WaterShaderParams {
vec2 u_Resolution;
float u_Tick;
};
void main() {
vec2 wave = v_Uv;
wave.x += sin(v_Uv.x * 40.0 + u_Tick / 20.0) * (sin(u_Tick / 10.0) * 0.01);
wave.y -= cos(v_Uv.y * 20.0 + u_Tick / 5.0) * (sin(u_Tick / 20.0) * 0.01);
float off = 0.4 / u_Resolution.y;
vec4 color = texture(t_Texture, wave);
color.r = texture(t_Texture, wave + off).r;
color.b = texture(t_Texture, wave - off).b;
Target0 = vec4(0.7, 0.8, 1.2, 1.0) * color * v_Color;
}

View file

@ -111,6 +111,10 @@ impl BuiltinFS {
FSNode::File("builtin_font_0.png", include_bytes!("builtin/builtin_font_0.png")),
FSNode::File("builtin_font_1.png", include_bytes!("builtin/builtin_font_1.png")),
FSNode::File("pixtone.pcm", include_bytes!("builtin/pixtone.pcm")),
FSNode::Directory("shaders", vec![
FSNode::File("basic_150.vert.glsl", include_bytes!("builtin/shaders/basic_150.vert.glsl")),
FSNode::File("water_150.frag.glsl", include_bytes!("builtin/shaders/water_150.frag.glsl")),
]),
FSNode::Directory("lightmap", vec![
FSNode::File("spot.png", include_bytes!("builtin/lightmap/spot.png")),
FSNode::File("direct.png", include_bytes!("builtin/lightmap/direct.png")),

View file

@ -55,6 +55,8 @@ pub struct Bullet {
pub vel_y: isize,
pub target_x: isize,
pub target_y: isize,
pub prev_x: isize,
pub prev_y: isize,
pub life: u16,
pub lifetime: u16,
pub damage: u16,
@ -97,6 +99,8 @@ impl Bullet {
vel_y: 0,
target_x: 0,
target_y: 0,
prev_x: x,
prev_y: y,
life: bullet.life as u16,
lifetime: bullet.lifetime,
damage: bullet.damage as u16,

View file

@ -35,6 +35,8 @@ pub struct Caret {
pub vel_y: isize,
pub offset_x: isize,
pub offset_y: isize,
pub prev_x: isize,
pub prev_y: isize,
pub cond: Condition,
pub direction: Direction,
pub anim_rect: Rect<usize>,
@ -54,6 +56,8 @@ impl Caret {
vel_y: 0,
offset_x,
offset_y,
prev_x: x,
prev_y: y,
cond: Condition(0x80),
direction: direct,
anim_rect: Rect::new(0, 0, 0, 0),

View file

@ -291,6 +291,7 @@ fn lerp_f64(v1: f64, v2: f64, t: f64) -> f64 {
}
#[inline(always)]
pub fn interpolate_fix9_scale(old_val: isize, val: isize, frame_delta: f64, scale: f32) -> f32 {
((lerp_f64(old_val as f64, val as f64, frame_delta) * scale as f64 / 512.0).floor() / (scale as f64)) as f32
pub fn interpolate_fix9_scale(old_val: isize, val: isize, frame_delta: f64) -> f32 {
(lerp_f64(old_val as f64, val as f64, frame_delta) / 512.0) as f32
//((lerp_f64(old_val as f64, val as f64, frame_delta) * scale as f64 / 512.0).floor() / (scale as f64)) as f32
}

View file

@ -1,7 +1,7 @@
use crate::player::Player;
use crate::shared_game_state::SharedGameState;
use crate::stage::Stage;
use crate::common::interpolate_fix9_scale;
use crate::common::{interpolate_fix9_scale, fix9_scale};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
@ -24,8 +24,12 @@ pub struct Frame {
impl Frame {
pub fn xy_interpolated(&self, frame_time: f64, scale: f32) -> (f32, f32) {
let x = interpolate_fix9_scale(self.prev_x, self.x, frame_time, scale);
let y = interpolate_fix9_scale(self.prev_y, self.y, frame_time, scale);
if self.prev_x == self.x && self.prev_y == self.y {
return (fix9_scale(self.x, scale), fix9_scale(self.y, scale));
}
let x = interpolate_fix9_scale(self.prev_x, self.x, frame_time);
let y = interpolate_fix9_scale(self.prev_y, self.y, frame_time);
(x, y)
}

View file

@ -68,6 +68,7 @@ struct Game {
def_matrix: ColumnMatrix4<f32>,
last_time: Instant,
start_time: Instant,
last_tick: u128,
next_tick: u128,
loops: u64,
}
@ -81,6 +82,7 @@ impl Game {
state: SharedGameState::new(ctx)?,
last_time: Instant::now(),
start_time: Instant::now(),
last_tick: 0,
next_tick: 0,
loops: 0,
};
@ -92,6 +94,8 @@ impl Game {
if let Some(scene) = self.scene.as_mut() {
match self.state.timing_mode {
TimingMode::_50Hz | TimingMode::_60Hz => {
let last_tick = self.next_tick;
while self.start_time.elapsed().as_nanos() >= self.next_tick && self.loops < 10 {
if (self.state.settings.speed - 1.0).abs() < 0.01 {
self.next_tick += self.state.timing_mode.get_delta() as u128;
@ -103,11 +107,16 @@ impl Game {
if self.loops == 10 {
log::warn!("Frame skip is way too high, a long system lag occurred?");
self.next_tick = self.start_time.elapsed().as_nanos()
+ (self.state.timing_mode.get_delta() as f64 / self.state.settings.speed) as u128;
self.last_tick = self.start_time.elapsed().as_nanos();
self.next_tick = self.last_tick + (self.state.timing_mode.get_delta() as f64 / self.state.settings.speed) as u128;
self.loops = 0;
}
if self.loops != 0 {
scene.draw_tick(&mut self.state, ctx)?;
self.last_tick = last_tick;
}
for _ in 0..self.loops {
scene.tick(&mut self.state, ctx)?;
}
@ -122,11 +131,9 @@ impl Game {
fn draw(&mut self, ctx: &mut Context) -> GameResult {
if self.state.timing_mode != TimingMode::FrameSynchronized {
let now = Instant::now();
let delta = (now.duration_since(self.last_time).as_nanos() as f64)
/ (self.state.timing_mode.get_delta() as f64 / self.state.settings.speed);
self.state.frame_time += delta;
self.last_time = now;
let n1 = (self.start_time.elapsed().as_nanos() - self.last_tick) as f64;
let n2 = (self.next_tick - self.last_tick) as f64;
self.state.frame_time = n1 / n2;
}
self.loops = 0;
@ -137,11 +144,6 @@ impl Game {
graphics::apply_transformations(ctx)?;
if let Some(scene) = self.scene.as_mut() {
if self.state.frame_time.floor() > self.state.prev_frame_time.floor() {
self.state.prev_frame_time = self.state.frame_time;
scene.draw_tick(&mut self.state, ctx)?;
}
scene.draw(&mut self.state, ctx)?;
if self.state.settings.touch_controls {
self.state.touch_controls.draw(&self.state.constants, &mut self.state.texture_set, ctx)?;
@ -350,7 +352,6 @@ pub fn init() -> GameResult {
if let Some(game) = &mut game {
game.loops = 0;
game.last_time = Instant::now();
}
}
Event::Suspended => {
@ -360,7 +361,6 @@ pub fn init() -> GameResult {
}
if let Some(game) = &mut game {
game.loops = 0;
game.last_time = Instant::now();
}
}
Event::WindowEvent { event, .. } => {
@ -373,8 +373,12 @@ pub fn init() -> GameResult {
}
WindowEvent::Resized(_) => {
if let (Some(ctx), Some(game)) = (&mut context, &mut game) {
game.state.handle_resize(ctx).unwrap();
let (w, h) = graphics::drawable_size(ctx);
game.state.tmp_canvas = Canvas::with_window_size(ctx).unwrap();
game.state.game_canvas = Canvas::with_window_size(ctx).unwrap();
game.state.lightmap_canvas = Canvas::with_window_size(ctx).unwrap();
game.state.handle_resize(ctx).unwrap();
graphics::window(ctx).update_gfx(&mut game.ui.main_color, &mut game.ui.main_depth);
}
}
@ -440,7 +444,6 @@ pub fn init() -> GameResult {
game.scene.as_mut().unwrap().init(&mut game.state, ctx).unwrap();
game.loops = 0;
game.state.frame_time = 0.0;
game.state.prev_frame_time = 0.0;
}
}
}

View file

@ -88,10 +88,10 @@ impl GameEntity<(&mut Player, &BTreeMap<u16, RefCell<NPC>>, &mut Stage)> for Bos
batch.add_rect(
interpolate_fix9_scale(npc.prev_x - off_x - frame.prev_x,
npc.x - off_x - frame.x,
state.frame_time, state.scale) + shock,
state.frame_time) + shock,
interpolate_fix9_scale(npc.prev_y - npc.display_bounds.top as isize - frame.prev_y,
npc.y - npc.display_bounds.top as isize - frame.y,
state.frame_time, state.scale),
state.frame_time),
&npc.anim_rect,
);
}

View file

@ -294,10 +294,10 @@ impl GameEntity<(&mut Player, &BTreeMap<u16, RefCell<NPC>>, &mut Stage)> for NPC
batch.add_rect(
interpolate_fix9_scale(self.prev_x - off_x - frame.prev_x,
self.x - off_x - frame.x,
state.frame_time, state.scale) + shock,
state.frame_time) + shock,
interpolate_fix9_scale(self.prev_y - self.display_bounds.top as isize - frame.prev_y,
self.y - self.display_bounds.top as isize - frame.y,
state.frame_time, state.scale),
state.frame_time),
&self.anim_rect,
);
batch.draw(ctx)?;

View file

@ -132,7 +132,7 @@ impl Player {
if self.equip.has_air_tank() {
self.air = 1000;
self.air_counter = 0;
} else if self.flags.in_water() {
} else if !state.settings.god_mode && self.flags.in_water() {
self.air_counter = 60;
if self.air > 0 {
self.air -= 1;
@ -651,10 +651,10 @@ impl GameEntity<()> for Player {
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as isize - frame.prev_x,
self.x - self.display_bounds.left as isize - frame.x,
state.frame_time, state.scale),
state.frame_time),
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as isize - frame.prev_y,
self.y - self.display_bounds.left as isize - frame.y,
state.frame_time, state.scale),
state.frame_time),
&self.anim_rect,
);
batch.draw(ctx)?;
@ -667,10 +667,10 @@ impl GameEntity<()> for Player {
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as isize - frame.prev_x,
self.x - self.display_bounds.left as isize - frame.x,
state.frame_time, state.scale) - 8.0,
state.frame_time) - 8.0,
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as isize - frame.prev_y,
self.y - self.display_bounds.left as isize - frame.y,
state.frame_time, state.scale) + self.weapon_offset_y as f32,
state.frame_time) + self.weapon_offset_y as f32,
&self.weapon_rect,
);
}
@ -678,10 +678,10 @@ impl GameEntity<()> for Player {
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as isize - frame.prev_x,
self.x - self.display_bounds.left as isize - frame.x,
state.frame_time, state.scale),
state.frame_time),
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as isize - frame.prev_y,
self.y - self.display_bounds.left as isize - frame.y,
state.frame_time, state.scale) + self.weapon_offset_y as f32,
state.frame_time) + self.weapon_offset_y as f32,
&self.weapon_rect,
);
}

View file

@ -1,13 +1,14 @@
use std::ops::Deref;
use ggez::{Context, GameResult, graphics, timer};
use ggez::graphics::{BlendMode, Color, Drawable, DrawParam, FilterMode};
use ggez::graphics::{BlendMode, Color, Drawable, DrawParam, FilterMode, mint};
use ggez::graphics::spritebatch::SpriteBatch;
use ggez::nalgebra::{clamp, Vector2};
use log::info;
use crate::bullet::BulletManager;
use crate::caret::CaretType;
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, Rect};
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, interpolate_fix9_scale, Rect};
use crate::components::boss_life_bar::BossLifeBar;
use crate::entity::GameEntity;
use crate::frame::{Frame, UpdateTarget};
@ -37,6 +38,7 @@ pub struct GameScene {
pub bullet_manager: BulletManager,
pub current_teleport_slot: u8,
pub intro_mode: bool,
water_visible: bool,
tex_background_name: String,
tex_tileset_name: String,
life_bar: u16,
@ -93,6 +95,7 @@ impl GameScene {
bullet_manager: BulletManager::new(),
current_teleport_slot: 0,
intro_mode: false,
water_visible: true,
tex_background_name,
tex_tileset_name,
life_bar: 0,
@ -235,6 +238,8 @@ impl GameScene {
match self.stage.data.background_type {
BackgroundType::Stationary => {
graphics::clear(ctx, Color::from_rgb(0, 0, 0));
let count_x = state.canvas_size.0 as usize / batch.width() + 1;
let count_y = state.canvas_size.1 as usize / batch.height() + 1;
@ -245,6 +250,8 @@ impl GameScene {
}
}
BackgroundType::MoveDistant | BackgroundType::MoveNear => {
graphics::clear(ctx, Color::from_rgb(0, 0, 0));
let (off_x, off_y) = if self.stage.data.background_type == BackgroundType::MoveNear {
(
frame_x % (batch.width() as f32),
@ -267,15 +274,24 @@ impl GameScene {
}
}
}
BackgroundType::Water => {}
BackgroundType::Water => {
graphics::clear(ctx, Color::from_rgb(0, 0, 32));
}
BackgroundType::Black => {
graphics::clear(ctx, Color::from_rgb(0, 0, 32));
}
BackgroundType::Autoscroll => {}
BackgroundType::OutsideWind | BackgroundType::Outside => {
graphics::clear(ctx, Color::from_rgb(0, 0, 0));
let offset = (self.tick % 640) as isize;
batch.add_rect(((state.canvas_size.0 - 320.0) / 2.0).floor(), 0.0,
for x in (0..(state.canvas_size.0 as isize)).step_by(200) {
batch.add_rect(x as f32, 0.0,
&Rect::<usize>::new_size(0, 0, 200, 88));
}
batch.add_rect(state.canvas_size.0 - 320.0, 0.0,
&Rect::<usize>::new_size(0, 0, 320, 88));
for x in ((-offset / 2)..(state.canvas_size.0 as isize)).step_by(320) {
@ -310,30 +326,44 @@ impl GameScene {
let scale = state.scale;
let mut x: isize;
let mut y: isize;
let mut prev_x: isize;
let mut prev_y: isize;
for bullet in self.bullet_manager.bullets.iter() {
match bullet.direction {
Direction::Left => {
x = bullet.x - bullet.display_bounds.left as isize;
y = bullet.y - bullet.display_bounds.top as isize;
prev_x = bullet.prev_x - bullet.display_bounds.left as isize;
prev_y = bullet.prev_y - bullet.display_bounds.top as isize;
}
Direction::Up => {
x = bullet.x - bullet.display_bounds.top as isize;
y = bullet.y - bullet.display_bounds.left as isize;
prev_x = bullet.prev_x - bullet.display_bounds.top as isize;
prev_y = bullet.prev_y - bullet.display_bounds.left as isize;
}
Direction::Right => {
x = bullet.x - bullet.display_bounds.right as isize;
y = bullet.y - bullet.display_bounds.top as isize;
prev_x = bullet.prev_x - bullet.display_bounds.right as isize;
prev_y = bullet.prev_y - bullet.display_bounds.top as isize;
}
Direction::Bottom => {
x = bullet.x - bullet.display_bounds.top as isize;
y = bullet.y - bullet.display_bounds.right as isize;
prev_x = bullet.prev_x - bullet.display_bounds.top as isize;
prev_y = bullet.prev_y - bullet.display_bounds.right as isize;
}
Direction::FacingPlayer => unreachable!(),
}
batch.add_rect(fix9_scale(x - self.frame.x, scale),
fix9_scale(y - self.frame.y, scale),
batch.add_rect(interpolate_fix9_scale(prev_x - self.frame.prev_x,
x - self.frame.x,
state.frame_time),
interpolate_fix9_scale(prev_y - self.frame.prev_y,
y - self.frame.y,
state.frame_time),
&bullet.anim_rect);
}
@ -346,8 +376,12 @@ impl GameScene {
let scale = state.scale;
for caret in state.carets.iter() {
batch.add_rect(fix9_scale(caret.x - caret.offset_x - self.frame.x, scale),
fix9_scale(caret.y - caret.offset_y - self.frame.y, scale),
batch.add_rect(interpolate_fix9_scale(caret.prev_x - caret.offset_x - self.frame.prev_x,
caret.x - caret.offset_x - self.frame.x,
state.frame_time),
interpolate_fix9_scale(caret.prev_y - caret.offset_y - self.frame.prev_y,
caret.y - caret.offset_y - self.frame.y,
state.frame_time),
&caret.anim_rect);
}
@ -708,8 +742,8 @@ impl GameScene {
batch.draw_filtered(FilterMode::Linear, ctx)?;
}
graphics::set_canvas(ctx, None);
graphics::set_blend_mode(ctx, BlendMode::Multiply)?;
graphics::set_canvas(ctx, Some(&state.game_canvas));
state.lightmap_canvas.set_filter(FilterMode::Linear);
state.lightmap_canvas.draw(ctx, DrawParam::new()
.scale(Vector2::new(1.0 / state.scale, 1.0 / state.scale)))?;
@ -719,6 +753,84 @@ impl GameScene {
Ok(())
}
fn is_water(&self, tile: u8) -> bool {
[0x02, 0x04, 0x60, 0x61, 0x62, 0x64, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0xa0, 0xa1, 0xa2, 0xa3].contains(&tile)
}
fn draw_water(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
{
state.shaders.water_shader_params.resolution = [state.canvas_size.0, state.canvas_size.1];
state.shaders.water_shader_params.t = self.tick as f32;
let _lock = graphics::use_shader(ctx, &state.shaders.water_shader);
state.shaders.water_shader.send(ctx, state.shaders.water_shader_params)?;
graphics::set_canvas(ctx, Some(&state.tmp_canvas));
graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 1.0));
state.game_canvas.draw(ctx, DrawParam::new()
.scale(mint::Vector2 { x: 1.0 / state.scale, y: -1.0 / state.scale })
.offset(mint::Point2 { x: 0.0, y: -1.0 }))?;
}
graphics::set_canvas(ctx, Some(&state.game_canvas));
// cheap, clones a reference underneath
let mut tmp_batch = SpriteBatch::new(state.tmp_canvas.image().clone());
let (frame_x, frame_y) = self.frame.xy_interpolated(state.frame_time, state.scale);
let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as isize) as usize;
let tile_start_y = clamp(self.frame.y / 0x200 / 16, 0, self.stage.map.height as isize) as usize;
let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as isize) / 16 + 1, 0, self.stage.map.width as isize) as usize;
let tile_end_y = clamp((self.frame.y / 0x200 + 8 + state.canvas_size.1 as isize) / 16 + 1, 0, self.stage.map.height as isize) as usize;
let mut rect = Rect {
left: 0.0,
top: 0.0,
right: 16.0,
bottom: 16.0,
};
for y in tile_start_y..tile_end_y {
for x in tile_start_x..tile_end_x {
let tile = self.stage.map.attrib[*self.stage.map.tiles
.get((y * self.stage.map.width) + x)
.unwrap() as usize];
let tile_above = self.stage.map.attrib[*self.stage.map.tiles
.get((y.saturating_sub(1) * self.stage.map.width) + x)
.unwrap() as usize];
if !self.is_water(tile) {
continue;
}
rect.left = (x as f32 * 16.0 - 8.0) - frame_x;
rect.top = (y as f32 * 16.0 - 8.0) - frame_y;
rect.right = rect.left + 16.0;
rect.bottom = rect.top + 16.0;
if tile_above == 0 {
rect.top += 3.0;
}
tmp_batch.add(DrawParam::new()
.src(ggez::graphics::Rect::new(rect.left / state.canvas_size.0,
rect.top / state.canvas_size.1,
(rect.right - rect.left) / state.canvas_size.0,
(rect.bottom - rect.top) / state.canvas_size.1))
.scale(mint::Vector2 {
x: 1.0 / state.scale,
y: 1.0 / state.scale,
})
.dest(mint::Point2 {
x: rect.left,
y: rect.top,
}));
}
}
tmp_batch.draw(ctx, DrawParam::new())?;
Ok(())
}
fn draw_tiles(&self, state: &mut SharedGameState, ctx: &mut Context, layer: TileLayer) -> GameResult {
let tex = match layer {
TileLayer::Snack => "Npc/NpcSym",
@ -1310,6 +1422,20 @@ impl Scene for GameScene {
}
}
for bullet in self.bullet_manager.bullets.iter_mut() {
if bullet.cond.alive() {
bullet.prev_x = bullet.x;
bullet.prev_y = bullet.y;
}
}
for caret in state.carets.iter_mut() {
if caret.cond.alive() {
caret.prev_x = caret.x;
caret.prev_y = caret.y;
}
}
Ok(())
}
@ -1358,9 +1484,10 @@ impl Scene for GameScene {
}
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
graphics::set_canvas(ctx, Some(&state.game_canvas));
self.draw_background(state, ctx)?;
self.draw_tiles(state, ctx, TileLayer::Background)?;
if state.settings.lighting_efects
if state.settings.shader_effects
&& self.stage.data.background_type != BackgroundType::Black
&& self.stage.data.background_type != BackgroundType::Outside
&& self.stage.data.background_type != BackgroundType::OutsideWind {
@ -1382,14 +1509,21 @@ impl Scene for GameScene {
}
self.draw_bullets(state, ctx)?;
self.player.draw(state, ctx, &self.frame)?;
if state.settings.shader_effects && self.water_visible {
self.draw_water(state, ctx)?;
}
self.draw_tiles(state, ctx, TileLayer::Foreground)?;
self.draw_tiles(state, ctx, TileLayer::Snack)?;
self.draw_carets(state, ctx)?;
if state.settings.lighting_efects
if state.settings.shader_effects
&& self.stage.data.background_type == BackgroundType::Black {
self.draw_light_map(state, ctx)?;
}
graphics::set_canvas(ctx, None);
state.game_canvas.draw(ctx, DrawParam::new()
.scale(Vector2::new(1.0 / state.scale, 1.0 / state.scale)))?;
self.draw_black_bars(state, ctx)?;
if state.control_flags.control_enabled() {

View file

@ -93,7 +93,7 @@ impl Scene for TitleScene {
self.main_menu.height = self.main_menu.entries.len() * 14 + 6;
self.option_menu.push_entry(MenuEntry::Toggle("Original timing (50TPS)".to_string(), state.timing_mode == TimingMode::_50Hz));
self.option_menu.push_entry(MenuEntry::Toggle("Lighting effects".to_string(), state.settings.lighting_efects));
self.option_menu.push_entry(MenuEntry::Toggle("Lighting effects".to_string(), state.settings.shader_effects));
if state.constants.is_cs_plus {
self.option_menu.push_entry(MenuEntry::Toggle("Freeware textures".to_string(), state.settings.original_textures));
self.option_menu.push_entry(MenuEntry::Toggle("Seasonal textures".to_string(), state.settings.seasonal_textures));
@ -151,9 +151,9 @@ impl Scene for TitleScene {
}
MenuSelectionResult::Selected(1, toggle) => {
if let MenuEntry::Toggle(_, value) = toggle {
state.settings.lighting_efects = !state.settings.lighting_efects;
state.settings.shader_effects = !state.settings.shader_effects;
*value = state.settings.lighting_efects;
*value = state.settings.shader_effects;
}
}
MenuSelectionResult::Selected(2, toggle) => {

View file

@ -1,17 +1,17 @@
use std::io::Seek;
use std::ops::Div;
use std::time::{Instant, SystemTime};
use std::time::Instant;
use bitvec::vec::BitVec;
use chrono::{Local, Datelike};
use chrono::{Datelike, Local};
use gfx::{self, *};
use ggez::{Context, filesystem, GameResult, graphics};
use ggez::filesystem::OpenOptions;
use ggez::graphics::{Canvas, Shader};
use crate::bmfont_renderer::BMFontRenderer;
use crate::caret::{Caret, CaretType};
use crate::common::{ControlFlags, Direction, FadeState, KeyState};
use crate::engine_constants::EngineConstants;
use ggez::{Context, filesystem, GameResult, graphics};
use ggez::filesystem::OpenOptions;
use ggez::graphics::Canvas;
use crate::npc::{NPC, NPCTable};
use crate::profile::GameProfile;
use crate::rng::RNG;
@ -77,11 +77,45 @@ pub struct Settings {
pub speed: f64,
pub seasonal_textures: bool,
pub original_textures: bool,
pub lighting_efects: bool,
pub shader_effects: bool,
pub motion_interpolation: bool,
pub debug_outlines: bool,
pub touch_controls: bool,
}
gfx_defines! {
constant WaterShaderParams {
resolution: [f32; 2] = "u_Resolution",
t: f32 = "u_Tick",
}
}
pub struct Shaders {
pub water_shader: Shader<WaterShaderParams>,
pub water_shader_params: WaterShaderParams,
}
impl Shaders {
pub fn new(ctx: &mut Context) -> GameResult<Shaders> {
let water_shader_params = WaterShaderParams {
t: 0.0,
resolution: [0.0, 0.0],
};
Ok(Shaders {
water_shader: Shader::new(
ctx,
"/builtin/shaders/basic_150.vert.glsl",
"/builtin/shaders/water_150.frag.glsl",
water_shader_params,
"WaterShaderParams",
None,
)?,
water_shader_params,
})
}
}
pub struct SharedGameState {
pub timing_mode: TimingMode,
pub control_flags: ControlFlags,
@ -103,8 +137,10 @@ pub struct SharedGameState {
pub stages: Vec<StageData>,
pub new_npcs: Vec<NPC>,
pub frame_time: f64,
pub prev_frame_time: f64,
pub scale: f32,
pub shaders: Shaders,
pub tmp_canvas: Canvas,
pub game_canvas: Canvas,
pub lightmap_canvas: Canvas,
pub canvas_size: (f32, f32),
pub screen_size: (f32, f32),
@ -175,8 +211,10 @@ impl SharedGameState {
stages: Vec::with_capacity(96),
new_npcs: Vec::with_capacity(8),
frame_time: 0.0,
prev_frame_time: 0.0,
scale,
shaders: Shaders::new(ctx)?,
tmp_canvas: Canvas::with_window_size(ctx)?,
game_canvas: Canvas::with_window_size(ctx)?,
lightmap_canvas: Canvas::with_window_size(ctx)?,
screen_size,
canvas_size,
@ -200,7 +238,8 @@ impl SharedGameState {
speed: 1.0,
seasonal_textures: true,
original_textures: false,
lighting_efects: true,
shader_effects: true,
motion_interpolation: true,
debug_outlines: false,
touch_controls: cfg!(target_os = "android"),
})
@ -324,7 +363,6 @@ impl SharedGameState {
pub fn set_speed(&mut self, value: f64) {
self.settings.speed = value;
self.frame_time = 0.0;
self.prev_frame_time = 0.0;
if let Err(err) = self.sound_manager.set_speed(value as f32) {
log::error!("Error while sending a message to sound manager: {}", err);