Hey guys, I've been working on some marching cubes, and I've been having some weird issues that I really can't seem to understand. Basically the mesh isn't forming properly. Here are some screenshots and the code as it will show you the issue quicker than me describing it.
As you can see the cube kinda forms but some areas are messed up, Here is the relevant code.
package voxels;
import java.util.ArrayList;
import java.util.List;
import javax.swing.plaf.basic.BasicTreeUI.TreeCancelEditingAction;
import org.lwjgl.util.vector.Vector3f;
import models.MarchingCubesCases;
import toolbox.UniversalFunctions;
public class MeshGenerator {
private static int[][] vertexOffset = new int[][] { { 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 }, { 0, 0, 1 },
{ 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 } };
private static float target = 0.5f;
public static Voxel[][][] generateVoxels(int size) {
Voxel[][][] voxels = new Voxel[size][size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
for (int k = 0; k < size; k++) {
voxels[i][j][k] = new Voxel(VoxelMaterial.Grass);
}
}
}
return voxels;
}
public static MeshData generateMeshMarchingCubes(Voxel[][][] voxels) {
List<Vector3f> verts = new ArrayList<Vector3f>();
List<Float> normals = new ArrayList<Float>();
List<Integer> indices = new ArrayList<Integer>();
List<Float> textureCoords = new ArrayList<Float>();
for (int x = 0; x < voxels.length; x++) {
for (int y = 0; y < voxels.length; y++) {
for (int z = 0; z < voxels.length; z++) {
if (voxels[x][y][z].getMaterial() == VoxelMaterial.Air) {
continue;
}
float[] cube = new float[8];
if (checkIfExpose(voxels, x, y, z, cube) == false) {
continue;
}
MarchCube(new Vector3f(x, y, z), cube, verts, indices);
normals.add(0f);
normals.add(1f);
normals.add(0f);
textureCoords.add(0f);
textureCoords.add(1f);
}
}
}
MeshData data = new MeshData();
data.setIndices(UniversalFunctions.intListToArray(indices));
data.setVertsFromVector3f(verts);
data.setNormals(UniversalFunctions.floatListToArray(normals));
data.setTextureCoords(UniversalFunctions.floatListToArray(textureCoords));
return data;
}
private static boolean checkIfExpose(Voxel[][][] voxels, int x, int y, int z, float[] cube) {
Voxel[] surroundingVoxels = getSurroundingVoxels(voxels, x, y, z);
boolean bool = false;
for (int i = 0; i < cube.length; i++) {
cube[i] = 1;
}
if (surroundingVoxels[0].getMaterial().equals(VoxelMaterial.Air)) {
cube[0] = 0;
cube[1] = 0;
cube[4] = 0;
cube[5] = 0;
bool = true;
}
if (surroundingVoxels[1].getMaterial().equals(VoxelMaterial.Air)) {
cube[0] = 0;
cube[3] = 0;
cube[4] = 0;
cube[7] = 0;
bool = true;
}
if (surroundingVoxels[2].getMaterial().equals(VoxelMaterial.Air)) {
cube[4] = 0;
cube[5] = 0;
cube[6] = 0;
cube[7] = 0;
bool = true;
}
if (surroundingVoxels[3].getMaterial().equals(VoxelMaterial.Air)) {
cube[0] = 0;
cube[1] = 0;
cube[2] = 0;
cube[3] = 0;
bool = true;
}
if (surroundingVoxels[4].getMaterial().equals(VoxelMaterial.Air)) {
cube[1] = 0;
cube[2] = 0;
cube[5] = 0;
cube[6] = 0;
bool = true;
}
if (surroundingVoxels[5].getMaterial().equals(VoxelMaterial.Air)) {
cube[2] = 0;
cube[3] = 0;
cube[6] = 0;
cube[7] = 0;
bool = true;
}
return bool;
}
private static Voxel[] getSurroundingVoxels(Voxel[][][] voxels, int x, int y, int z) {
int maxLength = voxels.length;
List<Voxel> voxelList = new ArrayList<Voxel>();
if (y - 1 > 0 && y - 1 < maxLength) {
voxelList.add(voxels[x][y - 1][z]);
} else {
voxelList.add(new Voxel(VoxelMaterial.Air));
}
if (x - 1 > 0 && x - 1 < maxLength) {
voxelList.add(voxels[x - 1][y][z]);
} else {
voxelList.add(new Voxel(VoxelMaterial.Air));
}
if (z - 1 > 0 && z - 1 < maxLength) {
voxelList.add(voxels[x][y][z - 1]);
} else {
voxelList.add(new Voxel(VoxelMaterial.Air));
}
if (z + 1 > 0 && z + 1 < maxLength) {
voxelList.add(voxels[x][y][z + 1]);
} else {
voxelList.add(new Voxel(VoxelMaterial.Air));
}
if (x + 1 > 0 && x + 1 < maxLength) {
voxelList.add(voxels[x + 1][y][z]);
} else {
voxelList.add(new Voxel(VoxelMaterial.Air));
}
if (y + 1 > 0 && y + 1 < maxLength) {
voxelList.add(voxels[x][y + 1][z]);
} else {
voxelList.add(new Voxel(VoxelMaterial.Air));
}
return voxelList.toArray(new Voxel[voxelList.size()]);
}
// MarchCube performs the Marching Cubes algorithm on a single cube
private static void MarchCube(Vector3f pos, float[] cube, List<Vector3f> vertList, List<Integer> indexList) {
int i, j, vert, idx;
int flagIndex = 0;
float offset = 0.0f;
Vector3f[] edgeVertex = new Vector3f[12];
for (int inter = 0; inter < 12; inter++) {
edgeVertex[inter] = new Vector3f();
}
// Find which vertices are inside of the surface and which are outside
for (i = 0; i < 8; i++) {
if (cube[i] <= target) {
flagIndex |= 1 << i;
}
}
// Find which edges are intersected by the surface
int edgeFlags = MarchingCubesCases.cubeEdgeFlags[flagIndex];
// If the cube is entirely inside or outside of the surface, then there will be
// no intersections
if (edgeFlags == 0)
return;
// Find the point of intersection of the surface with each edge
for (i = 0; i < 12; i++) {
// if there is an intersection on this edge
if ((edgeFlags & (1 << i)) != 0) {
offset = GetOffset(cube[MarchingCubesCases.edgeConnection[i][0]],
cube[MarchingCubesCases.edgeConnection[i][1]]);
edgeVertex[i].x = pos.x + (vertexOffset[MarchingCubesCases.edgeConnection[i][0]][0]
+ offset * MarchingCubesCases.edgeDirection[i][0]);
edgeVertex[i].y = pos.y + (vertexOffset[MarchingCubesCases.edgeConnection[i][0]][1]
+ offset * MarchingCubesCases.edgeDirection[i][1]);
edgeVertex[i].z = pos.z + (vertexOffset[MarchingCubesCases.edgeConnection[i][0]][2]
+ offset * MarchingCubesCases.edgeDirection[i][2]);
}
}
// Save the triangles that were found. There can be up to five per cube
for (i = 0; i < 5; i++) {
if (MarchingCubesCases.triangleConnectionTable[flagIndex][3 * i] < 0) {
break;
}
idx = vertList.size();
for (j = 0; j < 3; j++) {
vert = MarchingCubesCases.triangleConnectionTable[flagIndex][3 * i + j];
indexList.add(idx + MarchingCubesCases.windingOrder[j]);
vertList.add(edgeVertex[vert]);
}
}
}
// GetOffset finds the approximate point of intersection of the surface
// between two points with the values v1 and v2
private static float GetOffset(float v1, float v2) {
float delta = v2 - v1;
return (delta == 0.0f) ? 0.5f : (target - v1) / delta;
}
}
I can attach the look up tables if needed. The MeshData class is just a class to hold verts, indices, etc. The voxel data is generated using the generateVoxels method and it is for a size of 16. Finally the voxel material only maters if the material is air as then the voxel will need to be rendered. Any help would be amazing.
Thanks
Euan