From bd57f345ed9dbed1d81683e48199626de2ea9044 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 27 Jun 2025 10:18:39 -0700 Subject: Restructure project --- src/core/geometry.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 src/core/geometry.c (limited to 'src/core/geometry.c') diff --git a/src/core/geometry.c b/src/core/geometry.c new file mode 100644 index 0000000..cfc749f --- /dev/null +++ b/src/core/geometry.c @@ -0,0 +1,326 @@ +#include "geometry.h" + +#include "buffer.h" +#include "constants.h" + +#include + +#include +#include + +/// Determines whether a view is populated. +/// +/// Note that views are allowed to have no data, in which case a buffer of the +/// specified size is created. +#define view_is_populated(BUFFER_VIEW) (BUFFER_VIEW.size_bytes > 0) + +static GLenum primitive_type_to_gl(PrimitiveType type) { + switch (type) { + case Triangles: + return GL_TRIANGLES; + case TriangleFan: + return GL_TRIANGLE_FAN; + case TriangleStrip: + return GL_TRIANGLE_STRIP; + } + FAIL("primitive_type_to_gl(): missing case"); + return GL_INVALID_ENUM; +} + +/// Create a typed buffer for the buffer view if the view does not already point +/// to a buffer. +void init_view_buffer( + GfxCore* gfxcore, BufferView* view, BufferType buffer_type, + BufferUsage buffer_usage) { + if (!view->buffer) { + view->buffer = gfx_make_buffer( + gfxcore, + &(BufferDesc){ + .usage = buffer_usage, + .type = buffer_type, + .data.data = view->data, + .data.count = view->size_bytes / + gfx_get_buffer_type_size_bytes(buffer_type)}); + } + assert(view->size_bytes <= view->buffer->size_bytes); +} + +/// Configure the buffer in teh VAO. +static void configure_buffer( + GfxCore* gfxcore, const GeometryDesc* desc, BufferView* view, + size_t num_components, size_t component_size_bytes, GLenum component_type, + GLboolean normalized, GLuint channel) { + assert(gfxcore); + assert(desc); + assert(view); + assert(view->buffer); + assert( + desc->num_verts <= + view->size_bytes / (num_components * component_size_bytes)); + assert(view->size_bytes <= view->buffer->size_bytes); + + glBindBuffer(GL_ARRAY_BUFFER, view->buffer->vbo); + glEnableVertexAttribArray(channel); + if ((component_type == GL_FLOAT) || normalized) { + glVertexAttribPointer( + channel, num_components, component_type, normalized, view->stride_bytes, + (const void*)view->offset_bytes); + } else { + assert(!normalized); + assert( + (component_type == GL_BYTE) || (component_type == GL_UNSIGNED_BYTE) || + (component_type == GL_SHORT) || (component_type == GL_UNSIGNED_SHORT) || + (component_type == GL_INT) || component_type == GL_UNSIGNED_INT); + glVertexAttribIPointer( + channel, num_components, component_type, view->stride_bytes, + (const void*)view->offset_bytes); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { + assert(gfxcore); + assert(desc); + + if (view_is_populated(desc->positions3d)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->positions3d, Buffer3d, desc->buffer_usage); + if (!desc->positions3d.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->positions3d, 3, sizeof(float), + GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); + } else if (view_is_populated(desc->positions2d)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->positions2d, Buffer2d, desc->buffer_usage); + if (!desc->positions2d.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->positions2d, 2, sizeof(float), + GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); + } + if (view_is_populated(desc->normals)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->normals, Buffer3d, desc->buffer_usage); + if (!desc->normals.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->normals, 3, sizeof(float), GL_FLOAT, + GL_FALSE, GFX_NORMAL_CHANNEL); + } + if (view_is_populated(desc->tangents)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->tangents, Buffer4d, desc->buffer_usage); + if (!desc->tangents.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->tangents, 4, sizeof(float), GL_FLOAT, + GL_FALSE, GFX_TANGENT_CHANNEL); + } + if (view_is_populated(desc->texcoords)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->texcoords, Buffer2d, desc->buffer_usage); + if (!desc->texcoords.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->texcoords, 2, sizeof(float), + GL_FLOAT, GL_FALSE, GFX_TEXCOORDS_CHANNEL); + } + if (view_is_populated(desc->joints.u8)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->joints.u8, BufferU8, desc->buffer_usage); + if (!desc->joints.u8.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->joints.u8, 4, sizeof(uint8_t), + GL_UNSIGNED_BYTE, GL_FALSE, GFX_JOINTS_CHANNEL); + } else if (view_is_populated(desc->joints.u16)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->joints.u16, BufferU16, desc->buffer_usage); + if (!desc->joints.u16.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->joints.u16, 4, sizeof(uint16_t), + GL_UNSIGNED_SHORT, GL_FALSE, GFX_JOINTS_CHANNEL); + } + + // If weights are given as unsigned integers, then they are normalized + // when read by the shader. + if (view_is_populated(desc->weights.u8)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->weights.u8, BufferU8, desc->buffer_usage); + if (!desc->weights.u8.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->weights.u8, 4, sizeof(uint8_t), + GL_UNSIGNED_BYTE, GL_TRUE, GFX_WEIGHTS_CHANNEL); + } else if (view_is_populated(desc->weights.u16)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->weights.u16, BufferU16, + desc->buffer_usage); + if (!desc->weights.u16.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->weights.u16, 4, sizeof(uint16_t), + GL_UNSIGNED_SHORT, GL_TRUE, GFX_WEIGHTS_CHANNEL); + } else if (view_is_populated(desc->weights.floats)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->weights.floats, BufferFloat, + desc->buffer_usage); + if (!desc->weights.floats.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->weights.floats, 4, sizeof(float), + GL_FLOAT, GL_FALSE, GFX_WEIGHTS_CHANNEL); + } + + return true; +} + +static bool configure_indices(GfxCore* gfxcore, GeometryDesc* desc) { + assert(gfxcore); + assert(desc); + + if (view_is_populated(desc->indices8)) { + assert(desc->num_indices > 0); + assert( + desc->num_indices <= desc->indices8.size_bytes / sizeof(VertexIndex8)); + init_view_buffer( + gfxcore, (BufferView*)&desc->indices8, BufferU8, desc->buffer_usage); + if (!desc->indices8.buffer) { + return false; + } + } else if (view_is_populated(desc->indices16)) { + assert(desc->num_indices > 0); + assert( + desc->num_indices <= + desc->indices16.size_bytes / sizeof(VertexIndex16)); + init_view_buffer( + gfxcore, (BufferView*)&desc->indices16, BufferU16, desc->buffer_usage); + if (!desc->indices16.buffer) { + return false; + } + } + + return true; +} + +bool gfx_init_geometry( + Geometry* geometry, GfxCore* gfxcore, const GeometryDesc* input_desc) { + assert(geometry); + assert(gfxcore); + assert(input_desc); + assert( + view_is_populated(input_desc->positions3d) || + view_is_populated(input_desc->positions2d)); + assert(input_desc->num_verts > 0); + + geometry->mode = primitive_type_to_gl(input_desc->type); + geometry->desc = *input_desc; + geometry->num_verts = input_desc->num_verts; + geometry->gfxcore = gfxcore; + + // The geometry's copy of the descriptor is manipulated below. Create a + // shorter name for it. + GeometryDesc* desc = &geometry->desc; + + glGenVertexArrays(1, &geometry->vao); + glBindVertexArray(geometry->vao); + if (!configure_vertex_attributes(gfxcore, desc)) { + goto cleanup; + } + if (!configure_indices(gfxcore, desc)) { + goto cleanup; + } + glBindVertexArray(0); + ASSERT_GL; + + return true; + +cleanup: + gfx_del_geometry(geometry); + return 0; +} + +void gfx_del_geometry(Geometry* geometry) { + assert(geometry); + if (geometry->vao) { + glDeleteVertexArrays(1, &geometry->vao); + geometry->vao = 0; + } +} + +void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { + assert(geometry); + assert(desc); + // New geometry size cannot exceed original size. + assert(desc->positions3d.size_bytes <= geometry->desc.positions3d.size_bytes); + assert(desc->positions2d.size_bytes <= geometry->desc.positions2d.size_bytes); + assert(desc->normals.size_bytes <= geometry->desc.normals.size_bytes); + assert(desc->tangents.size_bytes <= geometry->desc.tangents.size_bytes); + assert(desc->texcoords.size_bytes <= geometry->desc.texcoords.size_bytes); + assert(desc->joints.u8.size_bytes <= geometry->desc.joints.u8.size_bytes); + assert(desc->joints.u16.size_bytes <= geometry->desc.joints.u16.size_bytes); + assert(desc->weights.u8.size_bytes <= geometry->desc.weights.u8.size_bytes); + assert(desc->weights.u16.size_bytes <= geometry->desc.weights.u16.size_bytes); + assert( + desc->weights.floats.size_bytes <= + geometry->desc.weights.floats.size_bytes); + + if (desc->positions3d.data) { + // The geometry must already have an underlying GPU buffer. + assert(geometry->desc.positions3d.buffer); + gfx_update_buffer( + geometry->desc.positions3d.buffer, + &(BufferDataDesc){ + .vec3s = desc->positions3d.data, + .count = desc->positions3d.size_bytes / sizeof(vec3)}); + } + // TODO: more + else { + FAIL("TODO: gfx_update_geometry() - handle other buffer types"); + } + + if (desc->num_verts != 0) { + geometry->num_verts = desc->num_verts; + } +} + +void gfx_render_geometry(const Geometry* geometry) { + assert(geometry); + assert(geometry->vao); + + const GeometryDesc* desc = &geometry->desc; + glBindVertexArray(geometry->vao); + + if (desc->indices8.buffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices8.buffer->vbo); + glDrawElements( + geometry->mode, desc->num_indices, GL_UNSIGNED_BYTE, + (const void*)desc->indices8.offset_bytes); + } else if (desc->indices16.buffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices16.buffer->vbo); + glDrawElements( + geometry->mode, desc->num_indices, GL_UNSIGNED_SHORT, + (const void*)desc->indices16.offset_bytes); + } else { + glDrawArrays(geometry->mode, 0, geometry->num_verts); + } + + glBindVertexArray(0); +} + +aabb3 gfx_get_geometry_aabb(const Geometry* geometry) { + assert(geometry); + return geometry->desc.aabb; +} -- cgit v1.2.3