diff --git a/include/affine_background.h b/include/affine_background.h index 31a5b65..d1ee08d 100644 --- a/include/affine_background.h +++ b/include/affine_background.h @@ -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); diff --git a/source/affine_background.c b/source/affine_background.c index c9e6991..7374a10 100644 --- a/source/affine_background.c +++ b/source/affine_background.c @@ -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); diff --git a/source/main.c b/source/main.c index 9463584..a9ca8af 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 text engine tte_init_se(0, BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB), 0, CLR_WHITE, TTE_BIT_UNPACK_OFFSET, NULL, NULL);