diff --git a/cmake/targets/dolphin.cmake b/cmake/targets/dolphin.cmake index 6194671..559465f 100644 --- a/cmake/targets/dolphin.cmake +++ b/cmake/targets/dolphin.cmake @@ -2,6 +2,8 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC DUSK_PLATFORM_ENDIAN_BIG DUSK_DOLPHIN + DUSK_DISPLAY_WIDTH=640 + DUSK_DISPLAY_HEIGHT=480 ) # Custom compiler flags diff --git a/src/dusk/display/texture/texture.c b/src/dusk/display/texture/texture.c index 7f1fdf1..7a6b2e5 100644 --- a/src/dusk/display/texture/texture.c +++ b/src/dusk/display/texture/texture.c @@ -22,12 +22,13 @@ errorret_t textureInit( ) { assertNotNull(texture, "Texture cannot be NULL"); assertTrue(width > 0 && height > 0, "width/height must be greater than 0"); + assertTrue(width == mathNextPowTwo(width), "Width must be a power of 2."); + assertTrue(height == mathNextPowTwo(height), "Height must be a power of 2."); + memoryZero(texture, sizeof(texture_t)); texture->width = width; texture->height = height; texture->format = format; - assertTrue(width == mathNextPowTwo(width), "Width must be a power of 2."); - assertTrue(height == mathNextPowTwo(height), "Height must be a power of 2."); errorChain(textureInitPlatform(texture, width, height, format, data)); errorOk(); diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index 80001a6..d973bb9 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -50,10 +50,6 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { } errorret_t engineUpdate(void) { - #if DOLPHIN - ENGINE.running = SYS_MainLoop(); - #endif - timeUpdate(); inputUpdate(); diff --git a/src/dusk/error/error.c b/src/dusk/error/error.c index 45b3d63..909378d 100644 --- a/src/dusk/error/error.c +++ b/src/dusk/error/error.c @@ -131,5 +131,6 @@ errorret_t errorPrint(const errorret_t retval) { retval.state->message, retval.state->lines ); + debugFlush(); return retval; } \ No newline at end of file diff --git a/src/dusk/input/inputbutton.c b/src/dusk/input/inputbutton.c index f87cbba..5a7f8af 100644 --- a/src/dusk/input/inputbutton.c +++ b/src/dusk/input/inputbutton.c @@ -10,61 +10,6 @@ #include "assert/assert.h" #include "util/string.h" -// inputbuttondata_t INPUT_BUTTON_DATA[] = { -// #if INPUT_SDL2 == 1 -// #if INPUT_GAMEPAD == 1 -// #if PSP -// { .name = "triangle", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_Y } }, -// { .name = "cross", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_A } }, -// { .name = "circle", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_B } }, -// { .name = "square", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_X } }, -// { .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_START } }, -// { .name = "select", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_BACK } }, -// { .name = "up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_DPAD_UP } }, -// { .name = "down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_DPAD_DOWN } }, -// { .name = "left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_DPAD_LEFT } }, -// { .name = "right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_DPAD_RIGHT } }, -// { .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_LEFTSHOULDER } }, -// { .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER } }, - -// { .name = "lstick_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTX, .positive = true } } }, -// { .name = "lstick_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTX, .positive = false } } }, -// { .name = "lstick_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTY, .positive = true } } }, -// { .name = "lstick_left", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTY, .positive = false } } }, -// #endif - - -// #elif DOLPHIN -// #if INPUT_GAMEPAD == 1 -// { .name = "a", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_A } }, -// { .name = "b", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_B } }, -// { .name = "x", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_X } }, -// { .name = "y", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_Y } }, -// { .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_START } }, -// { .name = "up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_UP } }, -// { .name = "down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_DOWN } }, -// { .name = "left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_LEFT } }, -// { .name = "right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_RIGHT } }, -// { .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_L } }, -// { .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_R } }, -// { .name = "z", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_Z } }, -// { .name = "menu", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_MENU } }, -// { .name = "lstick_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_X, .positive = true } } }, -// { .name = "lstick_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_X, .positive = false } } }, -// { .name = "lstick_left", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_Y, .positive = true } } }, -// { .name = "lstick_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_Y, .positive = false } } }, -// { .name = "rstick_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_X, .positive = true } } }, -// { .name = "rstick_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_X, .positive = false } } }, -// { .name = "rstick_left", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_Y, .positive = true } } }, -// { .name = "rstick_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_Y, .positive = false } } }, -// { .name = "ltrigger", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_TRIGGER_LEFT, .positive = true } } }, -// { .name = "rtrigger", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT, .positive = true } } }, -// #endif -// #endif - -// { .name = NULL } -// }; - inputbutton_t inputButtonGetByName(const char_t *name) { assertNotNull(name, "name must not be NULL"); @@ -77,4 +22,8 @@ inputbutton_t inputButtonGetByName(const char_t *name) { } return (inputbutton_t){ .type = INPUT_BUTTON_TYPE_NONE }; +} + +float_t inputButtonGetValue(const inputbutton_t button) { + return inputButtonGetValuePlatform(button); } \ No newline at end of file diff --git a/src/dusk/time/time.h b/src/dusk/time/time.h index a6a18fa..bb5f45a 100644 --- a/src/dusk/time/time.h +++ b/src/dusk/time/time.h @@ -7,13 +7,14 @@ #pragma once #include "dusk.h" -#include "time/timeplatform.h" #ifndef DUSK_TIME_STEP #define DUSK_TIME_STEP (16.0f / 1000.0f) #endif #ifdef DUSK_TIME_DYNAMIC + #include "time/timeplatform.h" + #ifndef timeTickPlatform #error "DUSK_TIME_DYNAMIC needs tick method defined" #endif diff --git a/src/duskdolphin/CMakeLists.txt b/src/duskdolphin/CMakeLists.txt index 6b8f187..06427fb 100644 --- a/src/duskdolphin/CMakeLists.txt +++ b/src/duskdolphin/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (c) 2026 Dominic Masters -# +# # This software is released under the MIT License. # https://opensource.org/licenses/MIT @@ -9,5 +9,13 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME} ${CMAKE_CURRENT_LIST_DIR} ) +# Sources +target_sources(${DUSK_BINARY_TARGET_NAME} + PUBLIC +) + # Subdirs -add_subdirectory(debug) \ No newline at end of file +add_subdirectory(asset) +add_subdirectory(debug) +add_subdirectory(display) +add_subdirectory(input) \ No newline at end of file diff --git a/src/duskdolphin/asset/CMakeLists.txt b/src/duskdolphin/asset/CMakeLists.txt new file mode 100644 index 0000000..ac94ce4 --- /dev/null +++ b/src/duskdolphin/asset/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + assetdolphin.c +) \ No newline at end of file diff --git a/src/duskdolphin/asset/assetdolphin.c b/src/duskdolphin/asset/assetdolphin.c index 36c1ded..29dc367 100644 --- a/src/duskdolphin/asset/assetdolphin.c +++ b/src/duskdolphin/asset/assetdolphin.c @@ -1,53 +1,73 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ - // Init FAT driver. - if(!fatInitDefault()) errorThrow("Failed to initialize FAT filesystem."); +#include "asset/asset.h" +#include "util/string.h" +#include +#include +#include +#include +#include +#include +#include +#include - char_t **dolphinSearchPath = (char_t **)ASSET_DOLPHIN_PATHS; - char_t foundPath[FILENAME_MAX]; - foundPath[0] = '\0'; - do { - // Try open dir - DIR *pdir = opendir(*dolphinSearchPath); - if(pdir == NULL) continue; +errorret_t assetInitDolphin(void) { + // Init FAT driver. + if(!fatInitDefault()) errorThrow("Failed to initialize FAT filesystem."); + char_t **dolphinSearchPath = (char_t **)ASSET_DOLPHIN_PATHS; + char_t foundPath[FILENAME_MAX]; + foundPath[0] = '\0'; + do { + // Try open dir + DIR *pdir = opendir(*dolphinSearchPath); + if(pdir == NULL) continue; - // Scan if file is present - while(true) { - struct dirent* pent = readdir(pdir); - if(pent == NULL) break; + // Scan if file is present + while(true) { + struct dirent* pent = readdir(pdir); + if(pent == NULL) break; - if(stringCompareInsensitive(pent->d_name, ASSET_FILE) != 0) { - continue; - } - - // Copy out filename - snprintf( - foundPath, - FILENAME_MAX, - "%s/%s", - *dolphinSearchPath, - ASSET_FILE - ); - break; + if(stringCompareInsensitive(pent->d_name, ASSET_FILE_NAME) != 0) { + continue; } - - // Close dir. - closedir(pdir); - // Did we find the file here? - if(foundPath[0] != '\0') break; - } while(*(++dolphinSearchPath) != NULL); - - if(foundPath[0] != '\0') { + // Copy out filename + snprintf( + foundPath, + FILENAME_MAX, + "%s/%s", + *dolphinSearchPath, + ASSET_FILE_NAME + ); + break; } + + // Close dir. + closedir(pdir); - // Did we find the asset file? - if(foundPath[0] == '\0') { - errorThrow("Failed to find asset file on FAT filesystem."); - } + // Did we find the file here? + if(foundPath[0] != '\0') break; + } while(*(++dolphinSearchPath) != NULL); - ASSET.zip = zip_open(foundPath, ZIP_RDONLY, NULL); - if(ASSET.zip == NULL) { - errorThrow("Failed to open asset file on FAT filesystem."); - } - errorOk(); \ No newline at end of file + // Did we find the asset file? + if(foundPath[0] == '\0') { + errorThrow("Failed to find asset file on FAT filesystem."); + } + + ASSET.zip = zip_open(foundPath, ZIP_RDONLY, NULL); + if(ASSET.zip == NULL) { + errorThrow("Failed to open asset file on FAT filesystem."); + } + errorOk(); +} + +errorret_t assetDisposeDolphin() { + // Nothing doing. + errorOk(); +} \ No newline at end of file diff --git a/src/duskdolphin/asset/assetdolphin.h b/src/duskdolphin/asset/assetdolphin.h index 0d7e5cd..0ad62e0 100644 --- a/src/duskdolphin/asset/assetdolphin.h +++ b/src/duskdolphin/asset/assetdolphin.h @@ -6,14 +6,7 @@ */ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include +#include "error/error.h" static const char_t *ASSET_DOLPHIN_PATHS[] = { "/", @@ -34,4 +27,22 @@ static const char_t *ASSET_DOLPHIN_PATHS[] = { "./apps/dusk", "./apps/DUSK", NULL -}; \ No newline at end of file +}; + +typedef struct { + uint8_t nothing; +} assetdolphin_t; + +/** + * Initializes the Dolphin asset system. + * + * @return An error code indicating success or failure. + */ +errorret_t assetInitDolphin(void); + +/** + * Disposes of the Dolphin asset system, freeing any allocated resources. + * + * @return An error code indicating success or failure. + */ +errorret_t assetDisposeDolphin(void); \ No newline at end of file diff --git a/src/duskdolphin/asset/assetplatform.h b/src/duskdolphin/asset/assetplatform.h new file mode 100644 index 0000000..a49bd83 --- /dev/null +++ b/src/duskdolphin/asset/assetplatform.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "assetdolphin.h" + +#define assetInitPlatform assetInitDolphin +#define assetDisposePlatform assetDisposeDolphin + +typedef assetdolphin_t assetplatform_t; \ No newline at end of file diff --git a/src/duskdolphin/debug/debug.c b/src/duskdolphin/debug/debug.c index a79a452..6f42aae 100644 --- a/src/duskdolphin/debug/debug.c +++ b/src/duskdolphin/debug/debug.c @@ -5,8 +5,9 @@ * https://opensource.org/licenses/MIT */ -#include "dolphin.h" +#include "debug/debug.h" #include "display/display.h" +#include static char_t DEBUG_ERROR_BUFFER[16*1024] = {0}; @@ -14,6 +15,10 @@ void debugPrint(const char_t *message, ...) { // append to error buffer size_t start = strlen(DEBUG_ERROR_BUFFER); va_list args; + va_start(args, message); + fprintf(stdout, message, args); + va_end(args); + va_start(args, message); vsnprintf( DEBUG_ERROR_BUFFER + start, @@ -25,6 +30,8 @@ void debugPrint(const char_t *message, ...) { } void debugFlush() { + fflush(stdout); + // Either create graphics, or hijack the displays' graphics. void *xfb = NULL; GXRModeObj *rmode = NULL; diff --git a/src/duskdolphin/display/CMakeLists.txt b/src/duskdolphin/display/CMakeLists.txt new file mode 100644 index 0000000..c52ff4f --- /dev/null +++ b/src/duskdolphin/display/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + displaydolphin.c + # debug.c +) + +# Subdirs +add_subdirectory(camera) +add_subdirectory(framebuffer) +add_subdirectory(mesh) +add_subdirectory(texture) \ No newline at end of file diff --git a/src/duskdolphin/display/camera/CMakeLists.txt b/src/duskdolphin/display/camera/CMakeLists.txt new file mode 100644 index 0000000..a7512fb --- /dev/null +++ b/src/duskdolphin/display/camera/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + cameradolphin.c +) \ No newline at end of file diff --git a/src/duskdolphin/display/camera/cameradolphin.c b/src/duskdolphin/display/camera/cameradolphin.c new file mode 100644 index 0000000..639561d --- /dev/null +++ b/src/duskdolphin/display/camera/cameradolphin.c @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "display/camera/camera.h" +#include "display/framebuffer/framebuffer.h" +#include "assert/assert.h" + +void cameraPushMatrixDolphin(camera_t *camera) { + assertNotNull(camera, "Camera cannot be null"); + + Mtx44 guProjection; + Mtx guView; + Mtx modelView; + + switch(camera->projType) { + case CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC: + guOrtho( + guProjection, + camera->orthographic.top, + camera->orthographic.bottom, + camera->orthographic.left, + camera->orthographic.right, + camera->nearClip, + camera->farClip + ); + break; + + case CAMERA_PROJECTION_TYPE_PERSPECTIVE: + guPerspective( + guProjection, + // FOV is in degrees. + camera->perspective.fov * (180.0f / GLM_PIf), + (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND) / + (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND), + camera->nearClip, + camera->farClip + ); + break; + + case CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED: + assertUnreachable("Flipped perspective not implemented on Dolphin"); + break; + + default: + assertUnreachable("Invalid camera projection type"); + } + + switch(camera->viewType) { + case CAMERA_VIEW_TYPE_LOOKAT: + guVector eye = { + camera->lookat.position[0], + camera->lookat.position[1], + camera->lookat.position[2] + }; + guVector up = { + camera->lookat.up[0], + camera->lookat.up[1], + camera->lookat.up[2] + }; + guVector look = { + camera->lookat.target[0], + camera->lookat.target[1], + camera->lookat.target[2] + }; + guLookAt(guView, &eye, &up, &look); + break; + + case CAMERA_VIEW_TYPE_MATRIX: + assertUnreachable("Matrix camera not implemented"); + break; + + case CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT: + assertUnreachable("Pixel perfect camera not implemented"); + break; + + case CAMERA_VIEW_TYPE_2D: + guMtxIdentity(guView); + guMtxTrans(guView, -camera->_2d.position[0], -camera->_2d.position[1], 0.0f); + guMtxScale(guView, camera->_2d.zoom, camera->_2d.zoom, 1.0f); + break; + + default: + assertUnreachable("Invalid camera view type"); + } + + // Set Projection Matrix + GX_LoadProjectionMtx( + guProjection, + camera->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC ? + GX_ORTHOGRAPHIC : + GX_PERSPECTIVE + ); + + // Set view and model matrix. Dunno how I'll handle models but whatever. + guMtxIdentity(modelView); + guMtxTransApply(modelView, modelView, 0.0F, 0.0F, 0.0F); + guMtxConcat(guView,modelView,modelView); + GX_LoadPosMtxImm(modelView, GX_PNMTX0); +} + +void cameraPopMatrixDolphin(void) { + +} \ No newline at end of file diff --git a/src/duskdolphin/display/camera/cameradolphin.h b/src/duskdolphin/display/camera/cameradolphin.h new file mode 100644 index 0000000..e7bd06b --- /dev/null +++ b/src/duskdolphin/display/camera/cameradolphin.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef struct camera_s camera_t; + +/** + * Pushes the camera's transformation matrix onto the graphics stack. + * + * @param camera The camera to push the matrix of. + */ +void cameraPushMatrixDolphin(camera_t *camera); + +/** + * Pops the camera's transformation matrix from the graphics stack. + */ +void cameraPopMatrixDolphin(void); \ No newline at end of file diff --git a/src/duskdolphin/display/camera/cameraplatform.h b/src/duskdolphin/display/camera/cameraplatform.h new file mode 100644 index 0000000..4264c04 --- /dev/null +++ b/src/duskdolphin/display/camera/cameraplatform.h @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "cameradolphin.h" + +#define cameraPushMatrixPlatform cameraPushMatrixDolphin +#define cameraPopMatrixPlatform cameraPopMatrixDolphin \ No newline at end of file diff --git a/src/duskdolphin/display/displaydolphin.c b/src/duskdolphin/display/displaydolphin.c new file mode 100644 index 0000000..a794c6f --- /dev/null +++ b/src/duskdolphin/display/displaydolphin.c @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "display/display.h" +#include "util/memory.h" +#include "engine/engine.h" + +errorret_t displayInitDolphin(void) { + VIDEO_Init(); + DISPLAY.screenMode = VIDEO_GetPreferredMode(NULL); + DISPLAY.frameBuffer[0] = MEM_K0_TO_K1( + SYS_AllocateFramebuffer(DISPLAY.screenMode) + ); + DISPLAY.frameBuffer[1] = MEM_K0_TO_K1( + SYS_AllocateFramebuffer(DISPLAY.screenMode) + ); + VIDEO_Configure(DISPLAY.screenMode); + + VIDEO_SetNextFramebuffer(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer]); + // VIDEO_SetPostRetraceCallback(copy_buffers); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if(DISPLAY.screenMode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); + + DISPLAY.fifoBuffer = memalign(32, DISPLAY_DOLPHIN_FIFO_SIZE); + memoryZero(DISPLAY.fifoBuffer, DISPLAY_DOLPHIN_FIFO_SIZE); + + GX_Init(DISPLAY.fifoBuffer, DISPLAY_DOLPHIN_FIFO_SIZE); + + // This seems to be mostly related to interlacing vs progressive + GX_SetViewport( + 0, 0, + DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight, + 0, 1 + ); + float_t yscale = GX_GetYScaleFactor( + DISPLAY.screenMode->efbHeight, DISPLAY.screenMode->xfbHeight + ); + uint32_t xfbHeight = GX_SetDispCopyYScale(yscale); + GX_SetScissor( + 0, 0, + DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight + ); + GX_SetDispCopySrc( + 0, 0, + DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight + ); + GX_SetDispCopyDst(DISPLAY.screenMode->fbWidth, xfbHeight); + GX_SetCopyFilter( + DISPLAY.screenMode->aa, + DISPLAY.screenMode->sample_pattern, + GX_TRUE, + DISPLAY.screenMode->vfilter + ); + GX_SetFieldMode( + DISPLAY.screenMode->field_rendering, + ( + (DISPLAY.screenMode->viHeight == 2 * DISPLAY.screenMode->xfbHeight) ? + GX_ENABLE : + GX_DISABLE + ) + ); + + // Setup cull modes + GX_SetCullMode(GX_CULL_NONE); + GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); + GX_CopyDisp(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer], GX_TRUE); + GX_SetDispCopyGamma(GX_GM_1_0); + + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_INDEX16); + GX_SetVtxDesc(GX_VA_CLR0, GX_INDEX16); + GX_SetVtxDesc(GX_VA_TEX0, GX_INDEX16); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_U8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + + errorOk(); +} + +errorret_t displayUpdateDolphin(void) { + ENGINE.running = SYS_MainLoop(); +} + +errorret_t displaySwapDolphin(void) { + GX_DrawDone(); + + DISPLAY.whichFrameBuffer ^= 1; + GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + GX_CopyDisp(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer], GX_TRUE); + VIDEO_SetNextFramebuffer(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer]); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + errorOk(); +} \ No newline at end of file diff --git a/src/duskdolphin/display/displaydolphin.h b/src/duskdolphin/display/displaydolphin.h new file mode 100644 index 0000000..ab7ab6d --- /dev/null +++ b/src/duskdolphin/display/displaydolphin.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" + +#define DISPLAY_DOLPHIN_FIFO_SIZE (256*1024) + +typedef struct { + void *frameBuffer[2];// Double-Bufferred + int whichFrameBuffer; + GXRModeObj *screenMode; + void *fifoBuffer; +} displaydolphin_t; + +/** + * Initializes the display system on Dolphin. + */ +errorret_t displayInitDolphin(void); + +/** + * Tells the display system to actually draw the frame on Dolphin. + */ +errorret_t displayUpdateDolphin(void); + +/** + * Swaps the display buffers on Dolphin. + */ +errorret_t displaySwapDolphin(void); \ No newline at end of file diff --git a/src/duskdolphin/display/displayplatform.h b/src/duskdolphin/display/displayplatform.h new file mode 100644 index 0000000..ab718d0 --- /dev/null +++ b/src/duskdolphin/display/displayplatform.h @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "displaydolphin.h" + +#define displayPlatformInit displayInitDolphin +#define displayPlatformUpdate displayUpdateDolphin +#define displayPlatformSwap displaySwapDolphin + +typedef displaydolphin_t displayplatform_t; \ No newline at end of file diff --git a/src/duskdolphin/display/framebuffer/CMakeLists.txt b/src/duskdolphin/display/framebuffer/CMakeLists.txt new file mode 100644 index 0000000..1f0c0ca --- /dev/null +++ b/src/duskdolphin/display/framebuffer/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + framebufferdolphin.c +) \ No newline at end of file diff --git a/src/duskdolphin/display/framebuffer/framebufferdolphin.c b/src/duskdolphin/display/framebuffer/framebufferdolphin.c new file mode 100644 index 0000000..34e48b3 --- /dev/null +++ b/src/duskdolphin/display/framebuffer/framebufferdolphin.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "display/framebuffer/framebuffer.h" +#include "display/display.h" +#include "assert/assert.h" + +errorret_t frameBufferInitBackBufferDolphin(void) { + errorOk(); +} + +uint32_t frameBufferGetWidthDolphin(const framebufferdolphin_t *framebuffer) { + assertNotNull(framebuffer, "Cannot get width of NULL framebuffer."); + + return DISPLAY.screenMode->fbWidth; +} + +uint32_t frameBufferGetHeightDolphin(const framebufferdolphin_t *framebuffer) { + assertNotNull(framebuffer, "Cannot get height of NULL framebuffer."); + + return DISPLAY.screenMode->efbHeight; +} + +errorret_t frameBufferBindDolphin(framebufferdolphin_t *framebuffer) { + assertNotNull(framebuffer, "Cannot bind NULL framebuffer."); + assertTrue( + framebuffer == &FRAMEBUFFER_BACKBUFFER, + "Cannot bind framebuffer that is not the back buffer." + ); + + GX_InvVtxCache(); + GX_InvalidateTexAll(); + GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); + + GX_SetViewport( + 0, 0, + frameBufferGetWidth(framebuffer), + frameBufferGetHeight(framebuffer), + 0, 1 + ); + + errorOk(); +} + +void frameBufferClearDolphin(const uint8_t flags, const color_t color) { + GX_SetCopyClear( + (GXColor){ color.r, color.g, color.b, color.a }, + GX_MAX_Z24 + ); +} \ No newline at end of file diff --git a/src/duskdolphin/display/framebuffer/framebufferdolphin.h b/src/duskdolphin/display/framebuffer/framebufferdolphin.h new file mode 100644 index 0000000..258a05a --- /dev/null +++ b/src/duskdolphin/display/framebuffer/framebufferdolphin.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/color.h" +#include "error/error.h" + +#ifdef DUSK_DISPLAY_SIZE_DYNAMIC + #error "Dolphin does not support dynamic display sizes." +#endif + +typedef struct { + uint8_t nothing; +} framebufferdolphin_t; + +/** + * Initializes the backbuffer framebuffer. (Dolphin implementation). + */ +errorret_t frameBufferInitBackBufferDolphin(void); + +/** + * Gets the height of the framebuffer. (Dolphin implementation). + * + * @param framebuffer The framebuffer to get the height of. + * @return The height of the framebuffer, or 0 if the framebuffer is NULL. + */ +uint32_t frameBufferGetWidthDolphin(const framebufferdolphin_t *framebuffer); + +/** + * Gets the width of the framebuffer. (Dolphin implementation). + * + * @param framebuffer The framebuffer to get the width of. + * @return The width of the framebuffer, or 0 if the framebuffer is NULL. + */ +uint32_t frameBufferGetHeightDolphin(const framebufferdolphin_t *framebuffer); + +/** + * Binds the framebuffer for rendering. (Dolphin implementation). + * + * @param framebuffer The framebuffer to bind. + * @return Either error or not. + */ +errorret_t frameBufferBindDolphin(framebufferdolphin_t *framebuffer); + +/** + * Clears the framebuffer with the specified flags and color. + * + * @param flags The clear flags. + * @param color The clear color. + */ +void frameBufferClearDolphin(const uint8_t flags, const color_t color); \ No newline at end of file diff --git a/src/duskdolphin/display/framebuffer/framebufferplatform.h b/src/duskdolphin/display/framebuffer/framebufferplatform.h new file mode 100644 index 0000000..65bad6b --- /dev/null +++ b/src/duskdolphin/display/framebuffer/framebufferplatform.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "framebufferdolphin.h" + +typedef framebufferdolphin_t framebufferplatform_t; + +#define frameBufferPlatformInitBackBuffer frameBufferInitBackBufferDolphin +#define frameBufferPlatformGetWidth frameBufferGetWidthDolphin +#define frameBufferPlatformGetHeight frameBufferGetHeightDolphin +#define frameBufferPlatformBind frameBufferBindDolphin +#define frameBufferPlatformClear frameBufferClearDolphin \ No newline at end of file diff --git a/src/duskdolphin/display/mesh/CMakeLists.txt b/src/duskdolphin/display/mesh/CMakeLists.txt new file mode 100644 index 0000000..00c265e --- /dev/null +++ b/src/duskdolphin/display/mesh/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + meshdolphin.c +) \ No newline at end of file diff --git a/src/duskdolphin/display/mesh/meshdolphin.c b/src/duskdolphin/display/mesh/meshdolphin.c new file mode 100644 index 0000000..580cd73 --- /dev/null +++ b/src/duskdolphin/display/mesh/meshdolphin.c @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "display/mesh/mesh.h" +#include "display/texture/texture.h" +#include "assert/assert.h" + +errorret_t meshInitDolphin( + meshdolphin_t *mesh, + const meshprimitivetypedolphin_t primitiveType, + const int32_t vertexCount, + const meshvertex_t *vertices +) { + assertNotNull(mesh, "Mesh cannot be null."); + assertNotNull(vertices, "Vertices cannot be null."); + assertTrue(vertexCount > 0, "Vertex count must be greater than 0."); + + mesh->primitiveType = primitiveType; + mesh->vertexCount = vertexCount; + mesh->vertices = vertices; + + errorOk(); +} + +errorret_t meshDrawDolphin( + const meshdolphin_t *mesh, + const int32_t vertexOffset, + const int32_t vertexCount +) { + assertNotNull(mesh, "Mesh cannot be NULL."); + assertTrue(vertexOffset >= 0, "Vertex offset must be >= 0"); + assertTrue(vertexCount > 0, "Vertex count must be > 0"); + assertTrue( + vertexOffset + vertexCount <= mesh->vertexCount, + "Requested vertex range is invalid" + ); + + // Prepare Vertex descriptor + DCFlushRange( + (void*)&mesh->vertices[vertexOffset], + sizeof(meshvertex_t) * vertexCount + ); + + const u8 stride = (u8)sizeof(meshvertex_t); + GX_SetArray(GX_VA_POS, (void*)&mesh->vertices[vertexOffset].pos[0], stride); + GX_SetArray(GX_VA_CLR0, (void*)&mesh->vertices[vertexOffset].color, stride); + GX_SetArray(GX_VA_TEX0, (void*)&mesh->vertices[vertexOffset].uv[0], stride); + + textureDolphinUploadTEV(); + + GX_Begin(mesh->primitiveType, GX_VTXFMT0, (uint16_t)vertexCount); + for(u16 i = 0; i < (u16)vertexCount; ++i) { + GX_Position1x16(i); + GX_Color1x16(i); + GX_TexCoord1x16(i); + } + GX_End(); + + errorOk(); +} + +int32_t meshGetVertexCountDolphin(const meshdolphin_t *mesh) { + assertNotNull(mesh, "Mesh cannot be NULL."); + return mesh->vertexCount; +} + +errorret_t meshDisposeDolphin(meshdolphin_t *mesh) { + errorOk(); +} \ No newline at end of file diff --git a/src/duskdolphin/display/mesh/meshdolphin.h b/src/duskdolphin/display/mesh/meshdolphin.h new file mode 100644 index 0000000..ec7e8a3 --- /dev/null +++ b/src/duskdolphin/display/mesh/meshdolphin.h @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/mesh/meshvertex.h" +#include "error/error.h" + +typedef enum { + MESH_PRIMITIVE_TYPE_TRIANGLES = GX_TRIANGLES, + MESH_PRIMITIVE_TYPE_LINES = GX_LINES, + MESH_PRIMITIVE_TYPE_POINTS = GX_POINTS, +} meshprimitivetypedolphin_t; + +typedef struct { + const meshvertex_t *vertices; + int32_t vertexCount; + meshprimitivetypedolphin_t primitiveType; +} meshdolphin_t; + +/** + * Initializes a mesh. + * + * @param mesh The mesh to initialize. + * @param primitiveType The Dolphin primitive type (e.g., GX_TRIANGLES). + * @param vertexCount The number of vertices in the mesh. + * @param vertices The vertex data for the mesh. + * @return An error indicating success or failure. + */ +errorret_t meshInitDolphin( + meshdolphin_t *mesh, + const meshprimitivetypedolphin_t primitiveType, + const int32_t vertexCount, + const meshvertex_t *vertices +); + +/** + * Draws a mesh. + * + * @param mesh The mesh to draw. + * @param vertexOffset The offset in the vertex array to start drawing from. + * @param vertexCount The number of vertices to draw. If -1, draws all vertices. + * @return An error indicating success or failure. + */ +errorret_t meshDrawDolphin( + const meshdolphin_t *mesh, + const int32_t vertexOffset, + const int32_t vertexCount +); + +/** + * Gets the vertex count of a mesh. + * + * @param mesh The mesh to get the vertex count from. + * @return The vertex count of the mesh. + */ +int32_t meshGetVertexCountDolphin(const meshdolphin_t *mesh); + +/** + * Disposes of a mesh, freeing any associated resources. + * + * @param mesh The mesh to dispose of. + * @return An error indicating success or failure. + */ +errorret_t meshDisposeDolphin(meshdolphin_t *mesh); \ No newline at end of file diff --git a/src/duskdolphin/display/mesh/meshplatform.h b/src/duskdolphin/display/mesh/meshplatform.h new file mode 100644 index 0000000..f3fb4c2 --- /dev/null +++ b/src/duskdolphin/display/mesh/meshplatform.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "meshdolphin.h" + +#define meshInitPlatform meshInitDolphin +#define meshDrawPlatform meshDrawDolphin +#define meshGetVertexCountPlatform meshGetVertexCountDolphin +#define meshDisposePlatform meshDisposeDolphin +typedef meshprimitivetypedolphin_t meshprimitivetypeplatform_t; +typedef meshdolphin_t meshplatform_t; \ No newline at end of file diff --git a/src/duskdolphin/display/texture/CMakeLists.txt b/src/duskdolphin/display/texture/CMakeLists.txt new file mode 100644 index 0000000..3315ce6 --- /dev/null +++ b/src/duskdolphin/display/texture/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + texturedolphin.c +) \ No newline at end of file diff --git a/src/duskdolphin/display/texture/texturedolphin.c b/src/duskdolphin/display/texture/texturedolphin.c new file mode 100644 index 0000000..bb86bbe --- /dev/null +++ b/src/duskdolphin/display/texture/texturedolphin.c @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "display/texture/texture.h" +#include "assert/assert.h" + +errorret_t textureInitDolphin( + texturedolphin_t *texture, + const int32_t width, + const int32_t height, + const textureformatdolphin_t format, + const texturedata_t data +) { + // switch(format) { + // case TEXTURE_FORMAT_RGBA: + // assertTrue( + // (width % 4) == 0 && (height % 4) == 0, + // "RGB5A3 requires w/h multiple of 4 (or pad)" + // ); + + // // Convert to RGB5A3 format + // size_t rgbaSize = width * height * sizeof(u16); + // texture->rgba = (u16*)memalign(32, rgbaSize); + // assertNotNull(texture->rgba, "Failed to allocate texture RGBA data"); + + // for(uint32_t y = 0; y < height; ++y) { + // for(uint32_t x = 0; x < width; ++x) { + // const int src = y * width + x; + + // const int tileX = x >> 2; + // const int tileY = y >> 2; + // const int tilesPerRow = width >> 2; + // const int tileIndex = tileY * tilesPerRow + tileX; + // const int tileBaseWords = tileIndex * 16; + // const int inTile = ((y & 3) << 2) + (x & 3); + // const int dest = tileBaseWords + inTile; + + // color4b_t col = data.rgba.colors[src]; + + // u16 outCol; + // if(col.a < 255) { + // // 0AAA RRRR GGGG BBBB + // outCol = ( + // (0u << 15) | + // ((u16)(col.a >> 5) << 12) | + // ((u16)(col.r >> 4) << 8) | + // ((u16)(col.g >> 4) << 4) | + // ((u16)(col.b >> 4) << 0) + // ); + // } else { + // // 1RRRR RRGG GGGB BBBB + // outCol = ( + // (1u << 15) | + // ((u16)(col.r >> 3) << 10) | + // ((u16)(col.g >> 3) << 5) | + // ((u16)(col.b >> 3) << 0) + // ); + // } + // texture->rgba[dest] = outCol; + // } + // } + + // DCFlushRange(texture->rgba, rgbaSize); + // GX_InitTexObj( + // &texture->texObj, + // texture->rgba, + // width, height, + // GX_TF_RGB5A3, + // GX_REPEAT, GX_REPEAT, + // GX_FALSE + // ); + + // DCFlushRange(texture->rgba, rgbaSize); + // GX_InvalidateTexAll(); + + // GX_InitTexObjLOD( + // &texture->texObj, + // GX_NEAR, GX_NEAR, + // 0.0f, 0.0f, 0.0f, + // GX_FALSE, + // GX_FALSE, + // GX_ANISO_1 + // ); + // break; + + // case TEXTURE_FORMAT_ALPHA: { + // assertTrue( + // (width % 4) == 0 && (height % 4) == 0, + // "GX_TF_I8 requires w/h multiple of 4 (or pad)" + // ); + + // // 1 byte per pixel (I8), GX expects 4x4 tiled layout + // const size_t alphaSize = (size_t)width * (size_t)height; + + // texture->alpha = (u8*)memalign(32, alphaSize); + // assertNotNull(texture->alpha, "Failed to allocate alpha texture data"); + + // const u32 tilesPerRow = ((u32)width) >> 3; // /8 + + // for (u32 y = 0; y < (u32)height; ++y) { + // const u32 tileY = y >> 2; // /4 + // const u32 inTileY = (y & 3) << 3; // (y%4)*8 + + // for (u32 x = 0; x < (u32)width; ++x) { + // const u32 srcI = y * (u32)width + x; + // const u8 srcA = data.alpha.data[srcI]; // linear input + + // const u32 tileX = x >> 3; // /8 + // const u32 tileIndex = tileY * tilesPerRow + tileX; + + // const u32 tileBase = tileIndex * 32; // 8*4*1 = 32 bytes per tile + // const u32 inTile = inTileY + (x & 7); // (y%4)*8 + (x%8) + + // texture->alpha[tileBase + inTile] = 0xFF - srcA;// Fixes inverted alpha. + // } + // } + + // // Flush CPU cache so GX sees the swizzled I8 texture data + // DCFlushRange(texture->alpha, alphaSize); + + // // Initialize GX texture object with swizzled data + // GX_InitTexObj( + // &texture->texObj, + // texture->alpha, + // width, height, + // GX_TF_I8, + // GX_REPEAT, GX_REPEAT, + // GX_FALSE + // ); + + // GX_InitTexObjLOD( + // &texture->texObj, + // GX_NEAR, GX_NEAR, + // 0.0f, 0.0f, 0.0f, + // GX_FALSE, + // GX_FALSE, + // GX_ANISO_1 + // ); + // break; + // } + + // case TEXTURE_FORMAT_PALETTE: { + // // Not supported, convert to RGBA using lookup + // color_t* formatted = memoryAllocate(width * height * sizeof(color_t)); + // for(int32_t i = 0; i < width * height; i++) { + // uint8_t index = data.palette.data[i]; + // assertTrue( + // index < data.palette.palette->colorCount, + // "Palette index out of range" + // ); + // formatted[i] = data.palette.palette->colors[index]; + // } + + // textureInit( + // texture, width, height, TEXTURE_FORMAT_RGBA, + // (texturedata_t){ + // .rgba = { .colors = formatted } + // } + // ); + // memoryFree(formatted); + // break; + // } + + // default: + // assertUnreachable("Unsupported texture format for Dolphin"); + // break; + // } + + // texture->ready = true; + + errorOk(); +} + +errorret_t textureBindDolphin(texturedolphin_t *texture) { + errorOk(); +} + +errorret_t textureDisposeDolphin(texturedolphin_t *texture) { + errorOk(); +} + +void textureDolphinUploadTEV(void) { + +} \ No newline at end of file diff --git a/src/duskdolphin/display/texture/texturedolphin.h b/src/duskdolphin/display/texture/texturedolphin.h new file mode 100644 index 0000000..311e2d8 --- /dev/null +++ b/src/duskdolphin/display/texture/texturedolphin.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef union texturedata_u texturedata_t; + +typedef enum { + TEXTURE_FORMAT_RGBA = GX_TF_RGBA8, + TEXTURE_FORMAT_PALETTE = GX_TF_CI8, +} textureformatdolphin_t; + +typedef struct { + GXTexObj texObj; + textureformatdolphin_t format; + int32_t width; + int32_t height; +} texturedolphin_t; + +/** + * Initializes a texture. + * + * @param texture The texture to initialize. + * @param width The width of the texture. + * @param height The height of the texture. + * @param format The format of the texture (e.g., GX_TF_RGBA8, GX_TF_CI8). + * @param data The data for the texture, the format changes per format. + * @return An error if the texture failed to initialize, otherwise success. + */ +errorret_t textureInitDolphin( + texturedolphin_t *texture, + const int32_t width, + const int32_t height, + const textureformatdolphin_t format, + const texturedata_t data +); + +/** + * Binds a texture for rendering. Providing NULL will unbind any texture. + * + * @param texture The texture to bind. + * @return An error if the texture failed to bind, otherwise success. + */ +errorret_t textureBindDolphin(texturedolphin_t *texture); + +/** + * Disposes a texture. + * + * @param texture The texture to dispose. + * @return An error if the texture failed to dispose, otherwise success. + */ +errorret_t textureDisposeDolphin(texturedolphin_t *texture); + +/** + * Internal method that uploads the texture environment variables to the GPU + * for rendering. This is basically uploading the shader information. + */ +void textureDolphinUploadTEV(void); \ No newline at end of file diff --git a/src/duskdolphin/display/texture/textureplatform.h b/src/duskdolphin/display/texture/textureplatform.h new file mode 100644 index 0000000..33585f7 --- /dev/null +++ b/src/duskdolphin/display/texture/textureplatform.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "texturedolphin.h" + +typedef textureformatdolphin_t textureformatplatform_t; +typedef texturedolphin_t textureplatform_t; + +#define textureInitPlatform textureInitDolphin +#define textureBindPlatform textureBindDolphin +#define textureDisposePlatform textureDisposeDolphin \ No newline at end of file diff --git a/src/duskdolphin/input/CMakeLists.txt b/src/duskdolphin/input/CMakeLists.txt new file mode 100644 index 0000000..b88e182 --- /dev/null +++ b/src/duskdolphin/input/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + inputdolphin.c +) + +# Subdirs \ No newline at end of file diff --git a/src/duskdolphin/input/inputdolphin.c b/src/duskdolphin/input/inputdolphin.c new file mode 100644 index 0000000..5a3a807 --- /dev/null +++ b/src/duskdolphin/input/inputdolphin.c @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "input/input.h" +#include "assert/assert.h" + +inputbuttondata_t INPUT_BUTTON_DATA[] = { + #ifdef DUSK_INPUT_GAMEPAD + { .name = "a", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_A } }, + { .name = "b", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_B } }, + { .name = "x", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_X } }, + { .name = "y", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_Y } }, + { .name = "start", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_START } }, + { .name = "up", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_UP } }, + { .name = "down", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_DOWN } }, + { .name = "left", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_LEFT } }, + { .name = "right", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_RIGHT } }, + { .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_L } }, + { .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_R } }, + { .name = "z", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_TRIGGER_Z } }, + { .name = "menu", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = PAD_BUTTON_MENU } }, + { .name = "lstick_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_X, .positive = true } } }, + { .name = "lstick_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_X, .positive = false } } }, + { .name = "lstick_left", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_Y, .positive = true } } }, + { .name = "lstick_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_LEFT_Y, .positive = false } } }, + { .name = "rstick_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_X, .positive = true } } }, + { .name = "rstick_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_X, .positive = false } } }, + { .name = "rstick_left", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_Y, .positive = true } } }, + { .name = "rstick_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_C_Y, .positive = false } } }, + { .name = "ltrigger", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_TRIGGER_LEFT, .positive = true } } }, + { .name = "rtrigger", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT, .positive = true } } }, + #endif + + { .name = NULL } +}; + +void inputUpdateDolphin(void) { + PAD_ScanPads(); + + #ifdef DUSK_INPUT_GAMEPAD + for(uint8_t i = 0; i < INPUT_PAD_COUNT; i++) { + INPUT.platform.padState[i] = PAD_ButtonsDown(i); + + INPUT.platform.pads[i][INPUT_GAMEPAD_AXIS_LEFT_X] = ( + INPUT_DOLPHIN_AXIS(PAD_StickX(i)) + ); + + INPUT.platform.pads[i][INPUT_GAMEPAD_AXIS_LEFT_Y] = ( + INPUT_DOLPHIN_AXIS(PAD_StickY(i)) + ); + + INPUT.platform.pads[i][INPUT_GAMEPAD_AXIS_C_X] = ( + INPUT_DOLPHIN_AXIS(PAD_SubStickX(i)) + ); + + INPUT.platform.pads[i][INPUT_GAMEPAD_AXIS_C_Y] = ( + INPUT_DOLPHIN_AXIS(PAD_SubStickY(i)) + ); + + INPUT.platform.pads[i][INPUT_GAMEPAD_AXIS_TRIGGER_LEFT] = ( + INPUT_DOLPHIN_AXIS(PAD_TriggerL(i)) + ); + + INPUT.platform.pads[i][INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT] = ( + INPUT_DOLPHIN_AXIS(PAD_TriggerR(i)) + ); + } + #endif +} + +float_t inputButtonGetValueDolphin(const inputbutton_t button) { + switch(button.type) { + #ifdef DUSK_INPUT_GAMEPAD + case INPUT_BUTTON_TYPE_GAMEPAD: { + if(INPUT.padState[0] & button.gpButton) return 1.0f; + return 0.0f; + } + + case INPUT_BUTTON_TYPE_GAMEPAD_AXIS: { + float_t value = INPUT.pads[0][button.gpAxis.axis]; + if(!button.gpAxis.positive) value = -value; + if(value >= INPUT.deadzone) return value; + return 0.0f; + } + #endif + + default: { + assertUnreachable("Unknown input button type"); + return 0.0f; + } + } +} \ No newline at end of file diff --git a/src/duskdolphin/input/inputdolphin.h b/src/duskdolphin/input/inputdolphin.h new file mode 100644 index 0000000..e17db80 --- /dev/null +++ b/src/duskdolphin/input/inputdolphin.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +#define INPUT_DOLPHIN_PAD_COUNT PAD_CHANMAX +#define INPUT_DOLPHIN_AXIS(value) ((float_t)(value) / 128.0f) + +typedef struct inputbutton_s inputbutton_t; + +#ifdef DUSK_INPUT_POINTER + #error "Wii not implemented" +#endif + +#ifdef DUSK_INPUT_KEYBOARD + #error "Keyboard not implemented" +#endif + +#ifdef DUSK_INPUT_GAMEPAD + // TODO: Can I support wiimote, gamecube, pro controller, etc? + + typedef u16 inputgamepadbuttondolphin_t; + typedef enum { + INPUT_GAMEPAD_AXIS_LEFT_X, + INPUT_GAMEPAD_AXIS_LEFT_Y, + INPUT_GAMEPAD_AXIS_C_X, + INPUT_GAMEPAD_AXIS_C_Y, + INPUT_GAMEPAD_AXIS_TRIGGER_LEFT, + INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT, + + INPUT_GAMEPAD_AXIS_COUNT + } inputgamepadaxisdolphin_t; +#endif + +typedef struct { + #ifdef DUSK_INPUT_GAMEPAD + int padState[INPUT_DOLPHIN_PAD_COUNT]; + float_t pads[INPUT_DOLPHIN_PAD_COUNT][INPUT_GAMEPAD_AXIS_COUNT]; + #endif +} inputdolphin_t; + +/** + * Updates the input state for Dolphin. + */ +void inputUpdateDolphin(void); + +/** + * Returns the input value (between 0 and 1) of the given button. + * + * @param button The button to get the value of. + * @return The value of the button, between 0 and 1. + */ +float_t inputButtonGetValueDolphin(const inputbutton_t button); \ No newline at end of file diff --git a/src/duskdolphin/input/inputplatform.h b/src/duskdolphin/input/inputplatform.h new file mode 100644 index 0000000..4a02e38 --- /dev/null +++ b/src/duskdolphin/input/inputplatform.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "inputdolphin.h" + +#ifdef DUSK_INPUT_GAMEPAD + typedef inputgamepadbuttondolphin_t inputgamepadbuttonplatform_t; + typedef inputgamepadaxisdolphin_t inputgamepadaxissplatform_t; +#endif + +typedef inputdolphin_t inputplatform_t; + +#define inputUpdatePlatform inputUpdateDolphin +#define inputButtonGetValuePlatform inputButtonGetValueDolphin \ No newline at end of file diff --git a/src/duskpsp/CMakeLists.txt b/src/duskpsp/CMakeLists.txt index 1606ee9..45c83bd 100644 --- a/src/duskpsp/CMakeLists.txt +++ b/src/duskpsp/CMakeLists.txt @@ -12,7 +12,6 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME} # Sources target_sources(${DUSK_BINARY_TARGET_NAME} PUBLIC - # hello.c ) # Subdirs diff --git a/src/duskpsp/hello.c b/src/duskpsp/hello.c deleted file mode 100644 index e097812..0000000 --- a/src/duskpsp/hello.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include - -// PSP_MODULE_INFO is required -PSP_MODULE_INFO("Hello World", 0, 1, 0); -PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER); - -int exit_callback(int arg1, int arg2, void *common) { - sceKernelExitGame(); - return 0; -} - -int callback_thread(SceSize args, void *argp) { - int cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL); - sceKernelRegisterExitCallback(cbid); - sceKernelSleepThreadCB(); - return 0; -} - -int setup_callbacks(void) { - int thid = sceKernelCreateThread("update_thread", callback_thread, 0x11, 0xFA0, 0, 0); - if(thid >= 0) - sceKernelStartThread(thid, 0, 0); - return thid; -} - -int main(void) { - // Use above functions to make exiting possible - setup_callbacks(); - - // Print Hello World! on a debug screen on a loop - pspDebugScreenInit(); - while(1) { - pspDebugScreenSetXY(0, 0); - pspDebugScreenPrintf("Hello World!"); - sceDisplayWaitVblankStart(); - } - - return 0; -} diff --git a/src/dusksdl2/input/inputsdl2.c b/src/dusksdl2/input/inputsdl2.c index b112aee..79ef30e 100644 --- a/src/dusksdl2/input/inputsdl2.c +++ b/src/dusksdl2/input/inputsdl2.c @@ -38,7 +38,7 @@ void inputUpdateSDL2(void) { #endif } -float_t inputButtonGetValue(const inputbutton_t button) { +float_t inputButtonGetValueSDL2(const inputbutton_t button) { switch(button.type) { #ifdef DUSK_INPUT_KEYBOARD case INPUT_BUTTON_TYPE_KEYBOARD: {