OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4Particles.cpp
Go to the documentation of this file.
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 "C4Include.h"
18 #include "landscape/C4Particles.h"
19 
20 // headers for particle loading
21 #include "c4group/C4Components.h"
22 
23 #ifndef USE_CONSOLE
24 // headers for particle execution
25 #include "game/C4Application.h"
26 #include "graphics/C4DrawGL.h"
27 #include "landscape/C4Material.h"
28 #include "landscape/C4Landscape.h"
29 #include "landscape/C4Weather.h"
30 #include "object/C4MeshAnimation.h"
31 #include "object/C4Object.h"
32 #include "script/C4Aul.h"
33 #include "script/C4Value.h"
34 #include "script/C4ValueArray.h"
35 #include <random>
36 #endif
37 
38 
40 {
41  pComp->Value(mkNamingAdapt(toC4CStrBuf(Name), "Name", ""));
42  pComp->Value(mkNamingAdapt(GfxFace, "Face"));
43 }
44 
46 {
47  GfxFace.Default();
48 }
49 
50 bool C4ParticleDefCore::Compile(char *particle_source, const char *name)
51 {
52  return CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "Particle"),
53  StdStrBuf(particle_source), name);
54 }
55 
57 {
58  // zero fields
59  Gfx.Default();
60  // link into list
61  if (!Particles.definitions.first)
62  {
63  previous = nullptr;
64  Particles.definitions.first = this;
65  }
66  else
67  {
69  previous->next = this;
70  }
71  Particles.definitions.last = this;
72  next = 0;
73 }
74 
76 {
77  // clear
78  Clear();
79  // unlink from list
80  if (previous) previous->next = next; else Particles.definitions.first = next;
82 }
83 
85 {
86  Name.Clear();
87 }
88 
90 {
91  // store file
92  Filename.Copy(group.GetFullName());
93  // load
94  char *particle_source;
95  if (group.LoadEntry(C4CFN_ParticleCore,&particle_source,nullptr,1))
96  {
97  if (!Compile(particle_source, Filename.getData()))
98  {
99  DebugLogF("invalid particle def at '%s'", group.GetFullName().getData());
100  delete [] particle_source; return false;
101  }
102  delete [] particle_source;
103  // load graphics
105  {
106  DebugLogF("particle %s has no valid graphics defined", Name.getData());
107  return false;
108  }
109  // set facet, if assigned - otherwise, assume full surface
111  // set phase num
112  int32_t Q; Gfx.GetPhaseNum(PhasesX, Q);
113  Length = PhasesX * Q;
114  if (!Length)
115  {
116  DebugLogF("invalid facet for particle '%s'", Name.getData());
117  return false;
118  }
119  // calc aspect
120  Aspect=(float) Gfx.Hgt/Gfx.Wdt;
121 
122  // particle overloading
123  C4ParticleDef *def_overload;
124  if ((def_overload = Particles.definitions.GetDef(Name.getData(), this)))
125  {
127  { char ostr[250]; sprintf(ostr,LoadResStr("IDS_PRC_DEFOVERLOAD"),def_overload->Name.getData(),"<particle>"); Log(ostr); }
128  delete def_overload;
129  }
130  // success
131  return true;
132  }
133  return false;
134 }
135 
137 {
138  // no file?
139  if (!Filename[0]) return false;
140  // open group
141  C4Group group;
142  if (!group.Open(Filename.getData())) return false;
143  // reset class
144  Clear();
145  // load
146  return Load(group);
147 }
148 
149 #ifndef USE_CONSOLE
151 
152 void C4Particle::DrawingData::SetPosition(float x, float y, float size, float rotation, float stretch)
153 {
154  if (size != originalSize || stretch != currentStretch)
155  {
156  currentStretch = stretch;
157  originalSize = std::max(size, 0.0001f); // a size of zero results in undefined behavior
160  }
161 
162  if (rotation == 0.f)
163  {
164  vertices[0].x = x - sizeX + offsetX;
165  vertices[0].y = y + sizeY + offsetY;
166  vertices[1].x = x - sizeX + offsetX;
167  vertices[1].y = y - sizeY + offsetY;
168  vertices[2].x = x + sizeX + offsetX;
169  vertices[2].y = y + sizeY + offsetY;
170  vertices[3].x = x + sizeX + offsetX;
171  vertices[3].y = y - sizeY + offsetY;
172  }
173  else
174  {
175  float sine = sinf(rotation);
176  float cosine = cosf(rotation);
177 
178  vertices[0].x = x + ((-sizeX) * cosine - (+sizeY) * sine) + offsetX;
179  vertices[0].y = y + ((-sizeX) * sine + (+sizeY) * cosine) + offsetY;
180  vertices[1].x = x + ((-sizeX) * cosine - (-sizeY) * sine) + offsetX;
181  vertices[1].y = y + ((-sizeX) * sine + (-sizeY) * cosine) + offsetY;
182  vertices[2].x = x + ((+sizeX) * cosine - (+sizeY) * sine) + offsetX;
183  vertices[2].y = y + ((+sizeX) * sine + (+sizeY) * cosine) + offsetY;
184  vertices[3].x = x + ((+sizeX) * cosine - (-sizeY) * sine) + offsetX;
185  vertices[3].y = y + ((+sizeX) * sine + (-sizeY) * cosine) + offsetY;
186  }
187 }
188 
190 {
191  this->phase = phase;
192  phase = phase % sourceDef->Length;
193  int offsetY = phase / sourceDef->PhasesX;
194  int offsetX = phase % sourceDef->PhasesX;
195  float wdt = 1.0f / (float)sourceDef->PhasesX;
196  int numOfLines = sourceDef->Length / sourceDef->PhasesX;
197  float hgt = 1.0f / (float)numOfLines;
198 
199  float x = wdt * (float)offsetX;
200  float y = hgt * (float)offsetY;
201  float xr = x + wdt;
202  float yr = y + hgt;
203 
204  vertices[0].u = x; vertices[0].v = yr;
205  vertices[1].u = x; vertices[1].v = y;
206  vertices[2].u = xr; vertices[2].v = yr;
207  vertices[3].u = xr; vertices[3].v = y;
208 }
209 
211 {
212  startValue = other.startValue;
213  endValue = other.endValue;
214  currentValue = other.currentValue;
215  rerollInterval = other.rerollInterval;
216  smoothing = other.smoothing;
217  valueFunction = other.valueFunction;
218  isConstant = other.isConstant;
219  keyFrameCount = other.keyFrameCount;
220  rng = other.rng;
221 
222  if (keyFrameCount > 0)
223  {
224  keyFrames.reserve(2 * keyFrameCount);
225  keyFrames.assign(other.keyFrames.begin(), other.keyFrames.end());
226  }
227 
228  typeOfValueToChange = other.typeOfValueToChange;
229  switch (typeOfValueToChange)
230  {
231  case VAL_TYPE_FLOAT:
232  floatValueToChange = other.floatValueToChange;
233  break;
234  case VAL_TYPE_INT:
235  intValueToChange = other.intValueToChange;
236  break;
237  case VAL_TYPE_KEYFRAMES:
238  keyFrameIndex = other.keyFrameIndex;
239  break;
240  default:
241  assert (false && "Trying to copy C4ParticleValueProvider with invalid value type");
242  break;
243  }
244 
245  // copy the other's children, too
246  for (std::vector<C4ParticleValueProvider*>::const_iterator iter = other.childrenValueProviders.begin(); iter != other.childrenValueProviders.end(); ++iter)
247  {
248  childrenValueProviders.push_back(new C4ParticleValueProvider(**iter)); // custom copy constructor usage
249  }
250  return (*this);
251 }
252 
253 void C4ParticleValueProvider::SetParameterValue(int type, const C4Value &value, float C4ParticleValueProvider::*floatVal, int C4ParticleValueProvider::*intVal, size_t keyFrameIndex)
254 {
255  // just an atomic data type
256  if (value.GetType() == C4V_Int)
257  {
258  if (type == VAL_TYPE_FLOAT)
259  this->*floatVal = (float)value.getInt();
260  else if (type == VAL_TYPE_INT)
261  this->*intVal = value.getInt();
262  else if (type == VAL_TYPE_KEYFRAMES)
263  this->keyFrames[keyFrameIndex] = (float)value.getInt();
264  }
265  else if (value.GetType() == C4V_Array)
266  {
267  // might be another value provider!
269  childrenValueProviders.push_back(child);
270 
271  child->Set(*value.getArray());
272  child->typeOfValueToChange = type;
273 
274  if (type == VAL_TYPE_FLOAT)
275  {
276  child->floatValueToChange = floatVal;
277  }
278  else if (type == VAL_TYPE_INT)
279  {
280  child->intValueToChange = intVal;
281  }
282  else if (type == VAL_TYPE_KEYFRAMES)
283  {
284  child->keyFrameIndex = keyFrameIndex;
285  }
286 
287  }
288  else // invalid
289  {
290  if (type == VAL_TYPE_FLOAT)
291  this->*floatVal = 0.f;
292  else if (type == VAL_TYPE_INT)
293  this->*intVal = 0;
294  else if (type == VAL_TYPE_KEYFRAMES)
295  this->keyFrames[keyFrameIndex] = 0.f;
296  }
297 }
298 
299 void C4ParticleValueProvider::UpdatePointerValue(C4Particle *particle, C4ParticleValueProvider *parent)
300 {
301  switch (typeOfValueToChange)
302  {
303  case VAL_TYPE_FLOAT:
304  parent->*floatValueToChange = GetValue(particle);
305  break;
306  case VAL_TYPE_INT:
307  parent->*intValueToChange = (int) GetValue(particle);
308  break;
309  case VAL_TYPE_KEYFRAMES:
310  parent->keyFrames[keyFrameIndex] = GetValue(particle);
311  break;
312  default:
313  assert (false);
314  }
315 }
316 
317 void C4ParticleValueProvider::UpdateChildren(C4Particle *particle)
318 {
319  for (std::vector<C4ParticleValueProvider*>::iterator iter = childrenValueProviders.begin(); iter != childrenValueProviders.end(); ++iter)
320  {
321  (*iter)->UpdatePointerValue(particle, this);
322  }
323 }
324 
325 void C4ParticleValueProvider::FloatifyParameterValue(float C4ParticleValueProvider::*value, float denominator, size_t keyFrameIndex)
326 {
327  if (value == 0)
328  this->keyFrames[keyFrameIndex] /= denominator;
329  else
330  this->*value /= denominator;
331 
332  for (std::vector<C4ParticleValueProvider*>::iterator iter = childrenValueProviders.begin(); iter != childrenValueProviders.end(); ++iter)
333  {
334  C4ParticleValueProvider *child = *iter;
335  if (value == 0)
336  {
337  if (child->typeOfValueToChange == VAL_TYPE_KEYFRAMES && child->keyFrameIndex == keyFrameIndex)
338  child->Floatify(denominator);
339  }
340  else
341  {
342  if (child->floatValueToChange == value)
343  child->Floatify(denominator);
344  }
345  }
346 
347 }
348 
349 void C4ParticleValueProvider::Floatify(float denominator)
350 {
351  assert (denominator != 0.f && "Trying to floatify C4ParticleValueProvider with denominator of 0");
352 
353  if (valueFunction == &C4ParticleValueProvider::Direction)
354  {
355  FloatifyParameterValue(&C4ParticleValueProvider::startValue, 1000.f);
356  return;
357  }
358 
359  FloatifyParameterValue(&C4ParticleValueProvider::startValue, denominator);
360  FloatifyParameterValue(&C4ParticleValueProvider::endValue, denominator);
361  FloatifyParameterValue(&C4ParticleValueProvider::currentValue, denominator);
362 
363  // special treatment for keyframes
364  if (valueFunction == &C4ParticleValueProvider::KeyFrames)
365  {
366  for (size_t i = 0; i < keyFrameCount; ++i)
367  {
368  FloatifyParameterValue(0, 1000.f, 2 * i); // even numbers are the time values
369  FloatifyParameterValue(0, denominator, 2 * i + 1); // odd numbers are the actual values
370  }
371  }
372  else if (valueFunction == &C4ParticleValueProvider::Speed || valueFunction == &C4ParticleValueProvider::Wind || valueFunction == &C4ParticleValueProvider::Gravity)
373  {
374  FloatifyParameterValue(&C4ParticleValueProvider::speedFactor, 1000.0f);
375  }
376  else if (valueFunction == &C4ParticleValueProvider::Step)
377  {
378  FloatifyParameterValue(&C4ParticleValueProvider::maxValue, denominator);
379  }
380  else if (valueFunction == &C4ParticleValueProvider::Sin || valueFunction == &C4ParticleValueProvider::Cos)
381  {
382  FloatifyParameterValue(&C4ParticleValueProvider::parameterValue, 1.0f);
383  FloatifyParameterValue(&C4ParticleValueProvider::maxValue, denominator);
384  }
385 }
386 
388 {
389  UpdateChildren(forParticle);
390  return (this->*valueFunction)(forParticle);
391 }
392 
393 float C4ParticleValueProvider::Linear(C4Particle *forParticle)
394 {
395  return startValue + (endValue - startValue) * forParticle->GetRelativeAge();
396 }
397 
398 float C4ParticleValueProvider::Const(C4Particle *forParticle)
399 {
400  return startValue;
401 }
402 
403 float C4ParticleValueProvider::Random(C4Particle *forParticle)
404 {
405  // We need to roll again if..
406  const bool needToReevaluate =
407  // .. we haven't rolled yet
408  alreadyRolled == 0
409  // .. we are still in the intialization stage (and thus, this value provider could be an intialization term for multiple particles)
410  || (forParticle->lifetime == forParticle->startingLifetime)
411  // .. or the reroll interval is set and expired.
412  || (rerollInterval != 0 && ((int)forParticle->GetAge() % rerollInterval == 0));
413 
414  if (needToReevaluate)
415  {
416  alreadyRolled = 1;
417  // Even for seeded PV_Random, each particle should behave differently. Thus, we use a different
418  // stream for each one. Since this is by no means synchronisation relevant and since the
419  // particles lie on the heap we just use the address here.
420  const std::uintptr_t ourAddress = reinterpret_cast<std::uintptr_t>(forParticle);
421  rng.set_stream(ourAddress);
422  // We need to advance the RNG a bit to make streams with the same seed diverge.
423  rng.advance(5);
424  std::uniform_real_distribution<float> distribution(std::min(startValue, endValue), std::max(startValue, endValue));
425  currentValue = distribution(rng);
426  }
427  return currentValue;
428 }
429 
430 float C4ParticleValueProvider::Direction(C4Particle *forParticle)
431 {
432  float distX = forParticle->currentSpeedX;
433  float distY = forParticle->currentSpeedY;
434 
435  if (distX == 0.f) return distY > 0.f ? M_PI : 0.f;
436  if (distY == 0.f) return distX < 0.f ? 3.0f * M_PI_2 : M_PI_2;
437 
438  return startValue * (atan2(distY, distX) + (float)M_PI_2);
439 }
440 
441 float C4ParticleValueProvider::Step(C4Particle *forParticle)
442 {
443  float value = currentValue + startValue * forParticle->GetAge() / delay;
444  if (maxValue != 0.0f && value > maxValue) value = maxValue;
445  return value;
446 }
447 
448 float C4ParticleValueProvider::KeyFrames(C4Particle *forParticle)
449 {
450  float age = forParticle->GetRelativeAge();
451  // todo, implement smoothing
452  //if (smoothing == 0) // linear
453  {
454  for (size_t i = 0; i < keyFrameCount; ++i)
455  {
456  if (age > keyFrames[i * 2]) continue;
457  assert(i >= 1);
458 
459  float x1 = keyFrames[(i - 1) * 2];
460  float x2 = keyFrames[i * 2];
461  float y1 = keyFrames[(i - 1) * 2 + 1];
462  float y2 = keyFrames[i * 2 + 1];
463  float position = (age - x1) / (x2 - x1);
464  float totalRange = (y2 - y1);
465 
466  float value = position * totalRange + y1;
467  return value;
468  }
469  }
470 
471  return startValue;
472 }
473 
474 float C4ParticleValueProvider::Sin(C4Particle *forParticle)
475 {
476  return sin(parameterValue * M_PI / 180.0f) * maxValue + startValue;
477 }
478 
479 float C4ParticleValueProvider::Cos(C4Particle *forParticle)
480 {
481  return cos(parameterValue * M_PI / 180.0f) * maxValue + startValue;
482 }
483 
484 float C4ParticleValueProvider::Speed(C4Particle *forParticle)
485 {
486  float distX = forParticle->currentSpeedX;
487  float distY = forParticle->currentSpeedY;
488  float speed = sqrtf((distX * distX) + (distY * distY));
489 
490  return startValue + speedFactor * speed;
491 }
492 
493 float C4ParticleValueProvider::Wind(C4Particle *forParticle)
494 {
495  return startValue + (0.01f * speedFactor * ::Weather.GetWind((int)forParticle->positionX, (int)forParticle->positionY));
496 }
497 
498 float C4ParticleValueProvider::Gravity(C4Particle *forParticle)
499 {
500  return startValue + (speedFactor * fixtof(::Landscape.GetGravity()));
501 }
502 
503 void C4ParticleValueProvider::SetType(C4ParticleValueProviderID what)
504 {
505  switch (what)
506  {
507  case C4PV_Const:
508  valueFunction = &C4ParticleValueProvider::Const;
509  break;
510  case C4PV_Linear:
511  valueFunction = &C4ParticleValueProvider::Linear;
512  break;
513  case C4PV_Random:
514  valueFunction = &C4ParticleValueProvider::Random;
515  break;
516  case C4PV_Direction:
517  valueFunction = &C4ParticleValueProvider::Direction;
518  break;
519  case C4PV_Step:
520  valueFunction = &C4ParticleValueProvider::Step;
521  break;
522  case C4PV_KeyFrames:
523  valueFunction = &C4ParticleValueProvider::KeyFrames;
524  break;
525  case C4PV_Sin:
526  valueFunction = &C4ParticleValueProvider::Sin;
527  break;
528  case C4PV_Cos:
529  valueFunction = &C4ParticleValueProvider::Cos;
530  break;
531  case C4PV_Speed:
532  valueFunction = &C4ParticleValueProvider::Speed;
533  break;
534  case C4PV_Wind:
535  valueFunction = &C4ParticleValueProvider::Wind;
536  break;
537  case C4PV_Gravity:
538  valueFunction = &C4ParticleValueProvider::Gravity;
539  break;
540  default:
541  assert(false && "Invalid C4ParticleValueProvider ID passed");
542  };
543 
544  if (what != C4PV_Const)
545  {
546  isConstant = false;
547  }
548 }
549 
551 {
552  startValue = endValue = 1.0f;
553  valueFunction = &C4ParticleValueProvider::Const;
554 
555  size_t arraySize = (size_t) fromArray.GetSize();
556  if (arraySize < 2) return;
557 
558  int type = fromArray[0].getInt();
559 
560  switch (type)
561  {
562  case C4PV_Const:
563  if (arraySize >= 2)
564  {
565  SetType(C4PV_Const);
566  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
567  }
568  break;
569 
570  case C4PV_Linear:
571  if (arraySize >= 3)
572  {
573  SetType(C4PV_Linear);
574  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
575  SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::endValue);
576  }
577  break;
578  case C4PV_Random:
579  if (arraySize >= 3)
580  {
581  SetType(C4PV_Random);
582  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
583  SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::endValue);
584  if (arraySize >= 4 && fromArray[3].GetType() != C4V_Type::C4V_Nil)
585  SetParameterValue(VAL_TYPE_INT, fromArray[3], 0, &C4ParticleValueProvider::rerollInterval);
586  if (arraySize >= 5 && fromArray[4].GetType() != C4V_Type::C4V_Nil)
587  {
588  // We don't need the seed later on, but SetParameterValue won't accept local
589  // variables. Use an unrelated member instead which is reset below.
590  SetParameterValue(VAL_TYPE_INT, fromArray[4], 0, &C4ParticleValueProvider::alreadyRolled);
591  rng.seed(alreadyRolled);
592  }
593  else
594  {
595  rng.seed(UnsyncedRandom());
596  }
597  alreadyRolled = 0;
598  }
599  break;
600  case C4PV_Direction:
601  if (arraySize >= 2)
602  {
603  SetType(C4PV_Direction);
604  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
605  }
606  break;
607  case C4PV_Step:
608  if (arraySize >= 4)
609  {
610  SetType(C4PV_Step);
611  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
612  SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::currentValue);
613  SetParameterValue(VAL_TYPE_FLOAT, fromArray[3], &C4ParticleValueProvider::delay);
614  SetParameterValue(VAL_TYPE_FLOAT, fromArray[4], &C4ParticleValueProvider::maxValue);
615  if (delay == 0.f) delay = 1.f;
616  }
617  break;
618  case C4PV_KeyFrames:
619  if (arraySize >= 5)
620  {
621  SetType(C4PV_KeyFrames);
622  SetParameterValue(VAL_TYPE_INT, fromArray[1], 0, &C4ParticleValueProvider::smoothing);
623  keyFrames.resize(arraySize + 4 - 1); // 2*2 additional information floats at the beginning and ending, offset the first array item, though
624 
625  keyFrameCount = 0;
626  const size_t startingOffset = 2;
627  size_t i = startingOffset;
628  for (; i < arraySize; ++i)
629  {
630  SetParameterValue(VAL_TYPE_KEYFRAMES, fromArray[(int32_t)i], 0, 0, 2 + i - startingOffset);
631  }
632  keyFrameCount = (i - startingOffset) / 2 + 2;
633 
634  startValue = keyFrames[2 + 1];
635  endValue = keyFrames[2 * keyFrameCount - 1];
636 
637  // add two points for easier interpolation at beginning and ending
638  keyFrames[0] = -500.f;
639  keyFrames[1] = keyFrames[2 + 1];
640  keyFrames[2 * keyFrameCount - 2] = 1500.f;
641  keyFrames[2 * keyFrameCount - 1] = keyFrames[keyFrameCount - 1 - 2];
642 
643  }
644  break;
645  case C4PV_Sin: // fallthrough
646  case C4PV_Cos:
647  if (arraySize >= 3)
648  {
649  SetType(static_cast<C4ParticleValueProviderID> (type)); // Sin(parameterValue) * maxValue + startValue
650  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::parameterValue);
651  SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::maxValue);
652  SetParameterValue(VAL_TYPE_FLOAT, fromArray[3], &C4ParticleValueProvider::startValue);
653  }
654  break;
655  case C4PV_Speed:
656  if (arraySize >= 3)
657  {
658  SetType(C4PV_Speed);
659  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::speedFactor);
660  SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::startValue);
661  }
662  break;
663  case C4PV_Wind:
664  if (arraySize >= 3)
665  {
666  SetType(C4PV_Wind);
667  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::speedFactor);
668  SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::startValue);
669  }
670  break;
671  case C4PV_Gravity:
672  if (arraySize >= 3)
673  {
674  SetType(C4PV_Gravity);
675  SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::speedFactor);
676  SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::startValue);
677  }
678  break;
679  default:
680  throw C4AulExecError("invalid particle value provider supplied");
681  break;
682  }
683 }
684 
686 {
687  C4ValueArray *valueArray= value.getArray();
688 
689  if (valueArray != 0)
690  Set(*valueArray);
691  else
692  Set((float)value.getInt());
693 }
694 
696 {
697  SetType(C4PV_Const);
698  startValue = endValue = to;
699 }
700 
702 {
703  blitMode = 0;
704  attachment = C4ATTACH_None;
705  hasConstantColor = false;
706  hasCollisionVertex = false;
707  collisionCallback = 0;
708  bouncyness = 0.f;
709 
710  // all values in pre-floatified range (f.e. 0..255 instead of 0..1)
711  collisionDensity.Set(static_cast<float>(C4M_Solid));
712  collisionVertex.Set(0.f);
713  size.Set(8.f);
714  stretch.Set(1000.f);
715  forceX.Set(0.f);
716  forceY.Set(0.f);
717  speedDampingX.Set(1000.f);
718  speedDampingY.Set(1000.f);
719  colorR.Set(255.f);
720  colorG.Set(255.f);
721  colorB.Set(255.f);
722  colorAlpha.Set(255.f);
723  rotation.Set(0.f);
724  phase.Set(0.f);
725 }
726 
728 {
729  bouncyness /= 1000.f;
730 
731  collisionDensity.Floatify(1.f);
732  collisionVertex.Floatify(1000.f);
733  size.Floatify(2.f);
734  stretch.Floatify(1000.f);
735  forceX.Floatify(100.f);
736  forceY.Floatify(100.f);
737  speedDampingX.Floatify(1000.f);
738  speedDampingY.Floatify(1000.f);
739  colorR.Floatify(255.f);
740  colorG.Floatify(255.f);
741  colorB.Floatify(255.f);
742  colorAlpha.Floatify(255.f);
743  rotation.Floatify(180.0f / (float)M_PI);
744  phase.Floatify(1.f);
745 
746  hasConstantColor = colorR.IsConstant() && colorG.IsConstant() && colorB.IsConstant() && colorAlpha.IsConstant();
747 }
748 
750 {
751  if (!dataSource) return;
752 
753  C4PropList::Iterator iter = dataSource->begin(), end = dataSource->end();
754 
755  for (;iter != end; ++iter)
756  {
757  const C4Property * p = *iter;
758  C4String *key = p->Key;
759  assert(key && "PropList returns non-string as key");
760  const C4Value &property = p->Value;
761 
762  if(&Strings.P[P_R] == key)
763  {
764  colorR.Set(property);
765  }
766  else if(&Strings.P[P_G] == key)
767  {
768  colorG.Set(property);
769  }
770  else if(&Strings.P[P_B] == key)
771  {
772  colorB.Set(property);
773  }
774  else if(&Strings.P[P_Alpha] == key)
775  {
776  colorAlpha.Set(property);
777  }
778  else if(&Strings.P[P_ForceX] == key)
779  {
780  forceX.Set(property);
781  }
782  else if(&Strings.P[P_ForceY] == key)
783  {
784  forceY.Set(property);
785  }
786  else if(&Strings.P[P_DampingX] == key)
787  {
788  speedDampingX.Set(property);
789  }
790  else if(&Strings.P[P_DampingY] == key)
791  {
792  speedDampingY.Set(property);
793  }
794  else if(&Strings.P[P_Size] == key)
795  {
796  size.Set(property);
797  }
798  else if(&Strings.P[P_Stretch] == key)
799  {
800  stretch.Set(property);
801  }
802  else if(&Strings.P[P_Rotation] == key)
803  {
804  rotation.Set(property);
805  }
806  else if(&Strings.P[P_BlitMode] == key)
807  {
808  // needs to be constant
809  blitMode = (uint32_t) property.getInt();
810  }
811  else if(&Strings.P[P_Attach] == key)
812  {
813  // needs to be constant
814  attachment = (uint32_t) property.getInt();
815  }
816  else if(&Strings.P[P_Phase] == key)
817  {
818  phase.Set(property);
819  }
820  else if(&Strings.P[P_CollisionVertex] == key)
821  {
822  collisionVertex.Set(property);
823  if (property.GetType() != C4V_Nil)
824  hasCollisionVertex = true;
825  }
826  else if (&Strings.P[P_CollisionDensity] == key)
827  {
828  collisionDensity.Set(property);
829  }
830  else if(&Strings.P[P_OnCollision] == key)
831  {
832  SetCollisionFunc(property);
833  }
834  }
835 
836 }
837 
839 {
840  C4ValueArray *valueArray;
841  if (!(valueArray = source.getArray())) return;
842 
843  int arraySize = valueArray->GetSize();
844  if (arraySize < 1) return;
845 
846  int type = (*valueArray)[0].getInt();
847 
848  switch (type)
849  {
850  case C4PC_Die:
851  collisionCallback = &C4ParticleProperties::CollisionDie;
852  break;
853  case C4PC_Bounce:
854  collisionCallback = &C4ParticleProperties::CollisionBounce;
855  bouncyness = 1.f;
856  if (arraySize >= 2)
857  bouncyness = ((float)(*valueArray)[1].getInt());
858  break;
859  case C4PC_Stop:
860  collisionCallback = &C4ParticleProperties::CollisionStop;
861  break;
862  default:
863  assert(false);
864  break;
865  }
866 }
867 
869 {
870  forParticle->currentSpeedX = -forParticle->currentSpeedX * bouncyness;
871  forParticle->currentSpeedY = -forParticle->currentSpeedY * bouncyness;
872  return true;
873 }
874 
876 {
877  forParticle->currentSpeedX = 0.f;
878  forParticle->currentSpeedY = 0.f;
879  return true;
880 }
881 
883 {
885  positionX = positionY = 0.f;
886  lifetime = startingLifetime = 5.f * 38.f;
887 }
888 
889 bool C4Particle::Exec(C4Object *obj, float timeDelta, C4ParticleDef *sourceDef)
890 {
891  // die of old age? :<
892  lifetime -= timeDelta;
893  // check only if we had a maximum lifetime to begin with (for permanent particles)
894  if (startingLifetime > 0.f)
895  {
896  if (lifetime <= 0.f) return false;
897  }
898 
899  // movement
900  float currentForceX = properties.forceX.GetValue(this);
901  float currentForceY = properties.forceY.GetValue(this);
902 
903  currentSpeedX += currentForceX;
904  currentSpeedY += currentForceY;
905 
906  if (currentSpeedX != 0.f || currentSpeedY != 0.f)
907  {
908  float currentDampingX = properties.speedDampingX.GetValue(this);
909  float currentDampingY = properties.speedDampingY.GetValue(this);
910  float size = properties.size.GetValue(this);
911 
912  currentSpeedX *= currentDampingX;
913  currentSpeedY *= currentDampingY;
914 
915  // move & collision check
916  // note: accessing Landscape.GetDensity here is not protected by locks
917  // it is assumed that the particle system is cleaned up before, f.e., the landscape memory is freed
918  bool collided = false;
920  {
921  float collisionPoint = properties.collisionVertex.GetValue(this);
922  float size_x = (currentSpeedX > 0.f ? size : -size) * 0.5f * collisionPoint;
923  float size_y = (currentSpeedY > 0.f ? size : -size) * 0.5f * collisionPoint;
924  float density = static_cast<float>(GBackDensity(positionX + size_x + timeDelta * currentSpeedX, positionY + size_y + timeDelta * currentSpeedY));
925 
926  if (density + 0.5f >= properties.collisionDensity.GetValue(this)) // Small offset against floating point insanities.
927  {
928  // exec collision func
929  if (properties.collisionCallback != 0 && !(properties.*properties.collisionCallback)(this)) return false;
930  collided = true;
931  }
932  }
933 
934  if (!collided)
935  {
936  positionX += timeDelta * currentSpeedX;
937  positionY += timeDelta * currentSpeedY;
938  }
940 
941  }
943  {
945  }
946 
947  // adjust color
949  {
951  }
952 
953  int currentPhase = (int)(properties.phase.GetValue(this) + 0.5f);
954  if (currentPhase != drawingData.phase)
955  drawingData.SetPhase(currentPhase, sourceDef);
956 
957  return true;
958 }
959 
961 {
962  for (size_t i = 0; i < particleCount; ++i)
963  {
964  delete particles[i];
965  }
966  particleCount = 0;
967  particles.clear();
968  vertexCoordinates.clear();
969 
970  ClearBufferObjects();
971 }
972 
973 void C4ParticleChunk::DeleteAndReplaceParticle(size_t indexToReplace, size_t indexFrom)
974 {
975  C4Particle *oldParticle = particles[indexToReplace];
976 
977  // try to replace the soon-to-be empty slot in the array
978  if (indexFrom != indexToReplace) // false when "replacing" the last one
979  {
980  std::copy(&vertexCoordinates[indexFrom * C4Particle::DrawingData::vertexCountPerParticle], &vertexCoordinates[indexFrom * C4Particle::DrawingData::vertexCountPerParticle] + C4Particle::DrawingData::vertexCountPerParticle, &vertexCoordinates[indexToReplace * C4Particle::DrawingData::vertexCountPerParticle]);
981  particles[indexToReplace] = particles[indexFrom];
982  particles[indexToReplace]->drawingData.SetPointer(&vertexCoordinates[indexToReplace * C4Particle::DrawingData::vertexCountPerParticle]);
983  }
984 
985  delete oldParticle;
986 }
987 
988 bool C4ParticleChunk::Exec(C4Object *obj, float timeDelta)
989 {
990  for (size_t i = 0; i < particleCount; ++i)
991  {
992  if (!particles[i]->Exec(obj, timeDelta, sourceDefinition))
993  {
994  DeleteAndReplaceParticle(i, particleCount - 1);
995  --particleCount;
996  }
997  }
998  return particleCount > 0;
999 }
1000 
1001 void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call, int texUnit, const StdProjectionMatrix& modelview)
1002 {
1003  if (particleCount == 0) return;
1004  const int stride = sizeof(C4Particle::DrawingData::Vertex);
1005  assert(sourceDefinition && "No source definition assigned to particle chunk.");
1006  C4TexRef *textureRef = sourceDefinition->Gfx.GetFace().texture.get();
1007  assert(textureRef != 0 && "Particle definition had no texture assigned.");
1008 
1009  // use a relative offset?
1010  // (note the normal matrix is unaffected by this)
1011  if ((attachment & C4ATTACH_MoveRelative) && (obj != 0))
1012  {
1013  StdProjectionMatrix new_modelview(modelview);
1014  Translate(new_modelview, fixtof(obj->GetFixedX()), fixtof(obj->GetFixedY()), 0.0f);
1015  call.SetUniformMatrix4x4(C4SSU_ModelViewMatrix, new_modelview);
1016  }
1017  else
1018  {
1019  call.SetUniformMatrix4x4(C4SSU_ModelViewMatrix, modelview);
1020  }
1021 
1022  // enable additive blending for particles with that blit mode
1023  glBlendFunc(GL_SRC_ALPHA, (blitMode & C4GFXBLIT_ADDITIVE) ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
1024 
1025  glActiveTexture(texUnit);
1026  glBindTexture(GL_TEXTURE_2D, textureRef->texName);
1027 
1028  // generate the buffer as necessary
1029  if (drawingDataVertexBufferObject == 0)
1030  {
1031  // clear up old data
1032  ClearBufferObjects();
1033  // generate new buffer objects
1034  glGenBuffers(1, &drawingDataVertexBufferObject);
1035  assert (drawingDataVertexBufferObject != 0 && "Could not generate OpenGL buffer object.");
1036  // Immediately bind the buffer.
1037  // glVertexAttribPointer requires a valid GL_ARRAY_BUFFER to be bound and we need the buffer to be created for glObjectLabel.
1038  glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
1039 
1040  pGL->ObjectLabel(GL_BUFFER, drawingDataVertexBufferObject, -1, "<particles>/VBO");
1041 
1042  // generate new VAO ID
1043  drawingDataVertexArraysObject = pGL->GenVAOID();
1044  assert (drawingDataVertexArraysObject != 0 && "Could not generate a VAO ID.");
1045  }
1046 
1047 
1048  // Push the new vertex data
1049  glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
1050  glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(C4Particle::DrawingData::Vertex) * particleCount, &vertexCoordinates[0], GL_DYNAMIC_DRAW);
1051  glBindBuffer(GL_ARRAY_BUFFER, 0);
1052 
1053  // set up the vertex array structure
1054  GLuint vao;
1055  const bool has_vao = pGL->GetVAO(drawingDataVertexArraysObject, vao);
1056  glBindVertexArray(vao);
1057 
1058  assert ((drawingDataVertexBufferObject != 0) && "No buffer object has been created yet.");
1059  assert ((drawingDataVertexArraysObject != 0) && "No vertex arrays object has been created yet.");
1060 
1061  if (!has_vao)
1062  {
1063  glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
1064  pGL->ObjectLabel(GL_VERTEX_ARRAY, vao, -1, "<particles>/VAO");
1065 
1066  glEnableVertexAttribArray(call.GetAttribute(C4SSA_Position));
1067  glEnableVertexAttribArray(call.GetAttribute(C4SSA_Color));
1068  glEnableVertexAttribArray(call.GetAttribute(C4SSA_TexCoord));
1069  glVertexAttribPointer(call.GetAttribute(C4SSA_Position), 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, x)));
1070  glVertexAttribPointer(call.GetAttribute(C4SSA_TexCoord), 2, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, u)));
1071  glVertexAttribPointer(call.GetAttribute(C4SSA_Color), 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, r)));
1072  }
1073 
1074  // We need to always bind the ibo, because it might change its size.
1075  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ::Particles.GetIBO(particleCount));
1076 
1077  glDrawElements(GL_TRIANGLE_STRIP, static_cast<GLsizei> (5 * particleCount), GL_UNSIGNED_INT, 0);
1078 
1079  // reset buffer data
1080  glBindVertexArray(0);
1081 }
1082 
1083 bool C4ParticleChunk::IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t _attachment) const
1084 {
1085  return def == sourceDefinition && blitMode == _blitMode && attachment == _attachment;
1086 }
1087 
1088 void C4ParticleChunk::ClearBufferObjects()
1089 {
1090  if (drawingDataVertexBufferObject != 0) // the value 0 as a buffer index is reserved and will never be returned by glGenBuffers
1091  glDeleteBuffers(1, &drawingDataVertexBufferObject);
1092  if (drawingDataVertexArraysObject != 0)
1093  pGL->FreeVAOID(drawingDataVertexArraysObject);
1094 
1095  drawingDataVertexArraysObject = 0;
1096  drawingDataVertexBufferObject = 0;
1097 }
1098 
1099 void C4ParticleChunk::ReserveSpace(uint32_t forAmount)
1100 {
1101  uint32_t newSize = static_cast<uint32_t>(particleCount) + forAmount + 1;
1102 
1103  if (particles.capacity() < newSize)
1104  particles.reserve(std::max<uint32_t>(newSize, particles.capacity() * 2));
1105 
1106  // resizing the points vector is relatively costly, hopefully we only do it rarely
1107  while (vertexCoordinates.capacity() <= newSize * C4Particle::DrawingData::vertexCountPerParticle)
1108  {
1109  vertexCoordinates.reserve(std::max<uint32_t>(C4Particle::DrawingData::vertexCountPerParticle * newSize, vertexCoordinates.capacity() * 2));
1110 
1111  // update all existing particles' pointers..
1112  for (size_t i = 0; i < particleCount; ++i)
1113  particles[i]->drawingData.SetPointer(&vertexCoordinates[i * C4Particle::DrawingData::vertexCountPerParticle]);
1114  }
1115 }
1116 
1118 {
1119  size_t currentIndex = particleCount++;
1120 
1121  if (currentIndex < particles.size())
1122  {
1123  particles[currentIndex] = new C4Particle();
1124  }
1125  else
1126  {
1127  particles.push_back(new C4Particle());
1128  vertexCoordinates.resize(vertexCoordinates.size() + C4Particle::DrawingData::vertexCountPerParticle);
1129  }
1130 
1131  C4Particle *newParticle = particles[currentIndex];
1132  newParticle->drawingData.SetPointer(&vertexCoordinates[currentIndex * C4Particle::DrawingData::vertexCountPerParticle], true);
1133  return newParticle;
1134 }
1135 
1136 void C4ParticleList::Exec(float timeDelta)
1137 {
1138  if (particleChunks.empty()) return;
1139 
1140  accessMutex.Enter();
1141 
1142  for (std::list<C4ParticleChunk*>::iterator iter = particleChunks.begin(); iter != particleChunks.end();++iter)
1143  {
1144  C4ParticleChunk *chunk = *iter;
1145  chunk->Exec(targetObject, timeDelta);
1146  }
1147 
1148  accessMutex.Leave();
1149 }
1150 
1152 {
1153  if (particleChunks.empty()) return;
1154 
1156  pDraw->ResetBlitMode();
1157 
1158  glPrimitiveRestartIndex(0xffffffff);
1159  glEnable(GL_PRIMITIVE_RESTART);
1160 
1161  // enable shader
1162  C4ShaderCall call(pGL->GetSpriteShader(true, false, false));
1163  // apply zoom and upload shader uniforms
1164  StdProjectionMatrix modelview = StdProjectionMatrix::Identity();
1165  pGL->SetupMultiBlt(call, nullptr, 0, 0, 0, 0, &modelview);
1166  // go to correct output position (note the normal matrix is unaffected
1167  // by this)
1168  Translate(modelview, cgo.X-cgo.TargetX, cgo.Y-cgo.TargetY, 0.0f);
1169  // allocate texture unit for particle texture, and remember allocated
1170  // texture unit. Will be used for each particle chunk to bind
1171  // their texture to this unit.
1172  const GLint texUnit = call.AllocTexUnit(C4SSU_BaseTex);
1173 
1174  accessMutex.Enter();
1175 
1176  for (std::list<C4ParticleChunk*>::iterator iter = particleChunks.begin(); iter != particleChunks.end(); )
1177  {
1178  if ((*iter)->IsEmpty())
1179  {
1180  delete *iter;
1181  iter = particleChunks.erase(iter);
1182  lastAccessedChunk = 0;
1183  }
1184  else
1185  {
1186  (*iter)->Draw(cgo, obj, call, texUnit, modelview);
1187  ++iter;
1188  }
1189  }
1190 
1191  accessMutex.Leave();
1192 
1193  glDisable(GL_PRIMITIVE_RESTART);
1194 }
1195 
1197 {
1198  accessMutex.Enter();
1199 
1200  for (std::list<C4ParticleChunk*>::iterator iter = particleChunks.begin(); iter != particleChunks.end(); ++iter)
1201  delete *iter;
1202  particleChunks.clear();
1203 
1204  if (targetObject)
1205  {
1206  if (this == targetObject->FrontParticles) targetObject->FrontParticles = nullptr;
1207  else if (this == targetObject->BackParticles) targetObject->BackParticles = nullptr;
1208  }
1209  else
1210  if(this == ::Particles.globalParticles) ::Particles.globalParticles = nullptr;
1211 
1212  accessMutex.Leave();
1213 }
1214 
1215 C4ParticleChunk *C4ParticleList::GetFittingParticleChunk(C4ParticleDef *def, uint32_t blitMode, uint32_t attachment, bool alreadyLocked)
1216 {
1217  if (!alreadyLocked)
1218  accessMutex.Enter();
1219 
1220  // if not cached, find correct chunk in list
1221  C4ParticleChunk *chunk = 0;
1222  if (lastAccessedChunk && lastAccessedChunk->IsOfType(def, blitMode, attachment))
1223  chunk = lastAccessedChunk;
1224  else
1225  {
1226  for (std::list<C4ParticleChunk*>::iterator iter = particleChunks.begin(); iter != particleChunks.end(); ++iter)
1227  {
1228  C4ParticleChunk *current = *iter;
1229  if (!current->IsOfType(def, blitMode, attachment)) continue;
1230  chunk = current;
1231  break;
1232  }
1233  }
1234 
1235  // add new chunk?
1236  if (!chunk)
1237  {
1238  particleChunks.push_back(new C4ParticleChunk());
1239  chunk = particleChunks.back();
1240  chunk->sourceDefinition = def;
1241  chunk->blitMode = blitMode;
1242  chunk->attachment = attachment;
1243  }
1244 
1245  assert(chunk && "No suitable particle chunk could be found or created.");
1246  lastAccessedChunk = chunk;
1247 
1248  if (!alreadyLocked)
1249  accessMutex.Leave();
1250 
1251  return chunk;
1252 }
1253 
1254 void C4ParticleSystem::CalculationThread::Execute()
1255 {
1256  Particles.ExecuteCalculation();
1257 }
1258 
1259 C4ParticleSystem::C4ParticleSystem() : frameCounterAdvancedEvent(false)
1260 {
1261  currentSimulationTime = 0;
1262  globalParticles = 0;
1263  ibo = 0;
1264  ibo_size = 0;
1265 }
1266 
1268 {
1269  Clear();
1270 
1271  calculationThread.SignalStop();
1273 }
1274 
1275 void C4ParticleSystem::ExecuteCalculation()
1276 {
1277  frameCounterAdvancedEvent.WaitFor(INFINITE);
1278  frameCounterAdvancedEvent.Reset();
1279 
1280  int gameTime = Game.FrameCounter;
1281  if (currentSimulationTime < gameTime)
1282  {
1283  float timeDelta = 1.f;
1284  if (currentSimulationTime != 0)
1285  timeDelta = (float)(gameTime - currentSimulationTime);
1286  currentSimulationTime = gameTime;
1287 
1288  particleListAccessMutex.Enter();
1289 
1290  for (std::list<C4ParticleList>::iterator iter = particleLists.begin(); iter != particleLists.end(); ++iter)
1291  {
1292  iter->Exec(timeDelta);
1293  }
1294 
1295  particleListAccessMutex.Leave();
1296  }
1297 }
1298 #endif
1299 
1301 {
1302 #ifdef USE_CONSOLE
1303  return 0;
1304 #else
1305  C4ParticleList *newList = 0;
1306 
1307  particleListAccessMutex.Enter();
1308  particleLists.emplace_back(forObject);
1309  newList = &particleLists.back();
1310  particleListAccessMutex.Leave();
1311 
1312  return newList;
1313 #endif
1314 }
1315 
1317 {
1318 #ifndef USE_CONSOLE
1319  particleListAccessMutex.Enter();
1320 
1321  for(std::list<C4ParticleList>::iterator iter = particleLists.begin(); iter != particleLists.end();)
1322  {
1323  C4ParticleList *list = &(*iter);
1324  if (list == first || list == second)
1325  {
1326  iter = particleLists.erase(iter);
1327  }
1328  else
1329  {
1330  ++iter;
1331  }
1332  }
1333 
1334  particleListAccessMutex.Leave();
1335 #endif
1336 }
1337 
1338 #ifndef USE_CONSOLE
1340 {
1341  // todo: check amount etc
1342 
1343  C4ParticleList * pxList(0);
1344 
1345 
1346  // initialize the particle properties
1347  // this is done here, because it would also be the right place to implement caching
1348  C4ParticleProperties particleProperties;
1349  particleProperties.Set(properties);
1350 
1351  speedX.Floatify(10.f);
1352  speedY.Floatify(10.f);
1353 
1354  // position offset that will be added to the particle
1355  float xoff(0.f), yoff(0.f);
1356 
1357  // offset only for the drawing position - this is needed so that particles relative to an object work correctly
1358  float drawingOffsetX(0.f), drawingOffsetY(0.f);
1359 
1360  if (object != 0)
1361  {
1362  // for all types of particles add object's offset (mainly for collision etc.)
1363  xoff = object->GetX();
1364  yoff = object->GetY();
1365 
1366  if (particleProperties.attachment & C4ATTACH_MoveRelative)
1367  {
1368  drawingOffsetX = -xoff;
1369  drawingOffsetY = -yoff;
1370 
1371  // move relative implies that the particle needs to be in the object's particle list (back OR front)
1372  // just select the front particles here - will be overwritten below if necessary
1373  if (!(particleProperties.attachment & C4ATTACH_Front) && !(particleProperties.attachment & C4ATTACH_Back))
1374  particleProperties.attachment |= C4ATTACH_Front;
1375  }
1376 
1377  // figure out particle list to use
1378  if (particleProperties.attachment & C4ATTACH_Front)
1379  {
1380  if (!object->FrontParticles) object->FrontParticles = GetNewParticleList(object);
1381  pxList = object->FrontParticles;
1382  }
1383  else if (particleProperties.attachment & C4ATTACH_Back)
1384  {
1385  if (!object->BackParticles) object->BackParticles = GetNewParticleList(object);
1386  pxList = object->BackParticles;
1387  }
1388  }
1389 
1390  // no assigned list implies that we are going to use the global particles
1391  if (!pxList)
1392  {
1393  if (!globalParticles) globalParticles = GetNewParticleList();
1394  pxList = globalParticles;
1395  }
1396 
1397  // It is necessary to lock the particle list, because we will have it create a particle first that we are going to modify.
1398  // Inbetween creation of the particle and modification, the particle list's calculations should not be executed
1399  // (this could f.e. lead to the particle being removed before it was fully instantiated).
1400  pxList->Lock();
1401 
1402  // retrieve the fitting chunk for the particle (note that we tell the particle list, we already locked it)
1403  C4ParticleChunk *chunk = pxList->GetFittingParticleChunk(of_def, particleProperties.blitMode, particleProperties.attachment, true);
1404 
1405  // set up chunk to be able to contain enough particles
1406  chunk->ReserveSpace(static_cast<uint32_t>(amount));
1407 
1408  while (amount--)
1409  {
1410  // create a particle in the fitting chunk (note that we tell the particle list, we already locked it)
1411  C4Particle *particle = chunk->AddNewParticle();
1412 
1413  // initialize some more properties
1414  particle->properties = particleProperties;
1415  // this will adjust the initial values of the (possibly cached) particle properties
1416  particle->properties.Floatify();
1417 
1418  // setup some more non-property attributes of the particle
1419  // The particle having lifetime == startingLifetime will force all random values to alway-reevaluate.
1420  // Thus we need to guarantee that even before setting the lifetime (to allow a PV_Random for the lifetime).
1421  particle->lifetime = particle->startingLifetime = 0.0f;
1422  const float lifetime_value = lifetime.GetValue(particle);
1423  // Negative values are not allowed (would crash later); using a value of 0 is most likely visible to the scripter.
1424  if (lifetime_value >= 0.0f)
1425  particle->lifetime = particle->startingLifetime = lifetime_value;
1426 
1427  particle->currentSpeedX = speedX.GetValue(particle);
1428  particle->currentSpeedY = speedY.GetValue(particle);
1429  particle->drawingData.aspect = of_def->Aspect;
1430  particle->drawingData.SetOffset(drawingOffsetX, drawingOffsetY);
1431  particle->SetPosition(x.GetValue(particle) + xoff, y.GetValue(particle) + yoff);
1432  particle->drawingData.SetColor(particle->properties.colorR.GetValue(particle), particle->properties.colorG.GetValue(particle), particle->properties.colorB.GetValue(particle), particle->properties.colorAlpha.GetValue(particle));
1433  particle->drawingData.SetPhase((int)(particle->properties.phase.GetValue(particle) + 0.5f), of_def);
1434  }
1435 
1436  pxList->Unlock();
1437 }
1438 
1439 GLuint C4ParticleSystem::GetIBO(size_t forParticleAmount)
1440 {
1441  PreparePrimitiveRestartIndices(forParticleAmount);
1442  return ibo;
1443 }
1444 
1445 void C4ParticleSystem::PreparePrimitiveRestartIndices(uint32_t forAmount)
1446 {
1447  if (ibo == 0) glGenBuffers(1, &ibo);
1448  // Each particle has 4 vertices and one PRI.
1449  const size_t neededEntryAmount = 5 * forAmount;
1450  const size_t neededIboSize = neededEntryAmount * sizeof(GLuint);
1451  // Nothing to do?
1452  if (ibo_size >= neededIboSize) return;
1453 
1454  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
1455 
1456  // prepare array with indices, separated by special primitive restart index
1457  const uint32_t PRI = 0xffffffff;
1458 
1459  std::vector<GLuint> ibo_data;
1460  ibo_data.reserve(neededEntryAmount);
1461 
1462  unsigned int index = 0;
1463  for (unsigned int i = 0; i < neededEntryAmount; ++i)
1464  {
1465  if ((i+1) % 5 == 0)
1466  ibo_data.push_back(PRI);
1467  else
1468  ibo_data.push_back(index++);
1469  }
1470 
1471  ibo_size = neededEntryAmount * sizeof(GLuint);
1472  glBufferData(GL_ELEMENT_ARRAY_BUFFER, ibo_size, &ibo_data[0], GL_STATIC_DRAW);
1473  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1474 }
1475 #endif
1476 
1478 {
1479 #ifndef USE_CONSOLE
1480  if (ibo != 0) glDeleteBuffers(1, &ibo);
1481  ibo = 0; ibo_size = 0;
1482 
1483  currentSimulationTime = 0;
1485 #endif
1486  // clear definitions even in console mode
1487  definitions.Clear();
1488 }
1489 
1491 {
1492 #ifndef USE_CONSOLE
1493  particleListAccessMutex.Enter();
1494  particleLists.clear();
1495  particleListAccessMutex.Leave();
1496 #endif
1497 }
1498 
1500 {
1501 #ifndef USE_CONSOLE
1502  // seek list
1503  for (C4ParticleDef *def = first; def != 0; def=def->next)
1504  if (def != exclude && def->Name == name)
1505  return def;
1506 #endif
1507  // nothing found
1508  return 0;
1509 }
1510 
1512 {
1513  // the particle definitions update the list in their destructor
1514  while (first)
1515  delete first;
1516 }
void Floatify(float denominator)
const char * getData() const
Definition: StdBuf.h:442
C4String P[P_LAST]
C4Value Value
Definition: C4PropList.h:55
void Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall &call, int texUnit, const StdProjectionMatrix &modelview)
C4Real GetFixedY() const
Definition: C4Object.h:290
C4Config Config
Definition: C4Config.cpp:833
C4ParticleProperties properties
Definition: C4Particles.h:314
C4ParticleValueProvider forceY
Definition: C4Particles.h:206
float Y
Definition: C4Facet.h:118
C4ParticleValueProvider phase
Definition: C4Particles.h:210
friend class C4ParticleChunk
Definition: C4Particles.h:335
C4ParticleValueProvider stretch
Definition: C4Particles.h:205
int32_t VerboseObjectLoading
Definition: C4Config.h:99
C4Game Game
Definition: C4Globals.cpp:52
uint32_t UnsyncedRandom()
Definition: C4Random.cpp:58
void Clear()
Definition: StdBuf.h:466
#define sprintf
Definition: Standard.h:164
C4ParticleSystem Particles
void ResetBlitMode()
Definition: C4Draw.h:192
bool Load(C4Group &hGroup, const char *szName, int iWdt, int iHgt, bool fNoErrIfNotFound, int iFlags)
Definition: C4FacetEx.cpp:78
bool GetPhaseNum(int32_t &rX, int32_t &rY)
Definition: C4Facet.cpp:472
C4ParticleValueProvider forceX
Definition: C4Particles.h:206
bool CollisionBounce(C4Particle *forParticle)
bool CollisionDie(C4Particle *forParticle)
Definition: C4Particles.h:229
GLint GetAttribute(int iAttribute) const
Definition: C4Shader.h:194
#define C4GFXBLIT_ADDITIVE
Definition: C4Surface.h:26
C4ParticleChunk * GetFittingParticleChunk(C4ParticleDef *def, uint32_t blitMode, uint32_t attachment, bool alreadyLocked)
C4ParticleValueProvider colorG
Definition: C4Particles.h:208
static const int vertexCountPerParticle
Definition: C4Particles.h:241
void FreeVAOID(unsigned int vaoid)
Definition: C4DrawGL.cpp:967
int32_t PhasesX
Definition: C4Particles.h:92
bool IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t attachment) const
int32_t Length
Definition: C4Particles.h:91
class C4ParticleList * BackParticles
Definition: C4Object.h:158
Iterator end()
Definition: C4PropList.h:206
bool LoadEntry(const char *szEntryName, char **lpbpBuf, size_t *ipSize=nullptr, int iAppendZeros=0)
Definition: C4Group.cpp:1893
C4Particle * AddNewParticle()
void SetPhase(int phase, C4ParticleDef *sourceDef)
void Set(const C4Value &nValue)
Definition: C4Value.h:134
C4FacetSurface Gfx
Definition: C4Particles.h:90
float positionY
Definition: C4Particles.h:311
float startingLifetime
Definition: C4Particles.h:312
void Set(const C4Facet &cpy)
Definition: C4FacetEx.h:46
bool Exec(C4Object *obj, float timeDelta)
C4ParticleValueProvider colorAlpha
Definition: C4Particles.h:208
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
void Set(C4PropList *dataSource)
void Default()
Definition: C4FacetEx.h:43
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
C4ConfigGraphics Graphics
Definition: C4Config.h:253
bool IsConstant() const
Definition: C4Particles.h:157
int32_t Wdt
Definition: C4Rect.h:30
int32_t FrameCounter
Definition: C4Game.h:128
void SetupMultiBlt(C4ShaderCall &call, const C4BltTransform *pTransform, GLuint baseTex, GLuint overlayTex, GLuint normalTex, DWORD dwOverlayModClr, StdProjectionMatrix *out_modelview)
Definition: C4DrawGL.cpp:361
bool DebugLogF(const char *strMessage...)
Definition: C4Log.cpp:278
bool Load(C4Group &group)
Definition: C4Particles.cpp:89
int32_t y
Definition: C4Rect.h:30
C4V_Type GetType() const
Definition: C4Value.h:161
C4StringTable Strings
Definition: C4Globals.cpp:42
void Reset()
Definition: StdSync.h:160
float lifetime
Definition: C4Particles.h:312
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2078
C4Landscape Landscape
void SetCollisionFunc(const C4Value &source)
bool WaitFor(int)
Definition: StdSync.h:161
Iterator begin()
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
void ReserveSpace(uint32_t forAmount)
C4Real GetFixedX() const
Definition: C4Object.h:289
const int C4SF_MipMap
Definition: C4Surface.h:50
float currentSpeedX
Definition: C4Particles.h:310
GLuint GetIBO(size_t forParticleAmount)
C4Draw * pDraw
Definition: C4Draw.cpp:42
C4Real GetGravity() const
bool GetVAO(unsigned int vaoid, GLuint &vao)
Definition: C4DrawGL.cpp:1013
StdStrBuf Filename
Definition: C4Particles.h:88
C4Weather Weather
Definition: C4Weather.cpp:206
void ReleaseParticleList(C4ParticleList *first, C4ParticleList *second=nullptr)
C4ValueArray * getArray() const
Definition: C4Value.h:118
void Default()
Definition: C4Rect.cpp:26
float GetValue(C4Particle *forParticle)
void Value(const T &rStruct)
Definition: StdCompiler.h:161
const int32_t C4M_Solid
Definition: C4Constants.h:172
float currentSpeedY
Definition: C4Particles.h:310
int32_t GetSize() const
Definition: C4ValueArray.h:36
C4ParticleValueProvider rotation
Definition: C4Particles.h:209
C4ParticleSystemDefinitionList definitions
Definition: C4Particles.h:510
void Exec(float timeDelta=1.f)
#define C4CFN_DefGraphics
Definition: C4Components.h:99
int32_t getInt() const
Definition: C4Value.h:112
bool Compile(char *particle_source, const char *name)
Definition: C4Particles.cpp:50
C4ParticleCollisionCallback collisionCallback
Definition: C4Particles.h:214
unsigned int texName
Definition: C4Surface.h:155
int32_t x
Definition: C4Rect.h:30
void SetPosition(float x, float y)
Definition: C4Particles.h:324
void SetColor(float r, float g, float b, float a=1.0f)
Definition: C4Particles.h:290
virtual void Leave()
Definition: StdSync.h:151
void Set(const C4Value &value)
float TargetX
Definition: C4Facet.h:165
C4String * Key
Definition: C4PropList.h:54
C4ParticleValueProvider speedDampingX
Definition: C4Particles.h:207
void SetPointer(Vertex *startingVertex, bool initial=false)
Definition: C4Particles.h:273
void Create(C4ParticleDef *of_def, C4ParticleValueProvider &x, C4ParticleValueProvider &y, C4ParticleValueProvider &speedX, C4ParticleValueProvider &speedY, C4ParticleValueProvider &lifetime, C4PropList *properties, int amount=1, C4Object *object=nullptr)
virtual void Enter()
Definition: StdSync.h:150
float Hgt
Definition: C4Facet.h:118
int32_t GBackDensity(int32_t x, int32_t y)
Definition: C4Landscape.h:226
unsigned int GenVAOID()
Definition: C4DrawGL.cpp:925
#define C4CFN_ParticleCore
Definition: C4Components.h:48
C4ParticleValueProvider speedDampingY
Definition: C4Particles.h:207
bool Log(const char *szMessage)
Definition: C4Log.cpp:192
float fixtof(const C4Fixed &x)
Definition: C4Real.h:257
C4ParticleDef * previous
Definition: C4Particles.h:86
C4ParticleValueProvider collisionVertex
Definition: C4Particles.h:211
float GetAge() const
Definition: C4Particles.h:317
void DeactivateBlitModulation()
Definition: C4Draw.h:189
C4ParticleList * GetNewParticleList(C4Object *forTarget=nullptr)
void ObjectLabel(uint32_t identifier, uint32_t name, int32_t length, const char *label)
Definition: C4DrawGL.cpp:274
C4ParticleValueProvider collisionDensity
Definition: C4Particles.h:211
C4ParticleValueProvider colorB
Definition: C4Particles.h:208
bool Exec(C4Object *obj, float timeDelta, C4ParticleDef *sourceDef)
class C4ParticleList * FrontParticles
Definition: C4Object.h:158
void SetUniformMatrix4x4(int iUniform, const StdMeshMatrix &matrix)
Definition: C4Shader.h:351
float GetRelativeAge() const
Definition: C4Particles.h:319
C4ParticleValueProvider & operator=(const C4ParticleValueProvider &other)
void Draw(C4TargetFacet cgo, C4Object *obj)
#define INFINITE
Definition: StdSync.h:58
StdStrBuf Name
Definition: C4Particles.h:73
CStdGL * pGL
Definition: C4DrawGL.cpp:905
int32_t Hgt
Definition: C4Rect.h:30
C4Surface * Surface
Definition: C4Facet.h:117
float TargetY
Definition: C4Facet.h:165
C4ParticleValueProvider colorR
Definition: C4Particles.h:208
#define toC4CStrBuf(rBuf)
Definition: StdAdaptors.h:25
C4Shader * GetSpriteShader(int ssc)
Definition: C4DrawGL.cpp:674
C4ParticleDef * GetDef(const char *name, C4ParticleDef *exclude=nullptr)
const int C4FCT_Full
Definition: C4FacetEx.h:26
void CompileFunc(StdCompiler *compiler)
Definition: C4Particles.cpp:39
void Copy()
Definition: StdBuf.h:467
float Wdt
Definition: C4Facet.h:118
struct C4Particle::DrawingData drawingData
void SetPosition(float x, float y, float size, float rotation=0.f, float stretch=1.f)
float X
Definition: C4Facet.h:118
C4ParticleValueProvider size
Definition: C4Particles.h:205
C4ParticleValueProviderID
Definition: C4Particles.h:31
void SetOffset(float x, float y)
Definition: C4Particles.h:267
float positionX
Definition: C4Particles.h:311
void CalculateNextStep()
Definition: C4Particles.h:481
int32_t GetWind(int32_t x, int32_t y)
Definition: C4Weather.cpp:94
C4ParticleDef * next
Definition: C4Particles.h:86
bool CollisionStop(C4Particle *forParticle)
friend class C4ParticleValueProvider
Definition: C4Particles.h:334