From 1b424f0b8029b9bab4960e7d1d6ebd1abc9348b7 Mon Sep 17 00:00:00 2001 From: Alula <6276139+alula@users.noreply.github.com> Date: Sun, 31 Oct 2021 09:14:13 +0100 Subject: [PATCH] improved npc lighting --- src/npc/mod.rs | 30 +++++++ src/scene/game_scene.rs | 110 +++++++++++++++++------ src/texture_set.rs | 195 +++++++++++++++++++++++++++++----------- 3 files changed, 258 insertions(+), 77 deletions(-) diff --git a/src/npc/mod.rs b/src/npc/mod.rs index 5ba2609..8a52266 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -182,6 +182,36 @@ impl NPC { Ok(()) } + + pub fn draw_lightmap(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult { + if !self.cond.alive() || self.cond.hidden() { + return Ok(()); + } + + let texture = state.npc_table.get_texture_name(self.spritesheet_id); + + if let Some(batch) = state.texture_set.get_or_load_batch(ctx, &state.constants, texture)?.glow() { + let off_x = + if self.direction == Direction::Left { self.display_bounds.left } else { self.display_bounds.right } as i32; + let shock = if self.shock > 0 { (2 * ((self.shock as i32 / 2) % 2) - 1) as f32 } else { 0.0 }; + + let (frame_x, frame_y) = frame.xy_interpolated(state.frame_time); + + batch.add_rect( + interpolate_fix9_scale(self.prev_x - off_x, self.x - off_x, state.frame_time) + shock - frame_x, + interpolate_fix9_scale( + self.prev_y - self.display_bounds.top as i32, + self.y - self.display_bounds.top as i32, + state.frame_time, + ) - frame_y, + &self.anim_rect, + ); + + batch.draw(ctx)?; + } + + Ok(()) + } } impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mut Flash)> for NPC { diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index 648da00..b505acd 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -795,17 +795,39 @@ impl GameScene { } fn draw_light_map(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { - let canvas = state.lightmap_canvas.as_mut(); - if let None = canvas { - return Ok(()); + { + let canvas = state.lightmap_canvas.as_mut(); + + if let None = canvas { + return Ok(()); + } + + let canvas = canvas.unwrap(); + graphics::set_render_target(ctx, Some(canvas))?; } - let canvas = canvas.unwrap(); - - graphics::set_render_target(ctx, Some(canvas))?; graphics::set_blend_mode(ctx, BlendMode::Add)?; graphics::clear(ctx, Color::from_rgb(100, 100, 110)); + + for npc in self.npc_list.iter_alive() { + if npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as i32 * 0x200) + || npc.x + > (self.frame.x + + 128 * 0x200 + + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200) + && npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200) + || npc.y + > (self.frame.y + + 128 * 0x200 + + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200) + { + continue; + } + + npc.draw_lightmap(state, ctx, &self.frame)?; + } + { let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "builtin/lightmap/spot")?; @@ -1024,7 +1046,7 @@ impl GameScene { (0, 0, 255), batch, ), - 32 | 87 | 211 => { + 32 | 87 => { self.draw_light( interpolate_fix9_scale( npc.prev_x - self.frame.prev_x, @@ -1041,6 +1063,23 @@ impl GameScene { batch, ); } + 211 => { + self.draw_light( + interpolate_fix9_scale( + npc.prev_x - self.frame.prev_x, + npc.x - self.frame.x, + state.frame_time, + ), + interpolate_fix9_scale( + npc.prev_y - self.frame.prev_y, + npc.y - self.frame.y, + state.frame_time, + ), + 1.5, + (128, 0, 0), + batch, + ); + } 38 => { let flicker = ((npc.anim_num.wrapping_add(npc.id) ^ 5) & 3) as u8 * 24; self.draw_light( @@ -1139,6 +1178,13 @@ impl GameScene { ); } } + 101 | 102 => self.draw_light( + interpolate_fix9_scale(npc.prev_x - self.frame.prev_x, npc.x - self.frame.x, state.frame_time), + interpolate_fix9_scale(npc.prev_y - self.frame.prev_y, npc.y - self.frame.y, state.frame_time), + 1.0, + (100, 100, 200), + batch, + ), 175 if npc.action_num < 10 => { self.draw_light( interpolate_fix9_scale( @@ -1156,6 +1202,13 @@ impl GameScene { batch, ); } + 189 => self.draw_light( + interpolate_fix9_scale(npc.prev_x - self.frame.prev_x, npc.x - self.frame.x, state.frame_time), + interpolate_fix9_scale(npc.prev_y - self.frame.prev_y, npc.y - self.frame.y, state.frame_time), + 1.0, + (10, 50, 255), + batch, + ), _ => {} } } @@ -1166,27 +1219,32 @@ impl GameScene { graphics::set_blend_mode(ctx, BlendMode::Multiply)?; graphics::set_render_target(ctx, None)?; - let rect = Rect { left: 0.0, top: 0.0, right: state.screen_size.0, bottom: state.screen_size.1 }; - canvas.clear(); - canvas.add(SpriteBatchCommand::DrawRect(rect, rect)); - canvas.draw()?; + { + let canvas = state.lightmap_canvas.as_mut().unwrap(); + let rect = Rect { left: 0.0, top: 0.0, right: state.screen_size.0, bottom: state.screen_size.1 }; - graphics::set_render_target(ctx, Some(canvas))?; - graphics::draw_rect( - ctx, - Rect { - left: 0, - top: 0, - right: (state.screen_size.0 + 1.0) as isize, - bottom: (state.screen_size.1 + 1.0) as isize, - }, - Color { r: 0.15, g: 0.12, b: 0.12, a: 1.0 }, - )?; - graphics::set_render_target(ctx, None)?; - graphics::set_blend_mode(ctx, BlendMode::Add)?; - canvas.draw()?; + canvas.clear(); + canvas.add(SpriteBatchCommand::DrawRect(rect, rect)); + canvas.draw()?; - graphics::set_blend_mode(ctx, BlendMode::Alpha)?; + + graphics::set_render_target(ctx, Some(canvas))?; + graphics::draw_rect( + ctx, + Rect { + left: 0, + top: 0, + right: (state.screen_size.0 + 1.0) as isize, + bottom: (state.screen_size.1 + 1.0) as isize, + }, + Color { r: 0.15, g: 0.12, b: 0.12, a: 1.0 }, + )?; + graphics::set_render_target(ctx, None)?; + graphics::set_blend_mode(ctx, BlendMode::Add)?; + canvas.draw()?; + + graphics::set_blend_mode(ctx, BlendMode::Alpha)?; + } Ok(()) } diff --git a/src/texture_set.rs b/src/texture_set.rs index 1815364..9420f95 100644 --- a/src/texture_set.rs +++ b/src/texture_set.rs @@ -34,6 +34,14 @@ pub trait SpriteBatch { fn has_normal_layer(&self) -> bool; + fn glow(&mut self) -> Option<&mut dyn SpriteBatch> { + None + } + + fn normal(&mut self) -> Option<&mut dyn SpriteBatch> { + None + } + fn to_rect(&self) -> common::Rect; fn clear(&mut self); @@ -130,37 +138,40 @@ impl SpriteBatch for DummyBatch { } } -pub struct SizedBatch { +pub struct SubBatch { batch: Box, - width: usize, - height: usize, - real_width: usize, - real_height: usize, + width: u16, + height: u16, + real_width: u16, + real_height: u16, scale_x: f32, scale_y: f32, - has_glow_layer: bool, - has_normal_layer: bool, } -impl SpriteBatch for SizedBatch { +pub struct CombinedBatch { + main_batch: SubBatch, + glow_batch: Option, +} + +impl SpriteBatch for SubBatch { #[inline(always)] fn width(&self) -> usize { - self.width + self.width as _ } #[inline(always)] fn height(&self) -> usize { - self.height + self.height as _ } #[inline(always)] fn dimensions(&self) -> (usize, usize) { - (self.width, self.height) + (self.width as _, self.height as _) } #[inline(always)] fn real_dimensions(&self) -> (usize, usize) { - (self.real_width, self.real_height) + (self.real_width as _, self.real_height as _) } #[inline(always)] @@ -170,17 +181,17 @@ impl SpriteBatch for SizedBatch { #[inline(always)] fn has_glow_layer(&self) -> bool { - self.has_glow_layer + false } #[inline(always)] fn has_normal_layer(&self) -> bool { - self.has_normal_layer + false } #[inline(always)] fn to_rect(&self) -> common::Rect { - common::Rect::::new(0, 0, self.width, self.height) + common::Rect::::new(0, 0, self.width as _, self.height as _) } #[inline(always)] @@ -300,6 +311,92 @@ impl SpriteBatch for SizedBatch { } } +impl SpriteBatch for CombinedBatch { + fn width(&self) -> usize { + self.main_batch.width as _ + } + + fn height(&self) -> usize { + self.main_batch.height as _ + } + + fn dimensions(&self) -> (usize, usize) { + self.main_batch.dimensions() + } + + fn real_dimensions(&self) -> (usize, usize) { + self.main_batch.real_dimensions() + } + + fn scale(&self) -> (f32, f32) { + self.main_batch.scale() + } + + fn has_glow_layer(&self) -> bool { + self.glow_batch.is_some() + } + + fn has_normal_layer(&self) -> bool { + false + } + + fn glow(&mut self) -> Option<&mut dyn SpriteBatch> { + if let Some(batch) = self.glow_batch.as_mut() { + Some(batch) + } else { + None + } + } + + fn to_rect(&self) -> Rect { + self.main_batch.to_rect() + } + + fn clear(&mut self) { + self.main_batch.clear() + } + + fn add(&mut self, x: f32, y: f32) { + self.main_batch.add(x, y) + } + + fn add_rect(&mut self, x: f32, y: f32, rect: &Rect) { + self.main_batch.add_rect(x, y, rect) + } + + fn add_rect_flip(&mut self, x: f32, y: f32, flip_x: bool, flip_y: bool, rect: &Rect) { + self.main_batch.add_rect_flip(x, y, flip_x, flip_y, rect) + } + + fn add_rect_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8, u8), rect: &Rect) { + self.main_batch.add_rect_tinted(x, y, color, rect) + } + + fn add_rect_scaled(&mut self, x: f32, y: f32, scale_x: f32, scale_y: f32, rect: &Rect) { + self.main_batch.add_rect_scaled(x, y, scale_x, scale_y, rect) + } + + fn add_rect_scaled_tinted( + &mut self, + x: f32, + y: f32, + color: (u8, u8, u8, u8), + scale_x: f32, + scale_y: f32, + rect: &Rect, + ) { + self.main_batch.add_rect_scaled_tinted(x, y, color, scale_x, scale_y, rect) + } + + fn draw(&mut self, ctx: &mut Context) -> GameResult { + self.main_batch.draw(ctx) + } + + fn draw_filtered(&mut self, filter: FilterMode, ctx: &mut Context) -> GameResult { + self.main_batch.draw_filtered(filter, ctx) + } +} + pub struct TextureSet { pub tex_map: HashMap>, pub paths: Vec, @@ -354,17 +451,10 @@ impl TextureSet { create_texture(ctx, width as u16, height as u16, &img) } - pub fn find_texture( - &self, - ctx: &mut Context, - name: &str, - ) -> Option { - self - .paths - .iter() - .find_map(|s| { - FILE_TYPES.iter().map(|ext| [s, name, ext].join("")).find(|path| filesystem::exists(ctx, path)) - }) + pub fn find_texture(&self, ctx: &mut Context, name: &str) -> Option { + self.paths.iter().find_map(|s| { + FILE_TYPES.iter().map(|ext| [s, name, ext].join("")).find(|path| filesystem::exists(ctx, path)) + }) } pub fn load_texture( @@ -373,38 +463,41 @@ impl TextureSet { constants: &EngineConstants, name: &str, ) -> GameResult> { - let path = self.find_texture(ctx, name) + let path = self + .find_texture(ctx, name) .ok_or_else(|| GameError::ResourceLoadError(format!("Texture {} does not exist.", name)))?; - let has_glow_layer = self - .paths - .iter() - .find_map(|s| { - FILE_TYPES.iter().map(|ext| [s, name, ".glow", ext].join("")).find(|path| filesystem::exists(ctx, path)) - }) - .is_some(); + let glow_path = self.find_texture(ctx, [name, ".glow"].join("").as_str()); info!("Loading texture: {} -> {}", name, path); - let batch = self.load_image(ctx, &path)?; - let size = batch.dimensions(); + fn make_batch(name: &str, constants: &EngineConstants, batch: Box) -> SubBatch { + let size = batch.dimensions(); - let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &size); - let scale = orig_dimensions.0 as f32 / size.0 as f32; - let width = (size.0 as f32 * scale) as usize; - let height = (size.1 as f32 * scale) as usize; + let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &size); + let scale = orig_dimensions.0 as f32 / size.0 as f32; + let width = (size.0 as f32 * scale) as _; + let height = (size.1 as f32 * scale) as _; - Ok(Box::new(SizedBatch { - batch, - width, - height, - scale_x: scale, - scale_y: scale, - real_width: size.0 as usize, - real_height: size.1 as usize, - has_glow_layer, - has_normal_layer: false, - })) + SubBatch { + batch, + width, + height, + scale_x: scale, + scale_y: scale, + real_width: size.0 as _, + real_height: size.1 as _, + } + } + + let main_batch = make_batch(name, constants, self.load_image(ctx, &path)?); + let glow_batch = if let Some(glow_path) = glow_path { + self.load_image(ctx, &glow_path).ok().map(|b| make_batch(name, constants, b)) + } else { + None + }; + + Ok(Box::new(CombinedBatch { main_batch, glow_batch })) } pub fn get_or_load_batch(