Elevox (Voxel Project) - Determine which faces to draw efficiently

Hello there!

In my voxel game I store the different in blocks as byte arrays like this:

byte[y * CHUNK_WIDTH * CHUNK_LENGTH + x * CHUNK_LENGTH + z]

Calculating the faces I have to draw is quite easy but I really check each
blocks neighbours (which can take quite a bit if I have to load an unloaded
neighbour chunk which has to generate first).

So is there a way to keep this form but to do it somehow faster?

And the second question: If i switch to Octrees, how can I determine the faces
to draw? Is there a way to do this faster?

This is the important code:


							if(Elevox.world.chunkMap.getBlockAt((int)xShift+x+1, (int)yShift+y, (int)zShift+z).isTransparent() && Elevox.world.chunkMap.getBlockAt((int)xShift+x+1, (int)yShift+y, (int)zShift+z) != Block.blockList[block]) {
								vertexData.put(new float[] {x+1.0f,y+0.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 1,0,0,
															x+1.0f,y+1.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 1,0,0,
															x+1.0f,y+1.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 1,0,0,
															x+1.0f,y+0.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 1,0,0});
							}
							if(Elevox.world.chunkMap.getBlockAt((int)xShift+x-1, (int)yShift+y, (int)zShift+z).isTransparent() && Elevox.world.chunkMap.getBlockAt((int)xShift+x-1, (int)yShift+y, (int)zShift+z) != Block.blockList[block]) {
								vertexData.put(new float[] {x+0.0f,y+0.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], -1,0,0,
															x+0.0f,y+0.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], -1,0,0,
															x+0.0f,y+1.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], -1,0,0,
															x+0.0f,y+1.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], -1,0,0});
							}
							if(Elevox.world.chunkMap.getBlockAt((int)xShift+x, (int)yShift+y+1, (int)zShift+z).isTransparent() && Elevox.world.chunkMap.getBlockAt((int)xShift+x, (int)yShift+y+1, (int)zShift+z) != Block.blockList[block]) {
								vertexData.put(new float[] {x+0.0f,y+1.0f,z+0.0f, color[0],color[1],color[2],color[3], 0,1,0,
															x+0.0f,y+1.0f,z+1.0f, color[0],color[1],color[2],color[3], 0,1,0,
															x+1.0f,y+1.0f,z+1.0f, color[0],color[1],color[2],color[3], 0,1,0,
															x+1.0f,y+1.0f,z+0.0f, color[0],color[1],color[2],color[3], 0,1,0});
							}
							if(Elevox.world.chunkMap.getBlockAt((int)xShift+x, (int)yShift+y-1, (int)zShift+z).isTransparent() && Elevox.world.chunkMap.getBlockAt((int)xShift+x, (int)yShift+y-1, (int)zShift+z) != Block.blockList[block]) {
								vertexData.put(new float[] {x+0.0f,y+0.0f,z+0.0f, color[0],color[1],color[2],color[3], 0,-1,0,
															x+1.0f,y+0.0f,z+0.0f, color[0],color[1],color[2],color[3], 0,-1,0,
															x+1.0f,y+0.0f,z+1.0f, color[0],color[1],color[2],color[3], 0,-1,0,
															x+0.0f,y+0.0f,z+1.0f, color[0],color[1],color[2],color[3], 0,-1,0});
							}
							if(Elevox.world.chunkMap.getBlockAt((int)xShift+x, (int)yShift+y, (int)zShift+z-1).isTransparent() && Elevox.world.chunkMap.getBlockAt((int)xShift+x, (int)yShift+y, (int)zShift+z-1) != Block.blockList[block]) {
								vertexData.put(new float[] {x+0.0f,y+0.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,-1,
															x+0.0f,y+1.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,-1,
															x+1.0f,y+1.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,-1,
															x+1.0f,y+0.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,-1});
							}
							if(Elevox.world.chunkMap.getBlockAt((int)xShift+x, (int)yShift+y, (int)zShift+z+1).isTransparent() && Elevox.world.chunkMap.getBlockAt((int)xShift+x, (int)yShift+y, (int)zShift+z+1) != Block.blockList[block]) {
								vertexData.put(new float[] {x+0.0f,y+0.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,1,
															x+1.0f,y+0.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,1,
															x+1.0f,y+1.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,1,
															x+0.0f,y+1.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,1});
							}

Elevox.world.chunkMap.getBlockAt() is a function that can cost quite some time in this critical part.

Ok I got it alot faster by myself now. I at least tripled the speed. I changed it like this:


							b = (x == CHUNK_WIDTH-1) ? chunkRight.chunkSlices[i].blockData[sliceY * CHUNK_WIDTH * CHUNK_LENGTH + z] : chunkSlices[i].blockData[sliceY * CHUNK_WIDTH * CHUNK_LENGTH + (x+1) * CHUNK_LENGTH + z];
							
							if(Block.blockList[b].isTransparent() && b != block) {
								vertexData.put(new float[] {x+1.0f,y+0.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 1,0,0,
															x+1.0f,y+1.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 1,0,0,
															x+1.0f,y+1.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 1,0,0,
															x+1.0f,y+0.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 1,0,0});
							}
							
							b = (x == 0) ? chunkLeft.chunkSlices[i].blockData[sliceY * CHUNK_WIDTH * CHUNK_LENGTH + (CHUNK_WIDTH-1) * CHUNK_LENGTH + z] : chunkSlices[i].blockData[sliceY * CHUNK_WIDTH * CHUNK_LENGTH + (x-1) * CHUNK_LENGTH + z];
							
							if(Block.blockList[b].isTransparent() && b != block) {
								vertexData.put(new float[] {x+0.0f,y+0.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], -1,0,0,
															x+0.0f,y+0.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], -1,0,0,
															x+0.0f,y+1.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], -1,0,0,
															x+0.0f,y+1.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], -1,0,0});
							}
							
							b = (sliceY == ChunkSlice.CHUNK_SLICE_HEIGHT-1) ? ((i == chunkSlices.length-1) ? Block.air.getID() : chunkSlices[i+1].blockData[x * CHUNK_LENGTH + z]) : chunkSlices[i].blockData[(sliceY+1) * CHUNK_WIDTH * CHUNK_LENGTH + x * CHUNK_LENGTH + z];

							if(Block.blockList[b].isTransparent() && b != block) {
								vertexData.put(new float[] {x+0.0f,y+1.0f,z+0.0f, color[0],color[1],color[2],color[3], 0,1,0,
															x+0.0f,y+1.0f,z+1.0f, color[0],color[1],color[2],color[3], 0,1,0,
															x+1.0f,y+1.0f,z+1.0f, color[0],color[1],color[2],color[3], 0,1,0,
															x+1.0f,y+1.0f,z+0.0f, color[0],color[1],color[2],color[3], 0,1,0});
							}

							b = (sliceY == 0) ? ((i == 0) ? Block.stone.getID() : chunkSlices[i-1].blockData[(ChunkSlice.CHUNK_SLICE_HEIGHT-1) * CHUNK_WIDTH * CHUNK_LENGTH + x * CHUNK_LENGTH + z]) : chunkSlices[i].blockData[(sliceY-1) * CHUNK_WIDTH * CHUNK_LENGTH + x * CHUNK_LENGTH + z];

							if(Block.blockList[b].isTransparent() && b != block) {
								vertexData.put(new float[] {x+0.0f,y+0.0f,z+0.0f, color[0],color[1],color[2],color[3], 0,-1,0,
															x+1.0f,y+0.0f,z+0.0f, color[0],color[1],color[2],color[3], 0,-1,0,
															x+1.0f,y+0.0f,z+1.0f, color[0],color[1],color[2],color[3], 0,-1,0,
															x+0.0f,y+0.0f,z+1.0f, color[0],color[1],color[2],color[3], 0,-1,0});
							}
							
							b = (z == 0) ? chunkBack.chunkSlices[i].blockData[sliceY * CHUNK_WIDTH * CHUNK_LENGTH + x * CHUNK_LENGTH + (CHUNK_LENGTH-1)] : chunkSlices[i].blockData[sliceY * CHUNK_WIDTH * CHUNK_LENGTH + x * CHUNK_LENGTH + (z-1)];
							
							if(Block.blockList[b].isTransparent() && b != block) {
								vertexData.put(new float[] {x+0.0f,y+0.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,-1,
															x+0.0f,y+1.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,-1,
															x+1.0f,y+1.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,-1,
															x+1.0f,y+0.0f,z+0.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,-1});
							}
							
							b = (z == CHUNK_LENGTH-1) ? chunkFront.chunkSlices[i].blockData[sliceY * CHUNK_WIDTH * CHUNK_LENGTH + x * CHUNK_LENGTH] : chunkSlices[i].blockData[sliceY * CHUNK_WIDTH * CHUNK_LENGTH + x * CHUNK_LENGTH + (z+1)];
							
							if(Block.blockList[b].isTransparent() && b != block) {
								vertexData.put(new float[] {x+0.0f,y+0.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,1,
															x+1.0f,y+0.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,1,
															x+1.0f,y+1.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,1,
															x+0.0f,y+1.0f,z+1.0f, col0r[0],col0r[1],col0r[2],color[3], 0,0,1});
							}

This way I don’t have to call this function. The neighbour chunks I needed
were loaded before this loop. It was so easy, but maybe someone can learn
from this code.

Sorry for opening this topic for nothing.

Greetings

If you continue in this direction, you might get some insights from here: http://etodd.io/2015/02/18/the-poor-mans-voxel-engine/

So this should tell me why an octree would be good? I could optimize my engine to load

31x31 chunks, but with a chunk size of 64x64x128 (chunks are splitted so I can update them fast) and this quite fast by
only using a max of 800M heap space (earlier I used ALOT more because I’m a retard xD). The CPU was just running
because of the GC collecting hundreds of useless FloatBuffers. I can see that an octree is good for some purposes but can it
hold half a million elements (per chunk) without exploding the memory?