diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index e20bda94..35eeed9e 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -688,6 +688,17 @@ impl Vector2D> { y: self.y.floor(), } } + + #[must_use] + /// Attempts to change the base returning None if the numbers cannot be represented + pub fn try_change_base, const M: usize>( + self, + ) -> Option>> { + Some(Vector2D::new( + self.x.try_change_base()?, + self.y.try_change_base()?, + )) + } } impl Vector2D> { diff --git a/agb/examples/affine_background.rs b/agb/examples/affine_background.rs index 7d783c25..e5d64d09 100644 --- a/agb/examples/affine_background.rs +++ b/agb/examples/affine_background.rs @@ -3,6 +3,7 @@ use agb::{ display::{ + affine::{AffineMatrix, AffineMatrixBackground}, tiled::{AffineBackgroundSize, TileFormat, TileSet, TiledMap}, Priority, }, @@ -32,8 +33,8 @@ fn main(mut gba: agb::Gba) -> ! { bg.commit(&mut vram); bg.show(); - let mut rotation: Num = num!(0.); - let rotation_increase = num!(1.); + let mut rotation = num!(0.); + let rotation_increase: Num = num!(0.01); let mut input = agb::input::ButtonController::new(); @@ -45,14 +46,19 @@ fn main(mut gba: agb::Gba) -> ! { scroll_x += input.x_tri() as i32; scroll_y += input.y_tri() as i32; - let scroll_pos = (scroll_x as i16, scroll_y as i16); - bg.set_scroll_pos(scroll_pos.into()); - bg.set_transform((0, 0), (1, 1), rotation); + let scroll_pos = (scroll_x, scroll_y).into(); rotation += rotation_increase; - if rotation >= num!(255.) { - rotation = 0.into(); - } + rotation = rotation.rem_euclid(1.into()); + + let transformation = AffineMatrixBackground::from_scale_rotation_position( + (0, 0).into(), + (1, 1).into(), + rotation, + scroll_pos, + ); + + bg.set_transform(transformation); vblank.wait_for_vblank(); bg.commit(&mut vram); diff --git a/agb/src/display/affine.rs b/agb/src/display/affine.rs index f62fa1a1..8fd23f73 100644 --- a/agb/src/display/affine.rs +++ b/agb/src/display/affine.rs @@ -62,7 +62,7 @@ impl AffineMatrix { #[must_use] /// Generates the matrix that represents a rotation pub fn from_rotation(angle: Num) -> Self { - fn from_rotation(angle: Num) -> AffineMatrix { + fn from_rotation(angle: Num) -> AffineMatrix { let cos = angle.cos().change_base(); let sin = angle.sin().change_base(); @@ -71,8 +71,8 @@ impl AffineMatrix { // space rather than how you might conventionally think of it. AffineMatrix { a: cos, - b: sin, - c: -sin, + b: -sin, + c: sin, d: cos, x: 0.into(), y: 0.into(), @@ -90,15 +90,15 @@ impl AffineMatrix { b: 0.into(), c: 0.into(), d: 1.into(), - x: position.x, - y: position.y, + x: -position.x, + y: -position.y, } } #[must_use] /// The position fields of the matrix pub fn position(&self) -> Vector2D> { - (self.x, self.y).into() + (-self.x, -self.y).into() } /// Attempts to convert the matrix to one which can be used in affine @@ -150,6 +150,20 @@ impl AffineMatrix { d: Num::from_raw(self.d.to_raw() as i16), } } + + #[must_use] + /// Creates an affine matrix from a given (x, y) scaling. This will scale by + /// the inverse, ie (2, 2) will produce half the size. + pub fn from_scale(scale: Vector2D>) -> AffineMatrix { + AffineMatrix { + a: scale.x, + b: 0.into(), + c: 0.into(), + d: scale.y, + x: 0.into(), + y: 0.into(), + } + } } impl Default for AffineMatrix { @@ -198,6 +212,35 @@ impl AffineMatrixBackground { y: self.y, } } + + #[must_use] + /// Creates a transformation matrix using GBA specific syscalls. + /// This can be done using the standard transformation matricies like + /// + /// ```rust,no_run + /// use agb::display::affine::AffineMatrix; + /// # #![no_std] + /// # #![no_main] + /// # fn something() { + /// let A = AffineMatrix::from_translation(-transform_origin) + /// * AffineMatrix::from_scale(scale) + /// * AffineMatrix::from_rotation(rotation) + /// * AffineMatrix::from_translation(position); + /// # } + /// ``` + pub fn from_scale_rotation_position( + transform_origin: Vector2D>, + scale: Vector2D>, + rotation: Num, + position: Vector2D>, + ) -> Self { + crate::syscall::bg_affine_matrix( + transform_origin, + position.try_change_base::().unwrap().floor(), + scale.try_change_base().unwrap(), + rotation.rem_euclid(1.into()).try_change_base().unwrap(), + ) + } } impl From for AffineMatrix { diff --git a/agb/src/display/tiled/map.rs b/agb/src/display/tiled/map.rs index 50cab171..43302806 100644 --- a/agb/src/display/tiled/map.rs +++ b/agb/src/display/tiled/map.rs @@ -344,16 +344,8 @@ impl AffineMap { *self.tiles_dirty() = true; } - pub fn set_transform( - &mut self, - transform_origin: impl Into>>, - scale: impl Into>>, - rotation: impl Into>, - ) { - let scale = scale.into(); - let rotation = rotation.into(); - self.transform = - crate::syscall::bg_affine_matrix(transform_origin.into(), self.scroll, scale, rotation); + pub fn set_transform(&mut self, transformation: impl Into) { + self.transform = transformation.into(); } fn bg_affine_matrix(&self) -> MemoryMapped { diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index 1d0c869c..14a0451c 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -145,20 +145,26 @@ pub fn bg_affine_matrix( bg_center: Vector2D>, display_center: Vector2D, scale: Vector2D>, - rotation: Num, + rotation: Num, ) -> AffineMatrixBackground { #[repr(C, packed(4))] struct Input { - bg_center: Vector2D>, - display_center: Vector2D, - scale: Vector2D>, - rotation: Num, + bg_center_x: Num, + bg_center_y: Num, + display_center_x: i16, + display_center_y: i16, + scale_x: Num, + scale_y: Num, + rotation: Num, } let input = Input { - bg_center, - display_center, - scale, + bg_center_x: bg_center.x, + bg_center_y: bg_center.y, + display_center_x: display_center.x, + display_center_y: display_center.y, + scale_x: scale.x, + scale_y: scale.y, rotation, };