So I realize there are many better ways to do this in modern OpenGL. But here's what I have:
class World
....
function render() {
foreach(chunk)
if (chunk.isReady && chunk.isVisible) {
chunk.render()
}
}
class WorldChunk
public int[][][] blocks;
...
function render() {
if (DISPLAY_LIST != 0) {
glCallList(DISPLAY_LIST);
} else {
buildDisplayList();
}
}
function buildDisplayList() {
int list = GL11.glGenLists(1);
GL11.glNewList(list, GL11.GL_COMPILE);
pushVertexData();
GL11.glEndList();
}
function pushVertexData() {
glPushMatrix();
glTranslatef(x,y,z);
glBindBuffer(GL_ARRAY_BUFFER, Block.vboVertexHandle); //From a single static vbo stored in Block class - VBO built once on startup
glVertexPointer(3, GL_FLOAT, 0, 0L);
glBindBuffer(GL_ARRAY_BUFFER, Block.vboNormalHandle); //From a single static vbo stored in Block class - VBO built once on startup
glNormalPointer(GL_FLOAT, 0, 0L);
GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
for (int i = 0; i < sizeX; i++) {
for (int j = 0; j < sizeY; j++) {
for (int k = 0; k < sizeZ; k++) {
//Determine exposed faces..
EXPOSED_FACES = determineExposedFaces(i,j,k);
//Contains boolean array [true, true, true, true, true, true] of faces to draw if they are not hidden
//
Block.render(i,j,k, EXPOSED_FACES);
}
}
}
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
}
class Block
...
function render(int x, int y, int z, int type, boolean[] faces) {
if (faces[0] || faces[1] || faces[2] || faces[3] || faces[4] || faces[5]) {
glPushMatrix();
glTranslatef(x + size, y + size, z + size);
if (faces[0]) {
glDrawElements(GL_TRIANGLES, frontIndicies);
}
if (faces[1]) {
glDrawElements(GL_TRIANGLES, rightIndicies);
}
if (faces[2]) {
glDrawElements(GL_TRIANGLES, topIndicies);
}
if (faces[3]) {
glDrawElements(GL_TRIANGLES, leftIndicies);
}
if (faces[4]) {
glDrawElements(GL_TRIANGLES, bottomIndicies);
}
if (faces[5]) {
glDrawElements(GL_TRIANGLES, backIndicies);
}
glPopMatrix();
}
}
As you can see, I'm basically just storing a single VBO of the cube and using indicies to decide which faces to actually draw. The problem is that I know this is not an optimal way to do things. I am currently:
1) Rendering only those chunks within +/- X, +/- Y units of the camera
2) Doing efficient frustum culling to decide which chunks to are outside the viewing area
3) Using display lists to "bake" a chunk's geometry
4) Using a single VBO for all cubes
In any given scene I might have only 20k-40k faces being drawn in actual on-GPU geometry, but even this is fairly slow. Even on a GTX 690, I can only sustain about 80fps with about 50k faces on frame.
I know I should switch to shaders and do this the true modern way, but is there a fundamental concept I'm abusing and causing such poor performance? I know that performance is not substantially consumed elsewhere in the code based on profiling the app. For example, I hold at 1650 fps if I just comment out each of the glDrawElements() calls above so nothing is drawn. I must be overwhelming the card with poorly scheduled draw calls on tiny vertex arrays...