Merge pull request #324 from corwinkuiper/affine-matrix-background

Use the new Affine Matrix stuff in the affine background
This commit is contained in:
Corwin 2022-10-09 20:51:26 +01:00 committed by GitHub
commit e30e0b76e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 289 additions and 216 deletions

View file

@ -133,8 +133,8 @@ fixed_width_signed_integer_impl!(i16);
fixed_width_signed_integer_impl!(i32); fixed_width_signed_integer_impl!(i32);
/// A fixed point number represented using `I` with `N` bits of fractional precision /// A fixed point number represented using `I` with `N` bits of fractional precision
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Num<I: FixedWidthUnsignedInteger, const N: usize>(I); pub struct Num<I: FixedWidthUnsignedInteger, const N: usize>(I);
/// An often convenient representation for the Game Boy Advance using word sized /// An often convenient representation for the Game Boy Advance using word sized
@ -571,7 +571,6 @@ impl<I: FixedWidthUnsignedInteger, const N: usize> Debug for Num<I, N> {
/// A vector of two points: (x, y) represened by integers or fixed point numbers /// A vector of two points: (x, y) represened by integers or fixed point numbers
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[repr(C)]
pub struct Vector2D<T: Number> { pub struct Vector2D<T: Number> {
/// The x coordinate /// The x coordinate
pub x: T, pub x: T,
@ -689,6 +688,17 @@ impl<I: FixedWidthUnsignedInteger, const N: usize> Vector2D<Num<I, N>> {
y: self.y.floor(), y: self.y.floor(),
} }
} }
#[must_use]
/// Attempts to change the base returning None if the numbers cannot be represented
pub fn try_change_base<J: FixedWidthUnsignedInteger + TryFrom<I>, const M: usize>(
self,
) -> Option<Vector2D<Num<J, M>>> {
Some(Vector2D::new(
self.x.try_change_base()?,
self.y.try_change_base()?,
))
}
} }
impl<const N: usize> Vector2D<Num<i32, N>> { impl<const N: usize> Vector2D<Num<i32, N>> {

View file

@ -3,6 +3,7 @@
use agb::{ use agb::{
display::{ display::{
affine::AffineMatrixBackground,
tiled::{AffineBackgroundSize, TileFormat, TileSet, TiledMap}, tiled::{AffineBackgroundSize, TileFormat, TileSet, TiledMap},
Priority, Priority,
}, },
@ -32,8 +33,8 @@ fn main(mut gba: agb::Gba) -> ! {
bg.commit(&mut vram); bg.commit(&mut vram);
bg.show(); bg.show();
let mut rotation: Num<u16, 8> = num!(0.); let mut rotation = num!(0.);
let rotation_increase = num!(1.); let rotation_increase: Num<i32, 16> = num!(0.01);
let mut input = agb::input::ButtonController::new(); 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_x += input.x_tri() as i32;
scroll_y += input.y_tri() as i32; scroll_y += input.y_tri() as i32;
let scroll_pos = (scroll_x as i16, scroll_y as i16); let scroll_pos = (scroll_x, scroll_y).into();
bg.set_scroll_pos(scroll_pos.into());
bg.set_transform((0, 0), (1, 1), rotation);
rotation += rotation_increase; rotation += rotation_increase;
if rotation >= num!(255.) { rotation = rotation.rem_euclid(1.into());
rotation = 0.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(); vblank.wait_for_vblank();
bg.commit(&mut vram); bg.commit(&mut vram);

View file

@ -7,19 +7,82 @@
//! Affine matricies are used in two places on the GBA, for affine backgrounds //! Affine matricies are used in two places on the GBA, for affine backgrounds
//! and for affine objects. //! and for affine objects.
//! //!
//! # Linear Algebra basics //! # Linear Algebra
//! As a matrix, they can be manipulated using linear algebra, although you //! As a matrix, they can be manipulated using linear algebra. The short version
//! shouldn't need to know linear algebra to use this apart from a few things //! of this section is to beware that the matrix is the inverse of the normal
//! transformation matricies.
//! //!
//! If `A` and `B` are matricies, then matrix `C = A * B` represents the //! One quick thing to point out at the start as it will become very relevant is
//! transformation `A` performed on `B`, or alternatively `C` is transformation //! that matrix-matrix multiplication is not commutative, meaning swapping the
//! `B` followed by transformation `A`. //! order changes the result, or **A** × **B** ≢ **B** × **A**. However,
//! matricies are, at least in the case they are used here, associative, meaning
//! (**AB**)**C** = **A**(**BC**).
//! //!
//! Additionally matrix multiplication is not commutative, meaning swapping the //! ## Normal (wrong on GBA!) transformation matricies
//! order changes the result, or `A * B ≢ B * A`. //!
//! As a start, normal transformation matricies will transform a shape from it's
//! original position to it's new position. Generally when people talk about
//! transformation matricies they are talking about them in this sense.
//!
//! > If **A** and **B** are transformation matricies, then matrix **C** = **A**
//! > × **B** represents the transformation **A** performed on **B**, or
//! > alternatively **C** is transformation **B** followed by transformation
//! > **A**.
//!
//! This is not what they represent on the GBA! If you are looking up more
//! information about tranformation matricies bear this in mind.
//!
//! ## Correct (on GBA) transformation matricies
//!
//! On the GBA, the affine matrix works the other way around. The GBA wants to
//! know for each pixel what colour it should render, to do this it applies the
//! affine transformation matrix to the pixel it is rendering to lookup correct
//! pixel in the texture.
//!
//! This describes the inverse of the previously given transformation matricies.
//!
//! Above I described the matrix **C** = **A** × **B**, but what the GBA wants
//! is the inverse of **C**, or **C**<sup>-1</sup> = (**AB**)<sup>-1</sup> =
//! **B**<sup>-1</sup> × **A**<sup>-1</sup>. This means that if we have the
//! matricies **I** and **J** in the form the GBA expects then
//!
//! > Transformation **K** = **I** × **J** is the transformation **I** followed
//! > by the transformation **J**.
//!
//! Beware if you are used to the other way around!
//!
//! ## Example, rotation around the center
//!
//! To rotate something around its center, you will need to move the thing such
//! that the center is at (0, 0) and then you can rotate it. After that you can
//! move it where you actually want it.
//!
//! These can be done in the order I stated, **A** = **Move To Origin** ×
//! **Rotate** × **Move to Final Position**. Or in code,
//!
//! ```rust,no_run
//! # #![no_std]
//! # #![no_main]
//! use agb::fixnum::{Vector2D, Num, num};
//! use agb::display::affine::AffineMatrix;
//!
//! # fn foo(_gba: &mut agb::Gba) {
//! // size of our thing is 10 pixels by 10 pixels
//! let size_of_thing: Vector2D<Num<i32, 8>> = (10, 10).into();
//! // rotation by a quarter turn
//! let rotation: Num<i32, 8> = num!(0.25);
//! // the final position
//! let position: Vector2D<Num<i32, 8>> = (100, 100).into();
//!
//! // now lets calculate the final transformation matrix!
//! let a = AffineMatrix::from_translation(-size_of_thing / 2)
//! * AffineMatrix::from_rotation(rotation)
//! * AffineMatrix::from_translation(position);
//! # }
//! ```
use core::{ use core::{
convert::{TryFrom, TryInto}, convert::TryFrom,
ops::{Mul, MulAssign}, ops::{Mul, MulAssign},
}; };
@ -62,7 +125,7 @@ impl AffineMatrix {
#[must_use] #[must_use]
/// Generates the matrix that represents a rotation /// Generates the matrix that represents a rotation
pub fn from_rotation<const N: usize>(angle: Num<i32, N>) -> Self { pub fn from_rotation<const N: usize>(angle: Num<i32, N>) -> Self {
fn from_rotation(angle: Num<i32, 28>) -> AffineMatrix { fn from_rotation(angle: Num<i32, 8>) -> AffineMatrix {
let cos = angle.cos().change_base(); let cos = angle.cos().change_base();
let sin = angle.sin().change_base(); let sin = angle.sin().change_base();
@ -71,8 +134,8 @@ impl AffineMatrix {
// space rather than how you might conventionally think of it. // space rather than how you might conventionally think of it.
AffineMatrix { AffineMatrix {
a: cos, a: cos,
b: sin, b: -sin,
c: -sin, c: sin,
d: cos, d: cos,
x: 0.into(), x: 0.into(),
y: 0.into(), y: 0.into(),
@ -90,27 +153,27 @@ impl AffineMatrix {
b: 0.into(), b: 0.into(),
c: 0.into(), c: 0.into(),
d: 1.into(), d: 1.into(),
x: position.x, x: -position.x,
y: position.y, y: -position.y,
} }
} }
#[must_use] #[must_use]
/// The position fields of the matrix /// The position fields of the matrix
pub fn position(&self) -> Vector2D<Num<i32, 8>> { pub fn position(&self) -> Vector2D<Num<i32, 8>> {
(self.x, self.y).into() (-self.x, -self.y).into()
} }
/// Attempts to convert the matrix to one which can be used in affine /// Attempts to convert the matrix to one which can be used in affine
/// backgrounds. /// backgrounds.
pub fn try_to_background(&self) -> Result<AffineMatrixBackground, OverflowError> { pub fn try_to_background(&self) -> Result<AffineMatrixBackground, OverflowError> {
Ok(AffineMatrixBackground { Ok(AffineMatrixBackground {
a: self.a.to_raw().try_into().map_err(|_| OverflowError(()))?, a: self.a.try_change_base().ok_or(OverflowError(()))?,
b: self.a.to_raw().try_into().map_err(|_| OverflowError(()))?, b: self.b.try_change_base().ok_or(OverflowError(()))?,
c: self.a.to_raw().try_into().map_err(|_| OverflowError(()))?, c: self.c.try_change_base().ok_or(OverflowError(()))?,
d: self.a.to_raw().try_into().map_err(|_| OverflowError(()))?, d: self.d.try_change_base().ok_or(OverflowError(()))?,
x: self.a.to_raw(), x: self.x,
y: self.a.to_raw(), y: self.y,
}) })
} }
@ -119,28 +182,75 @@ impl AffineMatrix {
/// wrapping any value which is too large to be represented there. /// wrapping any value which is too large to be represented there.
pub fn to_background_wrapping(&self) -> AffineMatrixBackground { pub fn to_background_wrapping(&self) -> AffineMatrixBackground {
AffineMatrixBackground { AffineMatrixBackground {
a: self.a.to_raw() as i16, a: Num::from_raw(self.a.to_raw() as i16),
b: self.a.to_raw() as i16, b: Num::from_raw(self.b.to_raw() as i16),
c: self.a.to_raw() as i16, c: Num::from_raw(self.c.to_raw() as i16),
d: self.a.to_raw() as i16, d: Num::from_raw(self.d.to_raw() as i16),
x: self.a.to_raw(), x: self.x,
y: self.a.to_raw(), y: self.y,
} }
} }
/// Attempts to convert the matrix to one which can be used in affine
/// objects.
pub fn try_to_object(&self) -> Result<AffineMatrixObject, OverflowError> {
Ok(AffineMatrixObject {
a: self.a.try_change_base().ok_or(OverflowError(()))?,
b: self.b.try_change_base().ok_or(OverflowError(()))?,
c: self.c.try_change_base().ok_or(OverflowError(()))?,
d: self.d.try_change_base().ok_or(OverflowError(()))?,
})
}
#[must_use]
/// Converts the matrix to one which can be used in affine objects
/// wrapping any value which is too large to be represented there.
pub fn to_object_wrapping(&self) -> AffineMatrixObject {
AffineMatrixObject {
a: Num::from_raw(self.a.to_raw() as i16),
b: Num::from_raw(self.b.to_raw() as i16),
c: Num::from_raw(self.c.to_raw() as i16),
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<Num<i32, 8>>) -> AffineMatrix {
AffineMatrix {
a: scale.x,
b: 0.into(),
c: 0.into(),
d: scale.y,
x: 0.into(),
y: 0.into(),
}
}
}
impl Default for AffineMatrix {
fn default() -> Self {
AffineMatrix::identity()
}
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(C, packed(4))] #[repr(C, packed(4))]
/// An affine matrix that can be used in affine backgrounds /// An affine matrix that can be used in affine backgrounds
pub struct AffineMatrixBackground { pub struct AffineMatrixBackground {
// Internally these can be thought of as Num<i16, 8> a: Num<i16, 8>,
a: i16, b: Num<i16, 8>,
b: i16, c: Num<i16, 8>,
c: i16, d: Num<i16, 8>,
d: i16, x: Num<i32, 8>,
// These are Num<i32, 8> y: Num<i32, 8>,
x: i32, }
y: i32,
impl Default for AffineMatrixBackground {
fn default() -> Self {
AffineMatrix::identity().to_background_wrapping()
}
} }
impl TryFrom<AffineMatrix> for AffineMatrixBackground { impl TryFrom<AffineMatrix> for AffineMatrixBackground {
@ -157,14 +267,49 @@ impl AffineMatrixBackground {
/// calculations. /// calculations.
pub fn to_affine_matrix(&self) -> AffineMatrix { pub fn to_affine_matrix(&self) -> AffineMatrix {
AffineMatrix { AffineMatrix {
a: Num::from_raw(self.a.into()), a: self.a.change_base(),
b: Num::from_raw(self.b.into()), b: self.b.change_base(),
c: Num::from_raw(self.c.into()), c: self.c.change_base(),
d: Num::from_raw(self.d.into()), d: self.d.change_base(),
x: Num::from_raw(self.x), x: self.x,
y: Num::from_raw(self.y), 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
/// # #![no_std]
/// # #![no_main]
/// # use agb_fixnum::{Vector2D, Num};
/// use agb::display::affine::AffineMatrix;
/// # fn from_scale_rotation_position(
/// # transform_origin: Vector2D<Num<i32, 8>>,
/// # scale: Vector2D<Num<i32, 8>>,
/// # rotation: Num<i32, 16>,
/// # position: Vector2D<Num<i32, 8>>,
/// # ) {
/// 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<Num<i32, 8>>,
scale: Vector2D<Num<i32, 8>>,
rotation: Num<i32, 16>,
position: Vector2D<Num<i32, 8>>,
) -> Self {
crate::syscall::bg_affine_matrix(
transform_origin,
position.try_change_base::<i16, 8>().unwrap().floor(),
scale.try_change_base().unwrap(),
rotation.rem_euclid(1.into()).try_change_base().unwrap(),
)
}
} }
impl From<AffineMatrixBackground> for AffineMatrix { impl From<AffineMatrixBackground> for AffineMatrix {
@ -173,9 +318,49 @@ impl From<AffineMatrixBackground> for AffineMatrix {
} }
} }
impl Default for AffineMatrix { #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(C, packed(4))]
/// An affine matrix that can be used in affine objects
pub struct AffineMatrixObject {
a: Num<i16, 8>,
b: Num<i16, 8>,
c: Num<i16, 8>,
d: Num<i16, 8>,
}
impl Default for AffineMatrixObject {
fn default() -> Self { fn default() -> Self {
AffineMatrix::identity() AffineMatrix::identity().to_object_wrapping()
}
}
impl TryFrom<AffineMatrix> for AffineMatrixObject {
type Error = OverflowError;
fn try_from(value: AffineMatrix) -> Result<Self, Self::Error> {
value.try_to_object()
}
}
impl AffineMatrixObject {
#[must_use]
/// Converts to the affine matrix that is usable in performing efficient
/// calculations.
pub fn to_affine_matrix(&self) -> AffineMatrix {
AffineMatrix {
a: self.a.change_base(),
b: self.b.change_base(),
c: self.c.change_base(),
d: self.d.change_base(),
x: 0.into(),
y: 0.into(),
}
}
}
impl From<AffineMatrixObject> for AffineMatrix {
fn from(mat: AffineMatrixObject) -> Self {
mat.to_affine_matrix()
} }
} }
@ -183,7 +368,7 @@ impl Mul for AffineMatrix {
type Output = Self; type Output = Self;
fn mul(self, rhs: Self) -> Self::Output { fn mul(self, rhs: Self) -> Self::Output {
AffineMatrix { AffineMatrix {
a: self.a * rhs.a + self.b + rhs.c, a: self.a * rhs.a + self.b * rhs.c,
b: self.a * rhs.b + self.b * rhs.d, b: self.a * rhs.b + self.b * rhs.d,
c: self.c * rhs.a + self.d * rhs.c, c: self.c * rhs.a + self.d * rhs.c,
d: self.c * rhs.b + self.d * rhs.d, d: self.c * rhs.b + self.d * rhs.d,
@ -193,26 +378,6 @@ impl Mul for AffineMatrix {
} }
} }
impl Mul<Num<i32, 8>> for AffineMatrix {
type Output = Self;
fn mul(self, rhs: Num<i32, 8>) -> Self::Output {
self * AffineMatrix {
a: rhs,
b: 0.into(),
c: 0.into(),
d: rhs,
x: 0.into(),
y: 0.into(),
}
}
}
impl MulAssign<Num<i32, 8>> for AffineMatrix {
fn mul_assign(&mut self, rhs: Num<i32, 8>) {
*self = *self * rhs;
}
}
impl MulAssign for AffineMatrix { impl MulAssign for AffineMatrix {
fn mul_assign(&mut self, rhs: Self) { fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs; *self = *self * rhs;

View file

@ -19,7 +19,7 @@ use super::{Priority, DISPLAY_CONTROL};
use crate::agb_alloc::block_allocator::BlockAllocator; use crate::agb_alloc::block_allocator::BlockAllocator;
use crate::agb_alloc::bump_allocator::StartEnd; use crate::agb_alloc::bump_allocator::StartEnd;
use crate::dma; use crate::dma;
use crate::fixnum::{Num, Vector2D}; use crate::fixnum::Vector2D;
use crate::hash_map::HashMap; use crate::hash_map::HashMap;
use attributes::*; use attributes::*;
@ -1236,42 +1236,6 @@ enum ColourMode {
Eight, Eight,
} }
/// The parameters used for the PPU's affine transformation function
/// that can apply to objects and background layers in modes 1 and 2.
/// This can be obtained from X/Y scale and rotation angle with
/// [`agb::syscall::affine_matrix`].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(C, packed(4))]
pub struct AffineMatrixAttributes {
/// Adjustment made to *X* coordinate when drawing *horizontal* lines.
/// Also known as "dx".
/// Typically computed as `x_scale * cos(angle)`.
pub p_a: Num<i16, 8>,
/// Adjustment made to *X* coordinate along *vertical* lines.
/// Also known as "dmx".
/// Typically computed as `y_scale * sin(angle)`.
pub p_b: Num<i16, 8>,
/// Adjustment made to *Y* coordinate along *horizontal* lines.
/// Also known as "dy".
/// Typically computed as `-x_scale * sin(angle)`.
pub p_c: Num<i16, 8>,
/// Adjustment made to *Y* coordinate along *vertical* lines.
/// Also known as "dmy".
/// Typically computed as `y_scale * cos(angle)`.
pub p_d: Num<i16, 8>,
}
impl Default for AffineMatrixAttributes {
fn default() -> Self {
Self {
p_a: 1.into(),
p_b: Default::default(),
p_c: Default::default(),
p_d: 1.into(),
}
}
}
// this mod is not public, so the internal parts don't need documenting. // this mod is not public, so the internal parts don't need documenting.
#[allow(dead_code)] #[allow(dead_code)]
mod attributes { mod attributes {

View file

@ -2,9 +2,10 @@ use core::cell::RefCell;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use crate::bitarray::Bitarray; use crate::bitarray::Bitarray;
use crate::display::{object::AffineMatrixAttributes, Priority, DISPLAY_CONTROL}; use crate::display::affine::AffineMatrixBackground;
use crate::display::{Priority, DISPLAY_CONTROL};
use crate::dma::dma_copy16; use crate::dma::dma_copy16;
use crate::fixnum::{Num, Vector2D}; use crate::fixnum::Vector2D;
use crate::memory_mapped::MemoryMapped; use crate::memory_mapped::MemoryMapped;
use super::{ use super::{
@ -12,7 +13,6 @@ use super::{
RegularBackgroundSize, Tile, TileFormat, TileIndex, TileSet, TileSetting, VRamManager, RegularBackgroundSize, Tile, TileFormat, TileIndex, TileSet, TileSetting, VRamManager,
}; };
use crate::syscall::BgAffineSetData;
use alloc::{vec, vec::Vec}; use alloc::{vec, vec::Vec};
pub trait TiledMapTypes: private::Sealed { pub trait TiledMapTypes: private::Sealed {
@ -247,7 +247,7 @@ pub struct AffineMap {
scroll: Vector2D<i16>, scroll: Vector2D<i16>,
transform: BgAffineSetData, transform: AffineMatrixBackground,
tiles: Vec<u8>, tiles: Vec<u8>,
tiles_dirty: bool, tiles_dirty: bool,
@ -259,7 +259,7 @@ impl TiledMapTypes for AffineMap {
impl TiledMapPrivate for AffineMap { impl TiledMapPrivate for AffineMap {
type TileType = u8; type TileType = u8;
type AffineMatrix = AffineMatrixAttributes; type AffineMatrix = AffineMatrixBackground;
fn tiles_mut(&mut self) -> &mut [Self::TileType] { fn tiles_mut(&mut self) -> &mut [Self::TileType] {
&mut self.tiles &mut self.tiles
@ -280,10 +280,7 @@ impl TiledMapPrivate for AffineMap {
self.size self.size
} }
fn update_bg_registers(&self) { fn update_bg_registers(&self) {
let register_pos = self.transform.position; self.bg_affine_matrix().set(self.transform);
self.bg_x().set(register_pos.x);
self.bg_y().set(register_pos.y);
self.bg_affine_matrix().set(self.transform.matrix);
} }
fn scroll_pos(&self) -> Vector2D<i16> { fn scroll_pos(&self) -> Vector2D<i16> {
self.scroll self.scroll
@ -347,25 +344,11 @@ impl AffineMap {
*self.tiles_dirty() = true; *self.tiles_dirty() = true;
} }
pub fn set_transform( pub fn set_transform(&mut self, transformation: impl Into<AffineMatrixBackground>) {
&mut self, self.transform = transformation.into();
transform_origin: impl Into<Vector2D<Num<i32, 8>>>,
scale: impl Into<Vector2D<Num<i16, 8>>>,
rotation: impl Into<Num<u16, 8>>,
) {
let scale = scale.into();
let rotation = rotation.into();
self.transform =
crate::syscall::bg_affine_matrix(transform_origin.into(), self.scroll, scale, rotation);
} }
fn bg_x(&self) -> MemoryMapped<Num<i32, 8>> { fn bg_affine_matrix(&self) -> MemoryMapped<AffineMatrixBackground> {
unsafe { MemoryMapped::new(0x0400_0008 + 0x10 * self.background_id()) }
}
fn bg_y(&self) -> MemoryMapped<Num<i32, 8>> {
unsafe { MemoryMapped::new(0x0400_000c + 0x10 * self.background_id()) }
}
fn bg_affine_matrix(&self) -> MemoryMapped<AffineMatrixAttributes> {
unsafe { MemoryMapped::new(0x0400_0000 + 0x10 * self.background_id()) } unsafe { MemoryMapped::new(0x0400_0000 + 0x10 * self.background_id()) }
} }
} }

View file

@ -2,7 +2,7 @@ use agb_fixnum::Vector2D;
use core::arch::asm; use core::arch::asm;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use crate::display::object::AffineMatrixAttributes; use crate::display::affine::AffineMatrixBackground;
use crate::fixnum::Num; use crate::fixnum::Num;
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -139,40 +139,33 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 {
result result
} }
#[repr(C, packed(4))] /// `rotation` is in revolutions. It is hard to create the rotation, usually
pub struct BgAffineSetData { /// you'll go in from a larger sized type.
pub matrix: AffineMatrixAttributes,
pub position: Vector2D<Num<i32, 8>>,
}
impl Default for BgAffineSetData {
fn default() -> Self {
Self {
matrix: AffineMatrixAttributes::default(),
position: (0, 0).into(),
}
}
}
/// `rotation` is in revolutions.
#[must_use] #[must_use]
pub fn bg_affine_matrix( pub(crate) fn bg_affine_matrix(
bg_center: Vector2D<Num<i32, 8>>, bg_center: Vector2D<Num<i32, 8>>,
display_center: Vector2D<i16>, display_center: Vector2D<i16>,
scale: Vector2D<Num<i16, 8>>, scale: Vector2D<Num<i16, 8>>,
rotation: Num<u16, 8>, rotation: Num<u16, 16>,
) -> BgAffineSetData { ) -> AffineMatrixBackground {
#[repr(C, packed(4))] #[repr(C, packed(4))]
struct Input { struct Input {
bg_center: Vector2D<Num<i32, 8>>, bg_center_x: Num<i32, 8>,
display_center: Vector2D<i16>, bg_center_y: Num<i32, 8>,
scale: Vector2D<Num<i16, 8>>, display_center_x: i16,
rotation: Num<u16, 8>, display_center_y: i16,
scale_x: Num<i16, 8>,
scale_y: Num<i16, 8>,
rotation: Num<u16, 16>,
} }
let input = Input { let input = Input {
bg_center, bg_center_x: bg_center.x,
display_center, bg_center_y: bg_center.y,
scale, display_center_x: display_center.x,
display_center_y: display_center.y,
scale_x: scale.x,
scale_y: scale.y,
rotation, rotation,
}; };
@ -193,56 +186,12 @@ pub fn bg_affine_matrix(
unsafe { output.assume_init() } unsafe { output.assume_init() }
} }
/// `rotation` is in revolutions.
#[must_use]
pub fn obj_affine_matrix(
scale: Vector2D<Num<i16, 8>>,
rotation: Num<u8, 8>,
) -> AffineMatrixAttributes {
#[allow(dead_code)]
#[repr(C, packed(4))]
struct Input {
scale: Vector2D<Num<i16, 8>>,
rotation: u16,
}
let input = Input {
scale,
rotation: u16::from(rotation.to_raw()) << 8,
};
let mut output = MaybeUninit::uninit();
unsafe {
asm!(
"swi {SWI}",
SWI = const { swi_map(0x0F) },
in("r0") &input as *const Input,
in("r1") output.as_mut_ptr(),
in("r2") 1,
in("r3") 2,
);
}
unsafe { output.assume_init() }
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::display::affine::AffineMatrix;
use super::*; use super::*;
#[test_case]
fn affine_obj(_gba: &mut crate::Gba) {
// expect identity matrix
let one: Num<i16, 8> = 1.into();
let aff = obj_affine_matrix((one, one).into(), Num::default());
let (p_a, p_d) = (aff.p_a, aff.p_d);
assert_eq!(p_a, one);
assert_eq!(p_d, one);
}
#[test_case] #[test_case]
fn affine_bg(_gba: &mut crate::Gba) { fn affine_bg(_gba: &mut crate::Gba) {
// expect the identity matrix // expect the identity matrix
@ -250,14 +199,10 @@ mod tests {
(0, 0).into(), (0, 0).into(),
(0i16, 0i16).into(), (0i16, 0i16).into(),
(1i16, 1i16).into(), (1i16, 1i16).into(),
0.into(), Default::default(),
); );
let matrix = aff.matrix; let matrix = aff.to_affine_matrix();
let (p_a, p_b, p_c, p_d) = (matrix.p_a, matrix.p_b, matrix.p_c, matrix.p_d); assert_eq!(matrix, AffineMatrix::identity());
assert_eq!(p_a, 1.into());
assert_eq!(p_b, 0.into());
assert_eq!(p_c, 0.into());
assert_eq!(p_d, 1.into());
} }
} }