/* Freetype GL - A C OpenGL Freetype engine * * Distributed under the OSI-approved BSD 2-Clause License. See accompanying * file `LICENSE` for more details. */ #pragma GCC diagnostic ignored "-Wformat" #include #include #include #include #include "vec234.h" #include "platform.h" #include "vertex-buffer.h" /** * Buffer status */ #define CLEAN (0) #define DIRTY (1) #define FROZEN (2) // ---------------------------------------------------------------------------- vertex_buffer_t *vertex_buffer_new(const char *format) { size_t i, index = 0, stride = 0; const char *start = 0, *end = 0; GLchar *pointer = 0; vertex_buffer_t *self = (vertex_buffer_t *) malloc(sizeof(vertex_buffer_t)); if (!self) { return NULL; } self->format = strdup(format); for (i = 0; i < MAX_VERTEX_ATTRIBUTE; ++i) { self->attributes[i] = 0; } start = format; do { char *desc = 0; vertex_attribute_t *attribute; GLuint attribute_size = 0; end = (char *) (strchr(start + 1, ',')); if (end == NULL) { desc = strdup(start); } else { desc = strndup(start, end - start); } attribute = vertex_attribute_parse(desc); start = end + 1; free(desc); attribute->pointer = pointer; switch (attribute->type) { case GL_BOOL: attribute_size = sizeof(GLboolean); break; case GL_BYTE: attribute_size = sizeof(GLbyte); break; case GL_UNSIGNED_BYTE: attribute_size = sizeof(GLubyte); break; case GL_SHORT: attribute_size = sizeof(GLshort); break; case GL_UNSIGNED_SHORT: attribute_size = sizeof(GLushort); break; case GL_INT: attribute_size = sizeof(GLint); break; case GL_UNSIGNED_INT: attribute_size = sizeof(GLuint); break; case GL_FLOAT: attribute_size = sizeof(GLfloat); break; default: attribute_size = 0; } stride += attribute->size * attribute_size; pointer += attribute->size * attribute_size; self->attributes[index] = attribute; index++; } while (end && (index < MAX_VERTEX_ATTRIBUTE)); for (i = 0; i < index; ++i) { self->attributes[i]->stride = stride; } #ifdef FREETYPE_GL_USE_VAO self->VAO_id = 0; #endif self->vertices = vector_new(stride); self->vertices_id = 0; self->GPU_vsize = 0; self->indices = vector_new(sizeof(GLuint)); self->indices_id = 0; self->GPU_isize = 0; self->items = vector_new(sizeof(ivec4)); self->state = DIRTY; self->mode = GL_TRIANGLES; return self; } // ---------------------------------------------------------------------------- void vertex_buffer_delete(vertex_buffer_t *self) { size_t i; assert(self); for (i = 0; i < MAX_VERTEX_ATTRIBUTE; ++i) { if (self->attributes[i]) { vertex_attribute_delete(self->attributes[i]); } } #ifdef FREETYPE_GL_USE_VAO if (self->VAO_id) { glDeleteVertexArrays(1, &self->VAO_id); } self->VAO_id = 0; #endif vector_delete(self->vertices); self->vertices = 0; if (self->vertices_id) { glDeleteBuffers(1, &self->vertices_id); } self->vertices_id = 0; vector_delete(self->indices); self->indices = 0; if (self->indices_id) { glDeleteBuffers(1, &self->indices_id); } self->indices_id = 0; vector_delete(self->items); if (self->format) { free(self->format); } self->format = 0; self->state = 0; free(self); } // ---------------------------------------------------------------------------- const char *vertex_buffer_format(const vertex_buffer_t *self) { assert(self); return self->format; } // ---------------------------------------------------------------------------- size_t vertex_buffer_size(const vertex_buffer_t *self) { assert(self); return vector_size(self->items); } // ---------------------------------------------------------------------------- void vertex_buffer_print(vertex_buffer_t *self) { int i = 0; static char *gltypes[9] = { "GL_BOOL", "GL_BYTE", "GL_UNSIGNED_BYTE", "GL_SHORT", "GL_UNSIGNED_SHORT", "GL_INT", "GL_UNSIGNED_INT", "GL_FLOAT", "GL_VOID" }; assert(self); fprintf(stderr, "%ld vertices, %ld indices\n", vector_size(self->vertices), vector_size(self->indices)); while (self->attributes[i]) { int j = 8; switch (self->attributes[i]->type) { case GL_BOOL: j = 0; break; case GL_BYTE: j = 1; break; case GL_UNSIGNED_BYTE: j = 2; break; case GL_SHORT: j = 3; break; case GL_UNSIGNED_SHORT: j = 4; break; case GL_INT: j = 5; break; case GL_UNSIGNED_INT: j = 6; break; case GL_FLOAT: j = 7; break; default: j = 8; break; } fprintf(stderr, "%s : %dx%s (+%p)\n", self->attributes[i]->name, self->attributes[i]->size, gltypes[j], self->attributes[i]->pointer); i += 1; } } // ---------------------------------------------------------------------------- void vertex_buffer_upload(vertex_buffer_t *self) { size_t vsize, isize; if (self->state == FROZEN) { return; } if (!self->vertices_id) { glGenBuffers(1, &self->vertices_id); } if (!self->indices_id) { glGenBuffers(1, &self->indices_id); } vsize = self->vertices->size * self->vertices->item_size; isize = self->indices->size * self->indices->item_size; // Always upload vertices first such that indices do not point to non // existing data (if we get interrupted in between for example). // Upload vertices glBindBuffer(GL_ARRAY_BUFFER, self->vertices_id); if (vsize != self->GPU_vsize) { glBufferData(GL_ARRAY_BUFFER, vsize, self->vertices->items, GL_DYNAMIC_DRAW); self->GPU_vsize = vsize; } else { glBufferSubData(GL_ARRAY_BUFFER, 0, vsize, self->vertices->items); } glBindBuffer(GL_ARRAY_BUFFER, 0); // Upload indices glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->indices_id); if (isize != self->GPU_isize) { glBufferData(GL_ELEMENT_ARRAY_BUFFER, isize, self->indices->items, GL_DYNAMIC_DRAW); self->GPU_isize = isize; } else { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, isize, self->indices->items); } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } // ---------------------------------------------------------------------------- void vertex_buffer_clear(vertex_buffer_t *self) { assert(self); self->state = FROZEN; vector_clear(self->indices); vector_clear(self->vertices); vector_clear(self->items); self->state = DIRTY; } // ---------------------------------------------------------------------------- void vertex_buffer_render_setup(vertex_buffer_t *self, GLenum mode) { size_t i; #ifdef FREETYPE_GL_USE_VAO // Unbind so no existing VAO-state is overwritten, // (e.g. the GL_ELEMENT_ARRAY_BUFFER-binding). glBindVertexArray(0); #endif if (self->state != CLEAN) { vertex_buffer_upload(self); self->state = CLEAN; } #ifdef FREETYPE_GL_USE_VAO if (self->VAO_id == 0) { // Generate and set up VAO glGenVertexArrays(1, &self->VAO_id); glBindVertexArray(self->VAO_id); glBindBuffer(GL_ARRAY_BUFFER, self->vertices_id); for (i = 0; i < MAX_VERTEX_ATTRIBUTE; ++i) { vertex_attribute_t *attribute = self->attributes[i]; if (attribute == 0) { continue; } else { vertex_attribute_enable(attribute); } } glBindBuffer(GL_ARRAY_BUFFER, 0); if (self->indices->size) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->indices_id); } } // Bind VAO for drawing glBindVertexArray(self->VAO_id); #else glBindBuffer(GL_ARRAY_BUFFER, self->vertices_id); for (i = 0; i < MAX_VERTEX_ATTRIBUTE; ++i) { vertex_attribute_t *attribute = self->attributes[i]; if (attribute == 0) { continue; } else { vertex_attribute_enable(attribute); } } if (self->indices->size) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->indices_id); } #endif self->mode = mode; } // ---------------------------------------------------------------------------- void vertex_buffer_render_finish(vertex_buffer_t *self) { #ifdef FREETYPE_GL_USE_VAO glBindVertexArray(0); #else int i; for (i = 0; i < MAX_VERTEX_ATTRIBUTE; ++i) { vertex_attribute_t *attribute = self->attributes[i]; if (attribute == 0) { continue; } else { glDisableVertexAttribArray(attribute->index); } } glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #endif } // ---------------------------------------------------------------------------- void vertex_buffer_render_item(vertex_buffer_t *self, size_t index) { ivec4 *item = (ivec4 *) vector_get(self->items, index); assert(self); assert(index < vector_size(self->items)); if (self->indices->size) { size_t start = item->istart; size_t count = item->icount; glDrawElements(self->mode, count, GL_UNSIGNED_INT, (void *) (start * sizeof(GLuint))); } else if (self->vertices->size) { size_t start = item->vstart; size_t count = item->vcount; glDrawArrays(self->mode, start * self->vertices->item_size, count); } } // ---------------------------------------------------------------------------- void vertex_buffer_render(vertex_buffer_t *self, GLenum mode) { size_t vcount = self->vertices->size; size_t icount = self->indices->size; vertex_buffer_render_setup(self, mode); if (icount) { glDrawElements(mode, icount, GL_UNSIGNED_INT, 0); } else { glDrawArrays(mode, 0, vcount); } vertex_buffer_render_finish(self); } // ---------------------------------------------------------------------------- void vertex_buffer_push_back_indices(vertex_buffer_t *self, const GLuint *indices, const size_t icount) { assert(self); self->state |= DIRTY; vector_push_back_data(self->indices, indices, icount); } // ---------------------------------------------------------------------------- void vertex_buffer_push_back_vertices(vertex_buffer_t *self, const void *vertices, const size_t vcount) { assert(self); self->state |= DIRTY; vector_push_back_data(self->vertices, vertices, vcount); } // ---------------------------------------------------------------------------- void vertex_buffer_insert_indices(vertex_buffer_t *self, const size_t index, const GLuint *indices, const size_t count) { assert(self); assert(self->indices); assert(index < self->indices->size + 1); self->state |= DIRTY; vector_insert_data(self->indices, index, indices, count); } // ---------------------------------------------------------------------------- void vertex_buffer_insert_vertices(vertex_buffer_t *self, const size_t index, const void *vertices, const size_t vcount) { size_t i; assert(self); assert(self->vertices); assert(index < self->vertices->size + 1); self->state |= DIRTY; for (i = 0; i < self->indices->size; ++i) { if (*(GLuint *) (vector_get(self->indices, i)) > index) { *(GLuint *) (vector_get(self->indices, i)) += index; } } vector_insert_data(self->vertices, index, vertices, vcount); } // ---------------------------------------------------------------------------- void vertex_buffer_erase_indices(vertex_buffer_t *self, const size_t first, const size_t last) { assert(self); assert(self->indices); assert(first < self->indices->size); assert((last) <= self->indices->size); self->state |= DIRTY; vector_erase_range(self->indices, first, last); } // ---------------------------------------------------------------------------- void vertex_buffer_erase_vertices(vertex_buffer_t *self, const size_t first, const size_t last) { size_t i; assert(self); assert(self->vertices); assert(first < self->vertices->size); assert(last <= self->vertices->size); assert(last > first); self->state |= DIRTY; for (i = 0; i < self->indices->size; ++i) { if (*(GLuint *) (vector_get(self->indices, i)) > first) { *(GLuint *) (vector_get(self->indices, i)) -= (last - first); } } vector_erase_range(self->vertices, first, last); } // ---------------------------------------------------------------------------- size_t vertex_buffer_push_back(vertex_buffer_t *self, const void *vertices, const size_t vcount, const GLuint *indices, const size_t icount) { return vertex_buffer_insert(self, vector_size(self->items), vertices, vcount, indices, icount); } // ---------------------------------------------------------------------------- size_t vertex_buffer_insert(vertex_buffer_t *self, const size_t index, const void *vertices, const size_t vcount, const GLuint *indices, const size_t icount) { size_t vstart, istart, i; ivec4 item; assert(self); assert(vertices); assert(indices); self->state = FROZEN; // Push back vertices vstart = vector_size(self->vertices); vertex_buffer_push_back_vertices(self, vertices, vcount); // Push back indices istart = vector_size(self->indices); vertex_buffer_push_back_indices(self, indices, icount); // Update indices within the vertex buffer for (i = 0; i < icount; ++i) { *(GLuint *) (vector_get(self->indices, istart + i)) += vstart; } // Insert item item.x = vstart; item.y = vcount; item.z = istart; item.w = icount; vector_insert(self->items, index, &item); self->state = DIRTY; return index; } // ---------------------------------------------------------------------------- void vertex_buffer_erase(vertex_buffer_t *self, const size_t index) { ivec4 *item; int vstart; size_t vcount, istart, icount, i; assert(self); assert(index < vector_size(self->items)); item = (ivec4 *) vector_get(self->items, index); vstart = item->vstart; vcount = item->vcount; istart = item->istart; icount = item->icount; // Update items for (i = 0; i < vector_size(self->items); ++i) { ivec4 *item = (ivec4 *) vector_get(self->items, i); if (item->vstart > vstart) { item->vstart -= vcount; item->istart -= icount; } } self->state = FROZEN; vertex_buffer_erase_indices(self, istart, istart + icount); vertex_buffer_erase_vertices(self, vstart, vstart + vcount); vector_erase(self->items, index); self->state = DIRTY; }