#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; }