diff --git a/libs/pcg/include/psemek/pcg/lazy_perlin.hpp b/libs/pcg/include/psemek/pcg/lazy_perlin.hpp index e4e65a52..dd228c19 100644 --- a/libs/pcg/include/psemek/pcg/lazy_perlin.hpp +++ b/libs/pcg/include/psemek/pcg/lazy_perlin.hpp @@ -1,14 +1,63 @@ #pragma once #include +#include #include +#include #include namespace psemek::pcg { + namespace detail + { + + template + T perlin_impl(geom::vector p, int grid_size, F && grid_at, G && bound) + { + 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); + } + ip = bound(ip); + + 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]); + } + + } + template struct lazy_perlin { @@ -17,7 +66,7 @@ namespace psemek::pcg using value_type = T; static constexpr auto dimension = N; - lazy_perlin (std::size_t grid_size, generator_func generator) + lazy_perlin(std::size_t grid_size, generator_func generator) : grid_size_(grid_size) , gen_(std::move(generator)) {} @@ -39,44 +88,67 @@ namespace psemek::pcg }; template - T lazy_perlin::operator()(geom::vector p) const + struct bounded_lazy_perlin { - geom::vector ip; - for (std::size_t i = 0; i < N; ++i) + using generator_func = util::function(geom::vector const &)>; + + using value_type = T; + static constexpr auto dimension = N; + + bounded_lazy_perlin(std::size_t grid_size, generator_func generator, geom::box const & bounds) + : grid_size_(grid_size) + , gen_(std::move(generator)) { - ip[i] = (p[i] >= 0) ? (p[i] / grid_size_) : -((- p[i] + grid_size_ - 1) / grid_size_); - } + std::array dims; - 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]; + origin_[i] = bounds[i].min / grid_size; + dims[i] = bounds[i].length() / grid_size + 1; + corner_[i] = origin_[i] + dims[i] - 2; } - values[mask] = geom::dot(grid_at(ii), tt); + + grid_.resize(dims); } - 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; - }; + T operator()(geom::vector p) const; - for (std::size_t i = N; i --> 0;) + private: + int const grid_size_; + geom::vector origin_; + geom::vector corner_; + mutable util::array>, N> grid_; + generator_func gen_; + + geom::vector grid_at(geom::vector const & c) const { - for (std::size_t mask = 0; mask < (1 << i); ++mask) - values[mask] = smootherstep(values[mask], values[mask | (1 << i)], t[i]); + std::array idx; + for (std::size_t i = 0; i < N; ++i) + idx[i] = c[i] - origin_[i]; + auto & result = grid_(idx); + if (!result) + result = gen_(c); + return *result; } + }; - return 0.5 * (1. + std::sqrt(2.) * values[0]); + template + T lazy_perlin::operator()(geom::vector p) const + { + return detail::perlin_impl(p, grid_size_, [this](auto const & c){ return grid_at(c); }, [](auto const & p){ return p; }); + } + + template + T bounded_lazy_perlin::operator()(geom::vector p) const + { + return detail::perlin_impl(p, grid_size_, [this](auto const & c){ return grid_at(c); }, [this](auto p){ + for (std::size_t i = 0; i < N; ++i) + { + p[i] = std::max(p[i], origin_[i]); + p[i] = std::min(p[i], corner_[i]); + } + return p; + }); } }