Add a faster bounded version of pcg::lazy_perlin
This commit is contained in:
parent
ec6552d1a4
commit
45eaab6a9d
1 changed files with 99 additions and 27 deletions
|
|
@ -1,14 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/function.hpp>
|
||||
#include <psemek/util/array.hpp>
|
||||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/box.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace psemek::pcg
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename T, std::size_t N, typename F, typename G>
|
||||
T perlin_impl(geom::vector<T, N> p, int grid_size, F && grid_at, G && bound)
|
||||
{
|
||||
geom::vector<int, N> 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, N> t = (p - geom::cast<T>(ip) * T(grid_size)) / T(grid_size);
|
||||
|
||||
T values[1 << N];
|
||||
|
||||
for (std::size_t mask = 0; mask < (1 << N); ++mask)
|
||||
{
|
||||
geom::vector<T, N> tt;
|
||||
geom::vector<int, N> 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 <typename T, std::size_t N>
|
||||
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 <typename T, std::size_t N>
|
||||
T lazy_perlin<T, N>::operator()(geom::vector<T, N> p) const
|
||||
struct bounded_lazy_perlin
|
||||
{
|
||||
geom::vector<int, N> ip;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
using generator_func = util::function<geom::vector<T, N>(geom::vector<int, N> const &)>;
|
||||
|
||||
using value_type = T;
|
||||
static constexpr auto dimension = N;
|
||||
|
||||
bounded_lazy_perlin(std::size_t grid_size, generator_func generator, geom::box<int, N> 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<std::size_t, N> dims;
|
||||
|
||||
geom::vector<T, N> t = (p - geom::cast<T>(ip) * T(grid_size_)) / T(grid_size_);
|
||||
|
||||
T values[1 << N];
|
||||
|
||||
for (std::size_t mask = 0; mask < (1 << N); ++mask)
|
||||
{
|
||||
geom::vector<T, N> tt;
|
||||
geom::vector<int, N> 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<T, N> p) const;
|
||||
|
||||
for (std::size_t i = N; i --> 0;)
|
||||
private:
|
||||
int const grid_size_;
|
||||
geom::vector<int, N> origin_;
|
||||
geom::vector<int, N> corner_;
|
||||
mutable util::array<std::optional<geom::vector<T, N>>, N> grid_;
|
||||
generator_func gen_;
|
||||
|
||||
geom::vector<T, N> grid_at(geom::vector<int, N> 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<std::size_t, N> 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 <typename T, std::size_t N>
|
||||
T lazy_perlin<T, N>::operator()(geom::vector<T, N> p) const
|
||||
{
|
||||
return detail::perlin_impl(p, grid_size_, [this](auto const & c){ return grid_at(c); }, [](auto const & p){ return p; });
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T bounded_lazy_perlin<T, N>::operator()(geom::vector<T, N> 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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue