Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/particles/particle_system.cc @ 6626

Last change on this file since 6626 was 6626, checked in by bensch, 18 years ago

trunk: segfault prevention

File size: 14.0 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Benjamin Grauer
13   co-programmer: ...
14*/
15
16#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_GRAPHICS
17
18#include "particle_system.h"
19
20#include "particle_emitter.h"
21
22#include "field.h"
23#include "model.h"
24
25#include "load_param.h"
26#include "factory.h"
27#include "material.h"
28#include "state.h"
29#include "shell_command.h"
30
31#include "parser/tinyxml/tinyxml.h"
32#include <algorithm>
33
34using namespace std;
35/**
36 *  standard constructor
37 * @param maxCount the Count of particles in the System
38 * @param type The Type of the ParticleSystem
39*/
40ParticleSystem::ParticleSystem (unsigned int maxCount)
41{
42  this->init();
43
44  this->setMaxCount(maxCount);
45}
46
47/**
48 *  standard deconstructor
49*/
50ParticleSystem::~ParticleSystem()
51{
52  // deleting all the living Particles
53  while (this->particles)
54  {
55    Particle* tmpDelPart = this->particles;
56    this->particles = this->particles->next;
57    delete tmpDelPart;
58  }
59
60  // deleting all the dead particles
61  while (this->deadList)
62  {
63    Particle* tmpDelPart = this->deadList;
64    this->deadList = this->deadList->next;
65    delete tmpDelPart;
66  }
67
68  while(!this->emitters.empty())
69  {
70    this->removeEmitter(this->emitters.front());
71  }
72
73}
74
75/**
76  \brief initializes the ParticleSystem with default values
77*/
78void ParticleSystem::init()
79{
80  this->setClassID(CL_PARTICLE_SYSTEM, "ParticleSystem");
81
82  this->setMaxCount(PARTICLE_DEFAULT_MAX_COUNT);
83  this->count = 0;
84  this->particles = NULL;
85  this->deadList = NULL;
86  this->setConserve(1);
87  this->setLifeSpan(1);
88
89  this->toList(OM_ENVIRON);
90}
91
92
93/**
94 * loads Parameters from a TiXmlElement
95 * @param root the XML-element to load from.
96 */
97void ParticleSystem::loadParams(const TiXmlElement* root)
98{
99  WorldEntity::loadParams(root);
100  PhysicsInterface::loadParams(root);
101
102  LoadParam(root, "max-count", this, ParticleSystem, setMaxCount)
103  .describe("the maximal count of Particles, that can be emitted into this system");
104
105  LoadParam(root, "life-span", this, ParticleSystem, setLifeSpan)
106  .describe("sets the life-span of the Particles.");
107
108  LoadParam(root, "conserve", this, ParticleSystem, setConserve)
109  .describe("sets the Conserve factor of the Particles (1.0: they keep all their energy, 0.0:they keep no energy)");
110
111  LoadParamXML(root, "emitters", this, ParticleSystem, loadEmitters);
112
113  LOAD_PARAM_START_CYCLE(root, element);
114  {
115    element->ToText();
116    // PER-PARTICLE-ATTRIBUTES:
117    LoadParam_CYCLE(element, "radius", this, ParticleSystem, setRadius)
118    .describe("The Radius of each particle over time (TimeIndex [0-1], radius at TimeIndex, randomRadius at TimeIndex)");
119
120    LoadParam_CYCLE(element, "mass", this, ParticleSystem, setMass)
121    .describe("The Mass of each particle over time (TimeIndex: [0-1], mass at TimeIndex, randomMass at TimeIndex)");
122
123    LoadParam_CYCLE(element, "color", this, ParticleSystem, setColor)
124    .describe("The Color of each particle over time (TimeIndex: [0-1], red: [0-1], green: [0-1], blue: [0-1], alpha: [0-1])");
125  }
126  LOAD_PARAM_END_CYCLE(element);
127}
128
129/**
130 * @brief loads the Emitters from An XML-Root
131 * @param root the XML-Element to load all emitters from
132 */
133void ParticleSystem::loadEmitters(const TiXmlElement* root)
134{
135  LOAD_PARAM_START_CYCLE(root, element);
136  {
137    BaseObject* emitter = Factory::fabricate(element);
138    if (emitter->isA(CL_PARTICLE_EMITTER))
139      this->addEmitter(dynamic_cast<ParticleEmitter*>(emitter));
140    else
141    {
142      PRINTF(2)("Tried to load an Element of type '%s' that should be a ParticleEmitter onto '%s::%s'.\n",
143                emitter->getClassName(), this->getClassName(), this->getName());
144      delete emitter;
145    }
146  }
147  LOAD_PARAM_END_CYCLE(element);
148}
149
150/**
151* @param maxCount the maximum count of particles that can be emitted
152 */
153void ParticleSystem::setMaxCount(int maxCount)
154{
155  this->maxCount = maxCount;
156}
157
158// setting properties
159/**
160 *  Sets the lifespan of newly created particles
161*/
162void ParticleSystem::setLifeSpan(float lifeSpan, float randomLifeSpan)
163{
164  this->lifeSpan = lifeSpan;
165  this->randomLifeSpan = randomLifeSpan;
166}
167
168/**
169 *  sets the conserve Factor of newly created particles
170*/
171void ParticleSystem::setConserve(float conserve)
172{
173  if (conserve > 1.0)
174    this->conserve = 1.0;
175  else if (conserve < 0.0)
176    this->conserve = 0.0;
177  else
178    this->conserve = conserve;
179}
180
181/////////////////////////////
182/* Per-Particle Attributes */
183/////////////////////////////
184/**
185 *  sets a key in the radius-animation on a per-particle basis
186 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
187 * @param radius the radius at this position
188 * @param randRadius the randRadius at this position
189*/
190void ParticleSystem::setRadius(float lifeCycleTime, float radius, float randRadius)
191{
192  this->radiusAnim.changeEntry(lifeCycleTime, radius);
193  this->randRadiusAnim.changeEntry(lifeCycleTime, randRadius);
194}
195
196/**
197 *  sets a key in the mass-animation on a per-particle basis
198 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
199 * @param mass the mass at this position
200 * @param randMass the randomMass at this position
201*/
202void ParticleSystem::setMass(float lifeCycleTime, float mass, float randMass)
203{
204  this->massAnim.changeEntry(lifeCycleTime, mass);
205  this->randMassAnim.changeEntry(lifeCycleTime, randMass);
206}
207
208/**
209 *  sets a key in the color-animation on a per-particle basis
210 * @param lifeCycleTime: the time (partilceLifeTime/particleAge) [0-1]
211 * @param red: red
212 * @param green: green
213 * @param blue: blue
214 * @param alpha: alpha
215*/
216void ParticleSystem::setColor(float lifeCycleTime, float red, float green, float blue, float alpha)
217{
218  this->colorAnim[0].changeEntry(lifeCycleTime, red);
219  this->colorAnim[1].changeEntry(lifeCycleTime, green);
220  this->colorAnim[2].changeEntry(lifeCycleTime, blue);
221  this->colorAnim[3].changeEntry(lifeCycleTime, alpha);
222}
223
224
225void ParticleSystem::addEmitter(ParticleEmitter* emitter)
226{
227  assert (emitter != NULL);
228  if (emitter->getSystem() != NULL)
229    emitter->getSystem()->removeEmitter(emitter);
230  emitter->system = this;
231  this->emitters.push_back(emitter);
232}
233
234
235void ParticleSystem::removeEmitter(ParticleEmitter* emitter)
236{
237  assert (emitter != NULL);
238  emitter->system = NULL;
239  this->emitters.remove(emitter);
240  /*  std::list<ParticleEmitter*>::iterator it = std::find(this->emitters.begin(), this->emitters.end(), emitter);
241  if (it != this->emitters.end())
242    this->emitters.erase(it);*/
243}
244
245/**
246 *  ticks the system.
247 * @param dt the time to tick all the Particles of the System
248
249   this is used to get all the particles some motion
250*/
251void ParticleSystem::tick(float dt)
252{
253  Particle* tickPart = particles;  // the particle to Tick
254  Particle* prevPart = NULL;
255  while (likely(tickPart != NULL))
256  {
257    // applying force to the System.
258    if (likely (tickPart->mass > 0.0))
259      tickPart->velocity += tickPart->extForce / tickPart->mass * dt;
260    // rendering new position.
261    tickPart->position += tickPart->velocity * dt;
262    tickPart->orientation += tickPart->momentum *dt;
263
264    tickPart->radius = radiusAnim.getValue(tickPart->lifeCycle)
265                       + randRadiusAnim.getValue(tickPart->lifeCycle) * tickPart->radiusRand;
266
267    tickPart->mass = massAnim.getValue(tickPart->lifeCycle)
268                     + randMassAnim.getValue(tickPart->lifeCycle) * tickPart->massRand;
269
270    tickPart->extForce = Vector(0,0,0);
271
272    // applying Color
273    tickPart->color[0] = this->colorAnim[0].getValue(tickPart->lifeCycle);
274    tickPart->color[1] = this->colorAnim[1].getValue(tickPart->lifeCycle);
275    tickPart->color[2] = this->colorAnim[2].getValue(tickPart->lifeCycle);
276    tickPart->color[3] = this->colorAnim[3].getValue(tickPart->lifeCycle);
277
278    // many more to come
279
280    if (this->conserve < 1.0)
281    {
282      tickPart->velocity *= this->conserve;
283      tickPart->momentum *= this->conserve;
284    }
285    // find out if we have to delete tickPart
286    if (unlikely((tickPart->lifeCycle += dt/tickPart->lifeTime) >= 1.0))
287    {
288      // remove the particle from the list
289      if (likely(prevPart != NULL))
290      {
291        prevPart->next = tickPart->next;
292        tickPart->next = this->deadList;
293        this->deadList = tickPart;
294        tickPart = prevPart->next;
295      }
296      else
297      {
298        prevPart = NULL;
299        this->particles = tickPart->next;
300        tickPart->next = this->deadList;
301        this->deadList = tickPart;
302        tickPart = this->particles;
303      }
304      --this->count;
305    }
306    else
307    {
308      prevPart = tickPart;
309      tickPart = tickPart->next;
310    }
311  }
312
313  std::list<ParticleEmitter*>::iterator emitter;
314  for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
315    (*emitter)->tick(dt);
316}
317
318/**
319  *  applies some force to a Particle.
320  * @param field the Field to apply.
321 */
322void ParticleSystem::applyField(Field* field)
323{
324  Particle* tickPart = particles;
325  while (tickPart)
326  {
327    tickPart->extForce += field->calcForce(tickPart->position);
328    tickPart = tickPart->next;
329  }
330}
331
332
333/**
334 * @returns the count of Faces of this ParticleSystem
335 */
336unsigned int ParticleSystem::getFaceCount() const
337{
338  return this->count;
339}
340
341
342/**
343 *  draws all the Particles of this System
344
345   The Cases in this Function all do the same:
346   Drawing all the particles with the appropriate Type.
347   This is just the fastest Way to do this, but will most likely be changed in the future.
348 */
349// void ParticleSystem::draw() const
350// {
351//   glPushAttrib(GL_ENABLE_BIT);
352//
353//   Particle* drawPart = particles;
354//
355//   switch (this->particleType)
356//   {
357//     case PARTICLE_SPARK:
358//       glDisable(GL_LIGHTING);
359//       glDepthMask(GL_FALSE);
360//       //glEnable(GL_LINE_SMOOTH);
361//       glEnable(GL_BLEND);
362//
363//       glBegin(GL_LINES);
364//       while (likely(drawPart != NULL))
365//       {
366//         glColor4fv(drawPart->color);
367//         glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
368//         glVertex3f(drawPart->position.x - drawPart->velocity.x * drawPart->radius,
369//                    drawPart->position.y - drawPart->velocity.y * drawPart->radius,
370//                    drawPart->position.z - drawPart->velocity.z * drawPart->radius);
371//         drawPart = drawPart->next;
372//       }
373//       glEnd();
374//       break;
375//
376//     case PARTICLE_MODEL:
377//       {
378//         GLfloat matrix[4][4];
379//
380//         if (likely(this->getModel() != NULL))
381//           while (likely(drawPart != NULL))
382//         {
383//           glPushMatrix();
384//           glMatrixMode(GL_MODELVIEW);
385//           /* move */
386//           glTranslatef(drawPart->position.x, drawPart->position.y, drawPart->position.z);
387//           /* scale */
388//           glScalef(drawPart->radius, drawPart->radius, drawPart->radius);
389//           /* rotate */
390//           drawPart->orientation.matrix (matrix);
391//           glMultMatrixf((float*)matrix);
392//
393//           this->getModel()->draw();
394//
395//           glPopMatrix();
396//           drawPart = drawPart->next;
397//         }
398//         else
399//           PRINTF(2)("no model loaded onto ParticleSystem-%s\n", this->getName());
400//       }
401//       break;
402//
403//     case PARTICLE_DOT:
404//       glDisable(GL_LIGHTING);
405//       glBegin(GL_POINTS);
406//       while (likely(drawPart != NULL))
407//       {
408//         glColor4fv(drawPart->color);
409//
410//         glLineWidth(drawPart->radius);
411//
412//         glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
413//         drawPart = drawPart->next;
414//       }
415//       glEnd();
416//       break;
417//   }
418//   glPopAttrib();
419// }
420
421/**
422 *  adds a new Particle to the System
423 * @param position the initial position, where the particle gets emitted.
424 * @param velocity the initial velocity of the particle.
425 * @param orientation the initial orientation of the Paritcle.
426 * @param momentum the initial momentum of the Particle (the speed of its rotation).
427 * @param data some more data given by the emitter
428*/
429void ParticleSystem::addParticle(const Vector& position, const Vector& velocity, const Quaternion& orientation, const Quaternion& momentum, unsigned int data)
430{
431  if (this->count <= this->maxCount)
432  {
433    // if it is the first Particle
434    if (unlikely(particles == NULL))
435    {
436      if (likely(deadList != NULL))
437      {
438        this->particles = this->deadList;
439        deadList = deadList->next;
440      }
441      else
442      {
443        PRINTF(5)("Generating new Particle\n");
444        this->particles = new Particle;
445      }
446      this->particles->next = NULL;
447    }
448    // filling the List from the beginning
449    else
450    {
451      Particle* tmpPart;
452      if (likely(deadList != NULL))
453      {
454        tmpPart = this->deadList;
455        deadList = deadList->next;
456      }
457      else
458      {
459        PRINTF(5)("Generating new Particle\n");
460        tmpPart = new Particle;
461      }
462      tmpPart->next = this->particles;
463      this->particles = tmpPart;
464    }
465    particles->lifeTime = this->lifeSpan + (float)(rand()/RAND_MAX)* this->randomLifeSpan;
466    particles->lifeCycle = 0.0;
467    particles->position = position;
468    particles->velocity = velocity;
469
470    particles->orientation = orientation;
471    particles->momentum = momentum;
472
473    //  particle->rotation = ; //! @todo rotation is once again something to be done.
474    particles->massRand = 2*(float)rand()/RAND_MAX -1;
475    particles->radiusRand = 2* (float)rand()/RAND_MAX -1;
476    particles->mass = this->massAnim.getValue(0.0) + this->randMassAnim.getValue(0.0)*particles->massRand;
477    particles->radius = this->radiusAnim.getValue(0.0) + this->randRadiusAnim.getValue(0.0)*particles->radiusRand;
478
479    ++this->count;
480  }
481  else
482    PRINTF(5)("maximum count of particles reached not adding any more\n");
483}
484
485/**
486 *  outputs some nice debug information
487*/
488void ParticleSystem::debug() const
489{
490  PRINT(0)("  ParticleCount: %d, maximumCount: %d :: filled %d%%\n", this->count, this->maxCount, 100*this->count/this->maxCount);
491  if (deadList)
492  {
493    PRINT(0)("  - ParticleDeadList is used: ");
494    int i = 1;
495    Particle* tmpPart = this->deadList;
496    while (tmpPart = tmpPart->next) ++i;
497    PRINT(0)("count: %d\n", i);
498  }
499}
Note: See TracBrowser for help on using the repository browser.