go-gl/glh — pull request #17 by jteeuwen (2012-10-26 21:00:00+00:00)
Title: Major overhaul of Meshbuffer.

glh.MeshBuffer: Major overhaul of Meshbuffer.

Previously, the `MeshBuffer` was fixed to using `GL_UNSIGNED_INT` for indices and `GL_FLOAT` for all vertex data. Additionally, it always used 14 floats to represents a single vertex, regardless of whether any of them where ever used. This caused major space requirement overhead.

This new implementation accepts buffer attributes as part of the buffer constructor. These attributes define the exact type and size of each vertex component (position, color, tex coords, normals).

This allows us to explicitly define how big each vertex component should be and what data type it should carry. Additionally, we can define the storage method per component. The storage method being any of `GL_STATIC_DRAW`, `GL_DYNAMIC_DRAW` or `GL_STREAM_DRAW`. This gives fine-grained control over where each of the vertex components will be stored as far as the GPU is concerned.

This adds quite a bit of code to accommodate for the different component data types. Unfortunately, this is all necessary and is one of the few places where Go's type system really works against us. In C this would all have easily been done with void pointers. We could use `unsafe` casting magic here, but to be honest, that opens up a whole new can of worms, I do not want to open.

## Example

mb := NewMeshBuffer(
// Indices: 1 unsigned int per index, static data.
NewUint32Attr(1, gl.STATIC_DRAW),

// Positions: 2 integers, static data.
NewInt32Attr(2, gl.STATIC_DRAW),

// Colors: 3 floats, changing regularly.
NewFloat32Attr(3, gl.DYNAMIC_DRAW),

nil, // No vertex normals.
nil, // No texture coords.
)

This example creates a buffer which accepts 1 unsigned integer for each index. 2 floats for the vertex position (x/y) and 3 floats for the vertex color (r/g/b). By supplying nil for the normal and texture-coord attributes, we are telling the buffer to omit storage for these alltogether. These will therefor not allocate any gl buffers, nor any excess space in the MeshBuffer itself.

The example also demonstrates how the indices and positions use `GL_STATIC_DRAW`. This means we expect them to never change. They can therefore be stored directly in GPU memory. The color is expected to change, so we define it as `GL_DYNAMIC_DRAW`, which stores it in AGP memory.

Loading a mesh into this buffer us very simple:

idx := []uint32{0, 1, 2, 3}
pos := []int32{0, 0, 0, 0, 0, 0, 0, 0}
clr := []float32{1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1}
....
mb.Add(idx, pos, clr, nil, nil)

Note that we use the same data types for each component as was defined in the meshbuffer attributes. And since we told it there would be no texcoord and normal data, we supply nil for these two in the Add() call as well.

## Interleaving

Just like the previous version, this version uses separate buffers for each vertex component (indices, position, color, texcoords and normals). Initially I intended to merge them all into a single, interleaved buffer. I quickly realized this would turn into a major mess to maintain. Additionally, it would not allow us to specify the storage type for each component.

Consider the example above, where the indices and position data are static, and the colors are expected to change regularly. When putting all data into a single buffer, one change to a color would require us to upload the entire data set to the GPU. With this new implementation, that is not necessary. It is designed to notice when any of the data sets are modified and will then automatically re-commit them to the GPU
independently.

This additionally gets rid of some global vars and constants we no longer need, along with the original `Mesh` type as it is no longer necessary (nor was it ever, really). It renames `MeshDescriptor` to `Mesh`.

This will likely break Travis, because the example program needs to be updated to use the new API.