#pragma once namespace DFHack { namespace Random { /* * A good explanation: * http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html */ // Interpolation functions template inline T s_curve(T t) { // Classical function //return t * t * (3 - 2*t); // 2002 version from http://mrl.nyu.edu/~perlin/paper445.pdf return t * t * t * (t * (t * 6 - 15) + 10); } template inline T lerp(T s, T a, T b) { return a + s * (b-a); } // Dot product of VSIZE vectors pointed by pa, pb template struct DotProduct { static inline T eval(T *pa, T *pb); }; template struct DotProduct { static inline T eval(T *pa, T *pb) { return pa[0]*pb[0]; } }; template inline T DotProduct::eval(T *pa, T *pb) { return DotProduct::eval(pa, pb) + pa[i]*pb[i]; } // Templates used to force unrolling and inlining of the loops template template struct PerlinNoise::Impl { typedef typename PerlinNoise::Temp Temp; static inline void setup(PerlinNoise *, const T *, Temp *) {} static inline T eval(PerlinNoise *self, Temp *pt, unsigned idx, T *pq); }; // Initialization of the temporaries from input coordinates template template inline void PerlinNoise::Impl::setup( PerlinNoise *self, const T *pv, Temp *pt ) { Impl::setup(self, pv, pt); int32_t t = int32_t(pv[i]); t -= (pv[i]idxmap[i][b & mask]; pt[i].b1 = self->idxmap[i][(b+1) & mask]; } // Main recursion. Uses tables from self and pt. // Recursion changes current index idx, and current offset vector pq. template template inline T PerlinNoise::Impl::eval( PerlinNoise *self, Temp *pt, unsigned idx, T *pq ) { return DotProduct::eval(pq, self->gradients[idx]); } template template inline T PerlinNoise::Impl::eval( PerlinNoise *self, Temp *pt, unsigned idx, T *pq ) { pq[i] = pt[i].r0; T u = Impl::eval(self, pt, idx ^ pt[i].b0, pq); pq[i] -= 1; T v = Impl::eval(self, pt, idx ^ pt[i].b1, pq); return lerp(pt[i].s, u, v); } // Actual methods of the object template void PerlinNoise::init(MersenneRNG &rng) { STATIC_ASSERT(VSIZE > 0 && BITS <= 8*sizeof(IDXT)); // Random unit gradient vectors for (unsigned i = 0; i < TSIZE; i++) rng.unitvector(gradients[i], VSIZE); // Random permutation tables for (unsigned j = 0; j < VSIZE; j++) { for (unsigned i = 0; i < TSIZE; i++) idxmap[j][i] = i; rng.permute(idxmap[j], TSIZE); } } template T PerlinNoise::eval(const T coords[VSIZE]) { // Precomputed properties from the coordinates Temp tmp[VSIZE]; // Temporary used to build the current offset vector T q[VSIZE]; Impl::setup(this, coords, tmp); return Impl::eval(this, tmp, 0, q); } }} // namespace