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