2
\$\begingroup\$

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!

UPDATE: Okay, here's some more code, showing how I determine my texture coordinates. In the TextureManager I define the texturecoordinates for each block type:

private void setupTextureCoordinatesForCube(CubeType type) throws IOException {
    if (cubeTextureId < 0) {
        cubeTextureId = loadTexture(terrainTextureFileName);
    }

    TextureCoordinates textureCoordinates;

    switch (type) {
        case DIRT:
            textureCoordinates = new TextureCoordinates(2, 0, 2, 0, 2, 0);
            break;
        case GRASS:
            textureCoordinates = new TextureCoordinates(3, 0, 0, 0, 2, 0);
            break;
        default:
            textureCoordinates = new TextureCoordinates(1, 0, 1, 0, 1, 0);
    }

    this.cubeTextureCoordinates.put(type, textureCoordinates);
}

And these are the coordinate arrays I'm using to represent each separate texture:

private static final float[] TEXTURE_U_COORDINATES = { 0f, 0.0625f, 0.125f, 0.1875f, 0.25f, 0.3125f, 0.375f, 0.4375f, 0.5f, 0.5625f, 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, 0.9375f, 1.0f };

private static final float[] TEXTURE_V_COORDINATES = { 0f, 0.0625f, 0.125f, 0.1875f, 0.25f, 0.3125f, 0.375f,    0.4375f, 0.5f, 0.5625f, 0.625f, 0.6875f, 0.75f, 0.8125f, 0.875f, 0.9375f, 1.0f };
\$\endgroup\$
1
  • \$\begingroup\$ You may be aware that texture coordinates are normalized, and a complete texture ranges from 0,0 (top left) to 1,1 (bottom right). So, if your texture is 256x256 pixels, and you wish to use 16x16 for one cube, each cube will use range of 1/(256/16) = 0.0625 = 1/16, so the first cube has the 4 coordinates (0,0) (1/16, 0) (1/16, 1/16) and (0, 1/16). You could check this is correct by inserting a breakpoint and inspecting your coordinates. You could also try to render a single cube without glitches. \$\endgroup\$ Commented Nov 8, 2013 at 8:27

1 Answer 1

1
\$\begingroup\$

I have found my problem!

It appears it was a stupid mistake (aren't they always?).

I was adding every block with size of 1 unit and rendering it on coordinates [-size,+size] which means the actual size of the blocks were 2 units big... This bug must have slipped back into my code at a certain moment because I already fixed this once before.

My terrain looks like this now:

Correct terrain textures!

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .