diff --git a/assets/scene/minesweeper.lua b/assets/scene/minesweeper.lua index 993693e..a6fa2ec 100644 --- a/assets/scene/minesweeper.lua +++ b/assets/scene/minesweeper.lua @@ -192,7 +192,7 @@ function sceneRender() spriteBatchPush( nil, x, y, x + 32, y + 32, - colorBlue() + colorWhite() ) -- Update mouse position diff --git a/cmake/targets/linux.cmake b/cmake/targets/linux.cmake index f83d136..3bc994a 100644 --- a/cmake/targets/linux.cmake +++ b/cmake/targets/linux.cmake @@ -30,6 +30,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC DUSK_OPENGL DUSK_LINUX DUSK_DISPLAY_SIZE_DYNAMIC + DUSK_OPENGL_SHADER DUSK_DISPLAY_WIDTH_DEFAULT=640 DUSK_DISPLAY_HEIGHT_DEFAULT=480 DUSK_DISPLAY_SCREEN_HEIGHT=240 diff --git a/src/dusk/display/CMakeLists.txt b/src/dusk/display/CMakeLists.txt index 6d9754a..ecdf8fa 100644 --- a/src/dusk/display/CMakeLists.txt +++ b/src/dusk/display/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(camera) add_subdirectory(framebuffer) add_subdirectory(mesh) add_subdirectory(screen) +add_subdirectory(shader) add_subdirectory(spritebatch) add_subdirectory(text) add_subdirectory(texture) diff --git a/src/dusk/display/display.c b/src/dusk/display/display.c index 8419140..50860ff 100644 --- a/src/dusk/display/display.c +++ b/src/dusk/display/display.c @@ -46,16 +46,16 @@ errorret_t displayUpdate(void) { errorChain(frameBufferBind(NULL)); // Bind screen and render scene - screenBind(); + errorChain(screenBind()); frameBufferClear( FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH, SCREEN.background ); - errorChain(sceneRender()); + errorCatch(errorPrint(sceneRender())); // Render UI - uiRender(); + // uiRender(); // Finish up screenUnbind(); diff --git a/src/dusk/display/screen/screen.c b/src/dusk/display/screen/screen.c index 831dc5d..27a96f5 100644 --- a/src/dusk/display/screen/screen.c +++ b/src/dusk/display/screen/screen.c @@ -44,11 +44,11 @@ errorret_t screenInit() { #endif // Init screen to backbuffer mode by default - screenBind(); + errorChain(screenBind()); errorOk(); } -void screenBind() { +errorret_t screenBind() { // Assume backbuffer is currently bound. switch(SCREEN.mode) { case SCREEN_MODE_BACKBUFFER: { @@ -59,7 +59,7 @@ void screenBind() { // No needd for a framebuffer. #ifdef DUSK_DISPLAY_SIZE_DYNAMIC if(SCREEN.framebufferReady) { - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } #endif @@ -79,19 +79,21 @@ void screenBind() { curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer); if(curFbWidth == SCREEN.width && curFbHeight == SCREEN.height) { // Correct size, nothing to do. - frameBufferBind(&SCREEN.framebuffer); - return; + errorChain(frameBufferBind(&SCREEN.framebuffer)); + errorOk(); } // Need a new framebuffer. - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } // Create new framebuffer - frameBufferInit(&SCREEN.framebuffer, SCREEN.width, SCREEN.height); + errorChain(frameBufferInit( + &SCREEN.framebuffer, SCREEN.width, SCREEN.height + )); SCREEN.framebufferReady = true; - frameBufferBind(&SCREEN.framebuffer); + errorChain(frameBufferBind(&SCREEN.framebuffer)); break; } @@ -109,10 +111,10 @@ void screenBind() { SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height; if(SCREEN.framebufferReady) { - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } - return; + errorOk(); } int32_t newFbWidth, newFbHeight; @@ -136,24 +138,26 @@ void screenBind() { SCREEN.width = newFbWidth; SCREEN.height = newFbHeight; SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height; - frameBufferBind(&SCREEN.framebuffer); - return; + errorChain(frameBufferBind(&SCREEN.framebuffer)); + errorOk(); } // Need a new framebuffer. - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } // Create new framebuffer - frameBufferInit(&SCREEN.framebuffer, newFbWidth, newFbHeight); + errorChain(frameBufferInit( + &SCREEN.framebuffer, newFbWidth, newFbHeight + )); SCREEN.width = newFbWidth; SCREEN.height = newFbHeight; SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height; SCREEN.framebufferReady = true; // Bind FB - frameBufferBind(&SCREEN.framebuffer); + errorChain(frameBufferBind(&SCREEN.framebuffer)); break; } @@ -173,10 +177,10 @@ void screenBind() { if(fbWidth == newFbWidth && fbHeight == newFbHeight) { // No need to use framebuffer. if(SCREEN.framebufferReady) { - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } - return; + errorOk(); } if(SCREEN.framebufferReady) { @@ -185,19 +189,21 @@ void screenBind() { curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer); curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer); if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) { - frameBufferBind(&SCREEN.framebuffer); - return; + errorChain(frameBufferBind(&SCREEN.framebuffer)); + errorOk(); } // Need a new framebuffer. - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } // Create a new framebuffer. - frameBufferInit(&SCREEN.framebuffer, newFbWidth, newFbHeight); + errorChain(frameBufferInit( + &SCREEN.framebuffer, newFbWidth, newFbHeight + )); SCREEN.framebufferReady = true; - frameBufferBind(&SCREEN.framebuffer); + errorChain(frameBufferBind(&SCREEN.framebuffer)); break; } @@ -217,10 +223,10 @@ void screenBind() { if(fbWidth == newFbWidth && fbHeight == newFbHeight) { // No need to use framebuffer. if(SCREEN.framebufferReady) { - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } - return; + errorOk(); } if(SCREEN.framebufferReady) { @@ -229,19 +235,21 @@ void screenBind() { curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer); curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer); if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) { - frameBufferBind(&SCREEN.framebuffer); - return; + errorChain(frameBufferBind(&SCREEN.framebuffer)); + errorOk(); } // Need a new framebuffer. - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } // Create a new framebuffer. - frameBufferInit(&SCREEN.framebuffer, newFbWidth, newFbHeight); + errorChain(frameBufferInit( + &SCREEN.framebuffer, newFbWidth, newFbHeight + )); SCREEN.framebufferReady = true; - frameBufferBind(&SCREEN.framebuffer); + errorChain(frameBufferBind(&SCREEN.framebuffer)); break; } @@ -262,9 +270,11 @@ void screenBind() { break; } } + + errorOk(); } -void screenUnbind() { +errorret_t screenUnbind() { switch(SCREEN.mode) { // Nothing to do here. case SCREEN_MODE_BACKBUFFER: @@ -275,7 +285,9 @@ void screenUnbind() { case SCREEN_MODE_FIXED_HEIGHT: case SCREEN_MODE_FIXED_SIZE: case SCREEN_MODE_FIXED_WIDTH: - if(SCREEN.framebufferReady) frameBufferBind(NULL); + if(SCREEN.framebufferReady) { + errorChain(frameBufferBind(NULL)); + } break; case SCREEN_MODE_FIXED_VIEWPORT_HEIGHT: @@ -286,17 +298,19 @@ void screenUnbind() { assertUnreachable("Invalid screen mode."); break; } + + errorOk(); } -void screenRender() { +errorret_t screenRender() { if(SCREEN.mode == SCREEN_MODE_BACKBUFFER) { - return; + errorOk(); } #ifdef DUSK_DISPLAY_SIZE_DYNAMIC if(SCREEN.mode == SCREEN_MODE_FIXED_VIEWPORT_HEIGHT) { glViewport(0, 0, SCREEN.width, SCREEN.height); - return; + errorOk(); } if( @@ -307,7 +321,7 @@ void screenRender() { ) { if(!SCREEN.framebufferReady) { // Nothing to do here. - return; + errorOk(); } float_t bbWidth, bbHeight; @@ -357,22 +371,25 @@ void screenRender() { COLOR_BLACK ); cameraPushMatrix(&SCREEN.framebufferCamera); - textureBind(&SCREEN.framebuffer.texture); - meshDraw(&SCREEN.frameBufferMesh, 0, -1); + errorChain(textureBind(&SCREEN.framebuffer.texture)); + errorChain(meshDraw(&SCREEN.frameBufferMesh, 0, -1)); cameraPopMatrix(); - return; + errorOk(); }; #endif assertUnreachable("Invalid screen mode."); + errorThrow("Invalid screen mode."); } -void screenDispose() { +errorret_t screenDispose() { #ifdef DUSK_DISPLAY_SIZE_DYNAMIC if(SCREEN.framebufferReady) { - frameBufferDispose(&SCREEN.framebuffer); + errorChain(frameBufferDispose(&SCREEN.framebuffer)); SCREEN.framebufferReady = false; } #endif + + errorOk(); } \ No newline at end of file diff --git a/src/dusk/display/screen/screen.h b/src/dusk/display/screen/screen.h index 148713d..b496da7 100644 --- a/src/dusk/display/screen/screen.h +++ b/src/dusk/display/screen/screen.h @@ -90,20 +90,28 @@ errorret_t screenInit(); /** * Binds the screen, this is done before rendering game content. + * + * @return Error code and state, if error occurs. */ -void screenBind(); +errorret_t screenBind(); /** * Unbinds the screen, does nothing for now. + * + * @return Error code and state, if error occurs. */ -void screenUnbind(); +errorret_t screenUnbind(); /** * Renders the screen to the current framebuffer. + * + * @return Error code and state, if error occurs. */ -void screenRender(); +errorret_t screenRender(); /** * Disposes the screen system. + * + * @return Error code and state, if error occurs. */ -void screenDispose(); \ No newline at end of file +errorret_t screenDispose(); \ No newline at end of file diff --git a/src/dusk/display/shader/CMakeLists.txt b/src/dusk/display/shader/CMakeLists.txt new file mode 100644 index 0000000..32debd1 --- /dev/null +++ b/src/dusk/display/shader/CMakeLists.txt @@ -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 + shader.c +) \ No newline at end of file diff --git a/src/dusk/display/shader/shader.c b/src/dusk/display/shader/shader.c new file mode 100644 index 0000000..9f7e914 --- /dev/null +++ b/src/dusk/display/shader/shader.c @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "shader.h" +#include "assert/assert.h" + +errorret_t shaderInit(shader_t *shader, const shaderdefinition_t *def) { + assertNotNull(shader, "Shader cannot be null"); + errorChain(shaderInitPlatform(shader, def)); + errorOk(); +} + +errorret_t shaderBind(shader_t *shader) { + assertNotNull(shader, "Shader cannot be null"); + errorChain(shaderBindPlatform(shader)); + errorOk(); +} + +errorret_t shaderSetMatrix( + shader_t *shader, + const char_t *name, + const mat4 matrix +) { + assertNotNull(shader, "Shader cannot be null"); + assertStrLenMin(name, 1, "Uniform name cannot be empty"); + assertNotNull(matrix, "Matrix cannot be null"); + errorChain(shaderSetMatrixPlatform(shader, name, matrix)); + errorOk(); +} + +errorret_t shaderDispose(shader_t *shader) { + assertNotNull(shader, "Shader cannot be null"); + errorChain(shaderDisposePlatform(shader)); + errorOk(); +} \ No newline at end of file diff --git a/src/dusk/display/shader/shader.h b/src/dusk/display/shader/shader.h new file mode 100644 index 0000000..b543ac4 --- /dev/null +++ b/src/dusk/display/shader/shader.h @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" +#include "display/texture/texture.h" +#include "display/shader/shaderplatform.h" + +#ifndef shaderInitPlatform + #error "shaderInitPlatform must be defined to use shader.h" +#endif +#ifndef shaderBindPlatform + #error "shaderBindPlatform must be defined to use shader.h" +#endif +#ifndef shaderSetMatrixPlatform + #error "shaderSetMatrixPlatform must be defined to use shader.h" +#endif +#ifndef shaderDisposePlatform + #error "shaderDisposePlatform must be defined to use shader.h" +#endif + +typedef shaderplatform_t shader_t; +typedef shaderdefinitionplatform_t shaderdefinition_t; + +/** + * Initializes a shader. This is platform dependant. + * + * @param shader Shader to initialize + * @param def Definition of the shader to initialize with. + * @return Error if failure, otherwise errorOk + */ +errorret_t shaderInit(shader_t *shader, const shaderdefinition_t *def); + +/** + * Binds a shader. This is platform dependant. + * + * @param shader Shader to bind + * @return Error if failure, otherwise errorOk + */ +errorret_t shaderBind(shader_t *shader); + +/** + * Sets a matrix uniform in the shader. This is platform dependant. + * + * @param shader Shader to set the matrix in + * @param name Name of the uniform to set + * @param matrix Matrix to set + * @return Error if failure, otherwise errorOk + */ +errorret_t shaderSetMatrix( + shader_t *shader, + const char_t *name, + const mat4 matrix +); + +/** + * Sets a texture uniform in the shader. This is platform dependant. + * + * @param shader Shader to set the texture in + * @param name Name of the uniform to set + * @param texture Texture to set + * @return Error if failure, otherwise errorOk + */ +errorret_t shaderSetTexture( + shader_t *shader, + const char_t *name, + texture_t *texture +); + +/** + * Disposes of a shader. This is platform dependant. + * + * @param shader Shader to dispose + * @return Error if failure, otherwise errorOk + */ +errorret_t shaderDispose(shader_t *shader); \ No newline at end of file diff --git a/src/dusk/display/shader/shaderunlit.h b/src/dusk/display/shader/shaderunlit.h new file mode 100644 index 0000000..15b4f42 --- /dev/null +++ b/src/dusk/display/shader/shaderunlit.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 "shader.h" + +#define SHADER_UNLIT_PROJECTION "u_Proj" +#define SHADER_UNLIT_VIEW "u_View" +#define SHADER_UNLIT_MODEL "u_Model" + +extern shaderdefinition_t SHADER_UNLIT_DEFINITION; \ No newline at end of file diff --git a/src/dusk/script/module/display/modulespritebatch.c b/src/dusk/script/module/display/modulespritebatch.c index 2d8e976..ce96b53 100644 --- a/src/dusk/script/module/display/modulespritebatch.c +++ b/src/dusk/script/module/display/modulespritebatch.c @@ -91,7 +91,7 @@ int moduleSpriteBatchPush(lua_State *L) { float_t maxX = (float_t)lua_tonumber(L, 4); float_t maxY = (float_t)lua_tonumber(L, 5); - spriteBatchPush( + errorret_t ret = spriteBatchPush( tex, minX, minY, @@ -103,6 +103,14 @@ int moduleSpriteBatchPush(lua_State *L) { u1, v1 ); + if(ret.code != ERROR_OK) { + int err = luaL_error(L, + "Failed to push sprite to batch: %s", + ret.state->message + ); + errorCatch(errorPrint(ret)); + return err; + } return 0; } \ No newline at end of file diff --git a/src/duskgl/assert/assertgl.h b/src/duskgl/assert/assertgl.h new file mode 100644 index 0000000..03425cd --- /dev/null +++ b/src/duskgl/assert/assertgl.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 "assert/assert.h" +#include "error/errorgl.h" + +#define assertNoGLError(message) \ + assertTrue(errorGLCheck().code == ERROR_OK, message) + +// EOF \ No newline at end of file diff --git a/src/duskgl/display/CMakeLists.txt b/src/duskgl/display/CMakeLists.txt index 4c8ef86..bd57837 100644 --- a/src/duskgl/display/CMakeLists.txt +++ b/src/duskgl/display/CMakeLists.txt @@ -13,4 +13,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} add_subdirectory(camera) add_subdirectory(framebuffer) add_subdirectory(texture) -add_subdirectory(mesh) \ No newline at end of file +add_subdirectory(mesh) +add_subdirectory(shader) \ No newline at end of file diff --git a/src/duskgl/display/camera/cameragl.c b/src/duskgl/display/camera/cameragl.c index 7f31a6e..528e213 100644 --- a/src/duskgl/display/camera/cameragl.c +++ b/src/duskgl/display/camera/cameragl.c @@ -8,7 +8,7 @@ #include "display/camera/camera.h" #include "display/framebuffer/framebuffer.h" #include "display/screen/screen.h" -#include "assert/assert.h" +#include "assert/assertgl.h" void cameraPushMatrixGL(camera_t *camera) { assertNotNull(camera, "Not a camera component"); @@ -117,16 +117,19 @@ void cameraPushMatrixGL(camera_t *camera) { assertUnreachable("Invalid camera view type"); } - glPushMatrix(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glLoadMatrixf((const GLfloat*)projection); + // glPushMatrix(); + // glMatrixMode(GL_PROJECTION); + // glLoadIdentity(); + // glLoadMatrixf((const GLfloat*)projection); + assertNoGLError("Failed to set projection matrix"); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glLoadMatrixf((const GLfloat*)view); + // glMatrixMode(GL_MODELVIEW); + // glLoadIdentity(); + // glLoadMatrixf((const GLfloat*)view); + assertNoGLError("Failed to set view matrix"); } void cameraPopMatrixGL(void) { - glPopMatrix(); + // glPopMatrix(); + assertNoGLError("Failed to pop camera matrix"); } \ No newline at end of file diff --git a/src/duskgl/display/displaygl.c b/src/duskgl/display/displaygl.c index c5d9cd5..212a571 100644 --- a/src/duskgl/display/displaygl.c +++ b/src/duskgl/display/displaygl.c @@ -9,8 +9,8 @@ errorret_t displayOpenGLInit(void) { glDisable(GL_CULL_FACE); - glDisable(GL_LIGHTING);// PSP defaults this on? - glShadeModel(GL_SMOOTH); // Fixes color on PSP? + // glDisable(GL_LIGHTING);// PSP defaults this on? + // glShadeModel(GL_SMOOTH); // Fixes color on PSP? errorChain(errorGLCheck()); glEnable(GL_DEPTH_TEST); @@ -24,9 +24,12 @@ errorret_t displayOpenGLInit(void) { 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()); + // glEnableClientState(GL_COLOR_ARRAY);// To confirm: every frame on PSP? + // glEnableClientState(GL_TEXTURE_COORD_ARRAY); + // glEnableClientState(GL_VERTEX_ARRAY); + // errorChain(errorGLCheck()); + + errorChain(shaderInit(&testShader, &SHADER_UNLIT_DEFINITION)); + errorOk(); } \ No newline at end of file diff --git a/src/duskgl/display/displaygl.h b/src/duskgl/display/displaygl.h index 1f2d59a..da3a118 100644 --- a/src/duskgl/display/displaygl.h +++ b/src/duskgl/display/displaygl.h @@ -7,8 +7,13 @@ #pragma once #include "error/errorgl.h" +#include "display/shader/shaderunlit.h" + +static shadergl_t testShader; /** * Initializes the OpenGL specific contexts for rendering. + * + * @return An errorret_t indicating success or failure of the initialization. */ errorret_t displayOpenGLInit(void); \ No newline at end of file diff --git a/src/duskgl/display/framebuffer/framebuffergl.c b/src/duskgl/display/framebuffer/framebuffergl.c index f87599d..43f7006 100644 --- a/src/duskgl/display/framebuffer/framebuffergl.c +++ b/src/duskgl/display/framebuffer/framebuffergl.c @@ -7,7 +7,7 @@ #include "display/display.h" #include "display/framebuffer/framebuffer.h" -#include "assert/assert.h" +#include "assert/assertgl.h" #include "util/memory.h" errorret_t frameBufferGLInitBackBuffer(void) { @@ -86,6 +86,7 @@ void frameBufferGLClear(const uint8_t flags, const color_t color) { color.b / 255.0f, color.a / 255.0f ); + assertNoGLError("Failed to set clear color"); } if(flags & FRAMEBUFFER_CLEAR_DEPTH) { @@ -93,6 +94,7 @@ void frameBufferGLClear(const uint8_t flags, const color_t color) { } glClear(glFlags); + assertNoGLError("Failed to clear framebuffer"); } #ifdef DUSK_DISPLAY_SIZE_DYNAMIC diff --git a/src/duskgl/display/mesh/meshgl.c b/src/duskgl/display/mesh/meshgl.c index fd68df3..e7f6257 100644 --- a/src/duskgl/display/mesh/meshgl.c +++ b/src/duskgl/display/mesh/meshgl.c @@ -6,7 +6,8 @@ */ #include "display/mesh/mesh.h" -#include "assert/assert.h" +#include "assert/assertgl.h" +#include "error/errorgl.h" errorret_t meshInitGL( meshgl_t *mesh, @@ -33,26 +34,27 @@ errorret_t meshDrawGL( // PSP style pointer legacy OpenGL const GLsizei stride = sizeof(meshvertex_t); - glColorPointer( - sizeof(color4b_t), - GL_UNSIGNED_BYTE, - stride, - (const GLvoid*)&mesh->vertices[offset].color - ); - glTexCoordPointer( - MESH_VERTEX_UV_SIZE, - GL_FLOAT, - stride, - (const GLvoid*)&mesh->vertices[offset].uv - ); - glVertexPointer( - MESH_VERTEX_POS_SIZE, - GL_FLOAT, - stride, - (const GLvoid*)&mesh->vertices[offset].pos - ); + // glColorPointer( + // sizeof(color4b_t), + // GL_UNSIGNED_BYTE, + // stride, + // (const GLvoid*)&mesh->vertices[offset].color + // ); + // glTexCoordPointer( + // MESH_VERTEX_UV_SIZE, + // GL_FLOAT, + // stride, + // (const GLvoid*)&mesh->vertices[offset].uv + // ); + // glVertexPointer( + // MESH_VERTEX_POS_SIZE, + // GL_FLOAT, + // stride, + // (const GLvoid*)&mesh->vertices[offset].pos + // ); - glDrawArrays(mesh->primitiveType, offset, count); + // glDrawArrays(mesh->primitiveType, offset, count); + // errorChain(errorGLCheck()); errorOk(); } diff --git a/src/duskgl/display/shader/CMakeLists.txt b/src/duskgl/display/shader/CMakeLists.txt new file mode 100644 index 0000000..719b437 --- /dev/null +++ b/src/duskgl/display/shader/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 + shadergl.c + shaderunlitgl.c +) \ No newline at end of file diff --git a/src/duskgl/display/shader/shadergl.c b/src/duskgl/display/shader/shadergl.c new file mode 100644 index 0000000..6f7726f --- /dev/null +++ b/src/duskgl/display/shader/shadergl.c @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "shadergl.h" +#include "util/memory.h" +#include "assert/assertgl.h" + +errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) { + assertNotNull(shader, "Shader cannot be null"); + assertNotNull(def, "Shader definition cannot be null"); + + memoryZero(shader, sizeof(shadergl_t)); + + // Create vertex shader + shader->vertexShaderId = glCreateShader(GL_VERTEX_SHADER); + errorret_t err = errorGLCheck(); + errorChain(err); + + glShaderSource(shader->vertexShaderId, 1, &def->vert, NULL); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + errorChain(err); + } + + glCompileShader(shader->vertexShaderId); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + errorChain(err); + } + + GLint ok = 0; + glGetShaderiv(shader->vertexShaderId, GL_COMPILE_STATUS, &ok); + if(!ok) { + GLchar log[1024]; + glGetShaderInfoLog(shader->vertexShaderId, sizeof(log), NULL, log); + glDeleteShader(shader->vertexShaderId); + errorThrow("Vertex shader compilation failed: %s", log); + } + + // Create fragment shader + shader->fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + errorChain(err); + } + + glShaderSource(shader->fragmentShaderId, 1, &def->frag, NULL); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorChain(err); + } + + glCompileShader(shader->fragmentShaderId); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorChain(err); + } + + glGetShaderiv(shader->fragmentShaderId, GL_COMPILE_STATUS, &ok); + if(!ok) { + GLchar log[1024]; + glGetShaderInfoLog(shader->fragmentShaderId, sizeof(log), NULL, log); + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorThrow("Fragment shader compilation failed: %s", log); + } + + // Create shader program + shader->shaderProgramId = glCreateProgram(); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorChain(err); + } + + glAttachShader(shader->shaderProgramId, shader->vertexShaderId); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteProgram(shader->shaderProgramId); + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorChain(err); + } + + glAttachShader(shader->shaderProgramId, shader->fragmentShaderId); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteProgram(shader->shaderProgramId); + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorChain(err); + } + + glLinkProgram(shader->shaderProgramId); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteProgram(shader->shaderProgramId); + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorChain(err); + } + + ok = 0; + glGetProgramiv(shader->shaderProgramId, GL_LINK_STATUS, &ok); + if(!ok) { + GLchar log[1024]; + glGetProgramInfoLog(shader->shaderProgramId, sizeof(log), NULL, log); + glDeleteProgram(shader->shaderProgramId); + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorThrow("Shader program linking failed: %s", log); + } + + errorOk(); +} + +errorret_t shaderParamGetLocationGL( + shadergl_t *shader, + const char_t *name, + GLint *location +) { + assertNotNull(shader, "Shader cannot be null"); + assertStrLenMin(name, 1, "Uniform name cannot be empty"); + assertNotNull(location, "Location cannot be null"); + + shadergl_t *shaderGL = (shadergl_t *)shader; + *location = glGetUniformLocation(shaderGL->shaderProgramId, name); + errorret_t err = errorGLCheck(); + if(err.code != ERROR_OK) { + errorChain(err); + } + + errorOk(); +} + +errorret_t shaderSetMatrixGL( + shadergl_t *shader, + const char_t *name, + const mat4 mat +) { + assertNotNull(shader, "Shader cannot be null"); + assertNotNull(mat, "Matrix data cannot be null"); + assertStrLenMin(name, 1, "Uniform name cannot be empty"); + + GLint location; + errorChain(shaderParamGetLocationGL(shader, name, &location)); + + glUniformMatrix4fv(location, 1, GL_FALSE, (const GLfloat *)mat); + errorChain(errorGLCheck()); + + errorOk(); +} + + +errorret_t shaderBindGL(shadergl_t *shader) { + assertNotNull(shader, "Shader cannot be null"); + + glUseProgram(shader->shaderProgramId); + errorChain(errorGLCheck()); + + errorOk(); +} + +errorret_t shaderDisposeGL(shadergl_t *shader) { + assertNotNull(shader, "Shader cannot be null"); + + if(shader->shaderProgramId != 0) { + glDeleteProgram(shader->shaderProgramId); + errorChain(errorGLCheck()); + } + + if(shader->vertexShaderId != 0) { + glDeleteShader(shader->vertexShaderId); + errorChain(errorGLCheck()); + } + + if(shader->fragmentShaderId != 0) { + glDeleteShader(shader->fragmentShaderId); + errorChain(errorGLCheck()); + } + + assertNoGLError("Failed disposing shader"); + memoryZero(shader, sizeof(shadergl_t)); + errorOk(); +} diff --git a/src/duskgl/display/shader/shadergl.h b/src/duskgl/display/shader/shadergl.h new file mode 100644 index 0000000..91bb5de --- /dev/null +++ b/src/duskgl/display/shader/shadergl.h @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/errorgl.h" + +typedef struct { + GLuint shaderProgramId; + GLuint vertexShaderId; + GLuint fragmentShaderId; +} shadergl_t; + +typedef struct { + const char_t *vert; + const char_t *frag; +} shaderdefinitiongl_t; + +/** + * Initializes a shader. + * + * @param shader The shader to initialize. + * @param def The definition of the shader to initialize with. + * @return An errorret_t indicating success or failure. + */ +errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def); + +/** + * Binds a shader for use in rendering. + * + * @param shader The shader to bind. + * @return An errorret_t indicating success or failure. + */ +errorret_t shaderBindGL(shadergl_t *shader); + +/** + * Retrieves the location of a shader uniform parameter. + * + * @param shader The shader to query. + * @param name The name of the uniform parameter. + * @param location Output parameter to receive the location of the uniform. + * @return An errorret_t indicating success or failure. + */ +errorret_t shaderParamGetLocationGL( + shadergl_t *shader, + const char_t *name, + GLint *location +); + +/** + * Sets a mat4 uniform parameter in the shader. + * + * @param shader The shader to update. + * @param name The name of the uniform parameter. + * @param mat The 4x4 matrix data to set. + * @return An errorret_t indicating success or failure. + */ +errorret_t shaderSetMatrixGL( + shadergl_t *shader, + const char_t *name, + const mat4 matrix +); + +/** + * Disposes of a shader, freeing any associated resources. + * + * @param shader The shader to dispose. + */ +errorret_t shaderDisposeGL(shadergl_t *shader); \ No newline at end of file diff --git a/src/duskgl/display/shader/shaderplatform.h b/src/duskgl/display/shader/shaderplatform.h new file mode 100644 index 0000000..9d38587 --- /dev/null +++ b/src/duskgl/display/shader/shaderplatform.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "shadergl.h" + +typedef shadergl_t shaderplatform_t; +typedef shaderdefinitiongl_t shaderdefinitionplatform_t; + +#define shaderInitPlatform shaderInitGL +#define shaderBindPlatform shaderBindGL +#define shaderSetMatrixPlatform shaderSetMatrixGL +// #define shaderSetTexturePlatform shaderSetTextureGL +#define shaderDisposePlatform shaderDisposeGL \ No newline at end of file diff --git a/src/duskgl/display/shader/shaderunlitgl.c b/src/duskgl/display/shader/shaderunlitgl.c new file mode 100644 index 0000000..8ac2cd0 --- /dev/null +++ b/src/duskgl/display/shader/shaderunlitgl.c @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "display/shader/shaderunlit.h" + +shaderdefinition_t SHADER_UNLIT_DEFINITION = { + .vert = + "#version 330 core\n" + "layout(location = 0) in vec3 aPos;\n" + "layout(location = 1) in vec2 aTexCoord;\n" + "uniform mat4 u_Proj;\n" + "uniform mat4 u_View;\n" + "uniform mat4 u_Model;\n" + "out vec2 v_TexCoord;\n" + "void main() {\n" + " gl_Position = u_Proj * u_View * u_Model * vec4(aPos, 1.0);\n" + " v_TexCoord = aTexCoord;\n" + "}\n", + + .frag = + "#version 330 core\n" + "in vec2 v_TexCoord;\n" + "out vec4 FragColor;\n" + "uniform sampler2D u_Texture;\n" + "void main() {\n" + " FragColor = texture(u_Texture, v_TexCoord);\n" + "}\n" +}; \ No newline at end of file diff --git a/src/dusksdl2/display/displaysdl2.c b/src/dusksdl2/display/displaysdl2.c index 0b03325..f8582d8 100644 --- a/src/dusksdl2/display/displaysdl2.c +++ b/src/dusksdl2/display/displaysdl2.c @@ -21,6 +21,11 @@ errorret_t displaySDL2Init(void) { // Set OpenGL attributes (Needs to be done now or later?) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + // SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + // SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); // Create window with OpenGL flag. DISPLAY.window = SDL_CreateWindow( @@ -44,7 +49,6 @@ errorret_t displaySDL2Init(void) { errorChain(errorGLCheck()); SDL_GL_SetSwapInterval(1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); errorChain(errorGLCheck()); errorChain(displayOpenGLInit()); @@ -82,6 +86,9 @@ errorret_t displaySDL2Update(void) { } SDL_GL_MakeCurrent(DISPLAY.window, DISPLAY.glContext); + + // errorChain(shaderPaletteTextureBindGL(&testShader)); + errorOk(); }