noise-tool: add command arguments
This commit is contained in:
parent
6e5bd1885d
commit
544d4d9624
499
tools/noise.cpp
499
tools/noise.cpp
@ -2,276 +2,168 @@
|
|||||||
#include "noise.hpp"
|
#include "noise.hpp"
|
||||||
|
|
||||||
#include "noise/fractal/fbm.hpp"
|
#include "noise/fractal/fbm.hpp"
|
||||||
#include "noise/fractal/rmf.hpp"
|
|
||||||
#include "noise/fractal/hmf.hpp"
|
|
||||||
#include "noise/fractal/hetero.hpp"
|
#include "noise/fractal/hetero.hpp"
|
||||||
|
#include "noise/fractal/hmf.hpp"
|
||||||
|
#include "noise/fractal/rmf.hpp"
|
||||||
|
#include "noise/fractal/runtime.hpp"
|
||||||
#include "noise/lerp.hpp"
|
#include "noise/lerp.hpp"
|
||||||
#include "noise/basis/constant.hpp"
|
#include "noise/basis/constant.hpp"
|
||||||
#include "noise/basis/value.hpp"
|
#include "noise/basis/value.hpp"
|
||||||
#include "noise/basis/perlin.hpp"
|
#include "noise/basis/perlin.hpp"
|
||||||
#include "noise/basis/worley.hpp"
|
#include "noise/basis/worley.hpp"
|
||||||
#include "noise/turbulence.hpp"
|
#include "noise/turbulence.hpp"
|
||||||
|
#include "noise/basis/runtime.hpp"
|
||||||
#include "extent.hpp"
|
#include "extent.hpp"
|
||||||
#include "colour.hpp"
|
#include "colour.hpp"
|
||||||
#include "netpbm.hpp"
|
#include "netpbm.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
|
#include "cmdopt.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
template struct util::noise::fractal::fbm<float, util::noise::basis::perlin<float,util::lerp::cubic>>;
|
template struct util::noise::fractal::fbm<float, util::noise::basis::perlin<float,util::lerp::cubic>>;
|
||||||
template struct util::noise::fractal::hmf<float, util::noise::basis::value<float,util::lerp::cubic>>;
|
template struct util::noise::fractal::hmf<float, util::noise::basis::value<float,util::lerp::cubic>>;
|
||||||
template struct util::noise::fractal::rmf<float, util::noise::basis::constant<float>>;
|
template struct util::noise::fractal::rmf<float, util::noise::basis::constant<float>>;
|
||||||
template struct util::noise::fractal::hetero<float, util::noise::basis::worley<float,2>>;
|
template struct util::noise::fractal::hetero<float, util::noise::basis::worley<float,2>>;
|
||||||
|
|
||||||
|
|
||||||
void
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
hydraulic_erode (util::image::buffer<float> &height, const unsigned ticks)
|
enum basis_t {
|
||||||
{
|
VALUE,
|
||||||
CHECK_GT (ticks, 0);
|
PERLIN,
|
||||||
|
WORLEY
|
||||||
std::cerr << "hydraulic_erosion\n";
|
|
||||||
|
|
||||||
const float RAINFALL = 0.001f; // quantity of water added each tick
|
|
||||||
const float SOLUBILITY = 0.1001f; // quantity of land picked up by unit rain
|
|
||||||
const float EVAPORATION = RAINFALL * .85f; // quantity of water evaporated each tick
|
|
||||||
|
|
||||||
auto water = height.alloc ();
|
|
||||||
CHECK (water.is_packed ());
|
|
||||||
CHECK (height.is_packed ());
|
|
||||||
|
|
||||||
std::fill (water.begin (), water.end (), 0);
|
|
||||||
|
|
||||||
for (unsigned t = 0; t < ticks; ++t) {
|
|
||||||
// apply new rain and erosion
|
|
||||||
for (auto &w: water)
|
|
||||||
w += RAINFALL;
|
|
||||||
for (auto &h: height)
|
|
||||||
h -= RAINFALL * SOLUBILITY;
|
|
||||||
|
|
||||||
float total = 0.f;
|
|
||||||
|
|
||||||
// move water to lowest neighbour cell
|
|
||||||
for (size_t y = 1; y < height.h - 1; ++y)
|
|
||||||
for (size_t x = 1; x < height.w - 1; ++x) {
|
|
||||||
const size_t indices[9] = {
|
|
||||||
(y - 1) * height.s + (x - 1),
|
|
||||||
(y - 1) * height.s + (x + 0),
|
|
||||||
(y - 1) * height.s + (x + 1),
|
|
||||||
|
|
||||||
(y + 0) * height.s + (x - 1),
|
|
||||||
(y + 0) * height.s + (x + 0),
|
|
||||||
(y + 0) * height.s + (x + 1),
|
|
||||||
|
|
||||||
(y + 1) * height.s + (x - 1),
|
|
||||||
(y + 1) * height.s + (x + 0),
|
|
||||||
(y + 1) * height.s + (x + 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
const float level[9] = {
|
|
||||||
height[indices[0]] + water[indices[0]],
|
|
||||||
height[indices[1]] + water[indices[1]],
|
|
||||||
height[indices[2]] + water[indices[2]],
|
|
||||||
|
|
||||||
height[indices[3]] + water[indices[3]],
|
|
||||||
height[indices[4]] + water[indices[4]],
|
|
||||||
height[indices[5]] + water[indices[5]],
|
|
||||||
|
|
||||||
height[indices[6]] + water[indices[6]],
|
|
||||||
height[indices[7]] + water[indices[7]],
|
|
||||||
height[indices[8]] + water[indices[8]],
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t dst = std::min_element (std::begin (level), std::end (level)) - level;
|
|
||||||
if (dst == 4) // if centre, bail
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// transfer as much water as possible to even the heights
|
|
||||||
float total_diff = level[4] - level[dst];
|
|
||||||
float transfer;
|
|
||||||
if (total_diff > water[4])
|
|
||||||
transfer = water[4];
|
|
||||||
else
|
|
||||||
transfer = (water[4] - total_diff) / 2;
|
|
||||||
|
|
||||||
water[indices[ 4]] -= transfer;
|
|
||||||
water[indices[dst]] += transfer;
|
|
||||||
|
|
||||||
total += transfer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaporate water, deposit sediment
|
|
||||||
for (size_t i = 0; i < water.extent ().area (); ++i) {
|
|
||||||
water[i] -= EVAPORATION;
|
|
||||||
height[i] += SOLUBILITY * EVAPORATION;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// forcibly evaporate all remaining water.
|
|
||||||
CHECK_EQ (water.extent ().area (), height.extent ().area ());
|
|
||||||
for (size_t i = 0; i < water.extent ().area (); ++i)
|
|
||||||
height[i] += water[i] * SOLUBILITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// talus: maximum desired slope in radians. [0, 90]
|
|
||||||
/// resistance: proportion of world to keep. [1, 0]
|
|
||||||
/// scale: horizontal distance scaling factor (relative to vertical)
|
|
||||||
void
|
|
||||||
thermal_erode (util::image::buffer<float> &height,
|
|
||||||
const float talus,
|
|
||||||
const float scale,
|
|
||||||
const float resistance)
|
|
||||||
{
|
|
||||||
CHECK_LIMIT (talus, 0, to_radians (90.f));
|
|
||||||
CHECK_GT (scale, 0);
|
|
||||||
CHECK_LIMIT (resistance, 0, 1);
|
|
||||||
|
|
||||||
// maximum height difference
|
|
||||||
const auto maxdiff = scale * std::atan (talus);
|
|
||||||
float total = 0.f;
|
|
||||||
|
|
||||||
for (size_t y = 1; y < height.h - 1; ++y)
|
|
||||||
for (size_t x = 1; x < height.w - 1; ++x) {
|
|
||||||
float centre = height[y * height.s + x];
|
|
||||||
|
|
||||||
float h[9] = {
|
|
||||||
height[(y - 1) * height.s + (x - 1)],
|
|
||||||
height[(y - 1) * height.s + (x + 0)],
|
|
||||||
height[(y - 1) * height.s + (x + 1)],
|
|
||||||
|
|
||||||
height[(y + 0) * height.s + (x - 1)],
|
|
||||||
height[(y + 0) * height.s + (x + 0)],
|
|
||||||
height[(y + 0) * height.s + (x + 1)],
|
|
||||||
|
|
||||||
height[(y + 1) * height.s + (x - 1)],
|
|
||||||
height[(y + 1) * height.s + (x + 0)],
|
|
||||||
height[(y + 1) * height.s + (x + 1)],
|
|
||||||
};
|
|
||||||
|
|
||||||
float diff = 0.f;
|
|
||||||
unsigned dests = 0;
|
|
||||||
for (size_t i = 0; i < elems (h); ++i) {
|
|
||||||
if (h[i] < centre - maxdiff) {
|
|
||||||
dests++;
|
|
||||||
diff = std::max (diff, centre - h[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diff < maxdiff)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float size = diff * (1 - resistance) / 2;
|
|
||||||
float dist = size / dests;
|
|
||||||
|
|
||||||
total += size;
|
|
||||||
height[y * height.s + x] -= size;
|
|
||||||
|
|
||||||
if (h[0] < centre) height[(y - 1) * height.s + (x - 1)] += dist;
|
|
||||||
if (h[1] < centre) height[(y - 1) * height.s + (x + 0)] += dist;
|
|
||||||
if (h[2] < centre) height[(y - 1) * height.s + (x + 1)] += dist;
|
|
||||||
|
|
||||||
if (h[3] < centre) height[(y + 0) * height.s + (x - 1)] += dist;
|
|
||||||
if (h[5] < centre) height[(y + 0) * height.s + (x + 1)] += dist;
|
|
||||||
|
|
||||||
if (h[6] < centre) height[(y + 1) * height.s + (x - 1)] += dist;
|
|
||||||
if (h[7] < centre) height[(y + 1) * height.s + (x + 0)] += dist;
|
|
||||||
if (h[8] < centre) height[(y + 1) * height.s + (x + 1)] += dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// create a coloured map with this gradient (from libnoise tut3)
|
|
||||||
static const struct {
|
|
||||||
float scale;
|
|
||||||
util::colour3u value;
|
|
||||||
} GRADPOINT[] = {
|
|
||||||
{-1000000.f, { 0, 0, 128 } },
|
|
||||||
{ 0 / 32.f, { 0, 0, 128 } }, // deeps
|
|
||||||
{ 12 / 32.f, { 0, 0, 255 } }, // shallow
|
|
||||||
{ 16 / 32.f, { 0, 128, 255 } }, // shore
|
|
||||||
{ 17 / 32.f, { 240, 240, 64 } }, // sand
|
|
||||||
{ 18 / 32.f, { 32, 160, 0 } }, // grass
|
|
||||||
{ 22 / 32.f, { 224, 224, 0 } }, // dirt
|
|
||||||
{ 28 / 32.f, { 128, 128, 128 } }, // rock
|
|
||||||
{ 32 / 32.f, { 255, 255, 255 } }, // snow
|
|
||||||
{ 1000000.f, { 255, 255, 255 } },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void
|
//-----------------------------------------------------------------------------
|
||||||
write_map (const util::image::buffer<float> &map, boost::filesystem::path name)
|
enum fractal_t {
|
||||||
|
FBM,
|
||||||
|
HMF,
|
||||||
|
RMF,
|
||||||
|
HETERO,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
enum lerp_t {
|
||||||
|
LINEAR,
|
||||||
|
CUBIC,
|
||||||
|
QUINTIC,
|
||||||
|
COSINE,
|
||||||
|
TRUNC
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::istream&
|
||||||
|
operator>> (std::istream &is, basis_t &b)
|
||||||
{
|
{
|
||||||
std::unique_ptr<uint8_t[]> coloured (new uint8_t[map.w * map.h * 3]);
|
std::string name;
|
||||||
for (size_t i = 0; i < map.w * map.h; ++i) {
|
is >> name;
|
||||||
auto v = map[i] + 0/32.f;
|
|
||||||
auto c1 = std::upper_bound (std::begin (GRADPOINT),
|
|
||||||
std::end (GRADPOINT),
|
|
||||||
v,
|
|
||||||
[] (auto a, auto b) { return a < b.scale; });
|
|
||||||
auto c0 = c1-1;
|
|
||||||
|
|
||||||
CHECK_GE (v, c0->scale);
|
b = name == "value" ? VALUE :
|
||||||
CHECK_LT (v, c1->scale);
|
name == "perlin" ? PERLIN :
|
||||||
float t = (v - c0->scale) / (c1->scale - c0->scale);
|
name == "worley" ? WORLEY :
|
||||||
CHECK_LIMIT (t, 0, 1);
|
(is.setstate (std::istream::failbit), b);
|
||||||
auto c = (
|
|
||||||
(1 - t) * c0->value.template cast<float> () +
|
|
||||||
( t) * c1->value.template cast<float> ()
|
|
||||||
).template cast<uint8_t> ();
|
|
||||||
|
|
||||||
coloured[i*3+0] = c[0];
|
return is;
|
||||||
coloured[i*3+1] = c[1];
|
|
||||||
coloured[i*3+2] = c[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
name.replace_extension (".ppm");
|
|
||||||
util::ppm::write (coloured.get (), map.w, map.h, map.w*3, name);
|
|
||||||
|
|
||||||
// write the image to disk
|
|
||||||
auto grey = map.clone<uint8_t> ();
|
|
||||||
name.replace_extension (".pgm");
|
|
||||||
util::pgm::write (grey, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// offset a map so a fixed percentage of tiles are below the water height
|
//-----------------------------------------------------------------------------
|
||||||
void
|
std::ostream&
|
||||||
adjust_ocean (util::image::buffer<float> &height,
|
operator<< (std::ostream &os, basis_t b)
|
||||||
const float percentage = .2f)
|
|
||||||
{
|
{
|
||||||
static const float WATER_HEIGHT = 0.5f;
|
switch (b) {
|
||||||
|
case VALUE: os << "value"; return os;
|
||||||
|
case PERLIN: os << "perlin"; return os;
|
||||||
|
case WORLEY: os << "worley"; return os;
|
||||||
|
|
||||||
CHECK_LIMIT (percentage, 0, 1);
|
default:
|
||||||
// we assume fully packed data for iteration purposes
|
unreachable ();
|
||||||
CHECK (height.is_packed ());
|
|
||||||
|
|
||||||
std::array<unsigned,256> buckets{ 0 };
|
|
||||||
for (const auto h: height)
|
|
||||||
buckets[size_t (h * 255u)]++;
|
|
||||||
|
|
||||||
size_t pivot = 0;
|
|
||||||
for (size_t accum = 0, target = size_t (percentage * height.extent ().area ()); pivot < buckets.size (); ++pivot) {
|
|
||||||
accum += buckets[pivot];
|
|
||||||
if (accum > target)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "ocean pivot: " << pivot << '\n';
|
|
||||||
|
|
||||||
float offset = WATER_HEIGHT - pivot / 256.f;
|
|
||||||
for (auto &h: height)
|
|
||||||
h += offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::istream&
|
||||||
|
operator>> (std::istream &is, fractal_t &f)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
is >> name;
|
||||||
|
|
||||||
static const unsigned THERMAL_ITERATIONS = 10;
|
f = name == "fbm" ? FBM :
|
||||||
static const unsigned HYDRAULIC_ITERATIONS = 100;
|
name == "hmf" ? HMF :
|
||||||
|
name == "rmf" ? RMF :
|
||||||
|
name == "hetero" ? HETERO :
|
||||||
|
(is.setstate (std::istream::failbit), f);
|
||||||
|
|
||||||
#include "cmdopt.hpp"
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::ostream&
|
||||||
|
operator<< (std::ostream &os, fractal_t f)
|
||||||
|
{
|
||||||
|
switch (f) {
|
||||||
|
case FBM: os << "fbm"; return os;
|
||||||
|
case HMF: os << "hmf"; return os;
|
||||||
|
case RMF: os << "rmf"; return os;
|
||||||
|
case HETERO: os << "hetero"; return os;
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable ();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::istream&
|
||||||
|
operator>> (std::istream &is, lerp_t &l)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
is >> name;
|
||||||
|
|
||||||
|
l = name == "linear" ? LINEAR :
|
||||||
|
name == "cubic" ? CUBIC :
|
||||||
|
name == "quintic" ? QUINTIC :
|
||||||
|
name == "cosine" ? COSINE :
|
||||||
|
name == "trunc" ? TRUNC :
|
||||||
|
(is.setstate (std::istream::failbit), l);
|
||||||
|
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::ostream&
|
||||||
|
operator<< (std::ostream &os, lerp_t &l)
|
||||||
|
{
|
||||||
|
switch (l) {
|
||||||
|
case LINEAR: os << "linear"; return os;
|
||||||
|
case CUBIC: os << "cubic"; return os;
|
||||||
|
case QUINTIC: os << "quintic"; return os;
|
||||||
|
case COSINE: os << "cosine"; return os;
|
||||||
|
case TRUNC: os << "trunc"; return os;
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
int
|
int
|
||||||
main (int argc, char **argv)
|
main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
#ifndef ENABLE_DEBUGGING
|
||||||
|
if (isatty (fileno (stdout))) {
|
||||||
|
std::cerr << "cowardly refusing to dump binary data to console\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// setup default variables
|
// setup default variables
|
||||||
#ifdef ENABLE_DEBUGGING
|
#ifdef ENABLE_DEBUGGING
|
||||||
util::extent2u res {320, 240};
|
util::extent2u res {320, 240};
|
||||||
@ -281,56 +173,116 @@ main (int argc, char **argv)
|
|||||||
|
|
||||||
uint64_t seed = time (nullptr);
|
uint64_t seed = time (nullptr);
|
||||||
|
|
||||||
|
basis_t basis = PERLIN;
|
||||||
|
fractal_t fractal = FBM;
|
||||||
|
lerp_t lerp = QUINTIC;
|
||||||
|
unsigned octaves = 8;
|
||||||
|
float H;
|
||||||
|
float scale = 1.f;
|
||||||
|
float turbulence = 0.f;
|
||||||
|
|
||||||
// fill variables from arguments
|
// fill variables from arguments
|
||||||
util::cmdopt::parser args;
|
util::cmdopt::parser args;
|
||||||
args.add<util::cmdopt::option::value<size_t>> ('w', "width", "output image width", res.w);
|
args.add<util::cmdopt::option::value<size_t>> ('w', "width", "output image width", res.w);
|
||||||
args.add<util::cmdopt::option::value<size_t>> ('h', "height", "output image height", res.h);
|
args.add<util::cmdopt::option::value<size_t>> ('h', "height", "output image height", res.h);
|
||||||
args.add<util::cmdopt::option::value<uint64_t>> ('s', "seed", "random seed", seed);
|
args.add<util::cmdopt::option::value<uint64_t>> ('s', "seed", "random seed", seed);
|
||||||
//args.add<util::cmdopt::option::value<unsigned>> ('o', "octaves", "total fractal iterations", octaves);
|
args.add<util::cmdopt::option::value<basis_t>> ('b', "basis", "primary basis function", basis);
|
||||||
//args.add<util::cmdopt::option::value<float>> ('H', "hurst", "Hurst exponent", H);
|
args.add<util::cmdopt::option::value<fractal_t>> ('f', "fractal", "primary fractal function", fractal);
|
||||||
|
args.add<util::cmdopt::option::value<lerp_t>> ('l', "lerp", "interpolation algorithm", lerp);
|
||||||
|
args.add<util::cmdopt::option::value<unsigned>> ('o', "octaves", "total fractal iterations", octaves);
|
||||||
|
args.add<util::cmdopt::option::value<float>> ('H', "hurst", "Hurst exponent", H);
|
||||||
|
args.add<util::cmdopt::option::value<float>> ('x', "scale", "frequency multiplier", scale);
|
||||||
|
args.add<util::cmdopt::option::value<float>> ('t', "turbulence","turbulence scale", turbulence);
|
||||||
|
|
||||||
args.scan (argc, argv);
|
args.scan (argc, argv);
|
||||||
|
|
||||||
util::image::buffer<float> img (res);
|
std::cerr << "turbulence: " << turbulence << '\n';
|
||||||
|
|
||||||
// setup the noise generator
|
|
||||||
#if 0
|
|
||||||
//util::noise::fractal::fbm<float, util::noise::basis::worley<float>> b (seed);
|
|
||||||
//util::noise::fractal::rmf<float, util::noise::basis::worley<float>> b (seed);
|
|
||||||
//util::noise::fractal::fbm<float, util::noise::basis::perlin<float,util::lerp::cubic>> b (seed);
|
|
||||||
//util::noise::fractal::rmf<float, util::noise::basis::perlin<float,util::lerp::cubic>> b (seed);
|
|
||||||
//util::noise::fractal::hmf<float, util::noise::basis::perlin<float,util::lerp::cubic>> b (seed);
|
|
||||||
util::noise::fractal::hetero<float, util::noise::basis::value<float,util::lerp::quintic>> b (seed);
|
|
||||||
|
|
||||||
b.octaves (8);
|
|
||||||
b.frequency (10.f / res.w);
|
|
||||||
b.lacunarity = 2.f;
|
|
||||||
b.H = 1.0f;
|
|
||||||
b.seed (seed);
|
|
||||||
#else
|
|
||||||
util::noise::turbulence<
|
util::noise::turbulence<
|
||||||
float,
|
float,
|
||||||
//util::noise::fractal::hetero<float, util::noise::basis::worley<float>>,
|
util::noise::fractal::runtime<
|
||||||
util::noise::fractal::hetero<float, util::noise::basis::perlin<float,util::lerp::cubic>>,
|
float,
|
||||||
util::noise::fractal::fbm<float, util::noise::basis::perlin<float,util::lerp::quintic>>
|
util::noise::basis::runtime<float>
|
||||||
> b (seed, { 0.13f, 0.13f });
|
>,
|
||||||
|
util::noise::fractal::fbm<
|
||||||
|
float,
|
||||||
|
util::noise::basis::perlin<
|
||||||
|
float,
|
||||||
|
util::lerp::cubic
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> t (seed, { turbulence, turbulence });
|
||||||
|
|
||||||
b.data.frequency (1.f / res.w);
|
auto &f = t.data;
|
||||||
b.perturb[0].octaves (4);
|
|
||||||
b.perturb[1].octaves (4);
|
switch (fractal) {
|
||||||
b.perturb[0].frequency (10.f / res.w);
|
using namespace util::noise;
|
||||||
b.perturb[1].frequency (10.f / res.w);
|
|
||||||
#endif
|
case FBM: f.reset<fractal::fbm <float,basis::runtime<float>>> (seed); break;
|
||||||
|
case HMF: f.reset<fractal::hmf <float,basis::runtime<float>>> (seed); break;
|
||||||
|
case RMF: f.reset<fractal::rmf <float,basis::runtime<float>>> (seed); break;
|
||||||
|
case HETERO: f.reset<fractal::hetero<float,basis::runtime<float>>> (seed); break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable ();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &b = f.basis ();
|
||||||
|
switch (basis) {
|
||||||
|
using namespace util::noise;
|
||||||
|
|
||||||
|
case PERLIN: {
|
||||||
|
switch (lerp) {
|
||||||
|
case LINEAR: b.reset<basis::perlin<float,util::lerp::linear>> (seed); break;
|
||||||
|
case CUBIC: b.reset<basis::perlin<float,util::lerp::cubic>> (seed); break;
|
||||||
|
case QUINTIC: b.reset<basis::perlin<float,util::lerp::quintic>> (seed); break;
|
||||||
|
case COSINE: b.reset<basis::perlin<float,util::lerp::cosine>> (seed); break;
|
||||||
|
case TRUNC: b.reset<basis::perlin<float,util::lerp::trunc>> (seed); break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable ();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VALUE: {
|
||||||
|
switch (lerp) {
|
||||||
|
case LINEAR: b.reset<basis::value<float,util::lerp::linear>> (seed); break;
|
||||||
|
case CUBIC: b.reset<basis::value<float,util::lerp::cubic>> (seed); break;
|
||||||
|
case QUINTIC: b.reset<basis::value<float,util::lerp::quintic>> (seed); break;
|
||||||
|
case COSINE: b.reset<basis::value<float,util::lerp::cosine>> (seed); break;
|
||||||
|
case TRUNC: b.reset<basis::value<float,util::lerp::trunc>> (seed); break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable ();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WORLEY: {
|
||||||
|
b.reset<util::noise::basis::worley<float>> (seed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable ();
|
||||||
|
}
|
||||||
|
|
||||||
|
t.seed (seed);
|
||||||
|
f.octaves (octaves);
|
||||||
|
f.frequency (scale / res.w);
|
||||||
|
t.perturb[0].frequency ( scale / res.w);
|
||||||
|
t.perturb[1].frequency ( scale / res.w);
|
||||||
|
|
||||||
|
util::image::buffer<float> img (res);
|
||||||
|
|
||||||
// generate the values. offset positions slightly to observe simple axis issues with perlin basis
|
|
||||||
{
|
{
|
||||||
|
// XXX: offset slightly to avoid origin artefacts in some basis functions
|
||||||
auto offset = util::vector2f { -100 };
|
auto offset = util::vector2f { -100 };
|
||||||
|
|
||||||
for (size_t y = 0; y < res.h; ++y)
|
for (size_t y = 0; y < res.h; ++y)
|
||||||
for (size_t x = 0; x < res.w; ++x) {
|
for (size_t x = 0; x < res.w; ++x)
|
||||||
auto v = b (util::point2f {float (x), float (y)} + offset);
|
img.data ()[y * img.s + x] = t (util::point2f {float (x), float (y)} + offset);
|
||||||
img.data ()[y * res.w + x] = v;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// rescale into the range [0, 1]
|
// rescale into the range [0, 1]
|
||||||
@ -341,19 +293,6 @@ main (int argc, char **argv)
|
|||||||
|
|
||||||
std::transform (img.begin (), img.end (), img.begin (), [offset,div] (auto i) { return (i - offset) / div; });
|
std::transform (img.begin (), img.end (), img.begin (), [offset,div] (auto i) { return (i - offset) / div; });
|
||||||
|
|
||||||
// ensure the ocean isn't too big
|
|
||||||
adjust_ocean (img, 0.2f);
|
|
||||||
|
|
||||||
// write the images to disk
|
// write the images to disk
|
||||||
write_map (img, "raw");
|
util::pgm::write (img.cast<uint8_t> (), std::cout);
|
||||||
|
|
||||||
auto soft = img.clone ();
|
|
||||||
|
|
||||||
std::cerr << "thermal_erosion\n";
|
|
||||||
for (size_t i = 0; i < THERMAL_ITERATIONS; ++i)
|
|
||||||
thermal_erode (soft, to_radians (30.f), 1.f / res.w, 0.f);
|
|
||||||
|
|
||||||
hydraulic_erode (soft, HYDRAULIC_ITERATIONS);
|
|
||||||
|
|
||||||
write_map (soft, "soft");
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user