I've decided a while ago to make my own voxel engine. To start, I coded my own basic 3D engine in Java using minimal OpenGL bindings for things such as SRGB ect. I set up my own VBA and VBOS and had it working with .obj files. I already have transformations and Quaternions set up.
I've recently started to implement my attempt at a voxel engine. Last night I set up a very crude jury-rigged attempt that actually worked well enough with perlin noise to draw random 1x1x1 blocks on the screen that resembled a world.
The issue is that I've used immediate mode and block-by block-rendering with no chunks. So today I've decided to start writing my chunk class, and was hoping someone could tell me if it seems correctly set up.
Edit: I forgot to mention that I currently am trying to figure out how to handle vertices, so that's why there is no code.
package cam.base.engine;
public class Chunk
{
public static final int X_CHUNK_SIZE = 16;
public static final int Y_CHUNK_SIZE = 128;
public static final int Z_CHUNK_SIZE = 16;
public int chunkXNumber;
public int chunkZNumber;
private Block [][][] blocks = new Block[X_CHUNK_SIZE][Y_CHUNK_SIZE][Z_CHUNK_SIZE];
public Chunk(int chunkXNumber, int chunkZNumber)
{
this.chunkXNumber = chunkXNumber;
this.chunkZNumber = chunkZNumber;
createBlocks(chunkXNumber, chunkZNumber);
Vertex [][][] vertices = createVertices(getActiveBlocks(), chunkXNumber, chunkZNumber);
int[] = createIndices(vertices);
}
private void createBlocks(int chunkXNum, int chunkZNum)
{
for(int i = 1; i <= X_CHUNK_SIZE; i++)
{
for(int j = 1; j <= Y_CHUNK_SIZE; j++)
{
for(int k = 1; k <= Z_CHUNK_SIZE; k++)
{
float density = getDensity(new Vector3(i,j,k), chunkXNum, chunkZNum);
byte material = getMaterial(density,j);
boolean isActive = checkBlockActive(material);
blocks[i - 1][j - 1][k - 1] = new Block(material, isActive);
}
}
}
}
private boolean[][][] getActiveBlocks()
{
boolean [][][] activeBlocks = new boolean[X_CHUNK_SIZE][Y_CHUNK_SIZE][Z_CHUNK_SIZE];
for(int i = 0; i < X_CHUNK_SIZE; i++)
{
for(int j = 0; j < Y_CHUNK_SIZE; j++)
{
for(int k = 0; k < Z_CHUNK_SIZE; k++)
{
activeBlocks[i][j][k] = blocks[i][j][k].isBlockEnabled();
}
}
}
return activeBlocks;
}
private float getDensity(Vector3 chunkPos, int chunkXNum, int chunkZNum)
{
float density = 0;
int x = chunkPos.getX() * chunkXNum;
int y = chunkPos.getY() * chunkZNum;
int z = chunkPos.getZ() * chunkZNum;
//TODO check to see if double is necessary
for(double i = 1; i <= Game.octaveLimit; i *= 2)
{
density += (1/i) * (Noise.noise((x / 160.0f) * i, (y / 160.0f) * i,( z / 160.0f) * i));
density = Math.abs(density);
}
return density;
}
private byte getMaterial(float density, int yVal)
{
byte material;
if(density > .2)
{
if(yVal >= 80)
{
material = 0;
}
else
{
material = 1;
}
}
else
{
if(yVal >= 64)
{
material = 0;
}
else
{
material = 2;
}
}
return material;
}
private boolean checkBlockActive(byte material)
{
if(material == 0)
{
return false;
}
else
{
return true;
}
}
private Vertex[][][] createVertices(boolean[][][] activeBlocks, int chunkXNum, int chunkZNum)
{
boolean[][][] verticesActive = new boolean[X_CHUNK_SIZE][Y_CHUNK_SIZE][Z_CHUNK_SIZE];
Vertex[][][] vertices = new Vertex[X_CHUNK_SIZE][Y_CHUNK_SIZE][Z_CHUNK_SIZE];
for(int i = 1; i <= X_CHUNK_SIZE; i++)
{
for(int j = 1; j <= Y_CHUNK_SIZE; j++)
{
for(int k = 1; k <= Z_CHUNK_SIZE; k++)
{
if(activeBlocks[i - 1][j - 1][k - 1])
{
if(verticesActive[i - 1][j - 1][k - 1] == false)
{
vertices[i - 1][j - 1][k - 1] = new Vertex(new Vector3(i * chunkXNum, j , k * chunkZNum));
}
continue;
}
continue;
}
}
}
return vertices;
}
private int[] createIndices(Vertex[][][] vertices)
{
}
}
1 Answer 1
createBlocks
actually fills the blocks array, creation happens before that, so this name is a little bit misleading. I'd move the array creation inside the method and use it as a return value:private Block[][][] blocks; public Chunk(int chunkXNumber, int chunkZNumber) { .. blocks = createBlocks(chunkXNumber, chunkZNumber); } private Block[][][] createBlocks(int chunkXNum, int chunkZNum) { final Block[][][] blocks = new Block[X_CHUNK_SIZE][Y_CHUNK_SIZE][Z_CHUNK_SIZE]; // loops here return blocks; }
private boolean checkBlockActive(byte material) { if (material == 0) { return false; } else { return true; } }
It could be simply
private boolean checkBlockActive(byte material) { return material != 0; }
In the
getMaterial
method you could use multiple returns and get rid of theresult
variable:private byte getMaterial(final float density, final int yVal) { if (density > .2) { if (yVal >= 80) { return 0; } else { return 1; } } else { if (yVal >= 64) { return 0; } else { return 2; } } }
160.0f
is used multiple times and it's a magic number. It would deserve a named constant.The code should follow the Code Conventions for the Java Programming Language.
Some of the loops runs from
1
to<= X_CHUNK_SIZE
, some of them0
to< X_CHUNK_SIZE
. It should be consistent.The two continue is unnecessary here:
for (int k = 1; k <= Z_CHUNK_SIZE; k++) { if (activeBlocks[i - 1][j - 1][k - 1]) { if (verticesActive[i - 1][j - 1][k - 1] == false) { vertices[i - 1][j - 1][k - 1] = new Vertex(new Vector3(i * chunkXNum, j, k * chunkZNum)); } continue; } continue; }
-
2\$\begingroup\$ Hahaha thanks for the reply. I actually made this a while ago and have since become less bad. My entire engine is in c++ now and actually lets you choose between using a Sparsevoxel octree, linear memory or hashing. Looking back a lot of the crap I was doing was bad :(. Here's a couple semi recent pics.imgur.com/JWkX6Va,MXj3mN8,9miaW8Z \$\endgroup\$Ryland Goldstein– Ryland Goldstein2014年02月28日 05:11:59 +00:00Commented Feb 28, 2014 at 5:11