Skip to content

Commit

Permalink
fix #5588
Browse files Browse the repository at this point in the history
  • Loading branch information
rtri committed May 30, 2017
1 parent 5f7bc5e commit ae0c9fc
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 57 deletions.
82 changes: 46 additions & 36 deletions rts/Rendering/Env/GrassDrawer.cpp
Expand Up @@ -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<LCG16, true> GrassRNG;
#else
typedef CGlobalRNG<PCG32, true> 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
Expand All @@ -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;



Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -178,7 +187,7 @@ CGrassDrawer::CGrassDrawer()
, grassMap(nullptr)
{
blockDrawer.ResetState();
rng.Seed(15);
grng.Seed(15);

const int detail = configHandler->GetInt("GrassDetail");

Expand Down Expand Up @@ -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;
}

Expand All @@ -397,20 +401,22 @@ static STurfParams GetTurfParams(CGlobalUnsyncedRNG& rng, const int x, const int
void CGrassDrawer::DrawNear(const std::vector<InviewNearGrass>& 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();
}
Expand All @@ -420,16 +426,17 @@ void CGrassDrawer::DrawNear(const std::vector<InviewNearGrass>& 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) };
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
77 changes: 56 additions & 21 deletions rts/System/GlobalRNG.h
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -50,40 +79,46 @@ struct PCG32 {
}

public:
static constexpr res_type min_res = std::numeric_limits<res_type>::min();
static constexpr res_type max_res = std::numeric_limits<res_type>::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<bool synced> class CGlobalRNG {

template<typename RNG, bool synced> 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<rng_val_type>(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<rng_val_type>(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<PCG32::res_type>::min()); }
static constexpr PCG32::res_type max() { return (std::numeric_limits<PCG32::res_type>::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)); }
Expand All @@ -100,17 +135,17 @@ template<bool synced> 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<true> CGlobalSyncedRNG;
typedef CGlobalRNG<false> CGlobalUnsyncedRNG;
typedef CGlobalRNG<PCG32, true> CGlobalSyncedRNG;
typedef CGlobalRNG<PCG32, false> CGlobalUnsyncedRNG;

#endif

0 comments on commit ae0c9fc

Please sign in to comment.