2014-01-01 16:45:50 +00:00
|
|
|
#include "FractalNoise.h"
|
|
|
|
|
2016-07-23 20:58:32 +00:00
|
|
|
#include "PackStream.h"
|
|
|
|
#include "RandomGenerator.h"
|
|
|
|
#include "Vector3.h"
|
2016-01-14 19:24:01 +00:00
|
|
|
#include <cassert>
|
2016-01-06 23:39:08 +00:00
|
|
|
#include <cmath>
|
2016-01-14 19:24:01 +00:00
|
|
|
#include <sstream>
|
2016-01-03 19:22:06 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
FractalNoise::FractalNoise() {
|
2014-01-01 16:45:50 +00:00
|
|
|
scaling = 1.0;
|
|
|
|
height = 1.0;
|
|
|
|
step_scaling = 2.0;
|
|
|
|
step_height = 0.5;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
FractalNoise::~FractalNoise() {
|
2014-01-21 20:51:11 +00:00
|
|
|
}
|
|
|
|
|
2016-01-04 00:13:14 +00:00
|
|
|
void FractalNoise::save(PackStream *stream) const {
|
2016-01-03 19:22:06 +00:00
|
|
|
stream->write(&scaling);
|
|
|
|
stream->write(&height);
|
|
|
|
stream->write(&step_scaling);
|
|
|
|
stream->write(&step_height);
|
|
|
|
state.save(stream);
|
|
|
|
}
|
|
|
|
|
2016-01-04 00:13:14 +00:00
|
|
|
void FractalNoise::load(PackStream *stream) {
|
2016-01-03 19:22:06 +00:00
|
|
|
stream->read(&scaling);
|
|
|
|
stream->read(&height);
|
|
|
|
stream->read(&step_scaling);
|
|
|
|
stream->read(&step_height);
|
|
|
|
state.load(stream);
|
|
|
|
}
|
|
|
|
|
2016-01-04 00:13:14 +00:00
|
|
|
void FractalNoise::copy(FractalNoise *destination) const {
|
2016-01-03 19:22:06 +00:00
|
|
|
destination->scaling = scaling;
|
|
|
|
destination->height = height;
|
|
|
|
destination->step_scaling = step_scaling;
|
|
|
|
destination->step_height = step_height;
|
|
|
|
state.copy(&destination->state);
|
|
|
|
}
|
|
|
|
|
2016-01-04 00:13:14 +00:00
|
|
|
void FractalNoise::randomize(RandomGenerator &random) {
|
|
|
|
state.randomizeOffsets(random);
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void FractalNoise::setScaling(double scaling, double height) {
|
2014-01-01 16:45:50 +00:00
|
|
|
this->scaling = scaling < 0.00000001 ? 0.0 : 1.0 / scaling;
|
|
|
|
this->height = scaling * height;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void FractalNoise::setStep(double scaling_factor, double height_factor) {
|
2014-01-01 16:45:50 +00:00
|
|
|
this->step_scaling = scaling_factor < 0.00000001 ? 0.0 : 1.0 / scaling_factor;
|
|
|
|
this->step_height = scaling_factor * height_factor;
|
2016-01-14 19:24:01 +00:00
|
|
|
|
|
|
|
// Ensure height will converge to 0
|
|
|
|
if (this->step_height >= 0.99) {
|
|
|
|
this->step_height = 0.99;
|
|
|
|
}
|
2014-01-01 16:45:50 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void FractalNoise::setState(const NoiseState &state) {
|
2014-01-01 17:21:34 +00:00
|
|
|
state.copy(&this->state);
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
double FractalNoise::get1d(double detail, double x) const {
|
2014-01-01 16:45:50 +00:00
|
|
|
double current_scaling = scaling;
|
|
|
|
double current_height = height;
|
|
|
|
double result = 0.0;
|
2015-12-31 00:29:59 +00:00
|
|
|
auto state_level_count = state.level_offsets.size();
|
|
|
|
decltype(state_level_count) i = 0;
|
2014-01-01 16:45:50 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
while (current_height >= detail) {
|
2016-01-14 19:24:01 +00:00
|
|
|
auto offset = state.level_offsets[i];
|
2014-01-01 17:21:34 +00:00
|
|
|
|
|
|
|
result += getBase1d(offset.x + x * current_scaling) * current_height;
|
2014-01-01 16:45:50 +00:00
|
|
|
|
|
|
|
current_scaling *= step_scaling;
|
|
|
|
current_height *= step_height;
|
2014-01-01 17:21:34 +00:00
|
|
|
|
|
|
|
i++;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (i >= state_level_count) {
|
2014-01-01 17:21:34 +00:00
|
|
|
i = 0;
|
|
|
|
}
|
2014-01-01 16:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
double FractalNoise::get2d(double detail, double x, double y) const {
|
2014-01-01 16:45:50 +00:00
|
|
|
double current_scaling = scaling;
|
|
|
|
double current_height = height;
|
|
|
|
double result = 0.0;
|
2015-12-31 00:29:59 +00:00
|
|
|
auto state_level_count = state.level_offsets.size();
|
|
|
|
decltype(state_level_count) i = 0;
|
2014-01-01 16:45:50 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
while (current_height >= detail) {
|
2016-01-14 19:24:01 +00:00
|
|
|
auto offset = state.level_offsets[i];
|
2014-01-01 17:21:34 +00:00
|
|
|
|
|
|
|
result += getBase2d(offset.x + x * current_scaling, offset.y + y * current_scaling) * current_height;
|
2014-01-01 16:45:50 +00:00
|
|
|
|
|
|
|
current_scaling *= step_scaling;
|
|
|
|
current_height *= step_height;
|
2014-01-01 17:21:34 +00:00
|
|
|
|
|
|
|
i++;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (i >= state_level_count) {
|
2014-01-01 17:21:34 +00:00
|
|
|
i = 0;
|
|
|
|
}
|
2014-01-01 16:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
double FractalNoise::get3d(double detail, double x, double y, double z) const {
|
2014-01-01 16:45:50 +00:00
|
|
|
double current_scaling = scaling;
|
|
|
|
double current_height = height;
|
|
|
|
double result = 0.0;
|
2015-12-31 00:29:59 +00:00
|
|
|
auto state_level_count = state.level_offsets.size();
|
|
|
|
decltype(state_level_count) i = 0;
|
2014-01-01 16:45:50 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
while (current_height >= detail) {
|
2016-01-05 23:47:45 +00:00
|
|
|
auto offset = state.level_offsets[i];
|
2014-01-01 17:21:34 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
result +=
|
|
|
|
getBase3d(offset.x + x * current_scaling, offset.y + y * current_scaling, offset.z + z * current_scaling) *
|
|
|
|
current_height;
|
2014-01-01 16:45:50 +00:00
|
|
|
|
|
|
|
current_scaling *= step_scaling;
|
|
|
|
current_height *= step_height;
|
2014-01-01 17:21:34 +00:00
|
|
|
|
|
|
|
i++;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (i >= state_level_count) {
|
2014-01-01 17:21:34 +00:00
|
|
|
i = 0;
|
|
|
|
}
|
2014-01-01 16:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-01-06 23:39:08 +00:00
|
|
|
double FractalNoise::getTriplanar(double detail, const Vector3 &location, const Vector3 &normal) const {
|
|
|
|
double noiseXY = get2d(detail, location.x, location.y);
|
|
|
|
double noiseXZ = get2d(detail, location.x, location.z);
|
|
|
|
double noiseYZ = get2d(detail, location.y, location.z);
|
|
|
|
|
|
|
|
double mXY = fabs(normal.z);
|
|
|
|
double mXZ = fabs(normal.y);
|
|
|
|
double mYZ = fabs(normal.x);
|
|
|
|
double total = 1.0 / (mXY + mXZ + mYZ);
|
|
|
|
mXY *= total;
|
|
|
|
mXZ *= total;
|
|
|
|
mYZ *= total;
|
|
|
|
|
|
|
|
return noiseXY * mXY + noiseXZ * mXZ + noiseYZ * mYZ;
|
|
|
|
}
|
|
|
|
|
2016-01-14 19:24:01 +00:00
|
|
|
void FractalNoise::estimateRange(double *min, double *max, double detail) const {
|
|
|
|
*min = 0.0;
|
|
|
|
*max = 0.0;
|
|
|
|
|
|
|
|
double current_height = height;
|
|
|
|
while (current_height >= detail) {
|
|
|
|
*min += -0.5 * current_height;
|
|
|
|
*max += 0.5 * current_height;
|
|
|
|
current_height *= step_height;
|
|
|
|
}
|
2016-01-06 00:46:25 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
double FractalNoise::getBase1d(double x) const {
|
2014-01-01 16:45:50 +00:00
|
|
|
return getBase2d(x, 0.0);
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
double FractalNoise::getBase2d(double x, double y) const {
|
2014-01-01 16:45:50 +00:00
|
|
|
return getBase3d(x, y, 0.0);
|
|
|
|
}
|
2016-01-14 19:24:01 +00:00
|
|
|
|
|
|
|
string FractalNoise::checkDistribution() {
|
|
|
|
stringstream result;
|
|
|
|
|
|
|
|
double val, min, max, mean;
|
|
|
|
RandomGenerator random;
|
|
|
|
|
|
|
|
int samples = 10000000;
|
|
|
|
double factor = 1.0 / to_double(samples);
|
|
|
|
|
|
|
|
min = 0.0;
|
|
|
|
max = 0.0;
|
|
|
|
mean = 0.0;
|
|
|
|
for (int i = 0; i < samples; i++) {
|
|
|
|
val = getBase1d((random.genDouble() - 0.5) * 10.0);
|
|
|
|
min = std::min(val, min);
|
|
|
|
max = std::max(val, max);
|
|
|
|
mean += val * factor;
|
|
|
|
}
|
|
|
|
result << "1d : min=" << min << " max=" << max << " mean=" << mean << endl;
|
|
|
|
|
|
|
|
min = 0.0;
|
|
|
|
max = 0.0;
|
|
|
|
mean = 0.0;
|
|
|
|
for (int i = 0; i < samples; i++) {
|
|
|
|
val = getBase2d((random.genDouble() - 0.5) * 10.0, (random.genDouble() - 0.5) * 10.0);
|
|
|
|
min = std::min(val, min);
|
|
|
|
max = std::max(val, max);
|
|
|
|
mean += val * factor;
|
|
|
|
}
|
|
|
|
result << "2d : min=" << min << " max=" << max << " mean=" << mean << endl;
|
|
|
|
|
|
|
|
min = 0.0;
|
|
|
|
max = 0.0;
|
|
|
|
mean = 0.0;
|
|
|
|
for (int i = 0; i < samples; i++) {
|
2016-01-14 23:07:02 +00:00
|
|
|
val = getBase3d((random.genDouble() - 0.5) * 10.0, (random.genDouble() - 0.5) * 10.0,
|
|
|
|
(random.genDouble() - 0.5) * 10.0);
|
2016-01-14 19:24:01 +00:00
|
|
|
min = std::min(val, min);
|
|
|
|
max = std::max(val, max);
|
|
|
|
mean += val * factor;
|
|
|
|
}
|
|
|
|
result << "3d : min=" << min << " max=" << max << " mean=" << mean << endl;
|
|
|
|
|
|
|
|
return result.str();
|
|
|
|
}
|