From f74ba148abaf476aa903d48ea71fa703132caf52 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 25 May 2021 12:53:36 +0100 Subject: [PATCH] Precomputed random numbers API Use precomputed tables to give stable random numbers : replaces previous recompuation when we exhaust the RNG pool. By Marc Eberhard --- simgear/environment/metar.cxx | 2 +- simgear/math/CMakeLists.txt | 4 +- simgear/math/SGGeometryTest.cxx | 2 +- simgear/math/SGMathTest.cxx | 2 +- simgear/math/{sg_random.c => sg_random.cxx} | 125 ++++++++++++- simgear/math/{sg_random.h => sg_random.hxx} | 50 +++++ simgear/scene/material/matmodel.cxx | 2 +- simgear/scene/material/matmodel.hxx | 2 +- simgear/scene/model/persparam.hxx | 2 +- simgear/scene/sky/CloudShaderGeometry.hxx | 2 +- simgear/scene/sky/cloud.cxx | 2 +- simgear/scene/sky/cloudfield.cxx | 2 +- simgear/scene/sky/newcloud.hxx | 2 +- simgear/scene/sky/sky.hxx | 2 +- simgear/scene/tgdb/ReaderWriterSTG.cxx | 10 +- simgear/scene/tgdb/SGBuildingBin.cxx | 51 +++--- simgear/scene/tgdb/SGTexturedTriangleBin.hxx | 2 +- simgear/scene/tgdb/TreeBin.cxx | 9 +- simgear/scene/tgdb/VPBTechnique.cxx | 183 ++++++++++--------- simgear/scene/tgdb/VPBTechnique.hxx | 3 - simgear/scene/tgdb/pt_lights.cxx | 2 +- simgear/scene/tgdb/userdata.cxx | 2 +- simgear/scene/tsync/terrasync.cxx | 2 +- simgear/sound/xmlsound.cxx | 2 +- 24 files changed, 326 insertions(+), 141 deletions(-) rename simgear/math/{sg_random.c => sg_random.cxx} (50%) rename simgear/math/{sg_random.h => sg_random.hxx} (67%) diff --git a/simgear/environment/metar.cxx b/simgear/environment/metar.cxx index ab57a2f5..5b7e740e 100644 --- a/simgear/environment/metar.cxx +++ b/simgear/environment/metar.cxx @@ -46,7 +46,7 @@ #include #include -#include +#include #include #include "metar.hxx" diff --git a/simgear/math/CMakeLists.txt b/simgear/math/CMakeLists.txt index 1de5b00e..f59e07ea 100644 --- a/simgear/math/CMakeLists.txt +++ b/simgear/math/CMakeLists.txt @@ -31,7 +31,7 @@ set(HEADERS leastsqs.hxx sg_geodesy.hxx sg_types.hxx - sg_random.h + sg_random.hxx simd.hxx simd4x4.hxx ) @@ -40,7 +40,7 @@ set(SOURCES SGGeodesy.cxx interpolater.cxx leastsqs.cxx - sg_random.c + sg_random.cxx ) simgear_component(math math "${SOURCES}" "${HEADERS}") diff --git a/simgear/math/SGGeometryTest.cxx b/simgear/math/SGGeometryTest.cxx index 77bf14c6..6640932b 100644 --- a/simgear/math/SGGeometryTest.cxx +++ b/simgear/math/SGGeometryTest.cxx @@ -23,7 +23,7 @@ #include #include "SGGeometry.hxx" -#include "sg_random.h" +#include "sg_random.hxx" template SGVec3 rndVec3(void) diff --git a/simgear/math/SGMathTest.cxx b/simgear/math/SGMathTest.cxx index c36b6e3e..d8f79251 100644 --- a/simgear/math/SGMathTest.cxx +++ b/simgear/math/SGMathTest.cxx @@ -26,7 +26,7 @@ #include "SGMath.hxx" #include "SGRect.hxx" -#include "sg_random.h" +#include "sg_random.hxx" int lineno = 0; diff --git a/simgear/math/sg_random.c b/simgear/math/sg_random.cxx similarity index 50% rename from simgear/math/sg_random.c rename to simgear/math/sg_random.cxx index d9f6bcb7..4e09587b 100644 --- a/simgear/math/sg_random.c +++ b/simgear/math/sg_random.cxx @@ -39,11 +39,12 @@ # include #endif +#include #include #include // for random(), srandom() #include // for time() to seed srandom() -#include "sg_random.h" +#include "sg_random.hxx" // Structure for the random number functions. mt random_seed; @@ -112,3 +113,125 @@ double sg_random() { return mt_rand(&random_seed); } +const int PC_SIZE = 1048576; /* = 2^20 */ +const int PC_MODULO = 1048573; /* = largest prime number smaller than 2^20 */ +const int PC_MAP_X = 251; /* = modulo for noise map in x direction */ +const int PC_MAP_Y = 257; /* = modulo for noise map in y direction */ +const int PC_MAP_I = 16; /* = number of indices for each [x;y] location */ + +static bool pc_initialised = false; +static int pc_int32[PC_SIZE]; +static double pc_uniform[PC_SIZE]; +static double pc_normal[PC_SIZE]; + +thread_local unsigned int pc_seed = 0; + +/** + * Precompute random numbers + */ +static void pc_precompute_numbers() { + mt seed; + mt_init(&seed, 3141592); + + for (int i = 0; i < PC_MODULO; i++) { + pc_int32[i] = mt_rand32(&seed); + pc_uniform[i] = mt_rand(&seed); + pc_normal[i] = -6.0; + + for (int k = 0; k < 12; k++) { + pc_normal[i] += mt_rand(&seed); + } + + } + + pc_initialised = true; +} + +/** + * Initialize current state with a given seed. + */ +void pc_init(unsigned int seed) { + + if (!pc_initialised) { + pc_precompute_numbers(); + } + + // https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key + + seed = ((seed >> 16) ^ seed) * 0x45d9f3b; + seed = ((seed >> 16) ^ seed) * 0x45d9f3b; + seed = (seed >> 16) ^ seed; + + pc_seed = seed % PC_MODULO; +} + +/** + * Initialize current state with a seed that only + * changes every 10 minutes. Used to synchronize + * multi-process deployments. + */ +void pc_init_time_10() { + pc_init((unsigned int) time(NULL) / 600); +} + +/** + * Return a 32-bit random number based on the current state. + */ +unsigned int pc_rand32() { + pc_seed = (pc_seed + 1) % PC_MODULO; + return pc_int32[pc_seed]; +} + +/** + * Return a double precision floating point random number + * between [0.0, 1.0) based on the current state. + */ +double pc_rand() { + pc_seed = (pc_seed + 1) % PC_MODULO; + return pc_uniform[pc_seed]; +} + +/** + * Return a double precision floating point random number + * between [-10.0, 10.0] with a normal distribution of + * average zero and standard deviation of one based on the + * current state. + */ +double pc_norm() { + pc_seed = (pc_seed + 1) % PC_MODULO; + return pc_normal[pc_seed]; +} + +/** + * Return a 32-bit random number from a noise map. + */ +unsigned int pc_map_rand32(const int x, const int y, const int idx) { + const int i = (((y % PC_MAP_Y) * PC_MAP_X) + + (x % PC_MAP_X)) * PC_MAP_I + + (idx % PC_MAP_I); + return pc_int32[i]; +} + +/** + * Return a double precision floating point random number + * between [0.0, 1.0) from a noise map. + */ +double pc_map_rand(const int x, const int y, const int idx) { + const int i = (((y % PC_MAP_Y) * PC_MAP_X) + + (x % PC_MAP_X)) * PC_MAP_I + + (idx % PC_MAP_I); + return pc_uniform[i]; +} + +/** + * Return a double precision floating point random number + * between [-10.0, 10.0] with a normal distribution of + * average zero and standard deviation of one from a noise + * map. + */ +double pc_map_norm(const int x, const int y, const int idx) { + const int i = (((y % PC_MAP_Y) * PC_MAP_X) + + (x % PC_MAP_X)) * PC_MAP_I + + (idx % PC_MAP_I); + return pc_normal[i]; +} diff --git a/simgear/math/sg_random.h b/simgear/math/sg_random.hxx similarity index 67% rename from simgear/math/sg_random.h rename to simgear/math/sg_random.hxx index aa084a63..221e8e40 100644 --- a/simgear/math/sg_random.h +++ b/simgear/math/sg_random.hxx @@ -91,6 +91,56 @@ void sg_srandom(unsigned int seed ); */ double sg_random(); +/** + * Initialize current state with a given seed. + */ +void pc_init(unsigned int seed); + +/** + * Initialize current state with a seed that only + * changes every 10 minutes. Used to synchronize + * multi-process deployments. + */ +void pc_init_time_10(); + +/** + * Return a 32-bit random number based on the current state. + */ +unsigned int pc_rand32(); + +/** + * Return a double precision floating point random number + * between [0.0, 1.0) based on the current state. + */ +double pc_rand(); + +/** + * Return a double precision floating point random number + * between [-10.0, 10.0] with a normal distribution of + * average zero and standard deviation of one based on the + * current state. + */ +double pc_norm(); + +/** + * Return a 32-bit random number from a noise map. + */ +unsigned int pc_map_rand32(const int x, const int y, const int idx); + +/** + * Return a double precision floating point random number + * between [0.0, 1.0) from a noise map. + */ +double pc_map_rand(const int x, const int y, const int idx); + +/** + * Return a double precision floating point random number + * between [-10.0, 10.0] with a normal distribution of + * average zero and standard deviation of one from a noise + * map. + */ +double pc_map_norm(const int x, const int y, const int idx); + #ifdef __cplusplus } diff --git a/simgear/scene/material/matmodel.cxx b/simgear/scene/material/matmodel.cxx index 2767322a..1675e029 100644 --- a/simgear/scene/material/matmodel.cxx +++ b/simgear/scene/material/matmodel.cxx @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include #include diff --git a/simgear/scene/material/matmodel.hxx b/simgear/scene/material/matmodel.hxx index 33fa4134..066dc10c 100644 --- a/simgear/scene/material/matmodel.hxx +++ b/simgear/scene/material/matmodel.hxx @@ -41,7 +41,7 @@ #include #include #include -#include +#include class SGMatModelGroup; diff --git a/simgear/scene/model/persparam.hxx b/simgear/scene/model/persparam.hxx index e8b571fc..25c64867 100644 --- a/simgear/scene/model/persparam.hxx +++ b/simgear/scene/model/persparam.hxx @@ -5,7 +5,7 @@ #ifndef _SG_PERSPARAM_HXX #define _SG_PERSPARAM_HXX 1 -#include +#include template diff --git a/simgear/scene/sky/CloudShaderGeometry.hxx b/simgear/scene/sky/CloudShaderGeometry.hxx index 7ac06c61..88c0cf24 100644 --- a/simgear/scene/sky/CloudShaderGeometry.hxx +++ b/simgear/scene/sky/CloudShaderGeometry.hxx @@ -34,7 +34,7 @@ #include #include -#include +#include namespace simgear diff --git a/simgear/scene/sky/cloud.cxx b/simgear/scene/sky/cloud.cxx index dbbcb6b7..cda13d8a 100644 --- a/simgear/scene/sky/cloud.cxx +++ b/simgear/scene/sky/cloud.cxx @@ -42,7 +42,7 @@ #include #include -#include +#include #include #include #include diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index 0b313920..e5d06eed 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -32,7 +32,7 @@ #include -#include +#include #include #include diff --git a/simgear/scene/sky/newcloud.hxx b/simgear/scene/sky/newcloud.hxx index 94fd6a99..ce1b719a 100644 --- a/simgear/scene/sky/newcloud.hxx +++ b/simgear/scene/sky/newcloud.hxx @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include diff --git a/simgear/scene/sky/sky.hxx b/simgear/scene/sky/sky.hxx index 9799280c..6e6e228b 100644 --- a/simgear/scene/sky/sky.hxx +++ b/simgear/scene/sky/sky.hxx @@ -30,7 +30,7 @@ #include -#include +#include #include #include diff --git a/simgear/scene/tgdb/ReaderWriterSTG.cxx b/simgear/scene/tgdb/ReaderWriterSTG.cxx index 01b73b64..f8162595 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.cxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.cxx @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include @@ -506,9 +506,7 @@ struct ReaderWriterSTG::_ModelBin { // Bucket provides a consistent seed // so we have consistent set of pseudo-random numbers for each STG file - mt seed; - int bucket = atoi(absoluteFileName.file_base().c_str()); - mt_init(&seed, bucket); + pc_init(std::stoi(absoluteFileName.file_base())); bool vpb_active = SGSceneFeatures::instance()->getVPBActive(); @@ -572,7 +570,7 @@ struct ReaderWriterSTG::_ModelBin { // Determine an appropriate range for the object, which has some randomness double range = _object_range_rough; - double lrand = mt_rand(&seed); + double lrand = pc_rand(); if (lrand < 0.1) range = range * 2.0; else if (lrand < 0.4) range = range * 1.5; @@ -660,7 +658,7 @@ struct ReaderWriterSTG::_ModelBin { if (token == BUILDING_DETAILED || token == ROAD_DETAILED || token == RAILWAY_DETAILED ) { // Apply a lower LOD range if this is a detailed mesh. range = _object_range_detailed; - double lrand = mt_rand(&seed); + double lrand = pc_rand(); if (lrand < 0.1) range = range * 2.0; else if (lrand < 0.4) range = range * 1.5; } diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx index 5db94088..4099d878 100644 --- a/simgear/scene/tgdb/SGBuildingBin.cxx +++ b/simgear/scene/tgdb/SGBuildingBin.cxx @@ -45,7 +45,7 @@ #include #include #include -#include +#include #include #include #include @@ -656,69 +656,68 @@ typedef QuadTreeBuilderget_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_small_max_width() - _material->get_building_small_min_width()); - depth = _material->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_small_max_depth() - _material->get_building_small_min_depth()); - floors = SGMisc::round(_material->get_building_small_min_floors() + mt_rand(&seed) * (_material->get_building_small_max_floors() - _material->get_building_small_min_floors())); - height = floors * (2.8 + mt_rand(&seed)); + width = _material->get_building_small_min_width() + pc_rand() * pc_rand() * (_material->get_building_small_max_width() - _material->get_building_small_min_width()); + depth = _material->get_building_small_min_depth() + pc_rand() * pc_rand() * (_material->get_building_small_max_depth() - _material->get_building_small_min_depth()); + floors = SGMisc::round(_material->get_building_small_min_floors() + pc_rand() * (_material->get_building_small_max_floors() - _material->get_building_small_min_floors())); + height = floors * (2.8 + pc_rand()); // Small buildings are never deeper than they are wide. if (depth > width) { depth = width; } - pitch_height = (mt_rand(&seed) < _material->get_building_small_pitch()) ? 3.0 : 0.0; + pitch_height = (pc_rand() < _material->get_building_small_pitch()) ? 3.0 : 0.0; if (pitch_height == 0.0) { roof_shape = 0; roof_orientation = 0; } else { - roof_shape = (int) (mt_rand(&seed) * 10.0); - roof_orientation = (int) std::round((float) mt_rand(&seed)); + roof_shape = (int) (pc_rand() * 10.0); + roof_orientation = (int) std::round((float) pc_rand()); } } else if (buildingtype == SGBuildingBin::MEDIUM) { // MEDIUM BUILDING - width = _material->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_medium_max_width() - _material->get_building_medium_min_width()); - depth = _material->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_medium_max_depth() - _material->get_building_medium_min_depth()); - floors = SGMisc::round(_material->get_building_medium_min_floors() + mt_rand(&seed) * (_material->get_building_medium_max_floors() - _material->get_building_medium_min_floors())); - height = floors * (2.8 + mt_rand(&seed)); + width = _material->get_building_medium_min_width() + pc_rand() * pc_rand() * (_material->get_building_medium_max_width() - _material->get_building_medium_min_width()); + depth = _material->get_building_medium_min_depth() + pc_rand() * pc_rand() * (_material->get_building_medium_max_depth() - _material->get_building_medium_min_depth()); + floors = SGMisc::round(_material->get_building_medium_min_floors() + pc_rand() * (_material->get_building_medium_max_floors() - _material->get_building_medium_min_floors())); + height = floors * (2.8 + pc_rand()); while ((height > width) && (floors > _material->get_building_medium_min_floors())) { // Ensure that medium buildings aren't taller than they are wide floors--; - height = floors * (2.8 + mt_rand(&seed)); + height = floors * (2.8 + pc_rand()); } - pitch_height = (mt_rand(&seed) < _material->get_building_medium_pitch()) ? 3.0 : 0.0; + pitch_height = (pc_rand() < _material->get_building_medium_pitch()) ? 3.0 : 0.0; if (pitch_height == 0.0) { roof_shape = 0; roof_orientation = 0; } else { - roof_shape = (int) (mt_rand(&seed) * 10.0); - roof_orientation = (int) std::round((float) mt_rand(&seed)); + roof_shape = (int) (pc_rand() * 10.0); + roof_orientation = (int) std::round((float) pc_rand()); } } else { // LARGE BUILDING - width = _material->get_building_large_min_width() + mt_rand(&seed) * (_material->get_building_large_max_width() - _material->get_building_large_min_width()); - depth = _material->get_building_large_min_depth() + mt_rand(&seed) * (_material->get_building_large_max_depth() - _material->get_building_large_min_depth()); - floors = SGMisc::round(_material->get_building_large_min_floors() + mt_rand(&seed) * (_material->get_building_large_max_floors() - _material->get_building_large_min_floors())); - height = floors * (2.8 + mt_rand(&seed)); - pitch_height = (mt_rand(&seed) < _material->get_building_large_pitch()) ? 3.0 : 0.0; + width = _material->get_building_large_min_width() + pc_rand() * (_material->get_building_large_max_width() - _material->get_building_large_min_width()); + depth = _material->get_building_large_min_depth() + pc_rand() * (_material->get_building_large_max_depth() - _material->get_building_large_min_depth()); + floors = SGMisc::round(_material->get_building_large_min_floors() + pc_rand() * (_material->get_building_large_max_floors() - _material->get_building_large_min_floors())); + height = floors * (2.8 + pc_rand()); + pitch_height = (pc_rand() < _material->get_building_large_pitch()) ? 3.0 : 0.0; if (pitch_height == 0.0) { roof_shape = 0; roof_orientation = 0; } else { - roof_shape = (int) (mt_rand(&seed) * 10.0); - roof_orientation = (int) std::round((float) mt_rand(&seed)); + roof_shape = (int) (pc_rand() * 10.0); + roof_orientation = (int) std::round((float) pc_rand()); } } - insert(p, r, buildingtype, width, depth, height, pitch_height, floors, roof_shape, roof_orientation, (int) (mt_rand(&seed) * 1000.0), (int) (mt_rand(&seed) * 1000.0)); + insert(p, r, buildingtype, width, depth, height, pitch_height, floors, roof_shape, roof_orientation, (int) (pc_rand() * 1000.0), (int) (pc_rand() * 1000.0)); } int SGBuildingBin::getNumBuildings() { diff --git a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx index 663933c1..45ce4441 100644 --- a/simgear/scene/tgdb/SGTexturedTriangleBin.hxx +++ b/simgear/scene/tgdb/SGTexturedTriangleBin.hxx @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include "SGTriangleBin.hxx" diff --git a/simgear/scene/tgdb/TreeBin.cxx b/simgear/scene/tgdb/TreeBin.cxx index 0827923d..26d124bf 100644 --- a/simgear/scene/tgdb/TreeBin.cxx +++ b/simgear/scene/tgdb/TreeBin.cxx @@ -40,7 +40,7 @@ #include #include -#include +#include #include #include #include @@ -119,8 +119,7 @@ TreesBoundingBoxCallback::computeBound(const Drawable& drawable) const Geometry* makeSharedTreeGeometry(int numQuads) { // generate a repeatable random seed - mt seed; - mt_init(&seed, unsigned(123)); + pc_init(123); // set up the coords osg::Vec3Array* v = new osg::Vec3Array; osg::Vec2Array* t = new osg::Vec2Array; @@ -128,7 +127,7 @@ Geometry* makeSharedTreeGeometry(int numQuads) t->reserve(numQuads * 4); for (int i = 0; i < numQuads; ++i) { // Apply a random scaling factor and texture index. - float h = (mt_rand(&seed) + mt_rand(&seed)) / 2.0f + 0.5f; + float h = (pc_rand() + pc_rand()) / 2.0f + 0.5f; float cw = h * .5; v->push_back(Vec3(0.0f, -cw, 0.0f)); v->push_back(Vec3(0.0f, cw, 0.0f)); @@ -138,7 +137,7 @@ Geometry* makeSharedTreeGeometry(int numQuads) // space, as the texture has a number of different trees on // it. Here we assign random coordinates and let the shader // choose the variety. - float variety = mt_rand(&seed); + float variety = pc_rand(); t->push_back(Vec2(variety, 0.0f)); t->push_back(Vec2(variety + 1.0f, 0.0f)); t->push_back(Vec2(variety + 1.0f, 0.234f)); diff --git a/simgear/scene/tgdb/VPBTechnique.cxx b/simgear/scene/tgdb/VPBTechnique.cxx index 61d559de..a8704a39 100644 --- a/simgear/scene/tgdb/VPBTechnique.cxx +++ b/simgear/scene/tgdb/VPBTechnique.cxx @@ -39,7 +39,7 @@ #include #include -#include +#include #include #include #include @@ -1328,33 +1328,10 @@ double VPBTechnique::det2(const osg::Vec2d a, const osg::Vec2d b) return a.x() * b.y() - b.x() * a.y(); } -osg::Vec2d* VPBTechnique::_randomOffsets = 0; -int idx =0; - -osg::Vec2d VPBTechnique::getRandomOffset() -{ - const unsigned initial_seed = 42; - const int N = 100; - - if (!_randomOffsets) - { - _randomOffsets = new osg::Vec2d[N]; - mt seed; - mt_init(&seed, initial_seed); - - for (int i = 0; i < N; i++) - { - _randomOffsets[i].set(mt_rand(&seed), mt_rand(&seed)); - } - } - - idx = (idx + 1) % N; - return _randomOffsets[idx]; -} - - void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) { + pc_init(2718281); + bool use_random_vegetation = false; float vegetation_density = 1.0; int vegetation_lod_level = 6; @@ -1374,7 +1351,6 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) // Determine tree spacing, assuming base density of 1 tree per 100m^2, though spacing // is linear here, as is the /sim/rendering/vegetation-density property. - float spacing = 10.0 / vegetation_density; SGMaterialLibPtr matlib = _options->getMaterialLib(); SGMaterial* mat = 0; @@ -1384,8 +1360,16 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) const SGGeod loc = computeCenterGeod(buffer, masterLocator); SGMaterialCache* matcache = matlib->generateMatCache(loc, _options); + const osg::Vec3d world = buffer._transform->getMatrix().getTrans(); + const SGGeoc cloc = SGGeoc::fromCart(toSG(world)); + if (!matcache) return; + const osg::Matrixd R_vert = osg::Matrixd::rotate( + M_PI / 2.0 - loc.getLatitudeRad(), osg::Vec3d(0.0, 1.0, 0.0), + loc.getLongitudeRad(), osg::Vec3d(0.0, 0.0, 1.0), + 0.0, osg::Vec3d(1.0, 0.0, 0.0)); + const osg::Array* vertices = buffer._landGeometry->getVertexArray(); const osg::Array* texture_coords = buffer._landGeometry->getTexCoordArray(0); osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(0); @@ -1405,6 +1389,28 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) const osg::DrawElements* drawElements = primSet->getDrawElements(); const unsigned int triangle_count = drawElements->getNumPrimitives(); + const double lon = loc.getLongitudeRad(); + const double lat = loc.getLatitudeRad(); + const double clon = cloc.getLongitudeRad(); + const double clat = cloc.getLatitudeRad(); + const double r_E_lat = /* 6356752.3 */ 6.375993e+06; + const double r_E_lon = /* 6378137.0 */ 6.389377e+06; + const double C = r_E_lon * cos(lat); + const double one_over_C = (fabs(C) > 1.0e-4) ? (1.0 / C) : 0.0; + const double one_over_r_E = 1.0 / r_E_lat; + const double delta_lat = sqrt(100.0 / vegetation_density) / r_E_lat; + const double delta_lon = sqrt(100.0 / vegetation_density) / (r_E_lon * cos(loc.getLatitudeRad())); + + const osg::Matrix rotation_vertices_c = osg::Matrix::rotate( + M_PI / 2 - clat, osg::Vec3d(0.0, 1.0, 0.0), + clon, osg::Vec3d(0.0, 0.0, 1.0), + 0.0, osg::Vec3d(1.0, 0.0, 0.0)); + + const osg::Matrix rotation_vertices_g = osg::Matrix::rotate( + M_PI / 2 - lat, osg::Vec3d(0.0, 1.0, 0.0), + lon, osg::Vec3d(0.0, 0.0, 1.0), + 0.0, osg::Vec3d(1.0, 0.0, 0.0)); + for (unsigned int i = 0; i < triangle_count; i++) { const int i0 = drawElements->index(3 * i); @@ -1416,8 +1422,30 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) const osg::Vec3 v2 = vertexPtr[i2]; const osg::Vec3d v_0 = v0; - osg::Vec3d v_x = v1 - v0; - osg::Vec3d v_y = v2 - v0; + const osg::Vec3d v_x = v1 - v0; + const osg::Vec3d v_y = v2 - v0; + + osg::Vec3 n = v_x ^ v_y; + n.normalize(); + + const osg::Vec3d v_0_g = R_vert * v0; + const osg::Vec3d v_1_g = R_vert * v1; + const osg::Vec3d v_2_g = R_vert * v2; + + const osg::Vec2d ll_0 = osg::Vec2d(v_0_g.y() * one_over_C + lon, -v_0_g.x() * one_over_r_E + lat); + const osg::Vec2d ll_1 = osg::Vec2d(v_1_g.y() * one_over_C + lon, -v_1_g.x() * one_over_r_E + lat); + const osg::Vec2d ll_2 = osg::Vec2d(v_2_g.y() * one_over_C + lon, -v_2_g.x() * one_over_r_E + lat); + + const osg::Vec2d ll_O = ll_0; + const osg::Vec2d ll_x = osg::Vec2d((v_1_g.y() - v_0_g.y()) * one_over_C, -(v_1_g.x() - v_0_g.x()) * one_over_r_E); + const osg::Vec2d ll_y = osg::Vec2d((v_2_g.y() - v_0_g.y()) * one_over_C, -(v_2_g.x() - v_0_g.x()) * one_over_r_E); + + const int off_x = ll_O.x() / delta_lon; + const int off_y = ll_O.y() / delta_lat; + const int min_lon = min(min(ll_0.x(), ll_1.x()), ll_2.x()) / delta_lon; + const int max_lon = max(max(ll_0.x(), ll_1.x()), ll_2.x()) / delta_lon; + const int min_lat = min(min(ll_0.y(), ll_1.y()), ll_2.y()) / delta_lat; + const int max_lat = max(max(ll_0.y(), ll_1.y()), ll_2.y()) / delta_lat; const osg::Vec2 t0 = texPtr[i0]; const osg::Vec2 t1 = texPtr[i1]; @@ -1427,30 +1455,33 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) const osg::Vec2d t_x = t1 - t0; const osg::Vec2d t_y = t2 - t0; - osg::Vec3 n = v_x ^ v_y; - n.normalize(); + const double D = det2(ll_x, ll_y); - osg::Vec3 up = buffer._transform->getMatrix().getTrans(); - up.normalize(); + for (int lat_int = min_lat - 1; lat_int <= max_lat + 1; lat_int++) + { + const double lat = (lat_int - off_y) * delta_lat; - double step_x = spacing / v_x.length(); - double step_y = spacing / v_y.length(); + for (int lon_int = min_lon - 1; lon_int <= max_lon + 1; lon_int++) + { + const double lon = (lon_int - off_x) * delta_lon; + osg::Vec2d p(lon, lat); + double x = det2(ll_x, p) / D; + double y = det2(p, ll_y) / D; - for (float x = 0; x < 1.0; x += step_x) { - for (float y = 0; y < 1.0; y += step_y) { - - // If outside the triangle, ignore - if ((x + y) > 1.0) continue; + if ((x < 0.0) || (y < 0.0) || (x + y > 1.0)) + { + continue; + } osg::Vec2 t = osg::Vec2(t_0 + t_x * x + t_y * y); - unsigned int tx = (unsigned int) (image->s() * t.x()) % image->s(); - unsigned int ty = (unsigned int) (image->t() * t.y()) % image->t(); if (!image) { SG_LOG(SG_TERRAIN, SG_ALERT, "Image disappeared under my feet."); continue; } + unsigned int tx = (unsigned int) (image->s() * t.x()) % image->s(); + unsigned int ty = (unsigned int) (image->t() * t.y()) % image->t(); const osg::Vec4 tc = image->getColor(tx, ty); const int land_class = int(round(tc.x() * 255.0)); mat = matcache->find(land_class); @@ -1460,32 +1491,33 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) continue; } - // Check vegetation density against the material, noting we assumed 100m^2 above if (mat->get_wood_coverage() <= 0) continue; - float wood_coverage = 100.0 / mat->get_wood_coverage(); - if (getRandomOffset().x() > wood_coverage) continue; + float wood_coverage = 2000.0 / mat->get_wood_coverage(); - osg::Vec3 p = v_0 + v_x*x + v_y*y; + if (pc_map_rand(lon_int, lat_int, 2) > wood_coverage) continue; - - if (! mat->get_is_plantation()) { - // Add some randomness - osg::Vec2 offset = getRandomOffset(); - float new_x = x + step_x * (offset.x() - 0.5); - float new_y = y + step_y * (offset.y() - 0.5); - - // Another check to ensure we're not outside the triangle - if ((new_x + new_y) > 1.0) continue; - - - // Update both position and texture coords to account for the new position. - p = v_0 + v_x*new_x + v_y*new_y; - t = osg::Vec2(t_0 + t_x * new_x + t_y * new_y); + if (mat->get_is_plantation()) { + p = osg::Vec2d(lon + 0.1 * delta_lon * pc_map_norm(lon_int, lat_int, 0), + lat + 0.1 * delta_lat * pc_map_norm(lon_int, lat_int, 1)); + } else { + p = osg::Vec2d(lon + delta_lon * pc_map_rand(lon_int, lat_int, 0), + lat + delta_lat * pc_map_rand(lon_int, lat_int, 1)); } + x = det2(ll_x, p) / D; + y = det2(p, ll_y) / D; + + if ((x < 0.0) || (y < 0.0) || (x + y > 1.0)) + { + continue; + } + + t = osg::Vec2(t_0 + t_x * x + t_y * y); + // Check against any object mask osg::Texture2D* object_mask = mat->get_one_object_mask(0); + if (object_mask != NULL) { osg::Image* img = object_mask->getImage(); if (!img || ! img->valid()) continue; @@ -1504,30 +1536,13 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) unsigned int y = (unsigned int) (img->t() * t.y() * y_scale) % img->t(); // green (for trees) channel - if (getRandomOffset().y() > img->getColor(x, y).g()) { + + if (pc_map_rand(lon_int, lat_int, 3) > img->getColor(x, y).g()) { continue; } + } - // Ensure the slope isn't too steep by checking the - // cos of the angle between the slope normal and the - // vertical, conveniently just the dot product of the up and normal - float alpha = n * up; - float cos_zero_density_angle = mat->get_cos_tree_zero_density_slope_angle(); - float cos_max_density_angle = mat->get_cos_tree_max_density_slope_angle(); - - if (alpha < cos_zero_density_angle) { - continue; // Too steep for any vegetation - } - - if (alpha < cos_max_density_angle) { - // Reduce density on steep slopes - float slope_density = (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle); - if (getRandomOffset().y() > slope_density) continue; - } - - // It's a hard life being a possible tree! But we now have a valid tree position. - // So put it in the correct bin. TreeBin* bin = NULL; bool found = false; @@ -1559,9 +1574,12 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) randomForest.push_back(bin); } - bin->insert(SGVec3f(p.x(), p.y(), p.z()), SGVec3f(n.x(), n.y(), n.z())); + const osg::Vec3 vp = v_x * x + v_y * y + v_0; + bin->insert(SGVec3f(vp.x(), vp.y(), vp.z()), SGVec3f(n.x(), n.y(), n.z())); } + } + } if (randomForest.size() > 0) { @@ -1576,9 +1594,10 @@ void VPBTechnique::applyTrees(BufferData& buffer, Locator* masterLocator) loc.getLongitudeRad(), osg::Vec3d(0.0, 0.0, 1.0), 0.0, osg::Vec3d(1.0, 0.0, 0.0)); - osg::Group* trees = createForest(randomForest, R_vert,_options, 1); + osg::Group* trees = createForest(randomForest, R_vert, _options, 1); buffer._transform->addChild(trees); } + } void VPBTechnique::applyLineFeatures(BufferData& buffer, Locator* masterLocator) diff --git a/simgear/scene/tgdb/VPBTechnique.hxx b/simgear/scene/tgdb/VPBTechnique.hxx index 7291c050..2f4f30c4 100644 --- a/simgear/scene/tgdb/VPBTechnique.hxx +++ b/simgear/scene/tgdb/VPBTechnique.hxx @@ -133,8 +133,6 @@ class VPBTechnique : public TerrainTechnique virtual double det2(const osg::Vec2d a, const osg::Vec2d b); - static osg::Vec2d getRandomOffset(); - virtual void applyTrees(BufferData& buffer, Locator* masterLocator); virtual void applyLineFeatures(BufferData& buffer, Locator* masterLocator); @@ -187,7 +185,6 @@ class VPBTechnique : public TerrainTechnique inline static osg::ref_ptr _constraintGroup = new osg::Group();; inline static std::mutex _constraint_mutex; // protects the _constraintGroup; - static osg::Vec2d* _randomOffsets; typedef std::pair BucketLineFeatureBinList; typedef std::pair BucketAreaFeatureBinList; diff --git a/simgear/scene/tgdb/pt_lights.cxx b/simgear/scene/tgdb/pt_lights.cxx index 16279d2f..b708bd10 100644 --- a/simgear/scene/tgdb/pt_lights.cxx +++ b/simgear/scene/tgdb/pt_lights.cxx @@ -53,7 +53,7 @@ #include #include -#include +#include #include #include #include diff --git a/simgear/scene/tgdb/userdata.cxx b/simgear/scene/tgdb/userdata.cxx index f71cb59c..2a452ae0 100644 --- a/simgear/scene/tgdb/userdata.cxx +++ b/simgear/scene/tgdb/userdata.cxx @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include diff --git a/simgear/scene/tsync/terrasync.cxx b/simgear/scene/tsync/terrasync.cxx index 38a8d001..7638ff23 100644 --- a/simgear/scene/tsync/terrasync.cxx +++ b/simgear/scene/tsync/terrasync.cxx @@ -65,7 +65,7 @@ #include #include #include -#include +#include using namespace simgear; using namespace std; diff --git a/simgear/sound/xmlsound.cxx b/simgear/sound/xmlsound.cxx index b358b2a6..fec99981 100644 --- a/simgear/sound/xmlsound.cxx +++ b/simgear/sound/xmlsound.cxx @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include "sample_group.hxx"