Moved a bunch of code around

This commit is contained in:
2026-03-07 09:35:56 -06:00
parent 93074d653e
commit dd048d9b0d
40 changed files with 810 additions and 842 deletions

View File

@@ -7,99 +7,7 @@
#include "sdl2.h"
void displaySDL2Init(void) {
uint32_t flags = SDL_INIT_VIDEO;
#if INPUT_GAMEPAD == 1
flags |= SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK;
#endif
if(SDL_Init(flags) != 0) {
errorThrow("SDL Failed to Initialize: %s", SDL_GetError());
}
// Set OpenGL attributes (Needs to be done now or later?)
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// Create window with OpenGL flag.
DISPLAY.window = SDL_CreateWindow(
"Dusk",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
DISPLAY_WINDOW_WIDTH_DEFAULT,
DISPLAY_WINDOW_HEIGHT_DEFAULT,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_OPENGL
);
if(!DISPLAY.window) {
errorThrow("SDL_CreateWindow failed: %s", SDL_GetError());
}
// Create OpenGL context
DISPLAY.glContext = SDL_GL_CreateContext(DISPLAY.window);
if(!DISPLAY.glContext) {
errorThrow("SDL_GL_CreateContext failed: %s", SDL_GetError());
}
SDL_GL_SetSwapInterval(1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
glDisable(GL_CULL_FACE);
glDisable(GL_LIGHTING);// PSP defaults this on?
glShadeModel(GL_SMOOTH); // Fixes color on PSP?
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearDepth(1.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glEnableClientState(GL_COLOR_ARRAY);// To confirm: every frame on PSP?
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
// Get and validate GL state.
GLenum err = glGetError();
if(err != GL_NO_ERROR) {
assertUnreachable("GL Error before checking support");
}
#if PSP
displayInitPSP();
#else
#endif
}
void displaySDL2Update(void) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_QUIT: {
ENGINE.running = false;
break;
}
case SDL_WINDOWEVENT: {
switch(event.window.event) {
case SDL_WINDOWEVENT_CLOSE: {
ENGINE.running = false;
break;
}
default: {
break;
}
}
}
default: {
break;
}
}
}
SDL_GL_MakeCurrent(DISPLAY.window, DISPLAY.glContext);
}
void displaySDL2Swap(void) {

View File

@@ -14,4 +14,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_SDL2
DUSK_OPENGL
DUSK_LINUX
DUSK_DISPLAY_SIZE_DYNAMIC
DUSK_DISPLAY_WIDTH_DEFAULT=640
DUSK_DISPLAY_HEIGHT_DEFAULT=480
)

View File

@@ -8,12 +8,12 @@ add_subdirectory(dusk)
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
add_subdirectory(dusklinux)
add_subdirectory(dusksdl2)
add_subdirectory(duskopengl)
add_subdirectory(duskgl)
elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
add_subdirectory(duskpsp)
add_subdirectory(dusksdl2)
add_subdirectory(duskopengl)
add_subdirectory(duskgl)
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
add_subdirectory(duskdolphin)

View File

@@ -7,9 +7,6 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
display.c
framebuffer.c
screen.c
spritebatch.c
text.c
)

View File

@@ -8,8 +8,8 @@
#include "camera.h"
#include "display/display.h"
#include "assert/assert.h"
#include "display/framebuffer.h"
#include "display/screen.h"
#include "display/framebuffer/framebuffer.h"
#include "display/screen/screen.h"
void cameraInit(camera_t *camera) {
cameraInitPerspective(camera);

View File

@@ -7,11 +7,11 @@
#include "display/display.h"
#include "engine/engine.h"
#include "display/framebuffer.h"
#include "display/framebuffer/framebuffer.h"
#include "scene/scene.h"
#include "display/spritebatch.h"
#include "display/spritebatch/spritebatch.h"
#include "display/mesh/quad.h"
#include "display/screen.h"
#include "display/screen/screen.h"
#include "ui/ui.h"
#include "debug/debug.h"
#include "display/text.h"
@@ -25,10 +25,8 @@ display_t DISPLAY = { 0 };
errorret_t displayInit(void) {
memoryZero(&DISPLAY, sizeof(DISPLAY));
#if DISPLAY_SDL2
displaySDL2Init();
#elif DOLPHIN
displayDolphinInit();
#ifdef displayPlatformInit
errorChain(displayPlatformInit());
#endif
quadInit();
@@ -42,8 +40,8 @@ errorret_t displayInit(void) {
}
errorret_t displayUpdate(void) {
#if DISPLAY_SDL2
displaySDL2Update();
#ifdef displayPlatformUpdate
errorChain(displayPlatformUpdate());
#endif
// Reset state
@@ -60,19 +58,16 @@ errorret_t displayUpdate(void) {
errorChain(sceneRender());
// Render UI
// uiRender();
uiRender();
// Finish up
screenUnbind();
screenRender();
#if DISPLAY_SDL2
displaySDL2Swap();
#elif DOLPHIN
displayDolphinSwap();
// Swap and return.
#ifdef displayPlatformSwap
errorChain(displayPlatformSwap());
#endif
// For now, we just return an OK error.
errorOk();
}
@@ -81,8 +76,8 @@ errorret_t displayDispose(void) {
screenDispose();
textDispose();
#if DISPLAY_SDL2
displaySDL2Dispose();
#ifdef displayPlatformDispose
displayPlatformDispose();
#endif
// For now, we just return an OK error.

View File

@@ -6,12 +6,35 @@
*/
#pragma once
#include "displaydefs.h"
#include "error/error.h"
#include "display/camera/camera.h"
#include "display/framebuffer.h"
#include "display/displayplatform.h"
#include "display/camera/camera.h"
// Expecting some definitions to be provided
#ifndef DUSK_DISPLAY_SIZE_DYNAMIC
#ifndef DUSK_DISPLAY_WIDTH
#error "DUSK_DISPLAY_WIDTH must be defined."
#endif
#ifndef DUSK_DISPLAY_HEIGHT
#error "DUSK_DISPLAY_HEIGHT must be defined"
#endif
#define DISPLAY_WINDOW_WIDTH_DEFAULT DUSK_DISPLAY_WIDTH
#define DISPLAY_WINDOW_HEIGHT_DEFAULT DUSK_DISPLAY_HEIGHT
#else
#ifndef DUSK_DISPLAY_WIDTH_DEFAULT
#error "DUSK_DISPLAY_WIDTH_DEFAULT must be defined."
#endif
#ifndef DUSK_DISPLAY_HEIGHT_DEFAULT
#error "DUSK_DISPLAY_HEIGHT_DEFAULT must be defined."
#endif
#ifdef DUSK_DISPLAY_WIDTH
#error "DUSK_DISPLAY_WIDTH should not be defined."
#endif
#ifdef DUSK_DISPLAY_HEIGHT
#error "DUSK_DISPLAY_HEIGHT should not be defined."
#endif
#endif
// Main Display Struct, platform-speicifc
typedef displayplatform_t display_t;
extern display_t DISPLAY;

View File

@@ -1,42 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#if DISPLAY_SDL2
#include <SDL2/SDL.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#ifndef DISPLAY_SIZE_DYNAMIC
#define DISPLAY_SIZE_DYNAMIC 1
#endif
#elif DOLPHIN
// Dolphin.
#define DISPLAY_FIFO_SIZE (256*1024)
#else
#error "Need to specify display backend."
#endif
#if DISPLAY_SIZE_DYNAMIC == 0
#ifndef DISPLAY_WIDTH
#error "DISPLAY_WIDTH must be defined when DISPLAY_SIZE_DYNAMIC is 0."
#endif
#ifndef DISPLAY_HEIGHT
#error "DISPLAY_HEIGHT must be defined when DISPLAY_SIZE_DYNAMIC is 0."
#endif
#endif
#ifndef DISPLAY_WINDOW_WIDTH_DEFAULT
#error "DISPLAY_WINDOW_WIDTH_DEFAULT must be defined."
#endif
#ifndef DISPLAY_WINDOW_HEIGHT_DEFAULT
#define DISPLAY_WINDOW_HEIGHT_DEFAULT DISPLAY_HEIGHT
#endif

View File

@@ -1,192 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "framebuffer.h"
#include "display/display.h"
#include "assert/assert.h"
#include "util/memory.h"
framebuffer_t FRAMEBUFFER_BACKBUFFER = {0};
const framebuffer_t *FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
void frameBufferInitBackbuffer() {
memoryZero(&FRAMEBUFFER_BACKBUFFER, sizeof(framebuffer_t));
FRAMEBUFFER_BACKBUFFER.id = -1;
FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
}
#if DISPLAY_SIZE_DYNAMIC == 1
void frameBufferInit(
framebuffer_t *framebuffer,
const uint32_t width,
const uint32_t height
) {
#if DISPLAY_SDL2 == 1
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
assertTrue(width > 0 && height > 0, "W/H must be greater than 0");
memoryZero(framebuffer, sizeof(framebuffer_t));
textureInit(&framebuffer->texture, width, height, TEXTURE_FORMAT_RGBA,(texturedata_t){
.rgbaColors = NULL
});
glGenFramebuffersEXT(1, &framebuffer->id);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
glFramebufferTexture2DEXT(
GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, framebuffer->texture.id, 0
);
if(
glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) !=
GL_FRAMEBUFFER_COMPLETE_EXT
) {
assertUnreachable("Framebuffer is not complete");
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
#endif
}
#endif
int32_t frameBufferGetWidth(const framebuffer_t *framebuffer) {
#if DISPLAY_SDL2
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
#if DISPLAY_SIZE_DYNAMIC == 0
return DISPLAY_WIDTH;
#else
int32_t windowWidth, windowHeight;
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
return windowWidth;
#endif
}
return framebuffer->texture.width;
#elif DOLPHIN
return DISPLAY.screenMode->fbWidth;
#else
#error "Unsupported DISPLAY_TYPE."
#endif
}
int32_t frameBufferGetHeight(const framebuffer_t *framebuffer) {
#if DISPLAY_SDL2
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
#if DISPLAY_SIZE_DYNAMIC == 0
return DISPLAY_HEIGHT;
#else
int32_t windowWidth, windowHeight;
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
return windowHeight;
#endif
}
return framebuffer->texture.height;
#elif DOLPHIN
return DISPLAY.screenMode->efbHeight;
#else
#error "Unsupported DISPLAY_TYPE."
#endif
}
void frameBufferBind(const framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
frameBufferBind(&FRAMEBUFFER_BACKBUFFER);
FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
return;
}
// Bind the framebuffer for rendering
#if DISPLAY_SDL2
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
#if PSP
#else
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
#endif
} else {
#if PSP
assertUnreachable("Framebuffers not supported on PSP");
#else
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
#endif
}
glViewport(
0, 0,
frameBufferGetWidth(framebuffer), frameBufferGetHeight(framebuffer)
);
#elif DOLPHIN
GX_InvVtxCache();
GX_InvalidateTexAll();
GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
GX_SetViewport(
0, 0,
frameBufferGetWidth(framebuffer),
frameBufferGetHeight(framebuffer),
0, 1
);
#endif
FRAMEBUFFER_BOUND = framebuffer;
}
void frameBufferClear(uint8_t flags, color_t color) {
#if DISPLAY_SDL2
GLbitfield glFlags = 0;
if(flags & FRAMEBUFFER_CLEAR_COLOR) {
glFlags |= GL_COLOR_BUFFER_BIT;
glClearColor(
color.r / 255.0f,
color.g / 255.0f,
color.b / 255.0f,
color.a / 255.0f
);
}
if(flags & FRAMEBUFFER_CLEAR_DEPTH) {
glFlags |= GL_DEPTH_BUFFER_BIT;
}
glClear(glFlags);
#elif DOLPHIN
GX_SetCopyClear(
(GXColor){ color.r, color.g, color.b, color.a },
GX_MAX_Z24
);
#endif
}
void frameBufferDispose(framebuffer_t *framebuffer) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
#if DISPLAY_SDL2
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
assertUnreachable("Cannot dispose of backbuffer");
}
#if DISPLAY_SIZE_DYNAMIC == 0
assertUnreachable("Dynamic size framebuffers not supported");
#else
textureDispose(&framebuffer->texture);
glDeleteFramebuffersEXT(1, &framebuffer->id);
#endif
#endif
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "framebuffer.h"
#include "display/display.h"
#include "assert/assert.h"
#include "util/memory.h"
framebuffer_t FRAMEBUFFER_BACKBUFFER = {0};
const framebuffer_t *FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
errorret_t frameBufferInitBackbuffer() {
memoryZero(&FRAMEBUFFER_BACKBUFFER, sizeof(framebuffer_t));
errorChain(frameBufferPlatformInitBackbuffer());
FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
}
void frameBufferBind(framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
frameBufferBind(&FRAMEBUFFER_BACKBUFFER);
FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
return;
}
errorChain(frameBufferPlatformBind(framebuffer));
FRAMEBUFFER_BOUND = framebuffer;
errorOk();
}
void frameBufferDispose(framebuffer_t *framebuffer) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
#if DISPLAY_SDL2
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
assertUnreachable("Cannot dispose of backbuffer");
}
#if DISPLAY_SIZE_DYNAMIC == 0
assertUnreachable("Dynamic size framebuffers not supported");
#else
textureDispose(&framebuffer->texture);
glDeleteFramebuffersEXT(1, &framebuffer->id);
#endif
#endif
}
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
errorret_t frameBufferInit(
framebuffer_t *fb,
const uint32_t width,
const uint32_t height
) {
assertNotNull(fb, "Framebuffer cannot be NULL");
assertTrue(width > 0 && height > 0, "W/H must be greater than 0");
memoryZero(fb, sizeof(framebuffer_t));
errorChain(frameBufferPlatformInit(fb, width, height));
errorOk();
}
#endif

View File

@@ -6,46 +6,32 @@
*/
#pragma once
#include "error/error.h"
#include "display/framebuffer/framebufferplatform.h"
#include "display/texture/texture.h"
// Expected defs.
#ifndef frameBufferPlatformInitBackBuffer
#error "frameBufferPlatformInitBackBuffer not defined for this platform"
#endif
#ifndef frameBufferPlatformBind
#error "frameBufferPlatformBind not defined for this platform"
#endif
#define FRAMEBUFFER_CLEAR_COLOR (1 << 0)
#define FRAMEBUFFER_CLEAR_DEPTH (1 << 1)
typedef struct {
#if DISPLAY_SDL2 == 1
// OpenGL Framebuffer Object ID
GLuint id;
texture_t texture;
#elif DOLPHIN
// --- IGNORE ---
uint8_t id;
#else
#error "Framebuffers not implemented on this platform."
#endif
} framebuffer_t;
typedef framebufferplatform_t framebuffer_t;
extern framebuffer_t FRAMEBUFFER_BACKBUFFER;
extern const framebuffer_t *FRAMEBUFFER_BOUND;
/**
* Initializes the backbuffer framebuffer.
*/
void frameBufferInitBackbuffer(void);
/**
* Initializes a framebuffer.
*
* @param framebuffer The framebuffer to initialize.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
* @return Error for initialization of the backbuffer.
*/
#if DISPLAY_SIZE_DYNAMIC == 1
void frameBufferInit(
framebuffer_t *framebuffer,
const uint32_t width,
const uint32_t height
);
#endif
errorret_t frameBufferInitBackBuffer(void);
/**
* Gets the width of the framebuffer.
@@ -53,7 +39,7 @@ void frameBufferInitBackbuffer(void);
* @param framebuffer The framebuffer to get the width of.
* @return The width of the framebuffer, or 0 if the framebuffer is NULL.
*/
int32_t frameBufferGetWidth(const framebuffer_t *framebuffer);
uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer);
/**
* Gets the height of the framebuffer.
@@ -61,7 +47,7 @@ int32_t frameBufferGetWidth(const framebuffer_t *framebuffer);
* @param framebuffer The framebuffer to get the height of.
* @return The height of the framebuffer, or 0 if the framebuffer is NULL.
*/
int32_t frameBufferGetHeight(const framebuffer_t *framebuffer);
uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer);
/**
* Binds the framebuffer for rendering, or the backbuffer if the framebuffer
@@ -80,30 +66,29 @@ void frameBufferBind(const framebuffer_t *framebuffer);
void frameBufferClear(uint8_t flags, color_t color);
/**
* Disposes of the framebuffer using EXT methods.
* Disposes of the framebuffer. Will also be used for request disposing of the
* backbuffer.
*
* @param framebuffer The framebuffer to dispose of.
*/
void frameBufferDispose(framebuffer_t *framebuffer);
// #if RENDER_USE_FRAMEBUFFER
// typedef struct {
// GLuint id;
// texture_t texture;
// } framebuffer_t;
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
#ifndef frameBufferPlatformInit
#error "frameBufferPlatformInit not defined for this platform"
#endif
// /**
// * Initializes a framebuffer using EXT methods.
// *
// * @param framebuffer The framebuffer to initialize.
// * @param width The width of the framebuffer.
// * @param height The height of the framebuffer.
// * @return An error code indicating success or failure.
// */
// void frameBufferInit(
// framebuffer_t *framebuffer,
// const uint32_t width,
// const uint32_t height
// );
// #endif
/**
* Initializes a framebuffer.
*
* @param framebuffer The framebuffer to initialize.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
* @return Error for initialization of the framebuffer.
*/
errorret_t frameBufferInit(
framebuffer_t *framebuffer,
const uint32_t width,
const uint32_t height
);
#endif

View File

@@ -11,9 +11,9 @@
#include "util/math.h"
#include "display/display.h"
const texture_t *TEXTURE_BOUND = NULL;
texture_t *TEXTURE_BOUND = NULL;
void textureInit(
errorret_t textureInit(
texture_t *texture,
const int32_t width,
const int32_t height,
@@ -22,401 +22,29 @@ void textureInit(
) {
assertNotNull(texture, "Texture cannot be NULL");
assertTrue(width > 0 && height > 0, "width/height must be greater than 0");
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.");
#if DISPLAY_SDL2
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
switch(format) {
case TEXTURE_FORMAT_RGBA:
glTexImage2D(
GL_TEXTURE_2D, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, (void*)data.rgbaColors
);
break;
case TEXTURE_FORMAT_PALETTE:
assertNotNull(data.paletteData, "Palette texture data cannot be NULL");
if(DISPLAY.usingShaderedPalettes) {
// Palette textures not supported, convert to GL_RED style texture
// so shader can perform the lookup.
uint8_t formatted[width * height];
for(int32_t i = 0; i < width * height; i++) {
uint8_t index = data.paletteData[i];
formatted[i] = index * 128;
}
glTexImage2D(
GL_TEXTURE_2D, 0, GL_R8, width, height, 0,
GL_RED, GL_UNSIGNED_BYTE, (void*)formatted
);
} else {
glTexImage2D(
GL_TEXTURE_2D,
0, GL_COLOR_INDEX8_EXT,
width, height,
0, GL_COLOR_INDEX8_EXT,
GL_UNSIGNED_BYTE, (void*)data.paletteData
);
// glColorTableEXT(
// GL_TEXTURE_2D, GL_RGBA, data.palette.palette->colorCount, GL_RGBA,
// GL_UNSIGNED_BYTE, (const void*)data.palette.palette->colors
// );
errorChain(textureInitPlatform(texture, width, height, format, data));
errorOk();
}
GLenum err = glGetError();
if(err != GL_NO_ERROR) {
printf("GL Error uploading palette texture: %d\n", err);
assertUnreachable("GL error uploading palette texture");
}
break;
default:
assertUnreachable("Unknown texture format");
break;
errorret_t textureBind(texture_t *texture) {
errorChain(textureBindPlatform(texture));
errorOk();
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, 0);
texture->ready = true;
#elif DOLPHIN
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;
#endif
}
void textureBind(texture_t *texture) {
if(TEXTURE_BOUND == texture) return;
if(texture == NULL) {
#if DISPLAY_SDL2
glDisable(GL_TEXTURE_2D);
#elif DOLPHIN
GX_SetNumChans(0);
#endif
TEXTURE_BOUND = NULL;
return;
}
assertTrue(texture->ready, "Texture ID must be ready");
assertTrue(
texture->width > 0 && texture->height > 0,
"Texture width and height must be greater than 0"
);
#if DISPLAY_SDL2
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture->id);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
#elif DOLPHIN
GX_SetNumChans(1);
GX_LoadTexObj(&texture->texObj, GX_TEXMAP0);
#endif
TEXTURE_BOUND = texture;
}
void textureDispose(texture_t *texture) {
errorret_t textureDispose(texture_t *texture) {
assertNotNull(texture, "Texture cannot be NULL");
assertTrue(texture->ready, "Texture ID must be ready");
if(TEXTURE_BOUND == texture) {
textureBind(NULL);
}
#if DISPLAY_SDL2
glDeleteTextures(1, &texture->id);
#elif DOLPHIN
switch(texture->format) {
case TEXTURE_FORMAT_RGBA:
free(texture->rgba);
break;
case TEXTURE_FORMAT_ALPHA:
free(texture->alpha);
break;
default:
break;
errorChain(textureDisposePlatform(texture));
errorOk();
}
memoryZero(texture, sizeof(texture_t));
#endif
}
#if DOLPHIN
void textureDolphinUploadTEV() {
if(TEXTURE_BOUND == NULL) {
GX_SetNumTexGens(0);
GX_SetNumTevStages(1);
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
return;
}
// Add channel for vertex color
GX_SetNumChans(1);
GX_SetChanCtrl(
GX_COLOR0A0,// Store in color channel 0
GX_DISABLE,// Lighting disabled
GX_SRC_REG,// Ambient color?
GX_SRC_VTX,// Material color?
GX_LIGHTNULL,// Light Mask
GX_DF_NONE,// Diffuse function
GX_AF_NONE// Attenuation function
);
// One set of UVs
GX_SetNumTexGens(1);
GX_SetTexCoordGen(
GX_TEXCOORD0,
GX_TG_MTX2x4,
GX_TG_TEX0,
GX_IDENTITY
);
// Basically the shader setup
switch(TEXTURE_BOUND->format) {
case TEXTURE_FORMAT_RGBA:
// One TEV stage: vertex color * texture color
GX_SetNumTevStages(1);
GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
GX_SetTevOrder(
GX_TEVSTAGE0,
GX_TEXCOORD0,
GX_TEXMAP0,
GX_COLOR0A0
);
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
break;
case TEXTURE_FORMAT_ALPHA:
// One TEV stage: vertex color * texture color
GX_SetNumTevStages(1);
GX_SetTevOrder(
GX_TEVSTAGE0,
GX_TEXCOORD0,
GX_TEXMAP0,
GX_COLOR0A0
);
// Color = vertex color
GX_SetTevColorIn(
GX_TEVSTAGE0,
GX_CC_RASC,
GX_CC_ZERO,
GX_CC_ZERO,
GX_CC_ZERO
);
GX_SetTevColorOp(
GX_TEVSTAGE0,
GX_TEV_ADD,
GX_TB_ZERO,
GX_CS_SCALE_1,
GX_TRUE,
GX_TEVPREV
);
// Alpha = vertex alpha * I8 intensity
GX_SetTevAlphaIn(
GX_TEVSTAGE0,
GX_CA_RASA,
GX_CA_ZERO,
GX_CA_TEXA,
GX_CA_ZERO
);
GX_SetTevAlphaOp(
GX_TEVSTAGE0,
GX_TEV_ADD,
GX_TB_ZERO,
GX_CS_SCALE_1,
GX_TRUE,
GX_TEVPREV
);
GX_SetBlendMode(
GX_BM_BLEND,
GX_BL_SRCALPHA,
GX_BL_INVSRCALPHA,
GX_LO_CLEAR
);
GX_SetColorUpdate(GX_TRUE);
GX_SetAlphaUpdate(GX_TRUE);
break;
default:
assertUnreachable("Unknown texture format in meshDraw");
break;
}
}
#endif

View File

@@ -6,44 +6,30 @@
*/
#pragma once
#include "error/error.h"
#include "display/color.h"
#include "display/displaydefs.h"
#include "display/texture/palette.h"
#include "display/texture/textureplatform.h"
typedef enum {
#if DISPLAY_SDL2
TEXTURE_FORMAT_RGBA = GL_RGBA,
TEXTURE_FORMAT_PALETTE = GL_COLOR_INDEX8_EXT,
#elif DOLPHIN
// TEXTURE_FORMAT_RGBA = GX_TF_RGBA8,
// TEXTURE_FORMAT_ALPHA = GX_TF_A8,
TEXTURE_FORMAT_PALETTE = GX_TF_CI8,
#ifndef textureInitPlatform
#error "textureInitPlatform should not be defined."
#endif
} textureformat_t;
typedef struct {
#if DISPLAY_SDL2
GLuint id;
#elif DOLPHIN
GXTexObj texObj;
union {
u16 *rgba;
u8 *alpha;
};
#ifndef textureBindPlatform
#error "textureBindPlatform should not be defined."
#endif
#ifndef textureDisposePlatform
#error "textureDisposePlatform should not be defined."
#endif
textureformat_t format;
bool_t ready;
int32_t width;
int32_t height;
} texture_t;
typedef textureformatplatform_t textureformat_t;
typedef textureplatform_t texture_t;
typedef union {
typedef union texturedata_u {
uint8_t *paletteData;
color_t *rgbaColors;
} texturedata_t;
extern const texture_t *TEXTURE_BOUND;
extern texture_t *TEXTURE_BOUND;
/**
* Initializes a texture.
@@ -54,7 +40,7 @@ extern const texture_t *TEXTURE_BOUND;
* @param format The format of the texture (e.g., GL_RGBA, GL_ALPHA).
* @param data The data for the texture, the format changes per format.
*/
void textureInit(
errorret_t textureInit(
texture_t *texture,
const int32_t width,
const int32_t height,
@@ -67,18 +53,11 @@ void textureInit(
*
* @param texture The texture to bind.
*/
void textureBind(texture_t *texture);
errorret_t textureBind(texture_t *texture);
/**
* Disposes a texture.
*
* @param texture The texture to dispose.
*/
void textureDispose(texture_t *texture);
#if DOLPHIN
/**
* Uploads the TEV settings for the currently bound texture.
*/
void textureDolphinUploadTEV();
#endif
errorret_t textureDispose(texture_t *texture);

View File

@@ -10,3 +10,5 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
)
# Subdirs
add_subdirectory(display)
add_subdirectory(error)

View File

@@ -0,0 +1,14 @@
# 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
displaygl.c
)
# Subdirs
add_subdirectory(framebuffer)
add_subdirectory(texture)

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "displaygl.h"
errorret_t displayOpenGLInit(void) {
glDisable(GL_CULL_FACE);
glDisable(GL_LIGHTING);// PSP defaults this on?
glShadeModel(GL_SMOOTH); // Fixes color on PSP?
errorChain(errorGLCheck());
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearDepth(1.0f);
errorChain(errorGLCheck());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
errorChain(errorGLCheck());
glEnableClientState(GL_COLOR_ARRAY);// To confirm: every frame on PSP?
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
errorChain(errorGLCheck());
}

View File

@@ -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 "error/errorgl.h"
/**
* Initializes the OpenGL specific contexts for rendering.
*/
errorret_t displayOpenGLInit(void);

View File

@@ -0,0 +1,9 @@
# 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
)

View File

@@ -0,0 +1,133 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/display.h"
#include "display/framebuffer/framebuffer.h"
#include "assert/assert.h"
errorret_t frameBufferGLInitBackBuffer(void) {
errorOk();
}
uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
return 0;
}
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
int32_t windowWidth, windowHeight;
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
return windowWidth;
#else
return DUSK_DISPLAY_WIDTH;
#endif
}
return framebuffer->texture.width;
}
uint32_t frameBufferGLGetHeight(const framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
return 0;
}
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
int32_t windowWidth, windowHeight;
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
return windowHeight;
#else
return DUSK_DISPLAY_HEIGHT;
#endif
}
return framebuffer->texture.height;
}
errorret_t frameBufferGLBind(framebuffer_t *framebuffer) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
} else {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
}
glViewport(
0, 0,
frameBufferGetWidth(framebuffer), frameBufferGetHeight(framebuffer)
);
#else
glViewport(
0, 0,
DUSK_DISPLAY_WIDTH, DUSK_DISPLAY_HEIGHT
);
#endif
errorChain(errorGLCheck());
errorOk();
}
void frameBufferClear(const uint8_t flags, const color_t color) {
GLbitfield glFlags = 0;
if(flags & FRAMEBUFFER_CLEAR_COLOR) {
glFlags |= GL_COLOR_BUFFER_BIT;
glClearColor(
color.r / 255.0f,
color.g / 255.0f,
color.b / 255.0f,
color.a / 255.0f
);
}
if(flags & FRAMEBUFFER_CLEAR_DEPTH) {
glFlags |= GL_DEPTH_BUFFER_BIT;
}
glClear(glFlags);
}
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
errorret_t frameBufferGLInit(
framebuffer_t *fb,
const uint32_t width,
const uint32_t height
) {
assertNotNull(fb, "Framebuffer cannot be NULL");
assertTrue(width > 0 && height > 0, "W/H must be greater than 0");
memoryZero(fb, sizeof(framebuffer_t));
textureInit(&fb->texture, width, height, TEXTURE_FORMAT_RGBA,(texturedata_t){
.rgbaColors = NULL
});
errorChain(errorGLCheck());
glGenFramebuffersEXT(1, &fb->id);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->id);
errorChain(errorGLCheck());
glFramebufferTexture2DEXT(
GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, fb->texture.id, 0
);
errorChain(errorGLCheck());
if(
glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) !=
GL_FRAMEBUFFER_COMPLETE_EXT
) {
assertUnreachable("Framebuffer is not complete");
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
errorChain(errorGLCheck());
errorOk();
}
#endif

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/texture/texture.h"
#include "error/errorgl.h"
typedef struct {
GLuint id;
texture_t texture;
} framebuffergl_t;
/**
* Initializes the backbuffer framebuffer. (OpenGL implementation).
*/
errorret_t frameBufferGLInitBackBuffer(void);
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
/**
* Initializes an OpenGL style framebuffer.
*
* @param fb The framebuffer to initialize.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
* @return Either error or not.
*/
errorret_t frameBufferGLInit(
framebuffergl_t *fb,
const uint32_t width,
const uint32_t height
);
#endif

View File

@@ -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 "display/framebuffer/framebuffergl.h"
typedef framebuffergl_t framebufferplatform_t;
#define frameBufferPlatformInitBackBuffer frameBufferGLInitBackBuffer
#define frameBufferPlatformInit frameBufferGLInit
#define frameBufferPlatformBind frameBufferGLBind

View File

@@ -0,0 +1,10 @@
# 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
texturegl.c
)

View File

@@ -0,0 +1,86 @@
/**
* 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"
#include "error/errorgl.h"
errorret_t textureInitGL(
texturegl_t *texture,
const int32_t width,
const int32_t height,
const textureformatgl_t format,
const texturedata_t data
) {
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
errorChain(errorGLCheck());
switch(format) {
case TEXTURE_FORMAT_RGBA:
glTexImage2D(
GL_TEXTURE_2D, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, (void*)data.rgbaColors
);
break;
case TEXTURE_FORMAT_PALETTE:
assertNotNull(data.paletteData, "Palette texture data cannot be NULL");
glTexImage2D(
GL_TEXTURE_2D,
0, GL_COLOR_INDEX8_EXT,
width, height,
0, GL_COLOR_INDEX8_EXT,
GL_UNSIGNED_BYTE, (void*)data.paletteData
);
errorChain(errorGLCheck());
glColorTableEXT(
GL_TEXTURE_2D, GL_RGBA, data.palette.palette->colorCount, GL_RGBA,
GL_UNSIGNED_BYTE, (const void*)data.palette.palette->colors
);
break;
default:
assertUnreachable("Unknown texture format");
break;
}
errorChain(errorGLCheck());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
errorChain(errorGLCheck());
glBindTexture(GL_TEXTURE_2D, 0);
errorChain(errorGLCheck());
errorOk();
}
errorret_t textureBindGL(texturegl_t *texture) {
if(texture == NULL) {
glBindTexture(GL_TEXTURE_2D, 0);
errorChain(errorGLCheck());
errorOk();
}
assertTrue(texture->id != 0, "Texture ID must be valid");
glBindTexture(GL_TEXTURE_2D, texture->id);
errorChain(errorGLCheck());
errorOk();
}
errorret_t textureDisposeGL(texturegl_t *texture) {
assertNotNull(texture, "Texture cannot be NULL");
assertTrue(texture->id != 0, "Texture ID must be valid");
glDeleteTextures(1, &texture->id);
errorChain(errorGLCheck());
errorOk();
}

View File

@@ -0,0 +1,57 @@
/**
* 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 = GL_RGBA,
TEXTURE_FORMAT_PALETTE = GL_COLOR_INDEX8_EXT,
} textureformatgl_t;
typedef struct {
GLuint id;
textureformatgl_t format;
int32_t width;
int32_t height;
} texturegl_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., GL_RGBA, GL_ALPHA).
* @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 textureInitGL(
texturegl_t *texture,
const int32_t width,
const int32_t height,
const textureformatgl_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 textureBindGL(texturegl_t *texture);
/**
* Disposes a texture.
*
* @param texture The texture to dispose.
* @return An error if the texture failed to dispose, otherwise success.
*/
errorret_t textureDisposeGL(texturegl_t *texture);

View File

@@ -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 "display/texture/texturegl.h"
typedef textureformatgl_t textureformatplatform_t;
typedef texturegl_t textureplatform_t;
#define textureInitPlatform textureInitGL
#define textureBindPlatform textureBindGL
#define textureDisposePlatform textureDisposeGL

View File

@@ -6,4 +6,6 @@
*/
#pragma once
#include "display/displaysdl2.h"
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>

View File

@@ -0,0 +1,10 @@
# 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
errorgl.c
)

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "errorgl.h"
errorret_t errorGLCheck(void) {
GLenum err = glGetError();
if(err != GL_NO_ERROR) {
errorThrow("GL Error: %d", err);
}
errorOk();
}

View File

@@ -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 "error/error.h"
/**
* Gets the GL error (if any) and throws it as a dusk style error.
*
* @return Error code, if GL error present.
*/
errorret_t errorGLCheck(void);

View File

@@ -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 "display/displaysdl2.h"
typedef displaysdl2_t displayplatform_t;
#define displayPlatformInit displaySDL2Init
#define displayPlatformUpdate displaySDL2Update
#define displayPlatformSwap displaySDL2Swap
#define displayPlatformDispose displaySDL2Dispose

View File

@@ -1,8 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once

View File

@@ -10,3 +10,4 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
)
# Subdirs
add_subdirectory(display)

View File

@@ -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
displaysdl2.c
)
# Subdirs

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/display.h"
#include "engine/engine.h"
#include "display/displaygl.h"
erroret_t displaySDL2Init(void) {
uint32_t flags = SDL_INIT_VIDEO;
#if INPUT_GAMEPAD == 1
flags |= SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK;
#endif
if(SDL_Init(flags) != 0) {
errorThrow("SDL Failed to Initialize: %s", SDL_GetError());
}
// Set OpenGL attributes (Needs to be done now or later?)
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// Create window with OpenGL flag.
DISPLAY.window = SDL_CreateWindow(
"Dusk",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
DUSK_WINDOW_WIDTH_DEFAULT,
DUSK_WINDOW_HEIGHT_DEFAULT,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_OPENGL
);
if(!DISPLAY.window) {
errorThrow("SDL_CreateWindow failed: %s", SDL_GetError());
}
// Create OpenGL context
DISPLAY.glContext = SDL_GL_CreateContext(DISPLAY.window);
if(!DISPLAY.glContext) {
errorThrow("SDL_GL_CreateContext failed: %s", SDL_GetError());
}
errorChain(errorGLCheck());
SDL_GL_SetSwapInterval(1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
errorChain(errorGLCheck());
errorChain(displayOpenGLInit());
// #if DUSK_PSP
// errorChain(displayPSPInit());
// #endif
errorChain(errorGLCheck());
}
errorret_t displaySDL2Update(void) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_QUIT: {
ENGINE.running = false;
break;
}
case SDL_WINDOWEVENT: {
switch(event.window.event) {
case SDL_WINDOWEVENT_CLOSE: {
ENGINE.running = false;
break;
}
default: {
break;
}
}
}
default: {
break;
}
}
}
SDL_GL_MakeCurrent(DISPLAY.window, DISPLAY.glContext);
errorOk();
}
errorret_t displaySDL2Swap(void) {
SDL_GL_SwapWindow(DISPLAY.window);
GLenum err;
while((err = glGetError()) != GL_NO_ERROR) {
debugPrint("GL Error: %d\n", err);
}
errorOk();
}
void displaySDL2Dispose(void) {
if(DISPLAY.glContext) {
SDL_GL_DeleteContext(DISPLAY.glContext);
DISPLAY.glContext = NULL;
}
if(DISPLAY.window) {
SDL_DestroyWindow(DISPLAY.window);
DISPLAY.window = NULL;
}
SDL_Quit();
}

View File

@@ -6,10 +6,30 @@
*/
#pragma once
#include "dusk.h"
#include "error/error.h"
typedef struct {
SDL_Window *window;
SDL_GLContext glContext;
bool_t usingShaderedPalettes;
} displayplatform_t;
} displaysdl2_t;
/**
* Initializes the SDL2 display system.
*/
errorret_t displaySDL2Init(void);
/**
* Handles SDL2 Updating (pre-render context initialization)
*/
errorret_t displaySDL2Update(void);
/**
* Handles SDL2 Display Swapping (to switch from frontbuffer to backbuffer).
*/
errorret_t displaySDL2Swap(void);
/**
* Disposes/Cleans up the SDL2 Render system.
*/
void displaySDL2Dispose(void);

View File

@@ -6,4 +6,5 @@
*/
#pragma once
#include "duskopengl.h"
#include <SDL2/SDL.h>
#include "duskgl.h"