
/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2008.
-------------------------------------------------------------------------
$Id$
$DateTime$
Description: Noise class based on Ken Perlin's papers
Modified to be a standalone class, speed improvements and uses the cryengine 
pseudo-random generator to generate the gradients from the unit sphere.
See comments in the class for usage

-------------------------------------------------------------------------
History:
- 11:04:2007   Created by Marco Corbetta

*************************************************************************/

#pragma once

#define NOISE_TABLE_SIZE 256
#define NOISE_MASK (NOISE_TABLE_SIZE-1)

//////////////////////////////////////////////////////////////////////////
class CPNoise3
{
public:
	CPNoise3(void)
	{
		Initialize();
	}	
	
	//////////////////////////////////////////////////////////////////////////
	// 1D quality noise generator, good for many situations like up/down movements,
	// flickering/ambient lights etc.
	// A typical usage would be to pass system time multiplied by a frequency value, like:
	// float fRes=pNoise->Noise1D(fCurrentTime*fFreq);	
	// the lower the frequency, the smoother the output
	inline float Noise1D(float x)				
	{
		// Compute what gradients to use
		int qx0 = (int)floorf(x);
		int qx1 = qx0 + 1;
		float tx0 = x - (float)qx0;
		float tx1 = tx0 - 1;

		// Make sure we don't come outside the lookup table
		qx0 = qx0 & NOISE_MASK;
		qx1 = qx1 & NOISE_MASK;

		// Compute the dotproduct between the vectors and the gradients
		float v0 = m_gx[qx0]*tx0;
		float v1 = m_gx[qx1]*tx1;

		// Modulate with the weight function
		float wx = (3 - 2*tx0)*tx0*tx0;
		float v = v0 - wx*(v0 - v1);

		return v;
	}

	//////////////////////////////////////////////////////////////////////////
	// 2D quality noise generator, twice slower than Noise1D.
	// A typical usage would be to pass 2d coordinates multiplied by a frequency value, like:
	// float fRes=pNoise->Noise2D(fX*fFreq,fY*fFreq);
	inline float Noise2D(float x, float y)
	{
		// Compute what gradients to use
		int qx0 = (int)floorf(x);
		int qx1 = qx0 + 1;
		float tx0 = x - (float)qx0;
		float tx1 = tx0 - 1;

		int qy0 = (int)floorf(y);
		int qy1 = qy0 + 1;
		float ty0 = y - (float)qy0;
		float ty1 = ty0 - 1;

		// Make sure we don't come outside the lookup table
		qx0 = qx0 & NOISE_MASK;
		qx1 = qx1 & NOISE_MASK;

		qy0 = qy0 & NOISE_MASK;
		qy1 = qy1 & NOISE_MASK;

		// Permutate values to get pseudo randomly chosen gradients
		int q00 = m_p[(qy0 + m_p[qx0]) & NOISE_MASK];
		int q01 = m_p[(qy0 + m_p[qx1]) & NOISE_MASK];

		int q10 = m_p[(qy1 + m_p[qx0]) & NOISE_MASK];
		int q11 = m_p[(qy1 + m_p[qx1]) & NOISE_MASK];

		// Compute the dotproduct between the vectors and the gradients
		float v00 = m_gx[q00]*tx0 + m_gy[q00]*ty0;
		float v01 = m_gx[q01]*tx1 + m_gy[q01]*ty0;

		float v10 = m_gx[q10]*tx0 + m_gy[q10]*ty1;
		float v11 = m_gx[q11]*tx1 + m_gy[q11]*ty1;

		// Modulate with the weight function
		float wx = (3 - 2*tx0)*tx0*tx0;
		float v0 = v00 - wx*(v00 - v01);
		float v1 = v10 - wx*(v10 - v11);

		float wy = (3 - 2*ty0)*ty0*ty0;
		float v = v0 - wy*(v0 - v1);

		return v;
	}

	//////////////////////////////////////////////////////////////////////////
	// 3D quality noise generator, twice slower than Noise2D, so use with care...
	// A typical usage would be to pass 3d coordinates multiplied by a frequency value, like:
	// float fRes=pNoise->Noise2D(fX*fFreq,fY*fFreq,fZ*fFreq);	
	inline float Noise3D(float x, float y, float z)
	{
		// Compute what gradients to use
		int qx0 = (int)floorf(x);
		int qx1 = qx0 + 1;
		float tx0 = x - (float)qx0;
		float tx1 = tx0 - 1;

		int qy0 = (int)floorf(y);
		int qy1 = qy0 + 1;
		float ty0 = y - (float)qy0;
		float ty1 = ty0 - 1;

		int qz0 = (int)floorf(z);
		int qz1 = qz0 + 1;
		float tz0 = z - (float)qz0;
		float tz1 = tz0 - 1;

		// Make sure we don't come outside the lookup table
		qx0 = qx0 & NOISE_MASK;
		qx1 = qx1 & NOISE_MASK;

		qy0 = qy0 & NOISE_MASK;
		qy1 = qy1 & NOISE_MASK;

		qz0 = qz0 & NOISE_MASK;
		qz1 = qz1 & NOISE_MASK;

		// Permutate values to get pseudo randomly chosen gradients
		int q000 = m_p[(qz0 + m_p[(qy0 + m_p[qx0]) & NOISE_MASK]) & NOISE_MASK];
		int q001 = m_p[(qz0 + m_p[(qy0 + m_p[qx1]) & NOISE_MASK]) & NOISE_MASK];

		int q010 = m_p[(qz0 + m_p[(qy1 + m_p[qx0]) & NOISE_MASK]) & NOISE_MASK];
		int q011 = m_p[(qz0 + m_p[(qy1 + m_p[qx1]) & NOISE_MASK]) & NOISE_MASK];

		int q100 = m_p[(qz1 + m_p[(qy0 + m_p[qx0]) & NOISE_MASK]) & NOISE_MASK];
		int q101 = m_p[(qz1 + m_p[(qy0 + m_p[qx1]) & NOISE_MASK]) & NOISE_MASK];

		int q110 = m_p[(qz1 + m_p[(qy1 + m_p[qx0]) & NOISE_MASK]) & NOISE_MASK];
		int q111 = m_p[(qz1 + m_p[(qy1 + m_p[qx1]) & NOISE_MASK]) & NOISE_MASK];

		// Compute the dotproduct between the vectors and the gradients
		float v000 = m_gx[q000]*tx0 + m_gy[q000]*ty0 + m_gz[q000]*tz0;
		float v001 = m_gx[q001]*tx1 + m_gy[q001]*ty0 + m_gz[q001]*tz0;  

		float v010 = m_gx[q010]*tx0 + m_gy[q010]*ty1 + m_gz[q010]*tz0;
		float v011 = m_gx[q011]*tx1 + m_gy[q011]*ty1 + m_gz[q011]*tz0;

		float v100 = m_gx[q100]*tx0 + m_gy[q100]*ty0 + m_gz[q100]*tz1;
		float v101 = m_gx[q101]*tx1 + m_gy[q101]*ty0 + m_gz[q101]*tz1;  

		float v110 = m_gx[q110]*tx0 + m_gy[q110]*ty1 + m_gz[q110]*tz1;
		float v111 = m_gx[q111]*tx1 + m_gy[q111]*ty1 + m_gz[q111]*tz1;

		// Modulate with the weight function
		float wx = (3 - 2*tx0)*tx0*tx0;
		float v00 = v000 - wx*(v000 - v001);
		float v01 = v010 - wx*(v010 - v011);
		float v10 = v100 - wx*(v100 - v101);
		float v11 = v110 - wx*(v110 - v111);

		float wy = (3 - 2*ty0)*ty0*ty0;
		float v0 = v00 - wy*(v00 - v01);
		float v1 = v10 - wy*(v10 - v11);

		float wz = (3 - 2*tz0)*tz0*tz0;
		float v = v0 - wz*(v0 - v1);

		return v;
	}

protected:

	//////////////////////////////////////////////////////////////////////////
	// this needs to be called only once and it's already done by crysystem in the singleton constructor
	// Note that every time the initialization function gets called, the PRNG will return 
	// different values, thus creating different gradients. Not a bad thing but probably
	// not your expected behavior
	inline void Initialize()
	{
		int i, j, nSwap;
		// seed is already set in PRNG in crysystem cryrand

		// Initialize the permutation table
		for(i = 0; i < NOISE_TABLE_SIZE; i++)
			m_p[i] = i;

		for(i = 0; i < NOISE_TABLE_SIZE; i++)
		{		
			//j = rand() & NOISE_MASK;
			j = (cry_rand32())&NOISE_MASK;

			nSwap = m_p[i];
			m_p[i]  = m_p[j];
			m_p[j]  = nSwap;
		}

		// Generate the gradient lookup tables
		for(i = 0; i < NOISE_TABLE_SIZE; i++)
		{		
			// Ken Perlin proposes that the gradients are taken from the unit 
			// circle/sphere for 2D/3D.
			// So lets generate a good pseudo-random vector and normalize it

			Vec3 v;
			// cry_frand is in the 0..1 range
			v.x=-0.5f+cry_frand(); 
			v.y=-0.5f+cry_frand();
			v.z=-0.5f+cry_frand();
			v.Normalize();		
			m_gx[i] = v.x; 
			m_gy[i] = v.y;
			m_gz[i] = v.z;
		}	
	}

	// Permutation table
	unsigned char m_p[NOISE_TABLE_SIZE];

	// Gradients
	float m_gx[NOISE_TABLE_SIZE];
	float m_gy[NOISE_TABLE_SIZE];
	float m_gz[NOISE_TABLE_SIZE];
};
