diff --git a/src/main.c b/src/main.c index 273793b..b8cf3f5 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,7 @@ #include "window.h" #include "arena_allocator.h" +#include "mesh.h" /* void test_task(void *data) { @@ -77,7 +78,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - // set up vertex data (and buffer(s)) and configure vertex attributes +// Set up mesh data float vertices[] = { 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right @@ -89,75 +90,12 @@ int main(int argc, char *argv[]) { 1, 2, 3 // second Triangle }; - GLuint VBO, VAO, EBO; - glGenVertexArrays(1, &VAO); - glGenBuffers(1, &VBO); - glGenBuffers(1, &EBO); - // bind the Vertex Array Object first, then bind and set vertex buffer(s), and - // then configure vertex attributes(s). - glBindVertexArray(VAO); - - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, - GL_STATIC_DRAW); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0); - glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), - (void *)(3 * sizeof(float))); - glEnableVertexAttribArray(1); - - // note that this is allowed, the call to glVertexAttribPointer registered VBO - // as the vertex attribute's bound vertex buffer object so afterwards we can - // safely unbind - // glBindBuffer(GL_ARRAY_BUFFER, 0); - - // remember: do NOT unbind the EBO while a VAO is active as the bound element - // buffer object IS stored in the VAO; keep the EBO bound. - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - // You can unbind the VAO afterwards so other VAO calls won't accidentally - // modify this VAO, but this rarely happens. Modifying other VAOs requires a - // call to glBindVertexArray anyways so we generally don't unbind VAOs (nor - // VBOs) when it's not directly necessary. - // glBindVertexArray(0); - - // Texture Load - SDL_Surface *image = IMG_Load("./data/testxure.png"); - if (image == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load texture"); + // Create mesh + wn_mesh *mesh = wn_mesh_init(global_arena, vertices, 20, indices, 6, "./data/testxure.png"); + if (mesh == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize mesh"); return EXIT_FAILURE; } - SDL_FlipSurface(image, SDL_FLIP_VERTICAL); - - GLuint texture; - glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &texture); - - // Handle different SDL Surface data types - int mode = GL_RGBA; - // if (image->format->BytesPerPixel == 4) { - // mode = GL_RGBA; - // } - - glBindTexture(GL_TEXTURE_2D, texture); - // set the texture wrapping parameters - glTexParameteri( - GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - // set texture filtering parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexImage2D(GL_TEXTURE_2D, 0, mode, image->w, image->h, 0, mode, - GL_UNSIGNED_BYTE, image->pixels); - - glGenerateMipmap(GL_TEXTURE_2D); - SDL_DestroySurface(image); // Initialise camera Camera camera = Camera_default; @@ -196,23 +134,14 @@ int main(int argc, char *argv[]) { glClearColor(0.1f, 0.3f, 0.4f, 1.f); glClear(GL_COLOR_BUFFER_BIT); - // draw our first triangle + // draw our mesh glUseProgram(shader->shaderProgram); - - // seeing as we only have a single VAO there's no need to bind it - // every time, but we'll do so to keep things a bit more organized - glBindVertexArray(VAO); - - // glDrawArrays(GL_TRIANGLES, 0, 6); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); - // glBindVertexArray(0); // no need to unbind it every time + wn_mesh_draw(mesh); wn_swapwindow(window); } - glDeleteVertexArrays(1, &VAO); - glDeleteBuffers(1, &VBO); - glDeleteBuffers(1, &EBO); + // Mesh cleanup is handled by arena deinit tasks arena_deinit(global_arena); return EXIT_SUCCESS; diff --git a/src/mesh.c b/src/mesh.c index e69de29..bcbbc29 100644 --- a/src/mesh.c +++ b/src/mesh.c @@ -0,0 +1,119 @@ +#include "mesh.h" +#include +#include +#include +#include +#include + +void wn_mesh_deinit(wn_mesh *mesh) { + if (mesh == NULL) { + return; + } + + glDeleteVertexArrays(1, &mesh->VAO); + glDeleteBuffers(1, &mesh->VBO); + glDeleteBuffers(1, &mesh->EBO); + if (mesh->texture != 0) { + glDeleteTextures(1, &mesh->texture); + } +} + +wn_mesh *wn_mesh_init(Arena *arena, const float *vertices, size_t vertex_count, + const unsigned int *indices, size_t index_count, + const char *texture_path) { + wn_mesh *mesh = arena_alloc(arena, sizeof(wn_mesh)); + + // Generate OpenGL objects + glGenVertexArrays(1, &mesh->VAO); + glGenBuffers(1, &mesh->VBO); + glGenBuffers(1, &mesh->EBO); + + // Store counts + mesh->vertex_count = vertex_count; + mesh->index_count = index_count; + mesh->texture = 0; + + // Bind VAO first + glBindVertexArray(mesh->VAO); + + // Set up VBO + glBindBuffer(GL_ARRAY_BUFFER, mesh->VBO); + glBufferData(GL_ARRAY_BUFFER, vertex_count * sizeof(float), vertices, + GL_STATIC_DRAW); + + // Set up EBO + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_count * sizeof(unsigned int), + indices, GL_STATIC_DRAW); + + // Set vertex attributes + // Position attribute (location = 0) + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0); + glEnableVertexAttribArray(0); + + // Texture coordinate attribute (location = 1) + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), + (void *)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + + // Load texture if path provided + if (texture_path != NULL) { + SDL_Surface *image = IMG_Load(texture_path); + if (image == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load texture: %s", + texture_path); + wn_mesh_deinit(mesh); + return NULL; + } + + SDL_FlipSurface(image, SDL_FLIP_VERTICAL); + + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &mesh->texture); + + // Determine texture format + int mode = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, mesh->texture); + + // Set texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Upload texture data + glTexImage2D(GL_TEXTURE_2D, 0, mode, image->w, image->h, 0, mode, + GL_UNSIGNED_BYTE, image->pixels); + glGenerateMipmap(GL_TEXTURE_2D); + + SDL_DestroySurface(image); + } + + // Unbind VAO + glBindVertexArray(0); + + // Register deinit task + arena_deinit_task_push( + arena, (ArenaDeinitTask){.func_param = mesh, + .func_ptr = (void (*)(void *))wn_mesh_deinit}); + + return mesh; +} + +void wn_mesh_draw(wn_mesh *mesh) { + if (mesh == NULL) { + return; + } + + // Bind texture if available + if (mesh->texture != 0) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mesh->texture); + } + + // Bind VAO and draw + glBindVertexArray(mesh->VAO); + glDrawElements(GL_TRIANGLES, mesh->index_count, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); +} diff --git a/src/mesh.h b/src/mesh.h index 9247ad8..f62c48b 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -1,14 +1,22 @@ #pragma once -#include "SDL3/SDL_opengl.h" -#include "shader.h" -#include +#include "arena_allocator.h" +#include +#include -typedef struct { - float *vertex_buffer; - uint vertex_buffer_size; - float *element_buffer; - uint element_buffer_size; - wn_shader *shader; - GLuint VBO, VAO, EBO, diffuse_texture; -} Mesh; +typedef struct wn_mesh wn_mesh; + +struct wn_mesh { + unsigned int VAO; + unsigned int VBO; + unsigned int EBO; + unsigned int vertex_count; + unsigned int index_count; + GLuint texture; +}; + +wn_mesh *wn_mesh_init(Arena *arena, const float *vertices, size_t vertex_count, + const unsigned int *indices, size_t index_count, + const char *texture_path); +void wn_mesh_draw(wn_mesh *mesh); +void wn_mesh_deinit(wn_mesh *mesh);