Index: binaries/data/config/default.cfg
===================================================================
--- binaries/data/config/default.cfg
+++ binaries/data/config/default.cfg
@@ -131,6 +131,10 @@
; Quality used for actors.
max_actor_quality=200
+; Distance range over which to apply LOD.
+lod.near = 100
+lod.far = 400
+
; Quality level of shader effects (set to 10 to display all effects)
materialmgr.quality = 2.0
Index: binaries/data/mods/public/art/actors/units/athenians/infantry_spearman_e.xml
===================================================================
--- binaries/data/mods/public/art/actors/units/athenians/infantry_spearman_e.xml
+++ binaries/data/mods/public/art/actors/units/athenians/infantry_spearman_e.xml
@@ -1,215 +1,240 @@
-
-
-
-
- skeletal/new/m_armor_tunic_short.dae
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- player_trans_spec.xml
-
+
+
+
+
+ skeletal/new/m_armor_tunic_short.dae
+
+
+
+
+
+
+
+
+ player_trans.xml
+
+
+
+
+
+
+
+
+ skeletal/new/m_armor_tunic_short.dae
+
+
+
+
+
+ skeletal/new/m_armor_tunic_short.dae
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ player_trans_spec.xml
+
+
Index: source/graphics/ObjectBase.h
===================================================================
--- source/graphics/ObjectBase.h
+++ source/graphics/ObjectBase.h
@@ -236,6 +236,11 @@
*/
const std::shared_ptr& GetBase(u8 QualityLevel) const;
+ /**
+ * Return the iterator for the base matching the given quality level.
+ */
+ std::vector>::const_iterator GetBaseIterator(u8 QualityLevel) const;
+
/**
* Initialise this object by loading from the given file.
* Returns false on error.
Index: source/graphics/ObjectBase.cpp
===================================================================
--- source/graphics/ObjectBase.cpp
+++ source/graphics/ObjectBase.cpp
@@ -761,16 +761,22 @@
const std::shared_ptr& CActorDef::GetBase(u8 QualityLevel) const
{
- for (const std::shared_ptr& base : m_ObjectBases)
- if (base->m_QualityLevel >= QualityLevel)
- return base;
+ // Must return something once the actor is initialised, so dereferencing is safe.
+ return *GetBaseIterator(QualityLevel);
+}
+
+std::vector>::const_iterator CActorDef::GetBaseIterator(u8 QualityLevel) const
+{
+ for (std::vector>::const_iterator it = m_ObjectBases.begin(); it != m_ObjectBases.end(); ++it)
+ if ((*it)->m_QualityLevel >= QualityLevel)
+ return it;
// This code path ought to be impossible to take,
// because by construction we must have at least one valid CObjectBase of quality MAX_QUALITY
// (which necessarily fits the u8 comparison above).
// However compilers will warn that we return a reference to a local temporary if I return nullptr,
// so just return something sane instead.
ENSURE(false);
- return m_ObjectBases.back();
+ return m_ObjectBases.end();
}
bool CActorDef::Load(const VfsPath& pathname)
Index: source/graphics/ObjectManager.h
===================================================================
--- source/graphics/ObjectManager.h
+++ source/graphics/ObjectManager.h
@@ -77,19 +77,20 @@
/**
* Get the object entry for a given actor & the given selections list.
- * @param selections - a possibly incomplete list of selections.
- * @param seed - the randomness seed to use to complete the random selections.
- */
- CObjectEntry* FindObjectVariation(const CActorDef* actor, const std::vector>& selections, uint32_t seed);
-
- /**
- * @see FindObjectVariation.
- * These take a complete selection. These are pointers to sets that are
+ * Takes a "complete selection set". These are pointers to sets that are
* guaranteed to exist (pointers are used to avoid copying the sets).
*/
CObjectEntry* FindObjectVariation(const std::shared_ptr& base, const std::vector*>& completeSelections);
CObjectEntry* FindObjectVariation(const CStrW& objname, const std::vector*>& completeSelections);
+ /**
+ * Return an object entry for each quality level of this actor. Random variations will be shared where possible.
+ * @param selections - a possibly incomplete list of selections.
+ * @param seed - the randomness seed to use to complete the random selections.
+ * @see FindObjectVariation
+ */
+ std::vector FindObjectVariations(const CActorDef& actor, const std::vector>& selections, uint32_t seed);
+
/**
* Get the terrain object that actors managed by this manager should be linked
* with (primarily for the purpose of decals)
Index: source/graphics/ObjectManager.cpp
===================================================================
--- source/graphics/ObjectManager.cpp
+++ source/graphics/ObjectManager.cpp
@@ -92,24 +92,40 @@
return { success, *m_ActorDefs.insert_or_assign(actorName, std::move(actor)).first->second.obj };
}
-CObjectEntry* CObjectManager::FindObjectVariation(const CActorDef* actor, const std::vector>& selections, uint32_t seed)
+std::vector CObjectManager::FindObjectVariations(const CActorDef& actor, const std::vector>& selections, uint32_t seed)
{
- if (!actor)
- return nullptr;
-
- const std::shared_ptr& base = actor->GetBase(m_QualityLevel);
+ std::vector ret;
std::vector*> completeSelections;
for (const std::set& selectionSet : selections)
completeSelections.emplace_back(&selectionSet);
+
+
+ std::vector>::const_iterator it = actor.m_ObjectBases.begin();
+ // Must exist.
+ ENSURE(actor.m_ObjectBases.size() > 0);
+ std::vector>::const_iterator highestQual = --actor.m_ObjectBases.end();
// To maintain a consistent look between quality levels, first complete with the highest-quality variants.
// then complete again at the required quality level (since not all variants may be available).
- std::set highQualitySelections = actor->GetBase(255)->CalculateRandomRemainingSelections(seed, selections);
+ const std::shared_ptr& highQualityBase = *highestQual;
+ std::set highQualitySelections = highQualityBase->CalculateRandomRemainingSelections(seed, selections);
completeSelections.emplace_back(&highQualitySelections);
- // We don't have to pass the high-quality selections here because they have higher priority anyways.
- std::set remainingSelections = base->CalculateRandomRemainingSelections(seed, selections);
- completeSelections.emplace_back(&remainingSelections);
- return FindObjectVariation(base, completeSelections);
+
+ std::vector>::const_iterator maxQuality = actor.GetBaseIterator(m_QualityLevel);
+
+ while (true)
+ {
+ const std::shared_ptr& base = *it;
+ std::set remainingSelections = base->CalculateRandomRemainingSelections(seed, selections);
+ completeSelections.emplace_back(&remainingSelections);
+ CObjectEntry* entry = FindObjectVariation(base, completeSelections);
+ if (entry)
+ ret.push_back(entry);
+ completeSelections.pop_back();
+ if (it++ == maxQuality)
+ break;
+ }
+ return ret;
}
CObjectEntry* CObjectManager::FindObjectVariation(const std::shared_ptr& base, const std::vector*>& completeSelections)
Index: source/graphics/Unit.h
===================================================================
--- source/graphics/Unit.h
+++ source/graphics/Unit.h
@@ -18,12 +18,15 @@
#ifndef INCLUDED_UNIT
#define INCLUDED_UNIT
-#include