Merge pull request #91 from cellos51/hblank_affine_background_effects

HBlank based visual effects for affine background
This commit is contained in:
Elliptical
2025-08-29 15:18:49 -04:00
committed by GitHub
3 changed files with 68 additions and 24 deletions
+3 -1
View File
@@ -5,6 +5,7 @@
#include "graphic_utils.h"
#define AFFINE_BG_IDX 2 // The index of the affine background BGCNT register etc.
#define AFFINE_BG_PAL_LEN 16
#define AFFINE_BG_PB (PAL_ROW_LEN * 10) // This isn't really a palette bank, just the starting index of the palette
@@ -15,7 +16,8 @@ enum AffineBackgroundID
};
void affine_background_init();
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);
+64 -23
View File
@@ -4,49 +4,88 @@
#include "graphic_utils.h"
BG_AFFINE bgaff;
#define ANIMATION_SPEED_DIVISOR 16
BG_AFFINE bgaff_arr[SCREEN_HEIGHT + 1];
AFF_SRC_EX asx = {0};
enum AffineBackgroundID background = AFFINE_BG_MAIN_MENU;
static uint timer = 0;
void affine_background_init()
{
affine_background_update();
bgaff = bg_aff_default;
REG_BG_AFFINE[AFFINE_BG_IDX] = bg_aff_default;
}
// 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++)
{
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);
asx.tex_y = (1000 * 1000);
asx.sx = 128;
asx.sy = 128;
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 >= SCREEN_HEIGHT)) // Exit the function if the scanline is outside the screen
{
return;
}
// See comment in affine_background_prep_bgaff_arr()
REG_BG_AFFINE[AFFINE_BG_IDX] = bgaff_arr[vcount + 1];
}
void affine_background_update()
{
static uint timer = 0;
timer++;
switch (background)
if (REG_IE & IRQ_HBLANK) // High quality mode with HBLANK interrupt
{
affine_background_prep_bgaff_arr();
}
else // Low quality mode without HBLANK interrupt
{
case AFFINE_BG_MAIN_MENU:
asx.scr_x = SCREEN_WIDTH / 2;
asx.scr_y = SCREEN_HEIGHT / 2;
asx.tex_x = 16560;
asx.tex_y = 14400;
asx.sx = 128;
asx.sy = 128;
asx.alpha = 1000; // This slightly rotates it which increases the perceived resolution of the background
break;
case AFFINE_BG_GAME:
asx.scr_x = 0;
asx.scr_y = 0;
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.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;
break;
bg_rotscale_ex(&bgaff_arr[0], &asx);
REG_BG_AFFINE[AFFINE_BG_IDX] = bgaff_arr[0];
}
bg_rotscale_ex(&bgaff, &asx);
REG_BG_AFFINE[2] = bgaff;
timer++;
}
void affine_background_set_color(COLOR color)
@@ -72,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);
@@ -80,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);
+1
View File
@@ -22,6 +22,7 @@ void init()
{
irq_init(NULL);
irq_add(II_VBLANK, mmVBlank);
irq_add(II_HBLANK, affine_background_hblank);
// Initialize text engine
tte_init_se(0, BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB), 0, CLR_WHITE, TTE_BIT_UNPACK_OFFSET, NULL, NULL);