From ae0c9fc41e62eb41e02b339a38031dd1087c59c7 Mon Sep 17 00:00:00 2001 From: rtri Date: Tue, 30 May 2017 15:52:56 +0200 Subject: [PATCH] fix #5588 --- rts/Rendering/Env/GrassDrawer.cpp | 82 +++++++++++++++++-------------- rts/System/GlobalRNG.h | 77 +++++++++++++++++++++-------- 2 files changed, 102 insertions(+), 57 deletions(-) diff --git a/rts/Rendering/Env/GrassDrawer.cpp b/rts/Rendering/Env/GrassDrawer.cpp index 47cff593f5c..c27a498e833 100644 --- a/rts/Rendering/Env/GrassDrawer.cpp +++ b/rts/Rendering/Env/GrassDrawer.cpp @@ -34,6 +34,15 @@ CONFIG(int, GrassDetail).defaultValue(7).headlessValue(0).minimumValue(0).description("Sets how detailed the engine rendered grass will be on any given map."); +// uses a 'synced' RNG s.t. grass turfs generated from the same +// seed also share identical sequences, otherwise an unpleasant +// shimmering effect occurs when zooming +#if 0 +typedef CGlobalRNG GrassRNG; +#else +typedef CGlobalRNG GrassRNG; +#endif + static const float turfSize = 20.0f; // single turf size static const float partTurfSize = turfSize * 1.0f; // single turf size static const int grassSquareSize = 4; // mapsquares per grass square @@ -43,7 +52,7 @@ static const int blockMapSize = grassSquareSize * grassBlockSize; static const int gSSsq = SQUARE_SIZE * grassSquareSize; static const int bMSsq = SQUARE_SIZE * blockMapSize; -static CGlobalUnsyncedRNG rng; +static GrassRNG grng; @@ -112,13 +121,13 @@ class CGrassBlockDrawer: public CReadMap::IQuadDrawer // blocks close to the camera for (int y2 = y * grassBlockSize; y2 < (y + 1) * grassBlockSize; ++y2) { for (int x2 = x * grassBlockSize; x2 < (x + 1) * grassBlockSize; ++x2) { - if (!gd->grassMap[y2 * mapDims.mapx / grassSquareSize + x2]) { + if (!gd->grassMap[y2 * mapDims.mapx / grassSquareSize + x2]) continue; - } - rng.Seed(y2 * mapDims.mapx / grassSquareSize + x2); + grng.Seed(y2 * mapDims.mapx / grassSquareSize + x2); + const float dist = GetCamDistOfGrassBlock(x2, y2, false); - const float rdist = 1.0f + rng.NextFloat() * 0.5f; + const float rdist = 1.0f + grng.NextFloat() * 0.5f; //TODO instead of adding grass turfs depending on their distance to the camera, // there should be a fixed sized pool for mesh & billboard turfs @@ -178,7 +187,7 @@ CGrassDrawer::CGrassDrawer() , grassMap(nullptr) { blockDrawer.ResetState(); - rng.Seed(15); + grng.Seed(15); const int detail = configHandler->GetInt("GrassDetail"); @@ -378,17 +387,12 @@ void CGrassDrawer::EnableShader(const GrassShaderProgram type) { ////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////// -struct STurfParams { - float x, y, rotation; -}; - - -static STurfParams GetTurfParams(CGlobalUnsyncedRNG& rng, const int x, const int y) +static float3 GetTurfParams(GrassRNG& rng, const int x, const int y) { - STurfParams result; + float3 result; result.x = (x + rng.NextFloat()) * gSSsq; result.y = (y + rng.NextFloat()) * gSSsq; - result.rotation = rng.NextFloat() * 360.f; + result.z = rng.NextFloat() * 360.0f; // rotation return result; } @@ -397,20 +401,22 @@ static STurfParams GetTurfParams(CGlobalUnsyncedRNG& rng, const int x, const int void CGrassDrawer::DrawNear(const std::vector& inviewGrass) { for (const InviewNearGrass& g: inviewGrass) { - rng.Seed(g.y * mapDims.mapx / grassSquareSize + g.x); + grng.Seed(g.y * mapDims.mapx / grassSquareSize + g.x); + // const float distSq = GetCamDistOfGrassBlock(g.x, g.y, true); - const float rdist = 1.0f + rng.NextFloat() * 0.5f; - const float alpha = linearstep(maxDetailedDist, maxDetailedDist + 128.f * rdist, g.dist); + const float rdist = 1.0f + grng.NextFloat() * 0.5f; + const float alpha = linearstep(maxDetailedDist, maxDetailedDist + 128.0f * rdist, g.dist); for (int a = 0; a < numTurfs; a++) { - const STurfParams& p = GetTurfParams(rng, g.x, g.y); + const float3& p = GetTurfParams(grng, g.x, g.y); float3 pos(p.x, CGround::GetHeightReal(p.x, p.y, false), p.y); - pos.y -= CGround::GetSlope(p.x, p.y, false) * 30.0f; - pos.y -= 2.0f * mapInfo->grass.bladeHeight * alpha; + + pos.y -= CGround::GetSlope(p.x, p.y, false) * 30.0f; + pos.y -= 2.0f * mapInfo->grass.bladeHeight * alpha; glPushMatrix(); glTranslatef3(pos); - glRotatef(p.rotation, 0.0f, 1.0f, 0.0f); + glRotatef(p.z, 0.0f, 1.0f, 0.0f); glCallList(grassDL); glPopMatrix(); } @@ -420,16 +426,17 @@ void CGrassDrawer::DrawNear(const std::vector& inviewGrass) void CGrassDrawer::DrawBillboard(const int x, const int y, const float dist, VA_TYPE_TN* va_tn) { - CGlobalUnsyncedRNG rng; // need our own, cause this function may run threaded - rng.Seed(y * mapDims.mapx / grassSquareSize + x); - const float rdist = 1.0f + rng.NextFloat() * 0.5f; - float alpha = 1.0f - linearstep(maxGrassDist, maxGrassDist + 127.f, dist + 128.f); - alpha = std::min(alpha, linearstep(maxDetailedDist, maxDetailedDist + 128.f * rdist, dist)); + GrassRNG trng; // need our own, cause this function may run threaded + trng.Seed(y * mapDims.mapx / grassSquareSize + x); + + const float rDist = 1.0f + trng.NextFloat() * 0.5f; + const float gStep = linearstep(maxGrassDist, maxGrassDist + 127.0f, dist + 128.0f); + const float dStep = linearstep(maxDetailedDist, maxDetailedDist + 128.0f * rDist, dist); + const float alpha = std::min(1.0f - gStep, dStep); for (int a = 0; a < numTurfs; a++) { - const STurfParams p = GetTurfParams(rng, x, y); - float3 pos(p.x, CGround::GetHeightReal(p.x, p.y, false), p.y); - pos.y -= CGround::GetSlope(p.x, p.y, false) * 30.0f; + const float3& p = GetTurfParams(trng, x, y); + const float3 pos(p.x, CGround::GetHeightReal(p.x, p.y, false) - CGround::GetSlope(p.x, p.y, false) * 30.0f, p.y); va_tn[a * 4 + 0] = { pos, 0.0f, 1.0f, float3(-partTurfSize, -partTurfSize, alpha) }; va_tn[a * 4 + 1] = { pos, 1.0f / 16.0f, 1.0f, float3( partTurfSize, -partTurfSize, alpha) }; @@ -799,22 +806,25 @@ void CGrassDrawer::CreateGrassDispList(int listNum) { CVertexArray* va = GetVertexArray(); va->Initialize(); - rng.Seed(15); + grng.Seed(15); for (int a = 0; a < strawPerTurf; ++a) { // draw a single blade - const float lngRnd = rng.NextFloat(); + const float lngRnd = grng.NextFloat(); const float length = mapInfo->grass.bladeHeight * (1.0f + lngRnd); - const float maxAng = mapInfo->grass.bladeAngle * std::max(rng.NextFloat(), 1.f - smoothstep(0.f,1.f,lngRnd)); + const float maxAng = mapInfo->grass.bladeAngle * std::max(grng.NextFloat(), 1.0f - smoothstep(0.0f, 1.0f, lngRnd)); - float3 sideVect(rng.NextFloat() - 0.5f, 0.0f, rng.NextFloat() - 0.5f); + float3 sideVect; + sideVect.x = grng.NextFloat() - 0.5f; + sideVect.z = grng.NextFloat() - 0.5f; sideVect.ANormalize(); float3 bendVect = sideVect.cross(UpVector); // direction to bend into sideVect *= mapInfo->grass.bladeWidth * (-0.15f * lngRnd + 1.0f); - const float3 basePos = rng.NextVector2D() * (turfSize - (bendVect * std::sin(maxAng) * length).Length2D()); + + const float3 basePos = grng.NextVector2D() * (turfSize - (bendVect * std::sin(maxAng) * length).Length2D()); // select one of the 16 color shadings - const float xtexCoord = rng.NextInt(16) / 16.0f; + const float xtexCoord = grng.NextInt(16) / 16.0f; const int numSections = 2 + int(maxAng * 1.2f + length * 0.2f); float3 normalBend = -bendVect; @@ -851,7 +861,7 @@ void CGrassDrawer::CreateGrassDispList(int listNum) void CGrassDrawer::CreateGrassBladeTex(unsigned char* buf) { float3 redish = float3(0.95f, 0.70f, 0.4f); - float3 col = mix(mapInfo->grass.color, redish, 0.1f * rng.NextFloat()); + float3 col = mix(mapInfo->grass.color, redish, 0.1f * grng.NextFloat()); col.x = Clamp(col.x, 0.f, 1.f); col.y = Clamp(col.y, 0.f, 1.f); col.z = Clamp(col.z, 0.f, 1.f); diff --git a/rts/System/GlobalRNG.h b/rts/System/GlobalRNG.h index 7cef74b472c..ab54e64dfc6 100644 --- a/rts/System/GlobalRNG.h +++ b/rts/System/GlobalRNG.h @@ -10,6 +10,35 @@ +#if 0 +struct LCG16 { +public: + typedef uint16_t res_type; + typedef uint32_t val_type; + + LCG16(const val_type _val = def_val, const val_type _seq = def_seq) { seed(_val, _seq); } + LCG16(const LCG16& rng) { *this = rng; } + + void seed(const val_type initval, const val_type initseq) { + val = initval; + seq = initseq; + } + + res_type next() { return (((val = (val * 214013L + seq)) >> 16) & max_res); } + res_type bnext(const res_type bound) { return (next() % bound); } + +public: + static constexpr res_type min_res = 0; + static constexpr res_type max_res = 0x7fff; + static constexpr val_type def_val = 0; + static constexpr val_type def_seq = 2531011L; + +private: + val_type val; + val_type seq; +}; +#endif + struct PCG32 { public: typedef uint32_t res_type; @@ -20,7 +49,7 @@ struct PCG32 { void seed(const val_type initval, const val_type initseq) { val = 0u; - inc = (initseq << 1u) | 1u; + seq = (initseq << 1u) | 1u; next(); val += initval; @@ -31,7 +60,7 @@ struct PCG32 { const val_type oldval = val; // advance internal state - val = oldval * 6364136223846793005ull + inc; + val = oldval * 6364136223846793005ull + seq; // calculate output function (XSH RR), uses old state for max ILP const res_type xsh = ((oldval >> 18u) ^ oldval) >> 27u; @@ -50,40 +79,46 @@ struct PCG32 { } public: + static constexpr res_type min_res = std::numeric_limits::min(); + static constexpr res_type max_res = std::numeric_limits::max(); static constexpr val_type def_val = 0x853c49e6748fea9bULL; static constexpr val_type def_seq = 0xda3e39cb94b95bdbULL; private: val_type val; - val_type inc; + val_type seq; }; -template class CGlobalRNG { + +template class CGlobalRNG { public: - void Seed(PCG32::val_type seed) { SetSeed(seed); } - void SetSeed(PCG32::val_type seed, bool init = false) { + typedef typename RNG::val_type rng_val_type; + typedef typename RNG::res_type rng_res_type; + + void Seed(rng_val_type seed) { SetSeed(seed); } + void SetSeed(rng_val_type seed, bool init = false) { // use address of this object as sequence-id for unsynced RNG, modern systems have ASLR if (init) { - pcg.seed(initSeed = seed, PCG32::val_type(this) * (1 - synced) + PCG32::def_seq * synced); + gen.seed(initSeed = seed, static_cast(size_t(this)) * (1 - synced) + RNG::def_seq * synced); } else { - pcg.seed(lastSeed = seed, PCG32::val_type(this) * (1 - synced) + PCG32::def_seq * synced); + gen.seed(lastSeed = seed, static_cast(size_t(this)) * (1 - synced) + RNG::def_seq * synced); } } - PCG32::val_type GetInitSeed() const { return initSeed; } - PCG32::val_type GetLastSeed() const { return lastSeed; } + rng_val_type GetInitSeed() const { return initSeed; } + rng_val_type GetLastSeed() const { return lastSeed; } // needed for std::{random_}shuffle - PCG32::res_type operator()( ) { return (pcg. next( )); } - PCG32::res_type operator()(PCG32::res_type N) { return (pcg.bnext(N)); } + rng_res_type operator()( ) { return (gen. next( )); } + rng_res_type operator()(rng_res_type N) { return (gen.bnext(N)); } - static constexpr PCG32::res_type min() { return (std::numeric_limits::min()); } - static constexpr PCG32::res_type max() { return (std::numeric_limits::max()); } + static constexpr rng_res_type min() { return RNG::min_res; } + static constexpr rng_res_type max() { return RNG::max_res; } - PCG32::res_type NextInt(PCG32::res_type N = max()) { return ((*this)(N)); } + rng_res_type NextInt(rng_res_type N = max()) { return ((*this)(N)); } - float NextFloat(PCG32::res_type N = max()) { return ((NextInt(N) * 1.0f) / N); } // [0,1) rounded to multiple of 1/N + float NextFloat(rng_res_type N = max()) { return ((NextInt(N) * 1.0f) / N); } // [0,1) rounded to multiple of 1/N float NextFloat32() { return (math::ldexp(NextInt(max()), -32)); } // [0,1) rounded to multiple of 1/(2^32) float3 NextVector2D() { return (NextVector(0.0f)); } @@ -100,17 +135,17 @@ template class CGlobalRNG { } private: - PCG32 pcg; + RNG gen; // initial and last-set seed - PCG32::val_type initSeed = 0; - PCG32::val_type lastSeed = 0; + rng_val_type initSeed = 0; + rng_val_type lastSeed = 0; }; // synced and unsynced RNG's no longer need to be different types -typedef CGlobalRNG CGlobalSyncedRNG; -typedef CGlobalRNG CGlobalUnsyncedRNG; +typedef CGlobalRNG CGlobalSyncedRNG; +typedef CGlobalRNG CGlobalUnsyncedRNG; #endif