Improve random number generation

The global RNG state is now protected from simultaneous accesses by
employing a mutex.

Performance sensitive code requiring generation of a large number of
random numbers can utilize `LocalRNG`. This is not thread-safe but
provides a much greater throughput. `LocalRNG` is seeded by the global
RNG only at construction time.
This commit is contained in:
yvt 2018-09-17 23:37:11 +09:00
parent 995e7f2ffc
commit 585cbfce1c
24 changed files with 367 additions and 245 deletions

View File

@ -52,13 +52,9 @@ DEFINE_SPADES_SETTING(s_gain, "1");
namespace spades {
namespace audio {
std::uniform_real_distribution<float> real_dist_audio(0, 1);
static Vector3 TransformVectorToAL(Vector3 v) { return MakeVector3(v.x, v.y, v.z); }
static Vector3 TransformVectorFromAL(Vector3 v) { return MakeVector3(v.x, v.y, v.z); }
static float NextRandom() { return real_dist_audio(mt_engine); }
namespace {
std::vector<uint8_t> ConvertFloatBufferToSignedShort(const std::vector<uint8_t> &bytes) {
if (bytes.size() & 3) {
@ -562,7 +558,7 @@ namespace spades {
ALSrc *AllocChunk() {
SPADES_MARK_FUNCTION();
size_t start = mt_engine() % srcs.size();
size_t start = SampleRandomInt<std::size_t>(0, srcs.size() - 1);
for (size_t i = 0; i < srcs.size(); i++) {
ALSrc *src = srcs[(i + start) % srcs.size()];
if (src->IsPlaying())
@ -570,7 +566,7 @@ namespace spades {
return src;
}
ALSrc *src = srcs[mt_engine() % srcs.size()];
ALSrc *src = SampleRandomElement(srcs);
src->Terminate();
return src;
}
@ -649,9 +645,9 @@ namespace spades {
Vector3 rayTo;
for (int rays = 0; rays < 4; rays++) {
rayTo.x = NextRandom() - NextRandom();
rayTo.y = NextRandom() - NextRandom();
rayTo.z = NextRandom() - NextRandom();
rayTo.x = SampleRandomFloat() - SampleRandomFloat();
rayTo.y = SampleRandomFloat() - SampleRandomFloat();
rayTo.z = SampleRandomFloat() - SampleRandomFloat();
rayTo = rayTo.Normalize();
IntVector3 hitPos;
@ -742,7 +738,7 @@ namespace spades {
for (size_t i = 0; i < srcs.size(); i++) {
ALSrc *s = srcs[i];
if ((mt_engine() % 8 == 0) && s->IsPlaying())
if ((SampleRandomInt(0, 7) == 0) && s->IsPlaying())
s->UpdateObstruction();
}
}

View File

@ -453,8 +453,6 @@ namespace spades {
old->Release();
}
static float NextRandom() { return real_dist(mt_engine); }
void YsrDevice::Respatialize(const spades::Vector3 &eye, const spades::Vector3 &front,
const spades::Vector3 &up) {
SPADES_MARK_FUNCTION();
@ -480,9 +478,9 @@ namespace spades {
Vector3 rayTo;
for (int rays = 0; rays < 4; rays++) {
rayTo.x = NextRandom() - NextRandom();
rayTo.y = NextRandom() - NextRandom();
rayTo.z = NextRandom() - NextRandom();
rayTo.x = SampleRandomFloat() - SampleRandomFloat();
rayTo.y = SampleRandomFloat() - SampleRandomFloat();
rayTo.z = SampleRandomFloat() - SampleRandomFloat();
rayTo = rayTo.Normalize();
IntVector3 hitPos;

View File

@ -66,10 +66,6 @@ SPADES_SETTING(cg_playerName);
namespace spades {
namespace client {
std::random_device r_device_client;
std::mt19937_64 mt_engine_client(
r_device_client()); // Seed Mersenne twister with non-deterministic 32-bit seed
Client::Client(IRenderer *r, IAudioDevice *audioDev, const ServerAddress &host,
FontManager *fontManager)
: playerName(cg_playerName.operator std::string().substr(0, 15)),

View File

@ -67,8 +67,6 @@ namespace spades {
class ClientUI;
extern std::mt19937_64 mt_engine_client; // randomness generator
class Client : public IWorldListener, public gui::View {
friend class ScoreboardView;
friend class LimboView;
@ -101,6 +99,11 @@ namespace spades {
FPSCounter fpsCounter;
FPSCounter upsCounter;
/**
* A random number generator. Only can be accessed by a main thread.
*/
LocalRNG rng;
std::unique_ptr<NetClient> net;
std::string playerName;
std::unique_ptr<IStream> logStream;

View File

@ -1192,7 +1192,7 @@ namespace spades {
case RIFLE_WEAPON:
model = renderer->RegisterModel("Models/Weapons/Rifle/Casing.kv6");
snd =
(mt_engine_client() & 0x1000)
SampleRandomBool()
? audioDevice->RegisterSound("Sounds/Weapons/Rifle/ShellDrop1.opus")
: audioDevice->RegisterSound(
"Sounds/Weapons/Rifle/ShellDrop2.opus");
@ -1207,7 +1207,7 @@ namespace spades {
case SMG_WEAPON:
model = renderer->RegisterModel("Models/Weapons/SMG/Casing.kv6");
snd =
(mt_engine_client() & 0x1000)
SampleRandomBool()
? audioDevice->RegisterSound("Sounds/Weapons/SMG/ShellDrop1.opus")
: audioDevice->RegisterSound("Sounds/Weapons/SMG/ShellDrop2.opus");
snd2 = audioDevice->RegisterSound("Sounds/Weapons/SMG/ShellWater.opus");

View File

@ -157,12 +157,13 @@ namespace spades {
for (int i = 0; i < 10; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(v,
MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) *
MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) *
10.f,
1.f, 0.7f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.1f + GetRandom() * GetRandom() * 0.2f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(0.1f + rng.SampleFloat() * rng.SampleFloat() * 0.2f);
ent->SetLifeTime(3.f, 0.f, 1.f);
localEntities.emplace_back(ent);
}
@ -175,14 +176,15 @@ namespace spades {
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 100.f, SmokeSpriteEntity::Type::Explosion);
ent->SetTrajectory(v,
MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) *
MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) *
.7f,
.8f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(.5f + GetRandom() * GetRandom() * 0.2f, 2.f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(.5f + rng.SampleFloat() * rng.SampleFloat() * 0.2f, 2.f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(.20f + GetRandom() * .2f, 0.06f, .20f);
ent->SetLifeTime(.20f + rng.SampleFloat() * .2f, 0.06f, .20f);
localEntities.emplace_back(ent);
}
@ -191,14 +193,15 @@ namespace spades {
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 40.f, SmokeSpriteEntity::Type::Steady);
ent->SetTrajectory(v,
MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) *
MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) *
.7f,
.8f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(.7f + GetRandom() * GetRandom() * 0.2f, 2.f, 0.1f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(.7f + rng.SampleFloat() * rng.SampleFloat() * 0.2f, 2.f, 0.1f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(.80f + GetRandom() * 0.4f, 0.06f, 1.0f);
ent->SetLifeTime(.80f + rng.SampleFloat() * 0.4f, 0.06f, 1.0f);
localEntities.emplace_back(ent);
}
}
@ -219,12 +222,13 @@ namespace spades {
for (int i = 0; i < 7; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) *
MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) *
7.f,
1.f, .9f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.2f + GetRandom() * GetRandom() * 0.1f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(0.2f + rng.SampleFloat() * rng.SampleFloat() * 0.1f);
ent->SetLifeTime(2.f, 0.f, 1.f);
if (distPowered < 16.f * 16.f)
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
@ -237,13 +241,13 @@ namespace spades {
if (distPowered < 32.f * 32.f) {
for (int i = 0; i < 16; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin, MakeVector3(GetRandom() - GetRandom(),
GetRandom() - GetRandom(),
GetRandom() - GetRandom()) *
ent->SetTrajectory(origin, MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) *
12.f,
1.f, .9f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.1f + GetRandom() * GetRandom() * 0.14f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(0.1f + rng.SampleFloat() * rng.SampleFloat() * 0.14f);
ent->SetLifeTime(2.f, 0.f, 1.f);
if (distPowered < 16.f * 16.f)
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
@ -256,13 +260,14 @@ namespace spades {
for (int i = 0; i < 2; i++) {
ParticleSpriteEntity *ent = new SmokeSpriteEntity(this, color, 100.f);
ent->SetTrajectory(origin,
MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) *
MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) *
.7f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(.6f + GetRandom() * GetRandom() * 0.2f, 0.8f);
ent->SetLifeTime(.3f + GetRandom() * .3f, 0.06f, .4f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(.6f + rng.SampleFloat() * rng.SampleFloat() * 0.2f, 0.8f);
ent->SetLifeTime(.3f + rng.SampleFloat() * .3f, 0.06f, .4f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
localEntities.emplace_back(ent);
}
@ -285,12 +290,13 @@ namespace spades {
for (int i = 0; i < 8; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) *
MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) *
7.f,
1.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.3f + GetRandom() * GetRandom() * 0.2f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(0.3f + rng.SampleFloat() * rng.SampleFloat() * 0.2f);
ent->SetLifeTime(2.f, 0.f, 1.f);
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
localEntities.emplace_back(ent);
@ -317,15 +323,16 @@ namespace spades {
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 120.f, SmokeSpriteEntity::Type::Explosion);
ent->SetTrajectory(
origin, (MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) +
origin, (MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) +
velBias * .5f) *
0.3f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(.4f, 3.f, 0.0000005f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(0.2f + GetRandom() * 0.1f, 0.f, .30f);
ent->SetLifeTime(0.2f + rng.SampleFloat() * 0.1f, 0.f, .30f);
localEntities.emplace_back(ent);
}
}
@ -381,15 +388,16 @@ namespace spades {
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 60.f, SmokeSpriteEntity::Type::Explosion);
ent->SetTrajectory(
origin, (MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) +
origin, (MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) +
velBias * .5f) *
2.f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(.6f + GetRandom() * GetRandom() * 0.4f, 2.f, .2f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(.6f + rng.SampleFloat() * rng.SampleFloat() * 0.4f, 2.f, .2f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(1.8f + GetRandom() * 0.1f, 0.f, .20f);
ent->SetLifeTime(1.8f + rng.SampleFloat() * 0.1f, 0.f, .20f);
localEntities.emplace_back(ent);
}
@ -398,18 +406,19 @@ namespace spades {
for (int i = 0; i < 8; i++) {
ParticleSpriteEntity *ent = new SmokeSpriteEntity(this, color, 20.f);
ent->SetTrajectory(
origin, (MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
(GetRandom() - GetRandom()) * .2f)) *
origin, (MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
(rng.SampleFloat() - rng.SampleFloat()) * .2f)) *
2.f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(1.5f + GetRandom() * GetRandom() * 0.8f, 0.2f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(1.5f + rng.SampleFloat() * rng.SampleFloat() * 0.8f, 0.2f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
switch ((int)cg_particles) {
case 1: ent->SetLifeTime(0.8f + GetRandom() * 1.f, 0.1f, 8.f); break;
case 2: ent->SetLifeTime(1.5f + GetRandom() * 2.f, 0.1f, 8.f); break;
case 1: ent->SetLifeTime(0.8f + rng.SampleFloat() * 1.f, 0.1f, 8.f); break;
case 2: ent->SetLifeTime(1.5f + rng.SampleFloat() * 2.f, 0.1f, 8.f); break;
case 3:
default: ent->SetLifeTime(2.f + GetRandom() * 5.f, 0.1f, 8.f); break;
default: ent->SetLifeTime(2.f + rng.SampleFloat() * 5.f, 0.1f, 8.f); break;
}
localEntities.emplace_back(ent);
}
@ -419,14 +428,15 @@ namespace spades {
color = MakeVector4(0.01, 0.03, 0, 1.f);
for (int i = 0; i < 42; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
Vector3 dir = MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom());
Vector3 dir = MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat());
dir += velBias * .5f;
float radius = 0.1f + GetRandom() * GetRandom() * 0.2f;
float radius = 0.1f + rng.SampleFloat() * rng.SampleFloat() * 0.2f;
ent->SetTrajectory(origin + dir * .2f, dir * 20.f, .1f + radius * 3.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(radius);
ent->SetLifeTime(3.5f + GetRandom() * 2.f, 0.f, 1.f);
ent->SetLifeTime(3.5f + rng.SampleFloat() * 2.f, 0.f, 1.f);
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);
localEntities.emplace_back(ent);
}
@ -437,15 +447,15 @@ namespace spades {
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(this, color, 120.f, SmokeSpriteEntity::Type::Explosion);
ent->SetTrajectory(
origin, (MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
GetRandom() - GetRandom()) +
origin, (MakeVector3(rng.SampleFloat() - rng.SampleFloat(), rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat()) +
velBias) *
6.f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(.3f + GetRandom() * GetRandom() * 0.4f, 3.f, .1f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(.3f + rng.SampleFloat() * rng.SampleFloat() * 0.4f, 3.f, .1f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(.18f + GetRandom() * 0.03f, 0.f, .10f);
ent->SetLifeTime(.18f + rng.SampleFloat() * 0.03f, 0.f, .10f);
// ent->SetAdditive(true);
localEntities.emplace_back(ent);
}
@ -471,14 +481,14 @@ namespace spades {
for (int i = 0; i < 7; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom() - GetRandom(),
GetRandom() - GetRandom(), -GetRandom() * 7.f)) *
(MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(), -rng.SampleFloat() * 7.f)) *
2.5f,
.3f, .6f);
ent->SetRotation(0.f);
ent->SetRadius(1.5f + GetRandom() * GetRandom() * 0.4f, 1.3f);
ent->SetRadius(1.5f + rng.SampleFloat() * rng.SampleFloat() * 0.4f, 1.3f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(3.f + GetRandom() * 0.3f, 0.f, .60f);
ent->SetLifeTime(3.f + rng.SampleFloat() * 0.3f, 0.f, .60f);
localEntities.emplace_back(ent);
}
@ -490,14 +500,14 @@ namespace spades {
for (int i = 0; i < 16; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom() - GetRandom(),
GetRandom() - GetRandom(), -GetRandom() * 10.f)) *
(MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(), -rng.SampleFloat() * 10.f)) *
3.5f,
1.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.9f + GetRandom() * GetRandom() * 0.4f, 0.7f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(0.9f + rng.SampleFloat() * rng.SampleFloat() * 0.4f, 0.7f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(3.f + GetRandom() * 0.3f, .7f, .60f);
ent->SetLifeTime(3.f + rng.SampleFloat() * 0.3f, .7f, .60f);
localEntities.emplace_back(ent);
}
@ -508,18 +518,18 @@ namespace spades {
for (int i = 0; i < 8; i++) {
ParticleSpriteEntity *ent = new SmokeSpriteEntity(this, color, 20.f);
ent->SetTrajectory(
origin, (MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
(GetRandom() - GetRandom()) * .2f)) *
origin, (MakeVector3(rng.SampleFloat() - rng.SampleFloat(), rng.SampleFloat() - rng.SampleFloat(),
(rng.SampleFloat() - rng.SampleFloat()) * .2f)) *
2.f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(1.4f + GetRandom() * GetRandom() * 0.8f, 0.2f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(1.4f + rng.SampleFloat() * rng.SampleFloat() * 0.8f, 0.2f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
switch ((int)cg_particles) {
case 1: ent->SetLifeTime(3.f + GetRandom() * 5.f, 0.1f, 8.f); break;
case 1: ent->SetLifeTime(3.f + rng.SampleFloat() * 5.f, 0.1f, 8.f); break;
case 2:
case 3:
default: ent->SetLifeTime(6.f + GetRandom() * 5.f, 0.1f, 8.f); break;
default: ent->SetLifeTime(6.f + rng.SampleFloat() * 5.f, 0.1f, 8.f); break;
}
localEntities.emplace_back(ent);
}
@ -529,15 +539,15 @@ namespace spades {
color = MakeVector4(1, 1, 1, 0.7f);
for (int i = 0; i < 42; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
Vector3 dir = MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
-GetRandom() * 3.f);
Vector3 dir = MakeVector3(rng.SampleFloat() - rng.SampleFloat(), rng.SampleFloat() - rng.SampleFloat(),
-rng.SampleFloat() * 3.f);
dir += velBias * .5f;
float radius = 0.1f + GetRandom() * GetRandom() * 0.2f;
float radius = 0.1f + rng.SampleFloat() * rng.SampleFloat() * 0.2f;
ent->SetTrajectory(origin + dir * .2f + MakeVector3(0, 0, -1.2f), dir * 13.f,
.1f + radius * 3.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(radius);
ent->SetLifeTime(3.5f + GetRandom() * 2.f, 0.f, 1.f);
ent->SetLifeTime(3.5f + rng.SampleFloat() * 2.f, 0.f, 1.f);
ent->SetBlockHitAction(ParticleSpriteEntity::Delete);
localEntities.emplace_back(ent);
}
@ -564,14 +574,14 @@ namespace spades {
for (int i = 0; i < 2; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom() - GetRandom(),
GetRandom() - GetRandom(), -GetRandom() * 7.f)) *
(MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(), -rng.SampleFloat() * 7.f)) *
1.f,
.3f, .6f);
ent->SetRotation(0.f);
ent->SetRadius(0.6f + GetRandom() * GetRandom() * 0.4f, .7f);
ent->SetRadius(0.6f + rng.SampleFloat() * rng.SampleFloat() * 0.4f, .7f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(3.f + GetRandom() * 0.3f, 0.1f, .60f);
ent->SetLifeTime(3.f + rng.SampleFloat() * 0.3f, 0.1f, .60f);
localEntities.emplace_back(ent);
}
@ -583,14 +593,14 @@ namespace spades {
for (int i = 0; i < 6; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
ent->SetTrajectory(origin,
(MakeVector3(GetRandom() - GetRandom(),
GetRandom() - GetRandom(), -GetRandom() * 10.f)) *
(MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(), -rng.SampleFloat() * 10.f)) *
2.f,
1.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.6f + GetRandom() * GetRandom() * 0.6f, 0.6f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(0.6f + rng.SampleFloat() * rng.SampleFloat() * 0.6f, 0.6f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(3.f + GetRandom() * 0.3f, GetRandom() * 0.3f, .60f);
ent->SetLifeTime(3.f + rng.SampleFloat() * 0.3f, rng.SampleFloat() * 0.3f, .60f);
localEntities.emplace_back(ent);
}
@ -599,14 +609,15 @@ namespace spades {
color = MakeVector4(1, 1, 1, 0.7f);
for (int i = 0; i < 10; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(this, img, color);
Vector3 dir = MakeVector3(GetRandom() - GetRandom(), GetRandom() - GetRandom(),
-GetRandom() * 3.f);
float radius = 0.03f + GetRandom() * GetRandom() * 0.05f;
Vector3 dir = MakeVector3(rng.SampleFloat() - rng.SampleFloat(),
rng.SampleFloat() - rng.SampleFloat(),
-rng.SampleFloat() * 3.f);
float radius = 0.03f + rng.SampleFloat() * rng.SampleFloat() * 0.05f;
ent->SetTrajectory(origin + dir * .2f + MakeVector3(0, 0, -1.2f), dir * 5.f,
.1f + radius * 3.f, 1.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRotation(rng.SampleFloat() * (float)M_PI * 2.f);
ent->SetRadius(radius);
ent->SetLifeTime(3.5f + GetRandom() * 2.f, 0.f, 1.f);
ent->SetLifeTime(3.5f + rng.SampleFloat() * 2.f, 0.f, 1.f);
ent->SetBlockHitAction(ParticleSpriteEntity::Delete);
localEntities.emplace_back(ent);
}

View File

@ -46,10 +46,6 @@ DEFINE_SPADES_SETTING(cg_manualFocus, "0");
DEFINE_SPADES_SETTING(cg_depthOfFieldAmount, "1");
DEFINE_SPADES_SETTING(cg_shake, "1");
static float nextRandom() {
return spades::real_dist(spades::client::mt_engine_client);
}
namespace spades {
namespace client {
@ -210,8 +206,8 @@ namespace spades {
localFireVibration *= 0.4f;
}
roll += (nextRandom() - nextRandom()) * 0.03f * localFireVibration;
scale += nextRandom() * 0.04f * localFireVibration;
roll += (SampleRandomFloat() - SampleRandomFloat()) * 0.03f * localFireVibration;
scale += SampleRandomFloat() * 0.04f * localFireVibration;
vibPitch += localFireVibration * (1.f - localFireVibration) * 0.01f;
vibYaw += sinf(localFireVibration * (float)M_PI * 2.f) * 0.001f;
@ -391,10 +387,10 @@ namespace spades {
grenVib *= 10.f;
if (grenVib > 1.f)
grenVib = 1.f;
roll += (nextRandom() - nextRandom()) * 0.2f * grenVib;
vibPitch += (nextRandom() - nextRandom()) * 0.1f * grenVib;
vibYaw += (nextRandom() - nextRandom()) * 0.1f * grenVib;
scale -= (nextRandom() - nextRandom()) * 0.1f * grenVib;
roll += (SampleRandomFloat() - SampleRandomFloat()) * 0.2f * grenVib;
vibPitch += (SampleRandomFloat() - SampleRandomFloat()) * 0.1f * grenVib;
vibYaw += (SampleRandomFloat() - SampleRandomFloat()) * 0.1f * grenVib;
scale -= (SampleRandomFloat() - SampleRandomFloat()) * 0.1f * grenVib;
def.radialBlur += grenVib * 0.1f;
}

View File

@ -524,7 +524,7 @@ namespace spades {
lastHurtTime = world->GetTime();
Handle<IAudioChunk> c;
switch ((mt_engine_client() >> 3) & 3) {
switch (SampleRandomInt(0, 3)) {
case 0:
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/FleshLocal1.opus");
break;
@ -545,10 +545,10 @@ namespace spades {
hurtSprites.resize(std::max(cnt, 6));
for (size_t i = 0; i < hurtSprites.size(); i++) {
HurtSprite &spr = hurtSprites[i];
spr.angle = GetRandom() * (2.f * static_cast<float>(M_PI));
spr.scale = .2f + GetRandom() * GetRandom() * .7f;
spr.horzShift = GetRandom();
spr.strength = .3f + GetRandom() * .7f;
spr.angle = SampleRandomFloat() * (2.f * static_cast<float>(M_PI));
spr.scale = .2f + SampleRandomFloat() * SampleRandomFloat() * .7f;
spr.horzShift = SampleRandomFloat();
spr.strength = .3f + SampleRandomFloat() * .7f;
if (hpper > .5f) {
spr.strength *= 1.5f - hpper;
}
@ -605,17 +605,17 @@ namespace spades {
SPADES_MARK_FUNCTION();
if (!IsMuted()) {
const char *snds[] = {"Sounds/Player/Footstep1.opus", "Sounds/Player/Footstep2.opus",
std::array<const char *, 8> snds = {"Sounds/Player/Footstep1.opus", "Sounds/Player/Footstep2.opus",
"Sounds/Player/Footstep3.opus", "Sounds/Player/Footstep4.opus",
"Sounds/Player/Footstep5.opus", "Sounds/Player/Footstep6.opus",
"Sounds/Player/Footstep7.opus", "Sounds/Player/Footstep8.opus"};
const char *rsnds[] = {
std::array<const char *, 12> rsnds = {
"Sounds/Player/Run1.opus", "Sounds/Player/Run2.opus", "Sounds/Player/Run3.opus",
"Sounds/Player/Run4.opus", "Sounds/Player/Run5.opus", "Sounds/Player/Run6.opus",
"Sounds/Player/Run7.opus", "Sounds/Player/Run8.opus", "Sounds/Player/Run9.opus",
"Sounds/Player/Run10.opus", "Sounds/Player/Run11.opus", "Sounds/Player/Run12.opus",
};
const char *wsnds[] = {"Sounds/Player/Wade1.opus", "Sounds/Player/Wade2.opus",
std::array<const char *, 8> wsnds = {"Sounds/Player/Wade1.opus", "Sounds/Player/Wade2.opus",
"Sounds/Player/Wade3.opus", "Sounds/Player/Wade4.opus",
"Sounds/Player/Wade5.opus", "Sounds/Player/Wade6.opus",
"Sounds/Player/Wade7.opus", "Sounds/Player/Wade8.opus"};
@ -623,13 +623,13 @@ namespace spades {
? clientPlayers[p->GetId()]->GetSprintState() > 0.5f
: false;
Handle<IAudioChunk> c =
p->GetWade() ? audioDevice->RegisterSound(wsnds[(mt_engine_client() >> 8) % 8])
: audioDevice->RegisterSound(snds[(mt_engine_client() >> 8) % 8]);
p->GetWade() ? audioDevice->RegisterSound(SampleRandomElement(wsnds))
: audioDevice->RegisterSound(SampleRandomElement(snds));
audioDevice->Play(c, p->GetOrigin(), AudioParam());
if (sprinting && !p->GetWade()) {
AudioParam param;
param.volume *= clientPlayers[p->GetId()]->GetSprintState();
c = audioDevice->RegisterSound(rsnds[(mt_engine_client() >> 8) % 12]);
c = audioDevice->RegisterSound(SampleRandomElement(rsnds));
audioDevice->Play(c, p->GetOrigin(), param);
}
}
@ -779,7 +779,7 @@ namespace spades {
if (victim != world->GetLocalPlayer()) {
if (!IsMuted()) {
Handle<IAudioChunk> c;
switch (mt_engine_client() % 3) {
switch (SampleRandomInt(0, 2)) {
case 0:
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh1.opus");
break;
@ -839,7 +839,7 @@ namespace spades {
}
corp->AddImpulse(dir);
} else if (kt == KillTypeGrenade) {
corp->AddImpulse(MakeVector3(0, 0, -4.f - GetRandom() * 4.f));
corp->AddImpulse(MakeVector3(0, 0, -4.f - SampleRandomFloat() * 4.f));
}
corp->AddImpulse(victim->GetVelocty() * 32.f);
corpses.emplace_back(corp);
@ -964,7 +964,7 @@ namespace spades {
audioDevice->Play(c, hitPos, AudioParam());
} else {
Handle<IAudioChunk> c;
switch ((mt_engine_client() >> 6) % 3) {
switch (SampleRandomInt(0, 2)) {
case 0:
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Flesh1.opus");
break;
@ -1019,8 +1019,8 @@ namespace spades {
Handle<IAudioChunk> c;
param.pitch = .9f + GetRandom() * 0.2f;
switch ((mt_engine_client() >> 6) & 3) {
param.pitch = .9f + SampleRandomFloat() * 0.2f;
switch (SampleRandomInt(0, 3)) {
case 0:
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Water1.opus");
break;
@ -1044,20 +1044,12 @@ namespace spades {
param.volume = 2.f;
Handle<IAudioChunk> c;
switch ((mt_engine_client() >> 6) & 3) {
case 0:
case 1:
case 2:
case 3:
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Block.opus");
break;
}
audioDevice->Play(c, shiftedHitPos, param);
param.pitch = .9f + GetRandom() * 0.2f;
param.pitch = .9f + SampleRandomFloat() * 0.2f;
param.volume = 2.f;
switch ((mt_engine_client() >> 6) & 3) {
switch (SampleRandomInt(0, 3)) {
case 0:
c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Ricochet1.opus");
break;
@ -1170,7 +1162,7 @@ namespace spades {
if (!IsMuted()) {
Handle<IAudioChunk> c, cs;
switch ((mt_engine_client() >> 8) & 1) {
switch (SampleRandomInt(0, 1)) {
case 0:
c = audioDevice->RegisterSound("Sounds/Weapons/Grenade/Explode1.opus");
cs = audioDevice->RegisterSound(

View File

@ -54,50 +54,56 @@ namespace spades {
Matrix4 torso;
LocalRNG rng;
if (crouch) {
lower = lower * Matrix4::Translate(0, 0, -0.4f);
torso = lower * Matrix4::Translate(0, 0, -0.3f);
SetNode(Torso1, torso * MakeVector3(0.4f, -.15f, 0.1f));
SetNode(Torso2, torso * MakeVector3(-0.4f, -.15f, 0.1f));
SetNode(Torso3, torso * MakeVector3(-0.4f, .8f, 0.7f));
SetNode(Torso4, torso * MakeVector3(0.4f, .8f, 0.7f));
SetNode(rng, Torso1, torso * MakeVector3(0.4f, -.15f, 0.1f));
SetNode(rng, Torso2, torso * MakeVector3(-0.4f, -.15f, 0.1f));
SetNode(rng, Torso3, torso * MakeVector3(-0.4f, .8f, 0.7f));
SetNode(rng, Torso4, torso * MakeVector3(0.4f, .8f, 0.7f));
SetNode(Leg1, lower * MakeVector3(-0.4f, .1f, 1.f));
SetNode(Leg2, lower * MakeVector3(0.4f, .1f, 1.f));
SetNode(rng, Leg1, lower * MakeVector3(-0.4f, .1f, 1.f));
SetNode(rng, Leg2, lower * MakeVector3(0.4f, .1f, 1.f));
SetNode(Arm1, torso * MakeVector3(0.2f, -.4f, .2f));
SetNode(Arm2, torso * MakeVector3(-0.2f, -.4f, .2f));
SetNode(rng, Arm1, torso * MakeVector3(0.2f, -.4f, .2f));
SetNode(rng, Arm2, torso * MakeVector3(-0.2f, -.4f, .2f));
} else {
torso = lower * Matrix4::Translate(0, 0, -1.1f);
SetNode(Torso1, torso * MakeVector3(0.4f, 0.f, 0.1f));
SetNode(Torso2, torso * MakeVector3(-0.4f, 0.f, 0.1f));
SetNode(Torso3, torso * MakeVector3(-0.4f, .0f, 1.f));
SetNode(Torso4, torso * MakeVector3(0.4f, .0f, 1.f));
SetNode(rng, Torso1, torso * MakeVector3(0.4f, 0.f, 0.1f));
SetNode(rng, Torso2, torso * MakeVector3(-0.4f, 0.f, 0.1f));
SetNode(rng, Torso3, torso * MakeVector3(-0.4f, .0f, 1.f));
SetNode(rng, Torso4, torso * MakeVector3(0.4f, .0f, 1.f));
SetNode(Leg1, lower * MakeVector3(-0.4f, .0f, 1.f));
SetNode(Leg2, lower * MakeVector3(0.4f, .0f, 1.f));
SetNode(rng, Leg1, lower * MakeVector3(-0.4f, .0f, 1.f));
SetNode(rng, Leg2, lower * MakeVector3(0.4f, .0f, 1.f));
SetNode(Arm1, torso * MakeVector3(0.2f, -.4f, .2f));
SetNode(Arm2, torso * MakeVector3(-0.2f, -.4f, .2f));
SetNode(rng, Arm1, torso * MakeVector3(0.2f, -.4f, .2f));
SetNode(rng, Arm2, torso * MakeVector3(-0.2f, -.4f, .2f));
}
SetNode(Head, (nodes[Torso1].pos + nodes[Torso2].pos) * .5f + MakeVector3(0, 0, -0.6f));
SetNode(rng, Head,
(nodes[Torso1].pos + nodes[Torso2].pos) * .5f + MakeVector3(0, 0, -0.6f));
}
static float VelNoise() { return (GetRandom() - GetRandom()) * 2.f; }
void Corpse::SetNode(LocalRNG &rng, NodeType n, spades::Vector3 v) {
auto velNoise = [&] { return (rng.SampleFloat() - rng.SampleFloat()) * 2.0f; };
void Corpse::SetNode(NodeType n, spades::Vector3 v) {
SPAssert(n >= 0);
SPAssert(n < NodeCount);
nodes[n].pos = v;
nodes[n].vel = MakeVector3(VelNoise(), VelNoise(), 0.f);
nodes[n].vel = MakeVector3(velNoise(), velNoise(), 0.f);
nodes[n].lastPos = v;
nodes[n].lastForce = MakeVector3(0, 0, 0);
}
void Corpse::SetNode(NodeType n, spades::Vector4 v) { SetNode(n, v.GetXYZ()); }
void Corpse::SetNode(LocalRNG &rng, NodeType n, spades::Vector4 v) {
SetNode(rng, n, v.GetXYZ());
}
Corpse::~Corpse() {}

View File

@ -69,8 +69,8 @@ namespace spades {
Node nodes[NodeCount];
Edge edges[8];
void SetNode(NodeType n, Vector3);
void SetNode(NodeType n, Vector4);
void SetNode(LocalRNG&, NodeType n, Vector3);
void SetNode(LocalRNG&, NodeType n, Vector4);
void Spring(NodeType n1, NodeType n2, float distance, float dt);
void Spring(NodeType n1a, NodeType n1b, NodeType n2, float distance, float dt);

View File

@ -136,6 +136,9 @@ namespace spades {
if (client->grenadeVibration > 1.f)
client->grenadeVibration = 1.f;
LocalRNG rng;
auto getRandom = [&] { return rng.SampleFloat(); };
for (int x = 0; x < w; x++) {
Vector3 p1 = vmOrigin + vmAxis1 * (float)x;
for (int y = 0; y < h; y++) {
@ -162,15 +165,15 @@ namespace spades {
{
ParticleSpriteEntity *ent =
new SmokeSpriteEntity(client, col, 70.f);
ent->SetTrajectory(p3, (MakeVector3(GetRandom() - GetRandom(),
GetRandom() - GetRandom(),
GetRandom() - GetRandom())) *
ent->SetTrajectory(p3, (MakeVector3(getRandom() - getRandom(),
getRandom() - getRandom(),
getRandom() - getRandom())) *
0.2f,
1.f, 0.f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRotation(getRandom() * (float)M_PI * 2.f);
ent->SetRadius(1.0f, 0.5f);
ent->SetBlockHitAction(ParticleSpriteEntity::Ignore);
ent->SetLifeTime(1.0f + GetRandom() * 0.5f, 0.f, 1.0f);
ent->SetLifeTime(1.0f + getRandom() * 0.5f, 0.f, 1.0f);
client->AddLocalEntity(ent);
}
@ -178,13 +181,13 @@ namespace spades {
for (int i = 0; i < 6; i++) {
ParticleSpriteEntity *ent =
new ParticleSpriteEntity(client, img, col);
ent->SetTrajectory(p3, MakeVector3(GetRandom() - GetRandom(),
GetRandom() - GetRandom(),
GetRandom() - GetRandom()) *
ent->SetTrajectory(p3, MakeVector3(getRandom() - getRandom(),
getRandom() - getRandom(),
getRandom() - getRandom()) *
13.f,
1.f, .6f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.35f + GetRandom() * GetRandom() * 0.1f);
ent->SetRotation(getRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.35f + getRandom() * getRandom() * 0.1f);
ent->SetLifeTime(2.f, 0.f, 1.f);
if (usePrecisePhysics)
ent->SetBlockHitAction(ParticleSpriteEntity::BounceWeak);

View File

@ -37,12 +37,14 @@ namespace spades {
GameMap::GameMap() {
SPADES_MARK_FUNCTION();
LocalRNG rng;
for (int x = 0; x < DefaultWidth; x++)
for (int y = 0; y < DefaultHeight; y++) {
solidMap[x][y] = 1; // ground only
for (int z = 0; z < DefaultDepth; z++) {
uint32_t col = 0x00284067;
col ^= 0x070707 & static_cast<uint32_t>(mt_engine());
col ^= 0x070707 & static_cast<uint32_t>(rng());
colorMap[x][y][z] = col + (100UL * 0x1000000UL);
}
}

View File

@ -64,9 +64,9 @@ namespace spades {
}
static Vector3 RandomAxis() {
Vector3 v;
v.x = GetRandom() - GetRandom();
v.y = GetRandom() - GetRandom();
v.z = GetRandom() - GetRandom();
v.x = SampleRandomFloat() - SampleRandomFloat();
v.y = SampleRandomFloat() - SampleRandomFloat();
v.z = SampleRandomFloat() - SampleRandomFloat();
return v.Normalize();
}
bool GunCasing::Update(float dt) {
@ -100,7 +100,7 @@ namespace spades {
IAudioDevice *dev = client->GetAudioDevice();
AudioParam param;
param.referenceDistance = .6f;
param.pitch = .9f + GetRandom() * .2f;
param.pitch = .9f + SampleRandomFloat() * .2f;
dev->Play(waterSound, lastMat.GetOrigin(), param);
}
@ -108,7 +108,7 @@ namespace spades {
}
if (dist < 40.f * 40.f) {
int splats = mt_engine_client() % 3;
int splats = SampleRandomInt(0, 2);
Handle<IImage> img = client->GetRenderer()->RegisterImage("Gfx/White.tga");
@ -117,13 +117,15 @@ namespace spades {
pt.z = 62.99f;
for (int i = 0; i < splats; i++) {
ParticleSpriteEntity *ent = new ParticleSpriteEntity(client, img, col);
ent->SetTrajectory(pt, MakeVector3(GetRandom() - GetRandom(),
GetRandom() - GetRandom(),
-GetRandom()) *
ent->SetTrajectory(
pt,
MakeVector3(SampleRandomFloat() - SampleRandomFloat(),
SampleRandomFloat() - SampleRandomFloat(),
-SampleRandomFloat()) *
2.f,
1.f, .4f);
ent->SetRotation(GetRandom() * (float)M_PI * 2.f);
ent->SetRadius(0.1f + GetRandom() * GetRandom() * 0.1f);
ent->SetRotation(SampleRandomFloat() * (float)M_PI * 2.f);
ent->SetRadius(0.1f + SampleRandomFloat() * SampleRandomFloat() * 0.1f);
ent->SetLifeTime(2.f, 0.f, 1.f);
client->AddLocalEntity(ent);
}
@ -194,9 +196,9 @@ namespace spades {
rotAxis = RandomAxis();
Vector3 r;
r.x = GetRandom() - GetRandom();
r.y = GetRandom() - GetRandom();
r.z = GetRandom() - GetRandom();
r.x = SampleRandomFloat() - SampleRandomFloat();
r.y = SampleRandomFloat() - SampleRandomFloat();
r.z = SampleRandomFloat() - SampleRandomFloat();
vel += r * 0.1f;

View File

@ -665,7 +665,7 @@ namespace spades {
visibleLength = shutterTime * velocity;
curDistance = -visibleLength;
curDistance += maxTimeSpread * GetRandom();
curDistance += maxTimeSpread * SampleRandomFloat();
firstUpdate = true;
}

View File

@ -27,9 +27,11 @@ namespace spades {
namespace client {
CoherentNoiseSampler1D::CoherentNoiseSampler1D() {
LocalRNG rng;
values.resize(1024);
for (float &value : values) {
value = GetRandom() - GetRandom();
value = rng.SampleFloat() - rng.SampleFloat();
}
}
@ -54,9 +56,11 @@ namespace spades {
}
GradientCoherentNoiseSampler1D::GradientCoherentNoiseSampler1D() {
LocalRNG rng;
derivatives.resize(1024);
for (float &derivative : derivatives) {
derivative = (GetRandom() - GetRandom()) * 4.f;
derivative = (rng.SampleFloat() - rng.SampleFloat()) * 4.f;
}
}

View File

@ -565,13 +565,15 @@ namespace spades {
// speed hack (shotgun does this)
bool blockDestroyed = false;
LocalRNG rng;
Vector3 dir2 = GetFront();
for (int i = 0; i < pellets; i++) {
// AoS 0.75's way (dir2 shouldn't be normalized!)
dir2.x += (GetRandom() - GetRandom()) * spread;
dir2.y += (GetRandom() - GetRandom()) * spread;
dir2.z += (GetRandom() - GetRandom()) * spread;
dir2.x += (rng.SampleFloat() - rng.SampleFloat()) * spread;
dir2.y += (rng.SampleFloat() - rng.SampleFloat()) * spread;
dir2.z += (rng.SampleFloat() - rng.SampleFloat()) * spread;
Vector3 dir = dir2.Normalize();
bulletVectors.push_back(dir);

View File

@ -25,7 +25,7 @@ namespace spades {
visibleLength = shutterTime * velocity;
curDistance = -visibleLength;
curDistance += maxTimeSpread * GetRandom();
curDistance += maxTimeSpread * SampleRandomFloat();
firstUpdate = true;

View File

@ -20,6 +20,7 @@
#include <new>
#include <cstdlib>
#include <mutex>
#include "Math.h"
#include <Core/Debug.h>
@ -37,11 +38,37 @@ namespace spades {
}
*/
namespace {
std::random_device r_device;
std::mt19937_64
mt_engine(r_device()); // Seed Mersenne twister with non-deterministic 32-bit seed
} // namespace
std::uniform_real_distribution<float> real_dist(0, 1);
ThreadSafeRNG globalThreadSafeRNG;
// Seed Mersenne twister with non-deterministic seed
ThreadSafeRNG::ThreadSafeRNG() : inner{r_device()} {}
auto ThreadSafeRNG::operator()() -> result_type {
std::lock_guard<std::mutex> lock{mutex};
return inner();
}
LocalRNG::LocalRNG() {
do {
s[0] = SampleRandom();
} while (s[0] == 0);
do {
s[1] = SampleRandom();
} while (s[1] == 0);
}
auto LocalRNG::operator()() -> result_type {
uint64_t x = s[0];
uint64_t y = s[1];
s[0] = y;
x ^= x << 23; // a
s[1] = x ^ y ^ (x >> 17) ^ (y >> 26); // b, c
return s[1] + y;
}
void Matrix4Multiply(const float a[16], const float b[16], float out[16]) {
out[0] = b[0] * a[0] + b[1] * a[4] + b[2] * a[8] + b[3] * a[12];
@ -634,7 +661,6 @@ namespace spades {
return (uint32_t)str[start];
}
float GetRandom() { return real_dist(mt_engine); }
float SmoothStep(float v) { return v * v * (3.f - 2.f * v); }
float Mix(float a, float b, float frac) { return a + (b - a) * frac; }

View File

@ -30,9 +30,88 @@
namespace spades {
// Make mt_engine and a real dist [0,1] accesible everywhere in the spades namespace
extern std::mt19937_64 mt_engine;
extern std::uniform_real_distribution<float> real_dist;
#pragma mark - Random number generation
/**
* A thread-safe random number generator satistying
* `UniformRandomBitGenerator`.
*/
class ThreadSafeRNG {
public:
using inner_type = std::mt19937_64;
using result_type = inner_type::result_type;
ThreadSafeRNG();
result_type operator()();
static constexpr result_type min() { return inner_type::min(); }
static constexpr result_type max() { return inner_type::max(); }
private:
inner_type inner;
std::mutex mutex;
};
extern ThreadSafeRNG globalThreadSafeRNG;
/** Generates a random `uint_fast64_t`. This function is thread-safe. */
inline std::uint_fast64_t SampleRandom() { return globalThreadSafeRNG(); }
/** Generates a random `float`. This function is thread-safe. */
inline float SampleRandomFloat() {
return std::uniform_real_distribution<float>{}(globalThreadSafeRNG);
}
/**
* Generates an integer in a specified inclusive range.
* This function is thread-safe.
*/
template <class T = int> inline T SampleRandomInt(T a, T b) {
return std::uniform_int_distribution<T>{a, b}(globalThreadSafeRNG);
}
/** Generates a random `bool`. This function is thread-safe. */
inline bool SampleRandomBool() { return globalThreadSafeRNG() & 0x1; }
/** Get a mutable reference to a random element from a container. */
template <class T> inline typename T::reference SampleRandomElement(T &container) {
auto begin = std::begin(container);
auto end = std::end(container);
auto numElements = std::distance(begin, end);
begin += SampleRandomInt<typeof(numElements)>(0, numElements - 1);
return *begin;
}
/** Get a constant reference to a random element from a container. */
template <class T> inline typename T::const_reference SampleRandomElement(const T &container) {
auto begin = std::begin(container);
auto end = std::end(container);
auto numElements = std::distance(begin, end);
begin += SampleRandomInt(0, numElements - 1);
return *begin;
}
/** Thread-unsafe random number generator. */
class LocalRNG {
public:
using result_type = std::uint64_t;
LocalRNG();
result_type operator()();
float SampleFloat() { return std::uniform_real_distribution<float>{}(*this); }
template <class T = int> inline T SampleInt(T a, T b) {
return std::uniform_int_distribution<T>{a, b}(*this);
}
static constexpr result_type min() { return 0; }
static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
private:
std::uint64_t s[2];
};
#pragma mark - Integer Vector
@ -936,7 +1015,6 @@ namespace spades {
std::string TrimSpaces(const std::string &);
float GetRandom();
float SmoothStep(float);
}

View File

@ -48,8 +48,10 @@ namespace spades {
: renderer(r), device(r->GetGLDevice()), map(m) {
SPADES_MARK_FUNCTION();
LocalRNG rng;
for (int i = 0; i < NumRays; i++) {
Vector3 dir = MakeVector3(GetRandom(), GetRandom(), GetRandom());
Vector3 dir = MakeVector3(rng.SampleFloat(), rng.SampleFloat(), rng.SampleFloat());
dir = dir.Normalize();
dir += 0.01f;
rays[i] = dir;
@ -337,10 +339,11 @@ namespace spades {
}
// limit update count per frame
LocalRNG rng;
for (int i = 0; i < 8; i++) {
if (numDirtyChunks <= 0)
break;
int idx = mt_engine() % numDirtyChunks;
int idx = rng.SampleInt(0, numDirtyChunks - 1);
Chunk &c = chunks[dirtyChunkIds[idx]];
// remove from list (fast)

View File

@ -139,9 +139,10 @@ namespace spades {
void GLLensDustFilter::UpdateNoise() {
SPADES_MARK_FUNCTION();
LocalRNG rng;
noise.resize(128 * 128);
for (size_t i = 0; i < 128 * 128; i++) {
noise[i] = static_cast<std::uint32_t>(mt_engine());
noise[i] = static_cast<std::uint32_t>(rng());
}
IGLDevice *dev = renderer->GetGLDevice();

View File

@ -438,10 +438,11 @@ namespace spades {
}
// limit update count per frame
LocalRNG rng;
for (int i = 0; i < 8; i++) {
if (numDirtyChunks <= 0)
break;
int idx = mt_engine() % numDirtyChunks;
int idx = rng.SampleInt(0, numDirtyChunks - 1);
Chunk &c = chunks[dirtyChunkIds[idx]];
// remove from list (fast)

View File

@ -187,6 +187,9 @@ namespace spades {
public:
FFTWaveTank() : IWaveTank(Size) {
LocalRNG rng;
auto getRandom = [&] { return rng.SampleFloat(); };
fft = kiss_fft_alloc(Size, 1, NULL, NULL);
for (int x = 0; x < Size; x++) {
@ -207,14 +210,14 @@ namespace spades {
mag *= expf(-scal * 3.f);
cell.magnitude = mag;
cell.phase = static_cast<uint32_t>(mt_engine());
cell.phase = static_cast<uint32_t>(rng());
cell.phasePerSecond = dist * 1.e+9f * 128 / Size;
}
cell.m00 = GetRandom() - GetRandom();
cell.m01 = GetRandom() - GetRandom();
cell.m10 = GetRandom() - GetRandom();
cell.m11 = GetRandom() - GetRandom();
cell.m00 = getRandom() - getRandom();
cell.m01 = getRandom() - getRandom();
cell.m10 = getRandom() - getRandom();
cell.m11 = getRandom() - getRandom();
}
}
}
@ -420,12 +423,13 @@ namespace spades {
int count = (int)floorf(dt * 600.f);
if (count > 400)
count = 400;
LocalRNG rng;
for (int i = 0; i < count; i++) {
int ox = mt_engine() % (size - 2);
int oy = mt_engine() % (size - 2);
int ox = rng.SampleInt(0, size - 3);
int oy = rng.SampleInt(0, size - 3);
static const float gauss[] = {0.225610111284052f, 0.548779777431897f,
0.225610111284052f};
float strength = (GetRandom() - GetRandom()) * 0.15f * 100.f;
float strength = (rng.SampleFloat() - rng.SampleFloat()) * 0.15f * 100.f;
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++) {
velocity[(x + ox) + (y + oy) * size] += strength * gauss[x] * gauss[y];

View File

@ -35,24 +35,22 @@ namespace spades {
case 1:
return 0;
case 2:
return mt_engine() & 1;
return SampleRandom() & 1;
case 4:
return mt_engine() & 3;
return SampleRandom() & 3;
case 8:
return mt_engine() & 7;
return SampleRandom() & 7;
case 16:
return mt_engine() & 15;
return SampleRandom() & 15;
case 32:
return mt_engine() & 31;
return SampleRandom() & 31;
case 64:
return mt_engine() & 63;
return SampleRandom() & 63;
}
// We can't directly use the engine here, we need a distribution
// range - 1 because it's inclusive and we want exclusive
std::uniform_int_distribution<unsigned int> int_dist(0, range - 1);
return int_dist(mt_engine);
return SampleRandomInt<unsigned int>(0, range - 1);
}
static unsigned int GetRandomUIntRange(unsigned int a,
unsigned int b){
@ -70,7 +68,7 @@ namespace spades {
return (int)(GetRandomInt((unsigned int)(b - a)) + (unsigned int)a);
}
static float GetRandomFloatRange(float a, float b) {
return GetRandom() * (b - a) + a;
return SampleRandomFloat() * (b - a) + a;
}
public:
MathScriptObjectRegistrar():
@ -995,7 +993,7 @@ namespace spades {
manager->CheckError(r);
r = eng->RegisterGlobalFunction("float GetRandom()",
asFUNCTION(GetRandom),
asFUNCTION(SampleRandomFloat),
asCALL_CDECL);
manager->CheckError(r);