Updated TerrainDefinition to use FractalNoise

This commit is contained in:
Michaël Lemaire 2016-01-14 20:24:01 +01:00
parent a098a19ee3
commit 14143ee278
16 changed files with 224 additions and 142 deletions

View file

@ -1,8 +1,11 @@
#include "FractalNoise.h" #include "FractalNoise.h"
#include <cassert>
#include <cmath> #include <cmath>
#include <sstream>
#include "PackStream.h" #include "PackStream.h"
#include "Vector3.h" #include "Vector3.h"
#include "RandomGenerator.h"
FractalNoise::FractalNoise() { FractalNoise::FractalNoise() {
scaling = 1.0; scaling = 1.0;
@ -50,6 +53,11 @@ void FractalNoise::setScaling(double scaling, double height) {
void FractalNoise::setStep(double scaling_factor, double height_factor) { void FractalNoise::setStep(double scaling_factor, double height_factor) {
this->step_scaling = scaling_factor < 0.00000001 ? 0.0 : 1.0 / scaling_factor; this->step_scaling = scaling_factor < 0.00000001 ? 0.0 : 1.0 / scaling_factor;
this->step_height = scaling_factor * height_factor; this->step_height = scaling_factor * height_factor;
// Ensure height will converge to 0
if (this->step_height >= 0.99) {
this->step_height = 0.99;
}
} }
void FractalNoise::setState(const NoiseState &state) { void FractalNoise::setState(const NoiseState &state) {
@ -64,7 +72,7 @@ double FractalNoise::get1d(double detail, double x) const {
decltype(state_level_count) i = 0; decltype(state_level_count) i = 0;
while (current_height >= detail) { while (current_height >= detail) {
const NoiseState::NoiseOffset &offset = state.level_offsets[i]; auto offset = state.level_offsets[i];
result += getBase1d(offset.x + x * current_scaling) * current_height; result += getBase1d(offset.x + x * current_scaling) * current_height;
@ -88,7 +96,7 @@ double FractalNoise::get2d(double detail, double x, double y) const {
decltype(state_level_count) i = 0; decltype(state_level_count) i = 0;
while (current_height >= detail) { while (current_height >= detail) {
const NoiseState::NoiseOffset &offset = state.level_offsets[i]; auto offset = state.level_offsets[i];
result += getBase2d(offset.x + x * current_scaling, offset.y + y * current_scaling) * current_height; result += getBase2d(offset.x + x * current_scaling, offset.y + y * current_scaling) * current_height;
@ -146,10 +154,16 @@ double FractalNoise::getTriplanar(double detail, const Vector3 &location, const
return noiseXY * mXY + noiseXZ * mXZ + noiseYZ * mYZ; return noiseXY * mXY + noiseXZ * mXZ + noiseYZ * mYZ;
} }
void FractalNoise::estimateRange(double *min, double *max) const { void FractalNoise::estimateRange(double *min, double *max, double detail) const {
// TODO Better estimate *min = 0.0;
*max = height; *max = 0.0;
*min = -*max;
double current_height = height;
while (current_height >= detail) {
*min += -0.5 * current_height;
*max += 0.5 * current_height;
current_height *= step_height;
}
} }
double FractalNoise::getBase1d(double x) const { double FractalNoise::getBase1d(double x) const {
@ -159,3 +173,48 @@ double FractalNoise::getBase1d(double x) const {
double FractalNoise::getBase2d(double x, double y) const { double FractalNoise::getBase2d(double x, double y) const {
return getBase3d(x, y, 0.0); return getBase3d(x, y, 0.0);
} }
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++) {
val = getBase3d((random.genDouble() - 0.5) * 10.0, (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 << "3d : min=" << min << " max=" << max << " mean=" << mean << endl;
return result.str();
}

View file

@ -3,6 +3,7 @@
#include "basics_global.h" #include "basics_global.h"
#include <string>
#include "NoiseState.h" #include "NoiseState.h"
namespace paysages { namespace paysages {
@ -56,7 +57,7 @@ class BASICSSHARED_EXPORT FractalNoise {
/** /**
* Estimate the range of values this generator will yield with a very small detail value. * Estimate the range of values this generator will yield with a very small detail value.
*/ */
void estimateRange(double *min, double *max) const; void estimateRange(double *min, double *max, double detail=0.000001) const;
virtual double getBase1d(double x) const; virtual double getBase1d(double x) const;
virtual double getBase2d(double x, double y) const; virtual double getBase2d(double x, double y) const;
@ -67,6 +68,11 @@ class BASICSSHARED_EXPORT FractalNoise {
*/ */
virtual double getBase3d(double x, double y, double z) const = 0; virtual double getBase3d(double x, double y, double z) const = 0;
/**
* Test the noise distribution, and return the report.
*/
string checkDistribution();
private: private:
NoiseState state; NoiseState state;

View file

@ -20,6 +20,11 @@ void NoiseNode::setConfig(double scaling, double height, double step_scaling, do
noise->setStep(step_scaling, step_height); noise->setStep(step_scaling, step_height);
} }
void NoiseNode::forceSetGenerator(FractalNoise *noise) {
delete this->noise;
this->noise = noise;
}
void NoiseNode::save(PackStream *stream) const { void NoiseNode::save(PackStream *stream) const {
noise->save(stream); noise->save(stream);
} }
@ -43,5 +48,5 @@ void NoiseNode::copy(DefinitionNode *destination) const {
string NoiseNode::toString(int indent) const { string NoiseNode::toString(int indent) const {
return DefinitionNode::toString(indent) + " - scaling: " + to_string(noise->getScaling()) + " step " + return DefinitionNode::toString(indent) + " - scaling: " + to_string(noise->getScaling()) + " step " +
to_string(noise->getStepScaling()) + " - height: " + to_string(noise->getHeight()) + " step " + to_string(noise->getStepScaling()) + " - height: " + to_string(noise->getHeight()) + " step " +
to_string(noise->getStepScaling()); to_string(noise->getStepHeight());
} }

View file

@ -30,6 +30,15 @@ class DEFINITIONSHARED_EXPORT NoiseNode : public DefinitionNode {
*/ */
void setConfig(double scaling, double height = 1.0, double step_scaling = 0.5, double step_height = 1.0); void setConfig(double scaling, double height = 1.0, double step_scaling = 0.5, double step_height = 1.0);
/**
* Force the noise generator to use.
*
* This should only be needed in testing, and is not thread-safe.
*
* The node will take ownership of the generator and will take care of its destruction.
*/
void forceSetGenerator(FractalNoise *noise);
protected: protected:
virtual void save(PackStream *stream) const override; virtual void save(PackStream *stream) const override;
virtual void load(PackStream *stream) override; virtual void load(PackStream *stream) override;

View file

@ -5,9 +5,10 @@
#include "NoiseGenerator.h" #include "NoiseGenerator.h"
#include "PackStream.h" #include "PackStream.h"
#include "FloatNode.h" #include "FloatNode.h"
#include "NoiseNode.h"
#include "FractalNoise.h"
TerrainDefinition::TerrainDefinition(DefinitionNode *parent) : DefinitionNode(parent, "terrain", "terrain") { TerrainDefinition::TerrainDefinition(DefinitionNode *parent) : DefinitionNode(parent, "terrain", "terrain") {
height = 1.0;
shadow_smoothing = 0.0; shadow_smoothing = 0.0;
height_map = new TerrainHeightMap(this); height_map = new TerrainHeightMap(this);
@ -15,57 +16,42 @@ TerrainDefinition::TerrainDefinition(DefinitionNode *parent) : DefinitionNode(pa
addChild(height_map); addChild(height_map);
water_height = new FloatNode(this, "water_height"); water_height = new FloatNode(this, "water_height");
height_noise = new NoiseNode(this, "height_noise");
_height_noise = new NoiseGenerator;
} }
TerrainDefinition::~TerrainDefinition() { TerrainDefinition::~TerrainDefinition() {
delete _height_noise;
} }
void TerrainDefinition::validate() { void TerrainDefinition::validate() {
_height_noise->validate(); DefinitionNode::validate();
if (height < 1.0) { // Get base noise range
height = 1.0; height_noise->getGenerator()->estimateRange(&_min_height, &_max_height, 0.1);
} // TODO Alter limits with heightmap min/max, and displacement textures
/* Get minimal and maximal height */
_height_noise->getRange(&_min_height, &_max_height);
_min_height *= height;
_max_height *= height;
/* TODO Alter limits with heightmap min/max */
has_painting = height_map->hasPainting(); has_painting = height_map->hasPainting();
} }
void TerrainDefinition::copy(DefinitionNode *_destination) const { void TerrainDefinition::copy(DefinitionNode *_destination) const {
TerrainDefinition *destination = (TerrainDefinition *)_destination; if (auto destination = static_cast<TerrainDefinition *>(_destination)) {
destination->height = height;
destination->shadow_smoothing = shadow_smoothing; destination->shadow_smoothing = shadow_smoothing;
height_map->copy(destination->height_map); height_map->copy(destination->height_map);
_height_noise->copy(destination->_height_noise);
destination->validate(); destination->validate();
}
} }
void TerrainDefinition::save(PackStream *stream) const { void TerrainDefinition::save(PackStream *stream) const {
DefinitionNode::save(stream); DefinitionNode::save(stream);
stream->write(&height);
stream->write(&shadow_smoothing); stream->write(&shadow_smoothing);
_height_noise->save(stream);
} }
void TerrainDefinition::load(PackStream *stream) { void TerrainDefinition::load(PackStream *stream) {
DefinitionNode::load(stream); DefinitionNode::load(stream);
stream->read(&height);
stream->read(&shadow_smoothing); stream->read(&shadow_smoothing);
_height_noise->load(stream);
validate(); validate();
} }
@ -74,29 +60,26 @@ double TerrainDefinition::getGridHeight(int x, int z, bool with_painting) {
double h; double h;
if (!with_painting || !has_painting || !height_map->getGridValue(x, z, &h)) { if (!with_painting || !has_painting || !height_map->getGridValue(x, z, &h)) {
h = _height_noise->get2DTotal(to_double(x), to_double(z)); h = height_noise->getGenerator()->get2d(0.1, to_double(x), to_double(z));
} }
return h; return h;
} }
double TerrainDefinition::getInterpolatedHeight(double x, double z, bool scaled, bool with_painting, double TerrainDefinition::getInterpolatedHeight(double x, double z, bool with_painting,
bool water_offset) { bool water_offset) {
double h; double h;
if (!with_painting || !has_painting || !height_map->getInterpolatedValue(x, z, &h)) { if (!with_painting || !has_painting || !height_map->getInterpolatedValue(x, z, &h)) {
h = _height_noise->get2DTotal(x, z); h = height_noise->getGenerator()->get2d(0.1, x, z);
} }
if (scaled) { return (water_offset ? (h + getWaterOffset()) : h);
return (water_offset ? (h - water_height->getValue()) : h) * height;
} else {
return h;
}
} }
double TerrainDefinition::getWaterOffset() const { double TerrainDefinition::getWaterOffset() const {
return -water_height->getValue() * height; double height_power = (_max_height - _min_height) * 0.5;
return -water_height->getValue() * height_power;
} }
HeightInfo TerrainDefinition::getHeightInfo() { HeightInfo TerrainDefinition::getHeightInfo() {
@ -114,20 +97,12 @@ unsigned long TerrainDefinition::getMemoryStats() {
} }
void TerrainDefinition::applyPreset(TerrainPreset preset, RandomGenerator &random) { void TerrainDefinition::applyPreset(TerrainPreset preset, RandomGenerator &random) {
int resolution = 8;
switch (preset) { switch (preset) {
case TERRAIN_PRESET_STANDARD: case TERRAIN_PRESET_STANDARD:
_height_noise->randomizeOffsets(random); height_noise->randomize(random);
_height_noise->clearLevels(); height_noise->setConfig(400.0, 0.1, 0.5, 1.02);
_height_noise->addLevelSimple(pow(2.0, resolution + 1), -1.0, 1.0);
_height_noise->addLevelsSimple(resolution - 2, pow(2.0, resolution - 1), -0.7, 0.7, 0.5);
_height_noise->normalizeAmplitude(-1.0, 1.0, 0);
_height_noise->setFunctionParams(NoiseGenerator::NOISE_FUNCTION_SIMPLEX, 0.0, 0.0);
height = 30.0;
shadow_smoothing = 0.03; shadow_smoothing = 0.03;
break; break;
default:
;
} }
water_height->setValue(-0.3); water_height->setValue(-0.3);

View file

@ -28,9 +28,12 @@ class DEFINITIONSHARED_EXPORT TerrainDefinition : public DefinitionNode {
inline FloatNode *propWaterHeight() const { inline FloatNode *propWaterHeight() const {
return water_height; return water_height;
} }
inline NoiseNode *propHeightNoise() const {
return height_noise;
}
double getGridHeight(int x, int z, bool with_painting); double getGridHeight(int x, int z, bool with_painting);
double getInterpolatedHeight(double x, double z, bool scaled, bool with_painting, bool water_offset = true); double getInterpolatedHeight(double x, double z, bool with_painting, bool water_offset = true);
double getWaterOffset() const; double getWaterOffset() const;
unsigned long getMemoryStats(); unsigned long getMemoryStats();
HeightInfo getHeightInfo(); HeightInfo getHeightInfo();
@ -40,19 +43,17 @@ class DEFINITIONSHARED_EXPORT TerrainDefinition : public DefinitionNode {
void applyPreset(TerrainPreset preset, RandomGenerator &random = RandomGeneratorDefault); void applyPreset(TerrainPreset preset, RandomGenerator &random = RandomGeneratorDefault);
public: public:
double height;
double shadow_smoothing; double shadow_smoothing;
TerrainHeightMap *height_map; TerrainHeightMap *height_map;
bool has_painting; bool has_painting;
double _detail;
NoiseGenerator *_height_noise;
double _min_height; double _min_height;
double _max_height; double _max_height;
private: private:
FloatNode *water_height; FloatNode *water_height;
NoiseNode *height_noise;
}; };
} }
} }

View file

@ -20,29 +20,29 @@ double TerrainHeightMap::getInitialValue(double x, double y) const {
void TerrainHeightMap::brushElevation(const PaintedGridBrush &brush, double x, double y, double value, bool commit) { void TerrainHeightMap::brushElevation(const PaintedGridBrush &brush, double x, double y, double value, bool commit) {
PaintedGridBrushRaiseLower sbrush(brush); PaintedGridBrushRaiseLower sbrush(brush);
applyBrush(sbrush, x, y, value / terrain->height, commit); applyBrush(sbrush, x, y, value, commit);
} }
void TerrainHeightMap::brushFlatten(const PaintedGridBrush &brush, double x, double y, double height, double force, void TerrainHeightMap::brushFlatten(const PaintedGridBrush &brush, double x, double y, double height, double force,
bool commit) { bool commit) {
PaintedGridBrushFlatten sbrush(brush, height); PaintedGridBrushFlatten sbrush(brush, height);
applyBrush(sbrush, x, y, force / terrain->height, commit); applyBrush(sbrush, x, y, force, commit);
} }
void TerrainHeightMap::brushSmooth(const PaintedGridBrush &brush, double x, double y, double value, bool commit) { void TerrainHeightMap::brushSmooth(const PaintedGridBrush &brush, double x, double y, double value, bool commit) {
PaintedGridBrushSmooth sbrush(brush); PaintedGridBrushSmooth sbrush(brush);
applyBrush(sbrush, x, y, value / terrain->height, commit); applyBrush(sbrush, x, y, value, commit);
} }
void TerrainHeightMap::brushAddNoise(const PaintedGridBrush &brush, double x, double y, NoiseGenerator *generator, void TerrainHeightMap::brushAddNoise(const PaintedGridBrush &brush, double x, double y, NoiseGenerator *generator,
double value, bool commit) { double value, bool commit) {
PaintedGridBrushAddNoise sbrush(brush, generator); PaintedGridBrushAddNoise sbrush(brush, generator);
applyBrush(sbrush, x, y, value / terrain->height, commit); applyBrush(sbrush, x, y, value, commit);
} }
void TerrainHeightMap::brushReset(const PaintedGridBrush &brush, double x, double y, double value, bool commit) { void TerrainHeightMap::brushReset(const PaintedGridBrush &brush, double x, double y, double value, bool commit) {
PaintedGridBrushReset sbrush(brush); PaintedGridBrushReset sbrush(brush);
applyBrush(sbrush, x, y, value / terrain->height, commit); applyBrush(sbrush, x, y, value, commit);
} }
void TerrainHeightMap::clearPainting() { void TerrainHeightMap::clearPainting() {

View file

@ -32,9 +32,8 @@ void TextureLayerDefinition::validate() {
material->validate(); material->validate();
/* Update zone height range */ // Update zone height range
const Scenery *scenery = getScenery(); if (auto scenery = getScenery()) {
if (scenery) {
TerrainDefinition *terrain = scenery->getTerrain(); TerrainDefinition *terrain = scenery->getTerrain();
HeightInfo height_info = terrain->getHeightInfo(); HeightInfo height_info = terrain->getHeightInfo();
terrain_zone->setRelativeHeight(height_info.min_height, height_info.base_height, height_info.max_height); terrain_zone->setRelativeHeight(height_info.min_height, height_info.base_height, height_info.max_height);
@ -80,15 +79,15 @@ void TextureLayerDefinition::applyPreset(TextureLayerPreset preset, RandomGenera
material->shininess = 4.0; material->shininess = 4.0;
break; break;
case TEXTURES_LAYER_PRESET_ROCK: case TEXTURES_LAYER_PRESET_ROCK:
terrain_zone->addHeightRangeQuick(1.0, 0.55, 0.7, 0.9, 1.0); terrain_zone->addHeightRangeQuick(1.0, 0.6, 0.7, 0.87, 0.95);
displacement_noise->setConfig(1.0, 0.3, 0.5, 0.85); displacement_noise->setConfig(1.0, 0.4, 0.5, 0.85);
detail_noise->setConfig(0.02, 0.04); detail_noise->setConfig(0.02, 0.04);
material->setColor(0.6, 0.55, 0.57, 1.0); material->setColor(0.6, 0.55, 0.57, 1.0);
material->reflection = 0.006; material->reflection = 0.006;
material->shininess = 6.0; material->shininess = 6.0;
break; break;
case TEXTURES_LAYER_PRESET_GRASS: case TEXTURES_LAYER_PRESET_GRASS:
terrain_zone->addHeightRangeQuick(1.0, 0.45, 0.5, 0.8, 1.0); terrain_zone->addHeightRangeQuick(1.0, 0.45, 0.5, 0.7, 0.9);
terrain_zone->addSlopeRangeQuick(1.0, 0.0, 0.0, 0.05, 0.4); terrain_zone->addSlopeRangeQuick(1.0, 0.0, 0.0, 0.05, 0.4);
displacement_noise->setConfig(0.4, 0.05); displacement_noise->setConfig(0.4, 0.05);
detail_noise->setConfig(0.01, 0.1); detail_noise->setConfig(0.01, 0.1);
@ -98,7 +97,7 @@ void TextureLayerDefinition::applyPreset(TextureLayerPreset preset, RandomGenera
break; break;
case TEXTURES_LAYER_PRESET_SAND: case TEXTURES_LAYER_PRESET_SAND:
terrain_zone->addHeightRangeQuick(1.0, 0.495, 0.505, 0.56, 0.63); terrain_zone->addHeightRangeQuick(1.0, 0.495, 0.505, 0.56, 0.63);
terrain_zone->addSlopeRangeQuick(1.0, 0.0, 0.0, 0.1, 0.4); terrain_zone->addSlopeRangeQuick(1.0, 0.0, 0.0, 0.05, 0.3);
displacement_noise->setConfig(0.04, 0.1, 0.5, 0.3); displacement_noise->setConfig(0.04, 0.1, 0.5, 0.3);
detail_noise->setConfig(0.004, 0.08); detail_noise->setConfig(0.004, 0.08);
material->setColor(1.2, 1.1, 0.9, 1.0); material->setColor(1.2, 1.1, 0.9, 1.0);
@ -106,8 +105,8 @@ void TextureLayerDefinition::applyPreset(TextureLayerPreset preset, RandomGenera
material->shininess = 1.0; material->shininess = 1.0;
break; break;
case TEXTURES_LAYER_PRESET_SNOW: case TEXTURES_LAYER_PRESET_SNOW:
terrain_zone->addHeightRangeQuick(1.0, 0.77, 0.85, 1.0, 1.0); terrain_zone->addHeightRangeQuick(1.0, 0.87, 0.95, 10.0, 100.0);
terrain_zone->addSlopeRangeQuick(1.0, 0.0, 0.0, 0.2, 1.0); terrain_zone->addSlopeRangeQuick(1.0, 0.0, 0.0, 0.1, 1.0);
displacement_noise->setConfig(0.4, 0.07); displacement_noise->setConfig(0.4, 0.07);
detail_noise->setConfig(0.01, 0.03); detail_noise->setConfig(0.01, 0.03);
material->setColor(5.0, 5.0, 5.0, 1.0); material->setColor(5.0, 5.0, 5.0, 1.0);

View file

@ -1,6 +1,5 @@
#include "Zone.h" #include "Zone.h"
#include <cstring>
#include "Curve.h" #include "Curve.h"
#include "PackStream.h" #include "PackStream.h"
#include "Vector3.h" #include "Vector3.h"
@ -39,8 +38,7 @@ void Zone::load(PackStream *stream) {
} }
void Zone::copy(DefinitionNode *_destination) const { void Zone::copy(DefinitionNode *_destination) const {
Zone *destination = (Zone *)_destination; if (auto destination = static_cast<Zone *>(_destination)) {
destination->absolute_height = absolute_height; destination->absolute_height = absolute_height;
destination->relative_height_min = relative_height_min; destination->relative_height_min = relative_height_min;
destination->relative_height_middle = relative_height_middle; destination->relative_height_middle = relative_height_middle;
@ -48,6 +46,7 @@ void Zone::copy(DefinitionNode *_destination) const {
value_by_height->copy(destination->value_by_height); value_by_height->copy(destination->value_by_height);
value_by_slope->copy(destination->value_by_slope); value_by_slope->copy(destination->value_by_slope);
}
} }
void Zone::clear() { void Zone::clear() {

View file

@ -59,8 +59,14 @@ static void startTestRender(SoftwareCanvasRenderer *renderer, const string &name
} }
static void testNoise() { static void testNoise() {
// TODO Test all noise functions similarly
NoiseFunctionSimplex noise; NoiseFunctionSimplex noise;
cout << "Testing simplex distribution..." << endl;
auto report = noise.checkDistribution();
cout << report;
string filename = getFileName("noise_simplex_cache_value"); string filename = getFileName("noise_simplex_cache_value");
cout << "Rendering " << filename << "..." << endl; cout << "Rendering " << filename << "..." << endl;
noise.getValueTexture()->saveToFile(filename); noise.getValueTexture()->saveToFile(filename);
@ -136,7 +142,7 @@ static void testCloudQuality() {
scenery.autoPreset(3); scenery.autoPreset(3);
scenery.getCamera()->setLocation(Vector3(5.0, 5.0, 5.0)); scenery.getCamera()->setLocation(Vector3(5.0, 5.0, 5.0));
scenery.getCamera()->setTarget(Vector3(8.0, 7.25, 8.0)); scenery.getCamera()->setTarget(Vector3(8.0, 7.25, 8.0));
scenery.getTerrain()->height = 0.0; scenery.getTerrain()->propHeightNoise()->setConfig(0.0);
scenery.getTerrain()->validate(); scenery.getTerrain()->validate();
SoftwareCanvasRenderer renderer(&scenery); SoftwareCanvasRenderer renderer(&scenery);
@ -182,7 +188,7 @@ static void testGodRays() {
scenery.getAtmosphere()->setDayTime(12); scenery.getAtmosphere()->setDayTime(12);
scenery.getCamera()->setLocation(Vector3(0.0, 1.0, -50.0)); scenery.getCamera()->setLocation(Vector3(0.0, 1.0, -50.0));
scenery.getCamera()->setTarget(Vector3(0.0, 15.0, 0.0)); scenery.getCamera()->setTarget(Vector3(0.0, 15.0, 0.0));
scenery.getTerrain()->height = 0.0; scenery.getTerrain()->propHeightNoise()->setConfig(0.0);
scenery.getTerrain()->validate(); scenery.getTerrain()->validate();
scenery.getClouds()->clear(); scenery.getClouds()->clear();
@ -408,15 +414,15 @@ static void testTextures() {
} }
void runTestSuite() { void runTestSuite() {
testAtmosphereBruneton();
testCloudQuality();
testCloudsNearGround();
testGodRays();
testGroundShadowQuality();
testNearFrustum();
testNoise(); testNoise();
testTextures();
testGodRays();
testNearFrustum();
testCloudsNearGround();
testVegetationModels();
testOpenGLVegetationImpostor(); testOpenGLVegetationImpostor();
testRasterizationQuality(); testRasterizationQuality();
testTextures(); testGroundShadowQuality();
testVegetationModels(); testCloudQuality();
testAtmosphereBruneton();
} }

View file

@ -38,7 +38,7 @@ void TerrainRenderer::setQuality(double factor) {
} }
double TerrainRenderer::getHeight(double x, double z, bool with_painting, bool water_offset) { double TerrainRenderer::getHeight(double x, double z, bool with_painting, bool water_offset) {
return parent->getScenery()->getTerrain()->getInterpolatedHeight(x, z, true, with_painting, water_offset); return parent->getScenery()->getTerrain()->getInterpolatedHeight(x, z, with_painting, water_offset);
} }
TerrainRenderer::TerrainResult TerrainRenderer::getResult(double x, double z, bool with_painting) { TerrainRenderer::TerrainResult TerrainRenderer::getResult(double x, double z, bool with_painting) {

View file

@ -204,3 +204,27 @@ TEST(FractalNoise, Noise3d) {
ASSERT_EQ(2, noise.calls); ASSERT_EQ(2, noise.calls);
EXPECT_DOUBLE_EQ(3.2 + 3.25, result); EXPECT_DOUBLE_EQ(3.2 + 3.25, result);
} }
TEST(FractalNoise, estimateRange) {
TestFractalNoise noise(0.8);
noise.setScaling(1.0, 1.0);
noise.setStep(0.1);
double min, max;
noise.estimateRange(&min, &max, 10.0);
EXPECT_DOUBLE_EQ(0.0, min);
EXPECT_DOUBLE_EQ(0.0, max);
noise.estimateRange(&min, &max, 0.5);
EXPECT_DOUBLE_EQ(-0.5, min);
EXPECT_DOUBLE_EQ(0.5, max);
noise.estimateRange(&min, &max, 0.05);
EXPECT_DOUBLE_EQ(-0.55, min);
EXPECT_DOUBLE_EQ(0.55, max);
noise.estimateRange(&min, &max, 0.005);
EXPECT_DOUBLE_EQ(-0.555, min);
EXPECT_DOUBLE_EQ(0.555, max);
}

View file

@ -4,6 +4,9 @@
#include "Scenery.h" #include "Scenery.h"
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "OpenGLVertexArray.h" #include "OpenGLVertexArray.h"
#include "TerrainDefinition.h"
#include "NoiseNode.h"
#include "TestToolNoise.h"
#include "Vector3.h" #include "Vector3.h"
static void checkVertex(const OpenGLVertexArray *array, int index, const Vector3 &expected_location, double expected_u, static void checkVertex(const OpenGLVertexArray *array, int index, const Vector3 &expected_location, double expected_u,
@ -20,6 +23,7 @@ static void checkVertex(const OpenGLVertexArray *array, int index, const Vector3
TEST(OpenGLTerrainChunk, setFirstStepVertices) { TEST(OpenGLTerrainChunk, setFirstStepVertices) {
Scenery scenery; Scenery scenery;
scenery.getTerrain()->propHeightNoise()->forceSetGenerator(new ConstantFractalNoise(0.0));
OpenGLRenderer renderer(&scenery); OpenGLRenderer renderer(&scenery);
OpenGLTerrainChunk chunk(&renderer, 0.0, 0.0, 1.0, 1); OpenGLTerrainChunk chunk(&renderer, 0.0, 0.0, 1.0, 1);
@ -40,6 +44,7 @@ TEST(OpenGLTerrainChunk, setFirstStepVertices) {
TEST(OpenGLTerrainChunk, augmentVertices) { TEST(OpenGLTerrainChunk, augmentVertices) {
Scenery scenery; Scenery scenery;
scenery.getTerrain()->propHeightNoise()->forceSetGenerator(new ConstantFractalNoise(0.0));
OpenGLRenderer renderer(&scenery); OpenGLRenderer renderer(&scenery);
OpenGLTerrainChunk chunk(&renderer, 0.0, 0.0, 1.0, 1); OpenGLTerrainChunk chunk(&renderer, 0.0, 0.0, 1.0, 1);

View file

@ -8,13 +8,13 @@ TEST(OpenGLVariable, setNoise) {
NoiseFunctionSimplex noise; NoiseFunctionSimplex noise;
noise.setScaling(0.5, 2.0); noise.setScaling(0.5, 2.0);
noise.setStep(3.0, 0.4); noise.setStep(0.5, 0.4);
var.set(noise); var.set(noise);
EXPECT_EQ(4, var.getIntValue()); EXPECT_EQ(4, var.getIntValue());
EXPECT_FLOAT_EQ(2.0f, var.getFloatArrayValue(0)); EXPECT_FLOAT_EQ(2.0f, var.getFloatArrayValue(0));
EXPECT_FLOAT_EQ(1.0f, var.getFloatArrayValue(1)); EXPECT_FLOAT_EQ(1.0f, var.getFloatArrayValue(1));
EXPECT_FLOAT_EQ(1.0f / 3.0f, var.getFloatArrayValue(2)); EXPECT_FLOAT_EQ(2.0f, var.getFloatArrayValue(2));
EXPECT_FLOAT_EQ(1.2f, var.getFloatArrayValue(3)); EXPECT_FLOAT_EQ(0.2f, var.getFloatArrayValue(3));
} }

View file

@ -2,39 +2,51 @@
#include <cmath> #include <cmath>
#include "Maths.h" #include "Maths.h"
#include "NoiseGenerator.h" #include "NoiseNode.h"
#include "TestToolNoise.h"
#include "TerrainDefinition.h" #include "TerrainDefinition.h"
#include "TerrainHeightMap.h" #include "TerrainHeightMap.h"
#include "PaintedGridBrush.h" #include "PaintedGridBrush.h"
#include "FloatNode.h" #include "FloatNode.h"
/* Noise sin period is defined at 20.0 */ // Noise sin period is defined at 20.0
#define X_FACTOR (Maths::PI / 10.0) static constexpr double X_FACTOR = Maths::PI / 10.0;
static double _noise1dMock(double x) { namespace {
return sin(x * X_FACTOR) * 0.5 + 0.5; class SinFractalNoise : public FractalNoise {
} public:
SinFractalNoise() {
setScaling(1.0, 2.0);
setStep(0.0);
NoiseState state;
state.setLevelCount(1);
state.setLevel(0, 0.0, 0.0, 0.0);
setState(state);
}
virtual ~SinFractalNoise();
virtual double getBase1d(double x) const override {
return sin(x * X_FACTOR) * 0.5;
}
virtual double getBase2d(double x, double) const override {
return sin(x * X_FACTOR) * 0.5;
}
virtual double getBase3d(double x, double, double) const override {
return sin(x * X_FACTOR) * 0.5;
}
};
static double _noise2dMock(double x, double) { SinFractalNoise::~SinFractalNoise() {
return sin(x * X_FACTOR) * 0.5 + 0.5;
}
static double _noise3dMock(double x, double, double) {
return sin(x * X_FACTOR) * 0.5 + 0.5;
} }
class TerrainPainting_Test : public BaseTestCase { class TerrainPainting_Test : public BaseTestCase {
public:
virtual ~TerrainPainting_Test();
protected: protected:
virtual void SetUp() { virtual void SetUp() {
terrain = new TerrainDefinition(NULL); terrain = new TerrainDefinition(NULL);
terrain->height = 3.0; terrain->propHeightNoise()->forceSetGenerator(new SinFractalNoise);
terrain->_height_noise->clearLevels();
terrain->propWaterHeight()->setValue(0.0); terrain->propWaterHeight()->setValue(0.0);
NoiseGenerator::NoiseLevel level = {1.0, 2.0, -1.0};
terrain->_height_noise->addLevel(level);
noise_state.resetOffsets();
terrain->_height_noise->setState(noise_state);
terrain->_height_noise->setCustomFunction(_noise1dMock, _noise2dMock, _noise3dMock);
} }
virtual void TearDown() { virtual void TearDown() {
@ -44,6 +56,9 @@ class TerrainPainting_Test : public BaseTestCase {
TerrainDefinition *terrain; TerrainDefinition *terrain;
NoiseState noise_state; NoiseState noise_state;
}; };
TerrainPainting_Test::~TerrainPainting_Test() {
}
}
TEST_F(TerrainPainting_Test, grid) { TEST_F(TerrainPainting_Test, grid) {
/* Test base grid */ /* Test base grid */
@ -63,12 +78,9 @@ TEST_F(TerrainPainting_Test, grid) {
EXPECT_DOUBLE_EQ(-1.0, terrain->getGridHeight(-5, 0, 0)); EXPECT_DOUBLE_EQ(-1.0, terrain->getGridHeight(-5, 0, 0));
/* Test interpolated result */ /* Test interpolated result */
EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(0.0, 0.0, 0, 0), 0.0); EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(0.0, 0.0, false, false), 0.0);
EXPECT_DOUBLE_IN_RANGE(terrain->getInterpolatedHeight(0.5, 0.0, 0, 0), 0.1564, 0.1566); EXPECT_DOUBLE_IN_RANGE(terrain->getInterpolatedHeight(0.5, 0.0, false, false), 0.1564, 0.1566);
EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(1.0, 0.0, 0, 0), sin(1.0 * X_FACTOR)); EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(1.0, 0.0, false, false), sin(X_FACTOR));
EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(0.0, 0.0, 1, 0), 0.0);
EXPECT_DOUBLE_IN_RANGE(terrain->getInterpolatedHeight(0.5, 0.0, 1, 0), 3.0 * 0.1564, 3.0 * 0.1566);
EXPECT_DOUBLE_EQ(terrain->getInterpolatedHeight(1.0, 0.0, 1, 0), 3.0 * sin(1.0 * X_FACTOR));
} }
static void _checkBrushResultSides(TerrainDefinition *terrain, PaintedGridBrush *, double center, double midhard, static void _checkBrushResultSides(TerrainDefinition *terrain, PaintedGridBrush *, double center, double midhard,
@ -103,8 +115,7 @@ static void _checkBrushResult(TerrainDefinition *terrain, PaintedGridBrush *brus
TEST_F(TerrainPainting_Test, brush_flatten) { TEST_F(TerrainPainting_Test, brush_flatten) {
/* Set up */ /* Set up */
PaintedGridBrush brush(2.0, 2.0, 4.0); PaintedGridBrush brush(2.0, 2.0, 4.0);
terrain->height = 1.0; terrain->propHeightNoise()->forceSetGenerator(new ConstantFractalNoise(0.0));
terrain->_height_noise->forceValue(0.0);
/* Test flattening center at 0.5 */ /* Test flattening center at 0.5 */
_checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); _checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0);
@ -120,21 +131,13 @@ TEST_F(TerrainPainting_Test, brush_flatten) {
/* Test cumulative effect */ /* Test cumulative effect */
terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 0.01, true); terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 0.01, true);
_checkBrushResult(terrain, &brush, 0.00995, 0.00995, 0.00995, 0.0049875, 0.0, 0.0, 0); _checkBrushResult(terrain, &brush, 0.00995, 0.00995, 0.00995, 0.0049875, 0.0, 0.0, 0);
/* Test with height modifier */
terrain->height = 10.0;
terrain->height_map->clearPainting();
_checkBrushResult(terrain, &brush, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0);
terrain->height_map->brushFlatten(brush, 0.0, 0.0, 0.5, 1.0, true);
_checkBrushResult(terrain, &brush, 0.05, 0.05, 0.05, 0.025, 0.0, 0.0, 0);
} }
TEST_F(TerrainPainting_Test, brush_reset) { TEST_F(TerrainPainting_Test, brush_reset) {
/* Set up */ /* Set up */
PaintedGridBrush brush(2.0, 2.0, 4.0); PaintedGridBrush brush(2.0, 2.0, 4.0);
PaintedGridBrush brush_full(4.0, 0.0, 4.0); PaintedGridBrush brush_full(4.0, 0.0, 4.0);
terrain->height = 1.0; terrain->propHeightNoise()->forceSetGenerator(new ConstantFractalNoise(1.0));
terrain->_height_noise->forceValue(1.0);
/* Test resetting at center */ /* Test resetting at center */
_checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0); _checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0);
@ -154,13 +157,4 @@ TEST_F(TerrainPainting_Test, brush_reset) {
/* Test cumulative effect */ /* Test cumulative effect */
terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1, true); terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1, true);
_checkBrushResult(terrain, &brush, 1.81, 1.81, 1.81, 1.9025, 2.0, 1.0, 0); _checkBrushResult(terrain, &brush, 1.81, 1.81, 1.81, 1.9025, 2.0, 1.0, 0);
/* Test with height modifier */
terrain->height = 10.0;
terrain->height_map->clearPainting();
_checkBrushResult(terrain, &brush, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0);
terrain->height_map->brushFlatten(brush_full, 0.0, 0.0, 2.0, 1.0, true);
_checkBrushResult(terrain, &brush, 1.1, 1.1, 1.1, 1.1, 1.1, 1.0, 0);
terrain->height_map->brushReset(brush, 0.0, 0.0, 0.1, true);
_checkBrushResult(terrain, &brush, 1.099, 1.099, 1.099, 1.0995, 1.1, 1.0, 0);
} }

View file

@ -10,10 +10,10 @@ namespace {
class ConstantFractalNoise : public FractalNoise { class ConstantFractalNoise : public FractalNoise {
public: public:
ConstantFractalNoise(double value) : value(value) { ConstantFractalNoise(double value) : value(value) {
// The noise will yield its value at first iteration, then its height will collapse to 0 // The noise will yield its value at first iteration, then will collapse to 0
setScaling(1.0, 0.0); setStep(0.0);
} }
virtual double getBase3d(double, double, double) const { virtual double getBase3d(double, double, double) const override {
return value; return value;
} }