From 1a072a3f376b03f39377a585cb3b1a55da071769 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Sat, 2 Apr 2011 12:51:42 +0000 Subject: [PATCH] Use a predictable RNG seed for random actor variations, so they are consistent between runs of the game. This was SVN commit r9143. --- source/graphics/ObjectBase.cpp | 19 ++++++++++++------- source/graphics/ObjectBase.h | 11 ++++++++++- source/graphics/Unit.cpp | 4 ++-- source/graphics/Unit.h | 5 +++-- source/graphics/UnitManager.cpp | 4 ++-- source/graphics/UnitManager.h | 2 +- .../components/CCmpProjectileManager.cpp | 5 ++++- .../components/CCmpVisualActor.cpp | 9 +++++++-- 8 files changed, 41 insertions(+), 18 deletions(-) diff --git a/source/graphics/ObjectBase.cpp b/source/graphics/ObjectBase.cpp index 09477fa221..d91bea21cf 100644 --- a/source/graphics/ObjectBase.cpp +++ b/source/graphics/ObjectBase.cpp @@ -27,9 +27,9 @@ #include "ps/Filesystem.h" #include "ps/CLogger.h" #include "lib/timer.h" -#include "lib/rand.h" #include "maths/MathUtil.h" +#include CObjectBase::CObjectBase(CObjectManager& objectManager) : m_ObjectManager(objectManager) @@ -411,7 +411,14 @@ const CObjectBase::Variation CObjectBase::BuildVariation(const std::vector& return variation; } -std::set CObjectBase::CalculateRandomVariation(const std::set& initialSelections) +std::set CObjectBase::CalculateRandomVariation(uint32_t seed, const std::set& initialSelections) +{ + rng_t rng; + rng.seed(seed); + return CalculateRandomVariation(rng, initialSelections); +} + +std::set CObjectBase::CalculateRandomVariation(rng_t& rng, const std::set& initialSelections) { std::set selections = initialSelections; @@ -471,10 +478,8 @@ std::set CObjectBase::CalculateRandomVariation(const std::set& initi bool allZero = (totalFreq == 0); if (allZero) totalFreq = (int)grp->size(); - // Choose a random number in the interval [0..totalFreq). - // (It shouldn't be necessary to use a network-synchronised RNG, - // since actors are meant to have purely visual manifestations.) - int randNum = (int)rand(0, (size_t)totalFreq); + // Choose a random number in the interval [0..totalFreq) + int randNum = boost::uniform_int<>(0, totalFreq-1)(rng); // and use that to choose one of the variants for (size_t i = 0; i < grp->size(); ++i) @@ -518,7 +523,7 @@ std::set CObjectBase::CalculateRandomVariation(const std::set& initi CObjectBase* prop = m_ObjectManager.FindObjectBase(it->second); if (prop) { - std::set propSelections = prop->CalculateRandomVariation(selections); + std::set propSelections = prop->CalculateRandomVariation(rng, selections); // selections = union(propSelections, selections) std::set newSelections; std::set_union(propSelections.begin(), propSelections.end(), diff --git a/source/graphics/ObjectBase.h b/source/graphics/ObjectBase.h index 956b872462..c6bd87adca 100644 --- a/source/graphics/ObjectBase.h +++ b/source/graphics/ObjectBase.h @@ -28,6 +28,8 @@ class CObjectManager; #include "lib/file/vfs/vfs_path.h" #include "ps/CStr.h" +#include + class CObjectBase { NONCOPYABLE(CObjectBase); @@ -106,7 +108,7 @@ public: // Get a set of selection strings that are complete enough to specify an // exact variation of the actor, using the initial selections wherever possible // and choosing randomly where a choice is necessary. - std::set CalculateRandomVariation(const std::set& initialSelections); + std::set CalculateRandomVariation(uint32_t seed, const std::set& initialSelections); // Get a list of variant groups for this object, plus for all possible // props. Duplicated groups are removed, if several props share the same @@ -144,6 +146,13 @@ public: VfsPath m_Material; private: + // A low-quality RNG like rand48 causes visible non-random patterns (particularly + // in large grids of the same actor with consecutive seeds, e.g. forests), + // so use a better one that appears to avoid those patterns + typedef boost::mt19937 rng_t; + + std::set CalculateRandomVariation(rng_t& rng, const std::set& initialSelections); + std::vector< std::vector > m_VariantGroups; CObjectManager& m_ObjectManager; }; diff --git a/source/graphics/Unit.cpp b/source/graphics/Unit.cpp index a20d438506..cf0c502bc1 100644 --- a/source/graphics/Unit.cpp +++ b/source/graphics/Unit.cpp @@ -44,14 +44,14 @@ CUnit::~CUnit() delete m_Model; } -CUnit* CUnit::Create(const CStrW& actorName, const std::set& selections, CObjectManager& objectManager) +CUnit* CUnit::Create(const CStrW& actorName, uint32_t seed, const std::set& selections, CObjectManager& objectManager) { CObjectBase* base = objectManager.FindObjectBase(actorName); if (! base) return NULL; - std::set actorSelections = base->CalculateRandomVariation(selections); + std::set actorSelections = base->CalculateRandomVariation(seed, selections); std::vector > selectionsVec; selectionsVec.push_back(actorSelections); diff --git a/source/graphics/Unit.h b/source/graphics/Unit.h index ddec350470..b31457ac95 100644 --- a/source/graphics/Unit.h +++ b/source/graphics/Unit.h @@ -42,9 +42,10 @@ private: public: // Attempt to create a unit with the given actor, with a set of - // suggested selections (with the rest being randomised). + // suggested selections (with the rest being randomised using the + // given random seed). // Returns NULL on failure. - static CUnit* Create(const CStrW& actorName, const std::set& selections, CObjectManager& objectManager); + static CUnit* Create(const CStrW& actorName, uint32_t seed, const std::set& selections, CObjectManager& objectManager); // destructor ~CUnit(); diff --git a/source/graphics/UnitManager.cpp b/source/graphics/UnitManager.cpp index 7a5ae0b2ff..aa4abce789 100644 --- a/source/graphics/UnitManager.cpp +++ b/source/graphics/UnitManager.cpp @@ -125,12 +125,12 @@ CUnit* CUnitManager::PickUnit(const CVector3D& origin, const CVector3D& dir) con /////////////////////////////////////////////////////////////////////////////// // CreateUnit: create a new unit and add it to the world -CUnit* CUnitManager::CreateUnit(const CStrW& actorName, const std::set& selections) +CUnit* CUnitManager::CreateUnit(const CStrW& actorName, uint32_t seed, const std::set& selections) { if (! m_ObjectManager) return NULL; - CUnit* unit = CUnit::Create(actorName, selections, *m_ObjectManager); + CUnit* unit = CUnit::Create(actorName, seed, selections, *m_ObjectManager); if (unit) AddUnit(unit); return unit; diff --git a/source/graphics/UnitManager.h b/source/graphics/UnitManager.h index 1e220a3ec9..7bbcc7c4b8 100644 --- a/source/graphics/UnitManager.h +++ b/source/graphics/UnitManager.h @@ -50,7 +50,7 @@ public: void DeleteAll(); // creates a new unit and adds it to the world - CUnit* CreateUnit(const CStrW& actorName, const std::set& selections); + CUnit* CreateUnit(const CStrW& actorName, uint32_t seed, const std::set& selections); // return the units const std::vector& GetUnits() const { return m_Units; } diff --git a/source/simulation2/components/CCmpProjectileManager.cpp b/source/simulation2/components/CCmpProjectileManager.cpp index e640359a90..fd5a8af4fa 100644 --- a/source/simulation2/components/CCmpProjectileManager.cpp +++ b/source/simulation2/components/CCmpProjectileManager.cpp @@ -57,6 +57,7 @@ public: virtual void Init(const CParamNode& UNUSED(paramNode)) { + m_ActorSeed = 0; } virtual void Deinit() @@ -119,6 +120,8 @@ private: std::vector m_Projectiles; + uint32_t m_ActorSeed; + void LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, entity_id_t targetEnt, fixed speed, fixed gravity); void AdvanceProjectile(Projectile& projectile, float dt, float frameOffset); @@ -151,7 +154,7 @@ void CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D std::set selections; Projectile projectile; - projectile.unit = GetSimContext().GetUnitManager().CreateUnit(name, selections); + projectile.unit = GetSimContext().GetUnitManager().CreateUnit(name, m_ActorSeed++, selections); if (!projectile.unit) { // The error will have already been logged diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp index 08c78f4caf..de442b9d20 100644 --- a/source/simulation2/components/CCmpVisualActor.cpp +++ b/source/simulation2/components/CCmpVisualActor.cpp @@ -117,7 +117,7 @@ public: m_R = m_G = m_B = fixed::FromInt(1); std::set selections; - m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, selections); + m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections); if (!m_Unit) { // The error will have already been logged @@ -336,7 +336,7 @@ public: return; std::set selections; - CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, selections); + CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections); if (!newUnit) return; @@ -368,6 +368,11 @@ public: } private: + int32_t GetActorSeed() + { + return GetEntityId(); + } + void Update(fixed turnLength); void Interpolate(float frameTime, float frameOffset); void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);