File: ObjectBase.h

package info (click to toggle)
0ad 0.0.26-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 130,460 kB
  • sloc: cpp: 261,824; ansic: 198,392; javascript: 19,067; python: 14,557; sh: 7,629; perl: 4,072; xml: 849; makefile: 741; java: 533; ruby: 229; php: 190; pascal: 30; sql: 21; tcl: 4
file content (272 lines) | stat: -rw-r--r-- 8,013 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/* 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
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * 0 A.D. is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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/>.
 */

#ifndef INCLUDED_OBJECTBASE
#define INCLUDED_OBJECTBASE

#include "lib/file/vfs/vfs_path.h"
#include "ps/CStr.h"
#include "ps/CStrIntern.h"

class CActorDef;
class CObjectEntry;
class CObjectManager;
class CXeromyces;
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
	{
		// constructor
		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
		CStr m_AnimName;
		// ID of the animation: if not empty, something specific to sync with props.
		CStr m_ID = "";
		int m_Frequency;
		// filename of the animation - manidle.psa, manrun.psa, etc
		VfsPath m_FileName;
		// animation speed, as specified in XML actor file
		float m_Speed;
		// fraction [0.0, 1.0] of the way through the animation that the interesting bit(s)
		// happens, or -1.0 if unspecified
		float m_ActionPos;
		float m_ActionPos2;
		float m_SoundPos;
	};

	struct Prop
	{
		// constructor
		Prop() : m_minHeight(0.f), m_maxHeight(0.f), m_selectable(true) {}
		// name of the prop point to attach to - "Prop01", "Prop02", "Head", "LeftHand", etc ..
		CStr m_PropPointName;
		// name of the model file - art/actors/props/sword.xml or whatever
		CStrW m_ModelName;
		// allow the prop to ajust the height from minHeight to maxHeight relative to the main model
		float m_minHeight;
		float m_maxHeight;
		bool m_selectable;
	};

	struct Samp
	{
		// identifier name of sampler in GLSL shaders
		CStrIntern m_SamplerName;
		// path to load from
		VfsPath m_SamplerFile;
	};

	struct Decal
	{
		Decal() : m_SizeX(0.f), m_SizeZ(0.f), m_Angle(0.f), m_OffsetX(0.f), m_OffsetZ(0.f) {}

		float m_SizeX;
		float m_SizeZ;
		float m_Angle;
		float m_OffsetX;
		float m_OffsetZ;
	};

	struct Variant
	{
		Variant() : m_Frequency(0) {}

		CStr m_VariantName; // lowercase name
		int m_Frequency;
		VfsPath m_ModelFilename;
		Decal m_Decal;
		VfsPath m_Particles;
		CStr m_Color;

		std::vector<Anim> m_Anims;
		std::vector<Prop> m_Props;
		std::vector<Samp> m_Samplers;
	};

	struct Variation
	{
		VfsPath model;
		Decal decal;
		VfsPath particles;
		CStr color;
		std::multimap<CStr, Prop> props;
		std::multimap<CStr, Anim> anims;
		std::multimap<CStr, Samp> samplers;
	};

	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.
	// 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) 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;

	[[nodiscard]] bool Load(const CXeromyces& XeroFile, const XMBElement& base);
	[[nodiscard]] bool 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; }

	/**
	 * Return a list of selections specifying a particular variant in all groups, based on the seed.
	 */
	std::set<CStr> PickSelectionsAtRandom(uint32_t seed) const;

// 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);

	/**
	 * Initialise this object with a default placeholder actor,
	 * pretending to be the actor at pathname.
	 */
	void LoadErrorPlaceholder(const VfsPath& pathname);

	/**
	 * 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