/**
   * Generate the local contents of a chunk. This should be purely deterministic from the chunk
   * contents, chunk position and world seed - should not depend on external state or other data.
   *
   * @param c
   */
  public void generateChunk(Chunk c) {

    int hm_x = (((c.getChunkWorldPosX() / Chunk.SIZE_X) % 512) + 512) % 512;
    int hm_z = (((c.getChunkWorldPosZ() / Chunk.SIZE_Z) % 512) + 512) % 512;

    double scaleFactor = 0.05 * Chunk.SIZE_Y;

    double p00 = heightmap[hm_x][hm_z] * scaleFactor;
    double p10 = heightmap[(hm_x - 1 + 512) % 512][(hm_z) % 512] * scaleFactor;
    double p11 = heightmap[(hm_x - 1 + 512) % 512][(hm_z + 1 + 512) % 512] * scaleFactor;
    double p01 = heightmap[(hm_x) % 512][(hm_z + 1 + 512) % 512] * scaleFactor;

    for (int x = 0; x < Chunk.SIZE_X; x++) {
      for (int z = 0; z < Chunk.SIZE_Z; z++) {
        WorldBiomeProvider.Biome type =
            biomeProvider.getBiomeAt(c.getBlockWorldPosX(x), c.getBlockWorldPosZ(z));

        // calculate avg height
        double interpolatedHeight =
            lerp(
                x / (double) Chunk.SIZE_X,
                lerp(z / (double) Chunk.SIZE_Z, p10, p11),
                lerp(z / (double) Chunk.SIZE_Z, p00, p01));

        // Scale the height to fit one chunk (suppose we have max height 20 on the Heigthmap
        // ToDo: Change this formula in later implementation of vertical chunks
        double threshold = Math.floor(interpolatedHeight);

        for (int y = Chunk.SIZE_Y - 1; y >= 0; y--) {
          if (y == 0) { // The very deepest layer of the world is an
            // indestructible mantle
            c.setBlock(x, y, z, mantle);
            break;
          } else if (y < threshold) {
            c.setBlock(x, y, z, stone);
          } else if (y == threshold) {
            if (y < Chunk.SIZE_Y * 0.05 + 1) {
              c.setBlock(x, y, z, sand);
            } else if (y < Chunk.SIZE_Y * 0.05 * 12) {
              c.setBlock(x, y, z, grass);
            } else {
              c.setBlock(x, y, z, snow);
            }
          } else {
            if (y <= Chunk.SIZE_Y / 20) { // Ocean
              c.setBlock(x, y, z, water);
              c.setLiquid(x, y, z, new LiquidData(LiquidType.WATER, Chunk.MAX_LIQUID_DEPTH));

            } else {
              c.setBlock(x, y, z, air);
            }
          }
        }
      }
    }
  }