#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <math.h>

#include "QF/qtypes.h"

#include "noise.h"

// returns the 3D noise value for a point in space
float
noise3d (vec3_t v, int num)
{
	int         n;

	n = floor (v[0]) + floor (v[1]) * 57 + floor (v[2]) * 3251;
	n = (n << 13) ^ n;
	n = n * (n * n * 15731 + 789221) + 1376312589;
	return (n & 0x7fffffff) / 2147483648.0;
}

// a variation of Noise3D that takes 3 floats
float
noiseXYZ (float x, float y, float z, int num)
{
	vec3_t      v;

	v[0] = x;
	v[1] = y;
	v[2] = z;
	return noise3d (v, num);
}

// returns a noise value from a scaled up noise pattern
//
// Actually created by sampling points on a certain grid
// and interpolating the inbetween value, making the noise smooth
float
noise_scaled (vec3_t v, float s, int num)
{
	float       n = 0;
	vec3_t      a, b, c, d;

	a[0] = floor (v[0] / s) * s;
	b[0] = a[0] + s;
	c[0] = (v[0] - a[0]) / s;
	d[0] = 1 - c[0];
	a[1] = floor (v[1] / s) * s;
	b[1] = a[1] + s;
	c[1] = (v[1] - a[1]) / s;
	d[1] = 1 - c[1];
	a[2] = floor (v[2] / s) * s;
	b[2] = a[2] + s;
	c[2] = (v[2] - a[2]) / s;
	d[2] = 1 - c[2];

	n += noiseXYZ (a[0], a[1], a[2], num) * d[0] * d[1] * d[2];
	n += noiseXYZ (a[0], a[1], b[2], num) * d[0] * d[1] * c[2];
	n += noiseXYZ (a[0], b[1], a[2], num) * d[0] * c[1] * d[2];
	n += noiseXYZ (a[0], b[1], b[2], num) * d[0] * c[1] * c[2];
	n += noiseXYZ (b[0], a[1], a[2], num) * c[0] * d[1] * d[2];
	n += noiseXYZ (b[0], a[1], b[2], num) * c[0] * d[1] * c[2];
	n += noiseXYZ (b[0], b[1], a[2], num) * c[0] * c[1] * d[2];
	n += noiseXYZ (b[0], b[1], b[2], num) * c[0] * c[1] * c[2];

	return n;
}

// returns a Perlin noise value for a point in 3D space
//
// This noise function combines noise at several different scales
// which makes it slower than just using one layer of noise
float
noise_perlin (vec3_t v, float p, int num)
{
	float       n = 0;

	n += noise_scaled (v, 1024, num);
	n += 0.5 + (noise_scaled (v, 256, num) - 0.5) * p;
	n += 0.5 + (noise_scaled (v, 64, num) - 0.5) * p * p;
	n += 0.5 + (noise_scaled (v, 16, num) - 0.5) * p * p * p;

	return n / 2 - 0.5;
}

// Use to create low-res noise patterns without interpolation
// A good strategy for avoiding seams on terrain and curves
// when called with a scale value of about 32
//
// This is because surfaces have some texture pixels which are
// actually not in the polygon at all so their edges don't
// perfectly fit together.
//
// Creates a random pattern of light and dark squares on flat
// axis aligned surfaces, which is usually not what you want
// but it may have interesting applications
void
snap_vector (vec3_t v_old, vec3_t v_new, float scale)
{
	if (scale <= 0) {
		v_new[0] = v_old[0];
		v_new[1] = v_old[1];
		v_new[2] = v_old[2];
	} else {
		v_new[0] = floor (v_old[0] / scale + 0.5) * scale;
		v_new[1] = floor (v_old[1] / scale + 0.5) * scale;
		v_new[2] = floor (v_old[2] / scale + 0.5) * scale;
	}
}
