mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 16:21:33 +11:00
Merge pull request #324 from corwinkuiper/affine-matrix-background
Use the new Affine Matrix stuff in the affine background
This commit is contained in:
commit
e30e0b76e2
|
@ -133,8 +133,8 @@ fixed_width_signed_integer_impl!(i16);
|
|||
fixed_width_signed_integer_impl!(i32);
|
||||
|
||||
/// A fixed point number represented using `I` with `N` bits of fractional precision
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct Num<I: FixedWidthUnsignedInteger, const N: usize>(I);
|
||||
|
||||
/// 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
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Vector2D<T: Number> {
|
||||
/// The x coordinate
|
||||
pub x: T,
|
||||
|
@ -689,6 +688,17 @@ impl<I: FixedWidthUnsignedInteger, const N: usize> Vector2D<Num<I, N>> {
|
|||
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>> {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use agb::{
|
||||
display::{
|
||||
affine::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<u16, 8> = num!(0.);
|
||||
let rotation_increase = num!(1.);
|
||||
let mut rotation = num!(0.);
|
||||
let rotation_increase: Num<i32, 16> = 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);
|
||||
|
|
|
@ -7,19 +7,82 @@
|
|||
//! Affine matricies are used in two places on the GBA, for affine backgrounds
|
||||
//! and for affine objects.
|
||||
//!
|
||||
//! # Linear Algebra basics
|
||||
//! As a matrix, they can be manipulated using linear algebra, although you
|
||||
//! shouldn't need to know linear algebra to use this apart from a few things
|
||||
//! # Linear Algebra
|
||||
//! As a matrix, they can be manipulated using linear algebra. The short version
|
||||
//! 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
|
||||
//! transformation `A` performed on `B`, or alternatively `C` is transformation
|
||||
//! `B` followed by transformation `A`.
|
||||
//! One quick thing to point out at the start as it will become very relevant is
|
||||
//! that matrix-matrix multiplication is not commutative, meaning swapping the
|
||||
//! 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
|
||||
//! order changes the result, or `A * B ≢ B * A`.
|
||||
//! ## Normal (wrong on GBA!) transformation matricies
|
||||
//!
|
||||
//! 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::{
|
||||
convert::{TryFrom, TryInto},
|
||||
convert::TryFrom,
|
||||
ops::{Mul, MulAssign},
|
||||
};
|
||||
|
||||
|
@ -62,7 +125,7 @@ impl AffineMatrix {
|
|||
#[must_use]
|
||||
/// Generates the matrix that represents a rotation
|
||||
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 sin = angle.sin().change_base();
|
||||
|
||||
|
@ -71,8 +134,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,27 +153,27 @@ 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<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
|
||||
/// backgrounds.
|
||||
pub fn try_to_background(&self) -> Result<AffineMatrixBackground, OverflowError> {
|
||||
Ok(AffineMatrixBackground {
|
||||
a: self.a.to_raw().try_into().map_err(|_| OverflowError(()))?,
|
||||
b: self.a.to_raw().try_into().map_err(|_| OverflowError(()))?,
|
||||
c: self.a.to_raw().try_into().map_err(|_| OverflowError(()))?,
|
||||
d: self.a.to_raw().try_into().map_err(|_| OverflowError(()))?,
|
||||
x: self.a.to_raw(),
|
||||
y: self.a.to_raw(),
|
||||
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(()))?,
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -119,28 +182,75 @@ impl AffineMatrix {
|
|||
/// wrapping any value which is too large to be represented there.
|
||||
pub fn to_background_wrapping(&self) -> AffineMatrixBackground {
|
||||
AffineMatrixBackground {
|
||||
a: self.a.to_raw() as i16,
|
||||
b: self.a.to_raw() as i16,
|
||||
c: self.a.to_raw() as i16,
|
||||
d: self.a.to_raw() as i16,
|
||||
x: self.a.to_raw(),
|
||||
y: self.a.to_raw(),
|
||||
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),
|
||||
x: self.x,
|
||||
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)]
|
||||
#[repr(C, packed(4))]
|
||||
/// An affine matrix that can be used in affine backgrounds
|
||||
pub struct AffineMatrixBackground {
|
||||
// Internally these can be thought of as Num<i16, 8>
|
||||
a: i16,
|
||||
b: i16,
|
||||
c: i16,
|
||||
d: i16,
|
||||
// These are Num<i32, 8>
|
||||
x: i32,
|
||||
y: i32,
|
||||
a: Num<i16, 8>,
|
||||
b: Num<i16, 8>,
|
||||
c: Num<i16, 8>,
|
||||
d: Num<i16, 8>,
|
||||
x: Num<i32, 8>,
|
||||
y: Num<i32, 8>,
|
||||
}
|
||||
|
||||
impl Default for AffineMatrixBackground {
|
||||
fn default() -> Self {
|
||||
AffineMatrix::identity().to_background_wrapping()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<AffineMatrix> for AffineMatrixBackground {
|
||||
|
@ -157,14 +267,49 @@ impl AffineMatrixBackground {
|
|||
/// calculations.
|
||||
pub fn to_affine_matrix(&self) -> AffineMatrix {
|
||||
AffineMatrix {
|
||||
a: Num::from_raw(self.a.into()),
|
||||
b: Num::from_raw(self.b.into()),
|
||||
c: Num::from_raw(self.c.into()),
|
||||
d: Num::from_raw(self.d.into()),
|
||||
x: Num::from_raw(self.x),
|
||||
y: Num::from_raw(self.y),
|
||||
a: self.a.change_base(),
|
||||
b: self.b.change_base(),
|
||||
c: self.c.change_base(),
|
||||
d: self.d.change_base(),
|
||||
x: self.x,
|
||||
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 {
|
||||
|
@ -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 {
|
||||
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;
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
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,
|
||||
c: self.c * rhs.a + self.d * rhs.c,
|
||||
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 {
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = *self * rhs;
|
||||
|
|
|
@ -19,7 +19,7 @@ use super::{Priority, DISPLAY_CONTROL};
|
|||
use crate::agb_alloc::block_allocator::BlockAllocator;
|
||||
use crate::agb_alloc::bump_allocator::StartEnd;
|
||||
use crate::dma;
|
||||
use crate::fixnum::{Num, Vector2D};
|
||||
use crate::fixnum::Vector2D;
|
||||
use crate::hash_map::HashMap;
|
||||
|
||||
use attributes::*;
|
||||
|
@ -1236,42 +1236,6 @@ enum ColourMode {
|
|||
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.
|
||||
#[allow(dead_code)]
|
||||
mod attributes {
|
||||
|
|
|
@ -2,9 +2,10 @@ use core::cell::RefCell;
|
|||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
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::fixnum::{Num, Vector2D};
|
||||
use crate::fixnum::Vector2D;
|
||||
use crate::memory_mapped::MemoryMapped;
|
||||
|
||||
use super::{
|
||||
|
@ -12,7 +13,6 @@ use super::{
|
|||
RegularBackgroundSize, Tile, TileFormat, TileIndex, TileSet, TileSetting, VRamManager,
|
||||
};
|
||||
|
||||
use crate::syscall::BgAffineSetData;
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
pub trait TiledMapTypes: private::Sealed {
|
||||
|
@ -247,7 +247,7 @@ pub struct AffineMap {
|
|||
|
||||
scroll: Vector2D<i16>,
|
||||
|
||||
transform: BgAffineSetData,
|
||||
transform: AffineMatrixBackground,
|
||||
|
||||
tiles: Vec<u8>,
|
||||
tiles_dirty: bool,
|
||||
|
@ -259,7 +259,7 @@ impl TiledMapTypes for AffineMap {
|
|||
|
||||
impl TiledMapPrivate for AffineMap {
|
||||
type TileType = u8;
|
||||
type AffineMatrix = AffineMatrixAttributes;
|
||||
type AffineMatrix = AffineMatrixBackground;
|
||||
|
||||
fn tiles_mut(&mut self) -> &mut [Self::TileType] {
|
||||
&mut self.tiles
|
||||
|
@ -280,10 +280,7 @@ impl TiledMapPrivate for AffineMap {
|
|||
self.size
|
||||
}
|
||||
fn update_bg_registers(&self) {
|
||||
let register_pos = self.transform.position;
|
||||
self.bg_x().set(register_pos.x);
|
||||
self.bg_y().set(register_pos.y);
|
||||
self.bg_affine_matrix().set(self.transform.matrix);
|
||||
self.bg_affine_matrix().set(self.transform);
|
||||
}
|
||||
fn scroll_pos(&self) -> Vector2D<i16> {
|
||||
self.scroll
|
||||
|
@ -347,25 +344,11 @@ impl AffineMap {
|
|||
*self.tiles_dirty() = true;
|
||||
}
|
||||
|
||||
pub fn set_transform(
|
||||
&mut self,
|
||||
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);
|
||||
pub fn set_transform(&mut self, transformation: impl Into<AffineMatrixBackground>) {
|
||||
self.transform = transformation.into();
|
||||
}
|
||||
|
||||
fn bg_x(&self) -> MemoryMapped<Num<i32, 8>> {
|
||||
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> {
|
||||
fn bg_affine_matrix(&self) -> MemoryMapped<AffineMatrixBackground> {
|
||||
unsafe { MemoryMapped::new(0x0400_0000 + 0x10 * self.background_id()) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use agb_fixnum::Vector2D;
|
|||
use core::arch::asm;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use crate::display::object::AffineMatrixAttributes;
|
||||
use crate::display::affine::AffineMatrixBackground;
|
||||
use crate::fixnum::Num;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -139,40 +139,33 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 {
|
|||
result
|
||||
}
|
||||
|
||||
#[repr(C, packed(4))]
|
||||
pub struct BgAffineSetData {
|
||||
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.
|
||||
/// `rotation` is in revolutions. It is hard to create the rotation, usually
|
||||
/// you'll go in from a larger sized type.
|
||||
#[must_use]
|
||||
pub fn bg_affine_matrix(
|
||||
pub(crate) fn bg_affine_matrix(
|
||||
bg_center: Vector2D<Num<i32, 8>>,
|
||||
display_center: Vector2D<i16>,
|
||||
scale: Vector2D<Num<i16, 8>>,
|
||||
rotation: Num<u16, 8>,
|
||||
) -> BgAffineSetData {
|
||||
rotation: Num<u16, 16>,
|
||||
) -> AffineMatrixBackground {
|
||||
#[repr(C, packed(4))]
|
||||
struct Input {
|
||||
bg_center: Vector2D<Num<i32, 8>>,
|
||||
display_center: Vector2D<i16>,
|
||||
scale: Vector2D<Num<i16, 8>>,
|
||||
rotation: Num<u16, 8>,
|
||||
bg_center_x: Num<i32, 8>,
|
||||
bg_center_y: Num<i32, 8>,
|
||||
display_center_x: i16,
|
||||
display_center_y: i16,
|
||||
scale_x: Num<i16, 8>,
|
||||
scale_y: Num<i16, 8>,
|
||||
rotation: Num<u16, 16>,
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
@ -193,56 +186,12 @@ pub fn bg_affine_matrix(
|
|||
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)]
|
||||
mod tests {
|
||||
use crate::display::affine::AffineMatrix;
|
||||
|
||||
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]
|
||||
fn affine_bg(_gba: &mut crate::Gba) {
|
||||
// expect the identity matrix
|
||||
|
@ -250,14 +199,10 @@ mod tests {
|
|||
(0, 0).into(),
|
||||
(0i16, 0i16).into(),
|
||||
(1i16, 1i16).into(),
|
||||
0.into(),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let matrix = aff.matrix;
|
||||
let (p_a, p_b, p_c, p_d) = (matrix.p_a, matrix.p_b, matrix.p_c, matrix.p_d);
|
||||
assert_eq!(p_a, 1.into());
|
||||
assert_eq!(p_b, 0.into());
|
||||
assert_eq!(p_c, 0.into());
|
||||
assert_eq!(p_d, 1.into());
|
||||
let matrix = aff.to_affine_matrix();
|
||||
assert_eq!(matrix, AffineMatrix::identity());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue