From 521b373bd2384e4528ef143d6a66de1a00a94a7b Mon Sep 17 00:00:00 2001 From: cellos51 Date: Sat, 9 Aug 2025 13:52:24 -0400 Subject: [PATCH 1/7] Added new background visuals --- include/affine_background.h | 1 + source/affine_background.c | 25 +++++++++++++++---------- source/main.c | 1 + 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/include/affine_background.h b/include/affine_background.h index 8e5f78f..d3f06e8 100644 --- a/include/affine_background.h +++ b/include/affine_background.h @@ -9,6 +9,7 @@ #define AFFINE_BG_PB (PAL_ROW_LEN * 15) // This isn't really a palette bank, just the starting index of the palette void affine_background_init(); +void affine_background_hblank(); void affine_background_update(); void affine_background_set_color(COLOR color); // Must be called with an array of size at least AFFINE_BG_PAL_LEN diff --git a/source/affine_background.c b/source/affine_background.c index f9e15e7..a5c29c5 100644 --- a/source/affine_background.c +++ b/source/affine_background.c @@ -5,6 +5,7 @@ BG_AFFINE bgaff; AFF_SRC_EX asx = {32<<8, 64<<8, 120, 80, 0x0100, 0x0100, 0}; +static uint timer = 0; void affine_background_init() { @@ -15,22 +16,26 @@ void affine_background_init() bgaff = bg_aff_default; } -void affine_background_update() +void affine_background_hblank() { - static uint timer = 0; - timer++; + const s32 timer_s32 = timer << 8; + const s32 vcount_s32 = REG_VCOUNT << 8; + const s16 vcount_s16 = REG_VCOUNT; - // These values are not permament, just the current configuration for the affine background - asx.tex_x += 5; - asx.tex_y += 12; - asx.sx = (lu_sin(timer * 100)) >> 8; // Scale the sine value to fit in a s16 - asx.sx += 256; // Add 256 to the sine value to make it positive - asx.sy = (lu_sin(timer * 100 + 0x4000)) >> 8; // Scale the sine value to fit in a s16 - asx.sy += 256; // Add 256 to the sine value to make it positive + asx.scr_y = vcount_s16 - 128; // 128 on x and y is an offset used to center the rotation + asx.scr_x = 128; + asx.tex_x = lu_sin(timer_s32 + vcount_s32) + timer / 8; + asx.alpha = (vcount_s32 + timer_s32) / 8; + bg_rotscale_ex(&bgaff, &asx); REG_BG_AFFINE[2] = bgaff; } +void affine_background_update() +{ + timer++; +} + void affine_background_set_color(COLOR color) { memcpy16(&pal_bg_mem[AFFINE_BG_PB], affine_background_gfxPal, AFFINE_BG_PAL_LEN); diff --git a/source/main.c b/source/main.c index 76abc07..fa3d2dd 100644 --- a/source/main.c +++ b/source/main.c @@ -22,6 +22,7 @@ void init() { irq_init(NULL); irq_add(II_VBLANK, mmVBlank); + irq_add(II_HBLANK, affine_background_hblank); // Initialize maxmod mmInitDefault((mm_addr)soundbank_bin, 12); From 1ef0ce212b30084e3956a68637dd0fdd5ca7e384 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Sat, 9 Aug 2025 14:58:21 -0400 Subject: [PATCH 2/7] Different animation and fixed top 3 scanlines --- source/affine_background.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/source/affine_background.c b/source/affine_background.c index a5c29c5..0b49cd2 100644 --- a/source/affine_background.c +++ b/source/affine_background.c @@ -18,14 +18,26 @@ void affine_background_init() void affine_background_hblank() { - const s32 timer_s32 = timer << 8; - const s32 vcount_s32 = REG_VCOUNT << 8; - const s16 vcount_s16 = REG_VCOUNT; + vu16 vcount = REG_VCOUNT; - asx.scr_y = vcount_s16 - 128; // 128 on x and y is an offset used to center the rotation - asx.scr_x = 128; - asx.tex_x = lu_sin(timer_s32 + vcount_s32) + timer / 8; - asx.alpha = (vcount_s32 + timer_s32) / 8; + // This fixes a visual issue where the top 3 pixels of the scanline are out of sync + if (vcount >= 160) + { + vcount -= 228; + } + + const s32 timer_s32 = timer << 8; + const s32 vcount_s32 = vcount << 8; + const s16 vcount_s16 = vcount; + const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / 16); // dividing the timer by 16 to make the animation slower + + asx.scr_x = 128; // 128 on x and y is an offset used to center the rotation + asx.scr_y = vcount_s16 - 128; // scr_y must equal vcount otherwise the background will have no vertical difference + asx.tex_x = vcount_sine; + asx.tex_y = vcount_sine / 64; + asx.sx = vcount_sine / 32; + asx.sy = vcount_sine / 16; + asx.alpha = (vcount_s32 + timer_s32) / 16; bg_rotscale_ex(&bgaff, &asx); REG_BG_AFFINE[2] = bgaff; From 0dde0cb6ef8a3b6e07018491b22cc39577069243 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Sat, 9 Aug 2025 15:14:57 -0400 Subject: [PATCH 3/7] Took Copilot's advice and defined some magic numbers --- source/affine_background.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/source/affine_background.c b/source/affine_background.c index 0b49cd2..7bdf945 100644 --- a/source/affine_background.c +++ b/source/affine_background.c @@ -7,6 +7,10 @@ BG_AFFINE bgaff; AFF_SRC_EX asx = {32<<8, 64<<8, 120, 80, 0x0100, 0x0100, 0}; static uint timer = 0; +#define TOP_SCANLINE_OFFSET 228 +#define ANIMATION_SPEED_DIVISOR 16 + + void affine_background_init() { memcpy32_tile8_with_palette_offset((u32*)&tile8_mem[AFFINE_BG_CBB], (const u32*)affine_background_gfxTiles, affine_background_gfxTilesLen/4, AFFINE_BG_PB); @@ -23,21 +27,21 @@ void affine_background_hblank() // This fixes a visual issue where the top 3 pixels of the scanline are out of sync if (vcount >= 160) { - vcount -= 228; + vcount -= TOP_SCANLINE_OFFSET; } const s32 timer_s32 = timer << 8; const s32 vcount_s32 = vcount << 8; const s16 vcount_s16 = vcount; - const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / 16); // dividing the timer by 16 to make the animation slower + const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / ANIMATION_SPEED_DIVISOR); // dividing the timer by 16 to make the animation slower - asx.scr_x = 128; // 128 on x and y is an offset used to center the rotation - asx.scr_y = vcount_s16 - 128; // scr_y must equal vcount otherwise the background will have no vertical difference + asx.scr_x = (SCREEN_WIDTH / 2); // 128 on x and y is an offset used to center the rotation + asx.scr_y = vcount_s16 - (SCREEN_HEIGHT / 2); // scr_y must equal vcount otherwise the background will have no vertical difference asx.tex_x = vcount_sine; asx.tex_y = vcount_sine / 64; asx.sx = vcount_sine / 32; asx.sy = vcount_sine / 16; - asx.alpha = (vcount_s32 + timer_s32) / 16; + asx.alpha = vcount_sine + (timer_s32 / ANIMATION_SPEED_DIVISOR); bg_rotscale_ex(&bgaff, &asx); REG_BG_AFFINE[2] = bgaff; From af2358f48743593206b1928bf0db6c3fb3010fb4 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Sat, 9 Aug 2025 17:08:18 -0400 Subject: [PATCH 4/7] Minor optimization --- source/affine_background.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/affine_background.c b/source/affine_background.c index 7bdf945..b360f4c 100644 --- a/source/affine_background.c +++ b/source/affine_background.c @@ -24,8 +24,11 @@ void affine_background_hblank() { vu16 vcount = REG_VCOUNT; - // This fixes a visual issue where the top 3 pixels of the scanline are out of sync - if (vcount >= 160) + if ((vcount > 160 && vcount < 225) || vcount > TOP_SCANLINE_OFFSET) // Exit the function if the scanline is outside the valid range + { + return; + } + else if (vcount > TOP_SCANLINE_OFFSET - 3) // This fixes a visual issue where the top 3 pixels of the scanline are out of sync { vcount -= TOP_SCANLINE_OFFSET; } From b043b82d3f7c3e1214ec58527ee86cf5394888c9 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Tue, 12 Aug 2025 00:06:04 +0300 Subject: [PATCH 5/7] Optimized affine background HBLANK effect - improved but still not good enough --- include/affine_background.h | 5 +-- source/affine_background.c | 65 +++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/include/affine_background.h b/include/affine_background.h index d3f06e8..504252d 100644 --- a/include/affine_background.h +++ b/include/affine_background.h @@ -7,10 +7,11 @@ #define AFFINE_BG_PAL_LEN 5 #define AFFINE_BG_PB (PAL_ROW_LEN * 15) // This isn't really a palette bank, just the starting index of the palette +#define AFFINE_BG_IDX 2 // The index of BGCNT register etc. void affine_background_init(); -void affine_background_hblank(); -void affine_background_update(); +IWRAM_CODE void affine_background_hblank(); +IWRAM_CODE void affine_background_update(); void affine_background_set_color(COLOR color); // Must be called with an array of size at least AFFINE_BG_PAL_LEN void affine_background_load_palette(const u16 *src); diff --git a/source/affine_background.c b/source/affine_background.c index b360f4c..f1b629d 100644 --- a/source/affine_background.c +++ b/source/affine_background.c @@ -3,11 +3,10 @@ #include "graphic_utils.h" -BG_AFFINE bgaff; -AFF_SRC_EX asx = {32<<8, 64<<8, 120, 80, 0x0100, 0x0100, 0}; +BG_AFFINE bgaff_arr[SCREEN_HEIGHT + 1]; + static uint timer = 0; -#define TOP_SCANLINE_OFFSET 228 #define ANIMATION_SPEED_DIVISOR 16 @@ -17,41 +16,57 @@ void affine_background_init() GRIT_CPY(&se_mem[AFFINE_BG_SBB], affine_background_gfxMap); affine_background_load_palette(affine_background_gfxPal); - bgaff = bg_aff_default; + REG_BG_AFFINE[AFFINE_BG_IDX] = bg_aff_default; } -void affine_background_hblank() +// Pre-computes the affine matrices values for each scanline +// and stores in bgaff_arr. +// This is to be done in VBLANK so the HBLANK code +// can just fetch the values quickly. +IWRAM_CODE void affine_background_prep_bgaff_arr() +{ + for (u16 vcount = 0; vcount < SCREEN_HEIGHT; vcount++) + { + AFF_SRC_EX asx = {32<<8, 64<<8, 120, 80, 0x0100, 0x0100, 0}; + const s32 timer_s32 = timer << 8; + const s32 vcount_s32 = vcount << 8; + const s16 vcount_s16 = vcount; + const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / ANIMATION_SPEED_DIVISOR); // dividing the timer by 16 to make the animation slower + + asx.scr_x = (SCREEN_WIDTH / 2); // 128 on x and y is an offset used to center the rotation + asx.scr_y = vcount_s16 - (SCREEN_HEIGHT / 2); // scr_y must equal vcount otherwise the background will have no vertical difference + asx.tex_x = vcount_sine; + asx.tex_y = vcount_sine / 64; + asx.sx = vcount_sine / 32; + asx.sy = vcount_sine / 16; + asx.alpha = vcount_sine + (timer_s32 / ANIMATION_SPEED_DIVISOR); + + bg_rotscale_ex(&bgaff_arr[vcount], &asx); + } + + /* HBLANK occurs after the scanline so REG_VCOUNT represents the + * the scanline that just passed, so when it's SCREEN_HEIGHT we will + * actually be updating the first line + */ + bgaff_arr[SCREEN_HEIGHT] = bgaff_arr[0]; +} + +IWRAM_CODE void affine_background_hblank() { vu16 vcount = REG_VCOUNT; - if ((vcount > 160 && vcount < 225) || vcount > TOP_SCANLINE_OFFSET) // Exit the function if the scanline is outside the valid range + if ((vcount >= SCREEN_HEIGHT)) // Exit the function if the scanline is outside the screen { return; } - else if (vcount > TOP_SCANLINE_OFFSET - 3) // This fixes a visual issue where the top 3 pixels of the scanline are out of sync - { - vcount -= TOP_SCANLINE_OFFSET; - } - const s32 timer_s32 = timer << 8; - const s32 vcount_s32 = vcount << 8; - const s16 vcount_s16 = vcount; - const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / ANIMATION_SPEED_DIVISOR); // dividing the timer by 16 to make the animation slower - - asx.scr_x = (SCREEN_WIDTH / 2); // 128 on x and y is an offset used to center the rotation - asx.scr_y = vcount_s16 - (SCREEN_HEIGHT / 2); // scr_y must equal vcount otherwise the background will have no vertical difference - asx.tex_x = vcount_sine; - asx.tex_y = vcount_sine / 64; - asx.sx = vcount_sine / 32; - asx.sy = vcount_sine / 16; - asx.alpha = vcount_sine + (timer_s32 / ANIMATION_SPEED_DIVISOR); - - bg_rotscale_ex(&bgaff, &asx); - REG_BG_AFFINE[2] = bgaff; + // See comment in affine_background_prep_bgaff_arr() + REG_BG_AFFINE[AFFINE_BG_IDX] = bgaff_arr[vcount + 1]; } void affine_background_update() { + affine_background_prep_bgaff_arr(); timer++; } From 5ba0b1034b8cb01148038f19fe36772756bb9c06 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Tue, 12 Aug 2025 01:07:20 +0300 Subject: [PATCH 6/7] Comment clarification --- include/affine_background.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/affine_background.h b/include/affine_background.h index 504252d..2b599f2 100644 --- a/include/affine_background.h +++ b/include/affine_background.h @@ -7,7 +7,7 @@ #define AFFINE_BG_PAL_LEN 5 #define AFFINE_BG_PB (PAL_ROW_LEN * 15) // This isn't really a palette bank, just the starting index of the palette -#define AFFINE_BG_IDX 2 // The index of BGCNT register etc. +#define AFFINE_BG_IDX 2 // The index of the affine background BGCNT register etc. void affine_background_init(); IWRAM_CODE void affine_background_hblank(); From d0d83eb2363d9348f3446fa6f9c99114fccf9846 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Thu, 28 Aug 2025 23:00:27 -0400 Subject: [PATCH 7/7] Main menu uses HBLANK and the game uses standard affine background --- source/affine_background.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/source/affine_background.c b/source/affine_background.c index be5f808..7374a10 100644 --- a/source/affine_background.c +++ b/source/affine_background.c @@ -8,7 +8,6 @@ BG_AFFINE bgaff_arr[SCREEN_HEIGHT + 1]; -BG_AFFINE bgaff; AFF_SRC_EX asx = {0}; enum AffineBackgroundID background = AFFINE_BG_MAIN_MENU; @@ -30,12 +29,11 @@ IWRAM_CODE void affine_background_prep_bgaff_arr() { for (u16 vcount = 0; vcount < SCREEN_HEIGHT; vcount++) { - AFF_SRC_EX asx = {32<<8, 64<<8, 120, 80, 0x0100, 0x0100, 0}; const s32 timer_s32 = timer << 8; const s32 vcount_s32 = vcount << 8; const s16 vcount_s16 = vcount; const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / ANIMATION_SPEED_DIVISOR); // dividing the timer by 16 to make the animation slower - + asx.scr_x = (SCREEN_WIDTH / 2); // 128 on x and y is an offset used to center the rotation asx.scr_y = vcount_s16 - (SCREEN_HEIGHT / 2); // scr_y must equal vcount otherwise the background will have no vertical difference asx.tex_x = (1000 * 1000) + (vcount_sine); @@ -69,7 +67,24 @@ IWRAM_CODE void affine_background_hblank() void affine_background_update() { - affine_background_prep_bgaff_arr(); + if (REG_IE & IRQ_HBLANK) // High quality mode with HBLANK interrupt + { + affine_background_prep_bgaff_arr(); + } + else // Low quality mode without HBLANK interrupt + { + asx.scr_x = 0; + asx.scr_y = 0; + asx.tex_x += 5; + asx.tex_y += 12; + asx.sx = ((lu_sin(timer * 100)) >> 8) + 256; // Scale the sine value to fit in a s16 + asx.sy = ((lu_sin(timer * 100 + 0x4000)) >> 8) + 256; // Scale the sine value to fit in a s16 + asx.alpha = 0; + + bg_rotscale_ex(&bgaff_arr[0], &asx); + REG_BG_AFFINE[AFFINE_BG_IDX] = bgaff_arr[0]; + } + timer++; } @@ -96,6 +111,7 @@ void affine_background_change_background(enum AffineBackgroundID new_bg) case AFFINE_BG_MAIN_MENU: REG_BG2CNT &= ~BG_AFF_32x32; REG_BG2CNT |= BG_AFF_16x16; + REG_IE |= IRQ_HBLANK; // Enable HBLANK memcpy32_tile8_with_palette_offset((u32*)&tile8_mem[AFFINE_BG_CBB], (const u32*)affine_main_menu_background_gfxTiles, affine_main_menu_background_gfxTilesLen/4, AFFINE_BG_PB); GRIT_CPY(&se_mem[AFFINE_BG_SBB], affine_main_menu_background_gfxMap); @@ -104,6 +120,7 @@ void affine_background_change_background(enum AffineBackgroundID new_bg) case AFFINE_BG_GAME: REG_BG2CNT &= ~BG_AFF_16x16; REG_BG2CNT |= BG_AFF_32x32; + REG_IE &= ~IRQ_HBLANK; // Disable HBLANK memcpy32_tile8_with_palette_offset((u32*)&tile8_mem[AFFINE_BG_CBB], (const u32*)affine_background_gfxTiles, affine_background_gfxTilesLen/4, AFFINE_BG_PB); GRIT_CPY(&se_mem[AFFINE_BG_SBB], affine_background_gfxMap);