From f9014a0bf9967c6987a64c56e6b0709a5504cae7 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Thu, 22 Jul 2021 17:55:16 +0100 Subject: [PATCH 01/15] started work on new background implementation --- agb/src/display/tiled0.rs | 348 ++++++++++---------------------------- agb/src/number.rs | 70 ++++++++ 2 files changed, 157 insertions(+), 261 deletions(-) diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 0ee5e8d9..b79ffd49 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -1,7 +1,7 @@ use core::{convert::TryInto, ops::Deref}; use crate::{ - memory_mapped::MemoryMapped1DArray, + memory_mapped::{MemoryMapped, MemoryMapped1DArray}, number::{Rect, Vector2D}, }; @@ -34,273 +34,131 @@ pub enum BackgroundSize { S64x64 = 3, } -#[non_exhaustive] /// The map background is the method of drawing game maps to the screen. It /// automatically handles copying the correct portion of a provided map to the /// assigned block depending on given coordinates. pub struct Background { background: u8, block: u8, - pos_x: i32, - pos_y: i32, + commited_position: Vector2D, + shadowed_position: Vector2D, + poisoned: bool, + shadowed_register: u16, } impl Background { - unsafe fn new(layer: u8, block: u8) -> Background { - let mut background = Background { - background: layer, + unsafe fn new(background: u8, block: u8) -> Background { + let mut b = Background { + background, block, - pos_x: 0, - pos_y: 0, + commited_position: (0, 0).into(), + shadowed_position: (0, 0).into(), + shadowed_register: 0, + poisoned: true, }; - background.set_colour_mode(ColourMode::FourBitPerPixel); - background.set_background_size(BackgroundSize::S32x32); - background.set_block(block); - background - } -} - -impl Background { - /// Sets the background to be shown on screen. Requires the background to - /// have a map enabled otherwise a panic is caused. - pub fn show(&mut self) { - let mode = DISPLAY_CONTROL.get(); - let new_mode = mode | (1 << (self.background + 0x08)); - DISPLAY_CONTROL.set(new_mode); + b.set_block(block); + b.set_colour_mode(ColourMode::FourBitPerPixel); + b.set_background_size(BackgroundSize::S32x32); + b } - /// Hides the background, nothing from this background is rendered to screen. - pub fn hide(&mut self) { - let mode = DISPLAY_CONTROL.get(); - let new_mode = mode & !(1 << (self.background + 0x08)); - DISPLAY_CONTROL.set(new_mode); + unsafe fn set_shadowed_register_bits(&mut self, value: u16, length: u16, shift: u16) { + let mask = !(((1 << length) - 1) << shift); + let new = (self.shadowed_register & mask) | (value << shift); + self.shadowed_register = new; } - unsafe fn get_register(&mut self) -> *mut u16 { - (0x0400_0008 + 2 * self.background as usize) as *mut u16 + unsafe fn get_register(&self) -> MemoryMapped { + MemoryMapped::new(0x0400_0008 + 2 * self.background as usize) } unsafe fn set_block(&mut self, block: u8) { - self.set_bits(0x08, 5, block as u16) + self.set_shadowed_register_bits(block as u16, 5, 0x8); } - unsafe fn set_bits(&mut self, start: u16, num_bits: u16, bits: u16) { - let reg = self.get_register(); - let control = reg.read_volatile(); - let mask = !(((1 << num_bits) - 1) << start); - let new_control = (control & mask) | ((bits as u16) << start); - reg.write_volatile(new_control); + unsafe fn set_colour_mode(&mut self, mode: ColourMode) { + self.set_shadowed_register_bits(mode as u16, 0x1, 0x7); } - /// Sets priority of the background layer. Backgrounds with higher priority - /// are drawn (above/below) backgrounds with lower priority. pub fn set_priority(&mut self, p: Priority) { - unsafe { self.set_bits(0, 2, p as u16) } + unsafe { self.set_shadowed_register_bits(p as u16, 0x2, 0x0) }; } - fn set_colour_mode(&mut self, mode: ColourMode) { - unsafe { self.set_bits(0x07, 1, mode as u16) } + unsafe fn set_background_size(&mut self, size: BackgroundSize) { + self.set_shadowed_register_bits(size as u16, 0x2, 0xE); } - fn set_background_size(&mut self, size: BackgroundSize) { - unsafe { self.set_bits(0x0E, 2, size as u16) } + pub fn set_position(&mut self, position: Vector2D) { + self.shadowed_position = position; } - fn map_get(&self, map: &T, dim_x: u32, dim_y: u32, x: i32, y: i32, default: u16) -> u16 - where - T: Deref, - { - if x >= dim_x as i32 || x < 0 || y >= dim_y as i32 || y < 0 { - default + pub fn map_has_changed(&mut self) { + self.poisoned = true; + } + + pub fn commit_area(&self, map: &[u16], map_dimensions: Vector2D, area: Rect) { + // commit shadowed register + unsafe { self.get_register().set(self.shadowed_register) }; + + let positions_to_be_updated = if self.poisoned { + area.iter() + .chain(Rect::new((0, 0).into(), (0, 0).into()).iter()) } else { - map[(dim_x as i32 * y + x) as usize] - } + // calculate difference in positions + let position_difference = self.shadowed_position - self.commited_position; + let tile_position_difference = position_difference / 8; + + // how much of the map needs updating + let difference_x: Rect = if tile_position_difference.x == 0 { + Rect::new((0, 0).into(), (0, 0).into()) + } else if tile_position_difference.x > 0 { + Rect::new((0, 0).into(), (tile_position_difference.x, 0).into()) + } else if tile_position_difference.x < 0 { + Rect::new( + (32 + tile_position_difference.x, 0).into(), + (tile_position_difference.x.abs(), 0).into(), + ) + } else { + unreachable!(); + }; + + let difference_y: Rect = if tile_position_difference.y == 0 { + Rect::new((0, 0).into(), (0, 0).into()) + } else if tile_position_difference.y > 0 { + Rect::new((0, 0).into(), (0, tile_position_difference.y).into()) + } else if tile_position_difference.y < 0 { + Rect::new( + (0, 32 + tile_position_difference.y).into(), + (0, tile_position_difference.y.abs()).into(), + ) + } else { + unreachable!(); + }; + + // update those positions + + let y_update = area.overlapping_rect(difference_y); + let x_update = area.overlapping_rect(difference_x); + + y_update.iter().chain(x_update.iter()) + }; + + for (x, y) in positions_to_be_updated {} + + // update commited position + + // update position in registers } - fn set_x(&self, x: u16) { - unsafe { ((0x0400_0010 + 4 * self.background as usize) as *mut u16).write_volatile(x) } - } - fn set_y(&self, y: u16) { - unsafe { ((0x0400_0012 + 4 * self.background as usize) as *mut u16).write_volatile(y) } - } - - /// Forces the portion of the map in current view to be copied to the map - /// block assigned to this background. This is currently unnecesary to call. - /// Setting position already updates the drawn map, and changing map forces - /// an update. - pub fn draw_full_map(&mut self, map: &[u16], dimensions: Vector2D, default: u16) { + pub fn commit(&self, map: &[u16], map_dimensions: Vector2D) { let area: Rect = Rect { position: Vector2D::new(-1, -1), size: Vector2D::new(32, 22), }; - self.draw_area(map, dimensions, area, default); - } - - /// Forces a specific area of the screen to be drawn, taking into account any positonal offsets. - pub fn draw_area(&self, map: &[u16], dimensions: Vector2D, area: Rect, default: u16) { - self.draw_area_mapped( - &map, - dimensions.x, - dimensions.y, - area.position.x, - area.position.y, - area.size.x, - area.size.y, - default, - ); - } - - #[allow(clippy::too_many_arguments)] - fn draw_area_mapped( - &self, - map: &T, - dim_x: u32, - dim_y: u32, - left: i32, - top: i32, - width: i32, - height: i32, - default: u16, - ) where - T: Deref, - { - let x_map_space = self.pos_x / 8; - let y_map_space = self.pos_y / 8; - - let x_block_space = x_map_space % 32; - let y_block_space = y_map_space % 32; - - for x in left..(left + width) { - for y in top..(top + height) { - unsafe { - (&mut (*MAP)[self.block as usize][(y_block_space + y).rem_euclid(32) as usize] - [(x_block_space + x).rem_euclid(32) as usize] - as *mut u16) - .write_volatile(self.map_get( - map, - dim_x, - dim_y, - x_map_space + x, - y_map_space + y, - default, - )) - }; - } - } - } - - pub fn set_offset(&mut self, position: Vector2D) { - self.set_x(position.x as u16); - self.set_y(position.y as u16); - } - - /// Sets the position of the map to be shown on screen. This automatically - /// manages copying the correct portion to the map block and moving the map - /// registers. - pub fn set_position( - &mut self, - map: &[u16], - dimensions: Vector2D, - position: Vector2D, - default: u16, - ) { - self.set_position_mapped( - &map, - dimensions.x, - dimensions.y, - position.x, - position.y, - default, - ); - } - - fn set_position_mapped( - &mut self, - map: &T, - dim_x: u32, - dim_y: u32, - x: i32, - y: i32, - default: u16, - ) where - T: Deref, - { - let x_map_space = x / 8; - let y_map_space = y / 8; - - let prev_x_map_space = self.pos_x / 8; - let prev_y_map_space = self.pos_y / 8; - - let x_difference = x_map_space - prev_x_map_space; - let y_difference = y_map_space - prev_y_map_space; - - let x_block_space = x_map_space % 32; - let y_block_space = y_map_space % 32; - - self.pos_x = x; - self.pos_y = y; - - // don't fancily handle if we've moved more than one tile, just copy the whole new map - if x_difference.abs() > 1 || y_difference.abs() > 1 { - self.draw_area_mapped(map, dim_x, dim_y, -1, -1, 32, 22, default); - } else { - if x_difference != 0 { - let x_offset = match x_difference { - -1 => -1, - 1 => 30, - _ => unreachable!(), - }; - for y in -1..21 { - unsafe { - (&mut (*MAP)[self.block as usize] - [(y_block_space + y).rem_euclid(32) as usize] - [(x_block_space + x_offset).rem_euclid(32) as usize] - as *mut u16) - .write_volatile(self.map_get( - map, - dim_x, - dim_y, - x_map_space + x_offset, - y_map_space + y, - default, - )) - }; - } - } - if y_difference != 0 { - let y_offset = match y_difference { - -1 => -1, - 1 => 20, - _ => unreachable!(), - }; - for x in -1..31 { - unsafe { - (&mut (*MAP)[self.block as usize] - [(y_block_space + y_offset).rem_euclid(32) as usize] - [(x_block_space + x).rem_euclid(32) as usize] - as *mut u16) - .write_volatile(self.map_get( - map, - dim_x, - dim_y, - x_map_space + x, - y_map_space + y_offset, - default, - )) - }; - } - } - } - - let x_remainder = x % (32 * 8); - let y_remainder = y % (32 * 8); - - self.set_x(x_remainder as u16); - self.set_y(y_remainder as u16); + self.commit_area(map, map_dimensions, area) } } -#[non_exhaustive] pub struct Tiled0 { used_blocks: u32, num_backgrounds: u8, @@ -316,35 +174,10 @@ impl Tiled0 { } } - fn set_sprite_palette_entry(&mut self, index: u8, colour: u16) { - PALETTE_SPRITE.set(index as usize, colour) - } - fn set_sprite_tilemap_entry(&mut self, index: u32, data: u32) { - TILE_SPRITE.set(index as usize, data); - } - fn set_background_tilemap_entry(&mut self, index: u32, data: u32) { TILE_BACKGROUND.set(index as usize, data); } - /// Copies raw palettes to the background palette without any checks. - pub fn set_sprite_palette_raw(&mut self, colour: &[u16]) { - for (index, &entry) in colour.iter().enumerate() { - self.set_sprite_palette_entry(index.try_into().unwrap(), entry) - } - } - fn set_sprite_palette(&mut self, pal_index: u8, palette: &palette16::Palette16) { - for (colour_index, &colour) in palette.colours.iter().enumerate() { - PALETTE_SPRITE.set(pal_index as usize * 16 + colour_index, colour); - } - } - - pub fn set_sprite_palettes(&mut self, palettes: &[palette16::Palette16]) { - for (palette_index, entry) in palettes.iter().enumerate() { - self.set_sprite_palette(palette_index as u8, entry) - } - } - /// Copies raw palettes to the background palette without any checks. pub fn set_background_palette_raw(&mut self, palette: &[u16]) { for (index, &colour) in palette.iter().enumerate() { @@ -365,13 +198,6 @@ impl Tiled0 { } } - /// Copies tiles to the sprite tilemap without any checks. - pub fn set_sprite_tilemap(&mut self, tiles: &[u32]) { - for (index, &tile) in tiles.iter().enumerate() { - self.set_sprite_tilemap_entry(index as u32, tile) - } - } - /// Gets a map background if possible and assigns an unused block to it. pub fn get_background(&mut self) -> Result { if self.num_backgrounds >= 4 { diff --git a/agb/src/number.rs b/agb/src/number.rs index bf9c57f3..a09e0e48 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -648,6 +648,76 @@ impl Rect { pub fn new(position: Vector2D, size: Vector2D) -> Self { Rect { position, size } } + + pub fn contains_point(&self, point: Vector2D) -> bool { + point.x > self.position.x + && point.x < self.position.x + self.size.x + && point.y > self.position.y + && point.y < self.position.y + self.size.y + } + + pub fn overlapping_rect(&self, other: Rect) -> Rect { + fn max(x: E, y: E) -> E { + if x > y { + x + } else { + y + } + } + fn min(x: E, y: E) -> E { + if x > y { + y + } else { + x + } + } + + let top_left: Vector2D = ( + max(self.position.x, other.position.x), + max(self.position.y, other.position.y), + ) + .into(); + let bottom_right: Vector2D = ( + min( + self.position.x + self.size.x, + other.position.x + other.size.x, + ), + min( + self.position.y + self.size.y, + other.position.y + other.size.y, + ), + ) + .into(); + + Rect::new(top_left, bottom_right - top_left) + } +} + +impl Rect { + pub fn iter(self) -> impl Iterator { + let mut current_x = self.position.x; + let mut current_y = self.position.y; + let mut is_done = false; + core::iter::from_fn(move || { + if is_done { + None + } else { + let (old_x, old_y) = (current_x, current_y); + + current_x = current_x + T::one(); + if current_x > self.position.x + self.size.x { + current_x = self.position.x; + current_y = current_y + T::one(); + } + + if current_y > self.position.y + self.size.y { + is_done = true; + } + + Some((old_x, old_y)) + } + }) + } } impl Vector2D { From e176e986ad96b72aa6a625a952664b70ed25590c Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 1 Aug 2021 00:18:09 +0100 Subject: [PATCH 02/15] part way implementation of background --- agb/examples/test_logo.rs | 1 + agb/src/display/example_logo.rs | 8 ++- agb/src/display/tiled0.rs | 88 +++++++++++++++++++++++++++------ 3 files changed, 82 insertions(+), 15 deletions(-) diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index 6eddc02b..a307db4c 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -4,6 +4,7 @@ extern crate agb; use agb::display::example_logo; +use agb::display::tiled0::Map; #[no_mangle] pub fn main() -> ! { diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 7b9c520d..4653c00b 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -2,6 +2,8 @@ use crate::display::tiled0::Tiled0; crate::include_gfx!("gfx/agb_logo.toml"); +use super::tiled0::Map; + pub fn display_logo(gfx: &mut Tiled0) { gfx.set_background_palettes(agb_logo::test_logo.palettes); gfx.set_background_tilemap(0, agb_logo::test_logo.tiles); @@ -14,7 +16,11 @@ pub fn display_logo(gfx: &mut Tiled0) { entries[tile_id as usize] = tile_id | (palette_entry << 12); } - back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0); + back.set_map(Map { + store: &mut entries, + dimensions: (30_u32, 20_u32).into(), + default: 0, + }); back.show(); } diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index b79ffd49..64fb5a99 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -1,5 +1,3 @@ -use core::{convert::TryInto, ops::Deref}; - use crate::{ memory_mapped::{MemoryMapped, MemoryMapped1DArray}, number::{Rect, Vector2D}, @@ -12,13 +10,9 @@ use super::{ const PALETTE_BACKGROUND: MemoryMapped1DArray = unsafe { MemoryMapped1DArray::new(0x0500_0000) }; -const PALETTE_SPRITE: MemoryMapped1DArray = - unsafe { MemoryMapped1DArray::new(0x0500_0200) }; const TILE_BACKGROUND: MemoryMapped1DArray = unsafe { MemoryMapped1DArray::new(0x06000000) }; -const TILE_SPRITE: MemoryMapped1DArray = - unsafe { MemoryMapped1DArray::new(0x06010000) }; const MAP: *mut [[[u16; 32]; 32]; 32] = 0x0600_0000 as *mut _; @@ -37,17 +31,36 @@ pub enum BackgroundSize { /// The map background is the method of drawing game maps to the screen. It /// automatically handles copying the correct portion of a provided map to the /// assigned block depending on given coordinates. -pub struct Background { +pub struct Background<'a> { background: u8, block: u8, commited_position: Vector2D, shadowed_position: Vector2D, poisoned: bool, shadowed_register: u16, + map: Option>, } -impl Background { - unsafe fn new(background: u8, block: u8) -> Background { +pub struct Map<'a> { + pub store: &'a mut [u16], + pub dimensions: Vector2D, + pub default: u16, +} + +impl<'a> Map<'a> { + fn get_position(&self, x: i32, y: i32) -> u16 { + if x < 0 || x as u32 >= self.dimensions.x { + self.default + } else if y < 0 || y as u32 >= self.dimensions.y { + self.default + } else { + self.store[y as usize * self.dimensions.x as usize + x as usize] + } + } +} + +impl<'a> Background<'a> { + unsafe fn new(background: u8, block: u8) -> Background<'a> { let mut b = Background { background, block, @@ -55,6 +68,7 @@ impl Background { shadowed_position: (0, 0).into(), shadowed_register: 0, poisoned: true, + map: None, }; b.set_block(block); b.set_colour_mode(ColourMode::FourBitPerPixel); @@ -62,6 +76,22 @@ impl Background { b } + /// Sets the background to be shown on screen. Requires the background to + /// have a map enabled otherwise a panic is caused. + pub fn show(&mut self) { + assert!(self.map.is_some()); + let mode = DISPLAY_CONTROL.get(); + let new_mode = mode | (1 << (self.background + 0x08)); + DISPLAY_CONTROL.set(new_mode); + } + + /// Hides the background, nothing from this background is rendered to screen. + pub fn hide(&mut self) { + let mode = DISPLAY_CONTROL.get(); + let new_mode = mode & !(1 << (self.background + 0x08)); + DISPLAY_CONTROL.set(new_mode); + } + unsafe fn set_shadowed_register_bits(&mut self, value: u16, length: u16, shift: u16) { let mask = !(((1 << length) - 1) << shift); let new = (self.shadowed_register & mask) | (value << shift); @@ -88,15 +118,27 @@ impl Background { self.set_shadowed_register_bits(size as u16, 0x2, 0xE); } + unsafe fn set_position_x_register(&self, x: u16) { + *((0x0400_0010 + 4 * self.background as usize) as *mut u16) = x + } + unsafe fn set_position_y_register(&self, y: u16) { + *((0x0400_0012 + 4 * self.background as usize) as *mut u16) = y + } + pub fn set_position(&mut self, position: Vector2D) { self.shadowed_position = position; } - pub fn map_has_changed(&mut self) { + pub fn get_map(&mut self) -> &mut Option> { self.poisoned = true; + &mut self.map } - pub fn commit_area(&self, map: &[u16], map_dimensions: Vector2D, area: Rect) { + pub fn set_map(&mut self, map: Map<'a>) { + self.map = Some(map); + } + + pub fn commit_area(&mut self, area: Rect) { // commit shadowed register unsafe { self.get_register().set(self.shadowed_register) }; @@ -143,19 +185,37 @@ impl Background { y_update.iter().chain(x_update.iter()) }; - for (x, y) in positions_to_be_updated {} + if let Some(map) = &self.map { + for (x, y) in positions_to_be_updated { + let tile_space_position = self.shadowed_position / 8; + unsafe { + (&mut (*MAP)[self.block as usize][y.rem_euclid(32) as usize] + [x.rem_euclid(32) as usize] as *mut u16) + .write_volatile( + map.get_position(x + tile_space_position.x, y + tile_space_position.y), + ); + } + } + } // update commited position + self.commited_position = self.shadowed_position; + // update position in registers + + unsafe { + self.set_position_x_register((self.commited_position.x % (32 * 8)) as u16); + self.set_position_y_register((self.commited_position.y % (32 * 8)) as u16); + } } - pub fn commit(&self, map: &[u16], map_dimensions: Vector2D) { + pub fn commit(&mut self) { let area: Rect = Rect { position: Vector2D::new(-1, -1), size: Vector2D::new(32, 22), }; - self.commit_area(map, map_dimensions, area) + self.commit_area(area) } } From 6652bdbd70f003e7261c7902201b5c032bdf5d14 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 1 Aug 2021 17:48:48 +0100 Subject: [PATCH 03/15] make to generics --- agb/examples/chicken.rs | 13 +++++++---- agb/src/display/example_logo.rs | 2 +- agb/src/display/object.rs | 39 ++++++++++++++++++++++++++++++++- agb/src/display/tiled0.rs | 30 +++++++++++++++---------- 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 8650730d..55a7f089 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -48,18 +48,23 @@ pub fn main() -> ! { let vblank = agb::interrupt::VBlank::get(); let mut input = agb::input::ButtonController::new(); - gfx.set_sprite_palette_raw(&CHICKEN_PALETTE); - gfx.set_sprite_tilemap(&CHICKEN_TILES); - gfx.set_background_palette_raw(&MAP_PALETTE); gfx.set_background_tilemap(0, &MAP_TILES); let mut background = gfx.get_background().unwrap(); - background.draw_full_map(&MAP_MAP, (32_u32, 32_u32).into(), 0); + background.set_map(agb::display::tiled0::Map { + store: &MAP_MAP, + dimensions: (32_u32, 32_u32).into(), + default: 0, + }); background.show(); + background.commit(); let mut object = gba.display.object.get(); + object.set_sprite_palette_raw(&CHICKEN_PALETTE); + object.set_sprite_tilemap(&CHICKEN_TILES); + object.enable(); let mut chicken = Character { object: object.get_object_standard(), diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 4653c00b..ec56af38 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -17,7 +17,7 @@ pub fn display_logo(gfx: &mut Tiled0) { } back.set_map(Map { - store: &mut entries, + store: &entries, dimensions: (30_u32, 20_u32).into(), default: 0, }); diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 2e1b670d..1cefa2af 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,12 +1,16 @@ use core::cell::RefCell; -use super::{Priority, DISPLAY_CONTROL}; +use super::{palette16, Priority, DISPLAY_CONTROL}; use crate::bitarray::Bitarray; use crate::memory_mapped::MemoryMapped1DArray; use crate::number::Vector2D; const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray = unsafe { MemoryMapped1DArray::new(0x0700_0000) }; +const PALETTE_SPRITE: MemoryMapped1DArray = + unsafe { MemoryMapped1DArray::new(0x0500_0200) }; +const TILE_SPRITE: MemoryMapped1DArray = + unsafe { MemoryMapped1DArray::new(0x06010000) }; /// Handles distributing objects and matricies along with operations that effect all objects. pub struct ObjectControl { @@ -310,6 +314,39 @@ impl ObjectControl { } } + fn set_sprite_tilemap_entry(&self, index: usize, data: u32) { + TILE_SPRITE.set(index, data); + } + + /// Copies raw palettes to the background palette without any checks. + pub fn set_sprite_palette_raw(&self, colour: &[u16]) { + for (index, &entry) in colour.iter().enumerate() { + self.set_sprite_palette_entry(index, entry) + } + } + fn set_sprite_palette_entry(&self, index: usize, colour: u16) { + PALETTE_SPRITE.set(index, colour) + } + + fn set_sprite_palette(&self, pal_index: u8, palette: &palette16::Palette16) { + for (colour_index, &colour) in palette.colours.iter().enumerate() { + PALETTE_SPRITE.set(pal_index as usize * 16 + colour_index, colour); + } + } + + pub fn set_sprite_palettes(&self, palettes: &[palette16::Palette16]) { + for (palette_index, entry) in palettes.iter().enumerate() { + self.set_sprite_palette(palette_index as u8, entry) + } + } + + /// Copies tiles to the sprite tilemap without any checks. + pub fn set_sprite_tilemap(&self, tiles: &[u32]) { + for (index, &tile) in tiles.iter().enumerate() { + self.set_sprite_tilemap_entry(index, tile) + } + } + /// Enable objects on the GBA. pub fn enable(&mut self) { let disp = DISPLAY_CONTROL.get(); diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 64fb5a99..0811a68c 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -1,3 +1,5 @@ +use core::ops::{Deref, Index}; + use crate::{ memory_mapped::{MemoryMapped, MemoryMapped1DArray}, number::{Rect, Vector2D}, @@ -31,23 +33,26 @@ pub enum BackgroundSize { /// The map background is the method of drawing game maps to the screen. It /// automatically handles copying the correct portion of a provided map to the /// assigned block depending on given coordinates. -pub struct Background<'a> { +pub struct Background + ?Sized, D: Deref> { background: u8, block: u8, commited_position: Vector2D, shadowed_position: Vector2D, poisoned: bool, shadowed_register: u16, - map: Option>, + map: Option>, } -pub struct Map<'a> { - pub store: &'a mut [u16], +pub struct Map> +where + S: Index + ?Sized, +{ + pub store: D, pub dimensions: Vector2D, pub default: u16, } -impl<'a> Map<'a> { +impl<'a, S: Index + ?Sized, D: Deref> Map { fn get_position(&self, x: i32, y: i32) -> u16 { if x < 0 || x as u32 >= self.dimensions.x { self.default @@ -59,8 +64,8 @@ impl<'a> Map<'a> { } } -impl<'a> Background<'a> { - unsafe fn new(background: u8, block: u8) -> Background<'a> { +impl<'a, S: Index + ?Sized, D: Deref> Background { + unsafe fn new(background: u8, block: u8) -> Background { let mut b = Background { background, block, @@ -129,12 +134,13 @@ impl<'a> Background<'a> { self.shadowed_position = position; } - pub fn get_map(&mut self) -> &mut Option> { + pub fn get_map(&mut self) -> Option<&mut Map> { self.poisoned = true; - &mut self.map + self.map.as_mut() } - pub fn set_map(&mut self, map: Map<'a>) { + pub fn set_map(&mut self, map: Map) { + self.poisoned = true; self.map = Some(map); } @@ -259,7 +265,9 @@ impl Tiled0 { } /// Gets a map background if possible and assigns an unused block to it. - pub fn get_background(&mut self) -> Result { + pub fn get_background + ?Sized, D: Deref>( + &mut self, + ) -> Result, &'static str> { if self.num_backgrounds >= 4 { return Err("too many backgrounds created, maximum is 4"); } From d6b388e1d6a8c11a3b835de371f4c5c6a609738b Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 14:56:38 +0100 Subject: [PATCH 04/15] simplify the generics --- agb/examples/chicken.rs | 2 +- agb/examples/test_logo.rs | 1 - agb/src/display/example_logo.rs | 2 +- agb/src/display/tiled0.rs | 29 ++++++++++++++--------------- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 55a7f089..704a0460 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -53,7 +53,7 @@ pub fn main() -> ! { let mut background = gfx.get_background().unwrap(); background.set_map(agb::display::tiled0::Map { - store: &MAP_MAP, + store: MAP_MAP.as_ref(), dimensions: (32_u32, 32_u32).into(), default: 0, }); diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index a307db4c..6eddc02b 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -4,7 +4,6 @@ extern crate agb; use agb::display::example_logo; -use agb::display::tiled0::Map; #[no_mangle] pub fn main() -> ! { diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index ec56af38..85e64bf4 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -17,7 +17,7 @@ pub fn display_logo(gfx: &mut Tiled0) { } back.set_map(Map { - store: &entries, + store: entries.as_ref(), dimensions: (30_u32, 20_u32).into(), default: 0, }); diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 0811a68c..782a7693 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -30,29 +30,30 @@ pub enum BackgroundSize { S64x64 = 3, } +pub trait MapStorage: Deref {} +impl MapStorage for &[u16] {} +impl MapStorage for &mut [u16] {} + /// The map background is the method of drawing game maps to the screen. It /// automatically handles copying the correct portion of a provided map to the /// assigned block depending on given coordinates. -pub struct Background + ?Sized, D: Deref> { +pub struct Background { background: u8, block: u8, commited_position: Vector2D, shadowed_position: Vector2D, poisoned: bool, shadowed_register: u16, - map: Option>, + map: Option>, } -pub struct Map> -where - S: Index + ?Sized, -{ - pub store: D, +pub struct Map { + pub store: S, pub dimensions: Vector2D, pub default: u16, } -impl<'a, S: Index + ?Sized, D: Deref> Map { +impl<'a, S: MapStorage> Map { fn get_position(&self, x: i32, y: i32) -> u16 { if x < 0 || x as u32 >= self.dimensions.x { self.default @@ -64,8 +65,8 @@ impl<'a, S: Index + ?Sized, D: Deref> Map } } -impl<'a, S: Index + ?Sized, D: Deref> Background { - unsafe fn new(background: u8, block: u8) -> Background { +impl<'a, S: MapStorage> Background { + unsafe fn new(background: u8, block: u8) -> Background { let mut b = Background { background, block, @@ -134,12 +135,12 @@ impl<'a, S: Index + ?Sized, D: Deref> Backgroun self.shadowed_position = position; } - pub fn get_map(&mut self) -> Option<&mut Map> { + pub fn get_map(&mut self) -> Option<&mut Map> { self.poisoned = true; self.map.as_mut() } - pub fn set_map(&mut self, map: Map) { + pub fn set_map(&mut self, map: Map) { self.poisoned = true; self.map = Some(map); } @@ -265,9 +266,7 @@ impl Tiled0 { } /// Gets a map background if possible and assigns an unused block to it. - pub fn get_background + ?Sized, D: Deref>( - &mut self, - ) -> Result, &'static str> { + pub fn get_background(&mut self) -> Result, &'static str> { if self.num_backgrounds >= 4 { return Err("too many backgrounds created, maximum is 4"); } From 7798f4c95f4b40b20d22e979de261ed07fb4abb8 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 18:07:31 +0100 Subject: [PATCH 05/15] nicer way of using mutable backing storages --- agb/examples/chicken.rs | 8 +-- agb/src/display/example_logo.rs | 9 +--- agb/src/display/tiled0.rs | 88 ++++++++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 704a0460..86cbfd15 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -3,7 +3,7 @@ extern crate agb; use agb::{ - display::{object::ObjectStandard, HEIGHT, WIDTH}, + display::{object::ObjectStandard, tiled0::Map, HEIGHT, WIDTH}, input::Button, }; use core::convert::TryInto; @@ -52,11 +52,7 @@ pub fn main() -> ! { gfx.set_background_tilemap(0, &MAP_TILES); let mut background = gfx.get_background().unwrap(); - background.set_map(agb::display::tiled0::Map { - store: MAP_MAP.as_ref(), - dimensions: (32_u32, 32_u32).into(), - default: 0, - }); + background.set_map(Map::new(&MAP_MAP, (32_u32, 32_u32).into(), 0)); background.show(); background.commit(); diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 85e64bf4..de8ef3d9 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -2,9 +2,8 @@ use crate::display::tiled0::Tiled0; crate::include_gfx!("gfx/agb_logo.toml"); -use super::tiled0::Map; - pub fn display_logo(gfx: &mut Tiled0) { + use super::tiled0::Map; gfx.set_background_palettes(agb_logo::test_logo.palettes); gfx.set_background_tilemap(0, agb_logo::test_logo.tiles); @@ -16,11 +15,7 @@ pub fn display_logo(gfx: &mut Tiled0) { entries[tile_id as usize] = tile_id | (palette_entry << 12); } - back.set_map(Map { - store: entries.as_ref(), - dimensions: (30_u32, 20_u32).into(), - default: 0, - }); + back.set_map(Map::new(&entries, (30_u32, 20_u32).into(), 0)); back.show(); } diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 782a7693..1d9df4b8 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -30,30 +30,86 @@ pub enum BackgroundSize { S64x64 = 3, } -pub trait MapStorage: Deref {} -impl MapStorage for &[u16] {} -impl MapStorage for &mut [u16] {} +#[derive(PartialEq, Eq, Clone, Copy)] +enum Mutability { + Immutable, + Mutable, +} + +struct MapStorage<'a> { + s: *const [u16], + mutability: Mutability, + _phantom: core::marker::PhantomData<&'a ()>, +} + +impl<'a> Index for MapStorage<'a> { + type Output = u16; + fn index(&self, index: usize) -> &Self::Output { + &self.get()[index] + } +} + +impl<'a> MapStorage<'a> { + fn new(store: &[u16]) -> MapStorage { + MapStorage { + s: store as *const _, + mutability: Mutability::Immutable, + _phantom: core::marker::PhantomData, + } + } + fn new_mutable(store: &mut [u16]) -> MapStorage { + MapStorage { + s: store as *const _, + mutability: Mutability::Mutable, + _phantom: core::marker::PhantomData, + } + } + fn get(&self) -> &[u16] { + unsafe { &*self.s } + } + fn get_mut(&mut self) -> &mut [u16] { + assert!( + self.mutability == Mutability::Mutable, + "backing storage must be mutable in order to get internal storage mutably" + ); + unsafe { &mut *(self.s as *mut _) } + } +} /// The map background is the method of drawing game maps to the screen. It /// automatically handles copying the correct portion of a provided map to the /// assigned block depending on given coordinates. -pub struct Background { +pub struct Background<'a> { background: u8, block: u8, commited_position: Vector2D, shadowed_position: Vector2D, poisoned: bool, shadowed_register: u16, - map: Option>, + map: Option>, } -pub struct Map { - pub store: S, +pub struct Map<'a> { + store: MapStorage<'a>, pub dimensions: Vector2D, pub default: u16, } -impl<'a, S: MapStorage> Map { +impl<'a> Map<'a> { + pub fn new(map: &[u16], dimensions: Vector2D, default: u16) -> Map { + Map { + store: MapStorage::new(map), + dimensions, + default, + } + } + pub fn new_mutable(map: &mut [u16], dimensions: Vector2D, default: u16) -> Map { + Map { + store: MapStorage::new_mutable(map), + dimensions, + default, + } + } fn get_position(&self, x: i32, y: i32) -> u16 { if x < 0 || x as u32 >= self.dimensions.x { self.default @@ -63,10 +119,16 @@ impl<'a, S: MapStorage> Map { self.store[y as usize * self.dimensions.x as usize + x as usize] } } + pub fn get_store(&self) -> &[u16] { + self.store.get() + } + pub fn get_mutable_store(&mut self) -> &mut [u16] { + self.store.get_mut() + } } -impl<'a, S: MapStorage> Background { - unsafe fn new(background: u8, block: u8) -> Background { +impl<'a> Background<'a> { + unsafe fn new(background: u8, block: u8) -> Background<'a> { let mut b = Background { background, block, @@ -135,12 +197,12 @@ impl<'a, S: MapStorage> Background { self.shadowed_position = position; } - pub fn get_map(&mut self) -> Option<&mut Map> { + pub fn get_map(&mut self) -> Option<&mut Map<'a>> { self.poisoned = true; self.map.as_mut() } - pub fn set_map(&mut self, map: Map) { + pub fn set_map(&mut self, map: Map<'a>) { self.poisoned = true; self.map = Some(map); } @@ -266,7 +328,7 @@ impl Tiled0 { } /// Gets a map background if possible and assigns an unused block to it. - pub fn get_background(&mut self) -> Result, &'static str> { + pub fn get_background(&mut self) -> Result { if self.num_backgrounds >= 4 { return Err("too many backgrounds created, maximum is 4"); } From 724147ecfab750fa181e902a79fbbbc0106d064a Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 18:20:08 +0100 Subject: [PATCH 06/15] commit step required now --- agb/src/display/example_logo.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index de8ef3d9..f601d999 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -17,6 +17,7 @@ pub fn display_logo(gfx: &mut Tiled0) { back.set_map(Map::new(&entries, (30_u32, 20_u32).into(), 0)); back.show(); + back.commit(); } #[test_case] From 0ccde7d328c5e34f9421a56595cd1e8c081c4a12 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 18:41:28 +0100 Subject: [PATCH 07/15] satisfy linter --- agb/src/display/tiled0.rs | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 1d9df4b8..7f4ed3b9 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -111,9 +111,7 @@ impl<'a> Map<'a> { } } fn get_position(&self, x: i32, y: i32) -> u16 { - if x < 0 || x as u32 >= self.dimensions.x { - self.default - } else if y < 0 || y as u32 >= self.dimensions.y { + if x < 0 || x as u32 >= self.dimensions.x || y < 0 || y as u32 >= self.dimensions.y { self.default } else { self.store[y as usize * self.dimensions.x as usize + x as usize] @@ -220,30 +218,22 @@ impl<'a> Background<'a> { let tile_position_difference = position_difference / 8; // how much of the map needs updating - let difference_x: Rect = if tile_position_difference.x == 0 { - Rect::new((0, 0).into(), (0, 0).into()) - } else if tile_position_difference.x > 0 { - Rect::new((0, 0).into(), (tile_position_difference.x, 0).into()) - } else if tile_position_difference.x < 0 { - Rect::new( + let difference_x: Rect = match tile_position_difference.x { + 0 => Rect::new((0, 0).into(), (0, 0).into()), + 1..=i32::MAX => Rect::new((0, 0).into(), (tile_position_difference.x, 0).into()), + i32::MIN..=-1 => Rect::new( (32 + tile_position_difference.x, 0).into(), (tile_position_difference.x.abs(), 0).into(), - ) - } else { - unreachable!(); + ), }; - let difference_y: Rect = if tile_position_difference.y == 0 { - Rect::new((0, 0).into(), (0, 0).into()) - } else if tile_position_difference.y > 0 { - Rect::new((0, 0).into(), (0, tile_position_difference.y).into()) - } else if tile_position_difference.y < 0 { - Rect::new( + let difference_y: Rect = match tile_position_difference.y { + 0 => Rect::new((0, 0).into(), (0, 0).into()), + 1..=i32::MAX => Rect::new((0, 0).into(), (0, tile_position_difference.y).into()), + i32::MIN..=-1 => Rect::new( (0, 32 + tile_position_difference.y).into(), (0, tile_position_difference.y.abs()).into(), - ) - } else { - unreachable!(); + ), }; // update those positions From 36c6ab1914d7c557b13d292edf579b645780c9a2 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 23:50:28 +0100 Subject: [PATCH 08/15] correct the commit routine --- agb/src/display/tiled0.rs | 110 +++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 7f4ed3b9..94b8b71f 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -205,62 +205,70 @@ impl<'a> Background<'a> { self.map = Some(map); } - pub fn commit_area(&mut self, area: Rect) { + pub fn commit(&mut self) { // commit shadowed register unsafe { self.get_register().set(self.shadowed_register) }; - let positions_to_be_updated = if self.poisoned { - area.iter() - .chain(Rect::new((0, 0).into(), (0, 0).into()).iter()) - } else { - // calculate difference in positions - let position_difference = self.shadowed_position - self.commited_position; - let tile_position_difference = position_difference / 8; + if self.poisoned { + let positions_to_be_updated = + Rect::new(self.shadowed_position / 8, (30, 20).into()).iter(); - // how much of the map needs updating - let difference_x: Rect = match tile_position_difference.x { - 0 => Rect::new((0, 0).into(), (0, 0).into()), - 1..=i32::MAX => Rect::new((0, 0).into(), (tile_position_difference.x, 0).into()), - i32::MIN..=-1 => Rect::new( - (32 + tile_position_difference.x, 0).into(), - (tile_position_difference.x.abs(), 0).into(), - ), - }; - - let difference_y: Rect = match tile_position_difference.y { - 0 => Rect::new((0, 0).into(), (0, 0).into()), - 1..=i32::MAX => Rect::new((0, 0).into(), (0, tile_position_difference.y).into()), - i32::MIN..=-1 => Rect::new( - (0, 32 + tile_position_difference.y).into(), - (0, tile_position_difference.y.abs()).into(), - ), - }; - - // update those positions - - let y_update = area.overlapping_rect(difference_y); - let x_update = area.overlapping_rect(difference_x); - - y_update.iter().chain(x_update.iter()) - }; - - if let Some(map) = &self.map { - for (x, y) in positions_to_be_updated { - let tile_space_position = self.shadowed_position / 8; - unsafe { - (&mut (*MAP)[self.block as usize][y.rem_euclid(32) as usize] - [x.rem_euclid(32) as usize] as *mut u16) - .write_volatile( - map.get_position(x + tile_space_position.x, y + tile_space_position.y), - ); + if let Some(map) = &self.map { + for (x, y) in positions_to_be_updated { + unsafe { + (&mut (*MAP)[self.block as usize][y.rem_euclid(32) as usize] + [x.rem_euclid(32) as usize] as *mut u16) + .write_volatile(map.get_position(x, y)); + } } } - } + } else { + let commited_block = self.commited_position / 8; + let shadowed_block = self.shadowed_position / 8; + + let top_bottom_rect: Rect = { + let top_bottom_height = commited_block.y - shadowed_block.y; + let new_y = if top_bottom_height < 0 { + commited_block.y + 20 + } else { + shadowed_block.y + }; + Rect::new( + (shadowed_block.x, new_y).into(), + (30, top_bottom_height.abs()).into(), + ) + }; + + let left_right_rect: Rect = { + let left_right_width = commited_block.x - shadowed_block.x; + let new_x = if left_right_width < 0 { + commited_block.x + 30 + } else { + shadowed_block.x + }; + Rect::new( + (new_x, shadowed_block.y).into(), + (left_right_width.abs(), 20).into(), + ) + }; + + if let Some(map) = &self.map { + for (x, y) in top_bottom_rect.iter().chain(left_right_rect.iter()) { + unsafe { + (&mut (*MAP)[self.block as usize][y.rem_euclid(32) as usize] + [x.rem_euclid(32) as usize] as *mut u16) + .write_volatile(map.get_position(x, y)); + } + } + } + }; // update commited position self.commited_position = self.shadowed_position; + self.poisoned = false; + // update position in registers unsafe { @@ -268,14 +276,6 @@ impl<'a> Background<'a> { self.set_position_y_register((self.commited_position.y % (32 * 8)) as u16); } } - - pub fn commit(&mut self) { - let area: Rect = Rect { - position: Vector2D::new(-1, -1), - size: Vector2D::new(32, 22), - }; - self.commit_area(area) - } } pub struct Tiled0 { @@ -283,7 +283,7 @@ pub struct Tiled0 { num_backgrounds: u8, } -impl Tiled0 { +impl<'b> Tiled0 { pub(crate) unsafe fn new() -> Self { set_graphics_settings(GraphicsSettings::empty() | GraphicsSettings::SPRITE1_D); set_graphics_mode(DisplayMode::Tiled0); @@ -318,7 +318,7 @@ impl Tiled0 { } /// Gets a map background if possible and assigns an unused block to it. - pub fn get_background(&mut self) -> Result { + pub fn get_background(&mut self) -> Result, &'static str> { if self.num_backgrounds >= 4 { return Err("too many backgrounds created, maximum is 4"); } From 7708ccc4e1fdd519ec8284250dc08b9ca12b86da Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 23:51:07 +0100 Subject: [PATCH 09/15] remove unused import --- agb/src/display/tiled0.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 94b8b71f..d8aab9a2 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -1,4 +1,4 @@ -use core::ops::{Deref, Index}; +use core::ops::Index; use crate::{ memory_mapped::{MemoryMapped, MemoryMapped1DArray}, From 2974711a383d03a58b3d266b0c1eb5bd0364128d Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 8 Aug 2021 00:01:58 +0100 Subject: [PATCH 10/15] update more --- agb/src/display/tiled0.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index d8aab9a2..81fb3b3c 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -211,7 +211,7 @@ impl<'a> Background<'a> { if self.poisoned { let positions_to_be_updated = - Rect::new(self.shadowed_position / 8, (30, 20).into()).iter(); + Rect::new(self.shadowed_position / 8 - (1, 1).into(), (31, 21).into()).iter(); if let Some(map) = &self.map { for (x, y) in positions_to_be_updated { From 631bf2bae25ddb4b477344c973df676c30ee2158 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 15 Aug 2021 23:40:25 +0100 Subject: [PATCH 11/15] add collision function --- agb/src/number.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/agb/src/number.rs b/agb/src/number.rs index a09e0e48..2ff6e7e8 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -656,6 +656,13 @@ impl Rect { && point.y < self.position.y + self.size.y } + pub fn touches(self, other: Rect) -> bool { + self.position.x < other.position.x + other.size.x + && self.position.x + self.size.x > other.position.x + && self.position.y < other.position.y + other.size.y + && self.position.y + self.size.y > self.position.y + } + pub fn overlapping_rect(&self, other: Rect) -> Rect { fn max(x: E, y: E) -> E { if x > y { From fe9461e5c5c58429f747a1933a7a2b6c33b18c3a Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 15 Aug 2021 23:40:57 +0100 Subject: [PATCH 12/15] redraw everything if there is no overlap --- agb/src/display/tiled0.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 81fb3b3c..2cd864d2 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -209,7 +209,10 @@ impl<'a> Background<'a> { // commit shadowed register unsafe { self.get_register().set(self.shadowed_register) }; - if self.poisoned { + let commited_screen = Rect::new(self.commited_position, (30, 20).into()); + let shadowed_screen = Rect::new(self.shadowed_position, (30, 20).into()); + + if self.poisoned || !shadowed_screen.touches(commited_screen) { let positions_to_be_updated = Rect::new(self.shadowed_position / 8 - (1, 1).into(), (31, 21).into()).iter(); From 1ae3c348772d289f1468a48c12bda006e33a08ce Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 28 Aug 2021 23:02:35 +0100 Subject: [PATCH 13/15] begin work on affine regular compatability --- agb/examples/chicken.rs | 4 +- agb/src/display/{tiled0.rs => background.rs} | 49 +++++++++++++------- agb/src/display/example_logo.rs | 8 ++-- agb/src/display/mod.rs | 4 +- agb/src/display/video.rs | 6 +-- 5 files changed, 43 insertions(+), 28 deletions(-) rename agb/src/display/{tiled0.rs => background.rs} (90%) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 86cbfd15..019af3c5 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -3,7 +3,7 @@ extern crate agb; use agb::{ - display::{object::ObjectStandard, tiled0::Map, HEIGHT, WIDTH}, + display::{background::Map, object::ObjectStandard, HEIGHT, WIDTH}, input::Button, }; use core::convert::TryInto; @@ -51,7 +51,7 @@ pub fn main() -> ! { gfx.set_background_palette_raw(&MAP_PALETTE); gfx.set_background_tilemap(0, &MAP_TILES); - let mut background = gfx.get_background().unwrap(); + let mut background = gfx.get_regular().unwrap(); background.set_map(Map::new(&MAP_MAP, (32_u32, 32_u32).into(), 0)); background.show(); background.commit(); diff --git a/agb/src/display/tiled0.rs b/agb/src/display/background.rs similarity index 90% rename from agb/src/display/tiled0.rs rename to agb/src/display/background.rs index 2cd864d2..c40d7266 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/background.rs @@ -79,7 +79,7 @@ impl<'a> MapStorage<'a> { /// The map background is the method of drawing game maps to the screen. It /// automatically handles copying the correct portion of a provided map to the /// assigned block depending on given coordinates. -pub struct Background<'a> { +pub struct BackgroundRegular<'a> { background: u8, block: u8, commited_position: Vector2D, @@ -125,9 +125,9 @@ impl<'a> Map<'a> { } } -impl<'a> Background<'a> { - unsafe fn new(background: u8, block: u8) -> Background<'a> { - let mut b = Background { +impl<'a> BackgroundRegular<'a> { + unsafe fn new(background: u8, block: u8) -> BackgroundRegular<'a> { + let mut b = BackgroundRegular { background, block, commited_position: (0, 0).into(), @@ -281,18 +281,32 @@ impl<'a> Background<'a> { } } -pub struct Tiled0 { - used_blocks: u32, - num_backgrounds: u8, +fn decide_background_mode(num_regular: u8, num_affine: u8) -> Option { + if num_affine == 0 && num_regular <= 4 { + Some(DisplayMode::Tiled0) + } else if num_affine == 1 && num_regular <= 2 { + Some(DisplayMode::Tiled1) + } else if num_affine == 2 && num_regular == 0 { + Some(DisplayMode::Tiled2) + } else { + None + } } -impl<'b> Tiled0 { +pub struct BackgroundDistributor { + used_blocks: u32, + num_regular: u8, + num_affine: u8, +} + +impl<'b> BackgroundDistributor { pub(crate) unsafe fn new() -> Self { set_graphics_settings(GraphicsSettings::empty() | GraphicsSettings::SPRITE1_D); set_graphics_mode(DisplayMode::Tiled0); - Tiled0 { + BackgroundDistributor { used_blocks: 0, - num_backgrounds: 0, + num_regular: 0, + num_affine: 0, } } @@ -321,10 +335,11 @@ impl<'b> Tiled0 { } /// Gets a map background if possible and assigns an unused block to it. - pub fn get_background(&mut self) -> Result, &'static str> { - if self.num_backgrounds >= 4 { - return Err("too many backgrounds created, maximum is 4"); - } + pub fn get_regular(&mut self) -> Result, &'static str> { + let new_mode = decide_background_mode(self.num_regular + 1, self.num_affine) + .ok_or("there is no mode compatible with the requested backgrounds")?; + + unsafe { set_graphics_mode(new_mode) }; if !self.used_blocks == 0 { return Err("all blocks are used"); @@ -346,9 +361,9 @@ impl<'b> Tiled0 { self.used_blocks |= 1 << availiable_block; - let background = self.num_backgrounds; - self.num_backgrounds = background + 1; - Ok(unsafe { Background::new(background, availiable_block) }) + let background = self.num_regular; + self.num_regular += 1; + Ok(unsafe { BackgroundRegular::new(background, availiable_block) }) } /// Copies tiles to tilemap starting at the starting tile. Cannot overwrite diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index f601d999..424a740f 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -1,13 +1,13 @@ -use crate::display::tiled0::Tiled0; +use crate::display::background::BackgroundDistributor; crate::include_gfx!("gfx/agb_logo.toml"); -pub fn display_logo(gfx: &mut Tiled0) { - use super::tiled0::Map; +pub fn display_logo(gfx: &mut BackgroundDistributor) { + use super::background::Map; gfx.set_background_palettes(agb_logo::test_logo.palettes); gfx.set_background_tilemap(0, agb_logo::test_logo.tiles); - let mut back = gfx.get_background().unwrap(); + let mut back = gfx.get_regular().unwrap(); let mut entries: [u16; 30 * 20] = [0; 30 * 20]; for tile_id in 0..(30 * 20) { diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index a05b137f..e4ce5206 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -5,6 +5,8 @@ use video::Video; use self::object::ObjectControl; +/// Graphics mode 0. Four regular backgrounds. +pub mod background; /// Graphics mode 3. Bitmap mode that provides a 16-bit colour framebuffer. pub mod bitmap3; /// Graphics mode 4. Bitmap 4 provides two 8-bit paletted framebuffers with page switching. @@ -17,8 +19,6 @@ pub mod object; pub mod palette16; /// Data produced by agb-image-converter pub mod tile_data; -/// Graphics mode 0. Four regular backgrounds. -pub mod tiled0; /// Giving out graphics mode. pub mod video; diff --git a/agb/src/display/video.rs b/agb/src/display/video.rs index 3c7974a2..2652b8a2 100644 --- a/agb/src/display/video.rs +++ b/agb/src/display/video.rs @@ -1,4 +1,4 @@ -use super::{bitmap3::Bitmap3, bitmap4::Bitmap4, tiled0::Tiled0}; +use super::{background::BackgroundDistributor, bitmap3::Bitmap3, bitmap4::Bitmap4}; #[non_exhaustive] pub struct Video {} @@ -14,7 +14,7 @@ impl Video { unsafe { Bitmap4::new() } } - pub fn tiled0(&mut self) -> Tiled0 { - unsafe { Tiled0::new() } + pub fn tiled0(&mut self) -> BackgroundDistributor { + unsafe { BackgroundDistributor::new() } } } From f7c2118a40e674b090e82c9fbc361b26a56da41f Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 19 Sep 2021 20:17:50 +0100 Subject: [PATCH 14/15] start work on separating out background sizes --- agb/src/display/background.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/agb/src/display/background.rs b/agb/src/display/background.rs index c40d7266..c2ae27b5 100644 --- a/agb/src/display/background.rs +++ b/agb/src/display/background.rs @@ -23,6 +23,7 @@ pub enum ColourMode { EightBitPerPixel = 1, } +#[derive(Clone, Copy)] pub enum BackgroundSize { S32x32 = 0, S64x32 = 1, @@ -86,6 +87,8 @@ pub struct BackgroundRegular<'a> { shadowed_position: Vector2D, poisoned: bool, shadowed_register: u16, + copy_size: Vector2D, + background_size: BackgroundSize, map: Option>, } @@ -126,19 +129,21 @@ impl<'a> Map<'a> { } impl<'a> BackgroundRegular<'a> { - unsafe fn new(background: u8, block: u8) -> BackgroundRegular<'a> { + unsafe fn new(background: u8, block: u8, size: BackgroundSize) -> BackgroundRegular<'a> { let mut b = BackgroundRegular { background, block, commited_position: (0, 0).into(), shadowed_position: (0, 0).into(), shadowed_register: 0, + copy_size: (30, 20).into(), + background_size: size, poisoned: true, map: None, }; b.set_block(block); b.set_colour_mode(ColourMode::FourBitPerPixel); - b.set_background_size(BackgroundSize::S32x32); + b.set_background_size(size); b } @@ -209,12 +214,15 @@ impl<'a> BackgroundRegular<'a> { // commit shadowed register unsafe { self.get_register().set(self.shadowed_register) }; - let commited_screen = Rect::new(self.commited_position, (30, 20).into()); - let shadowed_screen = Rect::new(self.shadowed_position, (30, 20).into()); + let commited_screen = Rect::new(self.commited_position, self.copy_size.change_base()); + let shadowed_screen = Rect::new(self.shadowed_position, self.copy_size.change_base()); if self.poisoned || !shadowed_screen.touches(commited_screen) { - let positions_to_be_updated = - Rect::new(self.shadowed_position / 8 - (1, 1).into(), (31, 21).into()).iter(); + let positions_to_be_updated = Rect::new( + self.shadowed_position / 8 - (1, 1).into(), + self.copy_size.change_base() + (1, 1).into(), + ) + .iter(); if let Some(map) = &self.map { for (x, y) in positions_to_be_updated { @@ -232,7 +240,7 @@ impl<'a> BackgroundRegular<'a> { let top_bottom_rect: Rect = { let top_bottom_height = commited_block.y - shadowed_block.y; let new_y = if top_bottom_height < 0 { - commited_block.y + 20 + commited_block.y + self.copy_size.y as i32 } else { shadowed_block.y }; @@ -245,7 +253,7 @@ impl<'a> BackgroundRegular<'a> { let left_right_rect: Rect = { let left_right_width = commited_block.x - shadowed_block.x; let new_x = if left_right_width < 0 { - commited_block.x + 30 + commited_block.x + self.copy_size.x as i32 } else { shadowed_block.x }; @@ -363,7 +371,7 @@ impl<'b> BackgroundDistributor { let background = self.num_regular; self.num_regular += 1; - Ok(unsafe { BackgroundRegular::new(background, availiable_block) }) + Ok(unsafe { BackgroundRegular::new(background, availiable_block, BackgroundSize::S32x32) }) } /// Copies tiles to tilemap starting at the starting tile. Cannot overwrite From 9192722f5ad398f2c6c320343af547673aa2fffd Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sun, 19 Sep 2021 20:56:36 +0100 Subject: [PATCH 15/15] fix build --- agb/src/display/background.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/src/display/background.rs b/agb/src/display/background.rs index c2ae27b5..05a0d404 100644 --- a/agb/src/display/background.rs +++ b/agb/src/display/background.rs @@ -87,7 +87,7 @@ pub struct BackgroundRegular<'a> { shadowed_position: Vector2D, poisoned: bool, shadowed_register: u16, - copy_size: Vector2D, + copy_size: Vector2D, background_size: BackgroundSize, map: Option>, } @@ -136,7 +136,7 @@ impl<'a> BackgroundRegular<'a> { commited_position: (0, 0).into(), shadowed_position: (0, 0).into(), shadowed_register: 0, - copy_size: (30, 20).into(), + copy_size: (30_u16, 20_u16).into(), background_size: size, poisoned: true, map: None,