From 4c1ee89ffb581ae21a9dd32f1369234140346681 Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Wed, 10 Jul 2013 22:58:31 +0100
Subject: [PATCH] [Add] Fancy new "cloud" generator. This needs some heavy
 tweaking/cleanup.

---
 src/ai.c     |  11 +-
 src/menu.c   |   4 +-
 src/perlin.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/perlin.h |  25 +++++
 src/rng.c    |   2 +-
 src/rng.h    |   2 +-
 6 files changed, 319 insertions(+), 8 deletions(-)
 create mode 100644 src/perlin.c
 create mode 100644 src/perlin.h

diff --git a/src/ai.c b/src/ai.c
index 25492b6..7557073 100644
--- a/src/ai.c
+++ b/src/ai.c
@@ -62,8 +62,9 @@
 #define AI_SECONDARY  (1<<1)  /* Firing secondary weapon. */
 
 /* file info. */
-#define AI_PREFIX  "../scripts/ai/"
+#define AI_PREFIX   "../scripts/ai/"
 #define AI_SUFFIX   ".lua"
+#define AI_INCLUDE  "include/"
 
 /* AI profiles. */
 static AI_Profile* profiles = NULL;
@@ -237,9 +238,11 @@ int ai_init(void) {
 
   /* Load the profiles. */
   for(i = 0; i < nfiles; i++)
-    if((strncmp(files[i], AI_PREFIX, strlen(AI_PREFIX))==0 &&
-        strncmp(files[i] + strlen(files[i]) - strlen(AI_SUFFIX),
-                AI_SUFFIX, strlen(AI_SUFFIX))==0))
+    if((strncmp(files[i], AI_PREFIX, strlen(AI_PREFIX))==0) &&  /* prefixed. */
+          (strncmp(files[i] + strlen(AI_PREFIX), AI_INCLUDE, /* Not an include. */
+                   strlen(AI_INCLUDE)) != 0) &&
+          (strncmp(files[i] + strlen(files[i]) - strlen(AI_PREFIX), /* Suffixed. */
+                   AI_SUFFIX, strlen(AI_SUFFIX))==0))
       if(ai_loadProfile(files[i]))
         WARN("Error loading AI profile '%s'", files[i]);
 
diff --git a/src/menu.c b/src/menu.c
index 1fe3100..1206792 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -7,7 +7,7 @@
 #include "pilot.h"
 #include "space.h"
 #include "player.h"
-#include "plasmaf.h"
+#include "perlin.h"
 #include "mission.h"
 #include "ltime.h"
 #include "save.h"
@@ -68,7 +68,7 @@ void menu_main(void) {
   unsigned int bwid, wid;
   glTexture* tex;
 
-  tex = pf_genFractal(SCREEN_W, SCREEN_H, 5.);
+  tex = noise_genCloud(SCREEN_W, SCREEN_H, 5.);
 
   /* Create background image window. */
   bwid = window_create("BG", -1, -1, SCREEN_W, SCREEN_H);
diff --git a/src/perlin.c b/src/perlin.c
new file mode 100644
index 0000000..7b26b9d
--- /dev/null
+++ b/src/perlin.c
@@ -0,0 +1,283 @@
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+#include "rng.h"
+
+#include "perlin.h"
+
+#define LERP(a, b, x)   (a + x * (b - a))
+#define ABS(a)          ((a)<0?-(a):(a))
+#define CLAMP(a, b, x)  ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x)))
+
+typedef struct {
+  int ndim;
+  unsigned char map[256]; /* Randomized map of indexes into buffer. */
+  float buffer[256][NOISE_MAX_DIMENSIONS];  // Random 256 x ndim buffer. */
+  /* Fractal stuff. */
+  float H;
+  float lacunarity;
+  float exponent[NOISE_MAX_OCTAVES];
+} perlin_data_t;
+
+static float* noise_genMap(const int w, const int h, float rug);
+
+static float lattice(perlin_data_t* data, int ix, float fx, int iy,
+    float fy, int iz, float fz, int iw, float fw) {
+
+  int   n[4] = { ix, iy, iz, iw };
+  float f[4] = { fx, fy, fz, fw };
+  int nindex = 0;
+  int i;
+  float value = 0;
+
+  for(i = 0; i < data->ndim; i++)
+    nindex = data->map[(nindex + n[i]) & 0xFF];
+  for(i = 0; i < data->ndim; i++)
+    value += data->buffer[nindex][i] * f[i];
+
+  return value;
+}
+
+#define DEFAULT_SEED    0x15687436
+#define DELTA           1e-6f
+#define SWAP(a, b, t)   t = a; a = b; b = t
+
+#define FLOOR(a)        ((int) a - (a < 0 && a != (int)a))
+#define CUBIC(a)        (a * a * (3 - 2 * a))
+
+static void normalize(perlin_data_t* data, float* f) {
+  float magnitude = 0;
+  int i;
+  for(i = 0; i < data->ndim; i++)
+    magnitude += f[i] * f[i];
+  magnitude = 1 / sqrtf(magnitude);
+  for(i = 0; i < data->ndim; i++)
+    f[i] *= magnitude;
+}
+
+noise_t noise_new(int ndim, float hurst, float lacunarity) {
+  perlin_data_t* data=(perlin_data_t*)calloc(sizeof(perlin_data_t), 1);
+  int i, j;
+  unsigned char tmp;
+  float f = 1;
+  data->ndim = ndim;
+  for(i = 0; i < 256; i++) {
+    data->map[i] = (unsigned char) i;
+    for(j = 0; j < data->ndim; j++)
+      data->buffer[i][j] = RNGF()-0.5;
+    normalize(data, data->buffer[i]);
+  }
+
+  while(--i) {
+    j = RNG(0, 255);
+    SWAP(data->map[i], data->map[j], tmp);
+  }
+
+  data->H = hurst;
+  data->lacunarity = lacunarity;
+  for(i = 0; i < NOISE_MAX_OCTAVES; i++) {
+    /*exponent[i] = powf(f, -H); */
+    data->exponent[i] = 1.0f / f;
+    f *= lacunarity;
+  }
+  return (noise_t)data;
+}
+
+float noise_get(noise_t noise, float *f )
+{
+  perlin_data_t* data = (perlin_data_t*) noise;
+  int n[NOISE_MAX_DIMENSIONS];     /* Indexes to pass to lattice function */
+  int i;
+  float r[NOISE_MAX_DIMENSIONS];   /* Remainders to pass to lattice function */
+  float w[NOISE_MAX_DIMENSIONS];   /* Cubic values to pass to interpolation function */
+  float value;
+
+  for(i=0; i<data->ndim; i++) {
+    n[i] = FLOOR(f[i]);
+    r[i] = f[i] - n[i];
+    w[i] = CUBIC(r[i]);
+  }
+
+  switch(data->ndim) {
+    case 1:
+      value = LERP(lattice(data,n[0], r[0],0,0,0,0,0,0),
+          lattice(data,n[0]+1, r[0]-1,0,0,0,0,0,0),
+          w[0]);
+      break;
+    case 2:
+      value = LERP(LERP(lattice(data,n[0], r[0], n[1], r[1],0,0,0,0),
+                 lattice(data,n[0]+1, r[0]-1, n[1], r[1],0,0,0,0),
+                 w[0]),
+              LERP(lattice(data,n[0], r[0], n[1]+1, r[1]-1,0,0,0,0),
+                 lattice(data,n[0]+1, r[0]-1, n[1]+1, r[1]-1,0,0,0,0),
+                 w[0]),
+              w[1]);
+      break;
+    case 3:
+      value = LERP(LERP(LERP(lattice(data,n[0], r[0], n[1], r[1], n[2], r[2],0,0),
+                  lattice(data,n[0]+1, r[0]-1, n[1], r[1], n[2], r[2],0,0),
+                  w[0]),
+                 LERP(lattice(data,n[0], r[0], n[1]+1, r[1]-1, n[2], r[2],0,0),
+                  lattice(data,n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2], r[2],0,0),
+                  w[0]),
+                 w[1]),
+              LERP(LERP(lattice(data,n[0], r[0], n[1], r[1], n[2]+1, r[2]-1,0,0),
+                  lattice(data,n[0]+1, r[0]-1, n[1], r[1], n[2]+1, r[2]-1,0,0),
+                  w[0]),
+                 LERP(lattice(data,n[0], r[0], n[1]+1, r[1]-1, n[2]+1, r[2]-1,0,0),
+                  lattice(data,n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2]+1, r[2]-1,0,0),
+                  w[0]),
+                 w[1]),
+              w[2]);
+      break;
+    case 4:
+    default:
+      value = LERP(LERP(LERP(LERP(lattice(data,n[0], r[0], n[1], r[1], n[2], r[2], n[3], r[3]),
+                     lattice(data,n[0]+1, r[0]-1, n[1], r[1], n[2], r[2], n[3], r[3]),
+                     w[0]),
+                  LERP(lattice(data,n[0], r[0], n[1]+1, r[1]-1, n[2], r[2], n[3], r[3]),
+                     lattice(data,n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2], r[2], n[3], r[3]),
+                     w[0]),
+                  w[1]),
+                  LERP(LERP(lattice(data,n[0], r[0], n[1], r[1], n[2]+1, r[2]-1, n[3], r[3]),
+                     lattice(data,n[0]+1, r[0]-1, n[1], r[1], n[2]+1, r[2]-1, n[3], r[3]),
+                     w[0]),
+                  LERP(lattice(data,n[0], r[0], n[1]+1, r[1]-1, n[2]+1, r[2]-1,0,0),
+                     lattice(data,n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2]+1, r[2]-1, n[3], r[3]),
+                     w[0]),
+                  w[1]),
+                 w[2]),
+              LERP(LERP(LERP(lattice(data,n[0], r[0], n[1], r[1], n[2], r[2], n[3]+1, r[3]-1),
+                     lattice(data,n[0]+1, r[0]-1, n[1], r[1], n[2], r[2], n[3]+1, r[3]-1),
+                     w[0]),
+                  LERP(lattice(data,n[0], r[0], n[1]+1, r[1]-1, n[2], r[2], n[3]+1, r[3]-1),
+                     lattice(data,n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2], r[2], n[3]+1, r[3]-1),
+                     w[0]),
+                  w[1]),
+                  LERP(LERP(lattice(data,n[0], r[0], n[1], r[1], n[2]+1, r[2]-1, n[3]+1, r[3]-1),
+                     lattice(data,n[0]+1, r[0]-1, n[1], r[1], n[2]+1, r[2]-1, n[3]+1, r[3]-1),
+                     w[0]),
+                  LERP(lattice(data,n[0], r[0], n[1]+1, r[1]-1, n[2]+1, r[2]-1,0,0),
+                     lattice(data,n[0]+1, r[0]-1, n[1]+1, r[1]-1, n[2]+1, r[2]-1, n[3]+1, r[3]-1),
+                     w[0]),
+                  w[1]),
+                 w[2]),
+              w[3]);
+      break;
+  }
+  return CLAMP(-0.99999f, 0.99999f, value);
+}
+
+float noise_fbm(noise_t noise, float* f, float octaves) {
+  float tf[NOISE_MAX_DIMENSIONS];
+  perlin_data_t* data = (perlin_data_t*) noise;
+  /* Init locals. */
+  float value = 0;
+  int i, j;
+  memcpy(tf, f, sizeof(float) * data->ndim);
+
+  /* Inner loop for spectral construction, where the fractal is build. */
+  for(i = 0; i < (int)octaves; i++) {
+    value += noise_get(noise, tf) * data->exponent[i];
+    for(j = 0; j < data->ndim; j++) tf[j] *= data->lacunarity;
+  }
+
+  /* Take care of remainder in octaves. */
+  octaves -= (int)octaves;
+  if(octaves > DELTA)
+    value += octaves * noise_get(noise, tf) * data->exponent[i];
+  return CLAMP(-0.99999f, 0.99999f, value);
+}
+
+float noise_turbulence(noise_t noise, float* f, float octaves) {
+  float tf[NOISE_MAX_DIMENSIONS];
+  perlin_data_t* data = (perlin_data_t*) noise;
+  /* Init locals. */
+  float value = 0;
+  int i, j;
+  memcpy(tf, f, sizeof(float) * data->ndim);
+
+  /* Inner loop of spectral construction, where the fractal is built. */
+  for(i = 0; i < (int)octaves; i++) {
+    value += ABS(noise_get(noise, tf)) * data->exponent[i];
+    for(j = 0; j < data->ndim; j++) tf[j] *= data->lacunarity;
+  }
+
+  /* Take care of remainders in octaves. */
+  octaves -= (int)octaves;
+  if(octaves > DELTA)
+    value += octaves * ABS(noise_get(noise, tf)) * data->exponent[i];
+  return CLAMP(-0.99999f, 0.99999f, value);
+}
+
+void noise_delete(noise_t noise) {
+  free((perlin_data_t*)noise);
+}
+
+static float* noise_genMap(const int w, const int h, float rug) {
+  int x, y;
+  float f[2];
+  float octaves;
+  float hurst;
+  float lacunarity;
+  noise_t noise;
+  float* map;
+  float value;
+
+  octaves = 3.;
+  hurst = NOISE_DEFAULT_HURST;
+  lacunarity = NOISE_DEFAULT_LACUNARITY;
+
+  noise = noise_new(2, hurst, lacunarity);
+
+  map = malloc(sizeof(float)*w*h);
+
+  for(y = 0; y < h; y++) {
+    for(x = 0; x < w; x++) {
+      f[0] = rug * (float)x / (float)w;
+      f[1] = rug * (float)y / (float)h;
+
+      /*value = noise_get(noise, f);*/
+      /*value = noise_fbm(noise, f, octaves);*/
+      value = noise_turbulence(noise, f, octaves);
+
+      value = value + 0.3;
+      map[y*w+x] = (value < 1.) ? value : 1.;
+    }
+  }
+
+  noise_delete(noise);
+
+  return map;
+}
+
+glTexture* noise_genCloud(const int w, const int h, double rug) {
+  int i;
+  float* map;
+  SDL_Surface* sur;
+  uint32_t* pix;
+  glTexture* tex;
+  double c;
+
+  map = noise_genMap(w, h, rug);
+
+  sur = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RGBMASK);
+  pix = sur->pixels;
+
+  /* Convert from mapping to actual colours. */
+  SDL_LockSurface(sur);
+  for(i = 0; i < h*w; i++) {
+    c = map[i];
+    pix[i] = RMASK + BMASK + GMASK + (AMASK & (uint32_t)(AMASK*c));
+  }
+  SDL_UnlockSurface(sur);
+
+  free(map);
+
+  tex = gl_loadImage(sur);
+
+  return tex;
+}
+
diff --git a/src/perlin.h b/src/perlin.h
new file mode 100644
index 0000000..ba7748e
--- /dev/null
+++ b/src/perlin.h
@@ -0,0 +1,25 @@
+#pragma once
+#include "opengl.h"
+
+typedef void* noise_t;
+
+#define NOISE_MAX_OCTAVES           128
+#define NOISE_MAX_DIMENSIONS        4
+#define NOISE_DEFAULT_HURST         0.5f
+#define NOISE_DEFAULT_LACUNARITY    2.0f
+
+noise_t noise_new(int dimensions, float hurst, float lacunarity);
+
+/* Basic perlin noise. */
+float noise_get(noise_t noise, float *f);
+
+/* Fractional brownian motion. */
+float noise_fbm(noise_t noise, float* f, float octaves);
+
+/* Turbulence. */
+float noise_turbulence(noise_t noise, float* f, float octaves);
+
+void  noise_delete(noise_t noise);
+
+glTexture* noise_genCloud(const int w, const int h, double rug);
+
diff --git a/src/rng.c b/src/rng.c
index 3a89fba..114d42d 100644
--- a/src/rng.c
+++ b/src/rng.c
@@ -108,7 +108,7 @@ unsigned int randint(void) {
 }
 
 /* Return a random double. */
-static double m_div = (double)(0xFFFFFFFF) + 1.;
+static double m_div = (double)(0xFFFFFFFF);
 double randfp(void) {
   double m = (double)mt_getInt();
   return m / m_div;
diff --git a/src/rng.h b/src/rng.h
index 9b4576a..95a3069 100644
--- a/src/rng.h
+++ b/src/rng.h
@@ -1,7 +1,7 @@
 #pragma once
 
 #define RNG(L,H)  ((int)L + (int)((double)(H-L+1) * randfp())) /* L <= RNG <= H */
-#define RNGF()  (randfp())
+#define RNGF()  (randfp()) /* 0. <= RNGF <= 1. */
 
 void rng_init(void);
 unsigned int randint(void);