Hey. I was working on a minecraft clone and I needed a greedy meshing algorithm. So I copied Roleary’s meshing algorithm that they posted.
void greedy() {
int i, j, k, l, w, h, u, v, n, side = 0;
final int[] x = new int []{0,0,0};
final int[] q = new int []{0,0,0};
final int[] du = new int[]{0,0,0};
final int[] dv = new int[]{0,0,0};
final Voxel[] mask = new Voxel[Constants.CHUNK_WIDTH * Constants.CHUNK_HEIGHT];
Voxel voxelFace, voxelFace1;
for (boolean backFace = true, b = false; b != backFace; backFace = backFace && b, b = !b) {
for(int d = 0; d < 3; d++) {
u = (d + 1) % 3;
v = (d + 2) % 3;
x[0] = 0;
x[1] = 0;
x[2] = 0;
q[0] = 0;
q[1] = 0;
q[2] = 0;
q[d] = 1;
if (d == 0) { side = backFace ? Constants.WEST : Constants.EAST; }
else if (d == 1) { side = backFace ? Constants.BOTTOM : Constants.TOP; }
else if (d == 2) { side = backFace ? Constants.SOUTH : Constants.NORTH; }
for(x[d] = -1; x[d] < Constants.CHUNK_WIDTH;) {
n = 0;
for(x[v] = 0; x[v] < Constants.CHUNK_HEIGHT; x[v]++) {
for(x[u] = 0; x[u] < Constants.CHUNK_WIDTH; x[u]++) {
if(x[0] + q[0] <= 63 && x[1] + q[1] <= 255 && x[2] + q[2] <= 63) {
voxelFace = (x[d] >= 0 ) ? getVoxel(x[0], x[1], x[2], side) : null;
voxelFace1 = (x[d] < Constants.CHUNK_WIDTH - 1) ? getVoxel(x[0] + q[0], x[1] + q[1], x[2] + q[2], side) : null;
mask[n++] = ((voxelFace != null && voxelFace1 != null && voxelFace.equals(voxelFace1)))
? null
: backFace ? voxelFace1 : voxelFace;
}
}
}
x[d]++;
n = 0;
for(j = 0; j < Constants.CHUNK_HEIGHT; j++) {
for(i = 0; i < Constants.CHUNK_WIDTH;) {
if(mask[n] != null) {
for(w = 1; i + w < Constants.CHUNK_WIDTH && mask[n + w] != null && mask[n + w].equals(mask[n]); w++) {}
boolean done = false;
for(h = 1; j + h < Constants.CHUNK_HEIGHT; h++) {
for(k = 0; k < w; k++) {
if(mask[n + k + h * Constants.CHUNK_WIDTH] == null || !mask[n + k + h * Constants.CHUNK_WIDTH].equals(mask[n])) { done = true; break; }
}
if(done) { break; }
}
if (!mask[n].transparent) {
x[u] = i;
x[v] = j;
du[0] = 0;
du[1] = 0;
du[2] = 0;
du[u] = w;
dv[0] = 0;
dv[1] = 0;
dv[2] = 0;
dv[v] = h;
quad(new Vector3f(x[0], x[1], x[2]),
new Vector3f(x[0] + du[0], x[1] + du[1], x[2] + du[2]),
new Vector3f(x[0] + du[0] + dv[0], x[1] + du[1] + dv[1], x[2] + du[2] + dv[2]),
new Vector3f(x[0] + dv[0], x[1] + dv[1], x[2] + dv[2]),
w,
h,
mask[n],
backFace);
}
for(l = 0; l < h; ++l) {
for(k = 0; k < w; ++k) { mask[n + k + l * Constants.CHUNK_WIDTH] = null; }
}
i += w;
n += w;
} else {
i++;
n++;
}
}
}
}
}
}
}
Voxel getVoxel(final int x, final int y, final int z, final int side) {
Voxel voxelFace = voxels[x][y][z];
voxelFace.side = side;
return voxelFace;
}
void quad(final Vector3f bottomLeft,
final Vector3f topLeft,
final Vector3f topRight,
final Vector3f bottomRight,
final int width,
final int height,
final Voxel voxel,
final boolean backFace) {
final Vector3f [] vertices = new Vector3f[4];
vertices[2] = topLeft.multLocal(Constants.VOXEL_SIZE);
vertices[3] = topRight.multLocal(Constants.VOXEL_SIZE);
vertices[0] = bottomLeft.multLocal(Constants.VOXEL_SIZE);
vertices[1] = bottomRight.multLocal(Constants.VOXEL_SIZE);
final int [] indexes = backFace ? new int[] { 2,0,1, 1,3,2 } : new int[]{ 2,3,1, 1,0,2 };
final float[] colorArray = new float[4*4];
for (int i = 0; i < colorArray.length; i+=4) {
/*
* Here I set different colors for quads depending on the "type" attribute, just
* so that the different groups of voxels can be clearly seen.
*
*/
if (voxel.type == BlockType.GRASS_BLOCK) {
colorArray[i] = 0.1f;
colorArray[i+1] = 0.3f;
colorArray[i+2] = 0.0f;
colorArray[i+3] = 1.0f;
} else {
colorArray[i] = 0.0f;
colorArray[i+1] = 0.0f;
colorArray[i+2] = 0.0f;
colorArray[i+3] = 0.0f;
}
}
Mesh mesh = new Mesh();
mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
mesh.setBuffer(Type.Color, 4, colorArray);
mesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indexes));
mesh.updateBound();
Geometry geo = new Geometry("ColoredMesh", mesh);
if(voxel.type == BlockType.AIR)geo.setCullHint(CullHint.Always);
Material mat = new Material(main.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.setBoolean("VertexColor", true);
/*
* To see the actual rendered quads rather than the wireframe, just comment outthis line.
*/
mat.getAdditionalRenderState().setWireframe(true);
//mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
geo.setMaterial(mat);
attachChild(geo);
this.setCullHint(CullHint.Never);
}
then I created a voxel class, chunk class and worldManager class:
Voxel:
package mygame;
public class Voxel {
public boolean transparent;
public int type;
public int side;
public boolean equals(final Voxel voxel) {
return voxel.transparent == this.transparent && voxel.type == this.type;
}
}
Chunk:
package mygame;
import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;
/**
*
* @author user
*/
public class Chunk extends Node {
WorldManager worldManager;
Main main;
NoiseGenerator noiseGen;
Vector3f loc;
Voxel[][][] voxels = new Voxel[Constants.CHUNK_WIDTH][Constants.CHUNK_HEIGHT][Constants.CHUNK_WIDTH];
public Chunk(Vector3f loc, WorldManager worldManager, Main main) {
this.worldManager = worldManager;
this.main = main;
this.noiseGen = worldManager.noiseGen;
this.loc = loc;
generate();
greedy();
this.setLocalTranslation(loc);
}
public void generate() {
for(int xx = 0; xx < Constants.CHUNK_WIDTH; xx++) {
for(int yy = 0; yy < Constants.CHUNK_HEIGHT; yy++) {
for(int zz = 0; zz < Constants.CHUNK_WIDTH; zz++) {
Voxel voxel = new Voxel();
if(yy <= (int) ((noiseGen.noise(xx + (int) loc.x, zz + (int) loc.z) + 1) * 7)) {
voxel.type = BlockType.GRASS_BLOCK;
} else voxel.type=BlockType.AIR;
voxels[xx][yy][zz] = voxel;
}
}
}
}
void greedy() {
int i, j, k, l, w, h, u, v, n, side = 0;
final int[] x = new int []{0,0,0};
final int[] q = new int []{0,0,0};
final int[] du = new int[]{0,0,0};
final int[] dv = new int[]{0,0,0};
final Voxel[] mask = new Voxel[Constants.CHUNK_WIDTH * Constants.CHUNK_HEIGHT];
Voxel voxelFace, voxelFace1;
for (boolean backFace = true, b = false; b != backFace; backFace = backFace && b, b = !b) {
for(int d = 0; d < 3; d++) {
u = (d + 1) % 3;
v = (d + 2) % 3;
x[0] = 0;
x[1] = 0;
x[2] = 0;
q[0] = 0;
q[1] = 0;
q[2] = 0;
q[d] = 1;
if (d == 0) { side = backFace ? Constants.WEST : Constants.EAST; }
else if (d == 1) { side = backFace ? Constants.BOTTOM : Constants.TOP; }
else if (d == 2) { side = backFace ? Constants.SOUTH : Constants.NORTH; }
for(x[d] = -1; x[d] < Constants.CHUNK_WIDTH;) {
n = 0;
for(x[v] = 0; x[v] < Constants.CHUNK_HEIGHT; x[v]++) {
for(x[u] = 0; x[u] < Constants.CHUNK_WIDTH; x[u]++) {
if(x[0] + q[0] <= 63 && x[1] + q[1] <= 255 && x[2] + q[2] <= 63) {
voxelFace = (x[d] >= 0 ) ? getVoxel(x[0], x[1], x[2], side) : null;
voxelFace1 = (x[d] < Constants.CHUNK_WIDTH - 1) ? getVoxel(x[0] + q[0], x[1] + q[1], x[2] + q[2], side) : null;
mask[n++] = ((voxelFace != null && voxelFace1 != null && voxelFace.equals(voxelFace1)))
? null
: backFace ? voxelFace1 : voxelFace;
}
}
}
x[d]++;
n = 0;
for(j = 0; j < Constants.CHUNK_HEIGHT; j++) {
for(i = 0; i < Constants.CHUNK_WIDTH;) {
if(mask[n] != null) {
for(w = 1; i + w < Constants.CHUNK_WIDTH && mask[n + w] != null && mask[n + w].equals(mask[n]); w++) {}
boolean done = false;
for(h = 1; j + h < Constants.CHUNK_HEIGHT; h++) {
for(k = 0; k < w; k++) {
if(mask[n + k + h * Constants.CHUNK_WIDTH] == null || !mask[n + k + h * Constants.CHUNK_WIDTH].equals(mask[n])) { done = true; break; }
}
if(done) { break; }
}
if (!mask[n].transparent) {
x[u] = i;
x[v] = j;
du[0] = 0;
du[1] = 0;
du[2] = 0;
du[u] = w;
dv[0] = 0;
dv[1] = 0;
dv[2] = 0;
dv[v] = h;
quad(new Vector3f(x[0], x[1], x[2]),
new Vector3f(x[0] + du[0], x[1] + du[1], x[2] + du[2]),
new Vector3f(x[0] + du[0] + dv[0], x[1] + du[1] + dv[1], x[2] + du[2] + dv[2]),
new Vector3f(x[0] + dv[0], x[1] + dv[1], x[2] + dv[2]),
w,
h,
mask[n],
backFace);
}
for(l = 0; l < h; ++l) {
for(k = 0; k < w; ++k) { mask[n + k + l * Constants.CHUNK_WIDTH] = null; }
}
i += w;
n += w;
} else {
i++;
n++;
}
}
}
}
}
}
}
Voxel getVoxel(final int x, final int y, final int z, final int side) {
Voxel voxelFace = voxels[x][y][z];
voxelFace.side = side;
return voxelFace;
}
void quad(final Vector3f bottomLeft,
final Vector3f topLeft,
final Vector3f topRight,
final Vector3f bottomRight,
final int width,
final int height,
final Voxel voxel,
final boolean backFace) {
final Vector3f [] vertices = new Vector3f[4];
vertices[2] = topLeft.multLocal(Constants.VOXEL_SIZE);
vertices[3] = topRight.multLocal(Constants.VOXEL_SIZE);
vertices[0] = bottomLeft.multLocal(Constants.VOXEL_SIZE);
vertices[1] = bottomRight.multLocal(Constants.VOXEL_SIZE);
final int [] indexes = backFace ? new int[] { 2,0,1, 1,3,2 } : new int[]{ 2,3,1, 1,0,2 };
final float[] colorArray = new float[4*4];
for (int i = 0; i < colorArray.length; i+=4) {
/*
* Here I set different colors for quads depending on the "type" attribute, just
* so that the different groups of voxels can be clearly seen.
*
*/
if (voxel.type == BlockType.GRASS_BLOCK) {
colorArray[i] = 0.1f;
colorArray[i+1] = 0.3f;
colorArray[i+2] = 0.0f;
colorArray[i+3] = 1.0f;
} else {
colorArray[i] = 0.0f;
colorArray[i+1] = 0.0f;
colorArray[i+2] = 0.0f;
colorArray[i+3] = 0.0f;
}
}
Mesh mesh = new Mesh();
mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
mesh.setBuffer(Type.Color, 4, colorArray);
mesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indexes));
mesh.updateBound();
Geometry geo = new Geometry("ColoredMesh", mesh);
if(voxel.type == BlockType.AIR)geo.setCullHint(CullHint.Always);
Material mat = new Material(main.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.setBoolean("VertexColor", true);
/*
* To see the actual rendered quads rather than the wireframe, just comment outthis line.
*/
mat.getAdditionalRenderState().setWireframe(true);
//mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
geo.setMaterial(mat);
attachChild(geo);
this.setCullHint(CullHint.Never);
}
}
WorldManager:
package mygame;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import java.util.ArrayList;
import java.util.HashMap;
public class WorldManager {
NoiseGenerator noiseGen;
HashMap<Vector3f, Chunk> chunkMap;
ArrayList<Chunk> chunks;
Node rootNode;
Main main;
public WorldManager(Main main) {
this.main = main;
this.rootNode = main.getRootNode();
chunkMap= new HashMap<Vector3f, Chunk>();
chunks = new ArrayList<Chunk>();
noiseGen = new NoiseGenerator(0);
}
public void generate() {
for(int xx = 0; xx < 128; xx+= 64) {
for(int zz = 0; zz < 128; zz+=64) {
Vector3f loc = new Vector3f(xx, 0, zz);
Chunk c = new Chunk(loc, this, main);
chunkMap.put(loc, c);
rootNode.attachChild(c);
}
}
}
}
When I generate only 1 chunk, this get’s rendered:
That’s nice
But, it only renders the top, and two adjacent sides:
the other two sides just don’t exist:
And that’s a problem, cause when I generate more chunks, it makes something like this:
Anyone know what’s going on?
Sorry for my huge wall of text