mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 16:51:33 +11:00
sprite allocator
This commit is contained in:
parent
beb9abbb7e
commit
1849571344
56
agb/Cargo.lock
generated
56
agb/Cargo.lock
generated
|
@ -24,6 +24,7 @@ dependencies = [
|
||||||
"agb_sound_converter",
|
"agb_sound_converter",
|
||||||
"bare-metal",
|
"bare-metal",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -64,6 +65,17 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -124,6 +136,26 @@ dependencies = [
|
||||||
"adler32",
|
"adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hound"
|
name = "hound"
|
||||||
version = "3.4.0"
|
version = "3.4.0"
|
||||||
|
@ -145,6 +177,12 @@ dependencies = [
|
||||||
"png",
|
"png",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.119"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -195,6 +233,12 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.17.5"
|
version = "0.17.5"
|
||||||
|
@ -270,3 +314,15 @@ name = "unicode-xid"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
|
@ -26,6 +26,7 @@ agb_sound_converter = { version = "0.1.0", path = "../agb-sound-converter" }
|
||||||
agb_macros = { version = "0.1.0", path = "../agb-macros" }
|
agb_macros = { version = "0.1.0", path = "../agb-macros" }
|
||||||
agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" }
|
agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" }
|
||||||
bare-metal = "1.0"
|
bare-metal = "1.0"
|
||||||
|
hashbrown = "0.12.0"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
default-target = "thumbv6m-none-eabi"
|
default-target = "thumbv6m-none-eabi"
|
||||||
|
|
|
@ -49,7 +49,7 @@ pub(crate) struct BlockAllocator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockAllocator {
|
impl BlockAllocator {
|
||||||
pub(super) const unsafe fn new(start: StartEnd) -> Self {
|
pub const unsafe fn new(start: StartEnd) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner_allocator: BumpAllocator::new(start),
|
inner_allocator: BumpAllocator::new(start),
|
||||||
state: Mutex::new(RefCell::new(BlockAllocatorState {
|
state: Mutex::new(RefCell::new(BlockAllocatorState {
|
||||||
|
|
|
@ -6,11 +6,9 @@ use super::SendNonNull;
|
||||||
use crate::interrupt::free;
|
use crate::interrupt::free;
|
||||||
use bare_metal::{CriticalSection, Mutex};
|
use bare_metal::{CriticalSection, Mutex};
|
||||||
|
|
||||||
pub(crate) struct AddrFn(pub fn() -> usize);
|
|
||||||
|
|
||||||
pub(crate) struct StartEnd {
|
pub(crate) struct StartEnd {
|
||||||
pub start: AddrFn,
|
pub start: fn() -> usize,
|
||||||
pub end: AddrFn,
|
pub end: fn() -> usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct BumpAllocator {
|
pub(crate) struct BumpAllocator {
|
||||||
|
@ -34,7 +32,7 @@ impl BumpAllocator {
|
||||||
let ptr = if let Some(c) = *current_ptr {
|
let ptr = if let Some(c) = *current_ptr {
|
||||||
c.as_ptr() as usize
|
c.as_ptr() as usize
|
||||||
} else {
|
} else {
|
||||||
self.start_end.borrow(*cs).start.0()
|
(self.start_end.borrow(*cs).start)()
|
||||||
};
|
};
|
||||||
|
|
||||||
let alignment_bitmask = layout.align() - 1;
|
let alignment_bitmask = layout.align() - 1;
|
||||||
|
@ -45,7 +43,7 @@ impl BumpAllocator {
|
||||||
let resulting_ptr = ptr + amount_to_add;
|
let resulting_ptr = ptr + amount_to_add;
|
||||||
let new_current_ptr = resulting_ptr + layout.size();
|
let new_current_ptr = resulting_ptr + layout.size();
|
||||||
|
|
||||||
if new_current_ptr as usize >= self.start_end.borrow(*cs).end.0() {
|
if new_current_ptr as usize >= (self.start_end.borrow(*cs).end)() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@ use core::alloc::Layout;
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
mod block_allocator;
|
pub(crate) mod block_allocator;
|
||||||
mod bump_allocator;
|
pub(crate) mod bump_allocator;
|
||||||
|
|
||||||
use block_allocator::BlockAllocator;
|
use block_allocator::BlockAllocator;
|
||||||
|
|
||||||
use self::bump_allocator::{AddrFn, StartEnd};
|
use self::bump_allocator::StartEnd;
|
||||||
|
|
||||||
struct SendNonNull<T>(NonNull<T>);
|
struct SendNonNull<T>(NonNull<T>);
|
||||||
unsafe impl<T> Send for SendNonNull<T> {}
|
unsafe impl<T> Send for SendNonNull<T> {}
|
||||||
|
@ -37,8 +37,8 @@ const EWRAM_END: usize = 0x0204_0000;
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL_ALLOC: BlockAllocator = unsafe {
|
static GLOBAL_ALLOC: BlockAllocator = unsafe {
|
||||||
BlockAllocator::new(StartEnd {
|
BlockAllocator::new(StartEnd {
|
||||||
start: AddrFn(get_data_end),
|
start: get_data_end,
|
||||||
end: AddrFn(|| EWRAM_END),
|
end: || EWRAM_END,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use hashbrown::{hash_map::Entry, HashMap};
|
||||||
|
|
||||||
|
use super::palette16::Palette16;
|
||||||
use super::{palette16, Priority, DISPLAY_CONTROL};
|
use super::{palette16, Priority, DISPLAY_CONTROL};
|
||||||
|
use crate::agb_alloc::block_allocator::BlockAllocator;
|
||||||
|
use crate::agb_alloc::bump_allocator::StartEnd;
|
||||||
use crate::bitarray::Bitarray;
|
use crate::bitarray::Bitarray;
|
||||||
use crate::fixnum::Vector2D;
|
use crate::fixnum::Vector2D;
|
||||||
use crate::memory_mapped::MemoryMapped1DArray;
|
use crate::memory_mapped::MemoryMapped1DArray;
|
||||||
|
|
||||||
type AffineLoan<'a> = crate::arena::Loan<'a, 32>;
|
static SPRITE_ALLOCATOR: BlockAllocator = unsafe {
|
||||||
type AffineArena = crate::arena::Arena<32>;
|
BlockAllocator::new(StartEnd {
|
||||||
|
start: || 0x06010000,
|
||||||
|
end: || 0x06010000 + 1024 * 8 * 4,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray<u16, 512> =
|
const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray<u16, 512> =
|
||||||
unsafe { MemoryMapped1DArray::new(0x0700_0000) };
|
unsafe { MemoryMapped1DArray::new(0x0700_0000) };
|
||||||
|
@ -15,523 +24,92 @@ const PALETTE_SPRITE: MemoryMapped1DArray<u16, 256> =
|
||||||
const TILE_SPRITE: MemoryMapped1DArray<u32, { 1024 * 8 }> =
|
const TILE_SPRITE: MemoryMapped1DArray<u32, { 1024 * 8 }> =
|
||||||
unsafe { MemoryMapped1DArray::new(0x06010000) };
|
unsafe { MemoryMapped1DArray::new(0x06010000) };
|
||||||
|
|
||||||
/// Handles distributing objects and matrices along with operations that effect all objects.
|
pub struct Sprite {
|
||||||
/// You can create an instance of this using the Gba struct.
|
palette: &'static Palette16,
|
||||||
///
|
data: &'static [u8],
|
||||||
/// This handles distribution of sprites, ensuring that object ids are not reused and are
|
|
||||||
/// returned to the pool once you're done handling them.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![no_std]
|
|
||||||
/// # #![no_main]
|
|
||||||
/// #
|
|
||||||
/// # use agb::Gba;
|
|
||||||
/// #
|
|
||||||
/// # #[agb::entry]
|
|
||||||
/// # fn main() -> ! {
|
|
||||||
/// let mut gba = Gba::new();
|
|
||||||
/// let mut object = gba.display.object.get();
|
|
||||||
/// #
|
|
||||||
/// # loop {}
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct ObjectControl {
|
|
||||||
objects: RefCell<Bitarray<4>>,
|
|
||||||
affines: AffineArena,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ObjectLoan<'a> {
|
struct SpriteBorrow<'a> {
|
||||||
index: u8,
|
id: SpriteId,
|
||||||
objects: &'a RefCell<Bitarray<4>>,
|
controller: &'a RefCell<SpriteControllerInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The standard object, without rotation.
|
struct Storage {
|
||||||
///
|
location: u16,
|
||||||
/// You should create this from an instance of ObjectControl created using the Gba struct. Note that
|
count: u16,
|
||||||
/// no changes made to this will be visible until `commit()` is called. You should call `commit()` during
|
|
||||||
/// vblank to ensure that you get no visual artifacts.
|
|
||||||
///
|
|
||||||
/// This struct implements a sort of builder pattern, allowing you to chain settings together.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # #![no_std]
|
|
||||||
/// # #![no_main]
|
|
||||||
/// #
|
|
||||||
/// # use agb::Gba;
|
|
||||||
/// use agb::display::object::Size;
|
|
||||||
///
|
|
||||||
/// # #[agb::entry]
|
|
||||||
/// # fn main() -> ! {
|
|
||||||
/// # let mut gba = Gba::new();
|
|
||||||
/// let mut object = gba.display.object.get();
|
|
||||||
///
|
|
||||||
/// let mut my_new_object = object.get_object_standard();
|
|
||||||
/// my_new_object.set_x(50)
|
|
||||||
/// .set_y(50)
|
|
||||||
/// .set_sprite_Size(Size::S8x8)
|
|
||||||
/// .set_tile_id(7)
|
|
||||||
/// .show();
|
|
||||||
///
|
|
||||||
/// // some time later in vblank
|
|
||||||
/// my_new_object.commit();
|
|
||||||
/// # loop {}
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct ObjectStandard<'a> {
|
|
||||||
attributes: ObjectAttribute,
|
|
||||||
loan: ObjectLoan<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The affine object, with potential for using a transformation matrix to alter
|
pub struct Object<'a> {
|
||||||
/// how the sprite is rendered to screen.
|
sprite: SpriteBorrow<'a>,
|
||||||
pub struct ObjectAffine<'a> {
|
|
||||||
attributes: ObjectAttribute,
|
|
||||||
loan: ObjectLoan<'a>,
|
|
||||||
aff_loan: Option<AffineLoan<'a>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refers to an affine matrix in the OAM. Includes both an index and the
|
struct SpriteControllerInner {
|
||||||
/// components of the affine matrix.
|
palette: HashMap<PaletteId, Storage>,
|
||||||
pub struct AffineMatrix<'a> {
|
sprite: HashMap<SpriteId, Storage>,
|
||||||
pub attributes: AffineMatrixAttributes,
|
|
||||||
loan: AffineLoan<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The components of the affine matrix. The components are fixed point 8:8.
|
pub struct SpriteController {
|
||||||
/// TODO is a type that can handle fixed point arithmetic.
|
inner: RefCell<SpriteControllerInner>,
|
||||||
pub struct AffineMatrixAttributes {
|
|
||||||
pub p_a: i16,
|
|
||||||
pub p_b: i16,
|
|
||||||
pub p_c: i16,
|
|
||||||
pub p_d: i16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub struct ObjectController {}
|
||||||
enum Mode {
|
|
||||||
Normal = 0,
|
|
||||||
Affine = 1,
|
|
||||||
Hidden = 2,
|
|
||||||
AffineDouble = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
/// The Sprite Id is a thin wrapper around the pointer to the sprite in
|
||||||
pub enum Size {
|
/// rom and is therefore a unique identifier to a sprite
|
||||||
// stored as attr0 attr1
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
S8x8 = 0b00_00,
|
struct SpriteId(usize);
|
||||||
S16x16 = 0b00_01,
|
|
||||||
S32x32 = 0b00_10,
|
|
||||||
S64x64 = 0b00_11,
|
|
||||||
|
|
||||||
S16x8 = 0b01_00,
|
/// The palette id is a thin wrapper around the pointer to the palette in rom
|
||||||
S32x8 = 0b01_01,
|
/// and is therefore a unique reference to a palette
|
||||||
S32x16 = 0b01_10,
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
S64x32 = 0b01_11,
|
struct PaletteId(usize);
|
||||||
|
|
||||||
S8x16 = 0b10_00,
|
impl Sprite {
|
||||||
S8x32 = 0b10_01,
|
fn get_id(&'static self) -> SpriteId {
|
||||||
S16x32 = 0b10_10,
|
SpriteId(self as *const _ as usize)
|
||||||
S32x64 = 0b10_11,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectStandard<'_> {
|
|
||||||
/// Commits the object to OAM such that the updated version is displayed on
|
|
||||||
/// screen. Recommend to do this during VBlank.
|
|
||||||
pub fn commit(&self) {
|
|
||||||
unsafe { self.attributes.commit(self.loan.index) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the x coordinate of the sprite on screen.
|
|
||||||
pub fn set_x(&mut self, x: u16) -> &mut Self {
|
|
||||||
self.attributes.set_x(x);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the y coordinate of the sprite on screen.
|
|
||||||
pub fn set_y(&mut self, y: u16) -> &mut Self {
|
|
||||||
self.attributes.set_y(y);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the index of the tile to use as the sprite. Potentially a temporary function.
|
|
||||||
pub fn set_tile_id(&mut self, id: u16) -> &mut Self {
|
|
||||||
self.attributes.set_tile_id(id);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether the sprite is horizontally mirrored or not.
|
|
||||||
pub fn set_hflip(&mut self, hflip: bool) -> &mut Self {
|
|
||||||
self.attributes.set_hflip(hflip);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the sprite size, will read tiles in x major order to construct this.
|
|
||||||
pub fn set_sprite_size(&mut self, size: Size) -> &mut Self {
|
|
||||||
self.attributes.set_size(size);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the object on screen.
|
|
||||||
pub fn show(&mut self) -> &mut Self {
|
|
||||||
self.attributes.set_mode(Mode::Normal);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hide the object and do not render.
|
|
||||||
pub fn hide(&mut self) -> &mut Self {
|
|
||||||
self.attributes.set_mode(Mode::Hidden);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the palette to use for this sprite
|
|
||||||
pub fn set_palette(&mut self, palette: u16) -> &mut Self {
|
|
||||||
self.attributes.set_palette(palette);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the x and y position of the object, performing casts as nessesary
|
|
||||||
/// to fit within the bits allocated for this purpose.
|
|
||||||
pub fn set_position(&mut self, position: Vector2D<i32>) -> &mut Self {
|
|
||||||
let x = position.x as u16;
|
|
||||||
let y = position.y as u16;
|
|
||||||
self.attributes.set_x(x);
|
|
||||||
self.attributes.set_y(y);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the priority (used for z ordering) of this sprite
|
|
||||||
pub fn set_priority(&mut self, p: Priority) -> &mut Self {
|
|
||||||
self.attributes.set_priority(p);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ObjectAffine<'a> {
|
impl SpriteController {
|
||||||
/// Commits the object to OAM such that the updated version is displayed on
|
fn get_sprite(&self, sprite: &'static Sprite) -> Option<SpriteBorrow> {
|
||||||
/// screen. Recommend to do this during VBlank.
|
let inner = self.inner.borrow_mut();
|
||||||
pub fn commit(&self) {
|
let id = sprite.get_id();
|
||||||
unsafe { self.attributes.commit(self.loan.index) }
|
if let Some(storage) = inner.sprite.get_mut(&id) {
|
||||||
|
storage.count += 1;
|
||||||
|
Some(SpriteBorrow {
|
||||||
|
id,
|
||||||
|
controller: &self.inner,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// allocate a new sprite
|
||||||
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the x coordinate of the sprite on screen.
|
|
||||||
pub fn set_x(&mut self, x: u16) {
|
|
||||||
self.attributes.set_x(x)
|
|
||||||
}
|
|
||||||
/// Sets the y coordinate of the sprite on screen.
|
|
||||||
pub fn set_y(&mut self, y: u16) {
|
|
||||||
self.attributes.set_y(y)
|
|
||||||
}
|
|
||||||
/// Sets the index of the tile to use as the sprite. Potentially a temporary function.
|
|
||||||
pub fn set_tile_id(&mut self, id: u16) {
|
|
||||||
self.attributes.set_tile_id(id)
|
|
||||||
}
|
|
||||||
/// Sets the sprite size, will read tiles in x major order to construct this.
|
|
||||||
pub fn set_sprite_size(&mut self, size: Size) {
|
|
||||||
self.attributes.set_size(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show the object on screen. Panics if affine matrix has not been set.
|
|
||||||
pub fn show(&mut self) {
|
|
||||||
if self.aff_loan.is_none() {
|
|
||||||
panic!("affine matrix should be set")
|
|
||||||
}
|
|
||||||
self.attributes.set_mode(Mode::Affine)
|
|
||||||
}
|
|
||||||
/// Hide the object and do not render the sprite.
|
|
||||||
pub fn hide(&mut self) {
|
|
||||||
self.attributes.set_mode(Mode::Hidden)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the affine matrix to use. Changing the affine matrix will change
|
|
||||||
/// how the sprite is rendered.
|
|
||||||
pub fn set_affine_mat(&mut self, aff: &AffineMatrix<'a>) {
|
|
||||||
self.attributes.set_affine(aff.loan.my_index);
|
|
||||||
self.aff_loan = Some(aff.loan.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the x and y position of the object, performing casts as nessesary
|
|
||||||
/// to fit within the bits allocated for this purpose.
|
|
||||||
pub fn set_position(&mut self, position: Vector2D<i32>) {
|
|
||||||
let x = position.x as u16;
|
|
||||||
let y = position.y as u16;
|
|
||||||
self.attributes.set_x(x);
|
|
||||||
self.attributes.set_y(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_priority(&mut self, p: Priority) {
|
|
||||||
self.attributes.set_priority(p)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_bits(current: u16, value: u16, length: u16, shift: u16) -> u16 {
|
impl<'a> Drop for SpriteBorrow<'a> {
|
||||||
let mask: u16 = (1 << length) - 1;
|
|
||||||
(current & !(mask << shift)) | ((value & mask) << shift)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ObjectLoan<'_> {
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let attributes = ObjectAttribute::new();
|
let inner = self.controller.borrow_mut();
|
||||||
unsafe {
|
let entry = inner
|
||||||
attributes.commit(self.index);
|
.sprite
|
||||||
|
.entry(self.id)
|
||||||
|
.and_replace_entry_with(|_, mut storage| {
|
||||||
|
storage.count -= 1;
|
||||||
|
if storage.count == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(storage)
|
||||||
}
|
}
|
||||||
let mut objs = self.objects.borrow_mut();
|
});
|
||||||
objs.set(self.index as usize, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ObjectAttribute {
|
match entry {
|
||||||
a0: u16,
|
Entry::Vacant(_) => {
|
||||||
a1: u16,
|
// free the underlying resource.
|
||||||
a2: u16,
|
// palette might be unused too.
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectAttribute {
|
|
||||||
unsafe fn commit(&self, index: u8) {
|
|
||||||
OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4, self.a0);
|
|
||||||
OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4 + 1, self.a1);
|
|
||||||
OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4 + 2, self.a2);
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
fn set_hflip(&mut self, hflip: bool) {
|
|
||||||
self.a1 = set_bits(self.a1, hflip as u16, 1, 0xC);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_size(&mut self, size: Size) {
|
|
||||||
let a1 = size as u16 & 0b11;
|
|
||||||
let a0 = (size as u16 >> 2) & 0b11;
|
|
||||||
|
|
||||||
self.a0 = set_bits(self.a0, a0, 2, 0xE);
|
|
||||||
self.a1 = set_bits(self.a1, a1, 2, 0xE);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_palette(&mut self, palette: u16) {
|
|
||||||
self.a2 = set_bits(self.a2, palette, 4, 0xC);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_x(&mut self, x: u16) {
|
|
||||||
self.a1 = set_bits(self.a1, x, 9, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_y(&mut self, y: u16) {
|
|
||||||
self.a0 = set_bits(self.a0, y, 8, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_tile_id(&mut self, id: u16) {
|
|
||||||
self.a2 = set_bits(self.a2, id, 10, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_mode(&mut self, mode: Mode) {
|
|
||||||
self.a0 = set_bits(self.a0, mode as u16, 2, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_affine(&mut self, aff_id: u8) {
|
|
||||||
self.a1 = set_bits(self.a1, aff_id as u16, 5, 0x9);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_priority(&mut self, p: Priority) {
|
|
||||||
self.a2 = set_bits(self.a2, p as u16, 2, 0x0A);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AffineMatrix<'_> {
|
|
||||||
/// Commits matrix to OAM, will cause any objects using this matrix to be updated.
|
|
||||||
pub fn commit(&self) {
|
|
||||||
unsafe { self.attributes.commit(self.loan.my_index) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AffineMatrixAttributes {
|
|
||||||
#[allow(clippy::identity_op)]
|
|
||||||
unsafe fn commit(&self, index: u8) {
|
|
||||||
let index = index as usize * 4;
|
|
||||||
OBJECT_ATTRIBUTE_MEMORY.set((index + 0) * 4 + 3, self.p_a as u16);
|
|
||||||
OBJECT_ATTRIBUTE_MEMORY.set((index + 1) * 4 + 3, self.p_b as u16);
|
|
||||||
OBJECT_ATTRIBUTE_MEMORY.set((index + 2) * 4 + 3, self.p_c as u16);
|
|
||||||
OBJECT_ATTRIBUTE_MEMORY.set((index + 3) * 4 + 3, self.p_d as u16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectAttribute {
|
|
||||||
fn new() -> Self {
|
|
||||||
let mut o = ObjectAttribute {
|
|
||||||
a0: 0,
|
|
||||||
a1: 0,
|
|
||||||
a2: 0,
|
|
||||||
};
|
|
||||||
o.set_mode(Mode::Hidden);
|
|
||||||
o
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectControl {
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
let o = ObjectAttribute::new();
|
|
||||||
for index in 0..128 {
|
|
||||||
unsafe { o.commit(index) };
|
|
||||||
}
|
|
||||||
ObjectControl {
|
|
||||||
objects: RefCell::new(Bitarray::new()),
|
|
||||||
affines: AffineArena::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_sprite_tilemap_at_idx(&self, idx: usize, tiles: &[u32]) {
|
|
||||||
for (index, &tile) in tiles.iter().enumerate() {
|
|
||||||
self.set_sprite_tilemap_entry(index + idx, tile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enable objects on the GBA.
|
|
||||||
pub fn enable(&mut self) {
|
|
||||||
let disp = DISPLAY_CONTROL.get();
|
|
||||||
let disp = disp | (1 << 0x0C);
|
|
||||||
DISPLAY_CONTROL.set(disp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disable objects, objects won't be rendered.
|
|
||||||
pub fn disable(&mut self) {
|
|
||||||
let disp = DISPLAY_CONTROL.get();
|
|
||||||
let disp = disp & !(1 << 0x0C);
|
|
||||||
DISPLAY_CONTROL.set(disp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_unused_object_index(&self) -> u8 {
|
|
||||||
let mut objects = self.objects.borrow_mut();
|
|
||||||
for index in 0..128 {
|
|
||||||
if !objects.get(index).unwrap() {
|
|
||||||
objects.set(index, true);
|
|
||||||
return index as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic!("object id must be less than 128");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an unused standard object. Panics if more than 128 objects are
|
|
||||||
/// obtained.
|
|
||||||
pub fn get_object_standard(&self) -> ObjectStandard {
|
|
||||||
let id = self.get_unused_object_index();
|
|
||||||
ObjectStandard {
|
|
||||||
attributes: ObjectAttribute::new(),
|
|
||||||
loan: ObjectLoan {
|
|
||||||
objects: &self.objects,
|
|
||||||
index: id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an unused affine object. Panics if more than 128 objects are
|
|
||||||
/// obtained.
|
|
||||||
pub fn get_object_affine(&self) -> ObjectAffine {
|
|
||||||
let id = self.get_unused_object_index();
|
|
||||||
ObjectAffine {
|
|
||||||
attributes: ObjectAttribute::new(),
|
|
||||||
loan: ObjectLoan {
|
|
||||||
objects: &self.objects,
|
|
||||||
index: id,
|
|
||||||
},
|
|
||||||
aff_loan: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an unused affine matrix. Panics if more than 32 affine matricies are
|
|
||||||
/// obtained.
|
|
||||||
pub fn get_affine(&self) -> AffineMatrix {
|
|
||||||
AffineMatrix {
|
|
||||||
attributes: AffineMatrixAttributes {
|
|
||||||
p_a: 0,
|
|
||||||
p_b: 0,
|
|
||||||
p_c: 0,
|
|
||||||
p_d: 0,
|
|
||||||
},
|
|
||||||
loan: self
|
|
||||||
.affines
|
|
||||||
.get_next_free()
|
|
||||||
.expect("there are no affines avaliable"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
impl ObjectController {}
|
||||||
mod tests {
|
|
||||||
#[test_case]
|
|
||||||
fn get_and_release_object(gba: &mut crate::Gba) {
|
|
||||||
let objs = gba.display.object.get();
|
|
||||||
|
|
||||||
let _o1 = {
|
|
||||||
let o0 = objs.get_object_standard();
|
|
||||||
let o1 = objs.get_object_standard();
|
|
||||||
assert_eq!(o0.loan.index, 0);
|
|
||||||
assert_eq!(o1.loan.index, 1);
|
|
||||||
o1
|
|
||||||
};
|
|
||||||
|
|
||||||
let o0 = objs.get_object_standard();
|
|
||||||
assert_eq!(o0.loan.index, 0);
|
|
||||||
let o2 = objs.get_object_affine();
|
|
||||||
assert_eq!(o2.loan.index, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn get_and_release_affine(gba: &mut crate::Gba) {
|
|
||||||
let objs = gba.display.object.get();
|
|
||||||
|
|
||||||
let _a1 = {
|
|
||||||
let a0 = objs.get_affine();
|
|
||||||
let a1 = objs.get_affine();
|
|
||||||
assert_eq!(a0.loan.my_index, 0);
|
|
||||||
assert_eq!(a1.loan.my_index, 1);
|
|
||||||
a1
|
|
||||||
};
|
|
||||||
|
|
||||||
let a0 = objs.get_affine();
|
|
||||||
assert_eq!(a0.loan.my_index, 0);
|
|
||||||
let a2 = objs.get_affine();
|
|
||||||
assert_eq!(a2.loan.my_index, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue