home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Encyclopedia of Graphics File Formats Companion
/
GFF_CD.ISO
/
formats
/
ttddd
/
code
/
noise.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-20
|
16KB
|
438 lines
/*---------------------------------------------------------------\
| Name: NOISE.C |
| Author: Charles Congdon |
| Created: 09/23/92 |
| Last Updated: 11/04/92 |
| |
| Purpose: |
| Implements 3D Perlin noise functions, support for the |
| Worley 3D noise interface, etc. The noise function itself is |
| from Graphics Gems II, page 396 - the rest guesswork and |
| intentional changes of my own. |
| Greg Ward is the author of the Graphics Gems II code |
| reproduced here. |
\---------------------------------------------------------------*/
#define INITNOISE 1
#include "t3dlib.h"
#include "noise.h"
/*--------------------------------------\
| 3D Noise function supporting cast |
\--------------------------------------*/
#define HERMITE(p0,p1,r0,r1,t,t5,var32,tt) ((p0)*(1.0 - (var32)) + \
(p1)*(var32) + \
(r0)*((t5)-(tt)+(t)) + \
(r1)*(t5) )
/*-----------------------------------------------------------------\
| The following assumes a 32-bit two's compliment architecture |
\-----------------------------------------------------------------*/
#define FRANDM(s,s1) (s) = ((s)<<13) ^ (s); \
(s) = ((s)*((s)*(s)*15731+789221) + 1376312589)&0x7fffffff; \
(s1) = ((double)1.0 - (double)(s) / (double)1073741824.0)
double frand (register SLONG s)
{
double s1;
s = (s<<13)^s;
s = (s*(s*s*15731 + 789221) + 1376312589) & 0x7fffffff;
s1 = (double)1.0 - (double)((ULNG)s) / (double)1073741824.0;
return (s1);
}
/*-----------------------------------------------------------\
| Noise function interpolator - optimized as best as I can |
| without completely re-writing (tempting). |
\-----------------------------------------------------------*/
void ninterp(double f[4], short int i, register short int n)
{
register SLONG x, y, z;
register SLONG s1;
register double t; /* Stack those floating point registers ! */
register double tt;
register double t5;
register double var32;
register double ttt;
register double f0[4], f1[4];
if (n == 0)
{
/*----------------------------------------\
| At zero, just return lattice value. |
\----------------------------------------*/
x = xlim[0][i&1];
y = xlim[1][(i>>1)&1];
z = xlim[2][i>>2];
s1 = (SLONG) (((SLONG)67)*x + ((SLONG)59)*y + ((SLONG)71)*z);
FRANDM(s1, t);
s1 = (SLONG) (((SLONG)73)*x + ((SLONG)79)*y + ((SLONG)83)*z);
FRANDM(s1, tt);
s1 = (SLONG) (((SLONG)89)*x + ((SLONG)97)*y + ((SLONG)101)*z);
FRANDM(s1, t5);
s1 = (SLONG) (((SLONG)103)*x + ((SLONG)107)*y + ((SLONG)109)*z);
FRANDM(s1, var32);
f[0] = t;
f[1] = tt;
f[2] = t5;
f[3] = var32;
return;
}
n--; /* Decrease order */
ninterp(f0, i, n); /* Compute first half */
ninterp(f1, (short int) (i | (1<<n)), n); /* Compute second half */
#ifdef CONSUME_MY_CPU
/*----------------------------------------------------------------\
| Use Hermite interpolation for tangent vector direction and |
| its magnitude. |
\----------------------------------------------------------------*/
t = xarg[n];
tt = t * t;
ttt = tt * t;
t5 = ttt - tt;
var32 = tt + tt + tt - ttt - ttt;
f[0] = HERMITE(f0[0], f1[0], f0[n], f1[n], t, t5, var32, tt);
f[1] = HERMITE(f0[1], f1[1], f0[n], f1[n], t, t5, var32, tt);
f[2] = HERMITE(f0[2], f1[2], f0[n], f1[n], t, t5, var32, tt);
f[3] = HERMITE(f0[3], f1[3], f0[n], f1[n], t, t5, var32, tt);
#else /* CONSUME_MY_CPU */
/*----------------------------------------------------\
| Use linear interpolation to find tangent vector |
\----------------------------------------------------*/
t = xarg[n];
tt = (double)1.0 - t;
f[0] = tt * f0[0] + t * f1[0];
f[1] = tt * f0[1] + t * f1[1];
f[2] = tt * f0[2] + t * f1[2];
tt = t * t;
ttt = tt * t;
t5 = ttt - tt;
var32 = tt + tt + tt - ttt - ttt;
/*--------------------------------------------------------\
| And Hermite interpolation for the vector magnitude. |
\--------------------------------------------------------*/
f[3] = HERMITE(f0[3], f1[3], f0[n], f1[n], t, t5, var32, tt);
#endif /* CONSUME_MY_CPU */
}
/*--------------------------------------------------------------------\
| Here is a recursive implementation of the Perlin Noise Function |
| as found in Graphics Gems II, page 396. It varies randomly |
| between 1 and -1 with an autocorollation distance of about |
| 1. It also appears to have no pronounced harmonics. |
\--------------------------------------------------------------------*/
double Noise3D(vector point, vector noisev)
{
static double f[4];
register double floored;
register double pnt;
/*------------------------\
| Define the unit cube. |
\------------------------*/
pnt = point[0]; /* Stuff the floating point registers */
floored = floor(pnt);
#ifdef FLOOR_ERROR
if (pnt < (double) 0.0)
floored = floored - (double)1.0;
#endif
xlim[0][0] = (SLONG)floored;
xlim[0][1] = xlim[0][0] + (SLONG)1;
xarg[0] = pnt - floored;
pnt = point[1];
floored = floor(pnt);
#ifdef FLOOR_ERROR
if (pnt < (double) 0.0)
floored = floored - (double)1.0;
#endif
xlim[1][0] = (SLONG)floored;
xlim[1][1] = xlim[1][0] + (SLONG)1;
xarg[1] = pnt - floored;
pnt = point[2];
floored = floor(pnt);
#ifdef FLOOR_ERROR
if (pnt < (double) 0.0)
floored = floored - (double)1.0;
#endif
xlim[2][0] = (SLONG)floored;
xlim[2][1] = xlim[2][0] + (SLONG)1;
xarg[2] = pnt - floored;
ninterp(f, 0, 3); /* Now interpolate the between the random values
at the unit cube's lattice points */
noisev[0] = f[0]; /* Return the noise vector */
noisev[1] = f[1];
noisev[2] = f[2];
return(f[3]); /* And the noise value */
}
/*------------------------------------------------------------\
| My partial implementation of the Worley Fractal Noise |
| function. Steve uses float[6] for input and output |
| vectors, for reasons I cannot fathom. |
| I return the vector magnitude as well, where his seems |
| not to. No big deal. If it serves the purpose, who needs |
| anything more... |
\------------------------------------------------------------*/
double wfractalval3(vector in, double Initial_scale, double NumScales,
double Scale_Ratio, double Amplitude_Ratio,
double Time_Ratio, double Time, vector out)
{ /* wfractval3 */
register double sratio1;
register double scaleaccum;
register double ampaccum;
register double ampl;
register vector naccum;
register vector inval;
register vector outval;
register short int i;
register double valaccum;
double randseed;
unsigned long int seedint;
short int seed[3];
static short int *pseed;
short int axisrot;
short int iterations;
double dummynoise;
double noisecent[IIMAX_SCALES][3];
static double noisecent0[IIMAX_SCALES][3];
static double axisgoal[IIMAX_SCALES][3];
/*---------------------------------\
| Do a little pre-calculation. |
\---------------------------------*/
ampl = Amplitude_Ratio;
sratio1 = 1.0 / Scale_Ratio;
scaleaccum = 1.0 / Initial_scale; /* Fit initial scale into unit cube */
ampaccum = 1.0;
/*-----------------------------------\
| Get info from NumScales value. |
\-----------------------------------*/
if (NumScales < 0.0)
{
axisrot = 1;
NumScales = fabs(NumScales);
}
else
{
axisrot = 0;
}
/*--------------------------------------------------\
| Get the random number generator's seed and the |
| number of iterations. |
\--------------------------------------------------*/
randseed = floor(NumScales);
iterations = (short int) randseed;
if (iterations > IIMAX_SCALES) /* Enforce scale limits */
iterations = (short int) IIMAX_SCALES;
if (iterations < (short int)0)
iterations = (short int)0;
if (randseed != NumScales)
{
randseed = NumScales - randseed;
}
else
{
randseed = 0.0;
}
/*-----------------------------------------------------------\
| Initialize the random number generator and noise values. |
\-----------------------------------------------------------*/
if (Noiseinit == 0)
{ /* Noiseinit */
/*--------------------------------------------------------\
| If you do not have rand48-type functions (which SAS |
| claim to be from the AT&T Unix System V run-time |
| library), modify the below to play with frand above. |
\--------------------------------------------------------*/
seedint = (unsigned long int) (2032353798 * randseed);
seed[0] = seedint >> 16;
seed[1] = seedint - (seed[0] << 16);
seed[2] = 0x330E;
pseed = seed48(seed);
/*---------------------------------------\
| Randomly displace the axes centers - |
| sorta - starting position of centers |
| is not as important as where they |
| move in subsequent frames. |
\---------------------------------------*/
noisecent0[0][0] = erand48(pseed) * Initial_scale;
noisecent0[0][1] = erand48(pseed) * Initial_scale;
noisecent0[0][2] = erand48(pseed) * Initial_scale;
for (i = 1; i < iterations; i++)
{
noisecent0[i][0] = noisecent0[0][0];
noisecent0[i][1] = noisecent0[0][1];
noisecent0[i][2] = noisecent0[0][2];
}
/*-------------------------------------------------------\
| Now figure out how the axis for each noise "center" |
| is going to move. |
\-------------------------------------------------------*/
if (Time > 0.0)
{ /* move noise vector */
/*-----------------------------------------------\
| Create a random vector of the proper scale |
\-----------------------------------------------*/
axisgoal[0][0] = erand48(pseed);
axisgoal[0][1] = erand48(pseed);
axisgoal[0][2] = erand48(pseed);
normalze(Initial_scale, axisgoal[0], axisgoal[0]);
if (axisrot == 1)
{ /* Move each axis individually */
dummynoise = Initial_scale * Scale_Ratio;
for (i = 1; i < iterations; i++)
{
axisgoal[i][0] = erand48(pseed);
axisgoal[i][1] = erand48(pseed);
axisgoal[i][2] = erand48(pseed);
normalze(dummynoise, axisgoal[i], axisgoal[i]);
dummynoise = dummynoise * Scale_Ratio;
}
} /* Move each axis individually */
else
{ /* All axes move together */
dummynoise = Initial_scale * Scale_Ratio;
for (i = 1; i < iterations; i++)
{
axisgoal[i][0] = axisgoal[0][0];
axisgoal[i][1] = axisgoal[0][1];
axisgoal[i][2] = axisgoal[0][2];
normalze(dummynoise, axisgoal[i], axisgoal[i]);
dummynoise = dummynoise * Scale_Ratio;
}
} /* All axis move together */
} /* move noise vector */
Noiseinit = 1;
} /* Noiseinit */
/*-----------------------------------------------\
| Position the noise centers as appropriate. |
\-----------------------------------------------*/
if (Time > 0.0)
{ /* Move to proper frame */
/*-----------------------------------\
| Now move us to the proper frame. |
| NOTE that you have to pass FRAME |
| in from some external source. |
\-----------------------------------*/
dummynoise = 1.0;
for (i = 0; i < iterations; i++)
{ /* Move to frame */
noisecent[i][0] = noisecent0[i][0] +
(Frame/Time) * axisgoal[i][0] * dummynoise;
noisecent[i][1] = noisecent0[i][1] +
(Frame/Time) * axisgoal[i][1] * dummynoise;
noisecent[i][2] = noisecent0[i][2] +
(Frame/Time) * axisgoal[i][2] * dummynoise;
dummynoise = dummynoise * Time_Ratio;
} /* Move to frame */
} /* Move to proper frame */
else
{ /* Frame 0 if Time == 0.0 */
for (i = 0; i < iterations; i++)
{
noisecent[i][0] = noisecent0[i][0];
noisecent[i][1] = noisecent0[i][1];
noisecent[i][2] = noisecent0[i][2];
}
} /* Frame 0 if Time == 0.0 */
/*---------------------------------\
| OK, sum up the noise values. |
\---------------------------------*/
valaccum = 0.0;
naccum[0] = 0.0;
naccum[1] = 0.0;
naccum[2] = 0.0;
for (i = 0; i < iterations; i++)
{
inval[0] = scaleaccum * in[0] + noisecent[i][0];
inval[1] = scaleaccum * in[1] + noisecent[i][1];
inval[2] = scaleaccum * in[2] + noisecent[i][2];
dummynoise = Noise3D(inval, outval);
valaccum = valaccum + ampaccum * dummynoise;
naccum[0] = naccum[0] + ampaccum * outval[0];
naccum[1] = naccum[1] + ampaccum * outval[1];
naccum[2] = naccum[2] + ampaccum * outval[2];
scaleaccum = scaleaccum * sratio1; /* Scale scaling factor */
ampaccum = ampaccum * ampl; /* Amplitude scaling factor */
}
out[0] = naccum[0];
out[1] = naccum[1];
out[2] = naccum[2];
return(valaccum);
} /* wfractval3 */
/*-----------------------------------------------\
| Vector normalization to the desired scale. |
\-----------------------------------------------*/
void normalze(double scale, vector in, vector out)
{ /* normalze */
double n;
n = sqrt(in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
if (n != 0.0)
{
n = scale / n;
out[0] = n * in[0];
out[1] = n * in[1];
out[2] = n * in[2];
}
else
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
} /* normalze */
/*--------------------------------------------------\
| A shorthand way of using the imitation Worley |
| noise function. |
\--------------------------------------------------*/
void NiceN3D(vector point, vector noisev, double scale, double NumScales)
{
wfractalval3(point, scale, NumScales , ISCALE_RATIO, IAMP_RATIO,
ITIME_RATIO, ITIME, noisev);
}