diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index f929c68c..8eedaf1c 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -55,7 +55,7 @@ pub fn main() -> ! { gfx.set_background_tilemap(0, &MAP_TILES); let mut background = gfx.get_background().unwrap(); - background.set_map(&MAP_MAP, 32, 32); + background.draw_full_map(&MAP_MAP, (32, 32).into()); background.show(); let mut object = gba.display.object.get(); diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index 724c84fa..002a8cb9 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -21,8 +21,7 @@ pub fn main() -> ! { entries[tile_id as usize] = tile_id | (palette_entry << 12); } - back.set_map(&entries, 30, 20); - back.set_position(0, 0); + back.draw_full_map(&entries, (30, 20).into()); back.show(); loop {} diff --git a/agb/iwillnotletyouletmedown.gba b/agb/iwillnotletyouletmedown.gba new file mode 100755 index 00000000..5eba92ba Binary files /dev/null and b/agb/iwillnotletyouletmedown.gba differ diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index de199d4b..10825f21 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -15,8 +15,7 @@ fn logo_display(gba: &mut crate::Gba) { entries[tile_id as usize] = tile_id | (palette_entry << 12); } - back.set_map(&entries, 30, 20); - back.set_position(0, 0); + back.draw_full_map(&entries, (30, 20).into()); back.show(); crate::assert_image_output("gfx/test_logo.png"); diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index cda2bccc..7cb3da72 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -1,10 +1,13 @@ -use core::{borrow::Borrow, cell::RefCell, convert::TryInto}; +use core::{convert::TryInto, ops::Deref}; -use crate::memory_mapped::MemoryMapped1DArray; +use crate::{ + memory_mapped::MemoryMapped1DArray, + number::{Rect, Vector2D}, +}; use super::{ - object::ObjectControl, palette16, set_graphics_mode, set_graphics_settings, DisplayMode, - GraphicsSettings, DISPLAY_CONTROL, + palette16, set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, + DISPLAY_CONTROL, }; const PALETTE_BACKGROUND: MemoryMapped1DArray = @@ -42,29 +45,18 @@ 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 { background: u8, block: u8, - map: Option>, - map_dim_x: u32, - map_dim_y: u32, pos_x: i32, pos_y: i32, } -enum MapStorage<'a> { - R(&'a RefCell<[u16]>), - S(&'a [u16]), -} - -impl<'a> Background<'a> { - unsafe fn new(layer: u8, block: u8) -> Background<'a> { +impl Background { + unsafe fn new(layer: u8, block: u8) -> Background { let mut background = Background { background: layer, block, - map: None, - map_dim_x: 0, - map_dim_y: 0, pos_x: 0, pos_y: 0, }; @@ -73,34 +65,12 @@ impl<'a> Background<'a> { background.set_block(block); background } - - /// Sets the internal map to the provided map. Dimensions should be the - /// dimensions of the map. The mapping between coordinate and index is given - /// by `y * dim_x + x`. The length of the map slice should be `dim_x * - /// dim_y`, or panics may occur. - /// - /// The portion of this map that is in view is copied to the map block - /// assigned to this background. - pub fn set_map(&mut self, map: &'a [u16], dim_x: u32, dim_y: u32) { - self.map = Some(MapStorage::S(map)); - self.map_dim_x = dim_x; - self.map_dim_y = dim_y; - self.draw_full_map(); - } - - pub fn set_map_refcell(&mut self, map: &'a RefCell<[u16]>, dim_x: u32, dim_y: u32) { - self.map = Some(MapStorage::R(map)); - self.map_dim_x = dim_x; - self.map_dim_y = dim_y; - self.draw_full_map(); - } } -impl 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) { - assert!(self.map.is_some(), "map should be set before showing"); let mode = DISPLAY_CONTROL.get(); let new_mode = mode | (1 << (self.background + 0x08)); DISPLAY_CONTROL.set(new_mode); @@ -143,30 +113,21 @@ impl Background<'_> { unsafe { self.set_bits(0x0E, 2, size as u16) } } - fn map_get(&self, x: i32, y: i32, default: u16) -> u16 { - match self.map.as_ref().unwrap() { - MapStorage::R(map) => { - let map = (*map).borrow(); - if x >= self.map_dim_x as i32 || x < 0 || y >= self.map_dim_y as i32 || y < 0 { - default - } else { - map[(self.map_dim_x as i32 * y + x) as usize] - } - } - MapStorage::S(map) => { - if x >= self.map_dim_x as i32 || x < 0 || y >= self.map_dim_y as i32 || y < 0 { - default - } else { - map[(self.map_dim_x as i32 * y + x) as usize] - } - } + 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 + } else { + map[(dim_x as i32 * y + x) as usize] } } - fn set_x(&mut self, x: u16) { + fn set_x(&self, x: u16) { unsafe { *((0x0400_0010 + 4 * self.background as usize) as *mut u16) = x } } - fn set_y(&mut self, y: u16) { + fn set_y(&self, y: u16) { unsafe { *((0x0400_0012 + 4 * self.background as usize) as *mut u16) = y } } @@ -174,20 +135,60 @@ impl Background<'_> { /// 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) { + pub fn draw_full_map(&mut self, map: &[u16], dimensions: Vector2D) { + let area: Rect = Rect { + position: Vector2D::new(-1, -1), + size: Vector2D::new(32, 22), + }; + self.draw_area(map, dimensions, area); + } + + /// 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) { + self.draw_area_mapped( + &map, + dimensions.x, + dimensions.y, + area.position.x, + area.position.y, + area.size.x, + area.size.y, + ); + } + + #[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, + ) 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 -1..31 { - for y in -1..21 { + 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(x_map_space + x, y_map_space + y, 0)) + .write_volatile(self.map_get( + map, + dim_x, + dim_y, + x_map_space + x, + y_map_space + y, + 0, + )) }; } } @@ -196,7 +197,21 @@ impl Background<'_> { /// 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, x: i32, y: i32) { + pub fn set_position( + &mut self, + map: &[u16], + dimensions: Vector2D, + position: Vector2D, + ) { + self.set_position_mapped(&map, dimensions.x, dimensions.y, position.x, position.y); + self.pos_x = position.x; + self.pos_y = position.y; + } + + fn set_position_mapped(&self, map: &T, dim_x: u32, dim_y: u32, x: i32, y: i32) + where + T: Deref, + { let x_map_space = x / 8; let y_map_space = y / 8; @@ -209,12 +224,9 @@ impl Background<'_> { 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_full_map(); + self.draw_area_mapped(map, dim_x, dim_y, -1, 32, -1, 22); } else { if x_difference != 0 { let x_offset = match x_difference { @@ -229,6 +241,9 @@ impl Background<'_> { [(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, 0, @@ -249,6 +264,9 @@ impl Background<'_> { [(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, 0, diff --git a/agb/src/number.rs b/agb/src/number.rs index 73f2c0f6..de0885dd 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -7,6 +7,24 @@ use core::{ }, }; +pub trait Number: + Sized + + Copy + + PartialOrd + + Ord + + PartialEq + + Eq + + Add + + Sub + + Rem + + Div + + Mul +{ +} + +impl Number for Num {} +impl Number for I {} + pub trait FixedWidthUnsignedInteger: Sized + Copy @@ -73,22 +91,8 @@ fixed_width_signed_integer_impl!(i32); #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Num(I); -pub type Number = Num; - -pub fn change_base< - I: FixedWidthUnsignedInteger, - J: FixedWidthUnsignedInteger + Into, - const N: usize, - const M: usize, ->( - num: Num, -) -> Num { - if N < M { - Num(num.0.into() << (M - N)) - } else { - Num(num.0.into() >> (N - M)) - } -} +pub type FixedNum = Num; +pub type Integer = Num; impl From for Num { fn from(value: I) -> Self { @@ -213,6 +217,15 @@ impl Neg for Num { } impl Num { + pub fn change_base, const M: usize>(self) -> Num { + let n: J = self.0.into(); + if N < M { + Num(n << (M - N)) + } else { + Num(n >> (N - M)) + } + } + pub fn from_raw(n: I) -> Self { Num(n) } @@ -276,7 +289,7 @@ impl Num { pub fn sin(self) -> Self { let one: Self = I::one().into(); let four: I = 4.into(); - (self - one / four).cos() + (self + one / four).cos() } } @@ -339,8 +352,8 @@ fn test_change_base(_gba: &mut super::Gba) { let two: Num = 2.into(); let three: Num = 3.into(); - assert_eq!(two + change_base(three), 5.into()); - assert_eq!(three + change_base(two), 5.into()); + assert_eq!(two + three.change_base(), 5.into()); + assert_eq!(three + two.change_base(), 5.into()); } #[test_case] @@ -432,3 +445,145 @@ impl Debug for Num { write!(f, "Num<{}, {}>({})", type_name::(), N, self) } } + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct Vector2D { + pub x: T, + pub y: T, +} + +impl Add> for Vector2D { + type Output = Vector2D; + fn add(self, rhs: Vector2D) -> Self::Output { + Vector2D { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} + +impl> Mul for Vector2D { + type Output = Vector2D; + fn mul(self, rhs: U) -> Self::Output { + Vector2D { + x: self.x * rhs.into(), + y: self.y * rhs.into(), + } + } +} + +impl> Div for Vector2D { + type Output = Vector2D; + fn div(self, rhs: U) -> Self::Output { + Vector2D { + x: self.x / rhs.into(), + y: self.y / rhs.into(), + } + } +} + +#[test_case] +fn test_vector_multiplication_and_division(_gba: &mut super::Gba) { + let a: Vector2D = (1, 2).into(); + let b = a * 5; + let c = b / 5; + assert_eq!(b, (5, 10).into()); + assert_eq!(a, c); +} + +impl AddAssign for Vector2D { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sub> for Vector2D { + type Output = Vector2D; + fn sub(self, rhs: Vector2D) -> Self::Output { + Vector2D { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } +} + +impl SubAssign for Vector2D { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Vector2D> { + pub fn trunc(self) -> Vector2D { + Vector2D { + x: self.x.trunc(), + y: self.y.trunc(), + } + } + pub fn floor(self) -> Vector2D { + Vector2D { + x: self.x.floor(), + y: self.y.floor(), + } + } +} + +impl From<(T, T)> for Vector2D { + fn from(f: (T, T)) -> Self { + Vector2D::new(f.0, f.1) + } +} + +impl Vector2D { + pub fn change_base>(self) -> Vector2D { + (self.x.into(), self.y.into()).into() + } +} + +impl Vector2D> { + pub fn new_from_angle(angle: Num) -> Self { + Vector2D { + x: angle.cos(), + y: angle.sin(), + } + } +} + +impl From> for Vector2D> { + fn from(n: Vector2D) -> Self { + Vector2D { + x: n.x.into(), + y: n.y.into(), + } + } +} + +pub struct Rect { + pub position: Vector2D, + pub size: Vector2D, +} + +impl Rect { + pub fn new(position: Vector2D, size: Vector2D) -> Self { + Rect { position, size } + } +} + +impl Vector2D { + pub fn new(x: T, y: T) -> Self { + Vector2D { x, y } + } + pub fn get(self) -> (T, T) { + (self.x, self.y) + } +} + +#[test_case] +fn test_vector_changing(_gba: &mut super::Gba) { + let v1: Vector2D> = Vector2D::new(1.into(), 2.into()); + + let v2 = v1.trunc(); + assert_eq!(v2.get(), (1, 2)); + + assert_eq!(v1 + v1, (v2 + v2).into()); +}