diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index d246405..24e5194 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -23,5 +23,6 @@ target_sources(${DUSK_TARGET_NAME} # Subdirs add_subdirectory(assert) -add_subdirectory(render) -add_subdirectory(overworld) \ No newline at end of file +add_subdirectory(display) +add_subdirectory(overworld) +add_subdirectory(util) \ No newline at end of file diff --git a/src/dusk/render/CMakeLists.txt b/src/dusk/display/CMakeLists.txt similarity index 100% rename from src/dusk/render/CMakeLists.txt rename to src/dusk/display/CMakeLists.txt diff --git a/src/dusk/display/displaydefs.h b/src/dusk/display/displaydefs.h new file mode 100644 index 0000000..df4906e --- /dev/null +++ b/src/dusk/display/displaydefs.h @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 240 \ No newline at end of file diff --git a/src/dusk/render/scene.c b/src/dusk/display/scene.c similarity index 100% rename from src/dusk/render/scene.c rename to src/dusk/display/scene.c diff --git a/src/dusk/render/scene.h b/src/dusk/display/scene.h similarity index 100% rename from src/dusk/render/scene.h rename to src/dusk/display/scene.h diff --git a/src/dusk/game.c b/src/dusk/game.c index b6cf387..e82f32e 100644 --- a/src/dusk/game.c +++ b/src/dusk/game.c @@ -7,7 +7,7 @@ #include "game.h" #include "input.h" -#include "render/scene.h" +#include "display/scene.h" game_t GAME; diff --git a/src/duskgl/display/CMakeLists.txt b/src/duskgl/display/CMakeLists.txt index 7d676c7..bd907eb 100644 --- a/src/duskgl/display/CMakeLists.txt +++ b/src/duskgl/display/CMakeLists.txt @@ -6,7 +6,11 @@ # Sources target_sources(${DUSK_TARGET_NAME} PRIVATE + framebuffer.c render.c + quad.c + texture.c ) -# Subdirs \ No newline at end of file +# Subdirs +add_subdirectory(shader) \ No newline at end of file diff --git a/src/duskgl/display/framebuffer.c b/src/duskgl/display/framebuffer.c new file mode 100644 index 0000000..e06dcb6 --- /dev/null +++ b/src/duskgl/display/framebuffer.c @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "framebuffer.h" +#include "assert/assert.h" +#include "assert/assertgl.h" +#include "display/window.h" + +framebuffer_t FRAMEBUFFER; + +void frameBufferInit(const int32_t width, const int32_t height) { + assertTrue(width > 0, "Width must be greater than 0"); + assertTrue(height > 0, "Height must be greater than 0"); + + FRAMEBUFFER.id = -1; + FRAMEBUFFER.width = -1; + FRAMEBUFFER.height = -1; + FRAMEBUFFER.texture = -1; + + frameBufferSetSize(width, height); +} + +void frameBufferSetSize(const int32_t width, const int32_t height) { + assertTrue(width > 0, "Width must be greater than 0"); + assertTrue(height > 0, "Height must be greater than 0"); + + if(FRAMEBUFFER.width == width && FRAMEBUFFER.height == height) return; + + FRAMEBUFFER.width = width; + FRAMEBUFFER.height = height; + + frameBufferUnbind(); + + // Delete old texture and depth buffer + if(FRAMEBUFFER.texture != -1) { + glDeleteTextures(1, &FRAMEBUFFER.texture); + assertNoGLError(); + FRAMEBUFFER.texture = -1; + } + + if(FRAMEBUFFER.id != -1) { + glDeleteFramebuffers(1, &FRAMEBUFFER.id); + assertNoGLError(); + FRAMEBUFFER.id = -1; + } + + // Generate framebuffer + glGenFramebuffers(1, &FRAMEBUFFER.id); + assertNoGLError(); + glBindFramebuffer(GL_FRAMEBUFFER, FRAMEBUFFER.id); + assertNoGLError(); + + // Create new texture + glGenTextures(1, &FRAMEBUFFER.texture); + assertNoGLError(); + glBindTexture(GL_TEXTURE_2D, FRAMEBUFFER.texture); + assertNoGLError(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + assertNoGLError(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + assertNoGLError(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + assertNoGLError(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + assertNoGLError(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + assertNoGLError(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FRAMEBUFFER.texture, 0); + assertNoGLError(); + + // Check framebuffer completeness + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + assertNoGLError(); + assertTrue(status == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is not complete"); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + assertNoGLError(); +} + +void frameBufferBind() { + if(FRAMEBUFFER.id == -1) return; + + glBindFramebuffer(GL_FRAMEBUFFER, FRAMEBUFFER.id); + assertNoGLError(); + + glViewport(0, 0, FRAMEBUFFER.width, FRAMEBUFFER.height); + assertNoGLError(); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + assertNoGLError(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + assertNoGLError(); +} + +void frameBufferTextureBind(const GLuint slot) { + if(FRAMEBUFFER.texture == -1) return; + + glActiveTexture(GL_TEXTURE0 + slot); + assertNoGLError(); + + glBindTexture(GL_TEXTURE_2D, FRAMEBUFFER.texture); + assertNoGLError(); +} + +void frameBufferUnbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + assertNoGLError(); + + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + assertNoGLError(); + + glClearColor(0.05f, 0.05f, 0.05f, 1.0f); + assertNoGLError(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + assertNoGLError(); +} + +void frameBufferDispose() { + glDeleteFramebuffers(1, &FRAMEBUFFER.id); + glDeleteTextures(1, &FRAMEBUFFER.texture); + + FRAMEBUFFER.id = -1; + FRAMEBUFFER.texture = -1; +} \ No newline at end of file diff --git a/src/duskgl/display/framebuffer.h b/src/duskgl/display/framebuffer.h new file mode 100644 index 0000000..7fa1cc2 --- /dev/null +++ b/src/duskgl/display/framebuffer.h @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "duskgl.h" + +typedef struct { + GLuint id; + GLuint texture; + int width; + int height; +} framebuffer_t; + +extern framebuffer_t FRAMEBUFFER; + +/** + * Initializes the framebuffer. + * + * @param width The width of the framebuffer. + * @param height The height of the framebuffer. + */ +void frameBufferInit(const int32_t width, const int32_t height); + +/** + * Sets the size of the framebuffer. + * + * @param width The width of the framebuffer. + * @param height The height of the framebuffer. + */ +void frameBufferSetSize(const int32_t width, const int32_t height); + +/** + * Binds the framebuffer. + */ +void frameBufferBind(); + +/** + * Unbinds the framebuffer. + */ +void frameBufferUnbind(); + +/** + * Binds the framebuffer texture to a slot. + * + * @param slot The slot to bind the texture to. + */ +void frameBufferTextureBind(const GLuint slot); + +/** + * Unbinds the framebuffer. + */ +void frameBufferDispose(); \ No newline at end of file diff --git a/src/duskgl/display/quad.c b/src/duskgl/display/quad.c new file mode 100644 index 0000000..768c6a0 --- /dev/null +++ b/src/duskgl/display/quad.c @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "assert/assertgl.h" +#include "quad.h" + +duskquad_t QUAD; + +void quadInit() { + // Create the single quad. + const float quadPositions[] = { + 0.0f, 0.0f, 0.0f, // Bottom-left corner + 1.0f, 0.0f, 0.0f, // Bottom-right corner + 1.0f, 1.0f, 0.0f, // Top-right corner + 0.0f, 1.0f, 0.0f // Top-left corner + }; + + const float quadUVs[] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f + }; + + const uint32_t quadIndices[] = { + 0, 1, 2, + 2, 3, 0, + }; + + glGenVertexArrays(1, &QUAD.quadVAO); + assertNoGLError(); + + glBindVertexArray(QUAD.quadVAO); + assertNoGLError(); + + // Positions + glGenBuffers(1, &QUAD.quadVBO); + assertNoGLError(); + + glBindBuffer(GL_ARRAY_BUFFER, QUAD.quadVBO); + assertNoGLError(); + + glBufferData( + GL_ARRAY_BUFFER, sizeof(quadPositions), quadPositions, GL_STATIC_DRAW + ); + assertNoGLError(); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + assertNoGLError(); + + glEnableVertexAttribArray(0); + assertNoGLError(); + + // UVs + glGenBuffers(1, &QUAD.quadUVVBO); + assertNoGLError(); + + glBindBuffer(GL_ARRAY_BUFFER, QUAD.quadUVVBO); + assertNoGLError(); + + glBufferData(GL_ARRAY_BUFFER, sizeof(quadUVs), quadUVs, GL_STATIC_DRAW); + assertNoGLError(); + + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); + assertNoGLError(); + + glEnableVertexAttribArray(1); + assertNoGLError(); + + // Indices + glGenBuffers(1, &QUAD.quadEBO); + assertNoGLError(); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, QUAD.quadEBO); + assertNoGLError(); + + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, sizeof(quadIndices), quadIndices, GL_STATIC_DRAW + ); + assertNoGLError(); +} + +void quadRender(const int32_t count) { + glBindVertexArray(QUAD.quadVAO); + assertNoGLError(); + + glDrawElementsInstanced( + GL_TRIANGLES, 6, + GL_UNSIGNED_INT, 0, + count + ); + assertNoGLError(); +} + +void quadDispose() { + glDeleteBuffers(1, &QUAD.quadVBO); + assertNoGLError(); + + glDeleteBuffers(1, &QUAD.quadEBO); + assertNoGLError(); + + glDeleteVertexArrays(1, &QUAD.quadVAO); + assertNoGLError(); +} \ No newline at end of file diff --git a/src/duskgl/display/quad.h b/src/duskgl/display/quad.h new file mode 100644 index 0000000..0ed75ab --- /dev/null +++ b/src/duskgl/display/quad.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "duskgl.h" + +typedef struct { + GLuint quadVBO; + GLuint quadUVVBO; + GLuint quadVAO; + GLuint quadEBO; +} duskquad_t; + +extern duskquad_t QUAD; + +/** + * Initializes the quad. + */ +void quadInit(); + +/** + * Renders quads. + * + * @param count The number of quads to render. + */ +void quadRender(const int32_t count); + +/** + * Disposes of the quad. + */ +void quadDispose(); \ No newline at end of file diff --git a/src/duskgl/display/render.c b/src/duskgl/display/render.c index 1fbd4d2..c77974c 100644 --- a/src/duskgl/display/render.c +++ b/src/duskgl/display/render.c @@ -7,12 +7,17 @@ #include "assert/assertgl.h" #include "render.h" -#include "render/scene.h" +#include "display/scene.h" +#include "display/shader/shadermanager.h" +#include "display/quad.h" render_t RENDER; void renderInit() { memset(&RENDER, 0, sizeof(render_t)); + + shaderManagerInit(); + quadInit(); } void renderUpdate() { @@ -21,5 +26,6 @@ void renderUpdate() { } void renderDispose() { - + quadDispose(); + shaderManagerDispose(); } \ No newline at end of file diff --git a/src/duskgl/display/shader/CMakeLists.txt b/src/duskgl/display/shader/CMakeLists.txt new file mode 100644 index 0000000..6da081e --- /dev/null +++ b/src/duskgl/display/shader/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_TARGET_NAME} + PRIVATE + shadermanager.c + shader.c + shaderbuffer.c +) + +# Subdirs +add_subdirectory(entityshader) \ No newline at end of file diff --git a/src/duskgl/display/shader/entityshader/CMakeLists.txt b/src/duskgl/display/shader/entityshader/CMakeLists.txt new file mode 100644 index 0000000..707a746 --- /dev/null +++ b/src/duskgl/display/shader/entityshader/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Shaders +glsltool(entity_vert.glsl) +glsltool(entity_frag.glsl) + +# Sources +target_sources(${DUSK_TARGET_NAME} + PRIVATE + entityshader.c +) \ No newline at end of file diff --git a/src/duskgl/display/shader/entityshader/entity.glsl b/src/duskgl/display/shader/entityshader/entity.glsl new file mode 100644 index 0000000..4b855d9 --- /dev/null +++ b/src/duskgl/display/shader/entityshader/entity.glsl @@ -0,0 +1,27 @@ +#include "../fragments/header.glsl" +#include "../fragments/transforms.glsl" + +#define ENTITY_TILE_OFFSET_X (MAP_TILE_WIDTH - ENTITY_WIDTH) / 2 +#define ENTITY_TILE_OFFSET_Y (MAP_TILE_HEIGHT - ENTITY_HEIGHT) / 2 + +struct Entity { + int x; + int y; + int tileset; + int tile; +}; + +layout(std140) uniform b_Data { + Entity entities[MAP_ENTITY_COUNT_MAX]; + int entityCount; +}; + +vec4 entityGetCoordinates(Entity ent) { + int vx = (ent.x * MAP_TILE_WIDTH) + ENTITY_TILE_OFFSET_X; + int vy = (ent.y * MAP_TILE_HEIGHT) + ENTITY_TILE_OFFSET_Y; + return vec4(vx, vy, vx + ENTITY_WIDTH, vy + ENTITY_HEIGHT); +} + +vec4 entityGetUVs(Entity ent) { + return tilesetGetUVsByIndex(ent.tileset, ent.tile); +} \ No newline at end of file diff --git a/src/duskgl/display/shader/entityshader/entity_frag.glsl b/src/duskgl/display/shader/entityshader/entity_frag.glsl new file mode 100644 index 0000000..109aa56 --- /dev/null +++ b/src/duskgl/display/shader/entityshader/entity_frag.glsl @@ -0,0 +1,18 @@ +#include "entity.glsl" + +in vec2 v_TextureCoord; +flat in int v_Entity; + +out vec4 FragColor; + +void main() { + Entity e = entities[v_Entity]; + vec4 textureColor = tilesetGetColor(e.tileset, v_TextureCoord); + + // We remove the pink color as transparent + if(textureColor.r == 1.0 && textureColor.g == 0.0 && textureColor.b == 1.0) { + discard; + } + + FragColor = textureColor; +} diff --git a/src/duskgl/display/shader/entityshader/entity_vert.glsl b/src/duskgl/display/shader/entityshader/entity_vert.glsl new file mode 100644 index 0000000..fda78dd --- /dev/null +++ b/src/duskgl/display/shader/entityshader/entity_vert.glsl @@ -0,0 +1,35 @@ +#include "entity.glsl" + +// Outputs to fragment shader +out vec2 v_TextureCoord; +flat out int v_Entity; + +void main() { + int entityIndex = gl_InstanceID; + int indiceIndex = gl_VertexID % 6; + + Entity entity = entities[entityIndex]; + + vec4 quadPos = entityGetCoordinates(entity); + vec4 quadUVs = entityGetUVs(entity); + + vec2 vertex; + vec2 uv; + if(indiceIndex == 0 || indiceIndex == 4) { + vertex = quadPos.xy; + uv = quadUVs.xy; + } else if(indiceIndex == 1) { + vertex = quadPos.zy; + uv = quadUVs.zy; + } else if(indiceIndex == 2 || indiceIndex == 5) { + vertex = quadPos.zw; + uv = quadUVs.zw; + } else if(indiceIndex == 3) { + vertex = quadPos.xw; + uv = quadUVs.xw; + } + + gl_Position = transforms.projection * transforms.view * vec4(vertex, 0.0, 1.0); + v_TextureCoord = uv; + v_Entity = entityIndex; +} \ No newline at end of file diff --git a/src/duskgl/display/shader/entityshader/entityshader.c b/src/duskgl/display/shader/entityshader/entityshader.c new file mode 100644 index 0000000..7c51b34 --- /dev/null +++ b/src/duskgl/display/shader/entityshader/entityshader.c @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "assert/assert.h" +#include "assert/assertgl.h" +#include "entityshader.h" +#include "entity_vert.glsl.h" +#include "entity_frag.glsl.h" + +entityshader_t ENTITY_SHADER; + +void entityShaderInit() { + memset(&ENTITY_SHADER, 0, sizeof(entityshader_t)); + + shaderInit( + &ENTITY_SHADER.shader, + entity_vertShaderSource, + entity_fragShaderSource + ); + + // Uniform buffers + + // Uniforms +} + +void entityShaderDispose() { + shaderDispose(&ENTITY_SHADER.shader); +} \ No newline at end of file diff --git a/src/duskgl/display/shader/entityshader/entityshader.h b/src/duskgl/display/shader/entityshader/entityshader.h new file mode 100644 index 0000000..63f72b1 --- /dev/null +++ b/src/duskgl/display/shader/entityshader/entityshader.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/shader/shader.h" + +typedef struct { + shader_t shader; + + // GLuint dataBlock; + // GLuint transformsBlock; + // GLuint tilesetsBlock; + + // GLuint uniformTilesetTextures; +} entityshader_t; + +extern entityshader_t ENTITY_SHADER; + +/** + * Initializes the entity shader. + */ +void entityShaderInit(); + +/** + * Disposes of the entity shader. + */ +void entityShaderDispose(); \ No newline at end of file diff --git a/src/duskgl/display/shader/fragments/header.glsl b/src/duskgl/display/shader/fragments/header.glsl new file mode 100644 index 0000000..c866201 --- /dev/null +++ b/src/duskgl/display/shader/fragments/header.glsl @@ -0,0 +1,6 @@ +#version 330 core + +// Copyright (c) 2025 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT \ No newline at end of file diff --git a/src/duskgl/display/shader/fragments/quad.glsl b/src/duskgl/display/shader/fragments/quad.glsl new file mode 100644 index 0000000..b64b682 --- /dev/null +++ b/src/duskgl/display/shader/fragments/quad.glsl @@ -0,0 +1,25 @@ +// Copyright (c) 2025 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +vec2 quadGetVertice(uint indiceIndex) { + vec2 vert = vec2(0, 0); + + if(indiceIndex == 0u || indiceIndex == 4u) { + // vert = vec2(0, 0); + } else if(indiceIndex == 1u) { + vert = vec2(1, 0); + } else if(indiceIndex == 2u || indiceIndex == 5u) { + vert = vec2(1, 1); + } else if(indiceIndex == 3u) { + vert = vec2(0, 1); + } + + return vert; +} + +vec2 quadGetTextureCoordinate(uint indiceIndex) { + vec2 vert = quadGetVertice(indiceIndex); + return vert; +} \ No newline at end of file diff --git a/src/duskgl/display/shader/fragments/transforms.glsl b/src/duskgl/display/shader/fragments/transforms.glsl new file mode 100644 index 0000000..8203653 --- /dev/null +++ b/src/duskgl/display/shader/fragments/transforms.glsl @@ -0,0 +1,32 @@ +// Copyright (c) 2025 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "../../../../dusk/display/displaydefs.h" + +struct Transform { + mat4 projection; + mat4 view; + vec2 resolution; +}; + +layout(std140) uniform b_Transforms { + Transform transforms; +}; + +vec2 transformDisplayGetSize() { + return vec2(DISPLAY_WIDTH, DISPLAY_HEIGHT); +} + +float transformDisplayGetAspectRatio() { + return (float(DISPLAY_WIDTH) / float(DISPLAY_HEIGHT)); +} + +vec2 transformResolutionGetSize() { + return transforms.resolution; +} + +float transformResolutionGetAspectRatio() { + return (transforms.resolution.x / transforms.resolution.y); +} \ No newline at end of file diff --git a/src/duskgl/display/shader/shader.c b/src/duskgl/display/shader/shader.c new file mode 100644 index 0000000..6a43e03 --- /dev/null +++ b/src/duskgl/display/shader/shader.c @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "shader.h" +#include "assert/assert.h" +#include "assert/assertgl.h" + +void shaderInit( + shader_t *shader, + const char *vertexSource, + const char *fragmentSource +) { + assertNotNull(shader, "shader must not be NULL"); + assertNotNull(vertexSource, "vertexSource must not be NULL"); + assertNotNull(fragmentSource, "fragmentSource must not be NULL"); + + int32_t success; + char infoLog[SHADER_LOG_LENGTH]; + + // Create vertex shader + shader->vertexShader = glCreateShader(GL_VERTEX_SHADER); + assertNoGLError(); + + glShaderSource(shader->vertexShader, 1, &vertexSource, NULL); + assertNoGLError(); + + glCompileShader(shader->vertexShader); + assertNoGLError(); + + glGetShaderiv(shader->vertexShader, GL_COMPILE_STATUS, &success); + assertNoGLError(); + + if(!success) { + glGetShaderInfoLog( + shader->vertexShader, SHADER_LOG_LENGTH, NULL, infoLog + ); + assertNoGLError(); + assertUnreachable(infoLog); + return; + } + + // Create fragment shader + shader->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + assertNoGLError(); + + glShaderSource(shader->fragmentShader, 1, &fragmentSource, NULL); + assertNoGLError(); + + glCompileShader(shader->fragmentShader); + assertNoGLError(); + + glGetShaderiv(shader->fragmentShader, GL_COMPILE_STATUS, &success); + assertNoGLError(); + + if(!success) { + glGetShaderInfoLog( + shader->fragmentShader, SHADER_LOG_LENGTH, NULL, infoLog + ); + assertNoGLError(); + assertUnreachable(infoLog); + return; + } + + // Create shader program + shader->shaderProgram = glCreateProgram(); + assertNoGLError(); + + glAttachShader(shader->shaderProgram, shader->vertexShader); + assertNoGLError(); + + glAttachShader(shader->shaderProgram, shader->fragmentShader); + assertNoGLError(); + + glLinkProgram(shader->shaderProgram); + assertNoGLError(); + + glGetProgramiv(shader->shaderProgram, GL_LINK_STATUS, &success); + assertNoGLError(); + + if(!success) { + glGetProgramInfoLog(shader->shaderProgram, SHADER_LOG_LENGTH, NULL, infoLog); + assertNoGLError(); + assertUnreachable(infoLog); + return; + } +} + +void shaderUse(const shader_t *shader) { + assertNotNull(shader, "shader must not be NULL"); + + glUseProgram(shader->shaderProgram); + assertNoGLError(); +} + +GLuint shaderGetUniform(const shader_t *shader, const char_t *name) { + assertNotNull(shader, "shader must not be NULL"); + assertNotNull(name, "name must not be NULL"); + + GLuint uniform = glGetUniformLocation(shader->shaderProgram, name); + assertNoGLError(); + + return uniform; +} + +GLuint shaderGetBlock(const shader_t *shader, const char *name) { + assertNotNull(shader, "shader must not be NULL"); + assertNotNull(name, "name must not be NULL"); + + GLuint blockIndex = glGetUniformBlockIndex(shader->shaderProgram, name); + assertNoGLError(); + + // TODO: I really don't think this should be here at all. + glUniformBlockBinding(shader->shaderProgram, blockIndex, blockIndex); + assertNoGLError(); + + return blockIndex; +} + +void shaderDispose(shader_t *shader) { + assertNotNull(shader, "shader must not be NULL"); + + glDeleteProgram(shader->shaderProgram); + assertNoGLError(); + + glDeleteShader(shader->vertexShader); + assertNoGLError(); + + glDeleteShader(shader->fragmentShader); + assertNoGLError(); +} \ No newline at end of file diff --git a/src/duskgl/display/shader/shader.h b/src/duskgl/display/shader/shader.h new file mode 100644 index 0000000..1bc6395 --- /dev/null +++ b/src/duskgl/display/shader/shader.h @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "duskgl.h" + +#define SHADER_LOG_LENGTH 512 + +typedef struct { + GLuint shaderProgram; + GLuint vertexShader; + GLuint fragmentShader; +} shader_t; + +/** + * Initializes a shader. + * + * @param shader The shader to initialize. + * @param vertexSource The vertex shader source. + * @param fragmentSource The fragment shader source. + */ +void shaderInit( + shader_t *shader, + const char_t *vertexSource, + const char_t *fragmentSource +); + +/** + * Uses a shader. + * + * @param shader The shader to use. + */ +void shaderUse(const shader_t *shader); + +/** + * Gets a uniform from a shader. + * + * @param shader The shader to get the uniform from. + * @param name The name of the uniform. + * @return The uniform. + */ +GLuint shaderGetUniform(const shader_t *shader, const char_t *name); + +/** + * Gets a block id from a shader. + * + * @param shader The shader to get the block from. + * @param name The name of the block. + * @return The block index/identifier. + */ +GLuint shaderGetBlock(const shader_t *shader, const char_t *name); + +/** + * Disposes of a shader. + * + * @param shader The shader to dispose of. + */ +void shaderDispose(shader_t *shader); \ No newline at end of file diff --git a/src/duskgl/display/shader/shaderbuffer.c b/src/duskgl/display/shader/shaderbuffer.c new file mode 100644 index 0000000..83ad611 --- /dev/null +++ b/src/duskgl/display/shader/shaderbuffer.c @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "assert/assert.h" +#include "shaderbuffer.h" +#include "assert/assertgl.h" + +void shaderBufferInit(shaderbuffer_t *shaderBuffer, const size_t size) { + assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL."); + assertTrue(size > 0, "size must be greater than 0."); + + shaderBuffer->size = size; + + glGenBuffers(1, &shaderBuffer->id); + assertNoGLError(); + + shaderBufferBind(shaderBuffer); + + glBufferData(GL_UNIFORM_BUFFER, size, NULL, GL_STATIC_DRAW); +} + +void shaderBufferBind(shaderbuffer_t *shaderBuffer) { + assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL."); + + glBindBuffer(GL_UNIFORM_BUFFER, shaderBuffer->id); + assertNoGLError(); +} + +void shaderBufferSetData(shaderbuffer_t *shaderBuffer, const void *data) { + assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL."); + assertNotNull(data, "data cannot be NULL."); + + glBufferData(GL_UNIFORM_BUFFER, shaderBuffer->size, data, GL_STATIC_DRAW); + assertNoGLError(); +} + +void shaderBufferBindToBlock( + shaderbuffer_t *shaderBuffer, + const GLuint blockIndex +) { + assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL."); + + glBindBufferBase(GL_UNIFORM_BUFFER, blockIndex, shaderBuffer->id); + assertNoGLError(); +} + +void shaderBufferDispose(shaderbuffer_t *shaderBuffer) { + assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL."); + glDeleteBuffers(1, &shaderBuffer->id); + assertNoGLError(); +} \ No newline at end of file diff --git a/src/duskgl/display/shader/shaderbuffer.h b/src/duskgl/display/shader/shaderbuffer.h new file mode 100644 index 0000000..a58fd44 --- /dev/null +++ b/src/duskgl/display/shader/shaderbuffer.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "duskgl.h" + +typedef struct { + GLuint id; + size_t size; +} shaderbuffer_t; + +/** + * Initializes a shader buffer. + * + * @param shaderBuffer The shader buffer to initialize. + * @param size The size of the buffer. + */ +void shaderBufferInit( + shaderbuffer_t *shaderBuffer, + const size_t size +); + +/** + * Binds a shader buffer. + * + * @param shaderBuffer The shader buffer to bind. + */ +void shaderBufferBind(shaderbuffer_t *shaderBuffer); + +/** + * Sets the data of a shader buffer. + * + * @param shaderBuffer The shader buffer to set the data of. + * @param data The data to set. + */ +void shaderBufferSetData( + shaderbuffer_t *shaderBuffer, + const void *data +); + +/** + * Binds a shader buffer to a block. + * + * @param shaderBuffer The shader buffer to bind. + * @param blockIndex The block index to bind to. + */ +void shaderBufferBindToBlock( + shaderbuffer_t *shaderBuffer, + const GLuint blockIndex +); + +/** + * Disposes of a shader buffer. + * + * @param shaderBuffer The shader buffer to dispose of. + */ +void shaderBufferDispose(shaderbuffer_t *shaderBuffer); \ No newline at end of file diff --git a/src/duskgl/display/shader/shadermanager.c b/src/duskgl/display/shader/shadermanager.c new file mode 100644 index 0000000..7ca650e --- /dev/null +++ b/src/duskgl/display/shader/shadermanager.c @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "shadermanager.h" + +void shaderManagerInit() { + +} + +void shaderManagerDispose() { + +} \ No newline at end of file diff --git a/src/duskgl/display/shader/shadermanager.h b/src/duskgl/display/shader/shadermanager.h new file mode 100644 index 0000000..152f08b --- /dev/null +++ b/src/duskgl/display/shader/shadermanager.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "duskgl.h" + +// typedef struct { + +// } shadermanager_t; + +/** + * Initializes the Shader Manager + */ +void shaderManagerInit(); + +/** + * Disposes of the Shader Manager + */ +void shaderManagerDispose(); \ No newline at end of file diff --git a/src/duskgl/display/texture.c b/src/duskgl/display/texture.c new file mode 100644 index 0000000..bc09e8d --- /dev/null +++ b/src/duskgl/display/texture.c @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2023 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "texture.h" +#include "assert/assert.h" +#include "assert/assertgl.h" +#include "asset.h" +#include "util/math.h" + +#define TEXTURE_BUFFER_SIZE 32768 + +int32_t TEXTURE_ACTIVE_COUNT; + +void textureLoad( + texture_t *texture, + const char_t *path +) { + assertNotNull(texture, "Texture is null."); + assertNotNull(path, "Path is null."); + + // Open asset + assetOpen(path); + + // Setup spng + spng_ctx *ctx = spng_ctx_new(0); + spng_set_png_stream(ctx, &textureSPNGBuffer, NULL); + + // Get image info + struct spng_ihdr ihdr; + spng_get_ihdr(ctx, &ihdr); + texture->width = ihdr.width; + texture->height = ihdr.height; + + // Decode the image. I can probably stream this in the future. + size_t dataSize = ihdr.width * ihdr.height * 4;// 4 for RGBA + uint8_t *data = (uint8_t *)malloc(dataSize); + assertNotNull(data, "Failed to allocate memory for texture data."); + spng_decode_image(ctx, data, dataSize, SPNG_FMT_RGBA8, 0); + + // Finish decoding + spng_ctx_free(ctx); + assetClose(); + + // Create texture + glGenTextures(1, &texture->id); + assertNoGLError(); + + glBindTexture(GL_TEXTURE_2D, texture->id); + assertNoGLError(); + + // Buffer then cleanup + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, + texture->width, texture->height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, data + ); + assertNoGLError(); + free(data); + + // Setup texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + assertNoGLError(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + assertNoGLError(); + + glBindTexture(GL_TEXTURE_2D, 0); + assertNoGLError(); +} + +void textureBind( + const texture_t *texture, + const GLuint slot +) { + assertNotNull(texture, "Texture is null."); + + glActiveTexture(GL_TEXTURE0 + slot); + assertNoGLError(); + + glBindTexture(GL_TEXTURE_2D, texture->id); + assertNoGLError(); +} + +void textureDispose(texture_t *texture) { + glDeleteTextures(1, &texture->id); + assertNoGLError(); +} + +int32_t textureSPNGBuffer( + spng_ctx *ctx, + void *user, + void *destination, + size_t length +) { + size_t read = assetRead(destination, length); + if(read == 0) return SPNG_IO_EOF; + return SPNG_OK; +} \ No newline at end of file diff --git a/src/duskgl/display/texture.h b/src/duskgl/display/texture.h new file mode 100644 index 0000000..04fcce2 --- /dev/null +++ b/src/duskgl/display/texture.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2023 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "duskgl.h" +#include + +extern int32_t TEXTURE_ACTIVE_COUNT; + +typedef struct { + uint32_t width; + uint32_t height; + GLuint id; +} texture_t; + +/** + * Initializes a texture to be read to be used. + * + * @param texture Texture to initialize. + * @param path Path of the file to load. + */ +void textureLoad( + texture_t *texture, + const char_t *path +); + +/** + * Binds the texture to the given slot (for use by the shaders). + * + * @param texture Texture to bind. + * @param slot Slot to bind to. + */ +void textureBind( + const texture_t *texture, + const GLuint slot +); + +/** + * Unloads a previously initialized texture. + * + * @param texture Texture to destroy. + */ +void textureDispose(texture_t *texture); + +/** + * Callback function for libspng to read from the asset. + * + * @param ctx The spng context. + * @param user User data. + * @param destination Destination buffer. + * @param length Length of the buffer. + * @return The amount of data read. + */ +int32_t textureSPNGBuffer( + spng_ctx *ctx, + void *user, + void *destination, + size_t length +); \ No newline at end of file diff --git a/src/duskglfw/display/window.h b/src/duskglfw/display/window.h index 7a42b6a..1c4ba96 100644 --- a/src/duskglfw/display/window.h +++ b/src/duskglfw/display/window.h @@ -7,9 +7,10 @@ #pragma once #include "duskglfw.h" +#include "display/displaydefs.h" -#define WINDOW_WIDTH_DEFAULT 800 -#define WINDOW_HEIGHT_DEFAULT 600 +#define WINDOW_WIDTH_DEFAULT SCREEN_WIDTH*4 +#define WINDOW_HEIGHT_DEFAULT SCREEN_HEIGHT*4 extern GLFWwindow* window; extern int32_t WINDOW_WIDTH;