src/vertexbuffer.h
Vertex buffers
They are used to store the vertex coordinates, normals and colors computed by the the OpenGL commands typically used by draw.h.
These vertex buffers are the minimal information required to render the objects.
In combination with the tiny implementation this allows to generate geometries using OpenGL commands but without any OpenGL library.
struct {
// public
Array * position, * normal, * color, * index;
float modelview[16];
int type; // type = 0 -> lines, type = 1 -> mesh
int dim;
int vertex, nvertex;
bool visible; // visible = true > only traverse visible vertices
// private
int line_loop, lines, line_strip ;
int quads, polygon, fan;
int state;
} VertexBuffer = {
.visible = false, // traverse all vertices by default
.modelview = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }
};
static void vertex_buffer_push_index (unsigned int i)
{
i -= VertexBuffer.vertex;
array_append (VertexBuffer.index, &i, sizeof(unsigned int));
}
void vertex_buffer_setup()
{
VertexBuffer.nvertex = 0;
VertexBuffer.type = -1;
VertexBuffer.dim = -1;
VertexBuffer.position = array_new();
VertexBuffer.normal = array_new();
VertexBuffer.color = array_new();
VertexBuffer.index = array_new();
}
void vertex_buffer_free()
{
array_free (VertexBuffer.position);
VertexBuffer.position = NULL;
array_free (VertexBuffer.normal);
VertexBuffer.normal = NULL;
array_free (VertexBuffer.color);
VertexBuffer.color = NULL;
array_free (VertexBuffer.index);
VertexBuffer.index = NULL;
}
static void vertex_buffer_glBegin (unsigned int state)
{
if (VertexBuffer.index) {
glGetFloatv (GL_MODELVIEW_MATRIX, VertexBuffer.modelview);
bview * view = get_view();
float q[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
- view->tx, - view->ty, 3, 1 };
matrix_multiply (q, VertexBuffer.modelview);
for (int i = 0; i < 16; i++)
VertexBuffer.modelview[i] = q[i];
gl_build_rotmatrix ((float (*)[4])q, view->quat);
swap (float, q[1], q[4]);
swap (float, q[2], q[8]);
swap (float, q[6], q[9]);
matrix_multiply (q, VertexBuffer.modelview);
for (int i = 0; i < 16; i++)
VertexBuffer.modelview[i] = q[i];
VertexBuffer.state = state;
switch (state) {
case GL_LINE_LOOP:
VertexBuffer.line_loop = VertexBuffer.nvertex;
break;
case GL_LINES:
VertexBuffer.lines = VertexBuffer.nvertex;
break;
case GL_LINE_STRIP:
VertexBuffer.line_strip = VertexBuffer.nvertex;
break;
case GL_QUADS:
VertexBuffer.quads = VertexBuffer.nvertex;
break;
case GL_POLYGON:
VertexBuffer.polygon = VertexBuffer.nvertex;
break;
case GL_TRIANGLE_FAN:
VertexBuffer.fan = VertexBuffer.nvertex;
break;
default:
fprintf (stderr, "glBegin (%d) not implemented yet\n", state);
break;
}
}
else
glBegin (state);
}
static void vertex_buffer_glEnd()
{
if (VertexBuffer.index) {
int type = -1;
switch (VertexBuffer.state) {
case GL_LINE_LOOP:
for (int i = VertexBuffer.line_loop; i < VertexBuffer.nvertex - 1; i++) {
vertex_buffer_push_index (i);
vertex_buffer_push_index (i + 1);
}
vertex_buffer_push_index (VertexBuffer.nvertex - 1);
vertex_buffer_push_index (VertexBuffer.line_loop);
type = 0;
break;
case GL_LINES:
for (int i = VertexBuffer.lines; i < VertexBuffer.nvertex; i += 2) {
vertex_buffer_push_index (i);
vertex_buffer_push_index (i + 1);
}
type = 0;
break;
case GL_LINE_STRIP:
for (int i = VertexBuffer.line_strip; i < VertexBuffer.nvertex - 1; i++) {
vertex_buffer_push_index (i);
vertex_buffer_push_index (i + 1);
}
type = 0;
break;
case GL_QUADS:
for (int i = VertexBuffer.quads; i < VertexBuffer.nvertex; i += 4)
for (int j = 1; j <= 2; j++) {
vertex_buffer_push_index (i);
vertex_buffer_push_index (i + j);
vertex_buffer_push_index (i + j + 1);
}
type = 1;
break;
case GL_POLYGON:
for (int j = 1; j <= VertexBuffer.nvertex - VertexBuffer.polygon - 2;
j++) {
vertex_buffer_push_index (VertexBuffer.polygon);
vertex_buffer_push_index (VertexBuffer.polygon + j);
vertex_buffer_push_index (VertexBuffer.polygon + j + 1);
}
type = 1;
break;
case GL_TRIANGLE_FAN:
for (int i = VertexBuffer.fan + 1; i < VertexBuffer.nvertex - 1; i++) {
vertex_buffer_push_index (VertexBuffer.fan);
vertex_buffer_push_index (i);
vertex_buffer_push_index (i + 1);
}
type = 1;
break;
default:
break;
}
VertexBuffer.state = 0;
if (VertexBuffer.type >= 0 && type >= 0) {
// cannot mix lines and surfaces primitives
assert (VertexBuffer.type == type);
}
else
VertexBuffer.type = type;
}
else
glEnd();
}
static void vertex_buffer_glColor3f (float r, float g, float b)
{
if (VertexBuffer.color) {
struct { float x, y, z; } color = {r, g, b}; // fixme: use r,g,b directly
array_append (VertexBuffer.color, &color, 3*sizeof(float));
}
else
glColor3f (r, g, b);
}
static void vertex_buffer_glNormal3d (double nx, double ny, double nz)
{
if (VertexBuffer.normal) {
struct { float x, y, z; } normal = {nx, ny, nz};
array_append (VertexBuffer.normal, &normal, 3*sizeof(float));
}
else
glNormal3d (nx, ny, nz);
}
static void vertex_buffer_glVertex3d (double x, double y, double z)
{
if (VertexBuffer.position) {
if (VertexBuffer.dim < 3)
VertexBuffer.dim = 3;
float v[4] = {x, y, z, 1.};
vector_multiply (v, VertexBuffer.modelview);
array_append (VertexBuffer.position, v, 3*sizeof(float));
VertexBuffer.nvertex++;
}
else
glVertex3d (x, y, z);
}
static void vertex_buffer_glVertex2d (double x, double y)
{
if (VertexBuffer.position) {
if (VertexBuffer.dim < 2)
VertexBuffer.dim = 2;
float v[4] = {x, y, 0, 1.};
vector_multiply (v, VertexBuffer.modelview);
array_append (VertexBuffer.position, v, 3*sizeof(float));
VertexBuffer.nvertex++;
}
else
glVertex3d (x, y, 0.);
}
Here we overload the default OpenGL commands, in order to call the corresponding vertex buffer operations defined above.
#define glBegin vertex_buffer_glBegin
#define glEnd vertex_buffer_glEnd
#define glVertex2d vertex_buffer_glVertex2d
#define glVertex2f vertex_buffer_glVertex2d
#define glVertex3d vertex_buffer_glVertex3d
#define glColor3f vertex_buffer_glColor3f
#define glNormal3d vertex_buffer_glNormal3d