improved npc lighting
This commit is contained in:
parent
23f0feaae0
commit
1b424f0b80
|
@ -182,6 +182,36 @@ impl NPC {
|
||||||
|
|
||||||
Ok(())
|
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 {
|
impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mut Flash)> for NPC {
|
||||||
|
|
|
@ -795,17 +795,39 @@ impl GameScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_light_map(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
fn draw_light_map(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
let canvas = state.lightmap_canvas.as_mut();
|
{
|
||||||
if let None = canvas {
|
let canvas = state.lightmap_canvas.as_mut();
|
||||||
return Ok(());
|
|
||||||
|
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::set_blend_mode(ctx, BlendMode::Add)?;
|
||||||
|
|
||||||
graphics::clear(ctx, Color::from_rgb(100, 100, 110));
|
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")?;
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "builtin/lightmap/spot")?;
|
||||||
|
|
||||||
|
@ -1024,7 +1046,7 @@ impl GameScene {
|
||||||
(0, 0, 255),
|
(0, 0, 255),
|
||||||
batch,
|
batch,
|
||||||
),
|
),
|
||||||
32 | 87 | 211 => {
|
32 | 87 => {
|
||||||
self.draw_light(
|
self.draw_light(
|
||||||
interpolate_fix9_scale(
|
interpolate_fix9_scale(
|
||||||
npc.prev_x - self.frame.prev_x,
|
npc.prev_x - self.frame.prev_x,
|
||||||
|
@ -1041,6 +1063,23 @@ impl GameScene {
|
||||||
batch,
|
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 => {
|
38 => {
|
||||||
let flicker = ((npc.anim_num.wrapping_add(npc.id) ^ 5) & 3) as u8 * 24;
|
let flicker = ((npc.anim_num.wrapping_add(npc.id) ^ 5) & 3) as u8 * 24;
|
||||||
self.draw_light(
|
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 => {
|
175 if npc.action_num < 10 => {
|
||||||
self.draw_light(
|
self.draw_light(
|
||||||
interpolate_fix9_scale(
|
interpolate_fix9_scale(
|
||||||
|
@ -1156,6 +1202,13 @@ impl GameScene {
|
||||||
batch,
|
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_blend_mode(ctx, BlendMode::Multiply)?;
|
||||||
graphics::set_render_target(ctx, None)?;
|
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();
|
let canvas = state.lightmap_canvas.as_mut().unwrap();
|
||||||
canvas.add(SpriteBatchCommand::DrawRect(rect, rect));
|
let rect = Rect { left: 0.0, top: 0.0, right: state.screen_size.0, bottom: state.screen_size.1 };
|
||||||
canvas.draw()?;
|
|
||||||
|
|
||||||
graphics::set_render_target(ctx, Some(canvas))?;
|
canvas.clear();
|
||||||
graphics::draw_rect(
|
canvas.add(SpriteBatchCommand::DrawRect(rect, rect));
|
||||||
ctx,
|
canvas.draw()?;
|
||||||
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)?;
|
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,14 @@ pub trait SpriteBatch {
|
||||||
|
|
||||||
fn has_normal_layer(&self) -> bool;
|
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<usize>;
|
fn to_rect(&self) -> common::Rect<usize>;
|
||||||
|
|
||||||
fn clear(&mut self);
|
fn clear(&mut self);
|
||||||
|
@ -130,37 +138,40 @@ impl SpriteBatch for DummyBatch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SizedBatch {
|
pub struct SubBatch {
|
||||||
batch: Box<dyn BackendTexture>,
|
batch: Box<dyn BackendTexture>,
|
||||||
width: usize,
|
width: u16,
|
||||||
height: usize,
|
height: u16,
|
||||||
real_width: usize,
|
real_width: u16,
|
||||||
real_height: usize,
|
real_height: u16,
|
||||||
scale_x: f32,
|
scale_x: f32,
|
||||||
scale_y: 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<SubBatch>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpriteBatch for SubBatch {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn width(&self) -> usize {
|
fn width(&self) -> usize {
|
||||||
self.width
|
self.width as _
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn height(&self) -> usize {
|
fn height(&self) -> usize {
|
||||||
self.height
|
self.height as _
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn dimensions(&self) -> (usize, usize) {
|
fn dimensions(&self) -> (usize, usize) {
|
||||||
(self.width, self.height)
|
(self.width as _, self.height as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn real_dimensions(&self) -> (usize, usize) {
|
fn real_dimensions(&self) -> (usize, usize) {
|
||||||
(self.real_width, self.real_height)
|
(self.real_width as _, self.real_height as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -170,17 +181,17 @@ impl SpriteBatch for SizedBatch {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn has_glow_layer(&self) -> bool {
|
fn has_glow_layer(&self) -> bool {
|
||||||
self.has_glow_layer
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn has_normal_layer(&self) -> bool {
|
fn has_normal_layer(&self) -> bool {
|
||||||
self.has_normal_layer
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn to_rect(&self) -> common::Rect<usize> {
|
fn to_rect(&self) -> common::Rect<usize> {
|
||||||
common::Rect::<usize>::new(0, 0, self.width, self.height)
|
common::Rect::<usize>::new(0, 0, self.width as _, self.height as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[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<usize> {
|
||||||
|
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<u16>) {
|
||||||
|
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<u16>) {
|
||||||
|
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<u16>) {
|
||||||
|
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<u16>) {
|
||||||
|
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<u16>,
|
||||||
|
) {
|
||||||
|
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 struct TextureSet {
|
||||||
pub tex_map: HashMap<String, Box<dyn SpriteBatch>>,
|
pub tex_map: HashMap<String, Box<dyn SpriteBatch>>,
|
||||||
pub paths: Vec<String>,
|
pub paths: Vec<String>,
|
||||||
|
@ -354,17 +451,10 @@ impl TextureSet {
|
||||||
create_texture(ctx, width as u16, height as u16, &img)
|
create_texture(ctx, width as u16, height as u16, &img)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_texture(
|
pub fn find_texture(&self, ctx: &mut Context, name: &str) -> Option<String> {
|
||||||
&self,
|
self.paths.iter().find_map(|s| {
|
||||||
ctx: &mut Context,
|
FILE_TYPES.iter().map(|ext| [s, name, ext].join("")).find(|path| filesystem::exists(ctx, path))
|
||||||
name: &str,
|
})
|
||||||
) -> Option<String> {
|
|
||||||
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(
|
pub fn load_texture(
|
||||||
|
@ -373,38 +463,41 @@ impl TextureSet {
|
||||||
constants: &EngineConstants,
|
constants: &EngineConstants,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> GameResult<Box<dyn SpriteBatch>> {
|
) -> GameResult<Box<dyn SpriteBatch>> {
|
||||||
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)))?;
|
.ok_or_else(|| GameError::ResourceLoadError(format!("Texture {} does not exist.", name)))?;
|
||||||
|
|
||||||
let has_glow_layer = self
|
let glow_path = self.find_texture(ctx, [name, ".glow"].join("").as_str());
|
||||||
.paths
|
|
||||||
.iter()
|
|
||||||
.find_map(|s| {
|
|
||||||
FILE_TYPES.iter().map(|ext| [s, name, ".glow", ext].join("")).find(|path| filesystem::exists(ctx, path))
|
|
||||||
})
|
|
||||||
.is_some();
|
|
||||||
|
|
||||||
info!("Loading texture: {} -> {}", name, path);
|
info!("Loading texture: {} -> {}", name, path);
|
||||||
|
|
||||||
let batch = self.load_image(ctx, &path)?;
|
fn make_batch(name: &str, constants: &EngineConstants, batch: Box<dyn BackendTexture>) -> SubBatch {
|
||||||
let size = batch.dimensions();
|
let size = batch.dimensions();
|
||||||
|
|
||||||
let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &size);
|
let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &size);
|
||||||
let scale = orig_dimensions.0 as f32 / size.0 as f32;
|
let scale = orig_dimensions.0 as f32 / size.0 as f32;
|
||||||
let width = (size.0 as f32 * scale) as usize;
|
let width = (size.0 as f32 * scale) as _;
|
||||||
let height = (size.1 as f32 * scale) as usize;
|
let height = (size.1 as f32 * scale) as _;
|
||||||
|
|
||||||
Ok(Box::new(SizedBatch {
|
SubBatch {
|
||||||
batch,
|
batch,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
scale_x: scale,
|
scale_x: scale,
|
||||||
scale_y: scale,
|
scale_y: scale,
|
||||||
real_width: size.0 as usize,
|
real_width: size.0 as _,
|
||||||
real_height: size.1 as usize,
|
real_height: size.1 as _,
|
||||||
has_glow_layer,
|
}
|
||||||
has_normal_layer: false,
|
}
|
||||||
}))
|
|
||||||
|
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(
|
pub fn get_or_load_batch(
|
||||||
|
|
Loading…
Reference in New Issue