Face culling on voxels.

I have a segment of code here that’s supposed to cull faces of voxels that should be invisible.


	private FloatBuffer voxelData(int x, int y, int z) {
		float r = rand.nextFloat(), g = rand.nextFloat(), b = rand.nextFloat();
		float xscale = x * scale, yscale = y * scale, zscale = z * scale;
		int numfaces = 6;

		boolean top = true, bottom = false, left = true, right = true, front = true, back = true;
		if (x != width--  && voxels[x + 1][y][z].isActive) {right = false; numfaces--;}
		if (x != 0 		  && voxels[x - 1][y][z].isActive) {left = false; numfaces--;}
		if (y != height-- && voxels[x][y + 1][z].isActive) {top = false; numfaces--;}
		if (y != 0 		  && voxels[x][y - 1][z].isActive) {bottom = false; numfaces--;}
		if (z != length-- && voxels[x][y][z + 1].isActive) {front = false; numfaces--;}
		if (z != 0 		  && voxels[x][y][z - 1].isActive) {back = false; numfaces--;}

		if (numfaces == 0) return null;

		FloatBuffer data = BufferUtils.createFloatBuffer(numfaces * 24);

		if (top) { data.put(new float[] {
				this.x + xscale + 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
				this.x + xscale + 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
		});}

		if (bottom) { data.put(new float[] {
				this.x + xscale - 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale + 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale + 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
		});}

		if (left) { data.put(new float[] {
				this.x + xscale - 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
		});}

		if (right) { data.put(new float[] {
				this.x + xscale + 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
				this.x + xscale + 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale + 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale + 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
		});}

		if (front) { data.put(new float[] {
				this.x + xscale + 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
				this.x + xscale + 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale + 0.5f * scale,	r, g, b,
		});}

		if (back) { data.put(new float[] {
				this.x + xscale + 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale - 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale - 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
				this.x + xscale + 0.5f * scale,		this.y + yscale + 0.5f * scale,		this.z + zscale - 0.5f * scale,	r, g, b,
		});}

		data.flip();
		return data;
	}

The segment is passed the x, y, and z values through a triple-nested for-loop checking each voxel in a 3d array. However, the code seems to only output left and back sides; never any others. Even then, it’s only the left-bottom-back corner that renders, and not even the bottom face; just the back, and three left faces from the edge, and a face one above that.
Here’s a screenshot:

I recommend you to use some kind of interface to access the voxels in your world/chunk.
It makes the code look better and some things easier.

For example:


interface IVoxelAccess{
    public ?Voxel? getVoxel(int x,int y,int z);
}

Then make your world implement the interface:


class ?World? implements IVoxelAccess{
    public ?Voxel? getVoxel(int x,int y,int z){
        return ?voxels[x][y][z]?;
    }
}

(?XXX? means that I don’t know how the class is called)

And then let the renderer use that interface:


      if (x != width--  && getVoxel(x + 1,y,z).isActive) {right = false; numfaces--;}
      if (x != 0         && getVoxel(x - 1,y,z).isActive) {left = false; numfaces--;}
      if (y != height-- && getVoxel(x,y + 1,z).isActive) {top = false; numfaces--;}
      if (y != 0         && getVoxel(x,y - 1,z).isActive) {bottom = false; numfaces--;}
      if (z != length-- && getVoxel(x,y,z + 1).isActive) {front = false; numfaces--;}
      if (z != 0         && getVoxel(x,y,z - 1).isActive) {back = false; numfaces--;}

Trust me: It makes your life easier.

Next thing: Wrong directions.

You should check all additions/subtractions to your coordinates,
and also make drawings for youself to know which side belongs to which axis in which direction.

  • Longor1996

Ahh, that’s actually what I was busy doing. I eventually figured out the whole access issue, although I’m not sure the method that I went about is correct.

By the way, there is no World class; each voxel group effectively needs to know about all the voxels it contains. So i suppose there are many World classes in that respect; the basic group and all of the ones that extend it (so they can behave differently - ie a turret of a ship needs to rotate differently than the ship itself.) To that end, this is what I’ve come up with:



	public void updateVBO() {
		int numfaces = 0;
		boolean front = false, back = false, left = false, right = false, top = false, bottom = false;
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				for (int z = 0; z < length; z++) {

					if (isOOB(x+1, y, z) || !isOCC(x+1, y, z)) {right = true; numfaces++;}
					if (isOOB(x-1, y, z) || !isOCC(x-1, y, z)) {left = true; numfaces++;}
					if (isOOB(x, y+1, z) || !isOCC(x, y+1, z)) {top = true; numfaces++;}
					if (isOOB(x, y-1, z) || !isOCC(x, y-1, z)) {bottom = true; numfaces++;}
					if (isOOB(x, y, z+1) || !isOCC(x, y, z+1)) {right = true; numfaces++;}
					if (isOOB(x, y, z-1) || !isOCC(x, y, z-1)) {left = true; numfaces++;}

					voxelData(x, y, z, voxels[x][y][z].type, numfaces, front, back, left, right, top, bottom);

				}
			}
		}
	}

	protected boolean isOOB(int x, int y, int z) {
		if (x * y * z > width * height * length || x * y * z < 0) return true;
		return false;
	}
	protected boolean isOCC(int x, int y, int z) {
		if (!isOOB(x, y, z)) if (voxels[x][y][z].opaque) return true;
		return false;
	}


Also, I’ve refrained from putting data into the actual floatbuffer for now because I don’t want the buffer being too big. That being said, compacting it would work to remove the extra space, right?