I wanted to create a destroyable world, so I used Java + OpenGL to render my world and made a quick test with the Marching Cubes Algorithm:
private static void processChunk(Chunk chunk) {
for (int x = 0; x < chunkSize - 1; x++) {
for (int y = 0; y < chunkSize - 1; y++) {
for (int z = 0; z < chunkSize - 1; z++) {
float[] cubeValues = {
chunk.getValue(x, y, z),
chunk.getValue(x + 1, y, z),
chunk.getValue(x, y + 1, z),
chunk.getValue(x + 1, y + 1, z),
chunk.getValue(x, y, z + 1),
chunk.getValue(x + 1, y, z + 1),
chunk.getValue(x, y + 1, z + 1),
chunk.getValue(x + 1, y + 1, z + 1)
};
Vector3f[] cubeOrigins = {
new Vector3f(chunk.getOrigin().x + x * voxelSize, chunk.getOrigin().y + y * voxelSize, chunk.getOrigin().z + z * voxelSize),
new Vector3f(chunk.getOrigin().x + x * voxelSize + voxelSize, chunk.getOrigin().y + y * voxelSize, chunk.getOrigin().z + z * voxelSize),
new Vector3f(chunk.getOrigin().x + x * voxelSize, chunk.getOrigin().y + y * voxelSize + voxelSize, chunk.getOrigin().z + z * voxelSize),
new Vector3f(chunk.getOrigin().x + x * voxelSize + voxelSize, chunk.getOrigin().y + y * voxelSize + voxelSize, chunk.getOrigin().z + z * voxelSize),
new Vector3f(chunk.getOrigin().x + x * voxelSize, chunk.getOrigin().y + y * voxelSize, chunk.getOrigin().z + z * voxelSize + voxelSize),
new Vector3f(chunk.getOrigin().x + x * voxelSize + voxelSize, chunk.getOrigin().y + y * voxelSize, chunk.getOrigin().z + z * voxelSize + voxelSize),
new Vector3f(chunk.getOrigin().x + x * voxelSize, chunk.getOrigin().y + y * voxelSize + voxelSize, chunk.getOrigin().z + z * voxelSize + voxelSize),
new Vector3f(chunk.getOrigin().x + x * voxelSize + voxelSize, chunk.getOrigin().y + y * voxelSize + voxelSize, chunk.getOrigin().z + z * voxelSize + voxelSize)
};
int cubeIndex = 0;
for (int i = 0; i < 8; i++) {
if (cubeValues[i] > 0) {
cubeIndex |= 1 << i;
}
}
cubeIndex = x+y+z;
int edgeMask = EdgeMasks[cubeIndex];
if (edgeMask != 0) {
Vector3f[] edgeVertices = new Vector3f[12];
for (int i = 0; i < 12; i++) {
if ((edgeMask & (1 << i)) != 0) {
edgeVertices[i] = interpolate(cubeOrigins[EdgeVertexIndices[i][0]], cubeOrigins[EdgeVertexIndices[i][1]], cubeValues[EdgeVertexIndices[i][0]], cubeValues[EdgeVertexIndices[i][1]]);
}
}
for (int i = 0; TriangleTable[cubeIndex][i] != -1; i++) {
Vector3f tempPos = edgeVertices[TriangleTable[cubeIndex][i]];
if (positionIndices.containsKey(tempPos)) {
indices.add(positionIndices.get(tempPos));
} else {
positions.add(tempPos);
positionIndices.put(tempPos, positions.size() - 1);
indices.add(positions.size() - 1);
}
//Vector3f normal = calculateNormal(tempPos, cube, cubeIndex);
normals.add(new Vector3f(0, 0, 0));
}
}
}
}
}
}
public static Vector3f interpolate(Vector3f p0, Vector3f p1, float val0, float val1) {
return p1.lerp(p0, -val1 / (val0 - val1));
}
Vertex and edge layout:
6 7
+-------------+ +-----6-------+
/ | / | / | /|
/ | / | 11 7 10 5
2 +-----+-------+ 3 | +-----+2------+ |
| 4 +-------+-----+ 5 | +-----4-+-----+
| / | / 3 8 1 9
| / | / | / | /
0 +-------------+ 1 +------0------+
Interestingly, some cases seem not to be working as intended or not at all. I found that the cases 16 (4,8,7), 32 (9,4,5), 64 (6,7,11) and 128 (10,5,6) produce no triangles even though they are supposed to, and that the cases 48 (8,7,9,9,7,5) and 243 (10,3,11,1,3,10) behave some, in my opinion, very strange. (There could be more, I did not try every case.) In the wireframe view however, cases 16,32,64,128 are normal.
Triangle view:
Wireframe view:
Basic terrain for reference: