Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #ifndef INCLUDED_OBJECTBASE
19 : #define INCLUDED_OBJECTBASE
20 :
21 : #include "lib/file/vfs/vfs_path.h"
22 : #include "ps/CStr.h"
23 : #include "ps/CStrIntern.h"
24 :
25 : class CActorDef;
26 : class CObjectEntry;
27 : class CObjectManager;
28 : class CXeromyces;
29 : class XMBElement;
30 :
31 : #include <boost/random/mersenne_twister.hpp>
32 : #include <map>
33 : #include <memory>
34 : #include <set>
35 : #include <unordered_set>
36 : #include <vector>
37 :
38 : /**
39 : * Maintains the tree of possible objects from a specific actor definition at a given quality level.
40 : * An Object Base is made of:
41 : * - a material
42 : * - a few properties (float on water / casts shadow / ...)
43 : * - a number of variant groups.
44 : * Any actual object in game will pick a variant from each group (see ObjectEntry).
45 : */
46 0 : class CObjectBase
47 : {
48 : friend CActorDef;
49 :
50 : // See CopyWithQuality() below.
51 : NONCOPYABLE(CObjectBase);
52 : public:
53 0 : struct Anim
54 : {
55 : // constructor
56 0 : Anim() : m_Frequency(0), m_Speed(1.f), m_ActionPos(-1.f), m_ActionPos2(-1.f), m_SoundPos(-1.f) {}
57 : // name of the animation - "Idle", "Run", etc
58 : CStr m_AnimName;
59 : // ID of the animation: if not empty, something specific to sync with props.
60 : CStr m_ID = "";
61 : int m_Frequency;
62 : // filename of the animation - manidle.psa, manrun.psa, etc
63 : VfsPath m_FileName;
64 : // animation speed, as specified in XML actor file
65 : float m_Speed;
66 : // fraction [0.0, 1.0] of the way through the animation that the interesting bit(s)
67 : // happens, or -1.0 if unspecified
68 : float m_ActionPos;
69 : float m_ActionPos2;
70 : float m_SoundPos;
71 : };
72 :
73 0 : struct Prop
74 : {
75 : // constructor
76 0 : Prop() : m_minHeight(0.f), m_maxHeight(0.f), m_selectable(true) {}
77 : // name of the prop point to attach to - "Prop01", "Prop02", "Head", "LeftHand", etc ..
78 : CStr m_PropPointName;
79 : // name of the model file - art/actors/props/sword.xml or whatever
80 : CStrW m_ModelName;
81 : // allow the prop to ajust the height from minHeight to maxHeight relative to the main model
82 : float m_minHeight;
83 : float m_maxHeight;
84 : bool m_selectable;
85 : };
86 :
87 0 : struct Samp
88 : {
89 : // identifier name of sampler in GLSL shaders
90 : CStrIntern m_SamplerName;
91 : // path to load from
92 : VfsPath m_SamplerFile;
93 : };
94 :
95 : struct Decal
96 : {
97 0 : Decal() : m_SizeX(0.f), m_SizeZ(0.f), m_Angle(0.f), m_OffsetX(0.f), m_OffsetZ(0.f) {}
98 :
99 : float m_SizeX;
100 : float m_SizeZ;
101 : float m_Angle;
102 : float m_OffsetX;
103 : float m_OffsetZ;
104 : };
105 :
106 0 : struct Variant
107 : {
108 0 : Variant() : m_Frequency(0) {}
109 :
110 : CStr m_VariantName; // lowercase name
111 : int m_Frequency;
112 : VfsPath m_ModelFilename;
113 : Decal m_Decal;
114 : VfsPath m_Particles;
115 : CStr m_Color;
116 :
117 : std::vector<Anim> m_Anims;
118 : std::vector<Prop> m_Props;
119 : std::vector<Samp> m_Samplers;
120 : };
121 :
122 0 : struct Variation
123 : {
124 : VfsPath model;
125 : Decal decal;
126 : VfsPath particles;
127 : CStr color;
128 : std::multimap<CStr, Prop> props;
129 : std::multimap<CStr, Anim> anims;
130 : std::multimap<CStr, Samp> samplers;
131 : };
132 :
133 : CObjectBase(CObjectManager& objectManager, CActorDef& actorDef, u8 QualityLevel);
134 :
135 : // Returns a set of selection such that, added to initialSelections, CalculateVariationKey can proceed.
136 : std::set<CStr> CalculateRandomRemainingSelections(uint32_t seed, const std::vector<std::set<CStr>>& initialSelections) const;
137 :
138 : // Get the variation key (indices of chosen variants from each group)
139 : // based on the selection strings.
140 : // Should not have to make a random choice: the selections should be complete.
141 : std::vector<u8> CalculateVariationKey(const std::vector<const std::set<CStr>*>& selections) const;
142 :
143 : // Get the final actor data, combining all selected variants
144 : const Variation BuildVariation(const std::vector<u8>& variationKey) const;
145 :
146 : // Get a list of variant groups for this object, plus for all possible
147 : // props. Duplicated groups are removed, if several props share the same
148 : // variant names.
149 : std::vector<std::vector<CStr> > GetVariantGroups() const;
150 :
151 : // Return a string identifying this actor uniquely (includes quality level information);
152 : const CStr& GetIdentifier() const;
153 :
154 : /**
155 : * Returns whether this object (including any possible props)
156 : * uses the given file. (This is used for hotloading.)
157 : */
158 : bool UsesFile(const VfsPath& pathname) const;
159 :
160 :
161 : struct {
162 : // cast shadows from this object
163 : bool m_CastShadows;
164 : // float on top of water
165 : bool m_FloatOnWater;
166 : } m_Properties;
167 :
168 : // the material file
169 : VfsPath m_Material;
170 :
171 : // Quality level - part of the data resource path.
172 : u8 m_QualityLevel;
173 :
174 : private:
175 : // Private interface for CActorDef/ObjectEntry
176 :
177 : /**
178 : * Acts as an explicit copy constructor, for a new quality level.
179 : * Note that this does not reload the actor, so this setting will only change props.
180 : */
181 : std::unique_ptr<CObjectBase> CopyWithQuality(u8 newQualityLevel) const;
182 :
183 : // A low-quality RNG like rand48 causes visible non-random patterns (particularly
184 : // in large grids of the same actor with consecutive seeds, e.g. forests),
185 : // so use a better one that appears to avoid those patterns
186 : using rng_t = boost::mt19937;
187 : std::set<CStr> CalculateRandomRemainingSelections(rng_t& rng, const std::vector<std::set<CStr>>& initialSelections) const;
188 :
189 : /**
190 : * Get all quality levels at which this object changes (includes props).
191 : * Intended to be called by CActorFef.
192 : * @param splits - a sorted vector of unique quality splits.
193 : */
194 : void GetQualitySplits(std::vector<u8>& splits) const;
195 :
196 : [[nodiscard]] bool Load(const CXeromyces& XeroFile, const XMBElement& base);
197 : [[nodiscard]] bool LoadVariant(const CXeromyces& XeroFile, const XMBElement& variant, Variant& currentVariant);
198 :
199 : private:
200 : // Backref to the owning actor.
201 : CActorDef& m_ActorDef;
202 :
203 : // Used to identify this actor uniquely in the ObjectManager (and for debug).
204 : CStr m_Identifier;
205 :
206 : std::vector< std::vector<Variant> > m_VariantGroups;
207 : CObjectManager& m_ObjectManager;
208 : };
209 :
210 : /**
211 : * Represents an actor file. Actors can contain various quality levels.
212 : * An ActorDef maintains a CObjectBase for each specified quality level, and provides access to it.
213 : */
214 0 : class CActorDef
215 : {
216 : // Friend these three so they can use GetBase.
217 : friend class CObjectManager;
218 : friend class CObjectBase;
219 : friend class CObjectEntry;
220 :
221 : NONCOPYABLE(CActorDef);
222 : public:
223 :
224 : CActorDef(CObjectManager& objectManager);
225 :
226 : std::vector<u8> QualityLevels() const;
227 :
228 0 : VfsPath GetPathname() const { return m_Pathname; }
229 :
230 : /**
231 : * Return a list of selections specifying a particular variant in all groups, based on the seed.
232 : */
233 : std::set<CStr> PickSelectionsAtRandom(uint32_t seed) const;
234 :
235 : // Interface accessible from CObjectManager / CObjectBase
236 : protected:
237 : /**
238 : * Return the Object base matching the given quality level.
239 : */
240 : const std::shared_ptr<CObjectBase>& GetBase(u8 QualityLevel) const;
241 :
242 : /**
243 : * Initialise this object by loading from the given file.
244 : * Returns false on error.
245 : */
246 : bool Load(const VfsPath& pathname);
247 :
248 : /**
249 : * Initialise this object with a default placeholder actor,
250 : * pretending to be the actor at pathname.
251 : */
252 : void LoadErrorPlaceholder(const VfsPath& pathname);
253 :
254 : /**
255 : * Returns whether this actor (including any possible props)
256 : * uses the given file. (This is used for hotloading.)
257 : */
258 : bool UsesFile(const VfsPath& pathname) const;
259 :
260 : // filename that this was loaded from
261 : VfsPath m_Pathname;
262 :
263 : private:
264 : CObjectManager& m_ObjectManager;
265 :
266 : // std::shared_ptr to avoid issues during hotloading.
267 : std::vector<std::shared_ptr<CObjectBase>> m_ObjectBases;
268 :
269 : std::unordered_set<VfsPath> m_UsedFiles;
270 : };
271 :
272 : #endif
|