RTS Voxel Engine using LWJGL - Textures glitching

Posted by Dieter Hubau on Game Development See other posts from Game Development or by Dieter Hubau
Published on 2013-11-07T17:42:31Z Indexed on 2013/11/07 22:23 UTC
Read the original article Hit count: 506

Filed under:
|
|
|

I'm currently working on an RTS game engine using voxels.

I have implemented a basic chunk manager using an Octree of Octrees which contains my voxels (simple square blocks, as in Minecraft). I'm using a Voronoi-based terrain generation to get a simplistic yet relatively realistic heightmap.

I have no problem showing a 256*256*256 grid of voxels with a decent framerate (>250), because of frustum culling, face culling and only rendering visible blocks. For example, in a random voxel grid of 256*256*256 I generally only render 100k-120k faces, not counting frustum culling. Frustum culling is only called every 100ms, since calling it every frame seemed a bit overkill.

Now I have reached the stage of texturing and I'm experiencing some problems:

Map of 256*256*256 voxels - different types of blocks by height

Some experienced people might already see the problem, but if we zoom in, you can see the glitches more clearly:

Textures glitch and show errors

All the seams between my blocks are glitching and kind of 'overlapping' or something. It's much more visible when you're moving around. I'm using a single, simple texture map to draw on my cubes, where each texture is 16*16 pixels big:

16 by 16 pixel textures in one simple texture map

I have added black edges around the textures to get a kind of cellshaded look, I think it's cool. The texture map has 256 textures of each 16*16 pixels, meaning the total size of my texture map is 256*256 pixels.

The code to update the ChunkManager:

public void update(ChunkManager chunkManager) {
    for (Octree<Cube> chunk : chunks) {
        if (chunk.getId() < 0) {
            // generate an id for the chunk to be able to call it later
            chunk.setId(glGenLists(1));
        }

        glNewList(chunk.getId(), GL_COMPILE);
        glBegin(GL_QUADS);

        faces += renderChunk(chunk);

        glEnd();
        glEndList();
    }
}

Where my renderChunk method is:

private int renderChunk(Octree<Cube> node) {
    // keep track of the number of visible faces in this chunk
    int faces = 0;

    if (!node.isEmpty()) {
        if (node.isLeaf()) {
            faces += renderItem(node);
    }

    List<Octree<Cube>> children = node.getChildren();

    if (children != null && !children.isEmpty()) {
        for (Octree<Cube> child : children) {
            faces += renderChunk(child);
        }
    }

    return faces;
}

Where my renderItem method is the following:

private int renderItem(Octree<Cube> node) {
    Cube cube = node.getItem(-1, -1, -1);
    int faces = 0;

    float x = node.getPosition().x;
    float y = node.getPosition().y;
    float z = node.getPosition().z;

    float size = cube.getSize();

    Vector3f point1 = new Vector3f(-size + x, -size + y, size + z);
    Vector3f point2 = new Vector3f(-size + x, size + y, size + z);
    Vector3f point3 = new Vector3f(size + x, size + y, size + z);
    Vector3f point4 = new Vector3f(size + x, -size + y, size + z);
    Vector3f point5 = new Vector3f(-size + x, -size + y, -size + z);
    Vector3f point6 = new Vector3f(-size + x, size + y, -size + z);
    Vector3f point7 = new Vector3f(size + x, size + y, -size + z);
    Vector3f point8 = new Vector3f(size + x, -size + y, -size + z);

    TextureCoordinates tc = textureManager.getTextureCoordinates(cube.getCubeType());

    // front face
    if (cube.isVisible(CubeSide.FRONT)) {
        faces++;

        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u], TEXTURE_V_COORDINATES[tc.v]);
        glVertex3f(point1.x, point1.y, point1.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u + 1], TEXTURE_V_COORDINATES[tc.v]);
        glVertex3f(point4.x, point4.y, point4.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u + 1], TEXTURE_V_COORDINATES[tc.v + 1]);
        glVertex3f(point3.x, point3.y, point3.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u], TEXTURE_V_COORDINATES[tc.v + 1]);
        glVertex3f(point2.x, point2.y, point2.z);
    }

    // back face
    if (cube.isVisible(CubeSide.BACK)) {
        faces++;

        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u + 1], TEXTURE_V_COORDINATES[tc.v]);
        glVertex3f(point5.x, point5.y, point5.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u + 1], TEXTURE_V_COORDINATES[tc.v + 1]);
        glVertex3f(point6.x, point6.y, point6.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u], TEXTURE_V_COORDINATES[tc.v + 1]);
        glVertex3f(point7.x, point7.y, point7.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u], TEXTURE_V_COORDINATES[tc.v]);
        glVertex3f(point8.x, point8.y, point8.z);
    }

    // left face
    if (cube.isVisible(CubeSide.SIDE_LEFT)) {
        faces++;

        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u], TEXTURE_V_COORDINATES[tc.v]);
        glVertex3f(point5.x, point5.y, point5.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u + 1], TEXTURE_V_COORDINATES[tc.v]);
        glVertex3f(point1.x, point1.y, point1.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u + 1], TEXTURE_V_COORDINATES[tc.v + 1]);
        glVertex3f(point2.x, point2.y, point2.z);
        glTexCoord2f(TEXTURE_U_COORDINATES[tc.u], TEXTURE_V_COORDINATES[tc.v + 1]);
        glVertex3f(point6.x, point6.y, point6.z);
    }

    // ETC ETC

    return faces;
}

When all this is done, I simply render my lists every frame, like this:

public void render(ChunkManager chunkManager) {
    glBindTexture(GL_TEXTURE_2D, textureManager.getCubeTextureId());

    // load all chunks from the tree
    List<Octree<Cube>> chunks = chunkManager.getTree().getAllItems();
    for (Octree<Cube> chunk : chunks) {
        if (frustum.cubeInFrustum(chunk.getPosition(), chunk.getSize() / 2)) {
            glCallList(chunk.getId());
        }
    }
}

I don't know if anyone is willing to go through all of this code or maybe you can spot the problem right away, but that is basically the problem, and I can't find a solution :-)

Thanks for reading and any help is appreciated!

© Game Development or respective owner

Related posts about opengl

Related posts about java