131 lines
3.6 KiB
C++
131 lines
3.6 KiB
C++
#include <psemek/pcg/perlin.hpp>
|
|
|
|
#include <psemek/geom/interval.hpp>
|
|
|
|
#include <psemek/util/assert.hpp>
|
|
|
|
namespace psemek::pcg
|
|
{
|
|
|
|
perlin::perlin(gfx::basic_pixmap<geom::vector<float, 2>> grad_map)
|
|
: grad_map_(std::move(grad_map))
|
|
{}
|
|
|
|
perlin::perlin(gfx::basic_pixmap<geom::vector<float, 2>> const & grad_map, seamless_tag)
|
|
{
|
|
auto const w = grad_map.width();
|
|
auto const h = grad_map.height();
|
|
|
|
grad_map_.resize({w + 1, h + 1});
|
|
|
|
for (std::size_t j = 0; j <= h; ++j)
|
|
{
|
|
for (std::size_t i = 0; i <= w; ++i)
|
|
{
|
|
grad_map_(i, j) = grad_map(i % w, j % h);
|
|
}
|
|
}
|
|
}
|
|
|
|
static float step(float x0, float x1, float t)
|
|
{
|
|
return x0 * (1.f - t) + x1 * t;
|
|
}
|
|
|
|
static float smoothstep(float x0, float x1, float t)
|
|
{
|
|
float const s = t * t * (3.f - 2.f * t);
|
|
return step(x0, x1, s);
|
|
}
|
|
|
|
float perlin::operator()(float x, float y) const
|
|
{
|
|
assert(x >= 0.f);
|
|
assert(y >= 0.f);
|
|
|
|
assert(x <= 1.f);
|
|
assert(y <= 1.f);
|
|
|
|
x *= width();
|
|
y *= height();
|
|
|
|
int const ix = geom::clamp<int>(std::floor(x), {0, static_cast<int>(width()) - 1});
|
|
int const iy = geom::clamp<int>(std::floor(y), {0, static_cast<int>(height()) - 1});
|
|
|
|
float const tx = x - ix;
|
|
float const ty = y - iy;
|
|
|
|
float const d00 = tx * grad_map_(ix, iy)[0] + ty * grad_map_(ix, iy)[1];
|
|
float const d10 = (tx-1.f) * grad_map_(ix+1, iy)[0] + ty * grad_map_(ix+1, iy)[1];
|
|
float const d01 = tx * grad_map_(ix, iy+1)[0] + (ty-1.f) * grad_map_(ix, iy+1)[1];
|
|
float const d11 = (tx-1.f) * grad_map_(ix+1, iy+1)[0] + (ty-1.f) * grad_map_(ix+1, iy+1)[1];
|
|
|
|
float v = smoothstep(smoothstep(d00, d10, tx), smoothstep(d01, d11, tx), ty) * std::sqrt(2.f);
|
|
return 0.5f + v * 0.5f;
|
|
}
|
|
|
|
static std::vector<float> estimate_weights(std::vector<gfx::basic_pixmap<geom::vector<float, 2>>> const & grad_maps)
|
|
{
|
|
std::vector<float> weights(grad_maps.size());
|
|
|
|
float sum = 0.f;
|
|
for (std::size_t i = 0; i < grad_maps.size(); ++i)
|
|
{
|
|
auto const & m = grad_maps[i];
|
|
weights[i] = 1.f / std::sqrt(m.width() * m.width() + m.height() * m.height());
|
|
sum += weights[i];
|
|
}
|
|
|
|
for (std::size_t i = 0; i < grad_maps.size(); ++i)
|
|
weights[i] /= sum;
|
|
|
|
return weights;
|
|
}
|
|
|
|
fractal_perlin::fractal_perlin(std::vector<gfx::basic_pixmap<geom::vector<float, 2>>> grad_maps)
|
|
{
|
|
weights_ = estimate_weights(grad_maps);
|
|
|
|
octaves_.resize(grad_maps.size());
|
|
for (std::size_t i = 0; i < grad_maps.size(); ++i)
|
|
octaves_[i] = perlin(std::move(grad_maps[i]));
|
|
}
|
|
|
|
fractal_perlin::fractal_perlin(std::vector<gfx::basic_pixmap<geom::vector<float, 2>>> grad_maps, std::vector<float> weights)
|
|
: weights_{std::move(weights)}
|
|
{
|
|
assert(weights_.size() == grad_maps.size());
|
|
|
|
octaves_.resize(grad_maps.size());
|
|
for (std::size_t i = 0; i < grad_maps.size(); ++i)
|
|
octaves_[i] = perlin(std::move(grad_maps[i]));
|
|
}
|
|
|
|
fractal_perlin::fractal_perlin(std::vector<gfx::basic_pixmap<geom::vector<float, 2>>> const & grad_maps, seamless_tag)
|
|
{
|
|
weights_ = estimate_weights(grad_maps);
|
|
|
|
octaves_.resize(grad_maps.size());
|
|
for (std::size_t i = 0; i < grad_maps.size(); ++i)
|
|
octaves_[i] = perlin(grad_maps[i], seamless);
|
|
}
|
|
|
|
fractal_perlin::fractal_perlin(std::vector<gfx::basic_pixmap<geom::vector<float, 2>>> const & grad_maps, std::vector<float> weights, seamless_tag)
|
|
: weights_{std::move(weights)}
|
|
{
|
|
assert(weights_.size() == grad_maps.size());
|
|
|
|
octaves_.resize(grad_maps.size());
|
|
for (std::size_t i = 0; i < grad_maps.size(); ++i)
|
|
octaves_[i] = perlin(grad_maps[i], seamless);
|
|
}
|
|
|
|
float fractal_perlin::operator()(float x, float y) const
|
|
{
|
|
float v = 0.f;
|
|
for (std::size_t i = 0; i < octaves_.size(); ++i)
|
|
v += weights_[i] * octaves_[i](x, y);
|
|
return v;
|
|
}
|
|
|
|
}
|