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