aboutsummaryrefslogtreecommitdiff
path: root/dep/src/g3dlite/PrecomputedRandom.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dep/src/g3dlite/PrecomputedRandom.cpp')
-rw-r--r--dep/src/g3dlite/PrecomputedRandom.cpp125
1 files changed, 125 insertions, 0 deletions
diff --git a/dep/src/g3dlite/PrecomputedRandom.cpp b/dep/src/g3dlite/PrecomputedRandom.cpp
new file mode 100644
index 00000000000..387ded35195
--- /dev/null
+++ b/dep/src/g3dlite/PrecomputedRandom.cpp
@@ -0,0 +1,125 @@
+/**
+ @file PrecomputedRandom.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-03-31
+ @edited 2009-07-01
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/PrecomputedRandom.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+PrecomputedRandom::PrecomputedRandom(int dataSize, uint32 seed) :
+ Random((void*)NULL),
+ m_hemiUniform(NULL),
+ m_sphereBits(NULL),
+ m_modMask(dataSize - 1),
+ m_freeData(true) {
+
+ alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
+ m_index = seed & m_modMask;
+
+ HemiUniformData* h;
+ SphereBitsData* s;
+ m_hemiUniform = h = (HemiUniformData*) System::malloc(sizeof(HemiUniformData) * dataSize);
+ m_sphereBits = s = (SphereBitsData*) System::malloc(sizeof(SphereBitsData) * dataSize);
+
+ Random r;
+
+ for (int i = 0; i < dataSize; ++i) {
+ h[i].uniform = r.uniform();
+ r.cosHemi(h[i].cosHemiX, h[i].cosHemiY, h[i].cosHemiZ);
+
+ s[i].bits = r.bits();
+ r.sphere(s[i].sphereX, s[i].sphereY, s[i].sphereZ);
+ }
+
+}
+
+
+PrecomputedRandom::PrecomputedRandom(const HemiUniformData* data1, const SphereBitsData* data2, int dataSize, uint32 seed) :
+ Random((void*)NULL),
+ m_hemiUniform(data1),
+ m_sphereBits(data2),
+ m_modMask(dataSize - 1),
+ m_freeData(false) {
+
+ m_index = seed & m_modMask;
+ alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
+}
+
+
+PrecomputedRandom::~PrecomputedRandom() {
+ if (m_freeData) {
+ System::free(const_cast<HemiUniformData*>(m_hemiUniform));
+ System::free(const_cast<SphereBitsData*>(m_sphereBits));
+ }
+}
+
+float PrecomputedRandom::uniform(float low, float high) {
+ m_index = (m_index + 1) & m_modMask;
+ return low + m_hemiUniform[m_index].uniform * (high - low);
+}
+
+
+float PrecomputedRandom::uniform() {
+ m_index = (m_index + 1) & m_modMask;
+ return m_hemiUniform[m_index].uniform;
+}
+
+
+void PrecomputedRandom::cosHemi(float& x, float& y, float& z) {
+ m_index = (m_index + 1) & m_modMask;
+ x = m_hemiUniform[m_index].cosHemiX;
+ y = m_hemiUniform[m_index].cosHemiY;
+ z = m_hemiUniform[m_index].cosHemiZ;
+}
+
+void PrecomputedRandom::cosPowHemi(const float k, float& x, float& y, float& z) {
+ // Computing a cosPowHemi costs 4 slow functions (pow, sqrt, sin,
+ // cos). We can do it with two, given a cosHemi sample, basically
+ // saving the cost of sin and cos and making a single 128-byte
+ // memory read (for a vector) instead of two (for adjacent uniform
+ // floats).
+
+ // cos^1 distribution sample
+ float cos1;
+ cosHemi(x, y, cos1);
+
+ // Fix the distribution by adjusting the cosine:
+ // rnd(cos^k t) = (rnd(cos(t))^2)^(1/k)
+
+ // produces cos^k distribution sample
+ z = pow(cos1, 2.0f / (1.0f + k));
+
+ // Rescale x and y by sqrt(1.0f - square(z)) / sqrt(x*x + y*y).
+ // Add a very tiny offset to handle the (almost impossibly unlikely) case where
+ // z = 1 and x^2+y^2 = 0.
+ static const float eps = 0.000001f;
+ const float s = sqrt((1.0f + eps - square(z)) / (square(x) + square(y) + eps));
+
+ x *= s;
+ y *= s;
+}
+
+
+uint32 PrecomputedRandom::bits() {
+ m_index = (m_index + 1) & m_modMask;
+ return m_sphereBits[m_index].bits;
+}
+
+
+void PrecomputedRandom::sphere(float& x, float& y, float& z) {
+ m_index = (m_index + 1) & m_modMask;
+ x = m_sphereBits[m_index].sphereX;
+ y = m_sphereBits[m_index].sphereY;
+ z = m_sphereBits[m_index].sphereZ;
+}
+
+}