From 07513b078c043bae4561f7dfdfb7227608b2682a Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Mon, 18 May 2015 17:16:04 +1000 Subject: [PATCH] noise/basis: parameterise basis functions --- noise/basis.cpp | 220 +++++++++++++++++++++++++++------------------- noise/basis.hpp | 34 ++++--- noise/fractal.cpp | 24 ++--- noise/fractal.hpp | 8 +- tools/noise.cpp | 6 +- 5 files changed, 171 insertions(+), 121 deletions(-) diff --git a/noise/basis.cpp b/noise/basis.cpp index ffdffe1f..6cb07fe0 100644 --- a/noise/basis.cpp +++ b/noise/basis.cpp @@ -34,93 +34,116 @@ using util::noise::cellular; // Generate a type from [-UNIT..UNIT] template T -generate (intmax_t x, intmax_t y, basis::seed_t); - - -//----------------------------------------------------------------------------- -template <> -double -generate (intmax_t x, intmax_t y, basis::seed_t seed) { +generate (intmax_t x, intmax_t y, uint64_t seed) +{ size_t idx = util::noise::permute (x, y, seed); - return util::noise::LUT[idx]; + return T(util::noise::LUT[idx]); } //----------------------------------------------------------------------------- template <> util::vector2d -generate (intmax_t x, intmax_t y, basis::seed_t seed) { +generate (intmax_t x, intmax_t y, uint64_t seed) +{ auto u = util::noise::permute (x, y, seed); auto v = util::noise::permute (u ^ seed); - return util::vector2d (util::noise::LUT[u], util::noise::LUT[v]); + return { + util::noise::LUT[u], + util::noise::LUT[v] + }; +} + +//----------------------------------------------------------------------------- +template <> +util::vector2f +generate (intmax_t x, intmax_t y, uint64_t seed) +{ + auto u = util::noise::permute (x, y, seed); + auto v = util::noise::permute (u ^ seed); + + return { + float (util::noise::LUT[u]), + float (util::noise::LUT[v]) + }; } /////////////////////////////////////////////////////////////////////////////// -basis::basis (seed_t _seed): +template +basis::basis (seed_t _seed): seed (_seed) { ; } //----------------------------------------------------------------------------- -basis::basis (): +template +basis::basis (): seed (util::random ()) { ; } //----------------------------------------------------------------------------- -basis::~basis () +template +basis::~basis () { ; } //----------------------------------------------------------------------------- -double -basis::eval (double, double) const +template +T +basis::eval (T, T) const { unreachable (); } +//----------------------------------------------------------------------------- +namespace util { namespace noise { + template struct basis; + template struct basis; +} } + /////////////////////////////////////////////////////////////////////////////// -template L> -value::value (seed_t _seed): - basis (_seed) +template L> +value::value (seed_t _seed): + basis (_seed) { ; } //----------------------------------------------------------------------------- -template L> -value::value () +template L> +value::value () { ; } //----------------------------------------------------------------------------- -template L> -util::range -value::bounds (void) const - { return { -1.0, 1.0 }; } +template L> +util::range +value::bounds (void) const + { return { -1, 1 }; } //----------------------------------------------------------------------------- -template L> -double -value::eval (double x, double y) const { +template L> +T +value::eval (T x, T y) const { intmax_t x_int = static_cast (x); intmax_t y_int = static_cast (y); - double x_fac = x - x_int; - double y_fac = y - y_int; + T x_fac = x - x_int; + T y_fac = y - y_int; // Shift the coordinate system down a little to ensure we get unit weights // for the lerp. It's better to do this than abs the fractional portion so // we don't get reflections along the origin. - if (x < 0) { x_fac = 1.0 + x_fac; x_int -= 1; } - if (y < 0) { y_fac = 1.0 + y_fac; y_int -= 1; } + if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; } + if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; } // Generate the four corner values - double p0 = generate (x_int, y_int, this->seed); - double p1 = generate (x_int + 1, y_int, this->seed); - double p2 = generate (x_int, y_int + 1, this->seed); - double p3 = generate (x_int + 1, y_int + 1, this->seed); + T p0 = generate (x_int, y_int, this->seed); + T p1 = generate (x_int + 1, y_int, this->seed); + T p2 = generate (x_int, y_int + 1, this->seed); + T p3 = generate (x_int + 1, y_int + 1, this->seed); // Interpolate on one dimension, then the other. return L (L (p0, p1, x_fac), @@ -130,62 +153,64 @@ value::eval (double x, double y) const { //----------------------------------------------------------------------------- -namespace util { - namespace noise { - template struct value; - template struct value; - template struct value; - } -} +namespace util { namespace noise { + template struct value; + template struct value; + template struct value; + + template struct value; + template struct value; + template struct value; +} } /////////////////////////////////////////////////////////////////////////////// -template L> -gradient::gradient (seed_t _seed): - basis (_seed) +template L> +gradient::gradient (seed_t _seed): + basis (_seed) { ; } //----------------------------------------------------------------------------- -template L> -gradient::gradient () +template L> +gradient::gradient () { ; } //----------------------------------------------------------------------------- -template L> -util::range -gradient::bounds (void) const - { return { -sqrt(2.0) / 2.0, sqrt (2.0) / 2.0 }; } +template L> +util::range +gradient::bounds (void) const + { return { -std::sqrt(T{2}) / 2, std::sqrt (T{2}) / 2 }; } //----------------------------------------------------------------------------- -template L> -double -gradient::eval (double x, double y) const { +template L> +T +gradient::eval (T x, T y) const { intmax_t x_int = static_cast (x); intmax_t y_int = static_cast (y); - double x_fac = x - x_int; - double y_fac = y - y_int; + T x_fac = x - x_int; + T y_fac = y - y_int; // Shift the coordinate system down a little to ensure we get unit weights // for the lerp. It's better to do this than abs the fractional portion so // we don't get reflections along the origin. - if (x < 0) { x_fac = 1.0 + x_fac; x_int -= 1; } - if (y < 0) { y_fac = 1.0 + y_fac; y_int -= 1; } + if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; } + if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; } // Generate the four corner values. It's not strictly necessary to // normalise the values, but we get a more consistent and visually // appealing range of outputs with normalised values. - vector2d p0 = generate (x_int, y_int, this->seed).normalise (); - vector2d p1 = generate (x_int + 1, y_int, this->seed).normalise (); - vector2d p2 = generate (x_int, y_int + 1, this->seed).normalise (); - vector2d p3 = generate (x_int + 1, y_int + 1, this->seed).normalise (); + auto p0 = generate> (x_int, y_int, this->seed).normalise (); + auto p1 = generate> (x_int + 1, y_int, this->seed).normalise (); + auto p2 = generate> (x_int, y_int + 1, this->seed).normalise (); + auto p3 = generate> (x_int + 1, y_int + 1, this->seed).normalise (); - double v0 = p0.x * x_fac + p0.y * y_fac; - double v1 = p1.x * (x_fac - 1.0) + p1.y * y_fac; - double v2 = p2.x * x_fac + p2.y * (y_fac - 1.0); - double v3 = p3.x * (x_fac - 1.0) + p3.y * (y_fac - 1.0); + T v0 = p0.x * x_fac + p0.y * y_fac; + T v1 = p1.x * (x_fac - 1) + p1.y * y_fac; + T v2 = p2.x * x_fac + p2.y * (y_fac - 1); + T v3 = p3.x * (x_fac - 1) + p3.y * (y_fac - 1); return L (L (v0, v1, x_fac), L (v2, v3, x_fac), @@ -196,45 +221,51 @@ gradient::eval (double x, double y) const { //----------------------------------------------------------------------------- namespace util { namespace noise { - template struct gradient; - template struct gradient; - template struct gradient; + template struct gradient; + template struct gradient; + template struct gradient; + + template struct gradient; + template struct gradient; + template struct gradient; } } /////////////////////////////////////////////////////////////////////////////// -cellular::cellular (seed_t _seed): - basis (_seed) +template +cellular::cellular (seed_t _seed): + basis (_seed) { ; } //----------------------------------------------------------------------------- -cellular::cellular () +template +cellular::cellular () { ; } //----------------------------------------------------------------------------- -util::range -cellular::bounds (void) const +template +util::range +cellular::bounds (void) const { return { 0.0, 1.5 }; } //----------------------------------------------------------------------------- -double -cellular::eval (double x, double y) const { - using util::point2d; - +template +T +cellular::eval (T x, T y) const { intmax_t x_int = static_cast (x); intmax_t y_int = static_cast (y); - double x_fac = x - x_int; - double y_fac = y - y_int; + T x_fac = x - x_int; + T y_fac = y - y_int; // Generate the four corner values. It's not strictly necessary to // normalise the values, but we get a more consistent and visually // appealing range of outputs with normalised values. - if (x < 0) { x_fac = 1.0 + x_fac; x_int -= 1; } - if (y < 0) { y_fac = 1.0 + y_fac; y_int -= 1; } + if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; } + if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; } // +---+---+---+ // | 0 | 1 | 2 | @@ -244,19 +275,19 @@ cellular::eval (double x, double y) const { // | 6 | 7 | 8 | // +---+---+---+ - point2d centre = { x_fac, y_fac }; - double distances[9] = { std::numeric_limits::quiet_NaN () }; - double *cursor = distances; + point<2,T> centre = { x_fac, y_fac }; + T distances[9] = { std::numeric_limits::quiet_NaN () }; + T *cursor = distances; for (signed y_off = -1; y_off <= 1 ; ++y_off) for (signed x_off = -1; x_off <= 1; ++x_off) { - auto pos = point2d (double (x_off), double (y_off)); - auto off = generate (x_int + x_off, y_int + y_off, this->seed); - off += 1.; - off /= 2.; + auto pos = point<2,T> (T (x_off), T (y_off)); + auto off = generate> (x_int + x_off, y_int + y_off, this->seed); + off += T{1}; + off /= T{2}; - CHECK (off.x >= 0 && off.x <= 1.0); - CHECK (off.y >= 0 && off.y <= 1.0); + CHECK (off.x >= 0 && off.x <= 1); + CHECK (off.y >= 0 && off.y <= 1); pos += off; *cursor++ = pos.distance2 (centre); @@ -267,3 +298,10 @@ cellular::eval (double x, double y) const { CHECK (bounds ().contains (distances[0])); return distances[0]; } + + +//----------------------------------------------------------------------------- +namespace util { namespace noise { + template struct cellular; + template struct cellular; +} } diff --git a/noise/basis.hpp b/noise/basis.hpp index a1bd30c5..460b3bb3 100644 --- a/noise/basis.hpp +++ b/noise/basis.hpp @@ -25,6 +25,7 @@ namespace util { namespace noise { template using lerp_t = T (*)(T,T,T); + template struct basis { typedef uint64_t seed_t; @@ -34,40 +35,47 @@ namespace util { seed_t seed; - virtual range bounds (void) const = 0; - virtual double eval (double x, double y) const = 0; + virtual range bounds (void) const = 0; + virtual T eval (T x, T y) const = 0; }; /// Perlin: single value per grid space - template > - struct value : public basis { + template > + struct value : public basis { + using seed_t = typename basis::seed_t; + value (seed_t); value (); - virtual range bounds (void) const; - virtual double eval (double x, double y) const; + virtual range bounds (void) const override; + virtual T eval (T x, T y) const override; }; /// Perlin: interpolated value across each grid space - template L> - struct gradient : public basis { + template L> + struct gradient : public basis { + using seed_t = typename basis::seed_t; + gradient (seed_t); gradient (); - virtual range bounds (void) const; - virtual double eval (double x, double y) const; + virtual range bounds (void) const override; + virtual T eval (T x, T y) const override; }; /// Cellular/Worley/Vornoi of order-1 - struct cellular : public basis { + template + struct cellular : public basis { + using seed_t = typename basis::seed_t; + cellular (seed_t); cellular (); - virtual range bounds (void) const; - virtual double eval (double x, double y) const; + virtual range bounds (void) const override; + virtual T eval (T x, T y) const override; }; } } diff --git a/noise/fractal.cpp b/noise/fractal.cpp index d502a250..d2862d6a 100644 --- a/noise/fractal.cpp +++ b/noise/fractal.cpp @@ -55,7 +55,7 @@ template util::noise::fbm::fbm (unsigned _octaves, double _frequency, double _lacunarity, - basis::seed_t _seed): + seed_t _seed): fractal (_octaves, _frequency, _lacunarity), basis (_seed) { ; } @@ -90,11 +90,11 @@ util::noise::fbm::eval (double x, double y) const { //----------------------------------------------------------------------------- -template struct util::noise::fbm; -template struct util::noise::fbm>; -template struct util::noise::fbm>; -template struct util::noise::fbm>; -template struct util::noise::fbm>; +template struct util::noise::fbm>; +template struct util::noise::fbm>; +template struct util::noise::fbm>; +template struct util::noise::fbm>; +template struct util::noise::fbm>; /////////////////////////////////////////////////////////////////////////////// @@ -102,7 +102,7 @@ template util::noise::musgrave::musgrave (unsigned _octaves, double _frequency, double _lacunarity, - basis::seed_t _seed): + seed_t _seed): fractal (_octaves, _frequency, _lacunarity), basis (_seed) { ; } @@ -156,9 +156,9 @@ util::noise::musgrave::eval (double x, double y) const { //----------------------------------------------------------------------------- -template struct util::noise::musgrave; -template struct util::noise::musgrave>; -template struct util::noise::musgrave>; -template struct util::noise::musgrave>; -template struct util::noise::musgrave>; +template struct util::noise::musgrave>; +template struct util::noise::musgrave>; +template struct util::noise::musgrave>; +template struct util::noise::musgrave>; +template struct util::noise::musgrave>; diff --git a/noise/fractal.hpp b/noise/fractal.hpp index b70b34e3..ce4fbf6e 100644 --- a/noise/fractal.hpp +++ b/noise/fractal.hpp @@ -41,10 +41,12 @@ namespace util { /// Fractal Brownian Motion summation. template struct fbm : public fractal { + using seed_t = typename basis::seed_t; + fbm (unsigned octaves, double frequency, double lacunarity, - basis::seed_t seed); + seed_t seed); fbm (); B basis; @@ -55,10 +57,12 @@ namespace util { /// Rigid Multifractal noise summation. template struct musgrave : public fractal { + using seed_t = typename basis::seed_t; + musgrave (unsigned octaves, double frequency, double lacunarity, - basis::seed_t seed); + seed_t seed); musgrave (); B basis; diff --git a/tools/noise.cpp b/tools/noise.cpp index 4e3e097d..a369d8e6 100644 --- a/tools/noise.cpp +++ b/tools/noise.cpp @@ -11,9 +11,9 @@ main (void) //using basis_t = util::noise::gradient; //using noise_t = util::noise::fbm; - //using noise_t = util::noise::fbm>; - //using noise_t = util::noise::musgrave>; - using noise_t = util::noise::fbm; + using noise_t = util::noise::fbm>; + //using noise_t = util::noise::musgrave>; + //using noise_t = util::noise::fbm>; util::noise::fill (img, noise_t {});