diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 4c6b015a..dc0051b9 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -13,3 +13,11 @@ add_subdirectory(glad) # GLFW FetchContent_Declare(glfw URL https://github.com/glfw/glfw/releases/download/3.4/glfw-3.4.zip) FetchContent_MakeAvailable(glfw) + +# GLM +FetchContent_Declare( + cglm + GIT_REPOSITORY https://github.com/recp/cglm + GIT_TAG 1796cc5ce298235b615dc7a4750b8c3ba56a05dd +) +FetchContent_MakeAvailable(cglm) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4562a3c0..b1b92ae9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,7 @@ if(DAWN_TARGET STREQUAL "linux-x64-terminal") add_subdirectory(dawntermlinux) elseif(DAWN_TARGET STREQUAL "linux-x64-glfw") add_subdirectory(dawnlinux) + add_subdirectory(dawnterm) add_subdirectory(dawnglfw) add_subdirectory(dawnopengl) else() diff --git a/src/dawn/assert/assert.h b/src/dawn/assert/assert.h index 80efdcda..9bc93c42 100644 --- a/src/dawn/assert/assert.h +++ b/src/dawn/assert/assert.h @@ -97,6 +97,19 @@ void assertTrueImplement( (value & flag) == flag, __VA_ARGS__ \ ) +/** + * Asserts that the given string is not null, and has a length that is less + * the maximum buffer size, including the NULL terminator. + * + * @param str String to check. + * @param bufferSize Maximum buffer size. + * @param message Message (sprintf format) to send to the logger. + * @param args Optional TParam args for the sprintf message to accept. + */ +#define assertStringValid(str, bufferSize, ...) assertTrue( \ + ((str != NULL) && ((strlen(str)+1) < bufferSize)), __VA_ARGS__ \ +) + /** * Asserts that the current code is deprecated and should not be used anymore. * @deprecated diff --git a/src/dawn/dawn.h b/src/dawn/dawn.h index e9515757..90e3fbf5 100644 --- a/src/dawn/dawn.h +++ b/src/dawn/dawn.h @@ -18,4 +18,5 @@ typedef bool bool_t; typedef char char_t; -typedef unsigned char uchar_t; \ No newline at end of file +typedef unsigned char uchar_t; +typedef uint_fast32_t flag_t; \ No newline at end of file diff --git a/src/dawn/util/math.h b/src/dawn/util/math.h new file mode 100644 index 00000000..99b6ffb4 --- /dev/null +++ b/src/dawn/util/math.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2023 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dawn.h" + +/** + * Returns the minimum of two values. + * + * @param a First value. + * @param b Second value. + * @return The minimum of the two values. + */ +#define mathMin(a, b) ((a) < (b) ? (a) : (b)) + +/** + * Returns the maximum of two values. + * + * @param a First value. + * @param b Second value. + * @return The maximum of the two values. + */ +#define mathMax(a, b) ((a) > (b) ? (a) : (b)) + +/** + * Clamps a value between two values. + * + * @param x Value to clamp. + * @param a Minimum value. + * @param b Maximum value. + * @return The clamped value. + */ +#define mathClamp(x, a, b) mathMin(mathMax(x, a), b) \ No newline at end of file diff --git a/src/dawnglfw/CMakeLists.txt b/src/dawnglfw/CMakeLists.txt index 8d8ab960..01f63f31 100644 --- a/src/dawnglfw/CMakeLists.txt +++ b/src/dawnglfw/CMakeLists.txt @@ -7,6 +7,7 @@ target_link_libraries(${DAWN_TARGET_NAME} PUBLIC glfw + glad ) # Includes diff --git a/src/dawnglfw/dawnglfw.c b/src/dawnglfw/dawnglfw.c index 75142b9c..861c84d3 100644 --- a/src/dawnglfw/dawnglfw.c +++ b/src/dawnglfw/dawnglfw.c @@ -69,6 +69,9 @@ int32_t dawnGlfwStart() { ((float_t)fbWidth) / ((float_t)windowWidth) ); + // Init game + gameInit(); + // Set the input binds dawnGlfwInputBind(INPUT_BIND_UP, GLFW_KEY_W); dawnGlfwInputBind(INPUT_BIND_UP, GLFW_KEY_UP); @@ -85,7 +88,6 @@ int32_t dawnGlfwStart() { dawnGlfwInputBind(INPUT_BIND_CANCEL, GLFW_KEY_Q); dawnGlfwInputBind(INPUT_BIND_CANCEL, GLFW_KEY_BACKSPACE); - // Event callbacks glfwSetFramebufferSizeCallback(DAWN_GLFW.window, &dawnGlfwOnResize); diff --git a/src/dawnglfw/dawnopenglimpl.h b/src/dawnglfw/dawnopenglimpl.h new file mode 100644 index 00000000..a0beca4d --- /dev/null +++ b/src/dawnglfw/dawnopenglimpl.h @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2024 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dawn.h" +#include \ No newline at end of file diff --git a/src/dawnglfw/linuxhost.c b/src/dawnglfw/linuxhost.c index 76ecc7f9..514b8458 100644 --- a/src/dawnglfw/linuxhost.c +++ b/src/dawnglfw/linuxhost.c @@ -8,6 +8,6 @@ #include "linuxhost.h" #include "dawnglfw.h" -int32_t linuxHostBeginUpdating() { +int32_t linuxHostHandoff() { return dawnGlfwStart(); } \ No newline at end of file diff --git a/src/dawnglfw/linuxhost.h b/src/dawnglfw/linuxhost.h index 525e75fa..770c09b2 100644 --- a/src/dawnglfw/linuxhost.h +++ b/src/dawnglfw/linuxhost.h @@ -11,4 +11,4 @@ /** * Linux Host Compatibility */ -int32_t linuxHostBeginUpdating(); \ No newline at end of file +int32_t linuxHostHandoff(); \ No newline at end of file diff --git a/src/dawnlinux/main.c b/src/dawnlinux/main.c index b1f2e000..db56e286 100644 --- a/src/dawnlinux/main.c +++ b/src/dawnlinux/main.c @@ -6,12 +6,8 @@ */ #include "main.h" -#include "rpg/world/map.h" -#include "game.h" #include "linuxhost.h" - int32_t main() { - gameInit(); - return linuxHostBeginUpdating(); + return linuxHostHandoff(); } \ No newline at end of file diff --git a/src/dawnopengl/CMakeLists.txt b/src/dawnopengl/CMakeLists.txt index e7f57ddd..e12eabbe 100644 --- a/src/dawnopengl/CMakeLists.txt +++ b/src/dawnopengl/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(${DAWN_TARGET_NAME} PUBLIC font8x8 - glad + cglm ) # Includes diff --git a/src/dawnopengl/dawnopengl.h b/src/dawnopengl/dawnopengl.h index a0beca4d..71aed5d2 100644 --- a/src/dawnopengl/dawnopengl.h +++ b/src/dawnopengl/dawnopengl.h @@ -6,5 +6,10 @@ */ #pragma once -#include "dawn.h" -#include \ No newline at end of file +#include "dawnopenglimpl.h" +#include + +typedef vec2 vec2_t; +typedef vec3 vec3_t; +typedef vec4 vec4_t; +typedef mat4 mat4_t; \ No newline at end of file diff --git a/src/dawnopengl/display/CMakeLists.txt b/src/dawnopengl/display/CMakeLists.txt index daf2246c..af930708 100644 --- a/src/dawnopengl/display/CMakeLists.txt +++ b/src/dawnopengl/display/CMakeLists.txt @@ -4,10 +4,16 @@ # https://opensource.org/licenses/MIT # Subdirs +add_subdirectory(shaders) # Sources target_sources(${DAWN_TARGET_NAME} PRIVATE display.c backbuffer.c + shader.c + mesh.c + font.c + texture.c + textureframebuffer.c ) \ No newline at end of file diff --git a/src/dawnopengl/display/colorgl.h b/src/dawnopengl/display/colorgl.h new file mode 100644 index 00000000..820a2579 --- /dev/null +++ b/src/dawnopengl/display/colorgl.h @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2024 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dawn.h" + +typedef float_t color3f_t[3]; +typedef float_t color4f_t[4]; + +typedef color4f_t color_t; + +#define COLOR3F(r,g,b) ((color3f_t){ r, g, b }) +#define COLOR3F_RED COLOR3F(1, 0, 0) +#define COLOR3F_GREEN COLOR3F(0, 1, 0) +#define COLOR3F_BLUE COLOR3F(0, 0, 1) +#define COLOR3F_BLACK COLOR3F(0, 0, 0) +#define COLOR3F_WHITE COLOR3F(1, 1, 1) +#define COLOR3F_MAGENTA COLOR3F(1, 0, 1) +#define COLOR3F_YELLOW COLOR3F(1, 1, 0) +#define COLOR3F_CYAN COLOR3F(0, 1, 1) +#define COLOR3F_GRAY COLOR3F(0.5f, 0.5f, 0.5f) +#define COLOR3F_DARKGRAY COLOR3F(0.25f, 0.25f, 0.25f) +#define COLOR3F_LIGHTGRAY COLOR3F(0.75f, 0.75f, 0.75f) +#define COLOR3F_ORANGE COLOR3F(1, 0.5f, 0) +#define COLOR3F_PURPLE COLOR3F(0.5f, 0, 1) +#define COLOR3F_PINK COLOR3F(1, 0, 0.5f) +#define COLOR3F_BROWN COLOR3F(0.5f, 0.25f, 0) +#define COLOR3F_GOLD COLOR3F(1, 0.75f, 0) +#define COLOR3F_SILVER COLOR3F(0.75f, 0.75f, 0.75f) +#define COLOR3F_BRONZE COLOR3F(0.75f, 0.5f, 0.25f) +#define COLOR3F_CORNFLOWERBLUE COLOR3F(0.4f, 0.6f, 0.9f) + +#define COLOR4F(r,g,b,a) ((color4f_t){ r, g, b, a }) +#define COLOR4F_RED COLOR4F(1, 0, 0, 1) +#define COLOR4F_GREEN COLOR4F(0, 1, 0, 1) +#define COLOR4F_BLUE COLOR4F(0, 0, 1, 1) +#define COLOR4F_BLACK COLOR4F(0, 0, 0, 1) +#define COLOR4F_WHITE COLOR4F(1, 1, 1, 1) +#define COLOR4F_MAGENTA COLOR4F(1, 0, 1, 1) +#define COLOR4F_YELLOW COLOR4F(1, 1, 0, 1) +#define COLOR4F_CYAN COLOR4F(0, 1, 1, 1) +#define COLOR4F_GRAY COLOR4F(0.5f, 0.5f, 0.5f, 1) +#define COLOR4F_DARKGRAY COLOR4F(0.25f, 0.25f, 0.25f, 1) +#define COLOR4F_LIGHTGRAY COLOR4F(0.75f, 0.75f, 0.75f, 1) +#define COLOR4F_ORANGE COLOR4F(1, 0.5f, 0, 1) +#define COLOR4F_PURPLE COLOR4F(0.5f, 0, 1, 1) +#define COLOR4F_PINK COLOR4F(1, 0, 0.5f, 1) +#define COLOR4F_BROWN COLOR4F(0.5f, 0.25f, 0, 1) +#define COLOR4F_GOLD COLOR4F(1, 0.75f, 0, 1) +#define COLOR4F_SILVER COLOR4F(0.75f, 0.75f, 0.75f, 1) +#define COLOR4F_BRONZE COLOR4F(0.75f, 0.5f, 0.25f, 1) +#define COLOR4F_CORNFLOWERBLUE COLOR4F(0.4f, 0.6f, 0.9f, 1) +#define COLOR4F_TRANSPARENT_BLACK COLOR4F(0, 0, 0, 0) +#define COLOR4F_TRANSPARENT_WHITE COLOR4F(1, 1, 1, 0) +#define COLOR4F_TRANSPARENT COLOR4F_TRANSPARENT_BLACK \ No newline at end of file diff --git a/src/dawnopengl/display/display.c b/src/dawnopengl/display/display.c index 4e1b59a8..fe27c51e 100644 --- a/src/dawnopengl/display/display.c +++ b/src/dawnopengl/display/display.c @@ -7,12 +7,30 @@ #include "display/display.h" #include "assert/assert.h" +#include "display/shaders/simpletexturedshader.h" +#include "display/backbuffer.h" +#include "display/font.h" +#include "display/mesh.h" +#include "display/frame.h" + + void displayInit() { + simpleTexturedShaderInit(); + fontInit(); + frameInit(); } void displayUpdate() { + frameUpdate(); + + backBufferBind(); + backBufferClear(); + + simpleTexturedShaderBind(); } void displayDispose() { + simpleTexturedShaderDispose(); + fontDispose(); } \ No newline at end of file diff --git a/src/dawnopengl/display/font.c b/src/dawnopengl/display/font.c new file mode 100644 index 00000000..3e398143 --- /dev/null +++ b/src/dawnopengl/display/font.c @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2024 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "font.h" +#include + +font_t FONT; + +void fontInit() { + memset(&FONT, 0, sizeof(FONT)); + + // font is uint8_t[8] * count of chars + size_t charCount = sizeof(font8x8_basic) / 8; + + int32_t totalPixels = charCount * 8 * 8; + textureInit( + &FONT.texture, + totalPixels / 8, 8, + TEXTURE_FORMAT_R, + TEXTURE_DATA_FORMAT_UNSIGNED_BYTE + ); +} + +void fontDispose() { + textureDispose(&FONT.texture); +} \ No newline at end of file diff --git a/src/dawnopengl/display/font.h b/src/dawnopengl/display/font.h new file mode 100644 index 00000000..9bb815b8 --- /dev/null +++ b/src/dawnopengl/display/font.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2024 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "texture.h" + +typedef struct { + texture_t texture; +} font_t; + +extern font_t FONT; + +/** + * Initializes the render font. + */ +void fontInit(); + +/** + * Disposes of the render font. + */ +void fontDispose(); \ No newline at end of file diff --git a/src/dawnopengl/display/mesh.c b/src/dawnopengl/display/mesh.c new file mode 100644 index 00000000..344c9e13 --- /dev/null +++ b/src/dawnopengl/display/mesh.c @@ -0,0 +1,309 @@ +/** + * Copyright (c) 2023 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "mesh.h" +#include "assert/assert.h" +#include "assert/assertgl.h" + +int32_t MESH_ACTIVE_COUNT = 0; + +void meshInit(mesh_t *mesh) { + assertNotNull(mesh, "meshInit: Mesh cannot be null."); + + mesh->vertexBuffer = -1; + mesh->indexBuffer = -1; + mesh->vertexArray = -1; + mesh->verticeCount = -1; + mesh->indiceCount = -1; + + MESH_ACTIVE_COUNT++; +} + +void meshCreateBuffers( + mesh_t *mesh, + const int32_t verticeCount, + const int32_t indiceCount +) { + assertNotNull(mesh, "meshCreateBuffers: Mesh cannot be null."); + assertTrue( + verticeCount > 0, + "meshCreateBuffers: Vertice count must be greater than 0." + ); + assertTrue( + indiceCount > 0, + "meshCreateBuffers: Indice count must be greater than 0." + ); + + // Dispose old buffers (if present). + meshDestroyBuffers(mesh); + + mesh->verticeCount = verticeCount; + mesh->indiceCount = indiceCount; + size_t sizePos = sizeof(vec3_t) * verticeCount; + size_t sizeInds = sizeof(meshindice_t) * indiceCount; + size_t sizeCoords = sizeof(vec2_t) * verticeCount; + + // Generate vertex array, I don't think I need to do this tbh. + glGenVertexArrays(1, &mesh->vertexArray); + assertNoGLError(); + glBindVertexArray(mesh->vertexArray); + assertNoGLError(); + + // Create some buffers, one for the vertex data, one for the indices + GLuint buffer[2]; + glGenBuffers(2, buffer); + assertNoGLError(); + mesh->vertexBuffer = buffer[0]; + assertTrue( + mesh->vertexBuffer >= 0, + "meshCreateBuffers: Failed to create vertex buffer" + ); + mesh->indexBuffer = buffer[1]; + assertTrue( + mesh->indexBuffer >= 0, + "meshCreateBuffers: Failed to create index buffer" + ); + + // Buffer an empty set of data then buffer each component + glBindBuffer(GL_ARRAY_BUFFER, mesh->vertexBuffer); + assertNoGLError(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->indexBuffer); + assertNoGLError(); + + glBufferData(GL_ARRAY_BUFFER, sizePos+sizeCoords, 0, GL_DYNAMIC_DRAW); + assertNoGLError(); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeInds, 0, GL_DYNAMIC_DRAW); + assertNoGLError(); + + // Setup the attrib pointers + size_t offset = 0; + glVertexAttribPointer( + 0, sizeof(vec3_t) / sizeof(float_t), + GL_FLOAT, GL_FALSE, + 0, (void *)offset + ); + assertNoGLError(); + glEnableVertexAttribArray(0); + assertNoGLError(); + + offset += sizePos; + glVertexAttribPointer( + 1, sizeof(vec2_t) / sizeof(float_t), + GL_FLOAT, GL_FALSE, + 0, (void *)offset + ); + assertNoGLError(); + glEnableVertexAttribArray(1); + assertNoGLError(); +} + +void meshDestroyBuffers(mesh_t *mesh) { + assertNotNull(mesh, "meshDestroyBuffers: Mesh cannot be null."); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + assertNoGLError(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + assertNoGLError(); + + if(mesh->vertexBuffer != -1) { + glDeleteBuffers(1, &mesh->vertexBuffer); + assertNoGLError(); + mesh->vertexBuffer = -1; + mesh->verticeCount = -1; + } + + if(mesh->indexBuffer != -1) { + glDeleteBuffers(1, &mesh->indexBuffer); + assertNoGLError(); + mesh->indexBuffer = -1; + mesh->indiceCount = -1; + } + + if(mesh->vertexArray != -1) { + glDeleteVertexArrays(1, &mesh->vertexArray); + assertNoGLError(); + mesh->vertexArray = -1; + } +} + +void meshBufferPositions( + const mesh_t *mesh, + const vec3_t *positions, + const size_t position, + const size_t count +) { + assertNotNull(mesh, "meshBufferPositions: Mesh cannot be null"); + assertNotNull( + positions, + "meshBufferPositions: Positions cannot be null" + ); + assertTrue( + position >= 0 && position < mesh->verticeCount, + "meshBufferPositions: Position must be within range" + ); + assertTrue( + position+count <= mesh->verticeCount, + "meshBufferPositions: Position + count must be within range" + ); + assertTrue( + count > 0, + "meshBufferPositions: Count must be greater than zero" + ); + + size_t offsetPositions = sizeof(vec3_t) * position; + + glBindBuffer(GL_ARRAY_BUFFER, mesh->vertexBuffer); + assertNoGLError(); + glBufferSubData( + GL_ARRAY_BUFFER, + offsetPositions, + sizeof(vec3_t) * count, + (void*)positions + ); + assertNoGLError(); +} + +void meshBufferCoordinates( + const mesh_t *mesh, + const vec2_t *coordinates, + const size_t position, + const size_t count +) { + assertNotNull(mesh, "meshBufferCoordinates: Mesh cannot be null"); + assertNotNull( + coordinates, + "meshBufferCoordinates: Coordinates cannot be null" + ); + assertTrue( + position >= 0 && position < mesh->verticeCount, + "meshBufferCoordinates: Position must be within range" + ); + assertTrue( + position+count <= mesh->verticeCount, + "meshBufferCoordinates: Position + count must be within range" + ); + assertTrue( + count > 0, + "meshBufferCoordinates: Count must be greater than zero" + ); + + size_t offsetCoordinates = ( + (sizeof(vec3_t) * mesh->verticeCount) + + (sizeof(vec2_t) * position) + ); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->vertexBuffer); + assertNoGLError(); + glBufferSubData( + GL_ARRAY_BUFFER, + offsetCoordinates, + sizeof(vec2_t) * count, + (void*)coordinates + ); + assertNoGLError(); +} + +void meshBufferIndices( + const mesh_t *mesh, + const meshindice_t *indices, + const size_t position, + const size_t count +) { + assertNotNull( + mesh, + "meshBufferIndices: Mesh cannot be null" + ); + assertNotNull( + indices, + "meshBufferIndices: Indices cannot be null" + ); + assertTrue( + position >= 0 && position < mesh->indiceCount, + "meshBufferIndices: Position must be within range" + ); + assertTrue( + position+count <= mesh->indiceCount, + "meshBufferIndices: Position + count must be within range" + ); + assertTrue( + count > 0, + "meshBufferIndices: Count must be greater than zero" + ); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->indexBuffer); + assertNoGLError(); + glBufferSubData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(meshindice_t) * position, + sizeof(meshindice_t) * count, + (void*)indices + ); + assertNoGLError(); +} + +void meshDraw( + const mesh_t *mesh, + const meshdrawmode_t mode, + const int32_t indiceStart, + const int32_t indiceCount +) { + assertNotNull(mesh, "meshDraw: Mesh cannot be null"); + + // Don't draw empty meshes. + if(indiceCount == 0 || mesh->vertexBuffer == -1 || mesh->indexBuffer == -1) { + return; + } + + assertTrue( + indiceCount > 0 || indiceCount == -1, + "meshDraw: Invalid indiceCount" + ); + + // Re-Bind the buffers + assertNoGLError(); + glBindVertexArray(mesh->vertexArray); + assertNoGLError(); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vertexBuffer); + assertNoGLError(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->indexBuffer); + assertNoGLError(); + + // Re-Calculate the attrib pointers. Will need to revist this later. + size_t offset = 0; + glVertexAttribPointer( + 0, sizeof(vec3_t) / sizeof(float_t), + GL_FLOAT, GL_FALSE, + 0, (void*)offset + ); + assertNoGLError(); + glEnableVertexAttribArray(0); + assertNoGLError(); + + offset += sizeof(vec3_t) * mesh->verticeCount; + glVertexAttribPointer( + 1, sizeof(vec2_t) / sizeof(float_t), + GL_FLOAT, GL_FALSE, + 0, (void*)offset + ); + assertNoGLError(); + glEnableVertexAttribArray(1); + assertNoGLError(); + + // Render the elements. + glDrawElements( + mode, + indiceCount == -1 ? (mesh->indiceCount - indiceStart) : indiceCount, + GL_UNSIGNED_INT, + (void*)(sizeof(meshindice_t) * indiceStart) + ); + assertNoGLError(); +} + +void meshDispose(mesh_t *mesh) { + meshDestroyBuffers(mesh); + MESH_ACTIVE_COUNT--; +} \ No newline at end of file diff --git a/src/dawnopengl/display/mesh.h b/src/dawnopengl/display/mesh.h new file mode 100644 index 00000000..5091075b --- /dev/null +++ b/src/dawnopengl/display/mesh.h @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2023 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dawn.h" +#include "dawnopengl.h" + +extern int32_t MESH_ACTIVE_COUNT; + +/** Indice that references a specific vertice */ +typedef int32_t meshindice_t; + +typedef enum { + /** Draw the mesh as a list of points */ + MESH_DRAW_MODE_POINTS = GL_POINTS, + /** Draw the mesh as a list of lines */ + MESH_DRAW_MODE_LINES = GL_LINES, + /** Draw the mesh as a list of triangles */ + MESH_DRAW_MODE_TRIANGLES = GL_TRIANGLES +} meshdrawmode_t; + +typedef struct { + /** Pointer to the vertex buffer on the GPU */ + GLuint vertexBuffer; + /** Pointer to the index buffer on the GPU */ + GLuint indexBuffer; + /** Pointer to the vertex buffer on the GPU */ + GLuint vertexArray; + + /** How many vertices are in the mesh */ + int32_t verticeCount; + /** How many indices are in the mesh */ + int32_t indiceCount; +} mesh_t; + +/** + * Initializes a mesh to a default state. Mesh's must be initialized and + * destroyed. + * + * @param mesh Mesh to initialize. + */ +void meshInit(mesh_t *mesh); + +/** + * Create a new set of buffers for the mesh to use. + * + * @param mesh Mesh to create buffers for. + * @param verticeCount How many Vertices will this buffer support. + * @param indiceCount How many Indices will this buffer support. + */ +void meshCreateBuffers( + mesh_t *mesh, + const int32_t verticeCount, + const int32_t indiceCount +); + +/** + * Cleanup the buffers on a given mesh. This is useful if you intend to expand + * the count of vertices your mesh supports. + * + * @param mesh Mesh to destroy buffers for. + */ +void meshDestroyBuffers(mesh_t *mesh); + +/** + * Write vertice coordinates to the mesh. + * + * @param mesh Mesh to write to. + * @param positions Array of coordinates to write. + * @param position Position, within the buffer, to write to. + * @param count How many coordinates are in the array. + */ +void meshBufferPositions( + const mesh_t *mesh, + const vec3_t *positions, + const size_t position, + const size_t count +); + +/** + * Write texture coordinates to the mesh. + * + * @param mesh Mesh to write to. + * @param coordinates Array of coordinates to write. + * @param position Position, within the buffer, to write to. + * @param count How many coordinates are in the array. + */ +void meshBufferCoordinates( + const mesh_t *mesh, + const vec2_t *coordinates, + const size_t position, + const size_t count +); + +/** + * Write indices to the mesh. + * + * @param mesh Mesh to write to. + * @param indices Array of indices to write. + * @param position Position, within the buffer, to write to. + * @param count How many indices are in the array. + */ +void meshBufferIndices( + const mesh_t *mesh, + const meshindice_t *indices, + const size_t position, + const size_t count +); + +/** + * Draw a mesh. Meshes are drawn by their indices. + * + * @param mesh Mesh to draw. + * @param mode Which drawing mode to use to draw the mesh. + * @param indiceStart Start indice (index) to draw. + * @param indiceCount Count of indices to draw. Use -1 to draw all. + */ +void meshDraw( + const mesh_t *mesh, + const meshdrawmode_t mode, + const int32_t indiceStart, + const int32_t indiceCount +); + +/** + * Cleanup a mesh. This will destroy the buffers and free the mesh. + * + * @param mesh Mesh to cleanup. + */ +void meshDispose(mesh_t *mesh); \ No newline at end of file diff --git a/src/dawnopengl/display/shader.c b/src/dawnopengl/display/shader.c new file mode 100644 index 00000000..0e372f2b --- /dev/null +++ b/src/dawnopengl/display/shader.c @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2023 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" + +int32_t SHADER_ACTIVE_COUNT; + +void shaderInit(shader_t *shader) { + assertNotNull(shader, "shaderInit: shader cannot be null."); + + shader->shaderVertex = -1; + shader->shaderFrag = -1; + shader->shaderProgram = -1; + + SHADER_ACTIVE_COUNT++; +} + +void shaderCompile( + shader_t *shader, + const char_t *vertex, + const char_t *fragment +) { + assertNotNull(shader, "shaderCompile: shader cannot be null."); + assertStringValid(vertex, 2048, "shaderGetParameterByName: name invalid."); + assertStringValid(fragment, 2048, "shaderGetParameterByName: name invalid."); + assertTrue(shader->shaderVertex == -1, "shaderCompile: vertex not -1."); + assertTrue(shader->shaderFrag == -1, "shaderCompile: fragment not -1."); + assertTrue(shader->shaderProgram == -1, "shaderCompile: program not -1."); + + GLint isSuccess; + int32_t maxLength; + char_t error[2048]; + + // Load the vertex shader first + shader->shaderVertex = glCreateShader(GL_VERTEX_SHADER); + assertNoGLError(); + glShaderSource(shader->shaderVertex, 1, &vertex, NULL); + assertNoGLError(); + glCompileShader(shader->shaderVertex); + assertNoGLError(); + + // Validate + glGetShaderiv(shader->shaderVertex, GL_COMPILE_STATUS, &isSuccess); + assertNoGLError(); + if(!isSuccess) { + glGetShaderiv(shader->shaderVertex, GL_INFO_LOG_LENGTH, &maxLength); + glGetShaderInfoLog(shader->shaderVertex, maxLength, &maxLength, error); + sprintf( + error, + "shaderCompile: Failed to compile vert shader:\n%s", + error + ); + assertUnreachable(error); + } + assertNoGLError(); + + // Now load the Frag shader + shader->shaderFrag = glCreateShader(GL_FRAGMENT_SHADER); + assertNoGLError(); + glShaderSource(shader->shaderFrag, 1, &fragment, NULL); + assertNoGLError(); + glCompileShader(shader->shaderFrag); + assertNoGLError(); + + glGetShaderiv(shader->shaderFrag, GL_COMPILE_STATUS, &isSuccess); + assertNoGLError(); + if(!isSuccess) { + glGetShaderiv(shader->shaderFrag, GL_INFO_LOG_LENGTH, &maxLength); + glGetShaderInfoLog(shader->shaderFrag, maxLength, &maxLength, error); + glDeleteShader(shader->shaderVertex); + sprintf( + error, + "shaderCompile: Failed to compile frag shader:\n%s", + error + ); + assertUnreachable(error); + } + assertNoGLError(); + + // Now create the shader program. + shader->shaderProgram = glCreateProgram(); + assertNoGLError(); + glAttachShader(shader->shaderProgram, shader->shaderVertex); + assertNoGLError(); + glAttachShader(shader->shaderProgram, shader->shaderFrag); + assertNoGLError(); + + //Bind, Verify & Use the shader program + glLinkProgram(shader->shaderProgram); + assertNoGLError(); + + glGetProgramiv(shader->shaderProgram, GL_LINK_STATUS, &isSuccess); + assertNoGLError(); + if(!isSuccess) { + glGetProgramiv(shader->shaderProgram, GL_INFO_LOG_LENGTH, &maxLength); + assertNoGLError(); + glGetProgramInfoLog(shader->shaderProgram, maxLength, &maxLength, error); + assertNoGLError(); + glDeleteShader(shader->shaderVertex); + assertNoGLError(); + glDeleteShader(shader->shaderFrag); + assertNoGLError(); + sprintf( + error, + "shaderCompile: Error compiling shader program:\n%s", + error + ); + assertUnreachable(error); + } +} + +shaderparameter_t shaderGetParameterByName( + const shader_t *shader, + const char_t *name +) { + assertNotNull(shader, "shaderGetParameterByName: shader cannot be null."); + assertStringValid(name, 128, "shaderGetParameterByName: name invalid."); + + shaderparameter_t param = glGetUniformLocation(shader->shaderProgram, name); + assertNoGLError(); + assertTrue(param != -1, "shaderGetParameterByName: param not found."); + return param; +} + +void shaderBind(const shader_t *shader) { + assertNotNull(shader, "shaderBind: Shader cannot be null."); + assertTrue( + shader->shaderProgram != -1, + "shaderBind: Cannot bind a program that is not ready" + ); + glUseProgram(shader->shaderProgram); + assertNoGLError(); +} + +void shaderSetMatrix(const shaderparameter_t param, const mat4_t matrix) { + glUniformMatrix4fv(param, 1, GL_FALSE, matrix[0]); + assertNoGLError(); +} + +void shaderSetBoolean(const shaderparameter_t param, const bool_t value) { + glUniform1i(param, value); + assertNoGLError(); +} + +void shaderSetInteger(const shaderparameter_t param, const int32_t value) { + glUniform1i(param, value); + assertNoGLError(); +} + +void shaderSetFloat(const shaderparameter_t param, const float_t value) { + glUniform1f(param, value); + assertNoGLError(); +} + +void shaderSetVector2(const shaderparameter_t param, const vec2_t vector) { + glUniform2f(param, vector[0], vector[1]); + assertNoGLError(); +} + +void shaderSetVector3(const shaderparameter_t param, const vec3_t vector) { + glUniform3f(param, vector[0], vector[1], vector[2]); + assertNoGLError(); +} + +void shaderSetVector4(const shaderparameter_t param, const vec4_t vector) { + glUniform4f(param, vector[0], vector[1], vector[2], vector[3]); + assertNoGLError(); +} + +void shaderSetColor3f(const shaderparameter_t param, const color3f_t color) { + glUniform3f(param, color[0], color[1], color[2]); + assertNoGLError(); +} + +void shaderSetColor4f(const shaderparameter_t param, const color4f_t color) { + glUniform4f(param, color[0], color[1], color[2], color[3]); + assertNoGLError(); +} + +void shaderSetTexture(const shaderparameter_t param, const textureslot_t slot) { + glUniform1i(param, slot); + assertNoGLError(); +} + +void shaderDispose(const shader_t *shader) { + if(shader->shaderProgram != -1) { + glDeleteProgram(shader->shaderProgram); + assertNoGLError(); + } + + if(shader->shaderVertex != -1) { + glDeleteShader(shader->shaderVertex); + assertNoGLError(); + } + + if(shader->shaderFrag != -1) { + glDeleteShader(shader->shaderFrag); + assertNoGLError(); + } + + SHADER_ACTIVE_COUNT--; +} \ No newline at end of file diff --git a/src/dawnopengl/display/shader.h b/src/dawnopengl/display/shader.h new file mode 100644 index 00000000..b1f8d4b0 --- /dev/null +++ b/src/dawnopengl/display/shader.h @@ -0,0 +1,152 @@ +/** + * Copyright (c) 2023 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dawn.h" +#include "dawnopengl.h" +#include "display/colorgl.h" +#include "display/texture.h" + +typedef GLuint shaderparameter_t; +extern int32_t SHADER_ACTIVE_COUNT; + +typedef struct { + /** Pointer to an uploaded vertex shader program */ + GLuint shaderVertex; + + /** Pointer to an uploaded fragment shader program */ + GLuint shaderFrag; + + /** Pointer to an uploaded shader program linked */ + GLuint shaderProgram; +} shader_t; + +/** + * Initializes a shader object. + * + * @param shader The shader object to initialize. + */ +void shaderInit(shader_t *shader); + +/** + * Compiles a shader object. + * + * @param shader The shader object to compile. + * @param vertex The vertex shader source. + * @param fragment The fragment shader source. + */ +void shaderCompile( + shader_t *shader, + const char_t *vertex, + const char_t *fragment +); + +/** + * Returns a shader parameter by name. + * + * @param shader Shader to get the parameter from. + * @param name Name of the parameter to get. + * @return The shader parameter. + */ +shaderparameter_t shaderGetParameterByName( + const shader_t *shader, + const char_t *name +); + +/** + * Attaches the supplied shader as the current shader. + * + * @param shader + */ +void shaderBind(const shader_t *shader); + +/** + * Sets a matrix parameter on the shader. + * + * @param param Parameter to set. + * @param matrix Matrix value to set. + */ +void shaderSetMatrix(const shaderparameter_t param, const mat4_t matrix); + +/** + * Sets a boolean parameter on the shader. + * + * @param param Parameter to set. + * @param value Boolean value to set. + */ +void shaderSetBoolean(const shaderparameter_t param, const bool_t value); + +/** + * Sets an integer parameter on the shader. + * + * @param param Parameter to set. + * @param value Integer value to set. + */ +void shaderSetInteger(const shaderparameter_t param, const int32_t value); + +/** + * Sets a float parameter on the shader. + * + * @param param Parameter to set. + * @param value Float value to set. + */ +void shaderSetFloat(const shaderparameter_t param, const float_t value); + +/** + * Sets a vector parameter on the shader. + * + * @param param Parameter to set. + * @param vector Vector value to set. + */ +void shaderSetVector2(const shaderparameter_t param, const vec2_t vector); + +/** + * Sets a vector parameter on the shader. + * + * @param param Parameter to set. + * @param vector Vector value to set. + */ +void shaderSetVector3(const shaderparameter_t param, const vec3_t vector); + +/** + * Sets a vector parameter on the shader. + * + * @param param Parameter to set. + * @param vector Vector value to set. + */ +void shaderSetVector4(const shaderparameter_t param, const vec4_t vector); + +/** + * Sets a color parameter on the shader. + * + * @param param Parameter to set. + * @param color Color value to set. + */ +void shaderSetColor3f(const shaderparameter_t param, const color3f_t color); + +/** + * Sets a color parameter on the shader. + * + * @param param Parameter to set. + * @param color Color value to set. + */ +void shaderSetColor4f(const shaderparameter_t param, const color4f_t color); + +/** + * Sets a texture parameter on the shader. + * + * @param param Parameter to set. + * @param slot Slot to bind the texture to. + */ +void shaderSetTexture(const shaderparameter_t param, const textureslot_t slot); + +/** + * Disposes of a shader object. + * + * @param shader Shader to destroy. + */ +void shaderDispose(const shader_t *shader); \ No newline at end of file diff --git a/src/dawnopengl/display/shaders/CMakeLists.txt b/src/dawnopengl/display/shaders/CMakeLists.txt new file mode 100644 index 00000000..c520d810 --- /dev/null +++ b/src/dawnopengl/display/shaders/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2022 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DAWN_TARGET_NAME} + PRIVATE + simpletexturedshader.c +) \ No newline at end of file diff --git a/src/dawnopengl/display/shaders/simpletexturedshader.c b/src/dawnopengl/display/shaders/simpletexturedshader.c new file mode 100644 index 00000000..fbceb1ff --- /dev/null +++ b/src/dawnopengl/display/shaders/simpletexturedshader.c @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2023 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "simpletexturedshader.h" + +shader_t SIMPLE_TEXTURED_SHADER; + +void simpleTexturedShaderInit() { + shaderInit(&SIMPLE_TEXTURED_SHADER); + shaderCompile(&SIMPLE_TEXTURED_SHADER, + // Vertex Shader + "#version 330 core\n" + "layout (location = 0) in vec3 aPos;\n" + "layout (location = 1) in vec2 aTexCoord;\n" + "uniform mat4 u_Projection;\n" + "uniform mat4 u_View;\n" + "uniform mat4 u_Model;\n" + "out vec2 o_TextCoord;\n" + "void main() {\n" + "gl_Position = u_Projection * u_View * u_Model * vec4(aPos, 1.0);\n" + "o_TextCoord = vec2(aTexCoord.x, aTexCoord.y);\n" + "}", + // Fragment Shader + "#version 330 core\n" + "in vec2 o_TextCoord;\n" + "out vec4 o_Color;\n" + "uniform vec4 u_Color;\n" + "uniform bool u_HasTexture;\n" + "uniform sampler2D u_Text;\n" + "void main() {\n" + "if(u_HasTexture) {\n" + "o_Color = texture(u_Text, o_TextCoord) * u_Color;\n" + "} else {\n" + "o_Color = u_Color;" + "}\n" + "}\n" + ); + + shaderBind(&SIMPLE_TEXTURED_SHADER); + + simpleTexturedShaderSetColor(COLOR4F_WHITE); + simpleTexturedShaderSetTexture(NULL); + + mat4 projection; + glm_mat4_identity(projection); + glm_perspective(45.0f, 16.0f/9.0f, 0.01f, 1000.0f, projection); + simpleTexturedShaderSetProjection(projection); + + mat4 view; + glm_mat4_identity(view); + glm_lookat((vec3_t){ 5,5,5 }, (vec3_t){ 0, 0, 0 }, (vec3_t){ 0, 1, 0 }, view); + simpleTexturedShaderSetView(view); + + mat4 model; + glm_mat4_identity(model); + glm_translate(model, (vec3_t){ 0, 0, 0 }); + simpleTexturedShaderSetModel(model); +} + +void simpleTexturedShaderBind() { + shaderBind(&SIMPLE_TEXTURED_SHADER); +} + +void simpleTexturedShaderSetModel(const mat4_t model) { + shaderparameter_t param = shaderGetParameterByName( + &SIMPLE_TEXTURED_SHADER, "u_Model" + ); + shaderSetMatrix(param, model); +} + +void simpleTexturedShaderSetView(const mat4_t view) { + shaderparameter_t param = shaderGetParameterByName( + &SIMPLE_TEXTURED_SHADER, "u_View" + ); + shaderSetMatrix(param, view); +} + +void simpleTexturedShaderSetProjection(const mat4_t projection) { + shaderparameter_t param = shaderGetParameterByName( + &SIMPLE_TEXTURED_SHADER, "u_Projection" + ); + shaderSetMatrix(param, projection); +} + +void simpleTexturedShaderSetColor(const color4f_t color) { + shaderparameter_t param = shaderGetParameterByName( + &SIMPLE_TEXTURED_SHADER, "u_Color" + ); + shaderSetVector4(param, color); +} + +void simpleTexturedShaderSetTexture(const texture_t *texture) { + shaderparameter_t paramHas = shaderGetParameterByName( + &SIMPLE_TEXTURED_SHADER, "u_HasTexture" + ); + if(texture == NULL) { + shaderSetBoolean(paramHas, false); + return; + } + shaderparameter_t param = shaderGetParameterByName( + &SIMPLE_TEXTURED_SHADER, "u_Text" + ); + textureBind(texture, 0); + shaderSetTexture(param, 0); + shaderSetBoolean(paramHas, true); +} + +void simpleTexturedShaderDispose() { + shaderDispose(&SIMPLE_TEXTURED_SHADER); +} \ No newline at end of file diff --git a/src/dawnopengl/display/shaders/simpletexturedshader.h b/src/dawnopengl/display/shaders/simpletexturedshader.h new file mode 100644 index 00000000..0f0a6f3b --- /dev/null +++ b/src/dawnopengl/display/shaders/simpletexturedshader.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2023 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/shader.h" + +extern shader_t SIMPLE_TEXTURED_SHADER; + +/** + * Initialize the simple textured shader. This is a simple shader for rendering + * textured objects without lighting or anything fancy. + */ +void simpleTexturedShaderInit(); + +/** + * Binds the simple textured shader. + */ +void simpleTexturedShaderBind(); + +/** + * Sets the model matrix for the shader. + * @param model The model matrix to set. + */ +void simpleTexturedShaderSetModel(const mat4_t model); + +/** + * Sets the color for the shader. + * @param color The color to set. + */ +void simpleTexturedShaderSetColor(const color4f_t color); + +/** + * Sets the projection matrix for the shader. + * @param projection The projection matrix to set. + */ +void simpleTexturedShaderSetProjection(const mat4_t projection); + +/** + * Sets the view matrix for the shader. + * @param view The view matrix to set. + */ +void simpleTexturedShaderSetView(const mat4_t view); + +/** + * Sets the texture for the shader. + * @param texture The texture to set. + */ +void simpleTexturedShaderSetTexture(const texture_t *texture); + +/** + * Destroys the simple textured shader. + */ +void simpleTexturedShaderDispose(); \ No newline at end of file diff --git a/src/dawnopengl/display/texture.c b/src/dawnopengl/display/texture.c new file mode 100644 index 00000000..4f2908e5 --- /dev/null +++ b/src/dawnopengl/display/texture.c @@ -0,0 +1,199 @@ +/** + * 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 "util/math.h" + +int32_t TEXTURE_ACTIVE_COUNT; + +void textureInit( + texture_t *texture, + int32_t width, + int32_t height, + textureformat_t format, + texturedataformat_t dataFormat +) { + assertNotNull(texture, "textureInit: texture cannot be null."); + assertTrue(width > 0, "textureInit: width must be greater than 0."); + assertTrue(height > 0, "textureInit: height must be greater than 0."); + + texture->width = width; + texture->height = height; + texture->format = format; + texture->dataFormat = dataFormat; + texture->wrapModeX = TEXTURE_WRAP_MODE_REPEAT; + texture->wrapModeY = TEXTURE_WRAP_MODE_REPEAT; + texture->filterModeMin = TEXTURE_FILTER_MODE_LINEAR; + texture->filterModeMag = TEXTURE_FILTER_MODE_LINEAR; + + int32_t maxSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); + assertTrue( + width > 0 && width <= maxSize, "textureInit: Width is out of bounds!" + ); + assertTrue( + height > 0 && height <= maxSize, "textureInit: Height is out of bounds!" + ); + + glGenTextures(1, &texture->id); + assertNoGLError(); + assertTrue(texture->id > 0, "textureInit: Failed to generate texture ID!"); + + TEXTURE_ACTIVE_COUNT++; + + switch(texture->dataFormat) { + case TEXTURE_DATA_FORMAT_FLOAT: + texture->elementSize = sizeof(float_t); + break; + case TEXTURE_DATA_FORMAT_UNSIGNED_BYTE: + texture->elementSize = sizeof(uint8_t); + break; + default: + assertUnreachable("textureInit: Unknown data format."); + break; + } + switch(texture->format) { + case TEXTURE_FORMAT_RGBA: + texture->elementSize *= 4; + break; + case TEXTURE_FORMAT_RGB: + texture->elementSize *= 3; + break; + case TEXTURE_FORMAT_RG: + texture->elementSize *= 2; + break; + case TEXTURE_FORMAT_R: + break; + default: + assertUnreachable("textureInit: Unknown texture format."); + break; + } + + textureBind(texture, 0); + textureBuffer(texture, NULL); +} + +void textureBind(const texture_t *texture, const textureslot_t slot) { + glActiveTexture(GL_TEXTURE0 + slot); + assertNoGLError(); + glBindTexture(GL_TEXTURE_2D, texture->id); + assertNoGLError(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->wrapModeX); + assertNoGLError(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture->wrapModeY); + assertNoGLError(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture->filterModeMin); + assertNoGLError(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texture->filterModeMag); + assertNoGLError(); +} + +void textureBuffer(const texture_t *texture, const void *data) { + assertNotNull(texture, "textureBuffer: texture cannot be null."); + + textureBind(texture, 0); + glTexImage2D( + GL_TEXTURE_2D, + 0, + texture->format, + texture->width, + texture->height, + 0, + texture->format, + texture->dataFormat, + data + ); + assertNoGLError(); + + // glGenerateMipmap(GL_TEXTURE_2D); + // assertNoGLError(); +} + +void textureBufferSub( + const texture_t *texture, + const void *data, + const size_t offset, + const size_t length +) { + assertNotNull(texture, "textureBufferSub: Texture cannot be null."); + assertNotNull(data, "textureBufferSub: Data cannot be null."); + assertTrue(offset >= 0, "textureBufferSub: Offset must be greater than 0."); + assertTrue(length > 0, "textureBufferSub: Length must be greater than 0."); + assertTrue( + offset + length <= texture->width * texture->height, + "textureBufferSub: Buffer out of bounds." + ); + + if(offset == 0 && length == texture->width * texture->height) { + textureBuffer(texture, data); + return; + } + + // Bind the texture. + textureBind(texture, 0); + + // Buffer initial row only. + int32_t xOffset = offset % texture->width; + int32_t yOffset = offset / texture->width; + size_t n = mathMin(length, texture->width - xOffset); + size_t buffered = 0; + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + xOffset, yOffset, + n, 1, + texture->format, texture->dataFormat, + data + ); + assertNoGLError(); + buffered += n; + yOffset += 1; + + // Buffer the creamy middle (inefficient?) + int32_t rows = 0; + size_t initialBuffered = buffered; + while(buffered + texture->width < length) { + rows += 1; + buffered += texture->width; + } + + if(rows > 0) { + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, yOffset, + texture->width, rows, + texture->format, texture->dataFormat, + data + (initialBuffered * texture->elementSize) + ); + assertNoGLError(); + yOffset += rows; + } + + // Buffer last row only. + if(buffered != length) { + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, yOffset, + length - buffered, 1, + texture->format, texture->dataFormat, + data + (buffered * texture->elementSize) + ); + assertNoGLError(); + } +} + +void textureDispose(const texture_t *texture) { + glDeleteTextures(1, &texture->id); + assertNoGLError(); + TEXTURE_ACTIVE_COUNT--; +} \ No newline at end of file diff --git a/src/dawnopengl/display/texture.h b/src/dawnopengl/display/texture.h new file mode 100644 index 00000000..2e8959c0 --- /dev/null +++ b/src/dawnopengl/display/texture.h @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2024 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dawn.h" +#include "dawnopengl.h" + +extern int32_t TEXTURE_ACTIVE_COUNT; + +typedef GLuint textureslot_t; + +typedef enum { + TEXTURE_FORMAT_R = GL_RED, + TEXTURE_FORMAT_RG = GL_RG, + TEXTURE_FORMAT_RGB = GL_RGB, + TEXTURE_FORMAT_RGBA = GL_RGBA +} textureformat_t; + +typedef enum { + TEXTURE_WRAP_MODE_REPEAT = GL_REPEAT, + TEXTURE_WRAP_MODE_MIRRORED_REPEAT = GL_MIRRORED_REPEAT, + TEXTURE_WRAP_MODE_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, +} texturewrapmode_t; + +typedef enum { + TEXTURE_FILTER_MODE_NEAREST = GL_NEAREST, + TEXTURE_FILTER_MODE_LINEAR = GL_LINEAR +} texturefiltermode_t; + +typedef enum { + TEXTURE_DATA_FORMAT_UNSIGNED_BYTE = GL_UNSIGNED_BYTE, + TEXTURE_DATA_FORMAT_FLOAT = GL_FLOAT +} texturedataformat_t; + +typedef struct { + GLuint id; + int32_t width; + int32_t height; + textureformat_t format; + texturedataformat_t dataFormat; + texturewrapmode_t wrapModeX; + texturewrapmode_t wrapModeY; + texturefiltermode_t filterModeMin; + texturefiltermode_t filterModeMag; + size_t elementSize; +} texture_t; + +/** + * Initializes a texture to be read to be used. + * + * @param texture Texture to initialize. + * @param width Width of the texture (in pixels). + * @param height Height of the texture (in pixels). + * @param format Data format of the texture to use. + * @param dataFormat Data format of the texture to use. + */ +void textureInit( + texture_t *texture, + int32_t width, + int32_t height, + textureformat_t format, + texturedataformat_t dataFormat +); + +/** + * 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 textureslot_t slot); + +/** + * Buffers pixels on to the texture. Set data to NULL to fill with empty pixels + * (black by default). + * + * @param texture Texture to buffer. + * @param data Data to buffer. + */ +void textureBuffer(const texture_t *texture, const void *data); + +/** + * Buffer sub data to this texture. + * + * @param texture Texture to buffer on to. + * @param data Data to buffer. + * @param offset Offset position to buffer on to. + * @param length Length of the data you are buffering. + */ +void textureBufferSub( + const texture_t *texture, + const void *data, + const size_t offset, + const size_t length +); + +/** + * Unloads a previously initialized texture. + * + * @param texture Texture to destroy. + */ +void textureDispose(const texture_t *texture); \ No newline at end of file diff --git a/src/dawnopengl/display/textureframebuffer.c b/src/dawnopengl/display/textureframebuffer.c new file mode 100644 index 00000000..c8d15c6c --- /dev/null +++ b/src/dawnopengl/display/textureframebuffer.c @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2024 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "textureframebuffer.h" +#include "assert/assert.h" +#include "assert/assertgl.h" + +void textureFrameBufferInit( + textureframebuffer_t *framebuffer, + const int32_t width, + const int32_t height +) { + assertNotNull(framebuffer, "Framebuffer cannot be NULL"); + assertTrue(width > 0, "Width must be greater than 0"); + assertTrue(height > 0, "Height must be greater than 0"); + + textureInit( + &framebuffer->texture, + width, height, + TEXTURE_FORMAT_RGBA, + TEXTURE_DATA_FORMAT_FLOAT + ); + + // Create Frame Buffer + glGenFramebuffers(1, &framebuffer->fboId); + assertNoGLError(); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->fboId); + assertNoGLError(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, framebuffer->texture.id, 0 + ); + assertNoGLError(); + + // Create Render Buffer + glGenRenderbuffers(1, &framebuffer->rboId); + assertNoGLError(); + glBindRenderbuffer(GL_RENDERBUFFER, framebuffer->rboId); + assertNoGLError(); + glRenderbufferStorage( + GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, + framebuffer->texture.width, framebuffer->texture.height + ); + assertNoGLError(); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + assertNoGLError(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, framebuffer->rboId + ); + assertNoGLError(); + + // Validate things went correct. + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + assertUnreachable("Framebuffer is not complete!"); + } +} + +void textureFrameBufferBind(const textureframebuffer_t *framebuffer) { + assertNotNull(framebuffer, "Framebuffer cannot be NULL"); + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->fboId); + assertNoGLError(); + glViewport(0, 0, framebuffer->texture.width, framebuffer->texture.height); + assertNoGLError(); +} + +void textureFrameBufferClear(const textureframebuffer_t *framebuffer) { + assertNotNull(framebuffer, "Framebuffer cannot be NULL"); + + glClearColor(0,0,0,1); + assertNoGLError(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + assertNoGLError(); +} + +void textureFrameBufferDispose(textureframebuffer_t *framebuffer) { + assertNotNull(framebuffer, "Framebuffer cannot be NULL"); + + glDeleteFramebuffers(1, &framebuffer->fboId); + glDeleteRenderbuffers(1, &framebuffer->rboId); + textureDispose(&framebuffer->texture); +} \ No newline at end of file diff --git a/src/dawnopengl/display/textureframebuffer.h b/src/dawnopengl/display/textureframebuffer.h new file mode 100644 index 00000000..2b09134a --- /dev/null +++ b/src/dawnopengl/display/textureframebuffer.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2024 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "display/texture.h" +#include "display/color.h" + +typedef struct { + texture_t texture; + GLuint fboId; + GLuint rboId; +} textureframebuffer_t; + +/** + * Initializes a texture framebuffer. + * + * @param framebuffer Framebuffer to initialize. + * @param width Width of the framebuffer (in pixels). + * @param height Height of the framebuffer (in pixels). + */ +void textureFrameBufferInit( + textureframebuffer_t *framebuffer, + const int32_t width, + const int32_t height +); + +/** + * Binds the framebuffer to the current context. + * + * @param framebuffer Framebuffer to bind. + */ +void textureFrameBufferBind(const textureframebuffer_t *framebuffer); + +/** + * Clears the framebuffer. + * + * @param framebuffer Framebuffer to clear. + */ +void textureFrameBufferClear(const textureframebuffer_t *framebuffer); + +/** + * Disposes of the framebuffer. + * + * @param framebuffer Framebuffer to dispose. + */ +void textureFrameBufferDispose(textureframebuffer_t *framebuffer); \ No newline at end of file diff --git a/src/dawntermlinux/linuxhost.c b/src/dawntermlinux/linuxhost.c index 83b392dd..2090d76f 100644 --- a/src/dawntermlinux/linuxhost.c +++ b/src/dawntermlinux/linuxhost.c @@ -48,7 +48,9 @@ int32_t keyboardCharacterRead() { } } -int32_t linuxHostBeginUpdating() { +int32_t linuxHostHandoff() { + gameInit(); + while(true) { terminalModeSetCONIO(); diff --git a/src/dawntermlinux/linuxhost.h b/src/dawntermlinux/linuxhost.h index ccaa0704..7b42f026 100644 --- a/src/dawntermlinux/linuxhost.h +++ b/src/dawntermlinux/linuxhost.h @@ -13,4 +13,4 @@ extern int32_t LINUX_TERM_HOST_KEY_PRESS; /** * Begins updating the host. */ -int32_t linuxHostBeginUpdating(); +int32_t linuxHostHandoff();