Precomputed random numbers API

Use precomputed tables to give stable random numbers : replaces previous
recompuation when we exhaust the RNG pool.

By Marc Eberhard
This commit is contained in:
James Turner 2021-05-25 12:53:36 +01:00
parent e7fbf83155
commit f74ba148ab
24 changed files with 326 additions and 141 deletions

View File

@ -46,7 +46,7 @@
#include <sstream>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/structure/exception.hxx>
#include "metar.hxx"

View File

@ -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}")

View File

@ -23,7 +23,7 @@
#include <iostream>
#include "SGGeometry.hxx"
#include "sg_random.h"
#include "sg_random.hxx"
template<typename T>
SGVec3<T> rndVec3(void)

View File

@ -26,7 +26,7 @@
#include "SGMath.hxx"
#include "SGRect.hxx"
#include "sg_random.h"
#include "sg_random.hxx"
int lineno = 0;

View File

@ -39,11 +39,12 @@
# include <simgear_config.h>
#endif
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h> // for random(), srandom()
#include <time.h> // 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];
}

View File

@ -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
}

View File

@ -37,7 +37,7 @@
#include <osg/Transform>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/scene/model/modellib.hxx>

View File

@ -41,7 +41,7 @@
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/props/props.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
class SGMatModelGroup;

View File

@ -5,7 +5,7 @@
#ifndef _SG_PERSPARAM_HXX
#define _SG_PERSPARAM_HXX 1
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
template <class T>

View File

@ -34,7 +34,7 @@
#include <osg/buffered_value>
#include <simgear/math/SGMath.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
namespace simgear

View File

@ -42,7 +42,7 @@
#include <osg/TexMat>
#include <osg/Fog>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/model/model.hxx>

View File

@ -32,7 +32,7 @@
#include <simgear/compiler.h>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>

View File

@ -28,7 +28,7 @@
#include <vector>
#include <osg/Fog>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>

View File

@ -30,7 +30,7 @@
#include <simgear/compiler.h>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>

View File

@ -42,7 +42,7 @@
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/SGGeometry.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/scene/util/OptionsReadFileCallback.hxx>
@ -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;
}

View File

@ -45,7 +45,7 @@
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/math/SGLimits.hxx>
#include <simgear/math/SGMisc.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/props/props.hxx>
@ -656,69 +656,68 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
int roof_orientation;
// Generate a random seed for the building generation.
mt seed;
mt_init(&seed, unsigned(p.x() + p.y() + p.z()));
pc_init(unsigned(p.x() + p.y() + p.z()));
if (buildingtype == SGBuildingBin::SMALL) {
// Small building
// Maximum number of floors is 3, and maximum width/depth is 192m.
width = _material->get_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<double>::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<double>::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<double>::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<double>::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<double>::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<double>::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() {

View File

@ -31,7 +31,7 @@
#include <osg/ref_ptr>
#include <stdio.h>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include "SGTriangleBin.hxx"

View File

@ -40,7 +40,7 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
@ -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));

View File

@ -39,7 +39,7 @@
#include <osg/Timer>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
@ -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)

View File

@ -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<osg::Group> _constraintGroup = new osg::Group();;
inline static std::mutex _constraint_mutex; // protects the _constraintGroup;
static osg::Vec2d* _randomOffsets;
typedef std::pair<SGBucket, LineFeatureBinList> BucketLineFeatureBinList;
typedef std::pair<SGBucket, AreaFeatureBinList> BucketAreaFeatureBinList;

View File

@ -53,7 +53,7 @@
#include <OpenThreads/Mutex>
#include <OpenThreads/ScopedLock>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/SGEnlargeBoundingBox.hxx>

View File

@ -29,7 +29,7 @@
#include <simgear/sg_inlines.h>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/material/matmodel.hxx>
#include <simgear/scene/model/ModelRegistry.hxx>

View File

@ -65,7 +65,7 @@
#include <simgear/io/HTTPRepository.hxx>
#include <simgear/io/DNSClient.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
using namespace simgear;
using namespace std;

View File

@ -37,7 +37,7 @@
#include <simgear/props/props.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/math/sg_random.hxx>
#include <simgear/misc/sg_path.hxx>
#include "sample_group.hxx"