diff --git a/src/basics/NoiseGenerator.cpp b/src/basics/NoiseGenerator.cpp index 549d95a..ac6fac5 100644 --- a/src/basics/NoiseGenerator.cpp +++ b/src/basics/NoiseGenerator.cpp @@ -135,8 +135,8 @@ void NoiseGenerator::setState(const NoiseState &state) { state.copy(&this->state); } -void NoiseGenerator::randomizeOffsets() { - state.randomizeOffsets(); +void NoiseGenerator::randomizeOffsets(RandomGenerator &random) { + state.randomizeOffsets(random); } NoiseGenerator::NoiseFunction NoiseGenerator::getFunction() { diff --git a/src/basics/NoiseGenerator.h b/src/basics/NoiseGenerator.h index 1d28850..cfcec14 100644 --- a/src/basics/NoiseGenerator.h +++ b/src/basics/NoiseGenerator.h @@ -39,7 +39,7 @@ class BASICSSHARED_EXPORT NoiseGenerator { } void setState(const NoiseState &state); - void randomizeOffsets(); + void randomizeOffsets(RandomGenerator &random = RandomGeneratorDefault); NoiseFunction getFunction(); void setCustomFunction(double (*func1d)(double x), double (*func2d)(double x, double y), double (*func3d)(double x, double y, double z)); diff --git a/src/basics/NoiseState.cpp b/src/basics/NoiseState.cpp index 4678947..e799737 100644 --- a/src/basics/NoiseState.cpp +++ b/src/basics/NoiseState.cpp @@ -42,11 +42,11 @@ void NoiseState::copy(NoiseState *destination) const { destination->level_offsets = level_offsets; } -void NoiseState::randomizeOffsets() { +void NoiseState::randomizeOffsets(RandomGenerator &random) { for (auto &level_offset : level_offsets) { - level_offset.x = RandomGenerator::random(); - level_offset.y = RandomGenerator::random(); - level_offset.z = RandomGenerator::random(); + level_offset.x = random.genDouble(); + level_offset.y = random.genDouble(); + level_offset.z = random.genDouble(); } } diff --git a/src/basics/NoiseState.h b/src/basics/NoiseState.h index f336095..d0bfae2 100644 --- a/src/basics/NoiseState.h +++ b/src/basics/NoiseState.h @@ -28,7 +28,7 @@ class BASICSSHARED_EXPORT NoiseState { void load(PackStream *stream); void copy(NoiseState *destination) const; - void randomizeOffsets(); + void randomizeOffsets(RandomGenerator &random = RandomGeneratorDefault); void resetOffsets(double x = 0.0, double y = 0.0, double z = 0.0); void setLevel(int level, double x, double y, double z); diff --git a/src/basics/Vector3.cpp b/src/basics/Vector3.cpp index 1902d14..41e84ee 100644 --- a/src/basics/Vector3.cpp +++ b/src/basics/Vector3.cpp @@ -19,7 +19,6 @@ Vector3::Vector3(const VectorSpherical &v) Vector3::Vector3(double x, double y, double z) : x(x), y(y), z(z) { } - void Vector3::save(PackStream *stream) const { stream->write(&x); stream->write(&y); @@ -115,10 +114,10 @@ Vector3 Vector3::midPointTo(const Vector3 &other) const { return Vector3((other.x + x) * 0.5, (other.y + y) * 0.5, (other.z + z) * 0.5); } -Vector3 Vector3::randomInSphere(double radius, bool only_surface) { +Vector3 Vector3::randomInSphere(double radius, bool only_surface, RandomGenerator &random) { // TODO More uniform spatial repartition // The current randomization clusters result near the center and at the poles - VectorSpherical vec = {only_surface ? radius : RandomGenerator::random() * radius, - (RandomGenerator::random() - 0.5) * M_PI, RandomGenerator::random() * M_2PI}; + VectorSpherical vec = {only_surface ? radius : random.genDouble() * radius, + (random.genDouble() - 0.5) * M_PI, random.genDouble() * M_2PI}; return Vector3(vec); } diff --git a/src/basics/Vector3.h b/src/basics/Vector3.h index 3b87ce3..c04b9d0 100644 --- a/src/basics/Vector3.h +++ b/src/basics/Vector3.h @@ -71,7 +71,7 @@ class BASICSSHARED_EXPORT Vector3 { * * If *only_surface* is true, produce a vector with *radius* as length. */ - static Vector3 randomInSphere(double radius = 1.0, bool only_surface = false); + static Vector3 randomInSphere(double radius = 1.0, bool only_surface = false, RandomGenerator &random = RandomGeneratorDefault); public: // TODO Make private diff --git a/src/definition/AtmosphereDefinition.cpp b/src/definition/AtmosphereDefinition.cpp index 08232cc..9d4e691 100644 --- a/src/definition/AtmosphereDefinition.cpp +++ b/src/definition/AtmosphereDefinition.cpp @@ -100,7 +100,7 @@ void AtmosphereDefinition::getHMS(int *hour, int *minute, int *second) const { *second = value - *minute * 60.0; } -void AtmosphereDefinition::applyPreset(AtmospherePreset preset) { +void AtmosphereDefinition::applyPreset(AtmospherePreset preset, RandomGenerator &random) { sun_color.r = 1.0; sun_color.g = 0.95; sun_color.b = 0.9; @@ -142,28 +142,28 @@ void AtmosphereDefinition::applyPreset(AtmospherePreset preset) { ; } - generateStars(2000); + generateStars(2000, random); validate(); } -void AtmosphereDefinition::generateStars(int count) { +void AtmosphereDefinition::generateStars(int count, RandomGenerator &random) { stars.clear(); for (int i = 0; i < count; ++i) { Star star; star.location = - Vector3((RandomGenerator::random() - 0.5) * 100000.0, (RandomGenerator::random() * 0.5) * 100000.0, - (RandomGenerator::random() - 0.5) * 100000.0); + Vector3((random.genDouble() - 0.5) * 100000.0, (random.genDouble() * 0.5) * 100000.0, + (random.genDouble() - 0.5) * 100000.0); if (star.location.getNorm() < 30000.0) { i--; continue; } - double brillance = RandomGenerator::random() * 0.05 + 0.1; - star.col = Color(brillance + RandomGenerator::random() * 0.03, brillance + RandomGenerator::random() * 0.03, - brillance + RandomGenerator::random() * 0.03, 1.0); - star.radius = 30.0 + RandomGenerator::random() * 20.0; + double brillance = random.genDouble() * 0.05 + 0.1; + star.col = Color(brillance + random.genDouble() * 0.03, brillance + random.genDouble() * 0.03, + brillance + random.genDouble() * 0.03, 1.0); + star.radius = 30.0 + random.genDouble() * 20.0; stars.push_back(star); } diff --git a/src/definition/AtmosphereDefinition.h b/src/definition/AtmosphereDefinition.h index a4affb0..5d614b6 100644 --- a/src/definition/AtmosphereDefinition.h +++ b/src/definition/AtmosphereDefinition.h @@ -64,8 +64,8 @@ class DEFINITIONSHARED_EXPORT AtmosphereDefinition : public DefinitionNode { */ void getHMS(int *hour, int *minute, int *second) const; - void applyPreset(AtmospherePreset preset); - void generateStars(int count); + void applyPreset(AtmospherePreset preset, RandomGenerator &random = RandomGeneratorDefault); + void generateStars(int count, RandomGenerator &random = RandomGeneratorDefault); public: AtmosphereModel model; diff --git a/src/definition/CloudsDefinition.cpp b/src/definition/CloudsDefinition.cpp index a73595a..abdd862 100644 --- a/src/definition/CloudsDefinition.cpp +++ b/src/definition/CloudsDefinition.cpp @@ -9,12 +9,13 @@ static DefinitionNode *_layerConstructor(Layers *parent, const std::string &name CloudsDefinition::CloudsDefinition(DefinitionNode *parent) : Layers(parent, "clouds", _layerConstructor) { } -void CloudsDefinition::applyPreset(CloudsPreset preset) { +void CloudsDefinition::applyPreset(CloudsPreset preset, RandomGenerator &random) { clear(); if (preset == CLOUDS_PRESET_PARTLY_CLOUDY) { CloudLayerDefinition layer(NULL, "Strato-cumulus"); layer.type = CloudLayerDefinition::STRATOCUMULUS; + layer.noise_state.randomizeOffsets(random); addLayer(layer); } } diff --git a/src/definition/CloudsDefinition.h b/src/definition/CloudsDefinition.h index f032c9d..08eaf3f 100644 --- a/src/definition/CloudsDefinition.h +++ b/src/definition/CloudsDefinition.h @@ -17,7 +17,7 @@ class DEFINITIONSHARED_EXPORT CloudsDefinition : public Layers { } typedef enum { CLOUDS_PRESET_PARTLY_CLOUDY } CloudsPreset; - void applyPreset(CloudsPreset preset); + void applyPreset(CloudsPreset preset, RandomGenerator &random = RandomGeneratorDefault); }; } } diff --git a/src/definition/Scenery.cpp b/src/definition/Scenery.cpp index 163ad0e..4bb4bc2 100644 --- a/src/definition/Scenery.cpp +++ b/src/definition/Scenery.cpp @@ -1,6 +1,5 @@ #include "Scenery.h" -#include #include #include "PackStream.h" @@ -11,6 +10,7 @@ #include "TexturesDefinition.h" #include "WaterDefinition.h" #include "Logs.h" +#include "RandomGenerator.h" static const double APP_HEADER = 19866544632.125; static const int DATA_VERSION = 1; @@ -93,24 +93,25 @@ Scenery *Scenery::getScenery() { return this; } -void Scenery::autoPreset(int seed) { - if (!seed) { - seed = time(NULL); - } - srand(seed); - - terrain->applyPreset(TerrainDefinition::TERRAIN_PRESET_STANDARD); - textures->applyPreset(TexturesDefinition::TEXTURES_PRESET_FULL); - atmosphere->applyPreset(AtmosphereDefinition::ATMOSPHERE_PRESET_CLEAR_DAY); - water->applyPreset(WaterDefinition::WATER_PRESET_LAKE); - clouds->applyPreset(CloudsDefinition::CLOUDS_PRESET_PARTLY_CLOUDY); +void Scenery::autoPreset(RandomGenerator &random) { + terrain->applyPreset(TerrainDefinition::TERRAIN_PRESET_STANDARD, random); + textures->applyPreset(TexturesDefinition::TEXTURES_PRESET_FULL, random); + atmosphere->applyPreset(AtmosphereDefinition::ATMOSPHERE_PRESET_CLEAR_DAY, random); + water->applyPreset(WaterDefinition::WATER_PRESET_LAKE, random); + clouds->applyPreset(CloudsDefinition::CLOUDS_PRESET_PARTLY_CLOUDY, random); camera->setLocation(VECTOR_ZERO); camera->setTarget(VECTOR_NORTH); validate(); - Logs::debug() << "[Definition] New scenery generated from seed " << seed << std::endl; + Logs::debug() << "[Definition] New scenery generated from seed " << random.getSeed() << std::endl; +} + +void Scenery::autoPreset(unsigned int seed) +{ + RandomGenerator random(seed); + autoPreset(random); } void Scenery::setAtmosphere(AtmosphereDefinition *atmosphere) { diff --git a/src/definition/Scenery.h b/src/definition/Scenery.h index a48b363..12ba2e0 100644 --- a/src/definition/Scenery.h +++ b/src/definition/Scenery.h @@ -34,7 +34,8 @@ class DEFINITIONSHARED_EXPORT Scenery : public DefinitionNode { virtual Scenery *getScenery() override; - void autoPreset(int seed = 0); + void autoPreset(RandomGenerator &random = RandomGeneratorDefault); + void autoPreset(unsigned int seed); void setAtmosphere(AtmosphereDefinition *atmosphere); inline AtmosphereDefinition *getAtmosphere() const { diff --git a/src/definition/TerrainDefinition.cpp b/src/definition/TerrainDefinition.cpp index 2bb7bcf..56c0895 100644 --- a/src/definition/TerrainDefinition.cpp +++ b/src/definition/TerrainDefinition.cpp @@ -113,11 +113,11 @@ unsigned long TerrainDefinition::getMemoryStats() { return height_map->getMemoryStats(); } -void TerrainDefinition::applyPreset(TerrainPreset preset) { +void TerrainDefinition::applyPreset(TerrainPreset preset, RandomGenerator &random) { int resolution = 8; switch (preset) { case TERRAIN_PRESET_STANDARD: - _height_noise->randomizeOffsets(); + _height_noise->randomizeOffsets(random); _height_noise->clearLevels(); _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); diff --git a/src/definition/TerrainDefinition.h b/src/definition/TerrainDefinition.h index e787192..689d2ec 100644 --- a/src/definition/TerrainDefinition.h +++ b/src/definition/TerrainDefinition.h @@ -37,7 +37,7 @@ class DEFINITIONSHARED_EXPORT TerrainDefinition : public DefinitionNode { public: typedef enum { TERRAIN_PRESET_STANDARD } TerrainPreset; - void applyPreset(TerrainPreset preset); + void applyPreset(TerrainPreset preset, RandomGenerator &random = RandomGeneratorDefault); public: double height; diff --git a/src/definition/TextureLayerDefinition.cpp b/src/definition/TextureLayerDefinition.cpp index 6a40cb7..3cf97e3 100644 --- a/src/definition/TextureLayerDefinition.cpp +++ b/src/definition/TextureLayerDefinition.cpp @@ -96,9 +96,9 @@ void TextureLayerDefinition::load(PackStream *stream) { _detail_noise->load(stream); } -void TextureLayerDefinition::applyPreset(TextureLayerPreset preset) { - _displacement_noise->randomizeOffsets(); - _detail_noise->randomizeOffsets(); +void TextureLayerDefinition::applyPreset(TextureLayerPreset preset, RandomGenerator &random) { + _displacement_noise->randomizeOffsets(random); + _detail_noise->randomizeOffsets(random); terrain_zone->clear(); diff --git a/src/definition/TextureLayerDefinition.h b/src/definition/TextureLayerDefinition.h index fc38ed2..6b52d1d 100644 --- a/src/definition/TextureLayerDefinition.h +++ b/src/definition/TextureLayerDefinition.h @@ -29,7 +29,7 @@ class DEFINITIONSHARED_EXPORT TextureLayerDefinition : public DefinitionNode { virtual void copy(DefinitionNode *destination) const override; virtual void validate() override; - void applyPreset(TextureLayerPreset preset); + void applyPreset(TextureLayerPreset preset, RandomGenerator &random = RandomGeneratorDefault); public: Zone *terrain_zone; diff --git a/src/definition/TexturesDefinition.cpp b/src/definition/TexturesDefinition.cpp index 92d1e77..77c10bc 100644 --- a/src/definition/TexturesDefinition.cpp +++ b/src/definition/TexturesDefinition.cpp @@ -9,44 +9,44 @@ static DefinitionNode *_layer_constructor(Layers *parent, const std::string &nam TexturesDefinition::TexturesDefinition(DefinitionNode *parent) : Layers(parent, "textures", _layer_constructor) { } -void TexturesDefinition::applyPreset(TexturesPreset preset) { +void TexturesDefinition::applyPreset(TexturesPreset preset, RandomGenerator &random) { TextureLayerDefinition layer(NULL, "temp"); clear(); - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_MUD); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_MUD, random); layer.setName("Mud"); addLayer(layer); if (preset == TEXTURES_PRESET_FULL) { - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_ROCK); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_ROCK, random); layer.setName("Ground"); addLayer(layer); - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_GRASS); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_GRASS, random); layer.setName("Grass"); addLayer(layer); - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_SAND); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_SAND, random); layer.setName("Sand"); addLayer(layer); - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_SNOW); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_SNOW, random); layer.setName("Snow"); addLayer(layer); } else if (preset == TEXTURES_PRESET_IRELAND) { - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_ROCK); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_ROCK, random); layer.setName("Ground"); addLayer(layer); - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_GRASS); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_GRASS, random); layer.setName("Grass"); addLayer(layer); } else if (preset == TEXTURES_PRESET_ALPS) { - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_ROCK); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_ROCK, random); layer.setName("Ground"); addLayer(layer); - layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_SNOW); + layer.applyPreset(TextureLayerDefinition::TEXTURES_LAYER_PRESET_SNOW, random); layer.setName("Snow"); addLayer(layer); } else if (preset == TEXTURES_PRESET_CANYON) { diff --git a/src/definition/TexturesDefinition.h b/src/definition/TexturesDefinition.h index 3b78f78..7c9d9ac 100644 --- a/src/definition/TexturesDefinition.h +++ b/src/definition/TexturesDefinition.h @@ -22,7 +22,7 @@ class DEFINITIONSHARED_EXPORT TexturesDefinition : public Layers { TEXTURES_PRESET_ALPS, TEXTURES_PRESET_CANYON } TexturesPreset; - void applyPreset(TexturesPreset preset); + void applyPreset(TexturesPreset preset, RandomGenerator &random = RandomGeneratorDefault); double getMaximalDisplacement(); }; diff --git a/src/definition/WaterDefinition.cpp b/src/definition/WaterDefinition.cpp index ccd275b..4753571 100644 --- a/src/definition/WaterDefinition.cpp +++ b/src/definition/WaterDefinition.cpp @@ -154,7 +154,7 @@ void WaterDefinition::nodeChanged(const DefinitionNode *node, const DefinitionDi } } -void WaterDefinition::applyPreset(WaterPreset preset) { +void WaterDefinition::applyPreset(WaterPreset preset, RandomGenerator &random) { if (preset == WATER_PRESET_LAKE) { model->setValue(0); } else if (preset == WATER_PRESET_SEA) { diff --git a/src/definition/WaterDefinition.h b/src/definition/WaterDefinition.h index e191045..4b4b4da 100644 --- a/src/definition/WaterDefinition.h +++ b/src/definition/WaterDefinition.h @@ -36,7 +36,7 @@ class DEFINITIONSHARED_EXPORT WaterDefinition : public DefinitionNode, public De virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff); typedef enum { WATER_PRESET_LAKE, WATER_PRESET_SEA } WaterPreset; - void applyPreset(WaterPreset preset); + void applyPreset(WaterPreset preset, RandomGenerator &random = RandomGeneratorDefault); public: double transparency; diff --git a/src/interface/commandline/tests.cpp b/src/interface/commandline/tests.cpp index f7ffe58..d88feb6 100644 --- a/src/interface/commandline/tests.cpp +++ b/src/interface/commandline/tests.cpp @@ -17,6 +17,7 @@ #include "LightFilter.h" #include "GodRaysSampler.h" #include "Rasterizer.h" +#include "RandomGenerator.h" #include @@ -39,8 +40,8 @@ static void startTestRender(SoftwareCanvasRenderer *renderer, const std::string static void testGroundShadowQuality() { Scenery scenery; - srand(5); - scenery.getTerrain()->applyPreset(TerrainDefinition::TERRAIN_PRESET_STANDARD); + RandomGenerator random(5); + scenery.getTerrain()->applyPreset(TerrainDefinition::TERRAIN_PRESET_STANDARD, random); scenery.getTerrain()->propWaterHeight()->setValue(-0.5); scenery.getWater()->propReflection()->setValue(0.0); scenery.getWater()->material->base->r = 0.0; @@ -49,7 +50,7 @@ static void testGroundShadowQuality() { scenery.getWater()->material->reflection = 0.0; scenery.getWater()->foam_coverage = 0.0; scenery.getWater()->transparency = 0.0; - scenery.getAtmosphere()->applyPreset(AtmosphereDefinition::ATMOSPHERE_PRESET_CLEAR_SUNSET); + scenery.getAtmosphere()->applyPreset(AtmosphereDefinition::ATMOSPHERE_PRESET_CLEAR_SUNSET, random); scenery.getAtmosphere()->setDayTime(16, 45); scenery.getTextures()->clear(); TextureLayerDefinition texture(NULL, "test"); diff --git a/src/system/RandomGenerator.cpp b/src/system/RandomGenerator.cpp index 5b2fb36..dcaf023 100644 --- a/src/system/RandomGenerator.cpp +++ b/src/system/RandomGenerator.cpp @@ -1 +1,38 @@ #include "RandomGenerator.h" + +#include +#include + +static RandomGenerator _RandomGeneratorDefault; +RandomGenerator& paysages::system::RandomGeneratorDefault = _RandomGeneratorDefault; + +class RandomGenerator::RandomGeneratorPrivate { + public: + RandomGeneratorPrivate(unsigned int seed): generator(seed) { + } + + std::default_random_engine generator; + std::uniform_real_distribution distribution_double; +}; + +RandomGenerator::RandomGenerator(RandomGenerator::Seed seed) { + if (not seed) { + std::random_device true_random; + if (true_random.entropy()) { + seed = true_random(); + } else { + seed = std::chrono::system_clock::now().time_since_epoch().count(); + } + } + data = new RandomGeneratorPrivate(seed); +} + +RandomGenerator::~RandomGenerator() +{ + delete data; +} + +double RandomGenerator::genDouble() +{ + return data->distribution_double(data->generator); +} diff --git a/src/system/RandomGenerator.h b/src/system/RandomGenerator.h index ffe9b01..b871a61 100644 --- a/src/system/RandomGenerator.h +++ b/src/system/RandomGenerator.h @@ -3,18 +3,30 @@ #include "system_global.h" -#include - namespace paysages { namespace system { class SYSTEMSHARED_EXPORT RandomGenerator { public: - RandomGenerator(); + typedef unsigned long Seed; + class RandomGeneratorPrivate; - static inline double random() { - return ((double)rand()) / (double)RAND_MAX; + public: + RandomGenerator(Seed seed = 0); + ~RandomGenerator(); + + inline Seed getSeed() const { + return seed; } + + /** + * Generate a 0.0-1.0 random double value. + */ + double genDouble(); + + private: + Seed seed; + RandomGeneratorPrivate *data; }; } } diff --git a/src/system/system_global.h b/src/system/system_global.h index 7daa901..154f1ab 100644 --- a/src/system/system_global.h +++ b/src/system/system_global.h @@ -29,6 +29,9 @@ class Mutex; class Semaphore; class PictureWriter; class Time; +class RandomGenerator; + +extern RandomGenerator& RandomGeneratorDefault; } } using namespace paysages::system;