mxwcore-legion/dep/g3dlite/source/Random.cpp

231 lines
5.5 KiB
C++
Raw Normal View History

2023-11-05 15:26:19 -05:00
/**
\file Random.cpp
\maintainer Morgan McGuire, http://graphics.cs.williams.edu
\created 2009-01-02
\edited 2012-03-29
Copyright 2000-2012, Morgan McGuire.
All rights reserved.
*/
#include "G3D/Random.h"
namespace G3D {
Random& Random::common() {
static Random r;
return r;
}
Random::Random(void* x) : state(NULL), m_threadsafe(false) {
(void)x;
}
Random::Random(uint32 seed, bool threadsafe) : m_threadsafe(threadsafe) {
state = new uint32[N];
reset(seed, threadsafe);
}
void Random::reset(uint32 seed, bool threadsafe) {
m_threadsafe = threadsafe;
const uint32 X = 1812433253UL;
state[0] = seed;
for (index = 1; index < (int)N; ++index) {
state[index] = X * (state[index - 1] ^ (state[index - 1] >> 30)) + index;
}
}
Random::~Random() {
delete[] state;
state = NULL;
}
uint32 Random::bits() {
// See http://en.wikipedia.org/wiki/Mersenne_twister
// Make a local copy of the index variable to ensure that it
// is not out of bounds
int localIndex = index;
// Automatically checks for index < 0 if corrupted
// by unsynchronized threads.
if ((unsigned int)localIndex >= (unsigned int)N) {
generate();
localIndex = 0;
}
// Increment the global index. It may go out of bounds on
// multiple threads, but the above check ensures that the
// array index actually used never goes out of bounds.
// It doesn't matter if we grab the same array index twice
// on two threads, since the distribution of random numbers
// will still be uniform.
++index;
// Return the next random in the sequence
uint32 r = state[localIndex];
// Temper the result
r ^= r >> U;
r ^= (r << S) & B;
r ^= (r << T) & C;
r ^= r >> L;
return r;
}
/** Generate the next N ints, and store them for readback later */
void Random::generate() {
// Lower R bits
static const uint32 LOWER_MASK = (1LU << R) - 1;
// Upper (32 - R) bits
static const uint32 UPPER_MASK = 0xFFFFFFFF << R;
static const uint32 mag01[2] = {0UL, (uint32)A};
if (m_threadsafe) {
bool contention = ! lock.lock();
if (contention) {
// Another thread just generated a set of numbers; no need for
// this thread to do it too
lock.unlock();
return;
}
}
// First N - M
for (unsigned int i = 0; i < N - M; ++i) {
uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
state[i] = state[i + M] ^ (x >> 1) ^ mag01[x & 1];
}
// Rest
for (unsigned int i = N - M + 1; i < N - 1; ++i) {
uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
state[i] = state[i + (M - N)] ^ (x >> 1) ^ mag01[x & 1];
}
uint32 y = (state[N - 1] & UPPER_MASK) | (state[0] & LOWER_MASK);
state[N - 1] = state[M - 1] ^ (y >> 1) ^ mag01[y & 1];
index = 0;
if (m_threadsafe) {
lock.unlock();
}
}
int Random::integer(int low, int high) {
debugAssert(high >= low);
int r = iFloor(low + (high - low + 1) * (double)bits() / 0xFFFFFFFFUL);
// There is a *very small* chance of generating
// a number larger than high.
if (r > high) {
return high;
} else {
return r;
}
}
float Random::gaussian(float mean, float stdev) {
// Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html
// Modified to specify standard deviation and mean of distribution
float w, x1, x2;
// Loop until w is less than 1 so that log(w) is negative
do {
x1 = uniform(-1.0, 1.0);
x2 = uniform(-1.0, 1.0);
w = float(square(x1) + square(x2));
} while (w > 1.0f);
// Transform to gassian distribution
// Multiply by sigma (stdev ^ 2) and add mean.
return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean;
}
void Random::cosSphere(float& x, float& y, float& z) {
cosHemi(x, y, z);
if (bits() & 1) {
// Choose the axis direction uniformly at random
z = -z;
}
}
void Random::cosHemi(float& x, float& y, float& z) {
const float e1 = uniform();
const float e2 = uniform();
// Jensen's method
const float sin_theta = sqrtf(1.0f - e1);
const float cos_theta = sqrtf(e1);
const float phi = 6.28318531f * e2;
x = cos(phi) * sin_theta;
y = sin(phi) * sin_theta;
z = cos_theta;
// We could also use Malley's method (pbrt p.657), since they are the same cost:
//
// r = sqrt(e1);
// t = 2*pi*e2;
// x = cos(t)*r;
// y = sin(t)*r;
// z = sqrt(1.0 - x*x + y*y);
}
void Random::cosPowHemi(const float k, float& x, float& y, float& z) {
const float e1 = uniform();
const float e2 = uniform();
const float cos_theta = pow(e1, 1.0f / (k + 1.0f));
const float sin_theta = sqrtf(1.0f - square(cos_theta));
const float phi = 6.28318531f * e2;
x = cos(phi) * sin_theta;
y = sin(phi) * sin_theta;
z = cos_theta;
}
void Random::hemi(float& x, float& y, float& z) {
sphere(x, y, z);
z = fabsf(z);
}
void Random::sphere(float& x, float& y, float& z) {
// Squared magnitude
float m2;
// Rejection sample
do {
x = uniform() * 2.0f - 1.0f,
y = uniform() * 2.0f - 1.0f,
z = uniform() * 2.0f - 1.0f;
m2 = x*x + y*y + z*z;
} while (m2 >= 1.0f);
// Divide by magnitude to produce a unit vector
float s = rsqrt(m2);
x *= s;
y *= s;
z *= s;
}
} // G3D