module Worldgen::Algorithms

Public Class Methods

diamond_square!(p1, p2 = v2) click to toggle source

Generate terrain using a diamond square algorithm. Arguments: `heightmap` - The heightmap to use for the algorithm `roughness` (optional) - How “rough” to make the surface

Return value: nil

VALUE diamond_square(int argc, VALUE *argv, VALUE self) {
  int x, y;
  double ratio = 500.0;
  VALUE heightmap_obj, vroughness;
  int size, side_size;
  double roughness;
  heightmap_points heights;

  rb_scan_args(argc, argv, "11", &heightmap_obj, &vroughness);
    
  size = get_size(heightmap_obj);
  side_size = size - 1;

  roughness = vroughness == Qnil ? DEFAULT_ROUGHNESS : NUM2DBL(vroughness);

  heights = (heightmap_points)malloc(sizeof(double) * num_points(size));

  memset(heights, 0.0, sizeof(double) * num_points(size));

  ARR(heights, 0, 0) = ARR(heights, 0, size - 1) =
    ARR(heights, size - 1, 0) = ARR(heights, size - 1, size - 1) =
    diamond_shift(roughness);

  while (side_size >= 2) {
    int half_side = side_size / 2;

    // Square step
    for (x = 0; x < size - 1; x += side_size) {
      for (y = 0; y < size - 1; y += side_size) {
        double avg = (ARR(heights, x, y) +
          ARR(heights, x + side_size, y) +
          ARR(heights, x, y + side_size) +
          ARR(heights, x + side_size, y + side_size)) / 4.0;

        ARR(heights, x + half_side, y + half_side) = avg + diamond_shift(roughness);
      }
    }

    for (x = 0; x < size - 1; x += half_side) {
      for (y = (x + half_side) % side_size; y < size - 1; y += side_size) {
        double avg = (ARR(heights, (x - half_side + size - 1) % (size - 1), y) +
          ARR(heights, (x + half_side) % (size - 1), y) +
          ARR(heights, x, (y + half_side) % (size - 1)) +
          ARR(heights, x, (y - half_side + size - 1) % (size - 1))) / 4.0;

        avg += diamond_shift(roughness);

        ARR(heights, x, y) = avg;

        if (x == 0) {
          ARR(heights, size - 1, y) = avg;
        }
        if (y == 0) {
          ARR(heights, x, size - 1) = avg;
        }
      }
    }

    side_size /= 2.0;
    ratio /= 2.0;
  }

  // normalize
  normalize(heights, size);

  // copy into the array
  set_heights(heightmap_obj, heights);

  return Qnil;
}