Changeset View
Changeset View
Standalone View
Standalone View
source/graphics/ObjectBase.h
/* Copyright (C) 2019 Wildfire Games. | /* Copyright (C) 2021 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#ifndef INCLUDED_OBJECTBASE | #ifndef INCLUDED_OBJECTBASE | ||||
#define INCLUDED_OBJECTBASE | #define INCLUDED_OBJECTBASE | ||||
#include "lib/file/vfs/vfs_path.h" | #include "lib/file/vfs/vfs_path.h" | ||||
#include "ps/CStr.h" | #include "ps/CStr.h" | ||||
#include "ps/CStrIntern.h" | #include "ps/CStrIntern.h" | ||||
class CActorDef; | |||||
class CModel; | class CModel; | ||||
class CObjectEntry; | |||||
class CObjectManager; | class CObjectManager; | ||||
class CSkeletonAnim; | class CSkeletonAnim; | ||||
class CXeromyces; | class CXeromyces; | ||||
class XMBElement; | class XMBElement; | ||||
#include <boost/random/mersenne_twister.hpp> | #include <boost/random/mersenne_twister.hpp> | ||||
#include <map> | #include <map> | ||||
#include <memory> | |||||
#include <set> | #include <set> | ||||
#include <unordered_set> | #include <unordered_set> | ||||
#include <vector> | #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 | class CObjectBase | ||||
{ | { | ||||
friend CActorDef; | |||||
// See CopyWithQuality() below. | |||||
NONCOPYABLE(CObjectBase); | NONCOPYABLE(CObjectBase); | ||||
public: | public: | ||||
struct Anim | struct Anim | ||||
{ | { | ||||
// constructor | // constructor | ||||
Anim() : m_Frequency(0), m_Speed(1.f), m_ActionPos(-1.f), m_ActionPos2(-1.f), m_SoundPos(-1.f) {} | Anim() : m_Frequency(0), m_Speed(1.f), m_ActionPos(-1.f), m_ActionPos2(-1.f), m_SoundPos(-1.f) {} | ||||
// name of the animation - "Idle", "Run", etc | // name of the animation - "Idle", "Run", etc | ||||
CStr m_AnimName; | CStr m_AnimName; | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | struct Variation | ||||
Decal decal; | Decal decal; | ||||
VfsPath particles; | VfsPath particles; | ||||
CStr color; | CStr color; | ||||
std::multimap<CStr, Prop> props; | std::multimap<CStr, Prop> props; | ||||
std::multimap<CStr, Anim> anims; | std::multimap<CStr, Anim> anims; | ||||
std::multimap<CStr, Samp> samplers; | 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; | |||||
Stan: Needs to be ordered? I suppose yes? | |||||
Done Inline ActionsDoesn't really, actually, but everything is std::set currently and I just reused that. Can be done in another diff. wraitii: Doesn't really, actually, but everything is std::set currently and I just reused that. Can be… | |||||
// Get the variation key (indices of chosen variants from each group) | // Get the variation key (indices of chosen variants from each group) | ||||
// based on the selection strings | // based on the selection strings. | ||||
std::vector<u8> CalculateVariationKey(const std::vector<std::set<CStr> >& selections); | // 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 | // Get the final actor data, combining all selected variants | ||||
const Variation BuildVariation(const std::vector<u8>& variationKey); | const Variation BuildVariation(const std::vector<u8>& variationKey) const; | ||||
// 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); | |||||
// Get a list of variant groups for this object, plus for all possible | // Get a list of variant groups for this object, plus for all possible | ||||
// props. Duplicated groups are removed, if several props share the same | // props. Duplicated groups are removed, if several props share the same | ||||
// variant names. | // variant names. | ||||
std::vector<std::vector<CStr> > GetVariantGroups() const; | std::vector<std::vector<CStr> > GetVariantGroups() const; | ||||
/** | // Return a string identifying this actor uniquely (includes quality level information); | ||||
* Initialise this object by loading from the given file. | const CStr& GetIdentifier() const; | ||||
* Returns false on error. | |||||
*/ | |||||
bool Load(const VfsPath& pathname); | |||||
/** | |||||
* Reload this object from the file that it was previously loaded from. | |||||
* Returns false on error. | |||||
*/ | |||||
bool Reload(); | |||||
/** | /** | ||||
* Returns whether this object (including any possible props) | * Returns whether this object (including any possible props) | ||||
* uses the given file. (This is used for hotloading.) | * 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 { | struct { | ||||
// cast shadows from this object | // cast shadows from this object | ||||
bool m_CastShadows; | bool m_CastShadows; | ||||
// float on top of water | // float on top of water | ||||
bool m_FloatOnWater; | bool m_FloatOnWater; | ||||
} m_Properties; | } m_Properties; | ||||
// the material file | // the material file | ||||
VfsPath m_Material; | VfsPath m_Material; | ||||
// Quality level - part of the data resource path. | |||||
u8 m_QualityLevel; | |||||
private: | 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; | |||||
Done Inline Actions#include memory Stan: #include memory | |||||
// A low-quality RNG like rand48 causes visible non-random patterns (particularly | // 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), | // in large grids of the same actor with consecutive seeds, e.g. forests), | ||||
// so use a better one that appears to avoid those patterns | // so use a better one that appears to avoid those patterns | ||||
using rng_t = boost::mt19937; | using rng_t = boost::mt19937; | ||||
std::set<CStr> CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr>>& initialSelections) const; | |||||
std::set<CStr> CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr> >& initialSelections); | /** | ||||
* 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; | std::vector< std::vector<Variant> > m_VariantGroups; | ||||
CObjectManager& m_ObjectManager; | CObjectManager& m_ObjectManager; | ||||
}; | |||||
std::unordered_set<VfsPath> m_UsedFiles; | /** | ||||
* 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; | |||||
void LoadVariant(const CXeromyces& XeroFile, const XMBElement& variant, Variant& currentVariant); | 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. | |||||
*/ | |||||
bool Load(const VfsPath& pathname); | |||||
/** | |||||
* Reload this object from the file that it was previously loaded from. | |||||
* Returns false on error. | |||||
*/ | |||||
bool Reload(); | |||||
/** | |||||
* Returns whether this actor (including any possible props) | |||||
* uses the given file. (This is used for hotloading.) | |||||
*/ | |||||
bool UsesFile(const VfsPath& pathname) const; | |||||
// filename that this was loaded from | |||||
VfsPath m_Pathname; | |||||
private: | |||||
CObjectManager& m_ObjectManager; | |||||
// std::shared_ptr to avoid issues during hotloading. | |||||
std::vector<std::shared_ptr<CObjectBase>> m_ObjectBases; | |||||
std::unordered_set<VfsPath> m_UsedFiles; | |||||
}; | }; | ||||
#endif | #endif |
Wildfire Games · Phabricator
Needs to be ordered? I suppose yes?