As promised, here are some code snippets for the visualiser. First up, assemble the vertex and normals buffers.

Constructing the geometry

In my case, I’ve already read the data into Triangle objects which hold their vertices and normals. I rearrange things into FloatBuffer objects

Collection<Triangle> triangles= Seer.triangles;
scale= (float)(1.0/Seer.maxSubsSize);

    vertexCount= triangles.size()*3; // three vertices per triangle
    vertices= BufferUtil.newFloatBuffer(vertexCount*3); // three coords per vertex
    normals= BufferUtil.newFloatBuffer(vertexCount*3); // one normal per vertex

    // add all triangles in the mesh to the vertex float buffer
    for(Iterator<Triangle> triIt= triangles.iterator(); triIt.hasNext(); ){

    Triangle triangle= triIt.next();
    double[] normal= triangle.getNormal();

    for(int i=0; i<3; i++){ // loop over vertices in the triangle
        double[] vertex= triangle.getVertex(i);

        for(int j=0; j<3; j++){ // loop over coords of the vertex
            vertices.put(substrateToGL(vertex[j], j)); // transform substrate space into openGL coords
            normals.put(-(float)normal[j]); // normals are (ahem) normalised and polar, so no transform
        }
    }
}
vertices.flip();
normals.flip();

I loop over the Collection of Triangles and re-parse everything into FloatBuffers for vertices an normals which are then flipped to reverse the order.

There are a couple of details to note: first, normal are repeated three times (once for each vertex), and second that I’ve flipped the normals. This turns out to be important for correct rendering in my case because the code that generates the meshes in the first place always makes them point inward.

Build the VBOs

Next up we need to generate and bind the buffers for the VBO. We need separate buffers for vertices and normals (we’d need separate ones for colour and texture data if we were doing that as well).

// Generate bnd bind the Vertex buffer
 gl.glGenBuffersARB(1, VBOVertices, 0); // Get A Valid Name
 gl.glBindBufferARB(GL.GL_ARRAY_BUFFER_ARB, VBOVertices[0]); // Bind The Buffer
 // Load The Data
 gl.glBufferDataARB(GL.GL_ARRAY_BUFFER_ARB, vertexCount * 3 * BufferUtil.SIZEOF_FLOAT, vertices, GL.GL_STATIC_DRAW_ARB);
// generate and bind the normals buffer
 gl.glGenBuffersARB(1, VBONormals, 0);
 gl.glBindBufferARB(GL.GL_ARRAY_BUFFER_ARB, VBONormals[0]);
 //load the normals data
 gl.glBufferDataARB(GL.GL_ARRAY_BUFFER_ARB, vertexCount * 3 * BufferUtil.SIZEOF_FLOAT, normals, GL.GL_STATIC_DRAW_ARB);

vertices = null;
normals = null;

So here we’ve generated buffer “names” (which are jut integer identifiers), bound the buffer to the data identifier and further bound the name to the data. After that we don’t need the original FloatBuffers any more and can free the memory.

Drawing the object

Now we’re all set, and just need to be able to render the mesh whenever we feel like it. I’ve added a method to my mesh object that renders it which looks like this.

// Enable Pointers 
gl.glEnableClientState(GL.GL_NORMAL_ARRAY); 
gl.glEnableClientState(GL.GL_VERTEX_ARRAY); 

gl.glDisable(GL.GL_COLOR_MATERIAL); 
gl.glDisable(GL.GL_TEXTURE_2D); 

gl.glBindBufferARB(GL.GL_ARRAY_BUFFER_ARB, this.VBONormals[0]); 
gl.glNormalPointer(GL.GL_FLOAT, 0, 0); 

gl.glBindBufferARB(GL.GL_ARRAY_BUFFER_ARB, this.VBOVertices[0]); 
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0); 

// Set The Vertex Pointer To The Vertex Buffer 
gl.glDrawArrays(GL.GL_TRIANGLES, 0, this.vertexCount); // Draw All Of The Triangles At Once 

// Disable Pointers 
gl.glDisableClientState(GL.GL_VERTEX_ARRAY);  // Disable Vertex Arrays 
gl.glDisableClientState(GL.GL_NORMAL_ARRAY); // Disable Normal Arrays

What turns out to be important here is that you do the vertices LAST. Activate the client state for normals before the vertices, specify the normals pointer before the vertices. Then make the call to gl.glDrawArrays to actually instruct the card to render. I also disable the client states vertices first (opposite order to enabling) which may not be essential, but does work.

And we’re done…

That about wraps it up. I’ve been able to render meshes with about a million triangles at upwards of 60fps and 5 million at around 30fps. The complete application links to the Camino simulation and renders diffusive dynamics restricted by the mesh. The simulation ends up running in a separate thread so that it doesn’t pull down the frame rate in the visualiser, and also renders a small displacement plot in the lower left corner that co-rotates with the main plot. It’s also got arcball rotation and mouse-wheel zoom.

Right, that’ll do i think. All comments gratefully received 🙂

Advertisements