Added Perlin noise class, which will be useful for water.

This was SVN commit r3871.
This commit is contained in:
Matei 2006-05-17 03:53:54 +00:00
parent 4879b98fc7
commit 18eb72a25a
10 changed files with 334 additions and 1 deletions

160
source/maths/Noise.cpp Normal file
View file

@ -0,0 +1,160 @@
#include "precompiled.h"
#include "Noise.h"
#include <cmath>
#include <boost/random.hpp>
using namespace std;
namespace
{
/// Random number generator (Boost Mersenne Twister)
boost::mt19937 rng;
/// Utility function for random numbers
float randFloat() {
return ((float)rng()) / 4294967296.0f;
}
/// Utility function used in both noises as an ease curve
float easeCurve(float t)
{
return t*t*t*(t*(t*6-15)+10);
}
}
Noise2D::Noise2D(int f)
{
freq = f;
grads = new CVector2D*[freq];
for(int i=0; i<freq; i++)
{
grads[i] = new CVector2D[freq];
for(int j=0; j<freq; j++)
{
float a = randFloat() * 2 * M_PI;
grads[i][j] = CVector2D(cos(a), sin(a));
}
}
}
Noise2D::~ Noise2D()
{
for(int i=0; i<freq; i++)
{
delete[] grads[i];
}
delete[] grads;
}
float Noise2D::operator()(float x, float y)
{
x *= freq;
y *= freq;
int ix = floor(x);
int iy = floor(y);
float fx = x - ix;
float fy = y - iy;
ix %= freq; if(ix<0) ix += freq;
iy %= freq; if(iy<0) iy += freq;
int ix1 = (ix+1) % freq;
int iy1 = (iy+1) % freq;
float s = grads[ix][iy].Dot(CVector2D(fx, fy));
float t = grads[ix1][iy].Dot(CVector2D(fx-1, fy));
float u = grads[ix][iy1].Dot(CVector2D(fx, fy-1));
float v = grads[ix1][iy1].Dot(CVector2D(fx-1, fy-1));
float ex = easeCurve(fx);
float ey = easeCurve(fy);
float a = s + ex*(t-s);
float b = u + ex*(v-u);
return (a + ey*(b-a)) * .5 + .5;
}
Noise3D::Noise3D(int f, int v) : freq(f), vfreq(v)
{
grads = new CVector3D**[freq];
for(int i=0; i<freq; i++)
{
grads[i] = new CVector3D*[freq];
for(int j=0; j<freq; j++)
{
grads[i][j] = new CVector3D[vfreq];
for(int k=0; k<vfreq; k++)
{
CVector3D v;
do {
v = CVector3D(2*randFloat()-1, 2*randFloat()-1, 2*randFloat()-1);
}
while(v.LengthSquared() > 1 || v.LengthSquared() < 0.1);
v.Normalize();
grads[i][j][k] = CVector3D(v.X, v.Y, v.Z);
}
}
}
}
Noise3D::~ Noise3D()
{
for(int i=0; i<freq; i++)
{
for(int j=0; j<freq; j++)
{
delete[] grads[i][j];
}
delete[] grads[i];
}
delete[] grads;
}
float Noise3D::operator()(float x, float y, float z)
{
x *= freq;
y *= freq;
z *= vfreq;
int ix = floor(x);
int iy = floor(y);
int iz = floor(z);
float fx = x - ix;
float fy = y - iy;
float fz = z - iz;
ix %= freq; if(ix<0) ix += freq;
iy %= freq; if(iy<0) iy += freq;
iz %= vfreq; if(iz<0) iz += vfreq;
int ix1 = (ix+1) % freq;
int iy1 = (iy+1) % freq;
int iz1 = (iz+1) % vfreq;
float s0 = grads[ix][iy][iz].Dot(CVector3D(fx, fy, fz));
float t0 = grads[ix1][iy][iz].Dot(CVector3D(fx-1, fy, fz));
float u0 = grads[ix][iy1][iz].Dot(CVector3D(fx, fy-1, fz));
float v0 = grads[ix1][iy1][iz].Dot(CVector3D(fx-1, fy-1, fz));
float s1 = grads[ix][iy][iz1].Dot(CVector3D(fx, fy, fz-1));
float t1 = grads[ix1][iy][iz1].Dot(CVector3D(fx-1, fy, fz-1));
float u1 = grads[ix][iy1][iz1].Dot(CVector3D(fx, fy-1, fz-1));
float v1 = grads[ix1][iy1][iz1].Dot(CVector3D(fx-1, fy-1, fz-1));
float ex = easeCurve(fx);
float ey = easeCurve(fy);
float ez = easeCurve(fz);
float a0 = s0 + ex*(t0-s0);
float b0 = u0 + ex*(v0-u0);
float c0 = a0 + ey*(b0-a0);
float a1 = s1 + ex*(t1-s1);
float b1 = u1 + ex*(v1-u1);
float c1 = a1 + ey*(b1-a1);
return (c0 + ez*(c1-c0)) * .5 + .5;
}

54
source/maths/Noise.h Normal file
View file

@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: Noise.h
// Author: Matei Zaharia
// Contact: matei@wildfiregames.com
//
// Description: 2D and 3D seamless Perlin noise classes. Not optimized for speed yet.
//
// Based on http://www.cs.cmu.edu/~mzucker/code/perlin-noise-math-faq.html
// and http://mrl.nyu.edu/~perlin/paper445.pdf.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef NOISE_H
#define NOISE_H
#include "Vector2D.h"
#include "Vector3D.h"
#include "MathUtil.h"
class Noise2D
{
/// Frequency in X and Y
int freq;
/// freq*freq random gradient vectors in the unit cube
CVector2D** grads;
public:
Noise2D(int freq);
~Noise2D();
/// Evaluate the noise function at a given point
float operator() (float x, float y);
};
class Noise3D
{
/// Frequency in X and Y
int freq;
/// Frequency in Z (vertical frequency)
int vfreq;
/// freq*freq*vfreq random gradient vectors in the unit cube
CVector3D*** grads;
public:
Noise3D(int freq, int vfreq);
~Noise3D();
/// Evaluate the noise function at a given point
float operator() (float x, float y, float z);
};
#endif

99
source/maths/Vector2D.h Normal file
View file

@ -0,0 +1,99 @@
//***********************************************************
//
// Name: CVector2D.h
// Author: Matei Zaharia
//
// Description: Provides an interface for a vector in R4 and
// allows vector and scalar operations on it
//
//***********************************************************
#ifndef VECTOR2D_H
#define VECTOR2D_H
#include <math.h>
///////////////////////////////////////////////////////////////////////////////
// CVector2D:
class CVector2D
{
public:
CVector2D() {}
CVector2D(float x,float y) { X=x; Y=y; }
CVector2D(const CVector2D& p) { X=p.X; Y=p.Y; }
operator float*() {
return &X;
}
operator const float*() const {
return &X;
}
CVector2D operator-() const {
return CVector2D(-X, -Y);
}
CVector2D operator+(const CVector2D& t) const {
return CVector2D(X+t.X, Y+t.Y);
}
CVector2D operator-(const CVector2D& t) const {
return CVector2D(X-t.X, Y-t.Y);
}
CVector2D operator*(float f) const {
return CVector2D(X*f, Y*f);
}
CVector2D operator/(float f) const {
float inv=1.0f/f;
return CVector2D(X*inv, Y*inv);
}
CVector2D& operator+=(const CVector2D& t) {
X+=t.X; Y+=t.Y;
return *this;
}
CVector2D& operator-=(const CVector2D& t) {
X-=t.X; Y-=t.Y;
return *this;
}
CVector2D& operator*=(float f) {
X*=f; Y*=f;
return *this;
}
CVector2D& operator/=(float f) {
float invf=1.0f/f;
X*=invf; Y*=invf;
return *this;
}
float Dot(const CVector2D& a) const {
return X*a.X + Y*a.Y;
}
float LengthSquared() const {
return Dot(*this);
}
float Length() const {
return (float) sqrt(LengthSquared());
}
void Normalize() {
float mag=Length();
X/=mag; Y/=mag;
}
public:
float X, Y;
};
//////////////////////////////////////////////////////////////////////////////////
#endif

View file

@ -117,9 +117,15 @@ CVector3D CVector3D::Cross (const CVector3D &vector) const
return Temp;
}
float CVector3D::LengthSquared () const
{
return ( SQR(X) + SQR(Y) + SQR(Z) );
}
float CVector3D::GetLength () const
{
return sqrtf ( SQR(X) + SQR(Y) + SQR(Z) );
return sqrtf ( LengthSquared() );
}
void CVector3D::Normalize ()

View file

@ -59,6 +59,7 @@ class CVector3D
//Returns length of the vector
float GetLength () const;
float LengthSquared () const;
void Normalize ();
// Returns 3 element array of floats, e.g. for glVertex3fv

View file

@ -22,6 +22,8 @@ bool g_NoGLVBO=false;
bool g_NoGLFramebufferObject = false;
// flag to switch on shadows
bool g_Shadows=false;
// flag to switch on reflective/refractive water
bool g_FancyWater=false;
// flag to switch on fixed frame timing (RC: I'm using this for profiling purposes)
bool g_FixedFrameTiming=false;
bool g_VSync = false;
@ -72,6 +74,7 @@ static void LoadGlobals()
CFG_GET_USER_VAL("novbo", Bool, g_NoGLVBO);
CFG_GET_USER_VAL("noframebufferobject", Bool, g_NoGLFramebufferObject);
CFG_GET_USER_VAL("shadows", Bool, g_Shadows);
CFG_GET_USER_VAL("fancywater", Bool, g_FancyWater);
CFG_GET_USER_VAL("renderpath", String, g_RenderPath);
CFG_GET_USER_VAL("lodbias", Float, g_LodBias);

View file

@ -7,6 +7,8 @@ extern bool g_NoGLVBO;
extern bool g_NoGLFramebufferObject;
// flag to switch on shadows
extern bool g_Shadows;
// flag to switch on reflective/refractive water
extern bool g_FancyWater;
// flag to switch on fixed frame timing (RC: I'm using this for profiling purposes)
extern bool g_FixedFrameTiming;
extern bool g_VSync;

View file

@ -667,6 +667,7 @@ static void InitRenderer()
g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO,g_NoGLVBO);
g_Renderer.SetOptionBool(CRenderer::OPT_NOFRAMEBUFFEROBJECT,g_NoGLFramebufferObject);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS,g_Shadows);
g_Renderer.SetOptionBool(CRenderer::OPT_FANCYWATER,g_FancyWater);
g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath));
g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, g_LodBias);

View file

@ -552,6 +552,9 @@ void CRenderer::SetOptionBool(enum Option opt,bool value)
case OPT_SHADOWS:
m_Options.m_Shadows=value;
break;
case OPT_FANCYWATER:
m_Options.m_FancyWater=value;
break;
default:
debug_warn("CRenderer::SetOptionBool: unknown option");
break;
@ -569,6 +572,8 @@ bool CRenderer::GetOptionBool(enum Option opt) const
return m_Options.m_NoFramebufferObject;
case OPT_SHADOWS:
return m_Options.m_Shadows;
case OPT_FANCYWATER:
return m_Options.m_FancyWater;
default:
debug_warn("CRenderer::GetOptionBool: unknown option");
break;

View file

@ -91,6 +91,7 @@ public:
OPT_NOVBO,
OPT_NOFRAMEBUFFEROBJECT,
OPT_SHADOWS,
OPT_FANCYWATER,
OPT_LODBIAS
};
@ -136,6 +137,7 @@ public:
bool m_NoVBO;
bool m_NoFramebufferObject;
bool m_Shadows;
bool m_FancyWater;
float m_LodBias;
RenderPath m_RenderPath;
} m_Options;