From ff92a78ddaa36a5f5a23317c377eb5f7f23ff0cc Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 17 Mar 2026 08:42:43 -0500 Subject: [PATCH 1/4] Shader first pass --- assets/scene/minesweeper.lua | 2 +- cmake/targets/linux.cmake | 1 + src/dusk/display/CMakeLists.txt | 1 + src/dusk/display/display.c | 6 +- src/dusk/display/screen/screen.c | 97 +++++---- src/dusk/display/screen/screen.h | 16 +- src/dusk/display/shader/CMakeLists.txt | 10 + src/dusk/display/shader/shader.c | 39 ++++ src/dusk/display/shader/shader.h | 80 +++++++ src/dusk/display/shader/shaderunlit.h | 15 ++ .../script/module/display/modulespritebatch.c | 10 +- src/duskgl/assert/assertgl.h | 15 ++ src/duskgl/display/CMakeLists.txt | 3 +- src/duskgl/display/camera/cameragl.c | 21 +- src/duskgl/display/displaygl.c | 15 +- src/duskgl/display/displaygl.h | 5 + .../display/framebuffer/framebuffergl.c | 4 +- src/duskgl/display/mesh/meshgl.c | 42 ++-- src/duskgl/display/shader/CMakeLists.txt | 11 + src/duskgl/display/shader/shadergl.c | 197 ++++++++++++++++++ src/duskgl/display/shader/shadergl.h | 72 +++++++ src/duskgl/display/shader/shaderplatform.h | 18 ++ src/duskgl/display/shader/shaderunlitgl.c | 32 +++ src/dusksdl2/display/displaysdl2.c | 9 +- 24 files changed, 634 insertions(+), 87 deletions(-) create mode 100644 src/dusk/display/shader/CMakeLists.txt create mode 100644 src/dusk/display/shader/shader.c create mode 100644 src/dusk/display/shader/shader.h create mode 100644 src/dusk/display/shader/shaderunlit.h create mode 100644 src/duskgl/assert/assertgl.h create mode 100644 src/duskgl/display/shader/CMakeLists.txt create mode 100644 src/duskgl/display/shader/shadergl.c create mode 100644 src/duskgl/display/shader/shadergl.h create mode 100644 src/duskgl/display/shader/shaderplatform.h create mode 100644 src/duskgl/display/shader/shaderunlitgl.c 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(); } From 66ebcb1608ebfd96d902a10eac96b79eb82cc818 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 17 Mar 2026 17:05:39 -0500 Subject: [PATCH 2/4] shader prog --- cmake/targets/linux.cmake | 1 - cmake/targets/psp.cmake | 1 + src/dusk/display/display.c | 70 +++++++- src/dusk/display/shader/shaderunlit.h | 3 +- src/duskgl/display/displaygl.c | 4 +- src/duskgl/display/displaygl.h | 3 - src/duskgl/display/mesh/meshgl.c | 132 ++++++++++++--- src/duskgl/display/mesh/meshgl.h | 8 +- src/duskgl/display/shader/shadergl.c | 196 +++++++++++----------- src/duskgl/display/shader/shadergl.h | 18 +- src/duskgl/display/shader/shaderunlitgl.c | 7 +- 11 files changed, 303 insertions(+), 140 deletions(-) diff --git a/cmake/targets/linux.cmake b/cmake/targets/linux.cmake index 3bc994a..f83d136 100644 --- a/cmake/targets/linux.cmake +++ b/cmake/targets/linux.cmake @@ -30,7 +30,6 @@ 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/cmake/targets/psp.cmake b/cmake/targets/psp.cmake index e58dd80..4f3a6b0 100644 --- a/cmake/targets/psp.cmake +++ b/cmake/targets/psp.cmake @@ -36,6 +36,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC DUSK_PSP DUSK_INPUT_GAMEPAD DUSK_PLATFORM_ENDIAN_LITTLE + DUSK_OPENGL_LEGACY DUSK_DISPLAY_WIDTH=480 DUSK_DISPLAY_HEIGHT=272 ) diff --git a/src/dusk/display/display.c b/src/dusk/display/display.c index 50860ff..e72cae2 100644 --- a/src/dusk/display/display.c +++ b/src/dusk/display/display.c @@ -17,6 +17,8 @@ #include "util/memory.h" #include "util/string.h" #include "asset/asset.h" +#include "display/shader/shaderunlit.h" +#include "time/time.h" display_t DISPLAY = { 0 }; @@ -27,6 +29,7 @@ errorret_t displayInit(void) { errorChain(displayPlatformInit()); #endif + errorChain(shaderInit(&SHADER_UNLIT, &SHADER_UNLIT_DEFINITION)); errorChain(quadInit()); errorChain(frameBufferInitBackBuffer()); errorChain(spriteBatchInit()); @@ -52,7 +55,71 @@ errorret_t displayUpdate(void) { SCREEN.background ); - errorCatch(errorPrint(sceneRender())); + errorChain(shaderBind(&SHADER_UNLIT)); + + mat4 proj; + glm_mat4_identity(proj); + glm_ortho(0.0f, (float_t)SCREEN.width, (float_t)SCREEN.height, 0.0f, -1.0f, 1.0f, proj); + + mat4 view; + glm_mat4_identity(view); + + mat4 model; + glm_mat4_identity(model); + glm_translate(model, (vec3){ + sinf(TIME.time * 10.0f) * 30.0f + 100.0f, + cosf(TIME.time * 10.0f) * 50.0f + 100.0f, + 0.0f + }); + + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model)); + + meshvertex_t verts[6] = { + { + .pos = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, + .color = COLOR_WHITE + }, + + { + .pos = { 100.0f, 0.0f, 0.0f }, + .uv = { 1.0f, 0.0f }, + .color = COLOR_WHITE + }, + + { + .pos = { 100.0f, 100.0f, 0.0f }, + .uv = { 1.0f, 1.0f }, + .color = COLOR_WHITE + }, + + { + .pos = { 100.0f, 100.0f, 0.0f }, + .uv = { 1.0f, 1.0f }, + .color = COLOR_WHITE + }, + + { + .pos = { 0.0f, 100.0f, 0.0f }, + .uv = { 0.0f, 1.0f }, + .color = COLOR_WHITE + }, + + { + .pos = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, + .color = COLOR_WHITE + } + }; + + mesh_t mesh; + meshInit(&mesh, MESH_PRIMITIVE_TYPE_TRIANGLES, 6, verts); + meshDraw(&mesh, 0, -1); + meshDispose(&mesh); + + // errorCatch(errorPrint(sceneRender())); // Render UI // uiRender(); @@ -69,6 +136,7 @@ errorret_t displayUpdate(void) { } errorret_t displayDispose(void) { + errorChain(shaderDispose(&SHADER_UNLIT)); errorChain(spriteBatchDispose()); screenDispose(); errorChain(textDispose()); diff --git a/src/dusk/display/shader/shaderunlit.h b/src/dusk/display/shader/shaderunlit.h index 15b4f42..881a695 100644 --- a/src/dusk/display/shader/shaderunlit.h +++ b/src/dusk/display/shader/shaderunlit.h @@ -12,4 +12,5 @@ #define SHADER_UNLIT_VIEW "u_View" #define SHADER_UNLIT_MODEL "u_Model" -extern shaderdefinition_t SHADER_UNLIT_DEFINITION; \ No newline at end of file +extern shaderdefinition_t SHADER_UNLIT_DEFINITION; +static shader_t SHADER_UNLIT; \ No newline at end of file diff --git a/src/duskgl/display/displaygl.c b/src/duskgl/display/displaygl.c index 212a571..0affce3 100644 --- a/src/duskgl/display/displaygl.c +++ b/src/duskgl/display/displaygl.c @@ -8,6 +8,8 @@ #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? @@ -29,7 +31,5 @@ errorret_t displayOpenGLInit(void) { // 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 da3a118..2944f22 100644 --- a/src/duskgl/display/displaygl.h +++ b/src/duskgl/display/displaygl.h @@ -7,9 +7,6 @@ #pragma once #include "error/errorgl.h" -#include "display/shader/shaderunlit.h" - -static shadergl_t testShader; /** * Initializes the OpenGL specific contexts for rendering. diff --git a/src/duskgl/display/mesh/meshgl.c b/src/duskgl/display/mesh/meshgl.c index e7f6257..233bb59 100644 --- a/src/duskgl/display/mesh/meshgl.c +++ b/src/duskgl/display/mesh/meshgl.c @@ -21,7 +21,74 @@ errorret_t meshInitGL( mesh->primitiveType = primitiveType; mesh->vertexCount = vertexCount; - mesh->vertices = vertices; + + #ifdef DUSK_OPENGL_LEGACY + mesh->vertices = vertices; + #else + // Generate Vertex Buffer Object + glGenBuffers(1, &mesh->vboId); + errorChain(errorGLCheck()); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId); + errorChain(errorGLCheck()); + glBufferData( + GL_ARRAY_BUFFER, + vertexCount * sizeof(meshvertex_t), + vertices, + GL_STATIC_DRAW + ); + errorChain(errorGLCheck()); + + // Generate Vertex Array Object + glGenVertexArrays(1, &mesh->vaoId); + errorChain(errorGLCheck()); + glBindVertexArray(mesh->vaoId); + errorChain(errorGLCheck()); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId); + errorChain(errorGLCheck()); + + // Set up vertex attribute pointers + glVertexAttribPointer( + 0, + MESH_VERTEX_POS_SIZE, + GL_FLOAT, + GL_FALSE, + sizeof(meshvertex_t), + (const GLvoid*)offsetof(meshvertex_t, pos) + ); + errorChain(errorGLCheck()); + glEnableVertexAttribArray(0); + errorChain(errorGLCheck()); + + glVertexAttribPointer( + 1, + MESH_VERTEX_UV_SIZE, + GL_FLOAT, + GL_FALSE, + sizeof(meshvertex_t), + (const GLvoid*)offsetof(meshvertex_t, uv) + ); + errorChain(errorGLCheck()); + glEnableVertexAttribArray(1); + errorChain(errorGLCheck()); + + glVertexAttribPointer( + 2, + sizeof(color_t) / sizeof(GLubyte), + GL_UNSIGNED_BYTE, + GL_TRUE, + sizeof(meshvertex_t), + (const GLvoid*)offsetof(meshvertex_t, color) + ); + errorChain(errorGLCheck()); + glEnableVertexAttribArray(2); + errorChain(errorGLCheck()); + + // Unbind VAO and VBO to prevent accidental modification + glBindBuffer(GL_ARRAY_BUFFER, 0); + errorChain(errorGLCheck()); + glBindVertexArray(0); + errorChain(errorGLCheck()); + #endif errorOk(); } @@ -31,30 +98,40 @@ errorret_t meshDrawGL( const int32_t offset, const int32_t count ) { - // PSP style pointer legacy OpenGL - const GLsizei stride = sizeof(meshvertex_t); + #ifdef DUSK_OPENGL_LEGACY + // Legacy pointer style rendering + 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); - // errorChain(errorGLCheck()); + glDrawArrays(mesh->primitiveType, offset, count); + errorChain(errorGLCheck()); + #else + // Modern VAO/VBO rendering + glBindVertexArray(mesh->vaoId); + errorChain(errorGLCheck()); + glDrawArrays(mesh->primitiveType, offset, count); + errorChain(errorGLCheck()); + glBindVertexArray(0); + errorChain(errorGLCheck()); + #endif errorOk(); } @@ -64,6 +141,13 @@ int32_t meshGetVertexCountGL(const meshgl_t *mesh) { } errorret_t meshDisposeGL(meshgl_t *mesh) { - // No dynamic resources to free for this mesh implementation + #ifdef DUSK_OPENGL_LEGACY + // No dynamic resources to free for this mesh implementation + #else + glDeleteBuffers(1, &mesh->vboId); + errorChain(errorGLCheck()); + glDeleteVertexArrays(1, &mesh->vaoId); + errorChain(errorGLCheck()); + #endif errorOk(); } \ No newline at end of file diff --git a/src/duskgl/display/mesh/meshgl.h b/src/duskgl/display/mesh/meshgl.h index 8acc6c3..208a3ab 100644 --- a/src/duskgl/display/mesh/meshgl.h +++ b/src/duskgl/display/mesh/meshgl.h @@ -16,9 +16,15 @@ typedef enum { } meshprimitivetypegl_t; typedef struct { - const meshvertex_t *vertices; int32_t vertexCount; meshprimitivetypegl_t primitiveType; + + #ifdef DUSK_OPENGL_LEGACY + const meshvertex_t *vertices; + #else + GLuint vaoId; + GLuint vboId; + #endif } meshgl_t; /** diff --git a/src/duskgl/display/shader/shadergl.c b/src/duskgl/display/shader/shadergl.c index 6f7726f..2c82aa3 100644 --- a/src/duskgl/display/shader/shadergl.c +++ b/src/duskgl/display/shader/shadergl.c @@ -12,116 +12,120 @@ 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); + #ifdef DUSK_OPENGL_LEGACY + // No initialization needed for legacy shader + errorOk(); + #else + // Create vertex shader + shader->vertexShaderId = glCreateShader(GL_VERTEX_SHADER); + errorret_t err = errorGLCheck(); errorChain(err); - } - glCompileShader(shader->vertexShaderId); - err = errorGLCheck(); - if(err.code != ERROR_OK) { - glDeleteShader(shader->vertexShaderId); - errorChain(err); - } + glShaderSource(shader->vertexShaderId, 1, &def->vert, NULL); + 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); - } + glCompileShader(shader->vertexShaderId); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + errorChain(err); + } - // Create fragment shader - shader->fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER); - 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); + } - glShaderSource(shader->fragmentShaderId, 1, &def->frag, NULL); - err = errorGLCheck(); - if(err.code != ERROR_OK) { - glDeleteShader(shader->vertexShaderId); - glDeleteShader(shader->fragmentShaderId); - errorChain(err); - } + // Create fragment shader + shader->fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + errorChain(err); + } - glCompileShader(shader->fragmentShaderId); - err = errorGLCheck(); - if(err.code != ERROR_OK) { - glDeleteShader(shader->vertexShaderId); - glDeleteShader(shader->fragmentShaderId); - errorChain(err); - } + glShaderSource(shader->fragmentShaderId, 1, &def->frag, NULL); + 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); - } + glCompileShader(shader->fragmentShaderId); + err = errorGLCheck(); + if(err.code != ERROR_OK) { + glDeleteShader(shader->vertexShaderId); + glDeleteShader(shader->fragmentShaderId); + errorChain(err); + } - // Create shader program - shader->shaderProgramId = glCreateProgram(); - 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); + } - glAttachShader(shader->shaderProgramId, shader->vertexShaderId); - err = errorGLCheck(); - if(err.code != ERROR_OK) { - glDeleteProgram(shader->shaderProgramId); - glDeleteShader(shader->vertexShaderId); - glDeleteShader(shader->fragmentShaderId); - errorChain(err); - } + // 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->fragmentShaderId); - err = errorGLCheck(); - if(err.code != ERROR_OK) { - glDeleteProgram(shader->shaderProgramId); - 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); + } - glLinkProgram(shader->shaderProgramId); - 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); + } - 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); - } + 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); + } + #endif errorOk(); } diff --git a/src/duskgl/display/shader/shadergl.h b/src/duskgl/display/shader/shadergl.h index 91bb5de..56d50cf 100644 --- a/src/duskgl/display/shader/shadergl.h +++ b/src/duskgl/display/shader/shadergl.h @@ -9,14 +9,22 @@ #include "error/errorgl.h" typedef struct { - GLuint shaderProgramId; - GLuint vertexShaderId; - GLuint fragmentShaderId; + #ifdef DUSK_OPENGL_LEGACY + void *nothing; + #else + GLuint shaderProgramId; + GLuint vertexShaderId; + GLuint fragmentShaderId; + #endif } shadergl_t; typedef struct { - const char_t *vert; - const char_t *frag; + #ifdef DUSK_OPENGL_LEGACY + void *nothing; + #else + const char_t *vert; + const char_t *frag; + #endif } shaderdefinitiongl_t; /** diff --git a/src/duskgl/display/shader/shaderunlitgl.c b/src/duskgl/display/shader/shaderunlitgl.c index 8ac2cd0..9a653d1 100644 --- a/src/duskgl/display/shader/shaderunlitgl.c +++ b/src/duskgl/display/shader/shaderunlitgl.c @@ -11,22 +11,17 @@ 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" + " FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n" }; \ No newline at end of file From ca0e9fc3b2150748c24b61a69cdf6119a0b65633 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sun, 22 Mar 2026 09:13:42 -0500 Subject: [PATCH 3/4] Implement spritebatch properly. --- archive/dusk/map.py | 4 +- src/dusk/display/camera/camera.c | 64 +++++++++++++++-- src/dusk/display/camera/camera.h | 14 ++-- src/dusk/display/display.c | 68 ++++--------------- src/dusk/display/mesh/mesh.c | 25 ++++++- src/dusk/display/mesh/mesh.h | 16 +++++ src/dusk/display/screen/screen.c | 15 +++- src/dusk/display/spritebatch/spritebatch.c | 10 +-- src/dusk/display/spritebatch/spritebatch.h | 2 +- src/dusk/script/module/display/modulecamera.c | 23 +------ src/dusk/script/module/display/modulecamera.h | 16 ----- src/dusk/script/module/scene/modulescene.c | 2 +- src/dusk/script/module/system/modulesystem.c | 4 +- src/dusk/ui/ui.c | 4 +- src/duskgl/display/mesh/meshgl.c | 26 ++++++- src/duskgl/display/mesh/meshgl.h | 17 ++++- src/duskgl/display/mesh/meshplatform.h | 1 + src/duskpsp/log/log.c | 4 +- tools/tileset-creator.html | 32 ++++----- 19 files changed, 205 insertions(+), 142 deletions(-) diff --git a/archive/dusk/map.py b/archive/dusk/map.py index 358e87d..4b2ebbf 100644 --- a/archive/dusk/map.py +++ b/archive/dusk/map.py @@ -156,7 +156,7 @@ class Map: newTopLeftChunkY = y // CHUNK_HEIGHT - (MAP_HEIGHT // 2) newTopLeftChunkZ = z // CHUNK_DEPTH - (MAP_DEPTH // 2) - if (newTopLeftChunkX != self.topLeftX or + if(newTopLeftChunkX != self.topLeftX or newTopLeftChunkY != self.topLeftY or newTopLeftChunkZ != self.topLeftZ): @@ -166,7 +166,7 @@ class Map: chunkWorldX = chunk.x chunkWorldY = chunk.y chunkWorldZ = chunk.z - if (chunkWorldX < newTopLeftChunkX or + if(chunkWorldX < newTopLeftChunkX or chunkWorldX >= newTopLeftChunkX + MAP_WIDTH or chunkWorldY < newTopLeftChunkY or chunkWorldY >= newTopLeftChunkY + MAP_HEIGHT or diff --git a/src/dusk/display/camera/camera.c b/src/dusk/display/camera/camera.c index af0639e..bcabaf0 100644 --- a/src/dusk/display/camera/camera.c +++ b/src/dusk/display/camera/camera.c @@ -45,13 +45,63 @@ void cameraInitOrthographic(camera_t *camera) { camera->_2d.zoom = 1.0f; } -void cameraPushMatrix(camera_t *camera) { - assertNotNull(camera, "Invalid camera"); - cameraPushMatrixPlatform(camera); +void cameraGetProjectionMatrix(camera_t *camera, mat4 dest) { + assertNotNull(camera, "Not a camera component"); + assertNotNull(dest, "Destination matrix must not be null"); + + if( + camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE || + camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED + ) { + glm_mat4_identity(dest); + glm_perspective( + camera->perspective.fov, + SCREEN.aspect, + camera->nearClip, + camera->farClip, + dest + ); + + if(camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED) { + dest[1][1] *= -1.0f; + } + } else if(camera->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) { + glm_mat4_identity(dest); + glm_ortho( + camera->orthographic.left, + camera->orthographic.right, + camera->orthographic.top, + camera->orthographic.bottom, + camera->nearClip, + camera->farClip, + dest + ); + } } -void cameraPopMatrix(void) { - #ifdef cameraPopMatrixPlatform - cameraPopMatrixPlatform(); - #endif +void cameraGetViewMatrix(camera_t *camera, mat4 dest) { + assertNotNull(camera, "Not a camera component"); + assertNotNull(dest, "Destination matrix must not be null"); + + if(camera->viewType == CAMERA_VIEW_TYPE_MATRIX) { + glm_mat4_copy(camera->view, dest); + } else if(camera->viewType == CAMERA_VIEW_TYPE_LOOKAT) { + glm_mat4_identity(dest); + glm_lookat( + camera->lookat.position, + camera->lookat.target, + camera->lookat.up, + dest + ); + } else if(camera->viewType == CAMERA_VIEW_TYPE_2D) { + glm_mat4_identity(dest); + glm_lookat( + (vec3){ camera->_2d.position[0], camera->_2d.position[1], 0.5f }, + (vec3){ camera->_2d.position[0], camera->_2d.position[1], 0.0f }, + (vec3){ 0.0f, 1.0f, 0.0f }, + dest + ); + } else if(camera->viewType == CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT) { + assertUnreachable("LOOKAT_PIXEL_PERFECT view type is not implemented yet"); + } } \ No newline at end of file diff --git a/src/dusk/display/camera/camera.h b/src/dusk/display/camera/camera.h index 77426d7..d83fb39 100644 --- a/src/dusk/display/camera/camera.h +++ b/src/dusk/display/camera/camera.h @@ -86,13 +86,17 @@ void cameraInitPerspective(camera_t *camera); void cameraInitOrthographic(camera_t *camera); /** - * Pushes the camera's view matrix onto the matrix stack. + * Gets the projection matrix for a camera. * - * @param id The ID of the camera entity to use. + * @param camera Camera to get the projection matrix for + * @param dest Matrix to store the projection matrix in */ -void cameraPushMatrix(camera_t *camera); +void cameraGetProjectionMatrix(camera_t *camera, mat4 dest); /** - * Pops the camera's view matrix off the matrix stack. + * Gets the view matrix for a camera. + * + * @param camera Camera to get the view matrix for + * @param dest Matrix to store the view matrix in */ -void cameraPopMatrix(void); \ No newline at end of file +void cameraGetViewMatrix(camera_t *camera, mat4 dest); \ No newline at end of file diff --git a/src/dusk/display/display.c b/src/dusk/display/display.c index e72cae2..61ce15a 100644 --- a/src/dusk/display/display.c +++ b/src/dusk/display/display.c @@ -57,67 +57,27 @@ errorret_t displayUpdate(void) { errorChain(shaderBind(&SHADER_UNLIT)); - mat4 proj; - glm_mat4_identity(proj); - glm_ortho(0.0f, (float_t)SCREEN.width, (float_t)SCREEN.height, 0.0f, -1.0f, 1.0f, proj); + camera_t camera; + cameraInitOrthographic(&camera); + camera.orthographic.left = 0.0f; + camera.orthographic.right = SCREEN.width; + camera.orthographic.top = SCREEN.height; + camera.orthographic.bottom = 0.0f; - mat4 view; - glm_mat4_identity(view); - - mat4 model; + mat4 proj, view, model; + cameraGetProjectionMatrix(&camera, proj); + cameraGetViewMatrix(&camera, view); glm_mat4_identity(model); - glm_translate(model, (vec3){ - sinf(TIME.time * 10.0f) * 30.0f + 100.0f, - cosf(TIME.time * 10.0f) * 50.0f + 100.0f, - 0.0f - }); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view)); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model)); - - meshvertex_t verts[6] = { - { - .pos = { 0.0f, 0.0f, 0.0f }, - .uv = { 0.0f, 0.0f }, - .color = COLOR_WHITE - }, - { - .pos = { 100.0f, 0.0f, 0.0f }, - .uv = { 1.0f, 0.0f }, - .color = COLOR_WHITE - }, - - { - .pos = { 100.0f, 100.0f, 0.0f }, - .uv = { 1.0f, 1.0f }, - .color = COLOR_WHITE - }, - - { - .pos = { 100.0f, 100.0f, 0.0f }, - .uv = { 1.0f, 1.0f }, - .color = COLOR_WHITE - }, - - { - .pos = { 0.0f, 100.0f, 0.0f }, - .uv = { 0.0f, 1.0f }, - .color = COLOR_WHITE - }, - - { - .pos = { 0.0f, 0.0f, 0.0f }, - .uv = { 0.0f, 0.0f }, - .color = COLOR_WHITE - } - }; - - mesh_t mesh; - meshInit(&mesh, MESH_PRIMITIVE_TYPE_TRIANGLES, 6, verts); - meshDraw(&mesh, 0, -1); - meshDispose(&mesh); + errorChain(spriteBatchPush( + NULL, + TIME.time * 2.0f, TIME.time * 3.0f, 100, 100, COLOR_WHITE, 0, 0, 1, 1 + )); + errorChain(spriteBatchFlush()); // errorCatch(errorPrint(sceneRender())); diff --git a/src/dusk/display/mesh/mesh.c b/src/dusk/display/mesh/mesh.c index 5dd79d8..005a6d4 100644 --- a/src/dusk/display/mesh/mesh.c +++ b/src/dusk/display/mesh/mesh.c @@ -25,6 +25,29 @@ errorret_t meshInit( errorOk(); } +errorret_t meshFlush( + mesh_t *mesh, + const int32_t vertexOffset, + const int32_t vertexCount +) { + #ifdef meshFlushPlatform + assertNotNull(mesh, "Mesh cannot be NULL"); + assertTrue(vertexOffset >= 0, "Vertex offset must be non-negative."); + assertTrue(vertexCount == -1 || vertexCount > 0, "Vertex count incorrect."); + + int32_t vertCount = meshGetVertexCount(mesh); + assertTrue(vertexOffset < (vertCount - 1), "Need at least one vert to draw"); + + int32_t drawCount = vertexCount; + if(vertexCount == -1) { + drawCount = vertCount - vertexOffset; + } + + errorChain(meshFlushPlatform(mesh, vertexOffset, vertexCount)); + #endif + errorOk(); +} + errorret_t meshDraw( const mesh_t *mesh, const int32_t vertexOffset, @@ -32,7 +55,7 @@ errorret_t meshDraw( ) { assertNotNull(mesh, "Mesh cannot be NULL"); assertTrue(vertexOffset >= 0, "Vertex offset must be non-negative"); - assertTrue(vertexCount >= -1, "Vertex count must be -1 or non-negative"); + assertTrue(vertexCount == -1 || vertexCount > 0, "Incorrect vert count"); int32_t vertDrawCount = vertexCount; if(vertexCount == -1) { diff --git a/src/dusk/display/mesh/mesh.h b/src/dusk/display/mesh/mesh.h index b580f94..da16166 100644 --- a/src/dusk/display/mesh/mesh.h +++ b/src/dusk/display/mesh/mesh.h @@ -40,6 +40,22 @@ errorret_t meshInit( const meshvertex_t *vertices ); +/** + * Instructs the mesh to flush the vertices to the GPU. This is surprisingly + * only really necessary on modern devices, as we tend to let older devices + * read the vertices from the main memory directly. + * + * @param mesh Mesh to flush the vertices for. + * @param vertexOffset Start vertex to flush. + * @param vertexCount Count of vertices to flush, set to -1 for all. + * @return Error state. + */ +errorret_t meshFlush( + mesh_t *mesh, + const int32_t vertexOffset, + const int32_t vertexCount +); + /** * Draws a mesh. * diff --git a/src/dusk/display/screen/screen.c b/src/dusk/display/screen/screen.c index 27a96f5..ad9ab66 100644 --- a/src/dusk/display/screen/screen.c +++ b/src/dusk/display/screen/screen.c @@ -9,6 +9,7 @@ #include "assert/assert.h" #include "util/memory.h" #include "display/mesh/quad.h" +#include "display/shader/shaderunlit.h" screen_t SCREEN; @@ -370,10 +371,18 @@ errorret_t screenRender() { FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH, COLOR_BLACK ); - cameraPushMatrix(&SCREEN.framebufferCamera); - errorChain(textureBind(&SCREEN.framebuffer.texture)); + + shaderBind(&SHADER_UNLIT); + mat4 proj, view, model; + cameraGetProjectionMatrix(&SCREEN.framebufferCamera, proj); + cameraGetViewMatrix(&SCREEN.framebufferCamera, view); + glm_mat4_identity(model); + shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj); + shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view); + shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model); + + // errorChain(textureBind(&SCREEN.framebuffer.texture)); errorChain(meshDraw(&SCREEN.frameBufferMesh, 0, -1)); - cameraPopMatrix(); errorOk(); }; diff --git a/src/dusk/display/spritebatch/spritebatch.c b/src/dusk/display/spritebatch/spritebatch.c index cf41509..5015bd5 100644 --- a/src/dusk/display/spritebatch/spritebatch.c +++ b/src/dusk/display/spritebatch/spritebatch.c @@ -19,7 +19,7 @@ errorret_t spriteBatchInit() { &SPRITEBATCH.mesh, QUAD_PRIMITIVE_TYPE, SPRITEBATCH_VERTEX_COUNT, - &SPRITEBATCH_VERTICES[0] + SPRITEBATCH_VERTICES )); errorOk(); } @@ -82,11 +82,11 @@ errorret_t spriteBatchFlush() { if(SPRITEBATCH.spriteCount == 0) { errorOk(); } - + + size_t vertexCount = QUAD_VERTEX_COUNT * SPRITEBATCH.spriteCount; + errorChain(meshFlush(&SPRITEBATCH.mesh, 0, vertexCount)); errorChain(textureBind(SPRITEBATCH.currentTexture)); - errorChain(meshDraw( - &SPRITEBATCH.mesh, 0, QUAD_VERTEX_COUNT * SPRITEBATCH.spriteCount - )); + errorChain(meshDraw(&SPRITEBATCH.mesh, 0, vertexCount)); spriteBatchClear(); errorOk(); } diff --git a/src/dusk/display/spritebatch/spritebatch.h b/src/dusk/display/spritebatch/spritebatch.h index 6a87c19..a22ec78 100644 --- a/src/dusk/display/spritebatch/spritebatch.h +++ b/src/dusk/display/spritebatch/spritebatch.h @@ -9,7 +9,7 @@ #include "display/mesh/quad.h" #include "display/texture/texture.h" -#define SPRITEBATCH_SPRITES_MAX 16 +#define SPRITEBATCH_SPRITES_MAX 1 #define SPRITEBATCH_VERTEX_COUNT (SPRITEBATCH_SPRITES_MAX * QUAD_VERTEX_COUNT) diff --git a/src/dusk/script/module/display/modulecamera.c b/src/dusk/script/module/display/modulecamera.c index 6d70443..737df9d 100644 --- a/src/dusk/script/module/display/modulecamera.c +++ b/src/dusk/script/module/display/modulecamera.c @@ -69,8 +69,6 @@ void moduleCamera(scriptcontext_t *context) { // Methods lua_register(context->luaState, "cameraCreate", moduleCameraCreate); - lua_register(context->luaState, "cameraPushMatrix", moduleCameraPushMatrix); - lua_register(context->luaState, "cameraPopMatrix", moduleCameraPopMatrix); } int moduleCameraCreate(lua_State *L) { @@ -118,26 +116,7 @@ int moduleCameraCreate(lua_State *L) { return 1; } -int moduleCameraPushMatrix(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL."); - assertTrue(lua_gettop(L) >= 1, "cameraPushMatrix requires 1 arg."); - assertTrue(lua_isuserdata(L, 1), "cameraPushMatrix arg must be userdata."); - - // Camera should be provided (pointer to camera_t). - camera_t *cam = (camera_t *)luaL_checkudata(L, 1, "camera_mt"); - assertNotNull(cam, "Camera pointer cannot be NULL."); - - cameraPushMatrix(cam); - return 0; -} - -int moduleCameraPopMatrix(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL."); - cameraPopMatrix(); - return 0; -} - -int moduleCameraIndex(lua_State *l) { + int moduleCameraIndex(lua_State *l) { assertNotNull(l, "Lua state cannot be NULL."); const char_t *key = luaL_checkstring(l, 2); diff --git a/src/dusk/script/module/display/modulecamera.h b/src/dusk/script/module/display/modulecamera.h index 7032e53..2931d3c 100644 --- a/src/dusk/script/module/display/modulecamera.h +++ b/src/dusk/script/module/display/modulecamera.h @@ -23,22 +23,6 @@ void moduleCamera(scriptcontext_t *context); */ int moduleCameraCreate(lua_State *L); -/** - * Script binding for pushing the camera matrix onto the matrix stack. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleCameraPushMatrix(lua_State *L); - -/** - * Script binding for popping the camera matrix from the matrix stack. - * - * @param L The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleCameraPopMatrix(lua_State *L); - /** * Getter for camera structure fields. * diff --git a/src/dusk/script/module/scene/modulescene.c b/src/dusk/script/module/scene/modulescene.c index acc56a3..30df0eb 100644 --- a/src/dusk/script/module/scene/modulescene.c +++ b/src/dusk/script/module/scene/modulescene.c @@ -19,7 +19,7 @@ int moduleSceneSet(lua_State *L) { assertNotNull(L, "Lua state cannot be NULL"); // Need string - if (!lua_isstring(L, 1)) { + if(!lua_isstring(L, 1)) { luaL_error(L, "sceneSet requires a string argument"); return 0; } diff --git a/src/dusk/script/module/system/modulesystem.c b/src/dusk/script/module/system/modulesystem.c index 1ce2ee7..6c885b7 100644 --- a/src/dusk/script/module/system/modulesystem.c +++ b/src/dusk/script/module/system/modulesystem.c @@ -26,12 +26,12 @@ int moduleSysPrint(lua_State *L) { luaL_Buffer b; luaL_buffinit(L, &b); - for (int i = 1; i <= n; ++i) { + for(int i = 1; i <= n; ++i) { size_t len; const char *s = luaL_tolstring(L, i, &len); // converts any value to string luaL_addlstring(&b, s, len); lua_pop(L, 1); // pop result of luaL_tolstring - if (i < n) luaL_addlstring(&b, "\t", 1); + if(i < n) luaL_addlstring(&b, "\t", 1); } luaL_pushresult(&b); diff --git a/src/dusk/ui/ui.c b/src/dusk/ui/ui.c index cfa73bf..c71ee80 100644 --- a/src/dusk/ui/ui.c +++ b/src/dusk/ui/ui.c @@ -30,11 +30,11 @@ void uiRender(void) { UI.camera.orthographic.right = SCREEN.width; UI.camera.orthographic.top = SCREEN.height; - cameraPushMatrix(&UI.camera); + // cameraPushMatrix(&UI.camera); spriteBatchClear(); spriteBatchFlush(); - cameraPopMatrix(); + // cameraPopMatrix(); } void uiDispose(void) { diff --git a/src/duskgl/display/mesh/meshgl.c b/src/duskgl/display/mesh/meshgl.c index 233bb59..88dd62d 100644 --- a/src/duskgl/display/mesh/meshgl.c +++ b/src/duskgl/display/mesh/meshgl.c @@ -21,9 +21,10 @@ errorret_t meshInitGL( mesh->primitiveType = primitiveType; mesh->vertexCount = vertexCount; + mesh->vertices = vertices; #ifdef DUSK_OPENGL_LEGACY - mesh->vertices = vertices; + // Nothing needed. #else // Generate Vertex Buffer Object glGenBuffers(1, &mesh->vboId); @@ -34,7 +35,7 @@ errorret_t meshInitGL( GL_ARRAY_BUFFER, vertexCount * sizeof(meshvertex_t), vertices, - GL_STATIC_DRAW + GL_DYNAMIC_DRAW ); errorChain(errorGLCheck()); @@ -93,6 +94,27 @@ errorret_t meshInitGL( errorOk(); } +errorret_t meshFlushGL( + meshgl_t *mesh, + const int32_t vertOffset, + const int32_t vertCount +) { + #ifdef DUSK_OPENGL_LEGACY + // Nothing doing, we use the glClientState stuff. + #else + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId); + errorChain(errorGLCheck()); + glBufferData( + GL_ARRAY_BUFFER, + vertCount * sizeof(meshvertex_t), + &mesh->vertices[vertOffset], + GL_DYNAMIC_DRAW + ); + errorChain(errorGLCheck()); + #endif + errorOk(); +} + errorret_t meshDrawGL( const meshgl_t *mesh, const int32_t offset, diff --git a/src/duskgl/display/mesh/meshgl.h b/src/duskgl/display/mesh/meshgl.h index 208a3ab..9b7533c 100644 --- a/src/duskgl/display/mesh/meshgl.h +++ b/src/duskgl/display/mesh/meshgl.h @@ -18,9 +18,10 @@ typedef enum { typedef struct { int32_t vertexCount; meshprimitivetypegl_t primitiveType; + const meshvertex_t *vertices; #ifdef DUSK_OPENGL_LEGACY - const meshvertex_t *vertices; + // Nothing needed #else GLuint vaoId; GLuint vboId; @@ -43,6 +44,20 @@ errorret_t meshInitGL( const meshvertex_t *vertices ); +/** + * Flushes the vertices (stored in memory) to the GPU. + * + * @param mesh Mesh to flush vertices for. + * @param vertOffset First vertice index to flush. + * @param vertCount Count of vertices to flush. + * @return Error state. + */ +errorret_t meshFlushGL( + meshgl_t *mesh, + const int32_t vertOffset, + const int32_t vertCount +); + /** * Draws a mesh using OpenGL. * diff --git a/src/duskgl/display/mesh/meshplatform.h b/src/duskgl/display/mesh/meshplatform.h index 0419534..f05ab50 100644 --- a/src/duskgl/display/mesh/meshplatform.h +++ b/src/duskgl/display/mesh/meshplatform.h @@ -12,6 +12,7 @@ typedef meshprimitivetypegl_t meshprimitivetypeplatform_t; typedef meshgl_t meshplatform_t; #define meshInitPlatform meshInitGL +#define meshFlushPlatform meshFlushGL #define meshDrawPlatform meshDrawGL #define meshGetVertexCountPlatform meshGetVertexCountGL #define meshDisposePlatform meshDisposeGL \ No newline at end of file diff --git a/src/duskpsp/log/log.c b/src/duskpsp/log/log.c index ba8eed9..040f0be 100644 --- a/src/duskpsp/log/log.c +++ b/src/duskpsp/log/log.c @@ -19,7 +19,7 @@ void logDebug(const char_t *message, ...) { // print to file FILE *file = fopen("ms0:/PSP/GAME/Dusk/debug.log", "a"); - if (file) { + if(file) { va_copy(copy, args); vfprintf(file, message, copy); va_end(copy); @@ -41,7 +41,7 @@ void logError(const char_t *message, ...) { // print to file FILE *file = fopen("ms0:/PSP/GAME/Dusk/error.log", "a"); - if (file) { + if(file) { va_copy(copy, args); vfprintf(file, message, copy); va_end(copy); diff --git a/tools/tileset-creator.html b/tools/tileset-creator.html index 3dc5ae3..1b74b08 100644 --- a/tools/tileset-creator.html +++ b/tools/tileset-creator.html @@ -247,13 +247,13 @@ // Draw red grid lines for tile boundaries ctx.strokeStyle = 'rgba(255,0,0,1)'; - for (let x = v.scaledTileWidth; x < elOutputPreview.width; x += v.scaledTileWidth) { + for(let x = v.scaledTileWidth; x < elOutputPreview.width; x += v.scaledTileWidth) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, elOutputPreview.height); ctx.stroke(); } - for (let y = v.scaledTileHeight; y < elOutputPreview.height; y += v.scaledTileHeight) { + for(let y = v.scaledTileHeight; y < elOutputPreview.height; y += v.scaledTileHeight) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(elOutputPreview.width, y); @@ -289,7 +289,7 @@ btnBackgroundGreen.addEventListener('click', () => document.body.style.background = 'green'); elDefineBySize.addEventListener('change', () => { - if (elDefineBySize.checked) { + if(elDefineBySize.checked) { elTileSizes.style.display = ''; elTileCounts.style.display = 'none'; } @@ -297,7 +297,7 @@ }); elDefineByCount.addEventListener('change', () => { - if (elDefineByCount.checked) { + if(elDefineByCount.checked) { elTileSizes.style.display = 'none'; elTileCounts.style.display = ''; } @@ -332,7 +332,7 @@ elOutputError.style.display = 'none'; pixels = null; - if (!elFileInput.files.length) { + if(!elFileInput.files.length) { elOutputError.textContent = 'No file selected'; elOutputError.style.display = 'block'; return; @@ -340,18 +340,18 @@ const file = elFileInput.files[0]; - if (file.name.endsWith('.dpt')) { + if(file.name.endsWith('.dpt')) { // Load DPT file const reader = new FileReader(); reader.onload = () => { const arrayBuffer = reader.result; const data = new Uint8Array(arrayBuffer); - if (data[0] !== 'D'.charCodeAt(0) || data[1] !== 'P'.charCodeAt(0) || data[2] !== 'T'.charCodeAt(0)) { + if(data[0] !== 'D'.charCodeAt(0) || data[1] !== 'P'.charCodeAt(0) || data[2] !== 'T'.charCodeAt(0)) { elOutputError.textContent = 'Invalid DPT file'; elOutputError.style.display = 'block'; return; - } else if (data[3] !== 0x01) { + } else if(data[3] !== 0x01) { elOutputError.textContent = 'Unsupported DPT version'; elOutputError.style.display = 'block'; return; @@ -381,15 +381,15 @@ } const uniqueIndexes = []; - for (let i = 0; i < width * height; i++) { + for(let i = 0; i < width * height; i++) { const colorIndex = data[12 + i]; - if (!uniqueIndexes.includes(colorIndex)) { + if(!uniqueIndexes.includes(colorIndex)) { uniqueIndexes.push(colorIndex); } } const adhocPalette = []; - for (let i = 0; i < uniqueIndexes.length; i++) { + for(let i = 0; i < uniqueIndexes.length; i++) { const index = uniqueIndexes[i]; // Get the most different possible color for this index const color = [ @@ -402,7 +402,7 @@ } pixels = new Uint8Array(width * height * 4); - for (let i = 0; i < width * height; i++) { + for(let i = 0; i < width * height; i++) { const colorIndex = data[12 + i]; const color = adhocPalette[colorIndex]; pixels[i * 4] = color[0]; @@ -487,13 +487,13 @@ input.accept = '.dtf'; input.addEventListener('change', (e) => { const files = e?.target?.files; - if (!files || !files.length || !files[0]) { + if(!files || !files.length || !files[0]) { alert('No file selected'); return; } const file = files[0]; - if (!file.name.endsWith('.dtf')) { + if(!file.name.endsWith('.dtf')) { alert('Invalid file type. Please select a .dtf file.'); return; } @@ -502,12 +502,12 @@ reader.onload = () => { const arrayBuffer = reader.result; const data = new Uint8Array(arrayBuffer); - if (data[0] !== 'D'.charCodeAt(0) || data[1] !== 'T'.charCodeAt(0) || data[2] !== 'F'.charCodeAt(0)) { + if(data[0] !== 'D'.charCodeAt(0) || data[1] !== 'T'.charCodeAt(0) || data[2] !== 'F'.charCodeAt(0)) { alert('Invalid DTF file'); return; } - if (data[3] !== 0x00) { + if(data[3] !== 0x00) { alert('Unsupported DTF version'); return; } From 5ac21db9976661298514fb5583b348efdde8bfcd Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sun, 22 Mar 2026 10:44:28 -0500 Subject: [PATCH 4/4] Shaders adapted for Legacy GL --- cmake/targets/linux.cmake | 1 + src/dusk/display/shader/shader.c | 2 +- src/dusk/display/shader/shader.h | 2 +- src/duskgl/display/displaygl.c | 7 -- src/duskgl/display/mesh/meshgl.c | 10 ++ src/duskgl/display/shader/shadergl.c | 141 +++++++++++++++++----- src/duskgl/display/shader/shadergl.h | 33 ++++- src/duskgl/display/shader/shaderunlitgl.c | 38 +++--- src/dusksdl2/display/displaysdl2.c | 11 +- 9 files changed, 184 insertions(+), 61 deletions(-) diff --git a/cmake/targets/linux.cmake b/cmake/targets/linux.cmake index f83d136..bec0a02 100644 --- a/cmake/targets/linux.cmake +++ b/cmake/targets/linux.cmake @@ -29,6 +29,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC DUSK_SDL2 DUSK_OPENGL DUSK_LINUX + DUSK_OPENGL_LEGACY DUSK_DISPLAY_SIZE_DYNAMIC DUSK_DISPLAY_WIDTH_DEFAULT=640 DUSK_DISPLAY_HEIGHT_DEFAULT=480 diff --git a/src/dusk/display/shader/shader.c b/src/dusk/display/shader/shader.c index 9f7e914..cc6550a 100644 --- a/src/dusk/display/shader/shader.c +++ b/src/dusk/display/shader/shader.c @@ -23,7 +23,7 @@ errorret_t shaderBind(shader_t *shader) { errorret_t shaderSetMatrix( shader_t *shader, const char_t *name, - const mat4 matrix + mat4 matrix ) { assertNotNull(shader, "Shader cannot be null"); assertStrLenMin(name, 1, "Uniform name cannot be empty"); diff --git a/src/dusk/display/shader/shader.h b/src/dusk/display/shader/shader.h index b543ac4..2af399d 100644 --- a/src/dusk/display/shader/shader.h +++ b/src/dusk/display/shader/shader.h @@ -54,7 +54,7 @@ errorret_t shaderBind(shader_t *shader); errorret_t shaderSetMatrix( shader_t *shader, const char_t *name, - const mat4 matrix + mat4 matrix ); /** diff --git a/src/duskgl/display/displaygl.c b/src/duskgl/display/displaygl.c index 0affce3..e8b2420 100644 --- a/src/duskgl/display/displaygl.c +++ b/src/duskgl/display/displaygl.c @@ -8,8 +8,6 @@ #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? @@ -26,10 +24,5 @@ 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()); - errorOk(); } \ No newline at end of file diff --git a/src/duskgl/display/mesh/meshgl.c b/src/duskgl/display/mesh/meshgl.c index 88dd62d..c931868 100644 --- a/src/duskgl/display/mesh/meshgl.c +++ b/src/duskgl/display/mesh/meshgl.c @@ -8,6 +8,7 @@ #include "display/mesh/mesh.h" #include "assert/assertgl.h" #include "error/errorgl.h" +#include "display/shader/shadergl.h" errorret_t meshInitGL( meshgl_t *mesh, @@ -25,6 +26,10 @@ errorret_t meshInitGL( #ifdef DUSK_OPENGL_LEGACY // Nothing needed. + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + errorChain(errorGLCheck()); #else // Generate Vertex Buffer Object glGenBuffers(1, &mesh->vboId); @@ -143,6 +148,11 @@ errorret_t meshDrawGL( (const GLvoid*)&mesh->vertices[offset].pos ); + // Shader may have model matrix here + #ifdef DUSK_OPENGL_LEGACY + errorChain(shaderLegacyMatrixUpdate()); + #endif + glDrawArrays(mesh->primitiveType, offset, count); errorChain(errorGLCheck()); #else diff --git a/src/duskgl/display/shader/shadergl.c b/src/duskgl/display/shader/shadergl.c index 2c82aa3..b753a43 100644 --- a/src/duskgl/display/shader/shadergl.c +++ b/src/duskgl/display/shader/shadergl.c @@ -7,7 +7,13 @@ #include "shadergl.h" #include "util/memory.h" +#include "util/string.h" #include "assert/assertgl.h" +#include "display/shader/shaderunlit.h" + +#ifdef DUSK_OPENGL_LEGACY + shaderlegacygl_t SHADER_LEGACY = { 0 }; +#endif errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) { assertNotNull(shader, "Shader cannot be null"); @@ -15,7 +21,11 @@ errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) { memoryZero(shader, sizeof(shadergl_t)); #ifdef DUSK_OPENGL_LEGACY - // No initialization needed for legacy shader + glm_mat4_identity(shader->view); + glm_mat4_identity(shader->proj); + glm_mat4_identity(shader->model); + + SHADER_LEGACY.boundShader = NULL; errorOk(); #else // Create vertex shader @@ -139,12 +149,16 @@ errorret_t shaderParamGetLocationGL( 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); - } + #ifdef DUSK_OPENGL_LEGACY + assertUnreachable("Cannot get uniform locations on legacy opengl."); + #else + shadergl_t *shaderGL = (shadergl_t *)shader; + *location = glGetUniformLocation(shaderGL->shaderProgramId, name); + errorret_t err = errorGLCheck(); + if(err.code != ERROR_OK) { + errorChain(err); + } + #endif errorOk(); } @@ -152,27 +166,58 @@ errorret_t shaderParamGetLocationGL( errorret_t shaderSetMatrixGL( shadergl_t *shader, const char_t *name, - const mat4 mat + 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)); + #ifdef DUSK_OPENGL_LEGACY + assertTrue( + SHADER_LEGACY.boundShader == shader, + "Shader must be bound to set legacy matrices." + ); + if(stringCompare(name, SHADER_UNLIT_PROJECTION) == 0) { + SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_PROJ; + glm_mat4_copy(mat, shader->proj); - glUniformMatrix4fv(location, 1, GL_FALSE, (const GLfloat *)mat); - errorChain(errorGLCheck()); + } else if(stringCompare(name, SHADER_UNLIT_VIEW) == 0) { + SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_VIEW; + glm_mat4_copy(mat, shader->view); + + } else if(stringCompare(name, SHADER_UNLIT_MODEL) == 0) { + SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_MODEL; + glm_mat4_copy(mat, shader->model); + + } else { + assertUnreachable("Cannot use a custom matrix on legacy opengl."); + } + #else + GLint location; + errorChain(shaderParamGetLocationGL(shader, name, &location)); + + glUniformMatrix4fv(location, 1, GL_FALSE, (const GLfloat *)mat); + errorChain(errorGLCheck()); + #endif errorOk(); } errorret_t shaderBindGL(shadergl_t *shader) { - assertNotNull(shader, "Shader cannot be null"); - - glUseProgram(shader->shaderProgramId); - errorChain(errorGLCheck()); + #ifdef DUSK_OPENGL_LEGACY + assertNotNull(shader, "Cannot bind a null shader."); + SHADER_LEGACY.boundShader = shader; + SHADER_LEGACY.dirty = ( + SHADER_LEGACY_DIRTY_MODEL | + SHADER_LEGACY_DIRTY_PROJ | + SHADER_LEGACY_DIRTY_VIEW + ); + #else + assertNotNull(shader, "Shader cannot be null"); + glUseProgram(shader->shaderProgramId); + errorChain(errorGLCheck()); + #endif errorOk(); } @@ -180,22 +225,58 @@ errorret_t shaderBindGL(shadergl_t *shader) { 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()); - } + #ifdef DUSK_OPENGL_LEGACY + SHADER_LEGACY.boundShader = NULL; + #else + if(shader->shaderProgramId != 0) { + glDeleteProgram(shader->shaderProgramId); + } + + if(shader->vertexShaderId != 0) { + glDeleteShader(shader->vertexShaderId); + } - if(shader->fragmentShaderId != 0) { - glDeleteShader(shader->fragmentShaderId); - errorChain(errorGLCheck()); - } + if(shader->fragmentShaderId != 0) { + glDeleteShader(shader->fragmentShaderId); + } + + assertNoGLError("Failed disposing shader"); + #endif - assertNoGLError("Failed disposing shader"); memoryZero(shader, sizeof(shadergl_t)); errorOk(); } + +#ifdef DUSK_OPENGL_LEGACY + errorret_t shaderLegacyMatrixUpdate() { + assertNotNull(SHADER_LEGACY.boundShader, "No shader is currently bound."); + + if((SHADER_LEGACY.dirty & SHADER_LEGACY_DIRTY_PROJ) != 0) { + glMatrixMode(GL_PROJECTION); + errorChain(errorGLCheck()); + glLoadIdentity(); + errorChain(errorGLCheck()); + glMultMatrixf((const GLfloat *)SHADER_LEGACY.boundShader->proj); + errorChain(errorGLCheck()); + } + + if((SHADER_LEGACY.dirty & SHADER_LEGACY_DIRTY_VIEW) != 0) { + glMatrixMode(GL_MODELVIEW); + errorChain(errorGLCheck()); + glLoadIdentity(); + errorChain(errorGLCheck()); + glMultMatrixf((const GLfloat *)SHADER_LEGACY.boundShader->view); + errorChain(errorGLCheck()); + } + + if((SHADER_LEGACY.dirty & SHADER_LEGACY_DIRTY_MODEL) != 0) { + glMatrixMode(GL_MODELVIEW); + errorChain(errorGLCheck()); + glMultMatrixf((const GLfloat *)SHADER_LEGACY.boundShader->model); + errorChain(errorGLCheck()); + } + + SHADER_LEGACY.dirty = 0; + errorOk(); + } +#endif \ No newline at end of file diff --git a/src/duskgl/display/shader/shadergl.h b/src/duskgl/display/shader/shadergl.h index 56d50cf..2ba660e 100644 --- a/src/duskgl/display/shader/shadergl.h +++ b/src/duskgl/display/shader/shadergl.h @@ -16,6 +16,12 @@ typedef struct { GLuint vertexShaderId; GLuint fragmentShaderId; #endif + + #if DUSK_OPENGL_LEGACY + mat4 view; + mat4 proj; + mat4 model; + #endif } shadergl_t; typedef struct { @@ -27,6 +33,19 @@ typedef struct { #endif } shaderdefinitiongl_t; +#if DUSK_OPENGL_LEGACY + typedef struct { + shadergl_t *boundShader; + uint_fast8_t dirty; + } shaderlegacygl_t; + + extern shaderlegacygl_t SHADER_LEGACY; + + #define SHADER_LEGACY_DIRTY_PROJ (1 << 0) + #define SHADER_LEGACY_DIRTY_VIEW (1 << 1) + #define SHADER_LEGACY_DIRTY_MODEL (1 << 2) +#endif + /** * Initializes a shader. * @@ -69,7 +88,7 @@ errorret_t shaderParamGetLocationGL( errorret_t shaderSetMatrixGL( shadergl_t *shader, const char_t *name, - const mat4 matrix + mat4 matrix ); /** @@ -77,4 +96,14 @@ errorret_t shaderSetMatrixGL( * * @param shader The shader to dispose. */ -errorret_t shaderDisposeGL(shadergl_t *shader); \ No newline at end of file +errorret_t shaderDisposeGL(shadergl_t *shader); + +#ifdef DUSK_OPENGL_LEGACY + /** + * During mesh rendering, this is requesting the legacy system to push all + * shaders necessary to render the currently bound shader's matrices. + * + * @return Any error state. + */ + errorret_t shaderLegacyMatrixUpdate(); +#endif \ No newline at end of file diff --git a/src/duskgl/display/shader/shaderunlitgl.c b/src/duskgl/display/shader/shaderunlitgl.c index 9a653d1..179dc4e 100644 --- a/src/duskgl/display/shader/shaderunlitgl.c +++ b/src/duskgl/display/shader/shaderunlitgl.c @@ -7,21 +7,25 @@ #include "display/shader/shaderunlit.h" -shaderdefinition_t SHADER_UNLIT_DEFINITION = { - .vert = - "#version 330 core\n" - "layout(location = 0) in vec3 aPos;\n" - "uniform mat4 u_Proj;\n" - "uniform mat4 u_View;\n" - "uniform mat4 u_Model;\n" - "void main() {\n" - " gl_Position = u_Proj * u_View * u_Model * vec4(aPos, 1.0);\n" - "}\n", +#ifdef DUSK_OPENGL_LEGACY + shaderdefinition_t SHADER_UNLIT_DEFINITION = { 0 }; +#else + shaderdefinition_t SHADER_UNLIT_DEFINITION = { + .vert = + "#version 330 core\n" + "layout(location = 0) in vec3 aPos;\n" + "uniform mat4 u_Proj;\n" + "uniform mat4 u_View;\n" + "uniform mat4 u_Model;\n" + "void main() {\n" + " gl_Position = u_Proj * u_View * u_Model * vec4(aPos, 1.0);\n" + "}\n", - .frag = - "#version 330 core\n" - "out vec4 FragColor;\n" - "void main() {\n" - " FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" - "}\n" -}; \ No newline at end of file + .frag = + "#version 330 core\n" + "out vec4 FragColor;\n" + "void main() {\n" + " FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + "}\n" + }; +#endif \ No newline at end of file diff --git a/src/dusksdl2/display/displaysdl2.c b/src/dusksdl2/display/displaysdl2.c index f8582d8..c34d8f8 100644 --- a/src/dusksdl2/display/displaysdl2.c +++ b/src/dusksdl2/display/displaysdl2.c @@ -23,9 +23,14 @@ errorret_t displaySDL2Init(void) { 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); + + #ifdef DUSK_OPENGL_LEGACY + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); + #else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + #endif // Create window with OpenGL flag. DISPLAY.window = SDL_CreateWindow(