diff options
| author | XaviDCR92 <xavi.dcr@gmail.com> | 2017-07-21 00:09:35 +0200 |
|---|---|---|
| committer | XaviDCR92 <xavi.dcr@gmail.com> | 2017-07-21 00:09:35 +0200 |
| commit | 627de0d81f81ad60d26d782f2425be1e6f5a3dbc (patch) | |
| tree | 91ffa502aa62c03c2fecf28529ebc8c6b20828c5 /Source/Gfx.c | |
| download | opensend-627de0d81f81ad60d26d782f2425be1e6f5a3dbc.tar.gz | |
+ First commit. It works painfully slow, but gets the job done. Still lots of room for improvement.
Diffstat (limited to 'Source/Gfx.c')
| -rw-r--r-- | Source/Gfx.c | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/Source/Gfx.c b/Source/Gfx.c new file mode 100644 index 0000000..03b33d2 --- /dev/null +++ b/Source/Gfx.c @@ -0,0 +1,595 @@ +/* ************************************* + * Includes + * *************************************/ + +#include "Gfx.h" + +/* ************************************* + * Defines + * *************************************/ + +#define PRIMITIVE_LIST_SIZE 0x1000 +#define DOUBLE_BUFFERING_SWAP_Y 256 +#define UPLOAD_IMAGE_FLAG 1 +#define MAX_LUMINANCE 0xFF +#define ROTATE_BIT_SHIFT 12 +#define GPUSTAT (*(unsigned int*)0x1F801814) +#define D2_CHCR (*(unsigned int*)0x1F8010A8) + +/* ************************************* + * Structs and enums + * *************************************/ + +enum +{ + BUTTON_SIZE = 16, + + BUTTON_CROSS_U = 48, + BUTTON_CROSS_V = 0, + + BUTTON_SQUARE_U = 0, + BUTTON_SQUARE_V = 0, + + BUTTON_TRIANGLE_U = 32, + BUTTON_TRIANGLE_V = 0, + + BUTTON_CIRCLE_U = 16, + BUTTON_CIRCLE_V = 0, + + BUTTON_DIRECTION_U = 64, + BUTTON_DIRECTION_V = 0, + + BUTTON_LR_U = 80, + BUTTON_LR_V = 0, + BUTTON_LR_SIZE = 24, + + LETTER_SIZE = 8, + + LETTER_L1_U = 104, + LETTER_L1_V = 0, + + LETTER_L2_U = 112, + LETTER_L2_V = 0, + + LETTER_R1_U = 104, + LETTER_R1_V = 8, + + LETTER_R2_U = 112, + LETTER_R2_V = 8, + + LETTER_OFFSET_INSIDE_BUTTON_LR_X = 8, + LETTER_OFFSET_INSIDE_BUTTON_LR_Y = 6 + +}; + +enum +{ + GFX_SECOND_DISPLAY_X = 384, + GFX_SECOND_DISPLAY_Y = 256, + GFX_SECOND_DISPLAY_TPAGE = 22 +}; + +/* ************************************* + * Global Variables + * *************************************/ + +GsSprite PSXButtons; + +/* ************************************* + * Local Prototypes + * *************************************/ + + + +/* ************************************* + * Local Variables + * *************************************/ + +// Drawing environment +static GsDrawEnv DrawEnv; +// Display environment +static GsDispEnv DispEnv; +// Primitive list (it contains all the graphical data for the GPU) +static unsigned int prim_list[PRIMITIVE_LIST_SIZE]; +// Tells other modules whether data is being loaded to GPU +static volatile bool gfx_busy; +// Dictates (R,G,B) brigthness to all sprites silently +static uint8_t global_lum; +// When true, it draws a rectangle on top of all primitives with +// information for development purposes. +static bool GfxDevMenuEnableFlag; + +static bool five_hundred_ms_show; +static bool one_second_show; + +void GfxSwapBuffers(void) +{ + // Consistency check +#if PSXSDK_DEBUG + + if(GsListPos() >= PRIMITIVE_LIST_SIZE) + { + dprintf("Linked list iterator overflow!\n"); + while(1); + } + + if( (DrawEnv.h != Y_SCREEN_RESOLUTION) + || + ( (DrawEnv.w != X_SCREEN_RESOLUTION) + && + (DrawEnv.w != X_SCREEN_RESOLUTION >> 1) ) + || + ( (DispEnv.y != DOUBLE_BUFFERING_SWAP_Y) + && + (DispEnv.y != 0) ) ) + { + dprintf("What the hell is happening?\n"); + DEBUG_PRINT_VAR(DispEnv.x); + DEBUG_PRINT_VAR(DispEnv.y); + DEBUG_PRINT_VAR(DrawEnv.x); + DEBUG_PRINT_VAR(DrawEnv.y); + + while(1); + } +#endif // PSXSDK_DEBUG + + if(DrawEnv.h == Y_SCREEN_RESOLUTION) + { + if(DispEnv.y == 0) + { + DispEnv.y = DOUBLE_BUFFERING_SWAP_Y; + DrawEnv.y = 0; + } + else if(DispEnv.y == DOUBLE_BUFFERING_SWAP_Y) + { + DispEnv.y = 0; + DrawEnv.y = DOUBLE_BUFFERING_SWAP_Y; + } + + GsSetDispEnv(&DispEnv); + GsSetDrawEnv(&DrawEnv); + } +} + +void GfxDevMenuEnable(void) +{ + GfxDevMenuEnableFlag = true; +} + +void GfxInitDrawEnv(void) +{ + DrawEnv.x = 0; + DrawEnv.y = 0; + DrawEnv.draw_on_display = false; + DrawEnv.w = X_SCREEN_RESOLUTION; + DrawEnv.h = Y_SCREEN_RESOLUTION; + + GsSetDrawEnv(&DrawEnv); +} + +void GfxInitDispEnv(void) +{ + DispEnv.x = 0; + DispEnv.y = 0; + + GsSetDispEnv(&DispEnv); +} + +void GfxSetPrimitiveList(void) +{ + GsSetList(prim_list); +} + +void GfxDrawScene_Fast(void) +{ + if(System1SecondTick() == true) + { + one_second_show = one_second_show? false:true; + } + + if(System500msTick() == true) + { + five_hundred_ms_show = five_hundred_ms_show? false:true; + } + + GfxSwapBuffers(); + FontCyclic(); + GsDrawList(); +} + +bool GfxReadyForDMATransfer(void) +{ + return ( (GPUSTAT & 1<<28) && !(D2_CHCR & 1<<24) ); +} + +void GfxDrawScene(void) +{ + while( (SystemRefreshNeeded() == false) + || + (GfxIsGPUBusy() == true) ); + + GfxDrawScene_Fast(); + + SystemCyclicHandler(); +} + +void GfxDrawScene_Slow(void) +{ + GfxDrawScene(); + while(GfxIsGPUBusy() == true); +} + +void GfxSortSprite(GsSprite * spr) +{ + uint8_t aux_r = spr->r; + uint8_t aux_g = spr->g; + uint8_t aux_b = spr->b; + unsigned char aux_tpage = spr->tpage; + short aux_w = spr->w; + short aux_x = spr->x; + bool has_1hz_flash = spr->attribute & GFX_1HZ_FLASH; + bool has_2hz_flash = spr->attribute & GFX_2HZ_FLASH; + + if( (spr->w <= 0) || (spr->h <= 0) ) + { + // Invalid width or heigth + return; + } + + if(GfxIsSpriteInsideScreenArea(spr) == false) + { + return; + } + else if(has_2hz_flash && Gfx2HzFlash() == false) + { + return; + } + else if(has_1hz_flash && Gfx1HzFlash() == false) + { + return; + } + + if(global_lum != NORMAL_LUMINANCE) + { + if(spr->r < NORMAL_LUMINANCE - global_lum) + { + spr->r = 0; + } + else + { + spr->r -= NORMAL_LUMINANCE - global_lum; + } + + if(spr->g < NORMAL_LUMINANCE - global_lum) + { + spr->g = 0; + } + else + { + spr->g -= NORMAL_LUMINANCE - global_lum; + } + + if(spr->b < NORMAL_LUMINANCE - global_lum) + { + spr->b = 0; + } + else + { + spr->b -= NORMAL_LUMINANCE - global_lum; + } + } + + if(has_1hz_flash == true) + { + spr->attribute &= ~(GFX_1HZ_FLASH); + } + + if(spr->w > MAX_SIZE_FOR_GSSPRITE) + { + // GsSprites can't be bigger than 256x256, so since display + // resolution is 384x240, it must be split into two primitives. + + spr->w = MAX_SIZE_FOR_GSSPRITE; + GsSortSprite(spr); + + spr->x += MAX_SIZE_FOR_GSSPRITE; + spr->w = X_SCREEN_RESOLUTION - MAX_SIZE_FOR_GSSPRITE; + spr->tpage += MAX_SIZE_FOR_GSSPRITE / GFX_TPAGE_WIDTH; + GsSortSprite(spr); + + // Restore original values after sorting + spr->w = aux_w; + spr->tpage = aux_tpage; + spr->x = aux_x; + } + else + { + GsSortSprite(spr); + } + + if(has_1hz_flash == true) + { + spr->attribute |= GFX_1HZ_FLASH; + } + + spr->r = aux_r; + spr->g = aux_g; + spr->b = aux_b; +} + +uint8_t GfxGetGlobalLuminance(void) +{ + return global_lum; +} + +void GfxSetGlobalLuminance(uint8_t value) +{ + global_lum = value; +} + +void GfxIncreaseGlobalLuminance(int8_t step) +{ + if( ( (global_lum + step) < MAX_LUMINANCE ) + && + ( (global_lum + step) > 0 ) ) + { + global_lum += step; + } + else + { + global_lum = MAX_LUMINANCE; + } +} + +int GfxRotateFromDegrees(int deg) +{ + return deg << ROTATE_BIT_SHIFT; +} + +bool GfxIsGPUBusy(void) +{ + return (GsIsDrawing() || gfx_busy || (GfxReadyForDMATransfer() == false) ); +} + +bool GfxSpriteFromFile(char* fname, GsSprite * spr) +{ + GsImage gsi; + + if(SystemLoadFile(fname) == false) + { + return false; + } + + while(GfxIsGPUBusy() == true); + + gfx_busy = true; + + GsImageFromTim(&gsi,SystemGetBufferAddress() ); + + GsSpriteFromImage(spr,&gsi,UPLOAD_IMAGE_FLAG); + gfx_busy = false; + + DEBUG_PRINT_VAR(spr->tpage); + DEBUG_PRINT_VAR(spr->u); + DEBUG_PRINT_VAR(spr->v); + DEBUG_PRINT_VAR(spr->w); + DEBUG_PRINT_VAR(spr->h); + + return true; +} + +bool GfxCLUTFromFile(char* fname) +{ + GsImage gsi; + + if(SystemLoadFile(fname) == false) + { + return false; + } + + while(GfxIsGPUBusy() == true); + + gfx_busy = true; + + GsImageFromTim(&gsi,SystemGetBufferAddress() ); + + GsUploadCLUT(&gsi); + + gfx_busy = false; + + return true; +} + +bool GfxIsInsideScreenArea(short x, short y, short w, short h) +{ + if( ( (x + w) >= 0) + && + (x < DrawEnv.w) + && + ( (y + h) >= 0) + && + (y < DrawEnv.h) ) + { + return true; + } + + return false; +} + +bool GfxIsSpriteInsideScreenArea(GsSprite * spr) +{ + return GfxIsInsideScreenArea(spr->x, spr->y, spr->w, spr->h); +} + +void GfxButtonSetFlags(uint8_t flags) +{ + PSXButtons.attribute |= flags; +} + +void GfxButtonRemoveFlags(uint8_t flags) +{ + PSXButtons.attribute &= ~flags; +} + +void GfxDrawButton(short x, short y, unsigned short btn) +{ + static bool first_entered = true; + static short orig_u; + static short orig_v; + + if(first_entered == true) + { + first_entered = false; + orig_u = PSXButtons.u; + orig_v = PSXButtons.v; + } + + PSXButtons.w = BUTTON_SIZE; + PSXButtons.h = BUTTON_SIZE; + + PSXButtons.r = NORMAL_LUMINANCE; + PSXButtons.g = NORMAL_LUMINANCE; + PSXButtons.b = NORMAL_LUMINANCE; + + PSXButtons.x = x; + PSXButtons.y = y; + PSXButtons.mx = PSXButtons.w >> 1; + PSXButtons.my = PSXButtons.h >> 1; + + switch(btn) + { + case PAD_CROSS: + PSXButtons.u = BUTTON_CROSS_U; + PSXButtons.v = BUTTON_CROSS_V; + break; + + case PAD_SQUARE: + PSXButtons.u = BUTTON_SQUARE_U; + PSXButtons.v = BUTTON_SQUARE_V; + break; + + case PAD_TRIANGLE: + PSXButtons.u = BUTTON_TRIANGLE_U; + PSXButtons.v = BUTTON_TRIANGLE_V; + break; + + case PAD_CIRCLE: + PSXButtons.u = BUTTON_CIRCLE_U; + PSXButtons.v = BUTTON_CIRCLE_V; + break; + + case PAD_RIGHT: + PSXButtons.u = BUTTON_DIRECTION_U; + PSXButtons.v = BUTTON_DIRECTION_V; + break; + + case PAD_UP: + PSXButtons.u = BUTTON_DIRECTION_U; + PSXButtons.v = BUTTON_DIRECTION_V; + PSXButtons.rotate = 90 << ROTATE_BIT_SHIFT; + break; + + case PAD_DOWN: + PSXButtons.u = BUTTON_DIRECTION_U; + PSXButtons.v = BUTTON_DIRECTION_V; + PSXButtons.rotate = 270 << ROTATE_BIT_SHIFT; + break; + + case PAD_LEFT: + PSXButtons.u = BUTTON_DIRECTION_U; + PSXButtons.v = BUTTON_DIRECTION_V; + PSXButtons.attribute |= H_FLIP; + break; + + case PAD_L1: + // Fall through + case PAD_L2: + // Fall through + case PAD_R1: + // Fall through + case PAD_R2: + PSXButtons.u = BUTTON_LR_U; + PSXButtons.v = BUTTON_LR_V; + PSXButtons.w = BUTTON_LR_SIZE; + break; + + case PAD_SELECT: + // Fall through + case PAD_START: + // Fall through + default: + // Set null width and height so that sprite doesn't get sorted + PSXButtons.w = 0; + PSXButtons.h = 0; + break; + } + + PSXButtons.u += orig_u; + PSXButtons.v += orig_v; + + GfxSortSprite(&PSXButtons); + + PSXButtons.attribute &= ~H_FLIP; + PSXButtons.rotate = 0; +} + +void GfxSaveDisplayData(GsSprite *spr) +{ + while(GfxIsGPUBusy() == true); + + MoveImage( DispEnv.x, + DispEnv.y, + GFX_SECOND_DISPLAY_X, + GFX_SECOND_DISPLAY_Y, + X_SCREEN_RESOLUTION, + Y_SCREEN_RESOLUTION); + + spr->x = 0; + spr->y = 0; + spr->tpage = GFX_SECOND_DISPLAY_TPAGE; + spr->attribute |= COLORMODE(COLORMODE_16BPP); + spr->w = X_SCREEN_RESOLUTION; + spr->h = Y_SCREEN_RESOLUTION; + spr->u = 0; + spr->v = 0; + spr->r = NORMAL_LUMINANCE; + spr->g = NORMAL_LUMINANCE; + spr->b = NORMAL_LUMINANCE; + + while(GfxIsGPUBusy() == true); +} + +bool Gfx1HzFlash(void) +{ + return one_second_show; +} + +bool Gfx2HzFlash(void) +{ + return five_hundred_ms_show; +} + +bool GfxTPageOffsetFromVRAMPosition(GsSprite * spr, short x, short y) +{ + if( (x >= VRAM_W) || (x < 0) || (y >= VRAM_H) || (y < 0) ) + { + return false; + } + + spr->tpage = x / GFX_TPAGE_WIDTH; + spr->tpage += (short)(VRAM_W / GFX_TPAGE_WIDTH) * (short)(y / GFX_TPAGE_HEIGHT); + + spr->u = (x % GFX_TPAGE_WIDTH); + + if(spr->attribute & COLORMODE(COLORMODE_8BPP)) + { + // On 8bpp images, it looks like U offset needs to be multiplied by 2. + spr->u <<= 1; + } + + spr->v = (y % GFX_TPAGE_HEIGHT); + + //dprintf("Sprite:\n\tTPAGE: %d\n\tU=%d\n\tV=%d\n",spr->tpage,spr->u, spr->v); + + return false; +} |
