mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
Implement quality levels for actors & corresponding setting.
An actor file, as referenced by the VisualActor, can now define different actors for different "quality level" setting. In this initial version, the quality is handled directly by the object manager. Actor format impact: - '<qualitylevels>' may be used as the root node, containing actor nodes as children. - such actor nodes can refer to a file, or to an inline actor, or simply be inlined. - such actor nodes may have a 'quality' attribute, specifying the maximum quality level of this actor. By default, 255 (the maximum) is implied. - The actor format remains valid, but 'groups', 'variants', 'material', 'castshadow' and 'float' can be given a [minquality, maxquality[ range via XML attributes. Outside of this range, the XML node is ignored (making it possible to define, in a single actor file, several quality levels). Quality is a 0-255 value, with: - Range 0-99 intended for lower level-of-detail actors (billboards, etc.) - Range 100-200 the 'normal' range for models. 100 is "low", 150 "medium", and 200 "high". - Range 201-255 used for higher quality actors that might be used for e.g. cinematics. The range is wide to make it easier to add intermediate levels in the future and it seemed easier given that an integer value of some kind was required anyways. Engine impacts: - A new CActorDef class is introduced, wrapping an art/actors XML file and its different quality levels. ObjectBase remains the definition of a given 'actor', now at a given quality level. - CActorDef imposes a maximal # of quality level for a particular actor definition (5 currently). - CUnit is made to refer to an Actor Definition explicitly, not a particular ObjectBase. - As a minor optimisation, variation keys are calculated on pointer-to-sets-of-selections, instead of raw sets-of-selections, as this reduces copying. - some refactoring, including better const-correctness and hotloading support via std::shared_ptr. Differential Revision: https://code.wildfiregames.com/D3787 This was SVN commit r25210.
This commit is contained in:
+114
-43
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2019 Wildfire Games.
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -22,7 +22,9 @@
|
||||
#include "ps/CStr.h"
|
||||
#include "ps/CStrIntern.h"
|
||||
|
||||
class CActorDef;
|
||||
class CModel;
|
||||
class CObjectEntry;
|
||||
class CObjectManager;
|
||||
class CSkeletonAnim;
|
||||
class CXeromyces;
|
||||
@@ -30,12 +32,24 @@ class XMBElement;
|
||||
|
||||
#include <boost/random/mersenne_twister.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Maintains the tree of possible objects from a specific actor definition at a given quality level.
|
||||
* An Object Base is made of:
|
||||
* - a material
|
||||
* - a few properties (float on water / casts shadow / ...)
|
||||
* - a number of variant groups.
|
||||
* Any actual object in game will pick a variant from each group (see ObjectEntry).
|
||||
*/
|
||||
class CObjectBase
|
||||
{
|
||||
friend CActorDef;
|
||||
|
||||
// See CopyWithQuality() below.
|
||||
NONCOPYABLE(CObjectBase);
|
||||
public:
|
||||
struct Anim
|
||||
@@ -118,33 +132,110 @@ public:
|
||||
std::multimap<CStr, Samp> samplers;
|
||||
};
|
||||
|
||||
CObjectBase(CObjectManager& objectManager);
|
||||
CObjectBase(CObjectManager& objectManager, CActorDef& actorDef, u8 QualityLevel);
|
||||
|
||||
// Returns a set of selection such that, added to initialSelections, CalculateVariationKey can proceed.
|
||||
std::set<CStr> CalculateRandomRemainingSelections(uint32_t seed, const std::vector<std::set<CStr>>& initialSelections) const;
|
||||
|
||||
// Get the variation key (indices of chosen variants from each group)
|
||||
// based on the selection strings
|
||||
std::vector<u8> CalculateVariationKey(const std::vector<std::set<CStr> >& selections);
|
||||
// based on the selection strings.
|
||||
// Should not have to make a random choice: the selections should be complete.
|
||||
std::vector<u8> CalculateVariationKey(const std::vector<const std::set<CStr>*>& selections) const;
|
||||
|
||||
// Get the final actor data, combining all selected variants
|
||||
const Variation BuildVariation(const std::vector<u8>& variationKey);
|
||||
|
||||
// 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<CStr> CalculateRandomVariation(uint32_t seed, const std::set<CStr>& initialSelections);
|
||||
|
||||
// Given a prioritized vector of selection string sets that partially specify
|
||||
// a variation, calculates a remaining set of selection strings such that the resulting
|
||||
// set merged with the initial selections fully specifies an exact variation of
|
||||
// the actor. The resulting selections are selected randomly, but only where a choice
|
||||
// is necessary (i.e. where there are multiple variants but the initial selections,
|
||||
// applied in priority order, fail to select one).
|
||||
std::set<CStr> CalculateRandomRemainingSelections(uint32_t seed, const std::vector<std::set<CStr> >& initialSelections);
|
||||
const Variation BuildVariation(const std::vector<u8>& variationKey) const;
|
||||
|
||||
// Get a list of variant groups for this object, plus for all possible
|
||||
// props. Duplicated groups are removed, if several props share the same
|
||||
// variant names.
|
||||
std::vector<std::vector<CStr> > GetVariantGroups() const;
|
||||
|
||||
// Return a string identifying this actor uniquely (includes quality level information);
|
||||
const CStr& GetIdentifier() const;
|
||||
|
||||
/**
|
||||
* Returns whether this object (including any possible props)
|
||||
* uses the given file. (This is used for hotloading.)
|
||||
*/
|
||||
bool UsesFile(const VfsPath& pathname) const;
|
||||
|
||||
|
||||
struct {
|
||||
// cast shadows from this object
|
||||
bool m_CastShadows;
|
||||
// float on top of water
|
||||
bool m_FloatOnWater;
|
||||
} m_Properties;
|
||||
|
||||
// the material file
|
||||
VfsPath m_Material;
|
||||
|
||||
// Quality level - part of the data resource path.
|
||||
u8 m_QualityLevel;
|
||||
|
||||
private:
|
||||
// Private interface for CActorDef/ObjectEntry
|
||||
|
||||
/**
|
||||
* Acts as an explicit copy constructor, for a new quality level.
|
||||
* Note that this does not reload the actor, so this setting will only change props.
|
||||
*/
|
||||
std::unique_ptr<CObjectBase> CopyWithQuality(u8 newQualityLevel) const;
|
||||
|
||||
// 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
|
||||
using rng_t = boost::mt19937;
|
||||
std::set<CStr> CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr>>& initialSelections) const;
|
||||
|
||||
/**
|
||||
* Get all quality levels at which this object changes (includes props).
|
||||
* Intended to be called by CActorFef.
|
||||
* @param splits - a sorted vector of unique quality splits.
|
||||
*/
|
||||
void GetQualitySplits(std::vector<u8>& splits) const;
|
||||
|
||||
void Load(const CXeromyces& XeroFile, const XMBElement& base);
|
||||
void LoadVariant(const CXeromyces& XeroFile, const XMBElement& variant, Variant& currentVariant);
|
||||
|
||||
private:
|
||||
// Backref to the owning actor.
|
||||
CActorDef& m_ActorDef;
|
||||
|
||||
// Used to identify this actor uniquely in the ObjectManager (and for debug).
|
||||
CStr m_Identifier;
|
||||
|
||||
std::vector< std::vector<Variant> > m_VariantGroups;
|
||||
CObjectManager& m_ObjectManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an actor file. Actors can contain various quality levels.
|
||||
* An ActorDef maintains a CObjectBase for each specified quality level, and provides access to it.
|
||||
*/
|
||||
class CActorDef
|
||||
{
|
||||
// Friend these three so they can use GetBase.
|
||||
friend class CObjectManager;
|
||||
friend class CObjectBase;
|
||||
friend class CObjectEntry;
|
||||
|
||||
NONCOPYABLE(CActorDef);
|
||||
public:
|
||||
|
||||
CActorDef(CObjectManager& objectManager);
|
||||
|
||||
std::vector<u8> QualityLevels() const;
|
||||
|
||||
VfsPath GetPathname() const { return m_Pathname; }
|
||||
|
||||
// Interface accessible from CObjectManager / CObjectBase
|
||||
protected:
|
||||
/**
|
||||
* Return the Object base matching the given quality level.
|
||||
*/
|
||||
const std::shared_ptr<CObjectBase>& GetBase(u8 QualityLevel) const;
|
||||
|
||||
/**
|
||||
* Initialise this object by loading from the given file.
|
||||
* Returns false on error.
|
||||
@@ -158,41 +249,21 @@ public:
|
||||
bool Reload();
|
||||
|
||||
/**
|
||||
* Returns whether this object (including any possible props)
|
||||
* Returns whether this actor (including any possible props)
|
||||
* uses the given file. (This is used for hotloading.)
|
||||
*/
|
||||
bool UsesFile(const VfsPath& pathname);
|
||||
bool UsesFile(const VfsPath& pathname) const;
|
||||
|
||||
// filename that this was loaded from
|
||||
VfsPath m_Pathname;
|
||||
|
||||
// short human-readable name
|
||||
CStrW m_ShortName;
|
||||
|
||||
struct {
|
||||
// cast shadows from this object
|
||||
bool m_CastShadows;
|
||||
// float on top of water
|
||||
bool m_FloatOnWater;
|
||||
} m_Properties;
|
||||
|
||||
// the material file
|
||||
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
|
||||
using rng_t = boost::mt19937;
|
||||
|
||||
std::set<CStr> CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr> >& initialSelections);
|
||||
|
||||
std::vector< std::vector<Variant> > m_VariantGroups;
|
||||
CObjectManager& m_ObjectManager;
|
||||
|
||||
std::unordered_set<VfsPath> m_UsedFiles;
|
||||
// std::shared_ptr to avoid issues during hotloading.
|
||||
std::vector<std::shared_ptr<CObjectBase>> m_ObjectBases;
|
||||
|
||||
void LoadVariant(const CXeromyces& XeroFile, const XMBElement& variant, Variant& currentVariant);
|
||||
std::unordered_set<VfsPath> m_UsedFiles;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user