Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/qt_gui/src/lib/particles/particle_system.cc @ 7422

Last change on this file since 7422 was 7027, checked in by bensch, 19 years ago

orxonox/trunk: saver and faster emitter

File size: 13.9 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  LoadParam(root, "precache", this, ParticleSystem, precache)
129      .describe("Precaches the ParticleSystem for %1 seconds, %2 times per Second")
130      .defaultValues(2, 1, 25);
131}
132
133/**
134 * @brief loads the Emitters from An XML-Root
135 * @param root the XML-Element to load all emitters from
136 */
137void ParticleSystem::loadEmitters(const TiXmlElement* root)
138{
139  LOAD_PARAM_START_CYCLE(root, element);
140  {
141    BaseObject* emitter = Factory::fabricate(element);
142    if (emitter != NULL)
143    {
144      if (emitter->isA(CL_PARTICLE_EMITTER))
145        this->addEmitter(dynamic_cast<ParticleEmitter*>(emitter));
146      else
147      {
148        PRINTF(2)("Tried to load an Element of type '%s' that should be a ParticleEmitter onto '%s::%s'.\n",
149                  emitter->getClassName(), this->getClassName(), this->getName());
150        delete emitter;
151      }
152    }
153    else
154    {
155      PRINTF(2)("Could not Generate Emitter for system %s::%s (wrong type in XML-format)\n", this->getClassName(), getName());
156    }
157  }
158  LOAD_PARAM_END_CYCLE(element);
159}
160
161/**
162* @param maxCount the maximum count of particles that can be emitted
163 */
164void ParticleSystem::setMaxCount(int maxCount)
165{
166  this->maxCount = maxCount;
167}
168
169// setting properties
170/**
171 *  Sets the lifespan of newly created particles
172*/
173void ParticleSystem::setLifeSpan(float lifeSpan, float randomLifeSpan)
174{
175  this->lifeSpan = lifeSpan;
176  this->randomLifeSpan = randomLifeSpan;
177}
178
179/**
180 *  sets the conserve Factor of newly created particles
181*/
182void ParticleSystem::setConserve(float conserve)
183{
184  if (conserve > 1.0)
185    this->conserve = 1.0;
186  else if (conserve < 0.0)
187    this->conserve = 0.0;
188  else
189    this->conserve = conserve;
190}
191
192/////////////////////////////
193/* Per-Particle Attributes */
194/////////////////////////////
195/**
196 *  sets a key in the radius-animation on a per-particle basis
197 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
198 * @param radius the radius at this position
199 * @param randRadius the randRadius at this position
200*/
201void ParticleSystem::setRadius(float lifeCycleTime, float radius, float randRadius)
202{
203  this->radiusAnim.changeEntry(lifeCycleTime, radius);
204  this->randRadiusAnim.changeEntry(lifeCycleTime, randRadius);
205}
206
207/**
208 *  sets a key in the mass-animation on a per-particle basis
209 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
210 * @param mass the mass at this position
211 * @param randMass the randomMass at this position
212*/
213void ParticleSystem::setMass(float lifeCycleTime, float mass, float randMass)
214{
215  this->massAnim.changeEntry(lifeCycleTime, mass);
216  this->randMassAnim.changeEntry(lifeCycleTime, randMass);
217}
218
219/**
220 *  sets a key in the color-animation on a per-particle basis
221 * @param lifeCycleTime: the time (partilceLifeTime/particleAge) [0-1]
222 * @param red: red
223 * @param green: green
224 * @param blue: blue
225 * @param alpha: alpha
226*/
227void ParticleSystem::setColor(float lifeCycleTime, float red, float green, float blue, float alpha)
228{
229  this->colorAnim[0].changeEntry(lifeCycleTime, red);
230  this->colorAnim[1].changeEntry(lifeCycleTime, green);
231  this->colorAnim[2].changeEntry(lifeCycleTime, blue);
232  this->colorAnim[3].changeEntry(lifeCycleTime, alpha);
233}
234
235/**
236 * @brief adds an Emitter to this System.
237 * @param emitter the Emitter to add.
238 */
239void ParticleSystem::addEmitter(ParticleEmitter* emitter)
240{
241  assert (emitter != NULL);
242  if (emitter->getSystem() != NULL)
243    emitter->getSystem()->removeEmitter(emitter);
244  emitter->system = this;
245  this->emitters.push_back(emitter);
246}
247
248/**
249 * @brief removes a ParticleEmitter from this System
250 * @param emitter the Emitter to remove
251 */
252void ParticleSystem::removeEmitter(ParticleEmitter* emitter)
253{
254  assert (emitter != NULL);
255  emitter->system = NULL;
256  this->emitters.remove(emitter);
257  /*  std::list<ParticleEmitter*>::iterator it = std::find(this->emitters.begin(), this->emitters.end(), emitter);
258  if (it != this->emitters.end())
259    this->emitters.erase(it);*/
260}
261
262/**
263 * @brief does a Precaching, meaning, that the ParticleSystem(and its emitters) will be ticked force
264 * @param seconds: seconds
265 * @param ticksPerSeconds times per Second.
266 */
267void ParticleSystem::precache(unsigned int seconds, unsigned int ticksPerSecond)
268{
269
270  std::list<ParticleEmitter*>::iterator emitter;
271  for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
272    (*emitter)->updateNode(.1), (*emitter)->updateNode(.1);
273
274  PRINTF(4)("Precaching %s::%s %d seconds %d timesPerSecond\n", this->getClassName(), this->getName(), seconds, ticksPerSecond);
275  this->debug();
276  for (unsigned int i = 0; i < seconds*ticksPerSecond; i++)
277    this->tick(1.0/(float)ticksPerSecond);
278}
279
280
281/**
282 *  ticks the system.
283 * @param dt the time to tick all the Particles of the System
284
285   this is used to get all the particles some motion
286*/
287void ParticleSystem::tick(float dt)
288{
289  Particle* tickPart = particles;  // the particle to Tick
290  Particle* prevPart = NULL;
291  while (likely(tickPart != NULL))
292  {
293    // applying force to the System.
294    if (likely (tickPart->mass > 0.0))
295      tickPart->velocity += tickPart->extForce / tickPart->mass * dt;
296
297    tickPart->radius = radiusAnim.getValue(tickPart->lifeCycle)
298                       + randRadiusAnim.getValue(tickPart->lifeCycle) * tickPart->radiusRand;
299
300    tickPart->mass = massAnim.getValue(tickPart->lifeCycle)
301                     + randMassAnim.getValue(tickPart->lifeCycle) * tickPart->massRand;
302
303    tickPart->extForce = Vector(0,0,0);
304
305    // applying Color
306    tickPart->color[0] = this->colorAnim[0].getValue(tickPart->lifeCycle);
307    tickPart->color[1] = this->colorAnim[1].getValue(tickPart->lifeCycle);
308    tickPart->color[2] = this->colorAnim[2].getValue(tickPart->lifeCycle);
309    tickPart->color[3] = this->colorAnim[3].getValue(tickPart->lifeCycle);
310
311    // rendering new position.
312    tickPart->position += tickPart->velocity * dt;
313    tickPart->orientation *= tickPart->momentum *dt;
314
315    // many more to come
316
317    if (this->conserve < 1.0)
318    {
319      tickPart->velocity *= this->conserve;
320      tickPart->momentum *= this->conserve;
321    }
322    // find out if we have to delete tickPart
323    if (unlikely((tickPart->lifeCycle += dt/tickPart->lifeTime) >= 1.0))
324    {
325      // remove the particle from the list
326      if (likely(prevPart != NULL))
327      {
328        prevPart->next = tickPart->next;
329        tickPart->next = this->deadList;
330        this->deadList = tickPart;
331        tickPart = prevPart->next;
332      }
333      else
334      {
335        prevPart = NULL;
336        this->particles = tickPart->next;
337        tickPart->next = this->deadList;
338        this->deadList = tickPart;
339        tickPart = this->particles;
340      }
341      --this->count;
342    }
343    else
344    {
345      prevPart = tickPart;
346      tickPart = tickPart->next;
347    }
348  }
349
350  std::list<ParticleEmitter*>::iterator emitter;
351  for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
352    (*emitter)->tick(dt);
353}
354
355/**
356  *  applies some force to a Particle.
357  * @param field the Field to apply.
358 */
359void ParticleSystem::applyField(Field* field)
360{
361  Particle* tickPart = particles;
362  while (tickPart)
363  {
364    tickPart->extForce += field->calcForce(tickPart->position);
365    tickPart = tickPart->next;
366  }
367}
368
369
370/**
371 * @returns the count of Faces of this ParticleSystem
372 */
373unsigned int ParticleSystem::getFaceCount() const
374{
375  return this->count;
376}
377
378
379/**
380 *  draws all the Particles of this System
381
382   The Cases in this Function all do the same:
383   Drawing all the particles with the appropriate Type.
384   This is just the fastest Way to do this, but will most likely be changed in the future.
385 */
386// void ParticleSystem::draw() const
387// {
388//   glPushAttrib(GL_ENABLE_BIT);
389//
390//   Particle* drawPart = particles;
391//
392//   switch (this->particleType)
393//   {
394//
395//     case PARTICLE_MODEL:
396//       {
397//       }
398//       break;
399//
400//     case PARTICLE_DOT:
401//       glDisable(GL_LIGHTING);
402//       glBegin(GL_POINTS);
403//       while (likely(drawPart != NULL))
404//       {
405//         glColor4fv(drawPart->color);
406//
407//         glLineWidth(drawPart->radius);
408//
409//         glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
410//         drawPart = drawPart->next;
411//       }
412//       glEnd();
413//       break;
414//   }
415//   glPopAttrib();
416// }
417
418/**
419 *  adds a new Particle to the System
420 * @param position the initial position, where the particle gets emitted.
421 * @param velocity the initial velocity of the particle.
422 * @param orientation the initial orientation of the Paritcle.
423 * @param momentum the initial momentum of the Particle (the speed of its rotation).
424 * @param data some more data given by the emitter
425*/
426void ParticleSystem::addParticle(const Vector& position, const Vector& velocity, const Quaternion& orientation, const Quaternion& momentum, unsigned int data)
427{
428  if (this->count <= this->maxCount)
429  {
430    // if it is the first Particle
431    if (unlikely(particles == NULL))
432    {
433      if (likely(deadList != NULL))
434      {
435        this->particles = this->deadList;
436        deadList = deadList->next;
437      }
438      else
439      {
440        PRINTF(5)("Generating new Particle\n");
441        this->particles = new Particle;
442      }
443      this->particles->next = NULL;
444    }
445    // filling the List from the beginning
446    else
447    {
448      Particle* tmpPart;
449      if (likely(deadList != NULL))
450      {
451        tmpPart = this->deadList;
452        deadList = deadList->next;
453      }
454      else
455      {
456        PRINTF(5)("Generating new Particle\n");
457        tmpPart = new Particle;
458      }
459      tmpPart->next = this->particles;
460      this->particles = tmpPart;
461    }
462    particles->lifeTime = this->lifeSpan + (float)(rand()/RAND_MAX)* this->randomLifeSpan;
463    particles->lifeCycle = 0.0;
464    particles->position = position;
465    particles->velocity = velocity;
466
467    particles->orientation = orientation;
468    particles->momentum = momentum;
469
470    //  particle->rotation = ; //! @todo rotation is once again something to be done.
471    particles->massRand = 2*(float)rand()/RAND_MAX -1;
472    particles->radiusRand = 2* (float)rand()/RAND_MAX -1;
473    particles->mass = this->massAnim.getValue(0.0) + this->randMassAnim.getValue(0.0)*particles->massRand;
474    particles->radius = this->radiusAnim.getValue(0.0) + this->randRadiusAnim.getValue(0.0)*particles->radiusRand;
475
476    ++this->count;
477  }
478  else
479    PRINTF(5)("maximum count of particles reached not adding any more\n");
480}
481
482/**
483 *  outputs some nice debug information
484*/
485void ParticleSystem::debug() const
486{
487  PRINT(0)("  ParticleCount: %d emitters: %d, maximumCount: %d :: filled %d%%\n", this->count, this->emitters.size(), this->maxCount, 100*this->count/this->maxCount);
488  if (deadList)
489  {
490    PRINT(0)("  - ParticleDeadList is used: ");
491    int i = 1;
492    Particle* tmpPart = this->deadList;
493    while (tmpPart = tmpPart->next) ++i;
494    PRINT(0)("count: %d\n", i);
495  }
496}
Note: See TracBrowser for help on using the repository browser.