#include #include #include namespace psemek::pcg { perlin::perlin(gfx::basic_pixmap> grad_map) : grad_map_(std::move(grad_map)) {} perlin::perlin(gfx::basic_pixmap> 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(std::floor(x), {0, static_cast(width()) - 1}); int const iy = geom::clamp(std::floor(y), {0, static_cast(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 estimate_weights(std::vector>> const & grad_maps) { std::vector 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>> 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>> grad_maps, std::vector 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>> 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>> const & grad_maps, std::vector 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; } }