// SPDX-License-Identifier: MIT // // Copyright (c) 2009-2014 Cesar Rincon "NightFox" // // NightFox LIB - Funciones 2D comunes // http://www.nightfoxandco.com/ // Version 20140413 // Includes devKitPro #include // Includes C #include #include // Includes propios #include "nf_basic.h" #include "nf_2d.h" #include "nf_tiledbg.h" #include "nf_sprite256.h" // Funcion NF_Set2D(); void NF_Set2D(u8 screen, u8 mode) { if (screen == 0) { // Pantalla Superior switch (mode) { // Selecciona modo case 0: videoSetMode(MODE_0_2D); break; case 2: videoSetMode(MODE_2_2D); break; case 5: videoSetMode(MODE_5_2D); break; } } else { // Pantalla inferior switch (mode) { // Seleccion modo case 0: videoSetModeSub(MODE_0_2D); break; case 2: videoSetModeSub(MODE_2_2D); break; case 5: videoSetModeSub(MODE_5_2D); break; } } } // Funcion NF_ShowBg(); void NF_ShowBg(u8 screen, u8 layer) { if (screen == 0) { // Pantalla Superior switch (layer) { // Segun la capa case 0: REG_DISPCNT |= (DISPLAY_BG0_ACTIVE); break; case 1: REG_DISPCNT |= (DISPLAY_BG1_ACTIVE); break; case 2: REG_DISPCNT |= (DISPLAY_BG2_ACTIVE); break; case 3: REG_DISPCNT |= (DISPLAY_BG3_ACTIVE); break; } } else { // Pantalla Inferior switch (layer) { // Segun la capa case 0: REG_DISPCNT_SUB |= (DISPLAY_BG0_ACTIVE); break; case 1: REG_DISPCNT_SUB |= (DISPLAY_BG1_ACTIVE); break; case 2: REG_DISPCNT_SUB |= (DISPLAY_BG2_ACTIVE); break; case 3: REG_DISPCNT_SUB |= (DISPLAY_BG3_ACTIVE); break; } } } // Funcion NF_HideBg(); void NF_HideBg(u8 screen, u8 layer) { if (screen == 0) { // Pantalla Superior switch (layer) { // Segun la capa case 0: REG_DISPCNT &= ~(DISPLAY_BG0_ACTIVE); break; case 1: REG_DISPCNT &= ~(DISPLAY_BG1_ACTIVE); break; case 2: REG_DISPCNT &= ~(DISPLAY_BG2_ACTIVE); break; case 3: REG_DISPCNT &= ~(DISPLAY_BG3_ACTIVE); break; } } else { // Pantalla Inferior switch (layer) { // Segun la capa case 0: REG_DISPCNT_SUB &= ~(DISPLAY_BG0_ACTIVE); break; case 1: REG_DISPCNT_SUB &= ~(DISPLAY_BG1_ACTIVE); break; case 2: REG_DISPCNT_SUB &= ~(DISPLAY_BG2_ACTIVE); break; case 3: REG_DISPCNT_SUB &= ~(DISPLAY_BG3_ACTIVE); break; } } } // Funcion NF_ScrollBg(); void NF_ScrollBg(u8 screen, u8 layer, s16 x, s16 y) { // Variables temporales s16 sx = x; s16 sy = y; // Si el mapa es infinito... > 512 if (NF_TILEDBG_LAYERS[screen][layer].bgtype > 0) { // Variables temporales de Fondos infinitos u32 address = 0; // Puntero a la VRAM u16 blockx = 0; // NÂș de bloque en pantalla u16 blocky = 0; u32 mapmovex = 0; // Desplazamiento de la copia de datos (block x 2048) u32 mapmovey = 0; u16 rowsize = 0; // Calcula el espacio ocupado en RAM por cada fila // Calcula la direccion base del mapa if (screen == 0) { // (VRAM_A) address = (0x6000000) + (NF_TILEDBG_LAYERS[screen][layer].mapbase << 11); } else { // (VRAM_C) address = (0x6200000) + (NF_TILEDBG_LAYERS[screen][layer].mapbase << 11); } // Ajusta el valor maximo de las variables a los limites del scroll if (sx < 0) { sx = 0; } if (sx > (NF_TILEDBG_LAYERS[screen][layer].bgwidth - 256)) { sx = (NF_TILEDBG_LAYERS[screen][layer].bgwidth - 256); } if (sy < 0) { sy = 0; } if (sy > (NF_TILEDBG_LAYERS[screen][layer].bgheight - 192)) { sy = (NF_TILEDBG_LAYERS[screen][layer].bgheight - 192); } // Segun el tipo de mapa... switch (NF_TILEDBG_LAYERS[screen][layer].bgtype) { case 1: // 512x256 - Bloque A y B (32x32) + (32x32) (2kb x 2 = 4kb) // Calcula el bloque blockx = (x >> 8); // Si has cambiado de bloque... if (NF_TILEDBG_LAYERS[screen][layer].blockx != blockx) { // Calcula el desplazamiento de datos mapmovex = (blockx << 11); // Copias los Bloques A y B (32x32) + (32x32) (2kb x 2 = 4kb) NF_DmaMemCopy((void*)address, (NF_BUFFER_BGMAP[NF_TILEDBG_LAYERS[screen][layer].bgslot] + mapmovex), 4096); // Y actualiza el bloque actual NF_TILEDBG_LAYERS[screen][layer].blockx = blockx; } // Calcula la X del fondo sx = x - (blockx << 8); break; case 2: // 256x512 - Bloque A (32x64) (2kb x 2 = 4kb) // Calcula el bloque blocky = (y >> 8); // Si has cambiado de bloque... if (NF_TILEDBG_LAYERS[screen][layer].blocky != blocky) { // Calcula el desplazamiento de datos mapmovey = (blocky << 11); // Copias los Bloques A y B (32x32) + (32x32) (2kb x 2 = 4kb) NF_DmaMemCopy((void*)address, (NF_BUFFER_BGMAP[NF_TILEDBG_LAYERS[screen][layer].bgslot] + mapmovey), 4096); // Y actualiza el bloque actual NF_TILEDBG_LAYERS[screen][layer].blocky = blocky; } // Calcula la X del fondo sy = y - (blocky << 8); break; case 3: // >512x>512 rowsize = (((((NF_TILEDBG_LAYERS[screen][layer].bgwidth - 1) >> 8)) + 1) << 11); // Calcula los bloques blockx = (x >> 8); blocky = (y >> 8); if ( // Si se ha cambiado de bloque en alguna direccion... (NF_TILEDBG_LAYERS[screen][layer].blockx != blockx) || (NF_TILEDBG_LAYERS[screen][layer].blocky != blocky) ) { // Y el desplazamiento de datos mapmovex = (blocky * rowsize) + (blockx << 11); mapmovey = mapmovex + rowsize; // Bloque A y B (32x32) + (32x32) (2kb x 2 = 4kb) NF_DmaMemCopy((void*)address, (NF_BUFFER_BGMAP[NF_TILEDBG_LAYERS[screen][layer].bgslot] + mapmovex), 4096); // Bloque (+4096) C y D (32x32) + (32x32) (2kb x 2 = 4kb) NF_DmaMemCopy((void*)(address + 4096), (NF_BUFFER_BGMAP[NF_TILEDBG_LAYERS[screen][layer].bgslot] + mapmovey), 4096); // Y actualiza el bloque actual NF_TILEDBG_LAYERS[screen][layer].blockx = blockx; NF_TILEDBG_LAYERS[screen][layer].blocky = blocky; } // Calcula la X e Y del fondo sx = x - (blockx << 8); sy = y - (blocky << 8); break; } } // Mueve el fondo usando los registros if (screen == 0) { // Pantalla Superior switch (layer) { // Segun la capa case 0: REG_BG0HOFS = sx; REG_BG0VOFS = sy; break; case 1: REG_BG1HOFS = sx; REG_BG1VOFS = sy; break; case 2: REG_BG2HOFS = sx; REG_BG2VOFS = sy; break; case 3: REG_BG3HOFS = sx; REG_BG3VOFS = sy; break; } } else { switch (layer) { // Segun la capa case 0: REG_BG0HOFS_SUB = sx; REG_BG0VOFS_SUB = sy; break; case 1: REG_BG1HOFS_SUB = sx; REG_BG1VOFS_SUB = sy; break; case 2: REG_BG2HOFS_SUB = sx; REG_BG2VOFS_SUB = sy; break; case 3: REG_BG3HOFS_SUB = sx; REG_BG3VOFS_SUB = sy; break; } } } // Funcion NF_MoveSprite(); void NF_MoveSprite(u8 screen, u8 id, s16 x, s16 y) { NF_SPRITEOAM[screen][id].x = x; // Coordenada X NF_SPRITEOAM[screen][id].y = y; // Coordenada Y } // Funcion NF_SpriteLayer(); void NF_SpriteLayer(u8 screen, u8 id, u8 layer) { NF_SPRITEOAM[screen][id].layer = layer; // Capa sobre la que esta el sprite } // Funcion NF_ShowSprite(); void NF_ShowSprite(u8 screen, u8 id, bool show) { NF_SPRITEOAM[screen][id].hide = !show; // Muestra o oculta el sprite } // Funcion NF_HflipSprite(); void NF_HflipSprite(u8 screen, u8 id, bool hflip) { NF_SPRITEOAM[screen][id].hflip = hflip; // Volteado horizontal; } // Funcion NF_GetSpriteHflip(); bool NF_GetSpriteHflip(u8 screen, u8 id) { return NF_SPRITEOAM[screen][id].hflip; } // Funcion NF_VflipSprite(); void NF_VflipSprite(u8 screen, u8 id, bool vflip) { NF_SPRITEOAM[screen][id].vflip = vflip; // Volteado vertical; } // Funcion NF_GetSpriteVflip(); bool NF_GetSpriteVflip(u8 screen, u8 id) { return NF_SPRITEOAM[screen][id].vflip; } // Funcion NF_SpriteFrame(); void NF_SpriteFrame(u8 screen, u8 id, u16 frame) { // Verifica el rango de Id's de Sprites if (id > 127) { NF_Error(106, "Sprite", 127); } // Verifica el rango de frames del Sprite if (frame > NF_SPRITEOAM[screen][id].lastframe) { NF_Error(106, "Sprite frame", NF_SPRITEOAM[screen][id].lastframe); } // Verifica si el frame necesita ser actualizado if (NF_SPRITEOAM[screen][id].frame != frame) { // Si debes de copiar el nuevo frame desde la RAM a la VRAM... if (NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][id].gfxid].keepframes) { // Variables temporales char* source; // Puntero de origen u32 destination = 0; // Puntero de destino u16 ramid = 0; // Slot de RAM donde se encuentra el Gfx // Calcula el origen y destino del nuevo frame a copiar ramid = NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][id].gfxid].ramid; source = NF_BUFFER_SPR256GFX[ramid] + (NF_SPRITEOAM[screen][id].framesize * frame); destination = NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][id].gfxid].address; // Copialo NF_DmaMemCopy((void*)destination, source, NF_SPRITEOAM[screen][id].framesize); } else { // Si todos los frames ya estan en VRAM... // Calcula la direccion del Gfx del frame u32 address = 0; address = NF_SPR256VRAM[screen][NF_SPRITEOAM[screen][id].gfxid].address + (NF_SPRITEOAM[screen][id].framesize * frame); NF_SPRITEOAM[screen][id].gfx = (u32*)address; } // Almacena el frame actual NF_SPRITEOAM[screen][id].frame = frame; } } // Funcion NF_EnableSpriteRotScale(); void NF_EnableSpriteRotScale(u8 screen, u8 sprite, u8 id, bool doublesize) { // Verifica el rango de Id's de Sprites if (sprite > 127) { NF_Error(106, "Sprite", 127); } // Verifica el rango de Id's de Rotacion if (id > 31) { NF_Error(106, "RotScale", 127); } // Verifica si el Sprite esta creado if (!NF_SPRITEOAM[screen][sprite].created) { char text[4]; snprintf(text, sizeof(text), "%d", screen); NF_Error(112, text, sprite); } NF_SPRITEOAM[screen][sprite].rot = id; // Id de rotacion (-1 ninguno) (0 - 31 Id de rotacion) NF_SPRITEOAM[screen][sprite].doublesize = doublesize; // Usar el "double size" al rotar ? ("NO" por defecto) } // Funcion NF_DisableSpriteRotScale(); void NF_DisableSpriteRotScale(u8 screen, u8 sprite) { // Verifica el rango de Id's de Sprites if (sprite > 127) { NF_Error(106, "Sprite", 127); } // Verifica si el Sprite esta creado if (!NF_SPRITEOAM[screen][sprite].created) { char text[4]; snprintf(text, sizeof(text), "%d", screen); NF_Error(112, text, sprite); } NF_SPRITEOAM[screen][sprite].rot = -1; // Id de rotacion (-1 ninguno) (0 - 31 Id de rotacion) NF_SPRITEOAM[screen][sprite].doublesize = false; // Usar el "double size" al rotar ? ("NO" por defecto) } // Funcion NF_SpriteRotScale(); void NF_SpriteRotScale(u8 screen, u8 id, s16 angle, u16 sx, u16 sy) { // Variables temporales s16 in = 0; // Angulo dado s16 out = 0; // Angulo convertido in = angle; // Limites del angulo if (in < -512) { in += 512; } if (in > 512) { in -= 512; } // Limites del factor X if (sx > 512) { sx = 512; } // Limites del factor Y if (sy > 512) { sy = 512; } // Si es un numero negativo... if (in < 0) { in = -in; // Pasa a positivo (para poder hacer el bitshift) out = (in << 6); // (in * 64); Pasa de base 512 a base 32768 // Dejalo en positivo para que <0 gire a la izquierda } else { out = (in << 6); out = -out; // Pasalo a negativo para que >0 gire a la derecha } // Actualiza el RotScale del OAM if (screen == 0) { oamRotateScale(&oamMain, id, out, (512 - sx), (512 - sy)); } else { oamRotateScale(&oamSub, id, out, (512 - sx), (512 - sy)); } }