//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // 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. //----------------------------------------------------------------------------- #include "util/noise2d.h" #include "core/util/tVector.h" //-------------------------------------- Noise2D::Noise2D() { dMemset(mPermutation, 0, sizeof(mPermutation)); dMemset(mGradient, 0, sizeof(mGradient)); mSeed = 0; } Noise2D::~Noise2D() { } //-------------------------------------- void Noise2D::normalize(F32 v[2]) { F32 s; s = mSqrt(v[0] * v[0] + v[1] * v[1]); v[0] = v[0] / s; v[1] = v[1] / s; } //-------------------------------------- void Noise2D::setSeed(U32 seed) { if (mSeed == seed) return; mSeed = seed; mRandom.setSeed(mSeed); S32 i, j, k; for (i = 0 ; i < SIZE ; i++) { mPermutation[i] = i; for (j = 0 ; j < 2 ; j++) mGradient[i][j] = mRandom.randF( -1.0f, 1.0f ); normalize(mGradient[i]); } while (--i) { k = mPermutation[i]; j = mRandom.randI(0, SIZE-1); mPermutation[i] = mPermutation[j]; mPermutation[j] = k; } // extend the size of the arrays x2 to get rid of a bunch of MODs // we'd have to do later in the code for (i = 0 ; i < SIZE + 2 ; i++) { mPermutation[SIZE + i] = mPermutation[i]; for (j = 0 ; j < 2 ; j++) mGradient[SIZE + i][j] = mGradient[i][j]; } } //-------------------------------------- U32 Noise2D::getSeed() { return mSeed; } inline F32 Noise2D::curve(F32 t) { return t * t * (3.0f - 2.0f * t); } inline F32 clamp(F32 f, F32 m) { while (f > m) f -= m; while (f < 0.0f) f += m; return f; } //-------------------------------------- void Noise2D::fBm( Vector *dst, U32 size, U32 interval, F32 h, F32 octaves ) { interval = getMin(U32(128), getMax(U32(1), interval)); F32 H = getMin(1.0f, getMax(0.0f, h)); octaves = getMin(5.0f, getMax(1.0f, octaves)); F32 lacunarity = 2.0f; F32 exponent_array[32]; U32 shift = getBinLog2( size ); // precompute and store spectral weights // seize required memory for exponent_array F32 frequency = 1.0; for (U32 i=0; i<=octaves; i++) { // compute weight for each frequency exponent_array[i] = mPow( frequency, -H ); frequency *= lacunarity; } // initialize dst for (S32 k=0; k < (size*size); k++) (*dst)[k] = 0.0f; F32 scale = 1.0f / (F32)size * interval; for (S32 o=0; o *dst, Vector *sig, U32 size, U32 interval, F32 h, F32 octaves) { interval = getMin(U32(128), getMax(U32(1), interval)); F32 H = getMin(1.0f, getMax(0.0f, h)); octaves = getMin(5.0f, getMax(1.0f, octaves)); F32 lacunarity = 2.0f; F32 offset = 1.0f; F32 gain = 2.0f; U32 shift = getBinLog2( size ); F32 exponent_array[32]; // precompute and store spectral weights // seize required memory for exponent_array F32 frequency = 1.0; for (U32 i=0; i<=octaves; i++) { // compute weight for each frequency exponent_array[i] = mPow( frequency, -H ); frequency *= lacunarity; } F32 scale = 1.0f / (F32)size * interval; //-------------------------------------- // compute first octave for (S32 y=0; y *src, Vector *dst, U32 iterations, U32 size ) { // early out if there is nothing to do if (iterations == 0 ) { *dst = *src; return true; } F32 fmin, fmax; getMinMax( src, &fmin, &fmax, size); U32 shift = getBinLog2( size ); U32 mask = size - 1; // currently using SCRATCH_3 for debugging -- Rick Vector scratch = *src; U32 *o = (U32*)scratch.address(); Vector a = *src; Vector b = *src; Vector c = *src; for (S32 k=0; k < (size*size); k++) c[k] = 0.0f; for (S32 i=0; i maxDelta) { maxDelta = delta; o[srcOffset] = adjOffset; } } } } } } for (S32 j=0; j < (size*size); j++) { F32 &s = a[j]; F32 &d = b[ o[j] ]; F32 delta = s - d; if (delta > 0.0f) { F32 alt = (s-fmin) / (fmax-fmin); F32 amt = delta * (0.1f * (1.0f-alt)); s -= amt; d += amt; } } // debug only for (S32 k=0; k < (size*size); k++) c[k] += b[k] - a[k]; Vector tmp = a; a = b; b = tmp; } *dst = b; //*dst = *c; return true; } bool Noise2D::erodeThermal(Vector *src, Vector *dst, F32 slope, F32 materialLoss, U32 iterations, U32 size, U32 squareSize, F32 maxHeight ) { // early out if there is nothing to do if (iterations == 0 ) { *dst = *src; return true; } F32 fmin, fmax; getMinMax(src, &fmin, &fmax, size); Vector a = *src; // Heightfield *b = getScratch(1); Vector r; r.setSize( size * size ); //dMemset( r.address(), 0, r.memSize() ); F32 conservation = 1.0f - mClampF(materialLoss, 0.0f, 100.0f)/100.0f; slope = mClampF(conservation, 0.0f, 89.0f); // clamp to 0-89 degrees F32 talusConst = mTan(mDegToRad(slope)) * squareSize; // in world units talusConst = talusConst * (fmax-fmin) / maxHeight; // scale to current height units F32 p = 0.1f; U32 mask = size - 1; U32 shift = getBinLog2( size ); for (U32 i=0; i talusConst) { F32 rubble = p * (delta - talusConst); r[adjOffset] -= rubble; *dstHeight += rubble * conservation; } } } } } } for (S32 k=0; k < (size*size); k++) a[k] += r[k]; } *dst = a; return true; } void Noise2D::getMinMax( Vector *src, F32 *fmin, F32 *fmax, U32 size ) { if (!src) return; F32 *p = (*src).address(); *fmin = *p; *fmax = *p; for (S32 i=0; i < (size*size); i++, p++) { if (*fmin > *p) *fmin = *p; if (*fmax < *p) *fmax = *p; } } //-------------------------------------- F32 Noise2D::turbulence(F32 x, F32 y, F32 freq) { F32 t, x2, y2; for ( t = 0.0f ; freq >= 3.0f ; freq /= 2.0f) { x2 = freq * x; y2 = freq * y; t += mFabs(getValue(x2, y2, (S32)freq)) / freq; } return t; } //-------------------------------------- inline void Noise2D::setup(F32 t, S32 &b0, S32 &b1, F32 &r0, F32 &r1) { // find the bounding integers of u b0 = S32(t) & SIZE_MASK; b1 = (b0+1) & SIZE_MASK; // seperate the fractional components r0 = t - (S32)t; r1 = r0 - 1.0f; } inline F32 Noise2D::dot(const F32 *q, F32 rx, F32 ry) { return (rx * q[0] + ry * q[1] ); } //-------------------------------------- F32 Noise2D::getValue(F32 x, F32 y, S32 interval) { S32 bx0, bx1, by0, by1; F32 rx0, rx1, ry0, ry1; // Imagine having a square of the type // p0---p1 Where p0 = (bx0, by0) +----> U // |(u,v)| p1 = (bx1, by0) | // | | p2 = (bx0, by1) | Coordinate System // p2---p3 p3 = (bx1, by1) V // The u, v point in 2D texture space is bounded by this rectangle. // Goal, determine the scalar at the points p0, p1, p2, p3. // Then the scalar of the point (u, v) will be found by linear interpolation. // First step: Get the 2D coordinates of the points p0, p1, p2, p3. // We also need vectors pointing from each point in the square above and // ending at the (u,v) coordinate located inside the square. // The vector (rx0, ry0) goes from P0 to the (u,v) coordinate. // The vector (rx1, ry0) goes from P1 to the (u,v) coordinate. // The vector (rx0, ry1) goes from P2 to the (u,v) coordinate. // The vector (rx1, ry1) goes from P3 to the (u,v) coordinate. setup(x, bx0, bx1, rx0, rx1); setup(y, by0, by1, ry0, ry1); // Make sure the box corners fall within the interval // so that the final output will wrap on itself bx0 = bx0 % interval; bx1 = bx1 % interval; by0 = by0 % interval; by1 = by1 % interval; S32 i = mPermutation[ bx0 ]; S32 j = mPermutation[ bx1 ]; S32 b00 = mPermutation[ i + by0 ]; S32 b10 = mPermutation[ j + by0 ]; S32 b01 = mPermutation[ i + by1 ]; S32 b11 = mPermutation[ j + by1 ]; // Next, calculate the dropoff component about the point p0. F32 sx = curve(rx0); F32 sy = curve(ry0); // Now, for each point in the square shown above, calculate the dot // product of the gradiant vector and the vector going from each square // corner point to the (u,v) point inside the square. F32 u = dot(mGradient[ b00 ], rx0,ry0); F32 v = dot(mGradient[ b10 ], rx1,ry0); // Interpolation along the X axis. F32 a = mLerp(u, v,sx); u = dot(mGradient[ b01 ], rx0,ry1); v = dot(mGradient[ b11 ], rx1,ry1); // Interpolation along the Y axis. F32 b = mLerp( u, v, sx); // Final Interpolation return mLerp(a, b,sy); }