651 lines
16 KiB
C
651 lines
16 KiB
C
/* Freetype GL - A C OpenGL Freetype engine
|
|
*
|
|
* Distributed under the OSI-approved BSD 2-Clause License. See accompanying
|
|
* file `LICENSE` for more details.
|
|
*/
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#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;
|
|
}
|