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:
parent
23602bde73
commit
7db891f5a1
|
@ -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"}
|
||||
|
|
27
src/builtin/shaders/basic_150.vert.glsl
Normal file
27
src/builtin/shaders/basic_150.vert.glsl
Normal 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;
|
||||
}
|
26
src/builtin/shaders/water_150.frag.glsl
Normal file
26
src/builtin/shaders/water_150.frag.glsl
Normal 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;
|
||||
}
|
|
@ -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")),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
10
src/frame.rs
10
src/frame.rs
|
@ -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)
|
||||
}
|
||||
|
|
35
src/lib.rs
35
src/lib.rs
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue