Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/new_class_id/src/lib/particles/particle_system.cc @ 9727

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

orxonox/new_class_id: new Executor construct, that is much more typesafe, faster, and easier to extend…

Also changed the LoadParam process, and adapted ScriptEngine calls

Then at the end, some missing headers appeared, and appended them to all the cc-files again.

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