public void generateChunk(int chunkX, int chunkZ) {
        byte[] blocks = new byte[32768];//128: 32768, 16: 4096
        
        SimplexOctaveGenerator ground = new SimplexOctaveGenerator(seed, 4);
        SimplexOctaveGenerator hills = new SimplexOctaveGenerator(seed, 6);
        SimplexOctaveGenerator mountains = new SimplexOctaveGenerator(seed, 4);
        SimplexOctaveGenerator particles = new SimplexOctaveGenerator(seed, 8);
        
        ground.setScale(1/96.0);
        hills.setScale(1/32.0);
        mountains.setScale(1/64.0);
        particles.setScale(1/16.0);
        
        int ground_elevation = 20;
        
        double ground_mag = 4;
        double hills_mag = 8;
        double mountains_mag = 12;

        for (int x = 0; x < 16; x++) {
            for (int z = 0; z < 16; z++) {
                int wx = x + chunkX * 16;
                int wz = z + chunkZ * 16;
                
                double groundHeight = ground.noise(wx, wz, 0.5, 0.5) * ground_mag + ground_elevation;
                double hills_height = hills.noise(wx, wz, 0.8, 0.5) * hills_mag + ground_elevation;
                double mountains_height = mountains.noise(wx, wz, 1.5, 0.6) * mountains_mag + ground_elevation;
                double particles_noise;
                
                int y = 0;
                for (; y < groundHeight; y++) {
                    particles_noise = particles.noise(wx, y, wz, 0.3, 0.5);
                    if (particles_noise < 0.9 || y == 0) {
                        blocks[locationToByteIndex(x, y, z)] = getBlockTypeByHeight(y, mountains_height);
                    } else {
                        blocks[locationToByteIndex(x, y, z)] = 4;
                    }
                }
                
                for (; y < hills_height; y++) {
                    particles_noise = particles.noise(wx, y, wz, 0.3, 0.5);
                    if (particles_noise < 0.9) {
                        blocks[locationToByteIndex(x, y, z)] = getBlockTypeByHeight(y, mountains_height);
                    }
                }
                
                for (; y < mountains_height; y++) {
                    particles_noise = particles.noise(wx, y, wz, 0.3, 0.5);
                    if (particles_noise < 0.9) {
                        blocks[locationToByteIndex(x, y, z)] = getBlockTypeByHeight(y, mountains_height);
                    }
                }
            }
        }
        addChunk(blocks, chunkX, chunkZ);
    }