mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
Add rudimentary affine background layer support.
This commit is contained in:
parent
0a991af2e9
commit
ee576597c2
|
@ -566,6 +566,7 @@ 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)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct Vector2D<T: Number> {
|
pub struct Vector2D<T: Number> {
|
||||||
/// The x coordinate
|
/// The x coordinate
|
||||||
pub x: T,
|
pub x: T,
|
||||||
|
@ -1211,6 +1212,15 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_only_frac_bits() {
|
||||||
|
let quarter: Num<u8, 8> = num!(0.25);
|
||||||
|
let neg_quarter: Num<i16, 15> = num!(-0.25);
|
||||||
|
|
||||||
|
assert_eq!(quarter + quarter, num!(0.5));
|
||||||
|
assert_eq!(neg_quarter + neg_quarter, num!(-0.5));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vector_multiplication_and_division() {
|
fn test_vector_multiplication_and_division() {
|
||||||
let a: Vector2D<i32> = (1, 2).into();
|
let a: Vector2D<i32> = (1, 2).into();
|
||||||
|
|
|
@ -132,7 +132,7 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
let optimised_results = optimiser.optimise_palettes();
|
let optimised_results = optimiser.optimise_palettes();
|
||||||
|
|
||||||
let (palette_data, tile_data, assignments) = palete_tile_data(&optimised_results, &images);
|
let (palette_data, tile_data, assignments) = palette_tile_data(&optimised_results, &images);
|
||||||
|
|
||||||
let palette_data = palette_data.iter().map(|colours| {
|
let palette_data = palette_data.iter().map(|colours| {
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -273,7 +273,7 @@ fn add_to_optimiser(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn palete_tile_data(
|
fn palette_tile_data(
|
||||||
optimiser: &Palette16OptimisationResults,
|
optimiser: &Palette16OptimisationResults,
|
||||||
images: &[Image],
|
images: &[Image],
|
||||||
) -> (Vec<Vec<u16>>, Vec<u8>, Vec<usize>) {
|
) -> (Vec<Vec<u16>>, Vec<u8>, Vec<usize>) {
|
||||||
|
@ -293,9 +293,33 @@ fn palete_tile_data(
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut tile_data = Vec::new();
|
let mut tile_data = Vec::new();
|
||||||
|
let tile_size = TileSize::Tile8;
|
||||||
|
|
||||||
for image in images {
|
for image in images {
|
||||||
let tile_size = 8;
|
add_image_to_tile_data(&mut tile_data, image, tile_size, &optimiser)
|
||||||
|
}
|
||||||
|
|
||||||
|
let tile_data = collapse_to_4bpp(&tile_data);
|
||||||
|
|
||||||
|
let assignments = optimiser.assignments.clone();
|
||||||
|
|
||||||
|
(palette_data, tile_data, assignments)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collapse_to_4bpp(tile_data: &[u8]) -> Vec<u8> {
|
||||||
|
tile_data
|
||||||
|
.chunks(2)
|
||||||
|
.map(|chunk| chunk[0] | (chunk[1] << 4))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_image_to_tile_data(
|
||||||
|
tile_data: &mut Vec<u8>,
|
||||||
|
image: &Image,
|
||||||
|
tile_size: TileSize,
|
||||||
|
optimiser: &&Palette16OptimisationResults,
|
||||||
|
) {
|
||||||
|
let tile_size = tile_size.to_size();
|
||||||
let tiles_x = image.width / tile_size;
|
let tiles_x = image.width / tile_size;
|
||||||
let tiles_y = image.height / tile_size;
|
let tiles_y = image.height / tile_size;
|
||||||
|
|
||||||
|
@ -309,9 +333,8 @@ fn palete_tile_data(
|
||||||
for j in inner_y * 8..inner_y * 8 + 8 {
|
for j in inner_y * 8..inner_y * 8 + 8 {
|
||||||
for i in inner_x * 8..inner_x * 8 + 8 {
|
for i in inner_x * 8..inner_x * 8 + 8 {
|
||||||
let colour = image.colour(x * tile_size + i, y * tile_size + j);
|
let colour = image.colour(x * tile_size + i, y * tile_size + j);
|
||||||
tile_data.push(
|
tile_data
|
||||||
palette.colour_index(colour, optimiser.transparent_colour),
|
.push(palette.colour_index(colour, optimiser.transparent_colour));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,16 +343,6 @@ fn palete_tile_data(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tile_data = tile_data
|
|
||||||
.chunks(2)
|
|
||||||
.map(|chunk| chunk[0] | (chunk[1] << 4))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let assignments = optimiser.assignments.clone();
|
|
||||||
|
|
||||||
(palette_data, tile_data, assignments)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flatten_group(expr: &Expr) -> &Expr {
|
fn flatten_group(expr: &Expr) -> &Expr {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Group(group) => &group.expr,
|
Expr::Group(group) => &group.expr,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::palette16::Palette16OptimisationResults;
|
use crate::palette16::Palette16OptimisationResults;
|
||||||
use crate::TileSize;
|
use crate::{add_image_to_tile_data, collapse_to_4bpp, TileSize};
|
||||||
use crate::{image_loader::Image, ByteString};
|
use crate::{image_loader::Image, ByteString};
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
@ -34,36 +34,11 @@ pub(crate) fn generate_code(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let tile_size = tile_size.to_size();
|
let mut tile_data = Vec::new();
|
||||||
|
|
||||||
let tiles_x = image.width / tile_size;
|
add_image_to_tile_data(&mut tile_data, image, tile_size, &results);
|
||||||
let tiles_y = image.height / tile_size;
|
|
||||||
|
|
||||||
let mut tile_data = vec![];
|
let tile_data = collapse_to_4bpp(&tile_data);
|
||||||
|
|
||||||
for y in 0..tiles_y {
|
|
||||||
for x in 0..tiles_x {
|
|
||||||
let palette_index = results.assignments[y * tiles_x + x];
|
|
||||||
let palette = &results.optimised_palettes[palette_index];
|
|
||||||
|
|
||||||
for inner_y in 0..tile_size / 8 {
|
|
||||||
for inner_x in 0..tile_size / 8 {
|
|
||||||
for j in inner_y * 8..inner_y * 8 + 8 {
|
|
||||||
for i in inner_x * 8..inner_x * 8 + 8 {
|
|
||||||
let colour = image.colour(x * tile_size + i, y * tile_size + j);
|
|
||||||
tile_data
|
|
||||||
.push(palette.colour_index(colour, results.transparent_colour));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let tile_data: Vec<_> = tile_data
|
|
||||||
.chunks(2)
|
|
||||||
.map(|chunk| (chunk[1] << 4) | chunk[0])
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let data = ByteString(&tile_data);
|
let data = ByteString(&tile_data);
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use agb::{
|
use agb::{
|
||||||
display::{
|
display::{
|
||||||
tiled::{RegularBackgroundSize, TileFormat, TileSet, TileSetting},
|
tiled::{RegularBackgroundSize, TileFormat, TileSet, TileSetting, TiledMap},
|
||||||
Priority,
|
Priority,
|
||||||
},
|
},
|
||||||
include_gfx,
|
include_gfx,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use agb::{
|
use agb::{
|
||||||
display::tiled::{TileFormat, TileSet, TileSetting},
|
display::tiled::{TileFormat, TileSet, TileSetting, TiledMap},
|
||||||
display::{
|
display::{
|
||||||
object::{Object, ObjectController, Size, Sprite},
|
object::{Object, ObjectController, Size, Sprite},
|
||||||
palette16::Palette16,
|
palette16::Palette16,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use agb::display::{
|
use agb::display::{
|
||||||
palette16::Palette16,
|
palette16::Palette16,
|
||||||
tiled::{RegularBackgroundSize, TileSetting},
|
tiled::{RegularBackgroundSize, TileSetting, TiledMap},
|
||||||
Priority,
|
Priority,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use agb::{
|
use agb::{
|
||||||
display::{
|
display::{
|
||||||
tiled::{RegularBackgroundSize, RegularMap, TileSetting, VRamManager},
|
tiled::{RegularBackgroundSize, RegularMap, TileSetting, TiledMap, VRamManager},
|
||||||
Font, Priority,
|
Font, Priority,
|
||||||
},
|
},
|
||||||
include_font, include_wav,
|
include_font, include_wav,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use agb::{
|
use agb::{
|
||||||
display::{
|
display::{
|
||||||
tiled::{RegularBackgroundSize, TileSetting},
|
tiled::{RegularBackgroundSize, TileSetting, TiledMap},
|
||||||
Font, Priority,
|
Font, Priority,
|
||||||
},
|
},
|
||||||
include_font,
|
include_font,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::tiled::{RegularMap, TileFormat, TileSet, TileSetting, VRamManager};
|
use super::tiled::{RegularMap, TileFormat, TileSet, TileSetting, TiledMap, VRamManager};
|
||||||
|
|
||||||
crate::include_gfx!("gfx/agb_logo.toml");
|
crate::include_gfx!("gfx/agb_logo.toml");
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ pub fn display_logo(map: &mut RegularMap, vram: &mut VRamManager) {
|
||||||
map.commit(vram);
|
map.commit(vram);
|
||||||
map.show();
|
map.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::display::{tiled::RegularBackgroundSize, Priority};
|
use crate::display::{tiled::RegularBackgroundSize, Priority};
|
||||||
|
|
|
@ -210,6 +210,7 @@ impl<'a> Drop for TextRenderer<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::display::tiled::TiledMap;
|
||||||
const FONT: Font = crate::include_font!("examples/font/yoster.ttf", 12);
|
const FONT: Font = crate::include_font!("examples/font/yoster.ttf", 12);
|
||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
|
|
|
@ -18,7 +18,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::Vector2D;
|
use crate::fixnum::{Num, Vector2D};
|
||||||
use crate::hash_map::HashMap;
|
use crate::hash_map::HashMap;
|
||||||
|
|
||||||
use attributes::*;
|
use attributes::*;
|
||||||
|
@ -627,7 +627,7 @@ impl ObjectController {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
(OBJECT_ATTRIBUTE_MEMORY as *mut u16)
|
(OBJECT_ATTRIBUTE_MEMORY as *mut u16)
|
||||||
.add((i as usize) * 4)
|
.add(i * 4)
|
||||||
.write_volatile(HIDDEN_VALUE);
|
.write_volatile(HIDDEN_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,7 +776,7 @@ impl ObjectController {
|
||||||
|
|
||||||
attrs.a2.set_tile_index(sprite.sprite_location);
|
attrs.a2.set_tile_index(sprite.sprite_location);
|
||||||
let shape_size = sprite.id.sprite().size.shape_size();
|
let shape_size = sprite.id.sprite().size.shape_size();
|
||||||
attrs.a2.set_palete_bank(sprite.palette_location as u8);
|
attrs.a2.set_palette_bank(sprite.palette_location as u8);
|
||||||
attrs.a0.set_shape(shape_size.0);
|
attrs.a0.set_shape(shape_size.0);
|
||||||
attrs.a1a.set_size(shape_size.1);
|
attrs.a1a.set_size(shape_size.1);
|
||||||
attrs.a1s.set_size(shape_size.1);
|
attrs.a1s.set_size(shape_size.1);
|
||||||
|
@ -877,7 +877,7 @@ impl<'a> Object<'a> {
|
||||||
object_inner
|
object_inner
|
||||||
.attrs
|
.attrs
|
||||||
.a2
|
.a2
|
||||||
.set_palete_bank(sprite.palette_location as u8);
|
.set_palette_bank(sprite.palette_location as u8);
|
||||||
object_inner.attrs.a0.set_shape(shape_size.0);
|
object_inner.attrs.a0.set_shape(shape_size.0);
|
||||||
object_inner.attrs.a1a.set_size(shape_size.1);
|
object_inner.attrs.a1a.set_size(shape_size.1);
|
||||||
object_inner.attrs.a1s.set_size(shape_size.1);
|
object_inner.attrs.a1s.set_size(shape_size.1);
|
||||||
|
@ -1193,6 +1193,31 @@ 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, Default, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(C)]
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
|
@ -1232,7 +1257,7 @@ mod attributes {
|
||||||
pub(super) struct ObjectAttribute2 {
|
pub(super) struct ObjectAttribute2 {
|
||||||
pub tile_index: B10,
|
pub tile_index: B10,
|
||||||
pub priority: Priority,
|
pub priority: Priority,
|
||||||
pub palete_bank: B4,
|
pub palette_bank: B4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
use super::{BackgroundID, MapLoan, RegularMap, TileSet, TileSetting, VRamManager};
|
use super::{
|
||||||
|
BackgroundID, BackgroundSizePrivate, MapLoan, RegularMap, TileSet, TileSetting, TiledMap,
|
||||||
|
VRamManager,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
display,
|
display,
|
||||||
|
|
|
@ -2,31 +2,184 @@ 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::{Priority, DISPLAY_CONTROL};
|
use crate::display::{object::AffineMatrixAttributes, Priority, DISPLAY_CONTROL};
|
||||||
use crate::dma::dma_copy16;
|
use crate::dma::dma_copy16;
|
||||||
use crate::fixnum::Vector2D;
|
use crate::fixnum::{Num, Number, Vector2D};
|
||||||
use crate::memory_mapped::MemoryMapped;
|
use crate::memory_mapped::MemoryMapped;
|
||||||
|
|
||||||
use super::{BackgroundID, RegularBackgroundSize, Tile, TileSet, TileSetting, VRamManager};
|
use super::{
|
||||||
|
AffineBackgroundSize, BackgroundID, BackgroundSize, BackgroundSizePrivate,
|
||||||
|
RegularBackgroundSize, Tile, TileFormat, TileIndex, TileSet, TileSetting, VRamManager,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::syscall::BgAffineSetData;
|
||||||
use alloc::{vec, vec::Vec};
|
use alloc::{vec, vec::Vec};
|
||||||
|
|
||||||
|
pub(super) trait TiledMapPrivateConst: TiledMapTypes {
|
||||||
|
type TileType: Into<TileIndex> + Copy + Default + Eq + PartialEq;
|
||||||
|
type AffineMatrix;
|
||||||
|
fn x_scroll(&self) -> Self::Position;
|
||||||
|
fn y_scroll(&self) -> Self::Position;
|
||||||
|
fn affine_matrix(&self) -> Self::AffineMatrix;
|
||||||
|
fn background_id(&self) -> usize;
|
||||||
|
fn screenblock(&self) -> usize;
|
||||||
|
fn priority(&self) -> Priority;
|
||||||
|
fn map_size(&self) -> Self::Size;
|
||||||
|
fn bg_x(&self) -> MemoryMapped<Self::Position>;
|
||||||
|
fn bg_y(&self) -> MemoryMapped<Self::Position>;
|
||||||
|
fn bg_affine_matrix(&self) -> MemoryMapped<Self::AffineMatrix>;
|
||||||
|
fn bg_control_register(&self) -> MemoryMapped<u16> {
|
||||||
|
unsafe { MemoryMapped::new(0x0400_0008 + 2 * self.background_id()) }
|
||||||
|
}
|
||||||
|
fn screenblock_memory(&self) -> *mut u16 {
|
||||||
|
(0x0600_0000 + 0x1000 * self.screenblock() as usize / 2) as *mut u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait TiledMapPrivate: TiledMapPrivateConst {
|
||||||
|
fn tiles_mut(&mut self) -> &mut [Self::TileType];
|
||||||
|
fn tiles_dirty(&mut self) -> &mut bool;
|
||||||
|
fn x_scroll_mut(&mut self) -> &mut Self::Position;
|
||||||
|
fn y_scroll_mut(&mut self) -> &mut Self::Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TiledMapTypes {
|
||||||
|
type Position: Number;
|
||||||
|
type Size: BackgroundSize + Copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TiledMap: TiledMapTypes {
|
||||||
|
fn clear(&mut self, vram: &mut VRamManager);
|
||||||
|
fn show(&mut self);
|
||||||
|
fn hide(&mut self);
|
||||||
|
fn commit(&mut self, vram: &mut VRamManager);
|
||||||
|
fn size(&self) -> Self::Size;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn scroll_pos(&self) -> Vector2D<Self::Position>;
|
||||||
|
fn set_scroll_pos(&mut self, pos: Vector2D<Self::Position>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TiledMap for T
|
||||||
|
where
|
||||||
|
T: TiledMapPrivateConst + TiledMapPrivate + TiledMapTypes,
|
||||||
|
T::Size: BackgroundSizePrivate,
|
||||||
|
{
|
||||||
|
fn clear(&mut self, vram: &mut VRamManager) {
|
||||||
|
for tile in self.tiles_mut() {
|
||||||
|
if *tile != Default::default() {
|
||||||
|
vram.remove_tile((*tile).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
*tile = Default::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self) {
|
||||||
|
let mode = DISPLAY_CONTROL.get();
|
||||||
|
let new_mode = mode | (1 << (self.background_id() + 0x08)) as u16;
|
||||||
|
DISPLAY_CONTROL.set(new_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide(&mut self) {
|
||||||
|
let mode = DISPLAY_CONTROL.get();
|
||||||
|
let new_mode = mode & !(1 << (self.background_id() + 0x08)) as u16;
|
||||||
|
DISPLAY_CONTROL.set(new_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(&mut self, vram: &mut VRamManager) {
|
||||||
|
let new_bg_control_value = (self.priority() as u16)
|
||||||
|
| ((self.screenblock() as u16) << 8)
|
||||||
|
| (self.map_size().size_flag() << 14);
|
||||||
|
|
||||||
|
self.bg_control_register().set(new_bg_control_value);
|
||||||
|
self.bg_x().set(self.x_scroll());
|
||||||
|
self.bg_y().set(self.y_scroll());
|
||||||
|
self.bg_affine_matrix().set(self.affine_matrix());
|
||||||
|
|
||||||
|
let screenblock_memory = self.screenblock_memory();
|
||||||
|
let x: TileIndex = unsafe { *self.tiles_mut().get_unchecked(0) }.into();
|
||||||
|
let x = x.format().tile_size() / TileFormat::FourBpp.tile_size();
|
||||||
|
if *self.tiles_dirty() {
|
||||||
|
unsafe {
|
||||||
|
dma_copy16(
|
||||||
|
self.tiles_mut().as_ptr() as *const u16,
|
||||||
|
screenblock_memory,
|
||||||
|
self.map_size().num_tiles() / x,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vram.gc();
|
||||||
|
|
||||||
|
*self.tiles_dirty() = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> Self::Size {
|
||||||
|
self.map_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn scroll_pos(&self) -> Vector2D<Self::Position> {
|
||||||
|
(self.x_scroll(), self.y_scroll()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_scroll_pos(&mut self, pos: Vector2D<Self::Position>) {
|
||||||
|
*self.x_scroll_mut() = pos.x;
|
||||||
|
*self.y_scroll_mut() = pos.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RegularMap {
|
pub struct RegularMap {
|
||||||
background_id: u8,
|
background_id: u8,
|
||||||
|
|
||||||
screenblock: u8,
|
screenblock: u8,
|
||||||
|
priority: Priority,
|
||||||
|
size: RegularBackgroundSize,
|
||||||
|
|
||||||
x_scroll: u16,
|
x_scroll: u16,
|
||||||
y_scroll: u16,
|
y_scroll: u16,
|
||||||
priority: Priority,
|
|
||||||
|
|
||||||
tiles: Vec<Tile>,
|
tiles: Vec<Tile>,
|
||||||
tiles_dirty: bool,
|
tiles_dirty: bool,
|
||||||
|
|
||||||
size: RegularBackgroundSize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const TRANSPARENT_TILE_INDEX: u16 = (1 << 10) - 1;
|
pub const TRANSPARENT_TILE_INDEX: u16 = (1 << 10) - 1;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
impl TiledMapPrivate for RegularMap {
|
||||||
|
fn tiles_mut(&mut self) -> &mut [Self::TileType] { &mut self.tiles }
|
||||||
|
fn tiles_dirty(&mut self) -> &mut bool { &mut self.tiles_dirty }
|
||||||
|
fn x_scroll_mut(&mut self) -> &mut Self::Position { &mut self.x_scroll }
|
||||||
|
fn y_scroll_mut(&mut self) -> &mut Self::Position { &mut self.y_scroll }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiledMapTypes for RegularMap {
|
||||||
|
type Position = u16;
|
||||||
|
type Size = RegularBackgroundSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
impl const TiledMapPrivateConst for RegularMap {
|
||||||
|
type TileType = Tile;
|
||||||
|
type AffineMatrix = ();
|
||||||
|
fn x_scroll(&self) -> Self::Position { self.x_scroll }
|
||||||
|
fn y_scroll(&self) -> Self::Position { self.y_scroll }
|
||||||
|
fn affine_matrix(&self) -> Self::AffineMatrix {}
|
||||||
|
fn background_id(&self) -> usize { self.background_id as usize }
|
||||||
|
fn screenblock(&self) -> usize { self.screenblock as usize }
|
||||||
|
fn priority(&self) -> Priority { self.priority }
|
||||||
|
fn map_size(&self) -> Self::Size { self.size }
|
||||||
|
fn bg_x(&self) -> MemoryMapped<Self::Position> {
|
||||||
|
unsafe { MemoryMapped::new(0x0400_0010 + 4 * self.background_id as usize) }
|
||||||
|
}
|
||||||
|
fn bg_y(&self) -> MemoryMapped<Self::Position> {
|
||||||
|
unsafe { MemoryMapped::new(0x0400_0012 + 4 * self.background_id as usize) }
|
||||||
|
}
|
||||||
|
fn bg_affine_matrix(&self) -> MemoryMapped<Self::AffineMatrix> {
|
||||||
|
unsafe { MemoryMapped::new(0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RegularMap {
|
impl RegularMap {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
background_id: u8,
|
background_id: u8,
|
||||||
|
@ -36,16 +189,15 @@ impl RegularMap {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
background_id,
|
background_id,
|
||||||
|
|
||||||
screenblock,
|
screenblock,
|
||||||
|
priority,
|
||||||
|
size,
|
||||||
|
|
||||||
x_scroll: 0,
|
x_scroll: 0,
|
||||||
y_scroll: 0,
|
y_scroll: 0,
|
||||||
priority,
|
|
||||||
|
|
||||||
tiles: vec![Default::default(); size.num_tiles()],
|
tiles: vec![Default::default(); size.num_tiles()],
|
||||||
tiles_dirty: true,
|
tiles_dirty: true,
|
||||||
|
|
||||||
size,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,11 +208,11 @@ impl RegularMap {
|
||||||
tileset: &TileSet<'_>,
|
tileset: &TileSet<'_>,
|
||||||
tile_setting: TileSetting,
|
tile_setting: TileSetting,
|
||||||
) {
|
) {
|
||||||
let pos = self.size.gba_offset(pos);
|
let pos = self.map_size().gba_offset(pos);
|
||||||
|
|
||||||
let old_tile = self.tiles[pos];
|
let old_tile = self.tiles_mut()[pos];
|
||||||
if old_tile != Tile::default() {
|
if old_tile != Tile::default() {
|
||||||
vram.remove_tile(old_tile.tile_index());
|
vram.remove_tile(old_tile.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let tile_index = tile_setting.index();
|
let tile_index = tile_setting.index();
|
||||||
|
@ -77,86 +229,129 @@ impl RegularMap {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tiles[pos] = new_tile;
|
self.tiles_mut()[pos] = new_tile;
|
||||||
self.tiles_dirty = true;
|
*self.tiles_dirty() = true;
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self, vram: &mut VRamManager) {
|
|
||||||
for tile in self.tiles.iter_mut() {
|
|
||||||
if *tile != Tile::default() {
|
|
||||||
vram.remove_tile(tile.tile_index());
|
|
||||||
}
|
|
||||||
|
|
||||||
*tile = Tile::default();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(&mut self) {
|
pub struct AffineMap {
|
||||||
let mode = DISPLAY_CONTROL.get();
|
background_id: u8,
|
||||||
let new_mode = mode | (1 << (self.background_id + 0x08));
|
screenblock: u8,
|
||||||
DISPLAY_CONTROL.set(new_mode);
|
priority: Priority,
|
||||||
|
size: AffineBackgroundSize,
|
||||||
|
|
||||||
|
bg_center: Vector2D<Num<i32, 8>>,
|
||||||
|
transform: BgAffineSetData,
|
||||||
|
|
||||||
|
tiles: Vec<u8>,
|
||||||
|
tiles_dirty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide(&mut self) {
|
#[rustfmt::skip]
|
||||||
let mode = DISPLAY_CONTROL.get();
|
impl TiledMapPrivate for AffineMap {
|
||||||
let new_mode = mode & !(1 << (self.background_id + 0x08));
|
fn tiles_mut(&mut self) -> &mut [Self::TileType] { &mut self.tiles }
|
||||||
DISPLAY_CONTROL.set(new_mode);
|
fn tiles_dirty(&mut self) -> &mut bool { &mut self.tiles_dirty }
|
||||||
|
fn x_scroll_mut(&mut self) -> &mut Self::Position { &mut self.transform.position.x }
|
||||||
|
fn y_scroll_mut(&mut self) -> &mut Self::Position { &mut self.transform.position.y }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit(&mut self, vram: &mut VRamManager) {
|
impl TiledMapTypes for AffineMap {
|
||||||
let new_bg_control_value = (self.priority as u16)
|
type Position = Num<i32, 8>;
|
||||||
| (u16::from(self.screenblock) << 8)
|
type Size = AffineBackgroundSize;
|
||||||
| (self.size.size_flag() << 14);
|
}
|
||||||
|
|
||||||
self.bg_control_register().set(new_bg_control_value);
|
#[rustfmt::skip]
|
||||||
self.bg_h_offset().set(self.x_scroll);
|
impl const TiledMapPrivateConst for AffineMap {
|
||||||
self.bg_v_offset().set(self.y_scroll);
|
type TileType = u8;
|
||||||
|
type AffineMatrix = AffineMatrixAttributes;
|
||||||
let screenblock_memory = self.screenblock_memory();
|
fn x_scroll(&self) -> Self::Position { self.transform.position.x }
|
||||||
|
fn y_scroll(&self) -> Self::Position { self.transform.position.y }
|
||||||
if self.tiles_dirty {
|
fn affine_matrix(&self) -> Self::AffineMatrix { self.transform.matrix }
|
||||||
unsafe {
|
fn background_id(&self) -> usize { self.background_id as usize }
|
||||||
dma_copy16(
|
fn screenblock(&self) -> usize { self.screenblock as usize }
|
||||||
self.tiles.as_ptr() as *const u16,
|
fn priority(&self) -> Priority { self.priority }
|
||||||
screenblock_memory,
|
fn map_size(&self) -> Self::Size { self.size }
|
||||||
self.size.num_tiles(),
|
fn bg_x(&self) -> MemoryMapped<Self::Position> {
|
||||||
);
|
unsafe { MemoryMapped::new(0x0400_0008 + 0x10 * self.background_id()) }
|
||||||
|
}
|
||||||
|
fn bg_y(&self) -> MemoryMapped<Self::Position> {
|
||||||
|
unsafe { MemoryMapped::new(0x0400_000c + 0x10 * self.background_id()) }
|
||||||
|
}
|
||||||
|
fn bg_affine_matrix(&self) -> MemoryMapped<Self::AffineMatrix> {
|
||||||
|
unsafe { MemoryMapped::new(0x0400_0000 + 0x10 * self.background_id()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vram.gc();
|
impl AffineMap {
|
||||||
|
pub(crate) fn new(
|
||||||
|
background_id: u8,
|
||||||
|
screenblock: u8,
|
||||||
|
priority: Priority,
|
||||||
|
size: AffineBackgroundSize,
|
||||||
|
bg_center: Vector2D<Num<i32, 8>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
background_id,
|
||||||
|
screenblock,
|
||||||
|
priority,
|
||||||
|
size,
|
||||||
|
|
||||||
self.tiles_dirty = false;
|
bg_center,
|
||||||
|
transform: Default::default(),
|
||||||
|
|
||||||
|
tiles: vec![Default::default(); size.num_tiles()],
|
||||||
|
tiles_dirty: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_scroll_pos(&mut self, pos: Vector2D<u16>) {
|
pub fn set_tile(
|
||||||
self.x_scroll = pos.x;
|
&mut self,
|
||||||
self.y_scroll = pos.y;
|
vram: &mut VRamManager,
|
||||||
|
pos: Vector2D<u16>,
|
||||||
|
tileset: &TileSet<'_>,
|
||||||
|
tile_id: u8,
|
||||||
|
) {
|
||||||
|
let pos = self.map_size().gba_offset(pos);
|
||||||
|
|
||||||
|
let old_tile = self.tiles_mut()[pos];
|
||||||
|
if old_tile != 0 {
|
||||||
|
vram.remove_tile(old_tile.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
let tile_index = tile_id as u16;
|
||||||
pub fn scroll_pos(&self) -> Vector2D<u16> {
|
|
||||||
(self.x_scroll, self.y_scroll).into()
|
let new_tile = if tile_index != TRANSPARENT_TILE_INDEX {
|
||||||
|
let new_tile_idx = vram.add_tile(tileset, tile_index);
|
||||||
|
new_tile_idx.raw_index() as u8
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if old_tile == new_tile {
|
||||||
|
// no need to mark as dirty if nothing changes
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn size(&self) -> RegularBackgroundSize {
|
self.tiles_mut()[pos] = new_tile;
|
||||||
self.size
|
*self.tiles_dirty() = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn bg_control_register(&self) -> MemoryMapped<u16> {
|
pub fn set_transform_raw(&mut self, transform: BgAffineSetData) {
|
||||||
unsafe { MemoryMapped::new(0x0400_0008 + 2 * self.background_id as usize) }
|
self.transform = transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn bg_h_offset(&self) -> MemoryMapped<u16> {
|
pub fn set_transform(
|
||||||
unsafe { MemoryMapped::new(0x0400_0010 + 4 * self.background_id as usize) }
|
&mut self,
|
||||||
}
|
display_center: Vector2D<i16>,
|
||||||
|
scale: Vector2D<Num<i16, 8>>,
|
||||||
const fn bg_v_offset(&self) -> MemoryMapped<u16> {
|
rotation: Num<u8, 8>,
|
||||||
unsafe { MemoryMapped::new(0x0400_0012 + 4 * self.background_id as usize) }
|
) {
|
||||||
}
|
self.set_transform_raw(crate::syscall::bg_affine_matrix(
|
||||||
|
self.bg_center,
|
||||||
const fn screenblock_memory(&self) -> *mut u16 {
|
display_center,
|
||||||
(0x0600_0000 + 0x1000 * self.screenblock as usize / 2) as *mut u16
|
scale,
|
||||||
|
rotation,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +360,7 @@ pub struct MapLoan<'a, T> {
|
||||||
background_id: u8,
|
background_id: u8,
|
||||||
screenblock_id: u8,
|
screenblock_id: u8,
|
||||||
screenblock_length: u8,
|
screenblock_length: u8,
|
||||||
regular_map_list: &'a RefCell<Bitarray<1>>,
|
map_list: &'a RefCell<Bitarray<1>>,
|
||||||
screenblock_list: &'a RefCell<Bitarray<1>>,
|
screenblock_list: &'a RefCell<Bitarray<1>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +384,7 @@ impl<'a, T> MapLoan<'a, T> {
|
||||||
background_id: u8,
|
background_id: u8,
|
||||||
screenblock_id: u8,
|
screenblock_id: u8,
|
||||||
screenblock_length: u8,
|
screenblock_length: u8,
|
||||||
regular_map_list: &'a RefCell<Bitarray<1>>,
|
map_list: &'a RefCell<Bitarray<1>>,
|
||||||
screenblock_list: &'a RefCell<Bitarray<1>>,
|
screenblock_list: &'a RefCell<Bitarray<1>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
MapLoan {
|
MapLoan {
|
||||||
|
@ -197,7 +392,7 @@ impl<'a, T> MapLoan<'a, T> {
|
||||||
background_id,
|
background_id,
|
||||||
screenblock_id,
|
screenblock_id,
|
||||||
screenblock_length,
|
screenblock_length,
|
||||||
regular_map_list,
|
map_list,
|
||||||
screenblock_list,
|
screenblock_list,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,7 +405,7 @@ impl<'a, T> MapLoan<'a, T> {
|
||||||
|
|
||||||
impl<'a, T> Drop for MapLoan<'a, T> {
|
impl<'a, T> Drop for MapLoan<'a, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.regular_map_list
|
self.map_list
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.set(self.background_id as usize, false);
|
.set(self.background_id as usize, false);
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,76 @@
|
||||||
mod infinite_scrolled_map;
|
mod infinite_scrolled_map;
|
||||||
mod map;
|
mod map;
|
||||||
mod tiled0;
|
mod tiled0;
|
||||||
|
mod tiled1;
|
||||||
|
mod tiled2;
|
||||||
mod vram_manager;
|
mod vram_manager;
|
||||||
|
|
||||||
use agb_fixnum::Vector2D;
|
use crate::bitarray::Bitarray;
|
||||||
|
use crate::display::Priority;
|
||||||
|
use agb_fixnum::{Num, Vector2D};
|
||||||
|
use core::cell::RefCell;
|
||||||
pub use infinite_scrolled_map::{InfiniteScrolledMap, PartialUpdateStatus};
|
pub use infinite_scrolled_map::{InfiniteScrolledMap, PartialUpdateStatus};
|
||||||
pub use map::{MapLoan, RegularMap};
|
pub use map::{AffineMap, MapLoan, RegularMap, TiledMap};
|
||||||
pub use tiled0::Tiled0;
|
pub use tiled0::Tiled0;
|
||||||
|
pub use tiled1::Tiled1;
|
||||||
|
pub use tiled2::Tiled2;
|
||||||
pub use vram_manager::{DynamicTile, TileFormat, TileIndex, TileSet, VRamManager};
|
pub use vram_manager::{DynamicTile, TileFormat, TileIndex, TileSet, VRamManager};
|
||||||
|
|
||||||
|
// affine layers start at BG2
|
||||||
|
pub(crate) const AFFINE_BG_ID_OFFSET: usize = 2;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(u16)]
|
||||||
pub enum RegularBackgroundSize {
|
pub enum RegularBackgroundSize {
|
||||||
Background32x32,
|
Background32x32 = 0,
|
||||||
Background64x32,
|
Background64x32 = 1,
|
||||||
Background32x64,
|
Background32x64 = 2,
|
||||||
Background64x64,
|
Background64x64 = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct BackgroundID(pub(crate) u8);
|
pub struct BackgroundID(pub(crate) u8);
|
||||||
|
|
||||||
impl RegularBackgroundSize {
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum AffineBackgroundSize {
|
||||||
|
Background16x16 = 0,
|
||||||
|
Background32x32 = 1,
|
||||||
|
Background64x64 = 2,
|
||||||
|
Background128x128 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BackgroundSize {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn width(&self) -> u32 {
|
fn width(&self) -> u32;
|
||||||
|
#[must_use]
|
||||||
|
fn height(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) trait BackgroundSizePrivate: BackgroundSize + Sized {
|
||||||
|
fn size_flag(self) -> u16;
|
||||||
|
fn num_tiles(&self) -> usize {
|
||||||
|
(self.width() * self.height()) as usize
|
||||||
|
}
|
||||||
|
fn num_screen_blocks(&self) -> usize;
|
||||||
|
fn gba_offset(&self, pos: Vector2D<u16>) -> usize;
|
||||||
|
fn tile_pos_x(&self, x: i32) -> u16 {
|
||||||
|
((x as u32) & (self.width() - 1)) as u16
|
||||||
|
}
|
||||||
|
fn tile_pos_y(&self, y: i32) -> u16 {
|
||||||
|
((y as u32) & (self.height() - 1)) as u16
|
||||||
|
}
|
||||||
|
fn px_offset_x(&self, x: i32) -> u16 {
|
||||||
|
((x as u32) & (self.width() * 8 - 1)) as u16
|
||||||
|
}
|
||||||
|
fn px_offset_y(&self, y: i32) -> u16 {
|
||||||
|
((y as u32) & (self.height() * 8 - 1)) as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackgroundSize for RegularBackgroundSize {
|
||||||
|
#[must_use]
|
||||||
|
fn width(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
RegularBackgroundSize::Background32x64 | RegularBackgroundSize::Background32x32 => 32,
|
RegularBackgroundSize::Background32x64 | RegularBackgroundSize::Background32x32 => 32,
|
||||||
RegularBackgroundSize::Background64x64 | RegularBackgroundSize::Background64x32 => 64,
|
RegularBackgroundSize::Background64x64 | RegularBackgroundSize::Background64x32 => 64,
|
||||||
|
@ -30,33 +78,26 @@ impl RegularBackgroundSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn height(&self) -> u32 {
|
fn height(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
RegularBackgroundSize::Background32x32 | RegularBackgroundSize::Background64x32 => 32,
|
RegularBackgroundSize::Background32x32 | RegularBackgroundSize::Background64x32 => 32,
|
||||||
RegularBackgroundSize::Background32x64 | RegularBackgroundSize::Background64x64 => 64,
|
RegularBackgroundSize::Background32x64 | RegularBackgroundSize::Background64x64 => 64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn size_flag(self) -> u16 {
|
|
||||||
match self {
|
|
||||||
RegularBackgroundSize::Background32x32 => 0,
|
|
||||||
RegularBackgroundSize::Background64x32 => 1,
|
|
||||||
RegularBackgroundSize::Background32x64 => 2,
|
|
||||||
RegularBackgroundSize::Background64x64 => 3,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn num_tiles(self) -> usize {
|
impl BackgroundSizePrivate for RegularBackgroundSize {
|
||||||
(self.width() * self.height()) as usize
|
fn size_flag(self) -> u16 {
|
||||||
|
self as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn num_screen_blocks(self) -> usize {
|
fn num_screen_blocks(&self) -> usize {
|
||||||
self.num_tiles() / (32 * 32)
|
self.num_tiles() / (32 * 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is hilariously complicated due to how the GBA stores the background screenblocks.
|
// This is hilariously complicated due to how the GBA stores the background screenblocks.
|
||||||
// See https://www.coranac.com/tonc/text/regbg.htm#sec-map for an explanation
|
// See https://www.coranac.com/tonc/text/regbg.htm#sec-map for an explanation
|
||||||
pub(crate) fn gba_offset(self, pos: Vector2D<u16>) -> usize {
|
fn gba_offset(&self, pos: Vector2D<u16>) -> usize {
|
||||||
let x_mod = pos.x & (self.width() as u16 - 1);
|
let x_mod = pos.x & (self.width() as u16 - 1);
|
||||||
let y_mod = pos.y & (self.height() as u16 - 1);
|
let y_mod = pos.y & (self.height() as u16 - 1);
|
||||||
|
|
||||||
|
@ -66,21 +107,43 @@ impl RegularBackgroundSize {
|
||||||
|
|
||||||
pos as usize
|
pos as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn tile_pos_x(self, x: i32) -> u16 {
|
|
||||||
((x as u32) & (self.width() - 1)) as u16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn tile_pos_y(self, y: i32) -> u16 {
|
impl BackgroundSize for AffineBackgroundSize {
|
||||||
((y as u32) & (self.height() - 1)) as u16
|
#[must_use]
|
||||||
|
fn width(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
AffineBackgroundSize::Background16x16 => 16,
|
||||||
|
AffineBackgroundSize::Background32x32 => 32,
|
||||||
|
AffineBackgroundSize::Background64x64 => 64,
|
||||||
|
AffineBackgroundSize::Background128x128 => 128,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn px_offset_x(self, x: i32) -> u16 {
|
#[must_use]
|
||||||
((x as u32) & (self.width() * 8 - 1)) as u16
|
fn height(&self) -> u32 {
|
||||||
|
self.width()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn px_offset_y(self, y: i32) -> u16 {
|
impl BackgroundSizePrivate for AffineBackgroundSize {
|
||||||
((y as u32) & (self.height() * 8 - 1)) as u16
|
fn size_flag(self) -> u16 {
|
||||||
|
self as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_screen_blocks(&self) -> usize {
|
||||||
|
// technically 16x16 and 32x32 only use the first 1/8 and 1/2 of the SB, respectively
|
||||||
|
1.max(self.num_tiles() / 2048)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Affine modes don't do the convoluted staggered block layout
|
||||||
|
fn gba_offset(&self, pos: Vector2D<u16>) -> usize {
|
||||||
|
let x_mod = pos.x & (self.width() as u16 - 1);
|
||||||
|
let y_mod = pos.y & (self.height() as u16 - 1);
|
||||||
|
|
||||||
|
let pos = x_mod + (self.width() as u16 * y_mod);
|
||||||
|
|
||||||
|
pos as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,11 +153,11 @@ struct Tile(u16);
|
||||||
|
|
||||||
impl Tile {
|
impl Tile {
|
||||||
fn new(idx: TileIndex, setting: TileSetting) -> Self {
|
fn new(idx: TileIndex, setting: TileSetting) -> Self {
|
||||||
Self(idx.index() | setting.setting())
|
Self(idx.raw_index() | setting.setting())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tile_index(self) -> TileIndex {
|
fn tile_index(self) -> TileIndex {
|
||||||
TileIndex::new(self.0 as usize & ((1 << 10) - 1))
|
TileIndex::new(self.0 as usize & ((1 << 10) - 1), TileFormat::FourBpp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +189,149 @@ impl TileSetting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(self) fn find_screenblock_gap(screenblocks: &Bitarray<1>, gap: usize) -> usize {
|
||||||
|
let mut candidate = 0;
|
||||||
|
|
||||||
|
'outer: while candidate < 16 - gap {
|
||||||
|
let starting_point = candidate;
|
||||||
|
for attempt in starting_point..(starting_point + gap) {
|
||||||
|
if screenblocks.get(attempt) == Some(true) {
|
||||||
|
candidate = attempt + 1;
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!(
|
||||||
|
"Failed to find screenblock gap of at least {} elements",
|
||||||
|
gap
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(self) trait TiledMode {
|
||||||
|
const REGULAR_BACKGROUNDS: usize;
|
||||||
|
const AFFINE_BACKGROUNDS: usize;
|
||||||
|
fn screenblocks(&self) -> &RefCell<Bitarray<1>>;
|
||||||
|
fn regular(&self) -> &RefCell<Bitarray<1>>;
|
||||||
|
fn affine(&self) -> &RefCell<Bitarray<1>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RegularTiledMode {
|
||||||
|
fn regular_background(
|
||||||
|
&self,
|
||||||
|
priority: Priority,
|
||||||
|
size: RegularBackgroundSize,
|
||||||
|
) -> MapLoan<'_, RegularMap>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AffineTiledMode {
|
||||||
|
fn affine_background(
|
||||||
|
&self,
|
||||||
|
priority: Priority,
|
||||||
|
size: AffineBackgroundSize,
|
||||||
|
) -> MapLoan<'_, AffineMap>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// withoutboats: https://internals.rust-lang.org/t/const-generics-where-restrictions/12742/6
|
||||||
|
pub struct If<const B: bool>;
|
||||||
|
pub trait True {}
|
||||||
|
impl True for If<true> {}
|
||||||
|
|
||||||
|
impl<T> RegularTiledMode for T
|
||||||
|
where
|
||||||
|
T: TiledMode,
|
||||||
|
If<{ T::REGULAR_BACKGROUNDS > 0 }>: True,
|
||||||
|
{
|
||||||
|
fn regular_background(
|
||||||
|
&self,
|
||||||
|
priority: Priority,
|
||||||
|
size: RegularBackgroundSize,
|
||||||
|
) -> MapLoan<'_, RegularMap> {
|
||||||
|
let mut regular = self.regular().borrow_mut();
|
||||||
|
let new_background = regular.first_zero().unwrap();
|
||||||
|
if new_background >= T::REGULAR_BACKGROUNDS {
|
||||||
|
panic!(
|
||||||
|
"can only have {} active regular backgrounds",
|
||||||
|
T::REGULAR_BACKGROUNDS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_screenblocks = size.num_screen_blocks();
|
||||||
|
let mut screenblocks = self.screenblocks().borrow_mut();
|
||||||
|
|
||||||
|
let screenblock = find_screenblock_gap(&screenblocks, num_screenblocks);
|
||||||
|
for id in screenblock..(screenblock + num_screenblocks) {
|
||||||
|
screenblocks.set(id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bg = RegularMap::new(new_background as u8, screenblock as u8 + 16, priority, size);
|
||||||
|
|
||||||
|
regular.set(new_background, true);
|
||||||
|
|
||||||
|
MapLoan::new(
|
||||||
|
bg,
|
||||||
|
new_background as u8,
|
||||||
|
screenblock as u8,
|
||||||
|
num_screenblocks as u8,
|
||||||
|
self.regular(),
|
||||||
|
self.screenblocks(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AffineTiledMode for T
|
||||||
|
where
|
||||||
|
T: TiledMode,
|
||||||
|
If<{ T::AFFINE_BACKGROUNDS > 0 }>: True,
|
||||||
|
{
|
||||||
|
fn affine_background(
|
||||||
|
&self,
|
||||||
|
priority: Priority,
|
||||||
|
size: AffineBackgroundSize,
|
||||||
|
) -> MapLoan<'_, AffineMap> {
|
||||||
|
let mut affine = self.affine().borrow_mut();
|
||||||
|
let new_background = affine.first_zero().unwrap();
|
||||||
|
if new_background >= T::AFFINE_BACKGROUNDS + AFFINE_BG_ID_OFFSET {
|
||||||
|
panic!(
|
||||||
|
"can only have {} active affine backgrounds",
|
||||||
|
T::AFFINE_BACKGROUNDS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_screenblocks = size.num_screen_blocks();
|
||||||
|
let mut screenblocks = self.screenblocks().borrow_mut();
|
||||||
|
|
||||||
|
let screenblock = find_screenblock_gap(&screenblocks, num_screenblocks);
|
||||||
|
for id in screenblock..(screenblock + num_screenblocks) {
|
||||||
|
screenblocks.set(id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let center_dim = Num::new(size.width() as i32 * 8 / 2);
|
||||||
|
let default_bg_center = (center_dim, center_dim).into();
|
||||||
|
|
||||||
|
let bg = AffineMap::new(
|
||||||
|
new_background as u8,
|
||||||
|
screenblock as u8 + 16,
|
||||||
|
priority,
|
||||||
|
size,
|
||||||
|
default_bg_center,
|
||||||
|
);
|
||||||
|
|
||||||
|
affine.set(new_background, true);
|
||||||
|
|
||||||
|
MapLoan::new(
|
||||||
|
bg,
|
||||||
|
new_background as u8,
|
||||||
|
screenblock as u8,
|
||||||
|
num_screenblocks as u8,
|
||||||
|
self.affine(),
|
||||||
|
self.screenblocks(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use super::{MapLoan, RegularBackgroundSize, RegularMap, RegularTiledMode, TiledMode};
|
||||||
use crate::{
|
use crate::{
|
||||||
bitarray::Bitarray,
|
bitarray::Bitarray,
|
||||||
display::{set_graphics_mode, DisplayMode, Priority},
|
display::{set_graphics_mode, DisplayMode, Priority},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{MapLoan, RegularBackgroundSize, RegularMap};
|
|
||||||
|
|
||||||
pub struct Tiled0 {
|
pub struct Tiled0 {
|
||||||
regular: RefCell<Bitarray<1>>,
|
regular: RefCell<Bitarray<1>>,
|
||||||
screenblocks: RefCell<Bitarray<1>>,
|
screenblocks: RefCell<Bitarray<1>>,
|
||||||
|
@ -27,52 +26,23 @@ impl Tiled0 {
|
||||||
priority: Priority,
|
priority: Priority,
|
||||||
size: RegularBackgroundSize,
|
size: RegularBackgroundSize,
|
||||||
) -> MapLoan<'_, RegularMap> {
|
) -> MapLoan<'_, RegularMap> {
|
||||||
let mut regular = self.regular.borrow_mut();
|
self.regular_background(priority, size)
|
||||||
let new_background = regular.first_zero().unwrap();
|
|
||||||
if new_background >= 4 {
|
|
||||||
panic!("can only have 4 active backgrounds");
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_screenblocks = size.num_screen_blocks();
|
|
||||||
let mut screenblocks = self.screenblocks.borrow_mut();
|
|
||||||
|
|
||||||
let screenblock = find_screenblock_gap(&screenblocks, num_screenblocks);
|
|
||||||
for id in screenblock..(screenblock + num_screenblocks) {
|
|
||||||
screenblocks.set(id, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
let bg = RegularMap::new(new_background as u8, screenblock as u8 + 16, priority, size);
|
|
||||||
|
|
||||||
regular.set(new_background, true);
|
|
||||||
|
|
||||||
MapLoan::new(
|
|
||||||
bg,
|
|
||||||
new_background as u8,
|
|
||||||
screenblock as u8,
|
|
||||||
num_screenblocks as u8,
|
|
||||||
&self.regular,
|
|
||||||
&self.screenblocks,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_screenblock_gap(screenblocks: &Bitarray<1>, gap: usize) -> usize {
|
impl TiledMode for Tiled0 {
|
||||||
let mut candidate = 0;
|
const REGULAR_BACKGROUNDS: usize = 4;
|
||||||
|
const AFFINE_BACKGROUNDS: usize = 0;
|
||||||
|
|
||||||
'outer: while candidate < 16 - gap {
|
fn screenblocks(&self) -> &RefCell<Bitarray<1>> {
|
||||||
let starting_point = candidate;
|
&self.screenblocks
|
||||||
for attempt in starting_point..(starting_point + gap) {
|
|
||||||
if screenblocks.get(attempt) == Some(true) {
|
|
||||||
candidate = attempt + 1;
|
|
||||||
continue 'outer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return candidate;
|
fn regular(&self) -> &RefCell<Bitarray<1>> {
|
||||||
|
&self.regular
|
||||||
}
|
}
|
||||||
|
|
||||||
panic!(
|
fn affine(&self) -> &RefCell<Bitarray<1>> {
|
||||||
"Failed to find screenblock gap of at least {} elements",
|
unimplemented!()
|
||||||
gap
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
47
agb/src/display/tiled/tiled1.rs
Normal file
47
agb/src/display/tiled/tiled1.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use super::TiledMode;
|
||||||
|
use crate::{
|
||||||
|
bitarray::Bitarray,
|
||||||
|
display::{set_graphics_mode, tiled::AFFINE_BG_ID_OFFSET, DisplayMode},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Tiled1 {
|
||||||
|
regular: RefCell<Bitarray<1>>,
|
||||||
|
affine: RefCell<Bitarray<1>>,
|
||||||
|
screenblocks: RefCell<Bitarray<1>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tiled1 {
|
||||||
|
pub(crate) unsafe fn new() -> Self {
|
||||||
|
set_graphics_mode(DisplayMode::Tiled1);
|
||||||
|
|
||||||
|
let affine = RefCell::new(Bitarray::new());
|
||||||
|
for i in 0..AFFINE_BG_ID_OFFSET {
|
||||||
|
affine.borrow_mut().set(i, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
regular: Default::default(),
|
||||||
|
affine,
|
||||||
|
screenblocks: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiledMode for Tiled1 {
|
||||||
|
const REGULAR_BACKGROUNDS: usize = 2;
|
||||||
|
const AFFINE_BACKGROUNDS: usize = 1;
|
||||||
|
|
||||||
|
fn screenblocks(&self) -> &RefCell<Bitarray<1>> {
|
||||||
|
&self.screenblocks
|
||||||
|
}
|
||||||
|
|
||||||
|
fn regular(&self) -> &RefCell<Bitarray<1>> {
|
||||||
|
&self.regular
|
||||||
|
}
|
||||||
|
|
||||||
|
fn affine(&self) -> &RefCell<Bitarray<1>> {
|
||||||
|
&self.affine
|
||||||
|
}
|
||||||
|
}
|
53
agb/src/display/tiled/tiled2.rs
Normal file
53
agb/src/display/tiled/tiled2.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use super::{AffineBackgroundSize, AffineMap, AffineTiledMode, MapLoan, TiledMode};
|
||||||
|
use crate::{
|
||||||
|
bitarray::Bitarray,
|
||||||
|
display::{set_graphics_mode, tiled::AFFINE_BG_ID_OFFSET, DisplayMode, Priority},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Tiled2 {
|
||||||
|
affine: RefCell<Bitarray<1>>,
|
||||||
|
screenblocks: RefCell<Bitarray<1>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tiled2 {
|
||||||
|
pub(crate) unsafe fn new() -> Self {
|
||||||
|
set_graphics_mode(DisplayMode::Tiled2);
|
||||||
|
|
||||||
|
let affine = RefCell::new(Bitarray::new());
|
||||||
|
for i in 0..AFFINE_BG_ID_OFFSET {
|
||||||
|
affine.borrow_mut().set(i, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
affine,
|
||||||
|
screenblocks: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn background(
|
||||||
|
&self,
|
||||||
|
priority: Priority,
|
||||||
|
size: AffineBackgroundSize,
|
||||||
|
) -> MapLoan<'_, AffineMap> {
|
||||||
|
self.affine_background(priority, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TiledMode for Tiled2 {
|
||||||
|
const REGULAR_BACKGROUNDS: usize = 0;
|
||||||
|
const AFFINE_BACKGROUNDS: usize = 2;
|
||||||
|
|
||||||
|
fn screenblocks(&self) -> &RefCell<Bitarray<1>> {
|
||||||
|
&self.screenblocks
|
||||||
|
}
|
||||||
|
|
||||||
|
fn regular(&self) -> &RefCell<Bitarray<1>> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn affine(&self) -> &RefCell<Bitarray<1>> {
|
||||||
|
&self.affine
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ use core::{alloc::Layout, ptr::NonNull};
|
||||||
|
|
||||||
use alloc::{slice, vec::Vec};
|
use alloc::{slice, vec::Vec};
|
||||||
|
|
||||||
|
use crate::display::tiled::Tile;
|
||||||
use crate::{
|
use crate::{
|
||||||
agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd},
|
agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd},
|
||||||
display::palette16,
|
display::palette16,
|
||||||
|
@ -22,18 +23,22 @@ static TILE_ALLOCATOR: BlockAllocator = unsafe {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const TILE_LAYOUT: Layout = unsafe { Layout::from_size_align_unchecked(8 * 8 / 2, 8 * 8 / 2) };
|
const fn layout_of(format: TileFormat) -> Layout {
|
||||||
|
unsafe { Layout::from_size_align_unchecked(format.tile_size(), format.tile_size()) }
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum TileFormat {
|
pub enum TileFormat {
|
||||||
FourBpp,
|
FourBpp,
|
||||||
|
EightBpp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TileFormat {
|
impl TileFormat {
|
||||||
/// Returns the size of the tile in bytes
|
/// Returns the size of the tile in bytes
|
||||||
fn tile_size(self) -> usize {
|
pub(crate) const fn tile_size(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
TileFormat::FourBpp => 8 * 8 / 2,
|
TileFormat::FourBpp => 8 * 8 / 2,
|
||||||
|
TileFormat::EightBpp => 8 * 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,15 +60,50 @@ impl<'a> TileSet<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct TileIndex(u16);
|
pub enum TileIndex {
|
||||||
|
FourBpp(u16),
|
||||||
impl TileIndex {
|
EightBpp(u8),
|
||||||
pub(crate) const fn new(index: usize) -> Self {
|
|
||||||
Self(index as u16)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn index(self) -> u16 {
|
impl TileIndex {
|
||||||
self.0
|
pub(crate) const fn new(index: usize, format: TileFormat) -> Self {
|
||||||
|
match format {
|
||||||
|
TileFormat::FourBpp => Self::FourBpp(index as u16),
|
||||||
|
TileFormat::EightBpp => Self::EightBpp(index as u8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn raw_index(self) -> u16 {
|
||||||
|
match self {
|
||||||
|
TileIndex::FourBpp(x) => x,
|
||||||
|
TileIndex::EightBpp(x) => x as u16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn format(self) -> TileFormat {
|
||||||
|
match self {
|
||||||
|
TileIndex::FourBpp(_) => TileFormat::FourBpp,
|
||||||
|
TileIndex::EightBpp(_) => TileFormat::EightBpp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refcount_key(self) -> usize {
|
||||||
|
match self {
|
||||||
|
TileIndex::FourBpp(x) => x as usize,
|
||||||
|
TileIndex::EightBpp(x) => x as usize * 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Tile> for TileIndex {
|
||||||
|
fn from(tile: Tile) -> Self {
|
||||||
|
tile.tile_index()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for TileIndex {
|
||||||
|
fn from(index: u8) -> TileIndex {
|
||||||
|
TileIndex::new(usize::from(index), TileFormat::EightBpp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +199,7 @@ impl DynamicTile<'_> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn tile_index(&self) -> u16 {
|
pub fn tile_index(&self) -> u16 {
|
||||||
let difference = self.tile_data.as_ptr() as usize - TILE_RAM_START;
|
let difference = self.tile_data.as_ptr() as usize - TILE_RAM_START;
|
||||||
(difference / (8 * 8 / 2)) as u16
|
(difference / TileFormat::FourBpp.tile_size()) as u16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,24 +222,27 @@ impl VRamManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index_from_reference(reference: TileReference) -> usize {
|
fn index_from_reference(reference: TileReference, format: TileFormat) -> TileIndex {
|
||||||
let difference = reference.0.as_ptr() as usize - TILE_RAM_START;
|
let difference = reference.0.as_ptr() as usize - TILE_RAM_START;
|
||||||
difference / (8 * 8 / 2)
|
TileIndex::new(difference / format.tile_size(), format)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reference_from_index(index: TileIndex) -> TileReference {
|
fn reference_from_index(index: TileIndex) -> TileReference {
|
||||||
let ptr = (index.index() * (8 * 8 / 2)) as usize + TILE_RAM_START;
|
let ptr = (index.raw_index() as usize * index.format().tile_size()) + TILE_RAM_START;
|
||||||
TileReference(NonNull::new(ptr as *mut _).unwrap())
|
TileReference(NonNull::new(ptr as *mut _).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_dynamic_tile<'a>(&mut self) -> DynamicTile<'a> {
|
pub fn new_dynamic_tile<'a>(&mut self) -> DynamicTile<'a> {
|
||||||
|
// TODO: format param?
|
||||||
let tile_format = TileFormat::FourBpp;
|
let tile_format = TileFormat::FourBpp;
|
||||||
let new_reference: NonNull<u32> =
|
let new_reference: NonNull<u32> = unsafe { TILE_ALLOCATOR.alloc(layout_of(tile_format)) }
|
||||||
unsafe { TILE_ALLOCATOR.alloc(TILE_LAYOUT) }.unwrap().cast();
|
.unwrap()
|
||||||
|
.cast();
|
||||||
let tile_reference = TileReference(new_reference);
|
let tile_reference = TileReference(new_reference);
|
||||||
|
|
||||||
let index = Self::index_from_reference(tile_reference);
|
let index = Self::index_from_reference(tile_reference, tile_format);
|
||||||
|
let key = index.refcount_key();
|
||||||
|
|
||||||
let tiles = unsafe {
|
let tiles = unsafe {
|
||||||
slice::from_raw_parts_mut(TILE_RAM_START as *mut u8, 1024 * tile_format.tile_size())
|
slice::from_raw_parts_mut(TILE_RAM_START as *mut u8, 1024 * tile_format.tile_size())
|
||||||
|
@ -208,23 +251,21 @@ impl VRamManager {
|
||||||
let tile_set = TileSet::new(tiles, tile_format);
|
let tile_set = TileSet::new(tiles, tile_format);
|
||||||
|
|
||||||
self.tile_set_to_vram.insert(
|
self.tile_set_to_vram.insert(
|
||||||
TileInTileSetReference::new(&tile_set, index as u16),
|
TileInTileSetReference::new(&tile_set, index.raw_index()),
|
||||||
tile_reference,
|
tile_reference,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.reference_counts.resize(
|
self.reference_counts
|
||||||
self.reference_counts.len().max(index + 1),
|
.resize(self.reference_counts.len().max(key + 1), Default::default());
|
||||||
Default::default(),
|
self.reference_counts[key] =
|
||||||
);
|
TileReferenceCount::new(TileInTileSetReference::new(&tile_set, index.raw_index()));
|
||||||
self.reference_counts[index] =
|
|
||||||
TileReferenceCount::new(TileInTileSetReference::new(&tile_set, index as u16));
|
|
||||||
|
|
||||||
DynamicTile {
|
DynamicTile {
|
||||||
tile_data: unsafe {
|
tile_data: unsafe {
|
||||||
slice::from_raw_parts_mut(
|
slice::from_raw_parts_mut(
|
||||||
tiles
|
tiles
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
.add((index * tile_format.tile_size()) as usize)
|
.add(index.raw_index() as usize * tile_format.tile_size())
|
||||||
.cast(),
|
.cast(),
|
||||||
tile_format.tile_size() / core::mem::size_of::<u32>(),
|
tile_format.tile_size() / core::mem::size_of::<u32>(),
|
||||||
)
|
)
|
||||||
|
@ -238,8 +279,9 @@ impl VRamManager {
|
||||||
let pointer = NonNull::new(dynamic_tile.tile_data.as_mut_ptr() as *mut _).unwrap();
|
let pointer = NonNull::new(dynamic_tile.tile_data.as_mut_ptr() as *mut _).unwrap();
|
||||||
let tile_reference = TileReference(pointer);
|
let tile_reference = TileReference(pointer);
|
||||||
|
|
||||||
let tile_index = Self::index_from_reference(tile_reference);
|
// TODO: dynamic_tile.format?
|
||||||
self.remove_tile(TileIndex::new(tile_index));
|
let tile_index = Self::index_from_reference(tile_reference, TileFormat::FourBpp);
|
||||||
|
self.remove_tile(tile_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_tile(&mut self, tile_set: &TileSet<'_>, tile: u16) -> TileIndex {
|
pub(crate) fn add_tile(&mut self, tile_set: &TileSet<'_>, tile: u16) -> TileIndex {
|
||||||
|
@ -248,37 +290,39 @@ impl VRamManager {
|
||||||
.get(&TileInTileSetReference::new(tile_set, tile));
|
.get(&TileInTileSetReference::new(tile_set, tile));
|
||||||
|
|
||||||
if let Some(reference) = reference {
|
if let Some(reference) = reference {
|
||||||
let index = Self::index_from_reference(*reference);
|
let tile_index = Self::index_from_reference(*reference, tile_set.format);
|
||||||
self.reference_counts[index].increment_reference_count();
|
let key = tile_index.refcount_key();
|
||||||
return TileIndex::new(index);
|
self.reference_counts[key].increment_reference_count();
|
||||||
|
return tile_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_reference: NonNull<u32> =
|
let new_reference: NonNull<u32> =
|
||||||
unsafe { TILE_ALLOCATOR.alloc(TILE_LAYOUT) }.unwrap().cast();
|
unsafe { TILE_ALLOCATOR.alloc(layout_of(tile_set.format)) }
|
||||||
|
.unwrap()
|
||||||
|
.cast();
|
||||||
let tile_reference = TileReference(new_reference);
|
let tile_reference = TileReference(new_reference);
|
||||||
|
|
||||||
self.copy_tile_to_location(tile_set, tile, tile_reference);
|
self.copy_tile_to_location(tile_set, tile, tile_reference);
|
||||||
|
|
||||||
let index = Self::index_from_reference(tile_reference);
|
let index = Self::index_from_reference(tile_reference, tile_set.format);
|
||||||
|
let key = index.refcount_key();
|
||||||
|
|
||||||
self.tile_set_to_vram
|
self.tile_set_to_vram
|
||||||
.insert(TileInTileSetReference::new(tile_set, tile), tile_reference);
|
.insert(TileInTileSetReference::new(tile_set, tile), tile_reference);
|
||||||
|
|
||||||
self.reference_counts.resize(
|
self.reference_counts
|
||||||
self.reference_counts.len().max(index + 1),
|
.resize(self.reference_counts.len().max(key + 1), Default::default());
|
||||||
Default::default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.reference_counts[index] =
|
self.reference_counts[key] =
|
||||||
TileReferenceCount::new(TileInTileSetReference::new(tile_set, tile));
|
TileReferenceCount::new(TileInTileSetReference::new(tile_set, tile));
|
||||||
|
|
||||||
TileIndex::new(index)
|
index
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn remove_tile(&mut self, tile_index: TileIndex) {
|
pub(crate) fn remove_tile(&mut self, tile_index: TileIndex) {
|
||||||
let index = tile_index.index() as usize;
|
let key = tile_index.refcount_key();
|
||||||
|
|
||||||
let new_reference_count = self.reference_counts[index].decrement_reference_count();
|
let new_reference_count = self.reference_counts[key].decrement_reference_count();
|
||||||
|
|
||||||
if new_reference_count != 0 {
|
if new_reference_count != 0 {
|
||||||
return;
|
return;
|
||||||
|
@ -289,23 +333,26 @@ impl VRamManager {
|
||||||
|
|
||||||
pub(crate) fn gc(&mut self) {
|
pub(crate) fn gc(&mut self) {
|
||||||
for tile_index in self.indices_to_gc.drain(..) {
|
for tile_index in self.indices_to_gc.drain(..) {
|
||||||
let index = tile_index.index() as usize;
|
let key = tile_index.refcount_key() as usize;
|
||||||
if self.reference_counts[index].current_count() > 0 {
|
if self.reference_counts[key].current_count() > 0 {
|
||||||
continue; // it has since been added back
|
continue; // it has since been added back
|
||||||
}
|
}
|
||||||
|
|
||||||
let tile_reference = Self::reference_from_index(tile_index);
|
let tile_reference = Self::reference_from_index(tile_index);
|
||||||
unsafe {
|
unsafe {
|
||||||
TILE_ALLOCATOR.dealloc_no_normalise(tile_reference.0.cast().as_ptr(), TILE_LAYOUT);
|
TILE_ALLOCATOR.dealloc_no_normalise(
|
||||||
|
tile_reference.0.cast().as_ptr(),
|
||||||
|
layout_of(tile_index.format()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tile_ref = self.reference_counts[index]
|
let tile_ref = self.reference_counts[key]
|
||||||
.tile_in_tile_set
|
.tile_in_tile_set
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.tile_set_to_vram.remove(tile_ref);
|
self.tile_set_to_vram.remove(tile_ref);
|
||||||
self.reference_counts[index].clear();
|
self.reference_counts[key].clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{
|
use super::{
|
||||||
bitmap3::Bitmap3,
|
bitmap3::Bitmap3,
|
||||||
bitmap4::Bitmap4,
|
bitmap4::Bitmap4,
|
||||||
tiled::{Tiled0, VRamManager},
|
tiled::{Tiled0, Tiled1, Tiled2, VRamManager},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The video struct controls access to the video hardware.
|
/// The video struct controls access to the video hardware.
|
||||||
|
@ -26,4 +26,14 @@ impl Video {
|
||||||
pub fn tiled0(&mut self) -> (Tiled0, VRamManager) {
|
pub fn tiled0(&mut self) -> (Tiled0, VRamManager) {
|
||||||
(unsafe { Tiled0::new() }, VRamManager::new())
|
(unsafe { Tiled0::new() }, VRamManager::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tiled 1 mode provides 2 regular tiled backgrounds and 1 affine tiled background
|
||||||
|
pub fn tiled1(&mut self) -> (Tiled1, VRamManager) {
|
||||||
|
(unsafe { Tiled1::new() }, VRamManager::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tiled 2 mode provides 2 affine tiled backgrounds
|
||||||
|
pub fn tiled2(&mut self) -> (Tiled2, VRamManager) {
|
||||||
|
(unsafe { Tiled2::new() }, VRamManager::new())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
#![feature(alloc_error_handler)]
|
#![feature(alloc_error_handler)]
|
||||||
#![feature(allocator_api)]
|
#![feature(allocator_api)]
|
||||||
#![feature(asm_const)]
|
#![feature(asm_const)]
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![allow(incomplete_features)] // generic_const_exprs, used for bg mode traits
|
||||||
|
#![feature(generic_const_exprs)]
|
||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
#![deny(clippy::must_use_candidate)]
|
#![deny(clippy::must_use_candidate)]
|
||||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||||
|
@ -80,7 +83,7 @@
|
||||||
/// #
|
/// #
|
||||||
/// use agb::{
|
/// use agb::{
|
||||||
/// display::{
|
/// display::{
|
||||||
/// tiled::{RegularBackgroundSize, TileFormat, TileSet, TileSetting, Tiled0, VRamManager},
|
/// tiled::{RegularBackgroundSize, TileFormat, TileSet, TileSetting, Tiled0, TiledMap, VRamManager},
|
||||||
/// Priority,
|
/// Priority,
|
||||||
/// },
|
/// },
|
||||||
/// include_gfx,
|
/// include_gfx,
|
||||||
|
|
|
@ -16,9 +16,11 @@ impl<T> MemoryMapped<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&self, val: T) {
|
pub fn set(&self, val: T) {
|
||||||
|
if core::mem::size_of::<T>() != 0 {
|
||||||
unsafe { self.address.write_volatile(val) }
|
unsafe { self.address.write_volatile(val) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> MemoryMapped<T>
|
impl<T> MemoryMapped<T>
|
||||||
where
|
where
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
use agb_fixnum::Vector2D;
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
// use crate::display::object::AffineMatrixAttributes;
|
use crate::display::object::AffineMatrixAttributes;
|
||||||
|
use crate::fixnum::Num;
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
|
||||||
|
@ -136,55 +139,103 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn affine_matrix(
|
#[repr(C)]
|
||||||
// x_scale: Num<i16, 8>,
|
pub struct BgAffineSetData {
|
||||||
// y_scale: Num<i16, 8>,
|
pub matrix: AffineMatrixAttributes,
|
||||||
// rotation: u8,
|
pub position: Vector2D<Num<i32, 8>>,
|
||||||
// ) -> AffineMatrixAttributes {
|
}
|
||||||
// let mut result = AffineMatrixAttributes {
|
impl Default for BgAffineSetData {
|
||||||
// p_a: 0,
|
fn default() -> Self {
|
||||||
// p_b: 0,
|
Self {
|
||||||
// p_c: 0,
|
matrix: AffineMatrixAttributes::default(),
|
||||||
// p_d: 0,
|
position: (0, 0).into(),
|
||||||
// };
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// #[allow(dead_code)]
|
/// `rotation` is in revolutions.
|
||||||
// #[repr(C, packed)]
|
#[must_use]
|
||||||
// struct Input {
|
pub fn bg_affine_matrix(
|
||||||
// x_scale: i16,
|
bg_center: Vector2D<Num<i32, 8>>,
|
||||||
// y_scale: i16,
|
display_center: Vector2D<i16>,
|
||||||
// rotation: u16,
|
scale: Vector2D<Num<i16, 8>>,
|
||||||
// }
|
rotation: Num<u8, 8>,
|
||||||
|
) -> BgAffineSetData {
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct Input {
|
||||||
|
bg_center: Vector2D<Num<i32, 8>>,
|
||||||
|
display_center: Vector2D<i16>,
|
||||||
|
scale: Vector2D<Num<i16, 8>>,
|
||||||
|
rotation: u16,
|
||||||
|
}
|
||||||
|
|
||||||
// let input = Input {
|
let input = Input {
|
||||||
// y_scale: x_scale.to_raw(),
|
bg_center,
|
||||||
// x_scale: y_scale.to_raw(),
|
display_center,
|
||||||
// rotation: rotation as u16,
|
scale,
|
||||||
// };
|
rotation: u16::from(rotation.to_raw()) << 8,
|
||||||
|
};
|
||||||
|
|
||||||
// unsafe {
|
let mut output = MaybeUninit::uninit();
|
||||||
// asm!("swi 0x0F",
|
|
||||||
// in("r0") &input as *const Input as usize,
|
|
||||||
// in("r1") &mut result as *mut AffineMatrixAttributes as usize,
|
|
||||||
// in("r2") 1,
|
|
||||||
// in("r3") 2,
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// result
|
unsafe {
|
||||||
// }
|
asm!(
|
||||||
|
"swi {SWI}",
|
||||||
|
SWI = const { swi_map(0x0E) },
|
||||||
|
in("r0") &input as *const Input,
|
||||||
|
in("r1") output.as_mut_ptr(),
|
||||||
|
in("r2") 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
unsafe { output.assume_init() }
|
||||||
// mod tests {
|
}
|
||||||
// use super::*;
|
|
||||||
|
|
||||||
// #[test_case]
|
/// `rotation` is in revolutions.
|
||||||
// fn affine(_gba: &mut crate::Gba) {
|
#[must_use]
|
||||||
// // expect identity matrix
|
pub fn obj_affine_matrix(
|
||||||
// let one: Num<i16, 8> = 1.into();
|
scale: Vector2D<Num<i16, 8>>,
|
||||||
|
rotation: Num<u8, 8>,
|
||||||
|
) -> AffineMatrixAttributes {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct Input {
|
||||||
|
scale: Vector2D<Num<i16, 8>>,
|
||||||
|
rotation: u16,
|
||||||
|
}
|
||||||
|
|
||||||
// let aff = affine_matrix(one, one, 0);
|
let input = Input {
|
||||||
// assert_eq!(aff.p_a, one.to_raw());
|
scale,
|
||||||
// assert_eq!(aff.p_d, one.to_raw());
|
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 super::*;
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn affine(_gba: &mut crate::Gba) {
|
||||||
|
// expect identity matrix
|
||||||
|
let one: Num<i16, 8> = 1.into();
|
||||||
|
|
||||||
|
let aff = obj_affine_matrix((one, one).into(), Num::default());
|
||||||
|
assert_eq!(aff.p_a, one);
|
||||||
|
assert_eq!(aff.p_d, one);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use agb::{
|
use agb::{
|
||||||
display::tiled::{RegularMap, TileFormat, TileSet, TileSetting, VRamManager},
|
display::tiled::{RegularMap, TileFormat, TileSet, TileSetting, TiledMap, VRamManager},
|
||||||
include_gfx, rng,
|
include_gfx, rng,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::sfx::Sfx;
|
||||||
use crate::{
|
use crate::{
|
||||||
graphics::SELECT_BOX, level_generation::generate_attack, Agb, EnemyAttackType, Face, PlayerDice,
|
graphics::SELECT_BOX, level_generation::generate_attack, Agb, EnemyAttackType, Face, PlayerDice,
|
||||||
};
|
};
|
||||||
use agb::display::tiled::RegularMap;
|
use agb::display::tiled::{RegularMap, TiledMap};
|
||||||
use agb::{hash_map::HashMap, input::Button};
|
use agb::{hash_map::HashMap, input::Button};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use agb::{
|
||||||
display::{
|
display::{
|
||||||
object::{Object, ObjectController},
|
object::{Object, ObjectController},
|
||||||
palette16::Palette16,
|
palette16::Palette16,
|
||||||
tiled::{RegularMap, TileSet, TileSetting},
|
tiled::{RegularMap, TileSet, TileSetting, TiledMap},
|
||||||
HEIGHT, WIDTH,
|
HEIGHT, WIDTH,
|
||||||
},
|
},
|
||||||
include_gfx,
|
include_gfx,
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
use agb::display;
|
use agb::display;
|
||||||
use agb::display::object::ObjectController;
|
use agb::display::object::ObjectController;
|
||||||
use agb::display::tiled::VRamManager;
|
use agb::display::tiled::{TiledMap, VRamManager};
|
||||||
use agb::display::Priority;
|
use agb::display::Priority;
|
||||||
use agb::interrupt::VBlank;
|
use agb::interrupt::VBlank;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use agb::display::{
|
use agb::display::{
|
||||||
tiled::{RegularMap, TileSet, TileSetting, VRamManager},
|
tiled::{RegularMap, TileSet, TileSetting, TiledMap, VRamManager},
|
||||||
HEIGHT, WIDTH,
|
HEIGHT, WIDTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ use agb::{
|
||||||
object::{Graphics, Object, ObjectController, Tag, TagMap},
|
object::{Graphics, Object, ObjectController, Tag, TagMap},
|
||||||
tiled::{
|
tiled::{
|
||||||
InfiniteScrolledMap, PartialUpdateStatus, RegularBackgroundSize, TileFormat, TileSet,
|
InfiniteScrolledMap, PartialUpdateStatus, RegularBackgroundSize, TileFormat, TileSet,
|
||||||
TileSetting, VRamManager,
|
TileSetting, TiledMap, VRamManager,
|
||||||
},
|
},
|
||||||
Priority, HEIGHT, WIDTH,
|
Priority, HEIGHT, WIDTH,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::sfx::MusicBox;
|
use super::sfx::MusicBox;
|
||||||
use agb::{
|
use agb::{
|
||||||
display::tiled::{RegularMap, TileFormat, TileSet, TileSetting, VRamManager},
|
display::tiled::{RegularMap, TiledMap, TileFormat, TileSet, TileSetting, VRamManager},
|
||||||
sound::mixer::Mixer,
|
sound::mixer::Mixer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue