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
|
#pragma once
|
||||||
|
|
||||||
#include <psemek/util/function.hpp>
|
#include <psemek/util/function.hpp>
|
||||||
|
#include <psemek/util/array.hpp>
|
||||||
|
|
||||||
#include <psemek/geom/vector.hpp>
|
#include <psemek/geom/vector.hpp>
|
||||||
|
#include <psemek/geom/box.hpp>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace psemek::pcg
|
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>
|
template <typename T, std::size_t N>
|
||||||
struct lazy_perlin
|
struct lazy_perlin
|
||||||
{
|
{
|
||||||
|
|
@ -17,7 +66,7 @@ namespace psemek::pcg
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
static constexpr auto dimension = N;
|
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)
|
: grid_size_(grid_size)
|
||||||
, gen_(std::move(generator))
|
, gen_(std::move(generator))
|
||||||
{}
|
{}
|
||||||
|
|
@ -39,44 +88,67 @@ namespace psemek::pcg
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
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;
|
using generator_func = util::function<geom::vector<T, N>(geom::vector<int, N> const &)>;
|
||||||
for (std::size_t i = 0; i < N; ++i)
|
|
||||||
|
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)
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
{
|
{
|
||||||
bool m = (mask & (1 << i));
|
origin_[i] = bounds[i].min / grid_size;
|
||||||
tt[i] = m ? t[i] - 1 : t[i];
|
dims[i] = bounds[i].length() / grid_size + 1;
|
||||||
ii[i] = m ? ip[i] + 1 : ip[i];
|
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)
|
T operator()(geom::vector<T, N> p) const;
|
||||||
{
|
|
||||||
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;)
|
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)
|
std::array<std::size_t, N> idx;
|
||||||
values[mask] = smootherstep(values[mask], values[mask | (1 << i)], t[i]);
|
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