From 0271da6d431c024eefdcac1929ece0c3353914c8 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 18:50:35 +0100 Subject: [PATCH 01/14] add a no game --- agb/gfx/square.aseprite | Bin 0 -> 562 bytes agb/src/display/object/sprites/sprite.rs | 1 + agb/src/lib.rs | 5 + agb/src/no_game.rs | 208 +++++++++++++++++++++++ template/src/main.rs | 14 +- 5 files changed, 215 insertions(+), 13 deletions(-) create mode 100644 agb/gfx/square.aseprite create mode 100644 agb/src/no_game.rs diff --git a/agb/gfx/square.aseprite b/agb/gfx/square.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..e2cf234ee837e2f4276825db54afabb2ca42b25c GIT binary patch literal 562 zcmcJN!7Bt&9LIl)X&t#Jr+rD{WM&d=wx*Pebvp z7Z>HAIPC`AOH%s;(@O!;azj?3T_w%ObHi$eN#H5*_Bqky=%)esIEJ_RG zKkAs%KVGdU)gqvc8COwcUED2wyHY&;i zuWi757{ZBc1}+Vi;Gyfn>UaQeR!?DhYXp|&Y#nleCiCAF4VT~s=;Mw!)+pnPET(AU zh#+>T;e`}N=olBRu|R=*!bv8UOae(GDq)gTBm6BCBrc=VE3*sA>EXjtwJpt~sFisZ R%e#T^zhB!$NA_F2^KarWjduV5 literal 0 HcmV?d00001 diff --git a/agb/src/display/object/sprites/sprite.rs b/agb/src/display/object/sprites/sprite.rs index 6751c47d..e41d0d1f 100644 --- a/agb/src/display/object/sprites/sprite.rs +++ b/agb/src/display/object/sprites/sprite.rs @@ -94,6 +94,7 @@ macro_rules! align_bytes { #[macro_export] macro_rules! include_aseprite { ($($aseprite_path: expr),*) => {{ + #[allow(unused_imports)] use $crate::display::object::{Size, Sprite, Tag, TagMap, Graphics}; use $crate::display::palette16::Palette16; use $crate::align_bytes; diff --git a/agb/src/lib.rs b/agb/src/lib.rs index cda67dae..073971cf 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -165,6 +165,11 @@ pub mod syscall; /// Interactions with the internal timers pub mod timer; +mod no_game; + +/// Default game +pub use no_game::no_game; + pub(crate) mod arena; pub use {agb_alloc::ExternalAllocator, agb_alloc::InternalAllocator}; diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs new file mode 100644 index 00000000..87ca2cb5 --- /dev/null +++ b/agb/src/no_game.rs @@ -0,0 +1,208 @@ +//! The no game screen is what is displayed if there isn't a game made yet. + +use agb_fixnum::{num, Num, Vector2D}; +use alloc::vec; +use alloc::vec::Vec; + +use crate::{ + display::{ + object::{OamIterator, ObjectUnmanaged, Sprite}, + palette16::Palette16, + HEIGHT, WIDTH, + }, + include_aseprite, + interrupt::VBlank, +}; + +const SQUARE: &Sprite = &include_aseprite!("gfx/square.aseprite").sprites()[0]; + +fn letters() -> Vec>>> { + vec![ + // N + vec![ + (num!(0.), num!(0.)).into(), + (num!(1.), num!(1.)).into(), + (num!(2.), num!(2.)).into(), + (num!(3.), num!(3.)).into(), + (num!(0.), num!(1.)).into(), + (num!(0.), num!(2.)).into(), + (num!(0.), num!(3.)).into(), + (num!(3.), num!(0.)).into(), + (num!(3.), num!(1.)).into(), + (num!(3.), num!(2.)).into(), + (num!(3.), num!(3.)).into(), + ], + // O + vec![ + (num!(0.), num!(0.)).into(), + (num!(0.), num!(1.)).into(), + (num!(0.), num!(2.)).into(), + (num!(0.), num!(3.)).into(), + (num!(1.), num!(3.)).into(), + (num!(2.), num!(3.)).into(), + (num!(3.), num!(3.)).into(), + (num!(3.), num!(2.)).into(), + (num!(3.), num!(1.)).into(), + (num!(3.), num!(0.)).into(), + (num!(2.), num!(0.)).into(), + (num!(1.), num!(0.)).into(), + ], + // G + vec![ + (num!(3.), num!(0.)).into(), + (num!(2.), num!(0.)).into(), + (num!(1.), num!(0.)).into(), + (num!(0.), num!(0.)).into(), + (num!(0.), num!(1.)).into(), + (num!(0.), num!(2.)).into(), + (num!(0.), num!(3.)).into(), + (num!(1.), num!(3.)).into(), + (num!(2.), num!(3.)).into(), + (num!(3.), num!(3.)).into(), + (num!(3.), num!(2.25)).into(), + (num!(3.), num!(1.5)).into(), + (num!(2.), num!(1.5)).into(), + ], + // A + vec![ + (num!(0.), num!(0.)).into(), + (num!(0.), num!(1.)).into(), + (num!(0.), num!(2.)).into(), + (num!(0.), num!(3.)).into(), + (num!(3.), num!(3.)).into(), + (num!(3.), num!(2.)).into(), + (num!(3.), num!(1.)).into(), + (num!(3.), num!(0.)).into(), + (num!(2.), num!(0.)).into(), + (num!(1.), num!(0.)).into(), + (num!(1.), num!(1.5)).into(), + (num!(2.), num!(1.5)).into(), + ], + // M + vec![ + (num!(0.), num!(0.)).into(), + (num!(0.), num!(1.)).into(), + (num!(0.), num!(2.)).into(), + (num!(0.), num!(3.)).into(), + (num!(3.), num!(3.)).into(), + (num!(3.), num!(2.)).into(), + (num!(3.), num!(1.)).into(), + (num!(3.), num!(0.)).into(), + (num!(1.5), num!(1.5)).into(), + (num!(0.5), num!(0.5)).into(), + (num!(2.5), num!(0.5)).into(), + (num!(1.), num!(1.)).into(), + (num!(2.), num!(1.)).into(), + ], + // E + vec![ + (num!(0.), num!(0.)).into(), + (num!(0.), num!(1.)).into(), + (num!(0.), num!(2.)).into(), + (num!(0.), num!(3.)).into(), + (num!(1.), num!(3.)).into(), + (num!(2.), num!(3.)).into(), + (num!(3.), num!(3.)).into(), + (num!(3.), num!(0.)).into(), + (num!(2.), num!(0.)).into(), + (num!(1.), num!(0.)).into(), + (num!(1.), num!(1.5)).into(), + (num!(2.), num!(1.5)).into(), + ], + ] +} + +trait Renderable { + fn render(&self, slots: &mut OamIterator) -> Option<()>; +} + +impl Renderable for ObjectUnmanaged { + fn render(&self, slots: &mut OamIterator) -> Option<()> { + slots.next()?.set(self); + Some(()) + } +} + +impl Renderable for &[T] { + fn render(&self, slots: &mut OamIterator) -> Option<()> { + for r in self.iter() { + r.render(slots)?; + } + + Some(()) + } +} + +pub fn no_game(mut gba: crate::Gba) -> ! { + let (mut oam, mut loader) = gba.display.object.get_unmanaged(); + + let square = loader.get_vram_sprite(SQUARE); + + let mut letter_positons = Vec::new(); + + let square_positions = { + let mut s = letters(); + for letter in s.iter_mut() { + letter.sort_by_key(|a| a.manhattan_distance()); + } + s + }; + for (letter_idx, letter_parts) in square_positions.iter().enumerate() { + for part in letter_parts.iter() { + let position = part + .hadamard((8, 10).into()) + .hadamard((num!(3.) / 2, num!(3.) / 2).into()); + + let letter_pos = Vector2D::new( + 60 * (1 + letter_idx as i32 - ((letter_idx >= 2) as i32 * 3)), + 70 * ((letter_idx >= 2) as i32), + ); + + letter_positons.push(position + letter_pos.change_base()); + } + } + + let bottom_right = letter_positons + .iter() + .copied() + .max_by_key(|x| x.manhattan_distance()) + .unwrap(); + + let difference = (Vector2D::new(WIDTH - 8, HEIGHT - 8).change_base() - bottom_right) / 2; + + for pos in letter_positons.iter_mut() { + *pos += difference; + } + + let mut time: Num = num!(0.); + let time_delta: Num = num!(0.025); + + let (_background, mut vram) = gba.display.video.tiled0(); + + vram.set_background_palettes(&[Palette16::new([u16::MAX; 16])]); + + let vblank = VBlank::get(); + + loop { + time += time_delta; + time %= 1; + let letters: Vec = letter_positons + .iter() + .enumerate() + .map(|(idx, position)| { + let time = time + Num::::new(idx as i32) / 128; + *position + Vector2D::new(time.sin(), time.cos()) * 10 + }) + .map(|pos| { + let mut obj = ObjectUnmanaged::new(square.clone()); + obj.show().set_position(pos.floor()); + obj + }) + .collect(); + + vblank.wait_for_vblank(); + for (obj, slot) in letters.iter().zip(oam.iter()) { + slot.set(obj); + } + } +} diff --git a/template/src/main.rs b/template/src/main.rs index 84683dc9..e58c652d 100644 --- a/template/src/main.rs +++ b/template/src/main.rs @@ -14,22 +14,10 @@ #![cfg_attr(test, reexport_test_harness_main = "test_main")] #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] -use agb::{display, syscall}; - // The main function must take 1 arguments and never return. The agb::entry decorator // ensures that everything is in order. `agb` will call this after setting up the stack // and interrupt handlers correctly. It will also handle creating the `Gba` struct for you. #[agb::entry] fn main(mut gba: agb::Gba) -> ! { - let mut bitmap = gba.display.video.bitmap3(); - - for x in 0..display::WIDTH { - let y = syscall::sqrt(x << 6); - let y = (display::HEIGHT - y).clamp(0, display::HEIGHT - 1); - bitmap.draw_point(x, y, 0x001F); - } - - loop { - syscall::halt(); - } + agb::no_game(gba); } From 1d7cc04cbf5251bf298f5e87ed1744e36241d4b9 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 19:11:39 +0100 Subject: [PATCH 02/14] more colours --- agb/gfx/square.aseprite | Bin 562 -> 735 bytes agb/src/no_game.rs | 15 ++++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/agb/gfx/square.aseprite b/agb/gfx/square.aseprite index e2cf234ee837e2f4276825db54afabb2ca42b25c..75f9581ea96b78a12e9dd331ab13114004aab32f 100644 GIT binary patch literal 735 zcmcJNPbkAt0LS0{Wk)VTX|Gm7lBHx6c9oN>3*o{+;b13Rv|=eY`E%l=QLH&QX^nEw z2nT;xPB!&xQuG^pK5tW&q8z-}`}BVA^?u*a`*UX%BC&24PO20ZA;bXft1zYqMKSe1 z$~J#D(~exKgMd<6k<3LF#9h)am-2JORWKujGVT?ON1(A0fG1BcFmx1$fzS@j-k9)W z>llWoHla#BXhx&(aW1I)Dg`r{LwI$$2v5y7xNB(kY{%eXVg#=2*F*n;8>V`@;Z2(r zF4a{+BY6&Y!XfCcl5ox241=CVIA1MbXa5+iXlPeM?8UCnUFv zG^#aK3PJ2p!wV^l(7^=}EKneyaFU57lR(mla_J;#D>yq8M3Ly~^AC2zd)v&wFCRUryrvI`s^PYKC bcY38gU|780GJlh8%}S-xC36;QFy{LJK8@8V literal 562 zcmcJN!7Bt&9LIl)X&t#Jr+rD{WM&d=wx*Pebvp z7Z>HAIPC`AOH%s;(@O!;azj?3T_w%ObHi$eN#H5*_Bqky=%)esIEJ_RG zKkAs%KVGdU)gqvc8COwcUED2wyHY&;i zuWi757{ZBc1}+Vi;Gyfn>UaQeR!?DhYXp|&Y#nleCiCAF4VT~s=;Mw!)+pnPET(AU zh#+>T;e`}N=olBRu|R=*!bv8UOae(GDq)gTBm6BCBrc=VE3*sA>EXjtwJpt~sFisZ R%e#T^zhB!$NA_F2^KarWjduV5 diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs index 87ca2cb5..d87412e9 100644 --- a/agb/src/no_game.rs +++ b/agb/src/no_game.rs @@ -14,7 +14,7 @@ use crate::{ interrupt::VBlank, }; -const SQUARE: &Sprite = &include_aseprite!("gfx/square.aseprite").sprites()[0]; +const SQUARES: &[Sprite] = include_aseprite!("gfx/square.aseprite").sprites(); fn letters() -> Vec>>> { vec![ @@ -136,7 +136,10 @@ impl Renderable for &[T] { pub fn no_game(mut gba: crate::Gba) -> ! { let (mut oam, mut loader) = gba.display.object.get_unmanaged(); - let square = loader.get_vram_sprite(SQUARE); + let squares: Vec<_> = SQUARES + .iter() + .map(|sprite| loader.get_vram_sprite(sprite)) + .collect(); let mut letter_positons = Vec::new(); @@ -177,13 +180,14 @@ pub fn no_game(mut gba: crate::Gba) -> ! { let mut time: Num = num!(0.); let time_delta: Num = num!(0.025); - let (_background, mut vram) = gba.display.video.tiled0(); + // let (_background, mut vram) = gba.display.video.tiled0(); - vram.set_background_palettes(&[Palette16::new([u16::MAX; 16])]); + // vram.set_background_palettes(&[Palette16::new([u16::MAX; 16])]); let vblank = VBlank::get(); loop { + let mut rng = crate::rng::RandomNumberGenerator::new(); time += time_delta; time %= 1; let letters: Vec = letter_positons @@ -194,7 +198,8 @@ pub fn no_game(mut gba: crate::Gba) -> ! { *position + Vector2D::new(time.sin(), time.cos()) * 10 }) .map(|pos| { - let mut obj = ObjectUnmanaged::new(square.clone()); + let mut obj = + ObjectUnmanaged::new(squares[rng.gen() as usize % squares.len()].clone()); obj.show().set_position(pos.floor()); obj }) From 54340358bc6c608c98cf9d2138590c4c55a1f1d8 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 19:44:08 +0100 Subject: [PATCH 03/14] remove unused import --- agb/src/no_game.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs index d87412e9..d241bce4 100644 --- a/agb/src/no_game.rs +++ b/agb/src/no_game.rs @@ -7,7 +7,6 @@ use alloc::vec::Vec; use crate::{ display::{ object::{OamIterator, ObjectUnmanaged, Sprite}, - palette16::Palette16, HEIGHT, WIDTH, }, include_aseprite, From 26090499e76b659a1211355936707b3c9cc37105 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 20:31:43 +0100 Subject: [PATCH 04/14] fix image converter bug --- agb-image-converter/src/lib.rs | 4 ++-- agb-image-converter/src/palette16.rs | 19 ++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index c63c4656..c844673a 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -432,8 +432,8 @@ fn palette_tile_data( let mut tile_data = Vec::new(); - for image in images { - add_image_to_tile_data(&mut tile_data, image, optimiser, 0) + for (image_idx, image) in images.iter().enumerate() { + add_image_to_tile_data(&mut tile_data, image, optimiser, image_idx) } let tile_data = collapse_to_4bpp(&tile_data); diff --git a/agb-image-converter/src/palette16.rs b/agb-image-converter/src/palette16.rs index 2d384c69..03ae3a57 100644 --- a/agb-image-converter/src/palette16.rs +++ b/agb-image-converter/src/palette16.rs @@ -138,17 +138,7 @@ impl Palette16Optimiser { while !unsatisfied_palettes.is_empty() { let palette = self.find_maximal_palette_for(&unsatisfied_palettes); - for test_palette in unsatisfied_palettes.clone() { - if test_palette.is_satisfied_by(&palette) { - unsatisfied_palettes.remove(&test_palette); - } - } - - for (i, overall_palette) in self.palettes.iter().enumerate() { - if overall_palette.is_satisfied_by(&palette) { - assignments[i] = optimised_palettes.len(); - } - } + unsatisfied_palettes.retain(|test_palette| !test_palette.is_satisfied_by(&palette)); optimised_palettes.push(palette); @@ -157,6 +147,13 @@ impl Palette16Optimiser { } } + for (i, overall_palette) in self.palettes.iter().enumerate() { + assignments[i] = optimised_palettes + .iter() + .position(|palette| overall_palette.is_satisfied_by(palette)) + .unwrap(); + } + Palette16OptimisationResults { optimised_palettes, assignments, From 13f0eaf5b0c1341000bc37a3a7cc4830cf6f82c6 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 20:31:52 +0100 Subject: [PATCH 05/14] add more colours --- agb/gfx/square.aseprite | Bin 735 -> 4408 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/agb/gfx/square.aseprite b/agb/gfx/square.aseprite index 75f9581ea96b78a12e9dd331ab13114004aab32f..b31eab3a2a4fe9333ee02ab77ed592f1bffca04f 100644 GIT binary patch literal 4408 zcmcJSZ%kWN7{+hiCNoCkVqE4IC0YF7&=_4LV&edleK5{wbY^j6{)wBBKr|AB6^0Gn zb&<$)&FB(VNn8Pu)rCWis_P}wdE!qin$zMU0W)svOHbVzjJ35Gog+^`TG~#kXO*RGEU=BkQ z-WBLEO9@o=(Dxh0>3&;Nl+-0dO-cpS9QH%6h2l_G1C6))lH=5;rRW4*U7d$|s_3eE zlfJVzN`1QICa7Zi78>rP`|rj)P=n^nsr9KE+!%>A(*3f9eASq!PgUzvcxm<`%SpQG z)M`!Cr(&Z%WfzT`6dE^QAzvf?)aRLM;#{S1HmJnM7LE!Zve-=PCJQ+r1SA0bU=Q%1 z4&>ks*dPteU<|-S_l5^t0To1n6D$D|6oC-@01xDV4a@)xw15hn0LdIS0}t2$45$DJ zcmM`S00S5R0Nleme8V(3_WI!!Mhzwv7U2)(;0(6l35MVXRvsrOshc*eEm)LNqJbCTag2KdaYCWh7!8fXUjA zFS2@dyPD@kA9{6vf3=&_t2Nvex#~d@_Cz$k~XwBvn zx&wu(7Ul2*13FHxr7wNfC&sDQt5aT9lnLWquMkxE?%*5?Y+AS`)|J2cLrFgL1FJAE zHzV401cepF4pyQ5&6ujKft0*!yeOTwP z{u%d)3JPRb8}k7hTOJpusdDe+JS)n{=F1`R4O~_pcCqkG8qP0?Ar!XatmL`GF`~Kj zJ=3e>RO(}Mp`^?E&xcu&=%1@k9_$w^Z7e*cpEs$(4wt6`!>o0uU(HoSFD3HCmt#Dz ziepVnb119IG8*Q^6wjBC6ZWM@`^=r`J1}H0&9QD8tW>+ zCJ}#}V--%06wHb8SQcRbib2T zXq_yKh$)zS1jh(xi~2GB%hP-y}~>5+=UC{;#G2K`eZhn6Qwo1WkEQx?k?pwvRqo}Kc_0%mCOgM{a9;I WJJ`z>dsujKmgDOO`@^TE%l`sYq)pTS literal 735 zcmcJNPbkAt0LS0{Wk)VTX|Gm7lBHx6c9oN>3*o{+;b13Rv|=eY`E%l=QLH&QX^nEw z2nT;xPB!&xQuG^pK5tW&q8z-}`}BVA^?u*a`*UX%BC&24PO20ZA;bXft1zYqMKSe1 z$~J#D(~exKgMd<6k<3LF#9h)am-2JORWKujGVT?ON1(A0fG1BcFmx1$fzS@j-k9)W z>llWoHla#BXhx&(aW1I)Dg`r{LwI$$2v5y7xNB(kY{%eXVg#=2*F*n;8>V`@;Z2(r zF4a{+BY6&Y!XfCcl5ox241=CVIA1MbXa5+iXlPeM?8UCnUFv zG^#aK3PJ2p!wV^l(7^=}EKneyaFU57lR(mla_J;#D>yq8M3Ly~^AC2zd)v&wFCRUryrvI`s^PYKC bcY38gU|780GJlh8%}S-xC36;QFy{LJK8@8V From e8479c093b7598d62c7ba0643b2cc619aa80714e Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 22:42:03 +0100 Subject: [PATCH 06/14] add macro to import a palette from a sprite --- agb-image-converter/src/lib.rs | 29 ++++++++++++++++++++++++++++- agb/src/display/palette16.rs | 9 +++++++++ agb/src/lib.rs | 3 +++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index c844673a..26c9a582 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -239,9 +239,36 @@ impl ToTokens for ByteString<'_> { } } +#[proc_macro] +pub fn include_colours_inner(input: TokenStream) -> TokenStream { + let input_filename = parse_macro_input!(input as LitStr); + let input_filename = input_filename.value(); + + let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); + let input_filename = Path::new(&root).join(input_filename); + + let image = Image::load_from_file(Path::new(&input_filename)); + + let mut palette_data = Vec::with_capacity(image.width * image.height); + for y in 0..image.height { + for x in 0..image.width { + palette_data.push(image.colour(x, y).to_rgb15()) + } + } + + let filename = input_filename.to_string_lossy(); + + TokenStream::from(quote! { + { + const _: &[u8] = include_bytes!(#filename); + [#(#palette_data),*] + } + }) +} + #[proc_macro] pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { - let parser = Punctuated::::parse_separated_nonempty; + let parser = Punctuated::::parse_terminated; let parsed = match parser.parse(input) { Ok(e) => e, Err(e) => return e.to_compile_error().into(), diff --git a/agb/src/display/palette16.rs b/agb/src/display/palette16.rs index 7296b5bd..06383f93 100644 --- a/agb/src/display/palette16.rs +++ b/agb/src/display/palette16.rs @@ -27,3 +27,12 @@ impl Palette16 { Layout::new::() } } + +#[macro_export] +macro_rules! include_palette { + ($palette:literal) => { + $crate::include_colours_inner!($palette) + }; +} + +pub use include_palette; diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 073971cf..5849a192 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -104,6 +104,9 @@ pub use agb_image_converter::include_aseprite_inner; #[doc(hidden)] pub use agb_image_converter::include_font as include_font_inner; +#[doc(hidden)] +pub use agb_image_converter::include_colours_inner; + #[macro_export] macro_rules! include_font { ($font_path: literal, $font_size: literal) => {{ From f95d68187b8fd8b08990c112390ae5f9e073c9d1 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 22:45:05 +0100 Subject: [PATCH 07/14] make dynamic sprite better --- agb/src/display/object/sprites/sprite.rs | 7 ++ .../object/sprites/sprite_allocator.rs | 74 +++++++++++++------ 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/agb/src/display/object/sprites/sprite.rs b/agb/src/display/object/sprites/sprite.rs index e41d0d1f..0857e283 100644 --- a/agb/src/display/object/sprites/sprite.rs +++ b/agb/src/display/object/sprites/sprite.rs @@ -368,4 +368,11 @@ impl Size { Size::S32x64 => (32, 64), } } + + #[must_use] + /// Returns the width and height of the size in pixels. + pub const fn to_tiles_width_height(self) -> (usize, usize) { + let wh = self.to_width_height(); + (wh.0 / 8, wh.1 / 8) + } } diff --git a/agb/src/display/object/sprites/sprite_allocator.rs b/agb/src/display/object/sprites/sprite_allocator.rs index ae28f908..1d9c9a00 100644 --- a/agb/src/display/object/sprites/sprite_allocator.rs +++ b/agb/src/display/object/sprites/sprite_allocator.rs @@ -1,6 +1,11 @@ -use core::ptr::NonNull; +use core::{alloc::Allocator, ptr::NonNull}; -use alloc::rc::{Rc, Weak}; +use alloc::{ + alloc::Global, + boxed::Box, + rc::{Rc, Weak}, + vec::Vec, +}; use crate::{ agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd}, @@ -285,34 +290,61 @@ impl Default for SpriteLoader { } /// Sprite data that can be used to create sprites in vram. -pub struct DynamicSprite<'a> { - data: &'a [u8], +pub struct DynamicSprite { + data: Box<[u8], A>, size: Size, } -impl DynamicSprite<'_> { +impl DynamicSprite { #[must_use] - /// Creates a new dynamic sprite from underlying bytes. Note that despite - /// being an array of u8, this must be aligned to at least a 2 byte - /// boundary. - pub fn new(data: &[u8], size: Size) -> DynamicSprite { - let ptr = &data[0] as *const _ as usize; - if ptr % 2 != 0 { - panic!("data is not aligned to a 2 byte boundary"); - } - if data.len() != size.number_of_tiles() * BYTES_PER_TILE_4BPP { - panic!( - "data is not of expected length, got {} expected {}", - data.len(), - size.number_of_tiles() * BYTES_PER_TILE_4BPP - ); - } + /// Creates a new dynamic sprite. + pub fn new(size: Size) -> Self { + Self::new_in(size, Global) + } +} + +impl DynamicSprite { + #[must_use] + /// Creates a new dynamic sprite of a given size in a given allocator. + pub fn new_in(size: Size, allocator: A) -> Self { + let num_bytes = size.number_of_tiles() * BYTES_PER_TILE_4BPP; + let mut data = Vec::with_capacity_in(num_bytes, allocator); + + data.resize(num_bytes, 0); + + let data = data.into_boxed_slice(); + DynamicSprite { data, size } } + /// Set the pixel of a sprite to a given paletted pixel. Panics if the + /// coordinate is out of range of the sprite or if the paletted pixel is + /// greater than 4 bits. + pub fn set_pixel(&mut self, x: usize, y: usize, paletted_pixel: usize) { + assert!(paletted_pixel < 0x10); + + let (sprite_pixel_x, sprite_pixel_y) = self.size.to_width_height(); + assert!(x < sprite_pixel_x, "x too big for sprite size"); + assert!(y < sprite_pixel_y, "y too big for sprite size"); + + let (sprite_tile_x, _) = self.size.to_tiles_width_height(); + + let (adjust_tile_x, adjust_tile_y) = (x / 8, y / 8); + + let tile_number_to_modify = adjust_tile_x + adjust_tile_y * sprite_tile_x; + + let byte_to_modify_in_tile = x / 2 + y * 4; + let byte_to_modify = tile_number_to_modify * BYTES_PER_TILE_4BPP + byte_to_modify_in_tile; + let mut byte = self.data[byte_to_modify]; + let parity = (x & 0b1) * 4; + + byte = (byte & !(0b1111 << parity)) | ((paletted_pixel as u8) << parity); + self.data[byte_to_modify] = byte; + } + /// Tries to copy the sprite to vram to be used to set object sprites. pub fn try_vram(&self, palette: PaletteVram) -> Result { - SpriteVram::new(self.data, self.size, palette) + SpriteVram::new(&self.data, self.size, palette) } #[must_use] From d4314213daebea97a97441b8227fe938b98148fe Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 22:45:26 +0100 Subject: [PATCH 08/14] use palette in no game to save on not useful aseprite file --- agb/gfx/pastel.png | Bin 0 -> 355 bytes agb/gfx/square.aseprite | Bin 4408 -> 0 bytes agb/src/no_game.rs | 65 +++++++++++++++++++++------------------- 3 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 agb/gfx/pastel.png delete mode 100644 agb/gfx/square.aseprite diff --git a/agb/gfx/pastel.png b/agb/gfx/pastel.png new file mode 100644 index 0000000000000000000000000000000000000000..7ece9c36c219e84510bc6ba43d2afc235e2cd61a GIT binary patch literal 355 zcmV-p0i6DcP)dGh6=nuNH~g39ZWsQ=x>c8!^Ygv7*o z>hhx3gt)YVqS2?3>-)&{e3g2H#H4xG>Y}gmqPT>D(TS1k)XC-gxTl1<%88Ndk;(k& zm3D^2l!WTudc*RfxP-6Kf{*LHk@xw;d3Kq!gv9E3yYi``gq66Rg3;=c>;K5qc9o5U zw8VLW>d~U|gt(1@(ZrFy>(a*hmYH^x#Dsb3sG{rgBBCS100009a7bBm000ie000ie z0hKEb8vpWis_P}wdE!qin$zMU0W)svOHbVzjJ35Gog+^`TG~#kXO*RGEU=BkQ z-WBLEO9@o=(Dxh0>3&;Nl+-0dO-cpS9QH%6h2l_G1C6))lH=5;rRW4*U7d$|s_3eE zlfJVzN`1QICa7Zi78>rP`|rj)P=n^nsr9KE+!%>A(*3f9eASq!PgUzvcxm<`%SpQG z)M`!Cr(&Z%WfzT`6dE^QAzvf?)aRLM;#{S1HmJnM7LE!Zve-=PCJQ+r1SA0bU=Q%1 z4&>ks*dPteU<|-S_l5^t0To1n6D$D|6oC-@01xDV4a@)xw15hn0LdIS0}t2$45$DJ zcmM`S00S5R0Nleme8V(3_WI!!Mhzwv7U2)(;0(6l35MVXRvsrOshc*eEm)LNqJbCTag2KdaYCWh7!8fXUjA zFS2@dyPD@kA9{6vf3=&_t2Nvex#~d@_Cz$k~XwBvn zx&wu(7Ul2*13FHxr7wNfC&sDQt5aT9lnLWquMkxE?%*5?Y+AS`)|J2cLrFgL1FJAE zHzV401cepF4pyQ5&6ujKft0*!yeOTwP z{u%d)3JPRb8}k7hTOJpusdDe+JS)n{=F1`R4O~_pcCqkG8qP0?Ar!XatmL`GF`~Kj zJ=3e>RO(}Mp`^?E&xcu&=%1@k9_$w^Z7e*cpEs$(4wt6`!>o0uU(HoSFD3HCmt#Dz ziepVnb119IG8*Q^6wjBC6ZWM@`^=r`J1}H0&9QD8tW>+ zCJ}#}V--%06wHb8SQcRbib2T zXq_yKh$)zS1jh(xi~2GB%hP-y}~>5+=UC{;#G2K`eZhn6Qwo1WkEQx?k?pwvRqo}Kc_0%mCOgM{a9;I WJJ`z>dsujKmgDOO`@^TE%l`sYq)pTS diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs index d241bce4..77920640 100644 --- a/agb/src/no_game.rs +++ b/agb/src/no_game.rs @@ -1,19 +1,18 @@ //! The no game screen is what is displayed if there isn't a game made yet. use agb_fixnum::{num, Num, Vector2D}; -use alloc::vec; use alloc::vec::Vec; +use alloc::{boxed::Box, vec}; +use crate::display::object::{DynamicSprite, PaletteVram, Size, SpriteVram}; +use crate::display::palette16::Palette16; use crate::{ - display::{ - object::{OamIterator, ObjectUnmanaged, Sprite}, - HEIGHT, WIDTH, - }, - include_aseprite, + display::{object::ObjectUnmanaged, HEIGHT, WIDTH}, + include_palette, interrupt::VBlank, }; -const SQUARES: &[Sprite] = include_aseprite!("gfx/square.aseprite").sprites(); +const PALETTE: &[u16] = &include_palette!("gfx/pastel.png"); fn letters() -> Vec>>> { vec![ @@ -111,34 +110,44 @@ fn letters() -> Vec>>> { ] } -trait Renderable { - fn render(&self, slots: &mut OamIterator) -> Option<()>; -} +fn generate_sprites() -> Box<[SpriteVram]> { + let mut sprites = Vec::new(); -impl Renderable for ObjectUnmanaged { - fn render(&self, slots: &mut OamIterator) -> Option<()> { - slots.next()?.set(self); - Some(()) - } -} + // generate palettes -impl Renderable for &[T] { - fn render(&self, slots: &mut OamIterator) -> Option<()> { - for r in self.iter() { - r.render(slots)?; + let palettes: Vec = PALETTE + .chunks(15) + .map(|x| { + core::iter::once(0) + .chain(x.iter().copied()) + .chain(core::iter::repeat(0)) + .take(16) + .collect::>() + }) + .map(|palette| { + let palette = Palette16::new(palette.try_into().unwrap()); + PaletteVram::new(&palette).unwrap() + }) + .collect(); + + // generate sprites + let mut sprite = DynamicSprite::new(Size::S8x8); + for (palette, colour) in (0..PALETTE.len()).map(|x| (x / 15, x % 15)) { + for y in 0..8 { + for x in 0..8 { + sprite.set_pixel(x, y, colour + 1); + } } - - Some(()) + sprites.push(sprite.to_vram(palettes[palette].clone())); } + + sprites.into_boxed_slice() } pub fn no_game(mut gba: crate::Gba) -> ! { let (mut oam, mut loader) = gba.display.object.get_unmanaged(); - let squares: Vec<_> = SQUARES - .iter() - .map(|sprite| loader.get_vram_sprite(sprite)) - .collect(); + let squares = generate_sprites(); let mut letter_positons = Vec::new(); @@ -179,10 +188,6 @@ pub fn no_game(mut gba: crate::Gba) -> ! { let mut time: Num = num!(0.); let time_delta: Num = num!(0.025); - // let (_background, mut vram) = gba.display.video.tiled0(); - - // vram.set_background_palettes(&[Palette16::new([u16::MAX; 16])]); - let vblank = VBlank::get(); loop { From cb8b42d416148a8cb929003655d4698465695636 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 22:53:09 +0100 Subject: [PATCH 09/14] don't use the loader --- agb/src/no_game.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs index 77920640..332dd164 100644 --- a/agb/src/no_game.rs +++ b/agb/src/no_game.rs @@ -145,7 +145,7 @@ fn generate_sprites() -> Box<[SpriteVram]> { } pub fn no_game(mut gba: crate::Gba) -> ! { - let (mut oam, mut loader) = gba.display.object.get_unmanaged(); + let (mut oam, _) = gba.display.object.get_unmanaged(); let squares = generate_sprites(); From 7ae3afab6c283028ac299cf35389ee060a5e4043 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 22:54:22 +0100 Subject: [PATCH 10/14] add example for no_game --- agb/examples/no_game.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 agb/examples/no_game.rs diff --git a/agb/examples/no_game.rs b/agb/examples/no_game.rs new file mode 100644 index 00000000..38aa29b5 --- /dev/null +++ b/agb/examples/no_game.rs @@ -0,0 +1,7 @@ +#![no_std] +#![no_main] + +#[agb::entry] +fn main(gba: agb::Gba) -> ! { + agb::no_game(gba); +} From 83c511a186cb7506f152fc789be31fd1b248577b Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 6 May 2023 23:59:47 +0100 Subject: [PATCH 11/14] neaten the letter M --- agb/src/no_game.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs index 332dd164..4e782549 100644 --- a/agb/src/no_game.rs +++ b/agb/src/no_game.rs @@ -87,10 +87,8 @@ fn letters() -> Vec>>> { (num!(3.), num!(1.)).into(), (num!(3.), num!(0.)).into(), (num!(1.5), num!(1.5)).into(), - (num!(0.5), num!(0.5)).into(), - (num!(2.5), num!(0.5)).into(), - (num!(1.), num!(1.)).into(), - (num!(2.), num!(1.)).into(), + (num!(0.75), num!(0.75)).into(), + (num!(2.25), num!(0.75)).into(), ], // E vec![ From 64308a993c6359f1955b2df33fc34c87d324d622 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 7 May 2023 00:47:38 +0100 Subject: [PATCH 12/14] use mag squared --- agb/src/no_game.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs index 4e782549..8e085af9 100644 --- a/agb/src/no_game.rs +++ b/agb/src/no_game.rs @@ -152,7 +152,7 @@ pub fn no_game(mut gba: crate::Gba) -> ! { let square_positions = { let mut s = letters(); for letter in s.iter_mut() { - letter.sort_by_key(|a| a.manhattan_distance()); + letter.sort_by_key(|a| a.magnitude_squared()); } s }; From 1cbc371d3e91783b2572ee90b7dab2c620eb2183 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 7 May 2023 23:41:17 +0100 Subject: [PATCH 13/14] go through colours in order rather than randomly --- agb/src/no_game.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/agb/src/no_game.rs b/agb/src/no_game.rs index 8e085af9..ed82f880 100644 --- a/agb/src/no_game.rs +++ b/agb/src/no_game.rs @@ -189,7 +189,6 @@ pub fn no_game(mut gba: crate::Gba) -> ! { let vblank = VBlank::get(); loop { - let mut rng = crate::rng::RandomNumberGenerator::new(); time += time_delta; time %= 1; let letters: Vec = letter_positons @@ -197,11 +196,10 @@ pub fn no_game(mut gba: crate::Gba) -> ! { .enumerate() .map(|(idx, position)| { let time = time + Num::::new(idx as i32) / 128; - *position + Vector2D::new(time.sin(), time.cos()) * 10 + (idx, *position + Vector2D::new(time.sin(), time.cos()) * 10) }) - .map(|pos| { - let mut obj = - ObjectUnmanaged::new(squares[rng.gen() as usize % squares.len()].clone()); + .map(|(idx, pos)| { + let mut obj = ObjectUnmanaged::new(squares[idx % squares.len()].clone()); obj.show().set_position(pos.floor()); obj }) From 8aee2753f9ae1b3454ea3cc4133c3702132161d7 Mon Sep 17 00:00:00 2001 From: Corwin Date: Thu, 11 May 2023 19:59:13 +0100 Subject: [PATCH 14/14] add changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0764120..450a5b70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- Changed the default template game + ## [0.15.0] - 2023/04/25 ### Added