// Modifications copyright Amazon.com, Inc. or its affiliates. /* Based in part on Perlin noise http ://mrl.nyu.edu/~perlin/doc/oscar.html#noise Copyright(c) Ken Perlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CRYINCLUDE_CRYCOMMON_PNOISE3_H #define CRYINCLUDE_CRYCOMMON_PNOISE3_H #pragma once #define NOISE_TABLE_SIZE 256 #define NOISE_MASK (NOISE_TABLE_SIZE-1) namespace { const int kDefaultSeed=0; } ////////////////////////////////////////////////////////////////////////// class CPNoise3 { public: CPNoise3(void) { SetSeedAndReinitialize(kDefaultSeed); } ////////////////////////////////////////////////////////////////////////// // 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; } ////////////////////////////////////////////////////////////////////////// // 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 SetSeedAndReinitialize(uint seedValue) { int i, j, nSwap; m_random_generator.Seed(seedValue); // 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 = (m_random_generator.GenerateUint32())&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+m_random_generator.GenerateFloat(); v.y=-0.5f+m_random_generator.GenerateFloat(); v.z=-0.5f+m_random_generator.GenerateFloat(); v.Normalize(); m_gx[i] = v.x; m_gy[i] = v.y; m_gz[i] = v.z; } } private: CRndGen m_random_generator; // 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]; }; #endif // CRYINCLUDE_CRYCOMMON_PNOISE3_H