// Noise.h // Declares the cNoise, cCubicNoise and cPerlinNoise classes for generating noise #pragma once /** The datatype used by all the noise generators. */ typedef float NOISE_DATATYPE; #include "../Vector3.h" #include "OctavedNoise.h" #include "RidgedNoise.h" class cNoise { public: cNoise(int a_Seed); cNoise(const cNoise & a_Noise); // The following functions, if not marked INLINE, are about 20 % slower inline NOISE_DATATYPE IntNoise1D(int a_X) const; inline NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const; inline NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const; inline NOISE_DATATYPE IntNoise3D(Vector3i a_Pos) const; // Return a float number in the specified range: inline NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const { return a_Min + std::abs(IntNoise2D(a_X, a_Y)) * (a_Max - a_Min); } // Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify. inline int IntNoise1DInt(int a_X) const; inline int IntNoise2DInt(int a_X, int a_Y) const; inline int IntNoise3DInt(int a_X, int a_Y, int a_Z) const; inline int IntNoise3DInt(Vector3i a_Pos) const; NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const; NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const; NOISE_DATATYPE CubicNoise1D (NOISE_DATATYPE a_X) const; NOISE_DATATYPE SmoothNoise1D(int a_X) const; NOISE_DATATYPE CubicNoise2D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const; NOISE_DATATYPE CubicNoise3D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const; void SetSeed(int a_Seed) { m_Seed = a_Seed; } int GetSeed(void) const { return m_Seed; } inline static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct); inline static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct); inline static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct); private: int m_Seed; } ; class cCubicNoise { public: /** Maximum size of each dimension of the query arrays. */ static const int MAX_SIZE = 512; /** Creates a new instance with the specified seed. */ cCubicNoise(int a_Seed); /** Fills a 2D array with the values of the noise. */ void Generate2D( NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] int a_SizeX, int a_SizeY, ///< Count of the array, in each direction NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction ) const; /** Fills a 3D array with the values of the noise. */ void Generate3D( NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction ) const; protected: /** Noise used for integral random values. */ cNoise m_Noise; /** Calculates the integral and fractional parts along one axis. a_Floor will receive the integral parts (array of a_Size ints). a_Frac will receive the fractional parts (array of a_Size floats). a_Same will receive the counts of items that have the same integral parts (array of up to a_Size ints). a_NumSame will receive the count of a_Same elements (total count of different integral parts). */ void CalcFloorFrac( int a_Size, NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End, int * a_Floor, NOISE_DATATYPE * a_Frac, int * a_Same, int & a_NumSame ) const; } ; /** Improved noise, as described by Ken Perlin: https://mrl.nyu.edu/~perlin/paper445.pdf Implementation adapted from Perlin's Java implementation: https://mrl.nyu.edu/~perlin/noise/ */ class cImprovedNoise { public: /** Constructs a new instance of the noise obbject. Note that this operation is quite expensive (the permutation array being constructed). */ cImprovedNoise(int a_Seed); /** Fills a 2D array with the values of the noise. */ void Generate2D( NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] int a_SizeX, int a_SizeY, ///< Count of the array, in each direction NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction ) const; /** Fills a 3D array with the values of the noise. */ void Generate3D( NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction ) const; /** Returns the value at the specified integral coords. Used for raw speed measurement. */ NOISE_DATATYPE GetValueAt(int a_X, int a_Y, int a_Z); protected: /** The permutation table used by the noise function. Initialized using seed. */ int m_Perm[512]; /** Calculates the fade curve, 6 * t^5 - 15 * t^4 + 10 * t^3. */ inline static NOISE_DATATYPE Fade(NOISE_DATATYPE a_T) { return a_T * a_T * a_T * (a_T * (a_T * 6 - 15) + 10); } /** Returns the gradient value based on the hash. */ inline static NOISE_DATATYPE Grad(int a_Hash, NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) { int hash = a_Hash % 16; NOISE_DATATYPE u = (hash < 8) ? a_X : a_Y; NOISE_DATATYPE v = (hash < 4) ? a_Y : (((hash == 12) || (hash == 14)) ? a_X : a_Z); return (((hash & 1) == 0) ? u : -u) + (((hash & 2) == 0) ? v : -v); } }; typedef cOctavedNoise cPerlinNoise; typedef cOctavedNoise> cRidgedMultiNoise; //////////////////////////////////////////////////////////////////////////////// // Inline function definitions: // These need to be in the header, otherwise linker error occur in MSVC NOISE_DATATYPE cNoise::IntNoise1D(int a_X) const { int x = ((a_X * m_Seed) << 13) ^ a_X; return (1 - static_cast((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824); // returns a float number in the range of [-1, 1] } NOISE_DATATYPE cNoise::IntNoise2D(int a_X, int a_Y) const { int n = a_X + a_Y * 57 + m_Seed * 57 * 57; n = (n << 13) ^ n; return (1 - static_cast((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824); // returns a float number in the range of [-1, 1] } NOISE_DATATYPE cNoise::IntNoise3D(int a_X, int a_Y, int a_Z) const { int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57; n = (n << 13) ^ n; return (static_cast(1) - static_cast((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f ); // returns a float number in the range of [-1, 1] } NOISE_DATATYPE cNoise::IntNoise3D(Vector3i a_Pos) const { return IntNoise3D(a_Pos.x, a_Pos.y, a_Pos.z); } int cNoise::IntNoise1DInt(int a_X) const { int x = ((a_X * m_Seed) << 13) ^ a_X; return ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff); } int cNoise::IntNoise2DInt(int a_X, int a_Y) const { int n = a_X + a_Y * 57 + m_Seed * 57 * 57; n = (n << 13) ^ n; return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); } int cNoise::IntNoise3DInt(int a_X, int a_Y, int a_Z) const { int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57; n = (n << 13) ^ n; return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); } int cNoise::IntNoise3DInt(Vector3i a_Pos) const { return IntNoise3DInt(a_Pos.x, a_Pos.y, a_Pos.z); } NOISE_DATATYPE cNoise::CubicInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct) { NOISE_DATATYPE P = (a_D - a_C) - (a_A - a_B); NOISE_DATATYPE Q = (a_A - a_B) - P; NOISE_DATATYPE R = a_C - a_A; NOISE_DATATYPE S = a_B; return ((P * a_Pct + Q) * a_Pct + R) * a_Pct + S; } NOISE_DATATYPE cNoise::CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) { const NOISE_DATATYPE ft = a_Pct * static_cast(3.1415927); const NOISE_DATATYPE f = static_cast(static_cast(1 - cos(ft)) * static_cast(0.5)); return a_A * (1 - f) + a_B * f; } NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) { return a_A * (1 - a_Pct) + a_B * a_Pct; } //////////////////////////////////////////////////////////////////////////////// // Global functions: /** Exports the noise array into a file. a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */ extern void Debug2DNoise(const NOISE_DATATYPE * a_Array, size_t a_SizeX, size_t a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32); /** Exports the noise array into a set of files, ordered by XY and XZ. a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */ extern void Debug3DNoise(const NOISE_DATATYPE * a_Array, size_t a_SizeX, size_t a_SizeY, size_t a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32); /** Linearly interpolates between two values. Assumes that a_Ratio is in range [0, 1]. */ inline NOISE_DATATYPE Lerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio) { return a_Val1 + (a_Val2 - a_Val1) * a_Ratio; } /** Linearly interpolates between two values, clamping the ratio to [0, 1] first. */ inline NOISE_DATATYPE ClampedLerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio) { if (a_Ratio < 0) { return a_Val1; } if (a_Ratio > 1) { return a_Val2; } return Lerp(a_Val1, a_Val2, a_Ratio); }