Friday, January 22, 2016

Adding 2d Perlin Noise to Hellcore/LambdaMOO

Add to extensions.c:
  static int p[512];
  static int permutation[] = { 151,160,137,91,90,15,
  131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,
  21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
  35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,
  74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,
  230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,
  80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,
  164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,
  118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,
  183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,
  172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,
  218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,
  145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,
  115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,
  141,128,195,78,66,215,61,156,180
  };
  /* Function declarations */
  double fade(double t);
  double lerp(double t, double a, double b);
  double grad(int hash, double x, double y, double z);
  void init_noise();
  double pnoise(double x, double y, double z);
  void init_noise()
  {
  int i;
  for(i = 0; i < 256 ; i++)
  p[256+i] = p[i] = permutation[i];
  }
  double pnoise(double x, double y, double z)
  {
  int X = (int)floor(x) & 255, /* FIND UNIT CUBE THAT */
  Y = (int)floor(y) & 255, /* CONTAINS POINT. */
  Z = (int)floor(z) & 255;
  x -= floor(x); /* FIND RELATIVE X,Y,Z */
  y -= floor(y); /* OF POINT IN CUBE. */
  z -= floor(z);
  double u = fade(x), /* COMPUTE FADE CURVES */
  v = fade(y), /* FOR EACH OF X,Y,Z. */
  w = fade(z);
  int A = p[X]+Y,
  AA = p[A]+Z,
  AB = p[A+1]+Z, /* HASH COORDINATES OF */
  B = p[X+1]+Y,
  BA = p[B]+Z,
  BB = p[B+1]+Z; /* THE 8 CUBE CORNERS, */
  return lerp(w,lerp(v,lerp(u, grad(p[AA ], x, y, z), /* AND ADD */
  grad(p[BA ], x-1, y, z)), /* BLENDED */
  lerp(u, grad(p[AB ], x, y-1, z), /* RESULTS */
  grad(p[BB ], x-1, y-1, z))), /* FROM 8 */
  lerp(v, lerp(u, grad(p[AA+1], x, y, z-1 ),/* CORNERS */
  grad(p[BA+1], x-1, y, z-1)), /* OF CUBE */
  lerp(u, grad(p[AB+1], x, y-1, z-1),
  grad(p[BB+1], x-1, y-1, z-1))));
  }
  double fade(double t){ return t * t * t * (t * (t * 6 - 15) + 10); }
  double lerp(double t, double a, double b){ return a + t * (b - a); }
  double grad(int hash, double x, double y, double z)
  {
  int h = hash & 15; /* CONVERT LO 4 BITS OF HASH CODE */
  double u = h < 8 ? x : y, /* INTO 12 GRADIENT DIRECTIONS. */
  v = h < 4 ? y : h==12||h==14 ? x : z;
  return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
  }
  int noise2d(int x, int y, double scalex, double scaley, int size) {
  double xf = (double)x;
  double yf = (double)y;
  double sizef = (double)size;
  double noiseval = pnoise(xf/(sizef * scalex), yf/(sizef * scaley), 0.5);
  noiseval = sizef * ((noiseval + 1.0)/2.0);
  return (int)noiseval;
  }
  int fbm2d(int x, int y, double scalex, double scaley, int size, int octaves)
    
  double xf = (double)x;
  double yf = (double)y;
  double sizef = (double)size;
  double noiseval = 0.0;
  int i;
  for(i = 1; i <= octaves; i++) {
  double n = pnoise(i * xf/(sizef * scalex), i * yf/(sizef * scaley), 0.5);
  n = sizef * ((n/2.0) + 0.5);
  noiseval = noiseval + n / (double)i;
  }
  return (int)noiseval;
  }
  static package bf_perlin_2d(Var arglist, Byte next, void *vdata, Objid progr)
  {
  Var r;
  int x = (int)arglist.v.list[1].v.num;
  int y = (int)arglist.v.list[2].v.num;
  double alpha = *arglist.v.list[3].v.fnum;
  double beta = *arglist.v.list[4].v.fnum;
  int n = (int)arglist.v.list[5].v.num;
  int octaves = (int)arglist.v.list[6].v.num;
  init_noise();
  r.v.num = (int)fbm2d(x, y, alpha, beta, n, octaves);
  r.type = TYPE_INT;
  free_var(arglist);
  return make_var_pack(r);
  }

Add to void register_extensions() in extensions.c:
  register_function("perlin_2d", 6, 6, bf_perlin_2d, TYPE_INT,
  TYPE_INT, TYPE_FLOAT, TYPE_FLOAT, TYPE_INT, TYPE_INT);

How to use: you have a list of biomes or room types. You get a size from this and then call this function:
biomes = {"flatland","forest","mountain"};
size = length(biomes); px = 10; py = 33; alpha = 1.0; beta = 1.5; octaves = 1; index = perlin_2d(px, py, alpha, beta, size, octaves);

 alpha, and beta are floats that affect the jagged/smoothness of the generated area.  octaves runs the noise function multiple times - WARNING - if you have a set number, for instance when selecting a list of biomes, this will cause values to fall outside the range of the list you've created.  For instance using multiple octaves with the list of 3 biomes above would result in many values at x, y co-ordinates of 4-8.  If you're using the noise to set altitude, you may wish to use the octave setting.

If you want to generate an entire map you would call this function repeatedly over the size of your map. Or, more usefully, you call it when a specific co-ordinate is needed. As long as the input to perlin_2d is the same, the index return will always be the same.