1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2013-2016, The OpenClonk Team and contributors
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 #include "C4ForbidLibraryCompilation.h"
17 #include "graphics/C4FacetEx.h"
18 #include "lib/C4Random.h"
19 
20 #include "platform/StdScheduler.h"
21 
22 #include <pcg/pcg_random.hpp>
23 #ifndef USE_CONSOLE
24 #include <GL/glew.h>
25 #endif
26 #include "graphics/C4Shader.h"
27 
28 #ifndef INC_C4Particles
29 #define INC_C4Particles
30 
31 enum C4ParticleValueProviderID
32 {
33 	C4PV_Const,
34 	C4PV_Linear,
35 	C4PV_Random,
36 	C4PV_KeyFrames,
37 	C4PV_Sin,
38 	C4PV_Cos,
39 	C4PV_Direction,
40 	C4PV_Step,
41 	C4PV_Speed,
42 	C4PV_Wind,
43 	C4PV_Gravity,
44 };
45 
46 enum C4ParticleAttachmentPropertyID
47 {
48 	C4ATTACH_None = 0,
49 	C4ATTACH_Front = 1,
50 	C4ATTACH_Back = 2,
51 	C4ATTACH_MoveRelative = 4
52 };
53 
54 enum C4ParticleCollisionFuncID
55 {
56 	C4PC_Die,
57 	C4PC_Bounce,
58 	C4PC_Stop,
59 };
60 
61 class C4ParticleDefCore;
62 class C4ParticleDef;
63 class C4ParticleList;
64 class C4ParticleChunk;
65 class C4Particle;
66 class C4ParticleProperties;
67 class C4ParticleValueProvider;
68 
69 // core for particle defs
70 class C4ParticleDefCore
71 {
72 public:
73 	StdStrBuf Name;                   // name
74 	C4Rect GfxFace;                   // rect for graphics
75 
76 	C4ParticleDefCore();          // ctor
77 	void CompileFunc(StdCompiler * compiler);
78 
79 	bool Compile(char *particle_source, const char *name); // compile from def file
80 };
81 
82 // one particle definition
83 class C4ParticleDef : public C4ParticleDefCore
84 {
85 public:
86 	C4ParticleDef *previous, *next; // linked list members
87 
88 	StdStrBuf Filename; // path to group this particle was loaded from (for EM reloading)
89 
90 	C4FacetSurface Gfx;               // graphics
91 	int32_t Length;                   // number of phases in gfx
92 	int32_t PhasesX;                  // number of phases per line in gfx
93 	float Aspect;                     // height:width
94 
95 	C4ParticleDef();      // ctor
96 	~C4ParticleDef();     // dtor
97 
98 	void Clear();             // free mem associated with this class
99 
100 	bool Load(C4Group &group);   // load particle from group; assume file to be accessed already
101 	bool Reload();              // reload particle from stored position
102 };
103 
104 typedef float (C4ParticleValueProvider::*C4ParticleValueProviderFunction) (C4Particle*);
105 typedef bool (C4ParticleProperties::*C4ParticleCollisionCallback) (C4Particle*);
106 
107 #ifndef USE_CONSOLE
108 // the value providers are used to change the attributes of a particle over the lifetime
109 class C4ParticleValueProvider
110 {
111 private:
112 	float startValue, endValue;
113 
114 	// used by Random
115 	float currentValue;
116 
117 	union
118 	{
119 		int rerollInterval; // for Random
120 		float delay; // for Step
121 		float speedFactor; // for Speed & Wind & Gravity
122 		float parameterValue; // for Sin
123 	};
124 
125 	union
126 	{
127 		int alreadyRolled; // for Random
128 		int smoothing; // for KeyFrames
129 		float maxValue; // for Step & Sin
130 	};
131 
132 	pcg32 rng; // for Random
133 
134 	size_t keyFrameCount;
135 	std::vector<float> keyFrames;
136 
137 	C4ParticleValueProviderFunction valueFunction;
138 	bool isConstant;
139 
140 	std::vector<C4ParticleValueProvider*> childrenValueProviders;
141 
142 	union
143 	{
144 		float C4ParticleValueProvider::*floatValueToChange;
145 		int C4ParticleValueProvider::*intValueToChange;
146 		size_t keyFrameIndex;
147 	};
148 	enum
149 	{
150 		VAL_TYPE_INT,
151 		VAL_TYPE_FLOAT,
152 		VAL_TYPE_KEYFRAMES,
153 	};
154 	int typeOfValueToChange;
155 
156 public:
IsConstant()157 	bool IsConstant() const { return isConstant; }
IsRandom()158 	bool IsRandom() const { return valueFunction == &C4ParticleValueProvider::Random; }
C4ParticleValueProvider()159 	C4ParticleValueProvider() :
160 		startValue(0.f), endValue(0.f), currentValue(0.f), rerollInterval(0), smoothing(0), keyFrameCount(0), valueFunction(nullptr), isConstant(true), floatValueToChange(nullptr), typeOfValueToChange(VAL_TYPE_FLOAT)
161 	{ }
~C4ParticleValueProvider()162 	~C4ParticleValueProvider()
163 	{
164 		for (auto &child : childrenValueProviders)
165 			delete child;
166 	}
C4ParticleValueProvider(const C4ParticleValueProvider & other)167 	C4ParticleValueProvider(const C4ParticleValueProvider &other) { *this = other; }
168 	C4ParticleValueProvider & operator= (const C4ParticleValueProvider &other);
169 
170 	// divides by denominator
171 	void Floatify(float denominator);
172 
173 	void Set(const C4Value &value);
174 	void Set(const C4ValueArray &fromArray);
175 	void Set(float to); // constant
176 	float GetValue(C4Particle *forParticle);
177 
178 private:
179 	void UpdatePointerValue(C4Particle *particle, C4ParticleValueProvider *parent);
180 	void UpdateChildren(C4Particle *particle);
181 	void FloatifyParameterValue(float C4ParticleValueProvider::*value, float denominator, size_t keyFrameIndex = 0);
182 	void SetParameterValue(int type, const C4Value &value, float C4ParticleValueProvider::*floatVal, int C4ParticleValueProvider::*intVal = nullptr, size_t keyFrameIndex = 0);
183 
184 	void SetType(C4ParticleValueProviderID what = C4PV_Const);
185 	float Linear(C4Particle *forParticle);
186 	float Const(C4Particle *forParticle);
187 	float Random(C4Particle *forParticle);
188 	float KeyFrames(C4Particle *forParticle);
189 	float Sin(C4Particle *forParticle);
190 	float Cos(C4Particle *forParticle);
191 	float Direction(C4Particle *forParticle);
192 	float Step(C4Particle *forParticle);
193 	float Speed(C4Particle *forParticle);
194 	float Wind(C4Particle *forParticle);
195 	float Gravity(C4Particle *forParticle);
196 };
197 
198 // the properties are part of every particle and contain certain changeable attributes
199 class C4ParticleProperties
200 {
201 public:
202 	bool hasConstantColor;
203 	bool hasCollisionVertex;
204 
205 	C4ParticleValueProvider size, stretch;
206 	C4ParticleValueProvider forceX, forceY;
207 	C4ParticleValueProvider speedDampingX, speedDampingY;
208 	C4ParticleValueProvider colorR, colorG, colorB, colorAlpha;
209 	C4ParticleValueProvider rotation;
210 	C4ParticleValueProvider phase;
211 	C4ParticleValueProvider collisionVertex, collisionDensity;
212 
213 	float bouncyness;
214 	C4ParticleCollisionCallback collisionCallback;
215 	void SetCollisionFunc(const C4Value &source);
216 
217 	uint32_t blitMode;
218 
219 	uint32_t attachment;
220 
221 	C4ParticleProperties();
222 
223 
224 	void Set(C4PropList *dataSource);
225 	// divides ints in certain properties by 1000f and in the color properties by 255f
226 	void Floatify();
227 
228 
CollisionDie(C4Particle * forParticle)229 	bool CollisionDie(C4Particle *forParticle) { return false; }
230 	bool CollisionBounce(C4Particle *forParticle);
231 	bool CollisionStop(C4Particle *forParticle);
232 };
233 
234 // one single particle
235 class C4Particle
236 {
237 public:
238 
239 	struct DrawingData
240 	{
241 		static const int vertexCountPerParticle;
242 
243 		struct Vertex
244 		{
245 			float x;
246 			float y;
247 
248 			float u;
249 			float v;
250 
251 			float r;
252 			float g;
253 			float b;
254 			float alpha;
255 		};
256 		Vertex *vertices;
257 
258 		int phase;
259 
260 		float currentStretch;
261 		float originalSize;
262 		float sizeX, sizeY;
263 		float aspect;
264 
265 		float offsetX, offsetY;
266 
SetOffsetDrawingData267 		void SetOffset(float x, float y)
268 		{
269 			offsetX = x;
270 			offsetY = y;
271 		}
272 
273 		void SetPointer(Vertex *startingVertex, bool initial = false)
274 		{
275 			vertices = startingVertex;
276 
277 			if (initial)
278 			{
279 				vertices[0].u = 0.f; vertices[0].v = 1.f;
280 				vertices[1].u = 0.f; vertices[1].v = 0.f;
281 				vertices[2].u = 1.f; vertices[2].v = 1.f;
282 				vertices[3].u = 1.f; vertices[3].v = 0.f;
283 
284 				SetColor(1.f, 1.f, 1.f, 1.f);
285 
286 				phase = -1;
287 			}
288 		}
289 
290 		void SetColor(float r, float g, float b, float a = 1.0f)
291 		{
292 			for (int vertex = 0; vertex < 4; ++vertex)
293 			{
294 				vertices[vertex].r = r;
295 				vertices[vertex].g = g;
296 				vertices[vertex].b = b;
297 				vertices[vertex].alpha = a;
298 			}
299 		}
300 
301 		void SetPosition(float x, float y, float size, float rotation = 0.f, float stretch = 1.f);
302 		void SetPhase(int phase, C4ParticleDef *sourceDef);
303 
DrawingDataDrawingData304 		DrawingData() : currentStretch(1.f), originalSize(0.0001f), aspect(1.f), offsetX(0.f), offsetY(0.f)
305 		{
306 		}
307 
308 	} drawingData;
309 protected:
310 	float currentSpeedX, currentSpeedY;
311 	float positionX, positionY;
312 	float lifetime, startingLifetime;
313 
314 	C4ParticleProperties properties;
315 
316 public:
GetAge()317 	float GetAge() const { return startingLifetime - lifetime; }
GetLifetime()318 	float GetLifetime() const { return lifetime; }
GetRelativeAge()319 	float GetRelativeAge() const { return (startingLifetime != 0.f) ? (1.0f - (lifetime / startingLifetime)) : 0.f; }
320 
321 	void Init();
C4Particle()322 	C4Particle() { Init(); }
323 
SetPosition(float x,float y)324 	void SetPosition(float x, float y)
325 	{
326 		positionX = x;
327 		positionY = y;
328 		drawingData.SetPosition(positionX, positionY, properties.size.GetValue(this),  properties.rotation.GetValue(this));
329 	}
330 
331 	bool Exec(C4Object *obj, float timeDelta, C4ParticleDef *sourceDef);
332 
333 	friend class C4ParticleProperties;
334 	friend class C4ParticleValueProvider;
335 	friend class C4ParticleChunk;
336 	friend class C4ParticleSystem;
337 };
338 
339 // a chunk contains all of the single particles that can be drawn with one draw call (~"have certain similar attributes")
340 class C4ParticleChunk
341 {
342 private:
343 	C4ParticleDef *sourceDefinition;
344 
345 	uint32_t blitMode;
346 
347 	// whether the particles are translated according to the object's position
348 	uint32_t attachment;
349 
350 	std::vector<C4Particle*> particles;
351 	std::vector<C4Particle::DrawingData::Vertex> vertexCoordinates;
352 	size_t particleCount;
353 
354 	// OpenGL optimizations
355 	GLuint drawingDataVertexBufferObject;
356 	unsigned int drawingDataVertexArraysObject;
357 	void ClearBufferObjects();
358 
359 	// delete the particle at indexTo. If possible, replace it with the particle at indexFrom to keep the particles tighly packed
360 	void DeleteAndReplaceParticle(size_t indexToReplace, size_t indexFrom);
361 
362 public:
C4ParticleChunk()363 	C4ParticleChunk() : sourceDefinition(nullptr), blitMode(0), attachment(C4ATTACH_None), particleCount(0), drawingDataVertexBufferObject(0), drawingDataVertexArraysObject(0)
364 	{
365 
366 	}
367 	// this is noncopyable to make sure that the OpenGL buffers are never freed multiple times
368 	C4ParticleChunk(const C4ParticleChunk&) = delete;
369 	C4ParticleChunk& operator=(const C4ParticleChunk&) = delete;
~C4ParticleChunk()370 	~C4ParticleChunk()
371 	{
372 		Clear();
373 	}
374 	// removes all particles
375 	void Clear();
376 	bool Exec(C4Object *obj, float timeDelta);
377 	void Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call, int texUnit, const StdProjectionMatrix& modelview);
378 	bool IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t attachment) const;
IsEmpty()379 	bool IsEmpty() const { return !particleCount; }
380 
381 	// before adding a particle, you should ReserveSpace for it
382 	C4Particle *AddNewParticle();
383 	// sets up internal data structures to be large enough for the passed amount of ADDITIONAL particles
384 	void ReserveSpace(uint32_t forAmount);
385 
386 	friend class C4ParticleList;
387 };
388 
389 // this class must not be copied, because deleting the contained CStdCSec twice would be fatal
390 // a particle list belongs to a game-world entity (objects or global particles) and contains the chunks associated with that entity
391 class C4ParticleList
392 {
393 private:
394 	std::list<C4ParticleChunk*> particleChunks;
395 
396 	C4Object *targetObject;
397 
398 	// caching..
399 	C4ParticleChunk *lastAccessedChunk;
400 
401 	// for making sure that the list is not drawn and calculated at the same time
402 	CStdCSec accessMutex;
403 
404 public:
targetObject(obj)405 	C4ParticleList(C4Object *obj = nullptr) : targetObject(obj), lastAccessedChunk(nullptr)
406 	{
407 
408 	}
409 	// non-copyable
410 	C4ParticleList(const C4ParticleList&) = delete;
411 	C4ParticleList& operator=(const C4ParticleList&) = delete;
412 
~C4ParticleList()413 	~C4ParticleList() { Clear(); }
414 
415 	// this enables third-parties to lock the particle list (for example because a particle in the list is modified from outside)
Lock()416 	void Lock() { accessMutex.Enter(); }
Unlock()417 	void Unlock() { accessMutex.Leave(); }
418 
419 	// deletes all the particles
420 	void Clear();
421 
422 	void Exec(float timeDelta = 1.f);
423 	void Draw(C4TargetFacet cgo, C4Object *obj);
424 	C4ParticleChunk *GetFittingParticleChunk(C4ParticleDef *def, uint32_t blitMode, uint32_t attachment, bool alreadyLocked);
425 	C4Particle *AddNewParticle(C4ParticleDef *def, uint32_t blitMode, uint32_t attachment, bool alreadyLocked, int remaining = 0);
426 };
427 #endif
428 
429 // cares for the management of particle definitions
430 class C4ParticleSystemDefinitionList
431 {
432 private:
433 	// pointers to the last and first element of linked list of particle definitions
434 	C4ParticleDef *first, *last;
435 public:
C4ParticleSystemDefinitionList()436 	C4ParticleSystemDefinitionList() : first(nullptr), last(nullptr) {}
437 	void Clear();
438 	C4ParticleDef *GetDef(const char *name, C4ParticleDef *exclude=nullptr);
439 
440 	friend class C4ParticleDef;
441 };
442 
443 // the global particle system interface class
444 class C4ParticleSystem
445 {
446 #ifndef USE_CONSOLE
447 	class CalculationThread : public StdThread
448 	{
449 	protected:
450 		void Execute() override;
451 	public:
CalculationThread()452 		CalculationThread() { StdThread::Start(); }
453 	};
454 	friend class CalculationThread;
455 
456 private:
457 	// contains an array with indices for vertices, separated by a primitive restart index
458 	GLuint ibo;
459 	size_t ibo_size;
460 	std::list<C4ParticleList> particleLists;
461 	void PreparePrimitiveRestartIndices(uint32_t forSize);
462 
463 	CStdCSec particleListAccessMutex;
464 	CStdEvent frameCounterAdvancedEvent;
465 	CalculationThread calculationThread;
466 
467 	int currentSimulationTime; // in game time
468 
469 	// calculates the physics in all of the existing particle lists
470 	void ExecuteCalculation();
471 
472 	C4ParticleList *globalParticles;
473 #endif
474 
475 public:
476 #ifndef USE_CONSOLE
477 	C4ParticleSystem();
478 	~C4ParticleSystem();
479 #endif
480 	// called to allow the particle system the simulation of another step
CalculateNextStep()481 	void CalculateNextStep()
482 	{
483 #ifndef USE_CONSOLE
484 		frameCounterAdvancedEvent.Set();
485 #endif
486 	}
487 	// resets the internal state of the particle system and unloads all definitions
488 	void Clear();
DrawGlobalParticles(C4TargetFacet cgo)489 	void DrawGlobalParticles(C4TargetFacet cgo)
490 	{
491 #ifndef USE_CONSOLE
492 		if (globalParticles) globalParticles->Draw(cgo, nullptr);
493 #endif
494 	}
495 
GetGlobalParticles()496 	C4ParticleList *GetGlobalParticles()
497 	{
498 #ifndef USE_CONSOLE
499 		return globalParticles;
500 #else
501 		return nullptr;
502 #endif
503 	}
504 
505 	C4ParticleList *GetNewParticleList(C4Object *forTarget = nullptr);
506 	// releases up to 2 lists
507 	void ReleaseParticleList(C4ParticleList *first, C4ParticleList *second = nullptr);
508 
509 	// interface for particle definitions
510 	C4ParticleSystemDefinitionList definitions;
511 
512 #ifndef USE_CONSOLE
513 	// Returns the IBO ID that contains the PRI data.
514 	// This makes sure that the IBO contains enough indices for at least 'forParticleAmount' particles.
515 	GLuint GetIBO(size_t forParticleAmount);
516 
517 	// creates a new particle
518 	void Create(C4ParticleDef *of_def, C4ParticleValueProvider &x, C4ParticleValueProvider &y, C4ParticleValueProvider &speedX, C4ParticleValueProvider &speedY, C4ParticleValueProvider &lifetime, C4PropList *properties, int amount = 1, C4Object *object=nullptr);
519 
520 #endif
521 
522 	// removes all of the existing particles (used f.e. for scenario section loading)
523 	void ClearAllParticles();
524 
525 	friend class C4ParticleList;
526 
527 
528 };
529 
530 extern C4ParticleSystem Particles;
531 
532 #endif
533