From 8ee99e7dad2d52890a83efc5dc9302ee843a5cdc Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sun, 7 Mar 2021 21:07:09 +0300 Subject: [PATCH] Add lazy perlin noise generator --- libs/pcg/include/psemek/pcg/lazy_perlin.hpp | 85 +++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 libs/pcg/include/psemek/pcg/lazy_perlin.hpp diff --git a/libs/pcg/include/psemek/pcg/lazy_perlin.hpp b/libs/pcg/include/psemek/pcg/lazy_perlin.hpp new file mode 100644 index 00000000..beb4b25f --- /dev/null +++ b/libs/pcg/include/psemek/pcg/lazy_perlin.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include + +#include + +namespace psemek::pcg +{ + + template + struct lazy_perlin + { + using generator_func = util::function(geom::vector const &)>; + + lazy_perlin (std::size_t grid_size, generator_func generator) + : grid_size_(grid_size) + , gen_(std::move(generator)) + {} + + template + T operator()(Args const & ... args) const + { + return (*this)(geom::vector{args...}); + } + + T operator()(geom::vector p) const; + + private: + int const grid_size_; + mutable std::unordered_map, geom::vector> grid_; + generator_func gen_; + + geom::vector grid_at(geom::vector const & c) const + { + auto it = grid_.find(c); + if (it == grid_.end()) + it = grid_.insert({c, gen_(c)}).first; + return it->second; + } + }; + + template + T lazy_perlin::operator()(geom::vector p) const + { + geom::vector ip; + for (std::size_t i = 0; i < N; ++i) + { + ip[i] = (p[i] >= 0) ? (p[i] / grid_size_) : -((- p[i] + grid_size_ - 1) / grid_size_); + } + + geom::vector t = (p - geom::cast(ip) * T(grid_size_)) / T(grid_size_); + + T values[1 << N]; + + for (std::size_t mask = 0; mask < (1 << N); ++mask) + { + geom::vector tt; + geom::vector ii; + for (std::size_t i = 0; i < N; ++i) + { + bool m = (mask & (1 << i)); + tt[i] = m ? t[i] - 1 : t[i]; + ii[i] = m ? ip[i] + 1 : ip[i]; + } + values[mask] = geom::dot(grid_at(ii), tt); + } + + auto smootherstep = [](float x0, float x1, float t) + { + auto const s = t * t * t * (10. + t * (-15. + 6. * t)); + return x0 * (1 - s) + x1 * s; + }; + + for (std::size_t i = N; i --> 0;) + { + for (std::size_t mask = 0; mask < (1 << i); ++mask) + values[mask] = smootherstep(values[mask], values[mask | (1 << i)], t[i]); + } + + return 0.5 * (1. + std::sqrt(2.) * values[0]); + } + +}