6
\$\begingroup\$

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)
    {




    }
}
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$
  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;
    }
    
  2. 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;
    }
    
  3. In the getMaterial method you could use multiple returns and get rid of the result 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;
            }
        }
    }
    
  4. 160.0f is used multiple times and it's a magic number. It would deserve a named constant.

  5. The code should follow the Code Conventions for the Java Programming Language.

  6. Some of the loops runs from 1 to <= X_CHUNK_SIZE, some of them 0 to < X_CHUNK_SIZE. It should be consistent.

  7. 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;
    }
    
\$\endgroup\$
2
  • 1
    \$\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\$ Commented Feb 28, 2014 at 5:11
  • \$\begingroup\$ @RylandGoldstein: Nice pictures, thanks! :) \$\endgroup\$
    – palacsint
    Commented Feb 28, 2014 at 8:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.