Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/world_entities/particles/particle_system.cc

Last change on this file was 10658, checked in by bknecht, 17 years ago

changes from landauf

File size: 15.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 "physics/fields/field.h"
23#include "model.h"
24
25#include "util/loading/load_param_xml.h"
26#include "util/loading/factory.h"
27#include "material.h"
28#include "state.h"
29#include "shell_command.h"
30
31#include <algorithm>
32
33ObjectListDefinition(ParticleSystem);
34
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->registerObject(this, ParticleSystem::_objectList);
43
44  this->setMaxCount(PARTICLE_DEFAULT_MAX_COUNT);
45  this->count = 0;
46  this->particles = NULL;
47  this->deadList = NULL;
48  this->conserve = 1.0;
49  this->lifeSpan = 1.0; this->randomLifeSpan = 0.0;
50
51  this->toList(OM_ENVIRON);
52
53  this->maxCount = maxCount;
54}
55
56/**
57 * @brief standard deconstructor
58 */
59ParticleSystem::~ParticleSystem()
60{
61  // deleting all the living Particles
62  while (this->particles)
63  {
64    Particle* tmpDelPart = this->particles;
65    this->particles = this->particles->next;
66    delete tmpDelPart;
67  }
68
69  // deleting all the dead particles
70  while (this->deadList)
71  {
72    Particle* tmpDelPart = this->deadList;
73    this->deadList = this->deadList->next;
74    delete tmpDelPart;
75  }
76
77  while(!this->emitters.empty())
78  {
79    this->removeEmitter(this->emitters.front());
80  }
81
82}
83
84
85/**
86 * @brief loads Parameters from a TiXmlElement
87 * @param root the XML-element to load from.
88 */
89void ParticleSystem::loadParams(const TiXmlElement* root)
90{
91  WorldEntity::loadParams(root);
92  PhysicsInterface::loadParams(root);
93
94  LoadParam(root, "max-count", this, ParticleSystem, setMaxCount)
95  .describe("the maximal count of Particles, that can be emitted into this system");
96
97  LoadParam(root, "life-span", this, ParticleSystem, setLifeSpan)
98  .describe("sets the life-span of the Particles.");
99
100  LoadParam(root, "conserve", this, ParticleSystem, setConserve)
101  .describe("sets the Conserve factor of the Particles (1.0: they keep all their energy, 0.0:they keep no energy)");
102
103  LoadParamXML(root, "emitters", this, ParticleSystem, loadEmitters);
104
105  LOAD_PARAM_START_CYCLE(root, element);
106  {
107    element->ToText();
108    // PER-PARTICLE-ATTRIBUTES:
109    LoadParam_CYCLE(element, "radius", this, ParticleSystem, setRadius)
110    .describe("The Radius of each particle over time (TimeIndex [0-1], radius at TimeIndex, randomRadius at TimeIndex)");
111
112    LoadParam_CYCLE(element, "mass", this, ParticleSystem, setMass)
113    .describe("The Mass of each particle over time (TimeIndex: [0-1], mass at TimeIndex, randomMass at TimeIndex)");
114
115    LoadParam_CYCLE(element, "color", this, ParticleSystem, setColor)
116    .describe("The Color of each particle over time (TimeIndex: [0-1], red: [0-1], green: [0-1], blue: [0-1], alpha: [0-1])");
117  }
118  LOAD_PARAM_END_CYCLE(element);
119
120  LoadParam(root, "precache", this, ParticleSystem, precache)
121  .describe("Precaches the ParticleSystem for %1 seconds, %2 times per Second")
122  .defaultValues(1.0, 25.0);
123}
124
125/**
126 * @brief loads the Emitters from An XML-Root
127 * @param root the XML-Element to load all emitters from
128 */
129void ParticleSystem::loadEmitters(const TiXmlElement* root)
130{
131  LOAD_PARAM_START_CYCLE(root, element);
132  {
133    BaseObject* emitter = Factory::fabricate(element);
134    if (emitter != NULL)
135    {
136      if (emitter->isA(ParticleEmitter::staticClassID()))
137      {
138        this->addEmitter(dynamic_cast<ParticleEmitter*>(emitter));
139      }
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->getClassCName(), this->getClassCName(), this->getCName());
144        delete emitter;
145      }
146    }
147    else
148    {
149      PRINTF(2)("Could not Generate Emitter for system %s::%s (wrong type in XML-format)\n", this->getClassCName(), getCName());
150    }
151  }
152  LOAD_PARAM_END_CYCLE(element);
153}
154
155/**
156 * @param maxCount the maximum count of particles that can be emitted
157 */
158void ParticleSystem::setMaxCount(unsigned int maxCount)
159{
160  this->maxCount = maxCount;
161  PRINTF(4)("MAXCOUNT of %s::%s is %d\n", this->getClassCName(), this->getCName(), maxCount);
162}
163
164// setting properties
165/**
166 * @brief Sets the lifespan of newly created particles
167 * @param lifeSpan the LifeSpan of each particle in the System
168 * @param randomLifeSpan the Deviation from lifeSpan (random Value).
169*/
170void ParticleSystem::setLifeSpan(float lifeSpan, float randomLifeSpan)
171{
172  this->lifeSpan = lifeSpan;
173  this->randomLifeSpan = randomLifeSpan;
174  PRINTF(4)("LifeTime of %s::%s is %f\n", this->getClassCName(), this->getCName(), lifeSpan);
175}
176
177/**
178 * @brief sets the conserve Factor of newly created particles
179 * @param conserve sets the conserve factor of each particle.
180 * Conserve is the ammount of energy a particle takes from the last Frame into the next.
181 * A Value of 1 means, that all energy is conserved, a Value of 0 means infinit friction.
182 */
183void ParticleSystem::setConserve(float conserve)
184{
185  if (conserve > 1.0)
186    this->conserve = 1.0;
187  else if (conserve < 0.0)
188    this->conserve = 0.0;
189  else
190    this->conserve = conserve;
191
192  PRINTF(4)("Conserve of %s::%s is %f\n", this->getClassCName(), this->getCName(), conserve);
193}
194
195/////////////////////////////
196/* Per-Particle Attributes */
197/////////////////////////////
198/**
199 * @brief sets a key in the radius-animation on a per-particle basis
200 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
201 * @param radius the radius at this position
202 * @param randRadius the randRadius at this position
203*/
204void ParticleSystem::setRadius(float lifeCycleTime, float radius, float randRadius)
205{
206  this->radiusAnim.changeValue(lifeCycleTime, radius);
207  this->randRadiusAnim.changeValue(lifeCycleTime, randRadius);
208
209  PRINTF(4)("Radius of %s::%s at timeSlice %f is %f with a Random of %f\n",
210    this->getClassCName(), this->getCName(), lifeCycleTime, radius, randRadius);
211}
212
213/**
214 * @brief sets a key in the mass-animation on a per-particle basis
215 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
216 * @param mass the mass at this position
217 * @param randMass the randomMass at this position
218*/
219void ParticleSystem::setMass(float lifeCycleTime, float mass, float randMass)
220{
221  this->massAnim.changeValue(lifeCycleTime, mass);
222  this->randMassAnim.changeValue(lifeCycleTime, randMass);
223}
224
225/**
226 * @brief sets a key in the color-animation on a per-particle basis
227 * @param lifeCycleTime: the time (partilceLifeTime/particleAge) [0-1]
228 * @param red: red
229 * @param green: green
230 * @param blue: blue
231 * @param alpha: alpha
232*/
233void ParticleSystem::setColor(float lifeCycleTime, float red, float green, float blue, float alpha)
234{
235  this->colorAnim[0].changeValue(lifeCycleTime, red);
236  this->colorAnim[1].changeValue(lifeCycleTime, green);
237  this->colorAnim[2].changeValue(lifeCycleTime, blue);
238  this->colorAnim[3].changeValue(lifeCycleTime, alpha);
239
240  PRINTF(4)("Color of %s::%s on timeslice %f is r:%f g:%f b:%f a:%f\n",
241    this->getClassCName(), this->getCName(), lifeCycleTime, red, green, blue, alpha);
242}
243
244/**
245 * @brief sets a key in the color-animation on a per-particle basis
246 * @param lifeCycleTime: the time (partilceLifeTime/particleAge) [0-1]
247 * @param color the Color.
248 */
249void ParticleSystem::setColor(float lifeCycleTime, const Color& color)
250{
251  this->setColor(lifeCycleTime, color.r(), color.g(), color.b(), color.a());
252}
253
254
255/**
256 * @brief adds an Emitter to this System.
257 * @param emitter the Emitter to add.
258 */
259void ParticleSystem::addEmitter(ParticleEmitter* emitter)
260{
261  assert (emitter != NULL);
262  if (emitter->getSystem() != NULL)
263    emitter->getSystem()->removeEmitter(emitter);
264  emitter->system = this;
265  this->emitters.push_back(emitter);
266
267}
268
269/**
270 * @brief removes a ParticleEmitter from this System
271 * @param emitter the Emitter to remove
272 */
273void ParticleSystem::removeEmitter(ParticleEmitter* emitter)
274{
275  assert (emitter != NULL);
276  emitter->system = NULL;
277  this->emitters.remove(emitter);
278  /*  std::list<ParticleEmitter*>::iterator it = std::find(this->emitters.begin(), this->emitters.end(), emitter);
279  if (it != this->emitters.end())
280    this->emitters.erase(it);*/
281}
282
283
284/**
285 * attackes
286 * @param node
287 */
288void ParticleSystem::attachEmmittersTo(PNode* node, const Vector& offset, const Quaternion& dir)
289{
290  std::list<ParticleEmitter*>::iterator it = this->emitters.begin();
291  for( ; it != this->emitters.end(); it++)
292  {
293    //
294    (*it)->setParent(node);
295    (*it)->setRelCoor(offset);
296    (*it)->setRelDir(dir);
297  }
298}
299
300
301
302/**
303 * @brief does a Precaching, meaning, that the ParticleSystem(and its emitters) will be ticked force
304 * @param seconds: seconds
305 * @param ticksPerSeconds times per Second.
306 */
307void ParticleSystem::precache(unsigned int seconds, unsigned int ticksPerSecond)
308{
309  std::list<ParticleEmitter*>::iterator emitter;
310  for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
311    (*emitter)->updateNode(.1), (*emitter)->updateNode(.1);
312
313  PRINTF(4)("Precaching %s::%s %d seconds %d timesPerSecond\n", this->getClassCName(), this->getCName(), seconds, ticksPerSecond);
314  for (unsigned int i = 0; i < seconds*ticksPerSecond; i++)
315    this->tick(1.0/(float)ticksPerSecond);
316}
317
318
319/**
320 * @brief ticks the system.
321 * @param dt the time to tick all the Particles of the System
322
323   this is used to get all the particles some motion
324*/
325void ParticleSystem::tick(float dt)
326{
327  Particle* tickPart = particles;  // the particle to Tick
328  Particle* prevPart = NULL;
329  while (likely(tickPart != NULL))
330  {
331    // applying force to the System.
332    if (likely (tickPart->mass > 0.0))
333      tickPart->velocity += tickPart->extForce / tickPart->mass * dt;
334
335    tickPart->radius = radiusAnim.getValue(tickPart->lifeCycle)
336                       + randRadiusAnim.getValue(tickPart->lifeCycle) * tickPart->radiusRand;
337
338    tickPart->mass = massAnim.getValue(tickPart->lifeCycle)
339                     + randMassAnim.getValue(tickPart->lifeCycle) * tickPart->massRand;
340
341    //tickPart->extForce = Vector(0,0,0);
342
343    // applying Color
344    this->colorAnim[0].getValue(tickPart->color[0], tickPart->lifeCycle);
345    this->colorAnim[1].getValue(tickPart->color[1], tickPart->lifeCycle);
346    this->colorAnim[2].getValue(tickPart->color[2], tickPart->lifeCycle);
347    this->colorAnim[3].getValue(tickPart->color[3], tickPart->lifeCycle);
348
349    // rendering new position.
350    tickPart->position += tickPart->velocity * dt;
351    tickPart->orientation *= tickPart->momentum *dt;
352
353    // many more to come
354
355    if (this->conserve < 1.0)
356    {
357      tickPart->velocity *= this->conserve;
358      tickPart->momentum *= this->conserve;
359    }
360    // find out if we have to delete tickPart
361    if (unlikely((tickPart->lifeCycle += dt/tickPart->lifeTime) >= 1.0))
362    {
363      // remove the particle from the list
364      if (likely(prevPart != NULL))
365      {
366        prevPart->next = tickPart->next;
367        tickPart->next = this->deadList;
368        this->deadList = tickPart;
369        tickPart = prevPart->next;
370      }
371      else
372      {
373        prevPart = NULL;
374        this->particles = tickPart->next;
375        tickPart->next = this->deadList;
376        this->deadList = tickPart;
377        tickPart = this->particles;
378      }
379      --this->count;
380    }
381    else
382    {
383      prevPart = tickPart;
384      tickPart = tickPart->next;
385    }
386  }
387
388  std::list<ParticleEmitter*>::iterator emitter;
389  for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
390    (*emitter)->tick(dt);
391}
392
393/**
394  *  applies some force to a Particle.
395  * @param field the Field to apply.
396 */
397void ParticleSystem::applyField(const Field* field)
398{
399  Particle* tickPart = particles;
400  while (tickPart)
401  {
402    tickPart->extForce += field->calcForce(tickPart->position);
403    tickPart = tickPart->next;
404  }
405}
406
407
408/**
409 * @returns the count of Faces of this ParticleSystem
410 */
411unsigned int ParticleSystem::getFaceCount() const
412{
413  return this->count;
414}
415
416/**
417 * @brief adds a new Particle to the System
418 * @param position the initial position, where the particle gets emitted.
419 * @param velocity the initial velocity of the particle.
420 * @param orientation the initial orientation of the Paritcle.
421 * @param momentum the initial momentum of the Particle (the speed of its rotation).
422 * @param data some more data given by the emitter
423*/
424void ParticleSystem::addParticle(const Vector& position, const Vector& velocity, const Quaternion& orientation, const Quaternion& momentum, const Vector& extForce, unsigned int data)
425{
426  if (this->count <= this->maxCount)
427  {
428    // if it is the first Particle
429    if (unlikely(particles == NULL))
430    {
431      if (likely(deadList != NULL))
432      {
433        this->particles = this->deadList;
434        deadList = deadList->next;
435      }
436      else
437      {
438        PRINTF(5)("Generating new Particle\n");
439        this->particles = new Particle;
440      }
441      this->particles->next = NULL;
442    }
443    // filling the List from the beginning
444    else
445    {
446      Particle* tmpPart;
447      if (likely(deadList != NULL))
448      {
449        tmpPart = this->deadList;
450        deadList = deadList->next;
451      }
452      else
453      {
454        PRINTF(5)("Generating new Particle\n");
455        tmpPart = new Particle;
456      }
457      tmpPart->next = this->particles;
458      this->particles = tmpPart;
459    }
460    particles->lifeTime = this->lifeSpan + (float)(rand()/RAND_MAX)* this->randomLifeSpan;
461    particles->lifeCycle = 0.0;
462    particles->position = position;
463    particles->velocity = velocity;
464    particles->extForce = extForce;
465
466    particles->orientation = orientation;
467    particles->momentum = momentum;
468
469    //  particle->rotation = ; //! @todo rotation is once again something to be done.
470    particles->massRand = 2*(float)rand()/RAND_MAX -1;
471    particles->radiusRand = 2* (float)rand()/RAND_MAX -1;
472    particles->mass = this->massAnim.getValue(0.0) + this->randMassAnim.getValue(0.0)*particles->massRand;
473    particles->radius = this->radiusAnim.getValue(0.0) + this->randRadiusAnim.getValue(0.0)*particles->radiusRand;
474
475    ++this->count;
476  }
477  else
478    PRINTF(4)("maximum count of particles reached not adding any more\n");
479}
480
481/**
482 *  outputs some nice debug information
483*/
484void ParticleSystem::debug() const
485{
486  PRINT(0)("  ParticleCount: %d emitters: %d, maximumCount: %d :: filled %d%%\n",
487           this->count,
488           this->emitters.size(),
489           this->maxCount,
490           ((this->maxCount!=0)?100*this->count/this->maxCount:0));
491
492
493  PRINT(0)("  Coloring sceme: r:"), this->colorAnim[0].debug();
494  PRINT(0)("  Coloring sceme: g:"), this->colorAnim[1].debug();
495  PRINT(0)("  Coloring sceme: b:"), this->colorAnim[2].debug();
496  PRINT(0)("  Coloring sceme: a:"), this->colorAnim[3].debug();
497
498  if (likely(this->deadList != NULL))
499  {
500    PRINT(0)("  - ParticleDeadList is used: ");
501    int i = 1;
502    Particle* tmpPart = this->deadList;
503    while ((tmpPart = tmpPart->next) != NULL) { ++i; }
504    PRINT(0)("count: %d\n", i);
505  }
506}
Note: See TracBrowser for help on using the repository browser.