diff --git a/Sources/Audio/ALDevice.cpp b/Sources/Audio/ALDevice.cpp index b72594c2..29fc90d7 100644 --- a/Sources/Audio/ALDevice.cpp +++ b/Sources/Audio/ALDevice.cpp @@ -52,13 +52,9 @@ DEFINE_SPADES_SETTING(s_gain, "1"); namespace spades { namespace audio { - std::uniform_real_distribution 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 ConvertFloatBufferToSignedShort(const std::vector &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(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(); } } diff --git a/Sources/Audio/YsrDevice.cpp b/Sources/Audio/YsrDevice.cpp index f31cf0c2..ea1669fa 100644 --- a/Sources/Audio/YsrDevice.cpp +++ b/Sources/Audio/YsrDevice.cpp @@ -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; diff --git a/Sources/Client/Client.cpp b/Sources/Client/Client.cpp index 3fee0659..eebde235 100644 --- a/Sources/Client/Client.cpp +++ b/Sources/Client/Client.cpp @@ -65,11 +65,7 @@ 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)), diff --git a/Sources/Client/Client.h b/Sources/Client/Client.h index 6f646b94..595e1c65 100644 --- a/Sources/Client/Client.h +++ b/Sources/Client/Client.h @@ -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 net; std::string playerName; std::unique_ptr logStream; diff --git a/Sources/Client/ClientPlayer.cpp b/Sources/Client/ClientPlayer.cpp index a644f942..8afaf9ce 100644 --- a/Sources/Client/ClientPlayer.cpp +++ b/Sources/Client/ClientPlayer.cpp @@ -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"); diff --git a/Sources/Client/Client_LocalEnts.cpp b/Sources/Client/Client_LocalEnts.cpp index a10084a0..5fa37adf 100644 --- a/Sources/Client/Client_LocalEnts.cpp +++ b/Sources/Client/Client_LocalEnts.cpp @@ -151,18 +151,19 @@ namespace spades { if ((int)cg_particles < 1) return; - + Handle img = renderer->RegisterImage("Gfx/White.tga"); Vector4 color = {0.5f, 0.02f, 0.04f, 1.f}; 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); } @@ -554,7 +564,7 @@ namespace spades { if ((int)cg_particles < 1) return; - + Vector4 color; color = MakeVector4(.95f, .95f, .95f, .3f); // water1 @@ -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); } diff --git a/Sources/Client/Client_Scene.cpp b/Sources/Client/Client_Scene.cpp index b9658b76..171e76a0 100644 --- a/Sources/Client/Client_Scene.cpp +++ b/Sources/Client/Client_Scene.cpp @@ -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; } diff --git a/Sources/Client/Client_Update.cpp b/Sources/Client/Client_Update.cpp index 83fa0bdc..a5a333b6 100644 --- a/Sources/Client/Client_Update.cpp +++ b/Sources/Client/Client_Update.cpp @@ -524,7 +524,7 @@ namespace spades { lastHurtTime = world->GetTime(); Handle 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(M_PI)); - spr.scale = .2f + GetRandom() * GetRandom() * .7f; - spr.horzShift = GetRandom(); - spr.strength = .3f + GetRandom() * .7f; + spr.angle = SampleRandomFloat() * (2.f * static_cast(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 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 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 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 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 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 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 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 c; - - switch ((mt_engine_client() >> 6) & 3) { - case 0: - case 1: - case 2: - case 3: - c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Block.opus"); - break; - } + c = audioDevice->RegisterSound("Sounds/Weapons/Impacts/Block.opus"); 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 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( diff --git a/Sources/Client/Corpse.cpp b/Sources/Client/Corpse.cpp index 6c34baba..8482e1b8 100644 --- a/Sources/Client/Corpse.cpp +++ b/Sources/Client/Corpse.cpp @@ -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() {} diff --git a/Sources/Client/Corpse.h b/Sources/Client/Corpse.h index 06b9f390..f2c75472 100644 --- a/Sources/Client/Corpse.h +++ b/Sources/Client/Corpse.h @@ -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); diff --git a/Sources/Client/FallingBlock.cpp b/Sources/Client/FallingBlock.cpp index 9e1db063..44f1b408 100644 --- a/Sources/Client/FallingBlock.cpp +++ b/Sources/Client/FallingBlock.cpp @@ -135,6 +135,9 @@ namespace spades { client->grenadeVibration += impact / (dist + 5.f); 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; @@ -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); @@ -215,4 +218,4 @@ namespace spades { client->GetRenderer()->RenderModel(model, param); } } -} \ No newline at end of file +} diff --git a/Sources/Client/GameMap.cpp b/Sources/Client/GameMap.cpp index 9db6bf4e..ce21d91a 100644 --- a/Sources/Client/GameMap.cpp +++ b/Sources/Client/GameMap.cpp @@ -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(mt_engine()); + col ^= 0x070707 & static_cast(rng()); colorMap[x][y][z] = col + (100UL * 0x1000000UL); } } diff --git a/Sources/Client/GunCasing.cpp b/Sources/Client/GunCasing.cpp index 4039a2ac..8095fe76 100644 --- a/Sources/Client/GunCasing.cpp +++ b/Sources/Client/GunCasing.cpp @@ -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 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()) * - 2.f, - 1.f, .4f); - ent->SetRotation(GetRandom() * (float)M_PI * 2.f); - ent->SetRadius(0.1f + GetRandom() * GetRandom() * 0.1f); + ent->SetTrajectory( + pt, + MakeVector3(SampleRandomFloat() - SampleRandomFloat(), + SampleRandomFloat() - SampleRandomFloat(), + -SampleRandomFloat()) * + 2.f, + 1.f, .4f); + 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; diff --git a/Sources/Client/MapView.cpp b/Sources/Client/MapView.cpp index 0ac7cf86..dbd4d9af 100644 --- a/Sources/Client/MapView.cpp +++ b/Sources/Client/MapView.cpp @@ -665,7 +665,7 @@ namespace spades { visibleLength = shutterTime * velocity; curDistance = -visibleLength; - curDistance += maxTimeSpread * GetRandom(); + curDistance += maxTimeSpread * SampleRandomFloat(); firstUpdate = true; } diff --git a/Sources/Client/NoiseSampler.cpp b/Sources/Client/NoiseSampler.cpp index 8658e3ce..17bc9731 100644 --- a/Sources/Client/NoiseSampler.cpp +++ b/Sources/Client/NoiseSampler.cpp @@ -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; } } diff --git a/Sources/Client/Player.cpp b/Sources/Client/Player.cpp index feafc7ce..0578e9e3 100644 --- a/Sources/Client/Player.cpp +++ b/Sources/Client/Player.cpp @@ -564,14 +564,16 @@ namespace spades { // pyspades takes destroying more than one block as a // 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); diff --git a/Sources/Client/Tracer.cpp b/Sources/Client/Tracer.cpp index 7f9d5953..e5656337 100644 --- a/Sources/Client/Tracer.cpp +++ b/Sources/Client/Tracer.cpp @@ -25,7 +25,7 @@ namespace spades { visibleLength = shutterTime * velocity; curDistance = -visibleLength; - curDistance += maxTimeSpread * GetRandom(); + curDistance += maxTimeSpread * SampleRandomFloat(); firstUpdate = true; diff --git a/Sources/Core/Math.cpp b/Sources/Core/Math.cpp index 6998063d..35691adc 100644 --- a/Sources/Core/Math.cpp +++ b/Sources/Core/Math.cpp @@ -20,6 +20,7 @@ #include #include +#include #include "Math.h" #include @@ -37,11 +38,37 @@ namespace spades { } */ - std::random_device r_device; - std::mt19937_64 - mt_engine(r_device()); // Seed Mersenne twister with non-deterministic 32-bit seed + namespace { + std::random_device r_device; + } // namespace - std::uniform_real_distribution 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 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; } diff --git a/Sources/Core/Math.h b/Sources/Core/Math.h index 03d5c3a8..232fb718 100644 --- a/Sources/Core/Math.h +++ b/Sources/Core/Math.h @@ -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 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{}(globalThreadSafeRNG); + } + + /** + * Generates an integer in a specified inclusive range. + * This function is thread-safe. + */ + template inline T SampleRandomInt(T a, T b) { + return std::uniform_int_distribution{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 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(0, numElements - 1); + return *begin; + } + + /** Get a constant reference to a random element from a container. */ + template 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{}(*this); } + + template inline T SampleInt(T a, T b) { + return std::uniform_int_distribution{a, b}(*this); + } + + static constexpr result_type min() { return 0; } + static constexpr result_type max() { return std::numeric_limits::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); } diff --git a/Sources/Draw/GLAmbientShadowRenderer.cpp b/Sources/Draw/GLAmbientShadowRenderer.cpp index 9637c87c..a9af9947 100644 --- a/Sources/Draw/GLAmbientShadowRenderer.cpp +++ b/Sources/Draw/GLAmbientShadowRenderer.cpp @@ -47,9 +47,11 @@ namespace spades { GLAmbientShadowRenderer::GLAmbientShadowRenderer(GLRenderer *r, client::GameMap *m) : 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) diff --git a/Sources/Draw/GLLensDustFilter.cpp b/Sources/Draw/GLLensDustFilter.cpp index c2551e41..ce4a63e7 100644 --- a/Sources/Draw/GLLensDustFilter.cpp +++ b/Sources/Draw/GLLensDustFilter.cpp @@ -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(mt_engine()); + noise[i] = static_cast(rng()); } IGLDevice *dev = renderer->GetGLDevice(); diff --git a/Sources/Draw/GLRadiosityRenderer.cpp b/Sources/Draw/GLRadiosityRenderer.cpp index 2eee0ba9..26d47128 100644 --- a/Sources/Draw/GLRadiosityRenderer.cpp +++ b/Sources/Draw/GLRadiosityRenderer.cpp @@ -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) diff --git a/Sources/Draw/GLWaterRenderer.cpp b/Sources/Draw/GLWaterRenderer.cpp index b7fb5e9a..48db3527 100644 --- a/Sources/Draw/GLWaterRenderer.cpp +++ b/Sources/Draw/GLWaterRenderer.cpp @@ -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(mt_engine()); + cell.phase = static_cast(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]; diff --git a/Sources/ScriptBindings/MathScript.cpp b/Sources/ScriptBindings/MathScript.cpp index 8ddeab88..f153ff58 100644 --- a/Sources/ScriptBindings/MathScript.cpp +++ b/Sources/ScriptBindings/MathScript.cpp @@ -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 int_dist(0, range - 1); - - return int_dist(mt_engine); + return SampleRandomInt(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);