etqw-sdk/source/idlib/math/Perlin.cpp

608 lines
18 KiB
C++
Raw Permalink Normal View History

2008-05-29 00:00:00 +00:00
// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
sdPerlin::sdPerlin( float _persistence, int _octaves, float _frequency ) {
persistence = _persistence;
octaves = _octaves;
frequency = _frequency;
// "no" tiling
tilex = 0x0FFFFFFF;
tiley = 0x0FFFFFFF;
tilez = 0x0FFFFFFF;
}
void sdPerlin::SetTileX( int tile ) {
tilex = ( int )( tile * frequency );
}
void sdPerlin::SetTileY( int tile ) {
tiley = ( int )( tile * frequency );
}
void sdPerlin::SetTileZ( int tile ) {
tilez = ( int )( tile * frequency );
}
float sdPerlin::RawNoise( int x, int y, int z ) {
int n = x + y * 57 + z * 227;
n = ( n << 13 ) ^ n;
//if (alternative)
// return (1 - ( ( n * ( n * n * 19417 + 189851) + 4967243) & 4945007) / 3354521.0);
//else
return 1 - ( (n * ( n * n * 15731 + 789221) + 1376312589) & 0x7fffffff ) / 1073741824.0;
}
/*
float sdPerlin::SmoothedNoise (int x, int y) {
float corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16;
float sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8;
float center = Noise(x, y) / 4;
return corners + sides + center;
}
*/
/**
Interpolated 3d noize
*/
float sdPerlin::InterpolatedNoise (float x, float y, float z) {
int ix, iy, iz;
float fx, fy, fz;
ix = static_cast< int >( x );
iy = static_cast< int >( y );
iz = static_cast< int >( z );
fx = x - ix;
fy = y - iy;
fz = z - iz;
float v1, v2, v3, v4, v5, v6, v7, v8;
v1 = RawNoise( ix, iy, iz );
v2 = RawNoise( ix + 1, iy, iz );
v3 = RawNoise( ix, iy + 1, iz );
v4 = RawNoise( ix + 1, iy + 1, iz );
v5 = RawNoise( ix, iy, iz + 1 );
v6 = RawNoise( ix + 1, iy, iz + 1 );
v7 = RawNoise( ix, iy + 1, iz + 1 );
v8 = RawNoise( ix + 1, iy + 1, iz + 1 );
float i1, i2, i3, i4;
i1 = CosineInterp( v1 , v2 , fx );
i2 = CosineInterp( v3 , v4 , fx );
i3 = CosineInterp( v5 , v6 , fx );
i4 = CosineInterp( v7 , v8 , fx );
float j1, j2;
j1 = CosineInterp( i1 , i2 , fy );
j2 = CosineInterp( i3 , i4 , fy );
return CosineInterp( j1 , j2 , fz );
}
float sdPerlin::NoiseFloat( const idVec3 &pos ) {
float rez = 0.0f;
float frequencyl = frequency;
float amplitude = 1.0f;
int i;
for ( i = 0; i < octaves; i++ ) {
rez += InterpolatedNoise( pos.x * frequencyl, pos.y * frequencyl, pos.z * frequencyl ) * amplitude;
frequencyl *= 2;
amplitude *= persistence;
}
if ( rez > 1.f ) {
rez = 1.f;
} else if ( rez < -1.f ) {
rez = -1.f;
}
return rez;
}
#if 0
bool sdPerlin2::inited = false;
int sdPerlin2::p[ ( PERLIN_RANDOM_SAMPLES + 1 ) * 2 ];
idVec3 sdPerlin2::g[ ( PERLIN_RANDOM_SAMPLES + 1 ) * 2 ];
sdPerlin2::sdPerlin2( void ) {
Init();
}
void sdPerlin2::Init( void ) {
if ( inited ) {
return;
}
inited = true;
idVec3 v;
float s;
srand( 1 );
int i, j;
for ( i = 0; i < PERLIN_RANDOM_SAMPLES; i++ ) {
do {
for ( j = 0; j < 3; j++ ) {
v[ j ] = ( ( rand() % ( PERLIN_RANDOM_SAMPLES * 2 ) ) - PERLIN_RANDOM_SAMPLES ) / static_cast< float >( PERLIN_RANDOM_SAMPLES );
}
s = v * v;
} while( s > 1.f && s != 0.f );
s = 1 / sqrt( s );
for ( j = 0; j < 3; j++ ) {
g[ i ][ j ] = v[ j ] * s;
}
}
for ( i = 0; i < PERLIN_RANDOM_SAMPLES; i++ ) {
p[ i ] = i;
}
for ( i = PERLIN_RANDOM_SAMPLES; i > 0; i -= 2 ) {
Swap( p[ i ], p[ rand() % PERLIN_RANDOM_SAMPLES ] );
}
for ( i = 0; i < PERLIN_RANDOM_SAMPLES + 2; i++ ) {
p[ PERLIN_RANDOM_SAMPLES + i ] = p[ i ];
g[ PERLIN_RANDOM_SAMPLES + i ] = g[ i ];
}
}
void Perlin_Setup( const float& v, int& b0, int& b1, float& r0, float& r1 ) {
float t = v + 10000;
int it = static_cast< int >( t );
b0 = it & ( PERLIN_RANDOM_SAMPLES - 1 );
b1 = ( b0 + 1 ) & ( PERLIN_RANDOM_SAMPLES - 1 );
r0 = t - it;
r1 = r0 - 1;
}
ID_INLINE float Perlin_At( const idVec3& q, float r0, float r1, float r2 ) {
return ( q[ 0 ] * r0 ) + ( q[ 1 ] * r1 ) + ( q[ 2 ] * r2 );
}
ID_INLINE float Perlin_SCurve( float t ) {
return ( t * t * ( 3 - ( 2 * t ) ) );
}
ID_INLINE float Perlin_Lerp( float a, float b, float t ) {
return ( a + ( t * ( b - a ) ) );
}
float sdPerlin2::Noise( const idVec3& noisePos ) {
int b00, b01, b11, b10;
int b0[ 3 ], b1[ 3 ];
idVec3 r0, r1, s;
idVec3 vec = noisePos; // * 0.0037972f;
Perlin_Setup( vec[ 0 ], b0[ 0 ], b1[ 0 ], r0[ 0 ], r1[ 0 ] );
Perlin_Setup( vec[ 1 ], b0[ 1 ], b1[ 1 ], r0[ 1 ], r1[ 1 ] );
Perlin_Setup( vec[ 2 ], b0[ 2 ], b1[ 2 ], r0[ 2 ], r1[ 2 ] );
int i, j;
i = p[ b0[ 0 ] ];
j = p[ b1[ 0 ] ];
b00 = p[ i + b0[ 1 ] ];
b10 = p[ j + b0[ 1 ] ];
b01 = p[ i + b1[ 1 ] ];
b11 = p[ j + b1[ 1 ] ];
s[ 0 ] = Perlin_SCurve( r0[ 0 ] );
s[ 1 ] = Perlin_SCurve( r0[ 1 ] );
s[ 2 ] = Perlin_SCurve( r0[ 2 ] );
float u, v, a, b, c ,d;
u = Perlin_At( g[ b00 + b0[ 2 ] ], r0[ 0 ], r0[ 1 ], r0[ 2 ] );
v = Perlin_At( g[ b10 + b0[ 2 ] ], r1[ 0 ], r0[ 1 ], r0[ 2 ] );
a = Perlin_Lerp( s[ 0 ], u, v );
u = Perlin_At( g[ b01 + b0[ 2 ] ], r0[ 0 ], r1[ 1 ], r0[ 2 ] );
v = Perlin_At( g[ b11 + b0[ 2 ] ], r1[ 0 ], r1[ 1 ], r0[ 2 ] );
b = Perlin_Lerp( s[ 0 ], u, v );
c = Perlin_Lerp( s[ 1 ], a, b );
u = Perlin_At( g[ b00 + b1[ 2 ] ], r0[ 0 ], r0[ 1 ], r1[ 2 ] );
v = Perlin_At( g[ b10 + b1[ 2 ] ], r1[ 0 ], r0[ 1 ], r1[ 2 ] );
a = Perlin_Lerp( s[ 0 ], u, v );
u = Perlin_At( g[ b01 + b1[ 2 ] ], r0[ 0 ], r1[ 1 ], r1[ 2 ] );
v = Perlin_At( g[ b11 + b1[ 2 ] ], r1[ 0 ], r1[ 1 ], r1[ 2 ] );
b = Perlin_Lerp( s[ 0 ], u, v );
d = Perlin_Lerp( s[ 1 ], a, b );
d = Perlin_Lerp( s[ 2 ], c, d );
if ( d > 1.f ) {
d = 1.f;
} else if ( d < -1.f ) {
d = -1.f;
}
return d;
}
#endif // 0
// TODO: change these preprocessor macros into inline functions
// S curve is (3x^2 - 2x^3) because it's quick to calculate
// though -cos(x * PI) * 0.5 + 0.5 would work too
ID_INLINE float EaseCurve( float t ) {
return t * t * ( 3.0 - 2.0 * t );
}
ID_INLINE float LinearInterp( float t, float a, float b ) {
return a + t * ( b - a );
}
ID_INLINE float Dot2( float rx, float ry, float* q ) {
return rx * q[ 0 ] + ry * q[ 1 ];
}
ID_INLINE float Dot3( float rx, float ry, float rz, float* q ) {
return rx * q[ 0 ] + ry * q[ 1 ] + rz * q[ 2 ];
}
#define SetupValues( t, axis, g0, g1, d0, d1, pos ) \
t = pos[axis] + NOISE_LARGE_PWR2; \
g0 = ((int)t) & NOISE_MOD_MASK; \
g1 = (g0 + 1) & NOISE_MOD_MASK; \
d0 = t - (int)t; \
d1 = d0 - 1.0;
/////////////////////////////////////////////////////////////////////
// return a random float in [-1,1]
ID_INLINE float sdPerlin2::RandNoiseFloat() {
return (float)((rand() % (NOISE_WRAP_INDEX + NOISE_WRAP_INDEX)) -
NOISE_WRAP_INDEX) / NOISE_WRAP_INDEX;
};
/////////////////////////////////////////////////////////////////////
// convert a 2D vector into unit length
void sdPerlin2::Normalize2d(float vector[2]) {
float length = sqrt((vector[0] * vector[0]) + (vector[1] * vector[1]));
vector[0] /= length;
vector[1] /= length;
}
/////////////////////////////////////////////////////////////////////
// convert a 3D vector into unit length
void sdPerlin2::Normalize3d(float vector[3]) {
float length = sqrt((vector[0] * vector[0]) +
(vector[1] * vector[1]) +
(vector[2] * vector[2]));
vector[0] /= length;
vector[1] /= length;
vector[2] /= length;
}
/////////////////////////////////////////////////////////////////////
//
// Mnemonics used in the following 3 functions:
// L = left (-X direction)
// R = right (+X direction)
// D = down (-Y direction)
// U = up (+Y direction)
// B = backwards (-Z direction)
// F = forwards (+Z direction)
//
// Not that it matters to the math, but a reader might want to know.
//
// noise1d - create 1-dimensional coherent noise
// if you want to learn about how noise works, look at this
// and then look at noise2d.
float sdPerlin2::Noise1d(float pos[1]) {
int gridPointL, gridPointR;
float distFromL, distFromR, sX, t, u, v;
if ( !initialized ) {
Reseed();
}
// find out neighboring grid points to pos and signed distances from pos to them.
SetupValues(t, 0, gridPointL, gridPointR, distFromL, distFromR, pos);
sX = EaseCurve( distFromL );
// u, v, are the vectors from the grid pts. times the random gradients for the grid points
// they are actually dot products, but this looks like scalar multiplication
u = distFromL * gradientTable1d[ permutationTable[ gridPointL ] ];
v = distFromR * gradientTable1d[ permutationTable[ gridPointR ] ];
// return the linear interpretation between u and v (0 = u, 1 = v) at sX.
return LinearInterp( sX, u, v );
}
/////////////////////////////////////////////////////////////////////
// create 2d coherent noise
float sdPerlin2::Noise2d(float pos[2]) {
int gridPointL, gridPointR, gridPointD, gridPointU;
int indexLD, indexRD, indexLU, indexRU;
float distFromL, distFromR, distFromD, distFromU;
float *q, sX, sY, a, b, t, u, v;
register int indexL, indexR;
if ( !initialized ) {
Reseed();
}
// find out neighboring grid points to pos and signed distances from pos to them.
SetupValues(t, 0, gridPointL, gridPointR, distFromL, distFromR, pos);
SetupValues(t, 1, gridPointD, gridPointU, distFromD, distFromU, pos);
// Generate some temporary indexes associated with the left and right grid values
indexL = permutationTable[ gridPointL ];
indexR = permutationTable[ gridPointR ];
// Generate indexes in the permutation table for all 4 corners
indexLD = permutationTable[ indexL + gridPointD ];
indexRD = permutationTable[ indexR + gridPointD ];
indexLU = permutationTable[ indexL + gridPointU ];
indexRU = permutationTable[ indexR + gridPointU ];
// Get the s curves at the proper values
sX = EaseCurve( distFromL );
sY = EaseCurve( distFromD );
// Do the dot products for the lower left corner and lower right corners.
// Interpolate between those dot products value sX to get a.
q = gradientTable2d[indexLD]; u = Dot2( distFromL, distFromD, q );
q = gradientTable2d[indexRD]; v = Dot2( distFromR, distFromD, q );
a = LinearInterp( sX, u, v );
// Do the dot products for the upper left corner and upper right corners.
// Interpolate between those dot products at value sX to get b.
q = gradientTable2d[indexLU]; u = Dot2( distFromL, distFromU, q );
q = gradientTable2d[indexRU]; v = Dot2( distFromR, distFromU, q );
b = LinearInterp( sX, u, v );
// Interpolate between a and b at value sY to get the noise return value.
return LinearInterp( sY, a, b );
}
/////////////////////////////////////////////////////////////////////
// you guessed it -- 3D coherent noise
float sdPerlin2::Noise3d(float pos[2]) {
int gridPointL, gridPointR, gridPointD, gridPointU, gridPointB, gridPointF;
int indexLD, indexLU, indexRD, indexRU;
float distFromL, distFromR, distFromD, distFromU, distFromB, distFromF;
float *q, sX, sY, sZ, a, b, c, d, t, u, v;
register int indexL, indexR;
if (! initialized) {
Reseed();
}
// find out neighboring grid points to pos and signed distances from pos to them.
SetupValues(t, 0, gridPointL, gridPointR, distFromL, distFromR, pos);
SetupValues(t, 1, gridPointD, gridPointU, distFromD, distFromU, pos);
SetupValues(t, 2, gridPointB, gridPointF, distFromB, distFromF, pos);
indexL = permutationTable[ gridPointL ];
indexR = permutationTable[ gridPointR ];
indexLD = permutationTable[ indexL + gridPointD ];
indexRD = permutationTable[ indexR + gridPointD ];
indexLU = permutationTable[ indexL + gridPointU ];
indexRU = permutationTable[ indexR + gridPointU ];
sX = EaseCurve( distFromL );
sY = EaseCurve( distFromD );
sZ = EaseCurve( distFromB );
q = gradientTable3d[indexLD+gridPointB]; u = Dot3( distFromL, distFromD, distFromB, q );
q = gradientTable3d[indexRD+gridPointB]; v = Dot3( distFromR, distFromD, distFromB, q );
a = LinearInterp( sX, u, v );
q = gradientTable3d[indexLU+gridPointB]; u = Dot3( distFromL, distFromU, distFromB, q );
q = gradientTable3d[indexRU+gridPointB]; v = Dot3( distFromR, distFromU, distFromB, q );
b = LinearInterp( sX, u, v );
c = LinearInterp( sY, a, b );
q = gradientTable3d[indexLD+gridPointF]; u = Dot3( distFromL, distFromD, distFromF, q );
q = gradientTable3d[indexRD+gridPointF]; v = Dot3( distFromR, distFromD, distFromF, q );
a = LinearInterp( sX, u, v );
q = gradientTable3d[indexLU+gridPointF]; u = Dot3( distFromL, distFromU, distFromF, q );
q = gradientTable3d[indexRU+gridPointF]; v = Dot3( distFromR, distFromU, distFromF, q );
b = LinearInterp( sX, u, v );
d = LinearInterp( sY, a, b );
return LinearInterp( sZ, c, d );
}
/////////////////////////////////////////////////////////////////////
// you can call noise component-wise, too.
float sdPerlin2::Noise(float x) {
return Noise1d(&x);
}
float sdPerlin2::Noise(float x, float y) {
float p[2] = { x, y };
return Noise2d(p);
}
float sdPerlin2::Noise(float x, float y, float z) {
float p[3] = { x, y, z };
return Noise3d(p);
}
/////////////////////////////////////////////////////////////////////
// reinitialize with new, random values.
void sdPerlin2::Reseed() {
srand((unsigned int) (time(NULL) + rand()));
GenerateLookupTables();
}
/////////////////////////////////////////////////////////////////////
// reinitialize using a user-specified random seed.
void sdPerlin2::Reseed(unsigned int rSeed) {
srand(rSeed);
GenerateLookupTables();
}
/////////////////////////////////////////////////////////////////////
// initialize everything during constructor or reseed -- note
// that space was already allocated for the gradientTable
// during the constructor
void sdPerlin2::GenerateLookupTables() {
unsigned i, j, temp;
for (i=0; i<NOISE_WRAP_INDEX; i++) {
// put index into permutationTable[index], we will shuffle later
permutationTable[i] = i;
gradientTable1d[i] = RandNoiseFloat();
for (j=0; j<2; j++) { gradientTable2d[i][j] = RandNoiseFloat(); }
Normalize2d(gradientTable2d[i]);
for (j=0; j<3; j++) { gradientTable3d[i][j] = RandNoiseFloat(); }
Normalize3d(gradientTable3d[i]);
}
// Shuffle permutation table up to NOISE_WRAP_INDEX
for (i=0; i<NOISE_WRAP_INDEX; i++) {
j = rand() & NOISE_MOD_MASK;
temp = permutationTable[i];
permutationTable[i] = permutationTable[j];
permutationTable[j] = temp;
}
// Add the rest of the table entries in, duplicating
// indices and entries so that they can effectively be indexed
// by unsigned chars. I think. Ask Perlin what this is really doing.
//
// This is the only part of the algorithm that I don't understand 100%.
for (i=0; i<NOISE_WRAP_INDEX+2; i++) {
permutationTable[NOISE_WRAP_INDEX + i] = permutationTable[i];
gradientTable1d[NOISE_WRAP_INDEX + i] = gradientTable1d[i];
for (j=0; j<2; j++) {
gradientTable2d[NOISE_WRAP_INDEX + i][j] = gradientTable2d[i][j];
}
for (j=0; j<3; j++) {
gradientTable3d[NOISE_WRAP_INDEX + i][j] = gradientTable3d[i][j];
}
}
// And we're done. Set initialized to true
initialized = 1;
}
////////////////////////////////////
int sdPerlin3::p[ 512 ] = {
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,
// and again
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
};
float sdPerlin3::Noise( float x ) {
return 0.f;
}
float sdPerlin3::Noise( float x, float y ) {
return 0.f;
}
float sdPerlin3::Noise( float x, float y, float z ) {
// Find unit cube that contains point
int ucX = static_cast< int >( idMath::Floor( x ) ) & 255;
int ucY = static_cast< int >( idMath::Floor( y ) ) & 255;
int ucZ = static_cast< int >( idMath::Floor( z ) ) & 255;
// Find relative x, y, z of point in cube
x -= idMath::Floor( x );
y -= idMath::Floor( y );
z -= idMath::Floor( z );
// Compute fade curves for each of x, y, z
float u = Fade( x );
float v = Fade( y );
float w = Fade( z );
// Hash coordinates of the 8 cube corners;
int a = p[ ucX ] + ucY;
int aa = p[ a ] + ucZ;
int ab = p[ a + 1 ] + ucZ;
int b = p[ ucX + 1 ] + ucY;
int ba = p[ b ] + ucZ;
int bb = p[ b + 1 ] + ucZ;
// And add blended results from 8 corners of cube
return( Lerp( w, Lerp( v, Lerp( u, Grad( p[ aa ], x, y, z ),
Grad( p[ ba ], x - 1, y, z ) ),
Lerp( u, Grad( p[ ab ], x, y - 1, z ),
Grad( p[ bb ], x - 1, y - 1, z ) ) ),
Lerp( v, Lerp( u, Grad( p[ aa + 1 ], x, y, z - 1 ),
Grad( p[ ba + 1 ], x - 1, y, z - 1) ),
Lerp( u, Grad( p[ ab + 1 ], x, y - 1, z - 1 ),
Grad( p[ bb + 1 ], x - 1, y - 1, z - 1 ) ) ) ) );
}