/* Freetype GL - A C OpenGL Freetype engine * * Distributed under the OSI-approved BSD 2-Clause License. See accompanying * file `LICENSE` for more details. */ #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; iattributes[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; iattributes[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; iattributes[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; iattributes[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; iattributes[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; iattributes[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; iindices->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; iindices->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; iindices, 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; iitems); ++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; }