Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: merged the proxy bache back with no conflicts

File size: 14.6 KB
RevLine 
[4597]1/*
[1853]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.
[1855]10
11   ### File Specific:
[3925]12   main-programmer: Benjamin Grauer
[1855]13   co-programmer: ...
[1853]14*/
15
[7301]16#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_GRAPHICS
[1853]17
[3925]18#include "particle_system.h"
[1853]19
[3930]20#include "particle_emitter.h"
[4338]21
22#include "field.h"
[5511]23#include "model.h"
[4338]24
[7193]25#include "util/loading/load_param.h"
26#include "util/loading/factory.h"
[3942]27#include "material.h"
[4338]28#include "state.h"
[5446]29#include "shell_command.h"
[3930]30
[5944]31#include "parser/tinyxml/tinyxml.h"
[6612]32#include <algorithm>
[4338]33
[9406]34
[3245]35/**
[4836]36 *  standard constructor
37 * @param maxCount the Count of particles in the System
38 * @param type The Type of the ParticleSystem
[3245]39*/
[6621]40ParticleSystem::ParticleSystem (unsigned int maxCount)
[3365]41{
[7300]42  this->setClassID(CL_PARTICLE_SYSTEM, "ParticleSystem");
[4597]43
[7300]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;
[3365]54}
[1853]55
[4677]56/**
[9656]57 * @brief standard deconstructor
58 */
[4176]59ParticleSystem::~ParticleSystem()
[3543]60{
[6625]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  }
[4123]68
[6625]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  }
[4176]76
[6625]77  while(!this->emitters.empty())
78  {
79    this->removeEmitter(this->emitters.front());
80  }
[6623]81
[3543]82}
[1853]83
[4602]84
85/**
[9656]86 * @brief loads Parameters from a TiXmlElement
[4602]87 * @param root the XML-element to load from.
88 */
89void ParticleSystem::loadParams(const TiXmlElement* root)
90{
[6512]91  WorldEntity::loadParams(root);
92  PhysicsInterface::loadParams(root);
[4602]93
[5671]94  LoadParam(root, "max-count", this, ParticleSystem, setMaxCount)
[6625]95  .describe("the maximal count of Particles, that can be emitted into this system");
[4727]96
[5671]97  LoadParam(root, "life-span", this, ParticleSystem, setLifeSpan)
[6625]98  .describe("sets the life-span of the Particles.");
[4602]99
[5671]100  LoadParam(root, "conserve", this, ParticleSystem, setConserve)
[6625]101  .describe("sets the Conserve factor of the Particles (1.0: they keep all their energy, 0.0:they keep no energy)");
[4725]102
[6623]103  LoadParamXML(root, "emitters", this, ParticleSystem, loadEmitters);
[6222]104
[5654]105  LOAD_PARAM_START_CYCLE(root, element);
[4727]106  {
[5654]107    element->ToText();
[6625]108    // PER-PARTICLE-ATTRIBUTES:
[5654]109    LoadParam_CYCLE(element, "radius", this, ParticleSystem, setRadius)
[6625]110    .describe("The Radius of each particle over time (TimeIndex [0-1], radius at TimeIndex, randomRadius at TimeIndex)");
[4725]111
[5654]112    LoadParam_CYCLE(element, "mass", this, ParticleSystem, setMass)
[6625]113    .describe("The Mass of each particle over time (TimeIndex: [0-1], mass at TimeIndex, randomMass at TimeIndex)");
[4725]114
[5654]115    LoadParam_CYCLE(element, "color", this, ParticleSystem, setColor)
[6625]116    .describe("The Color of each particle over time (TimeIndex: [0-1], red: [0-1], green: [0-1], blue: [0-1], alpha: [0-1])");
[4727]117  }
[5654]118  LOAD_PARAM_END_CYCLE(element);
[6629]119
120  LoadParam(root, "precache", this, ParticleSystem, precache)
[7300]121  .describe("Precaches the ParticleSystem for %1 seconds, %2 times per Second")
122  .defaultValues(1.0, 25.0);
[4602]123}
[4727]124
[4725]125/**
[6623]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);
[6823]134    if (emitter != NULL)
135    {
136      if (emitter->isA(CL_PARTICLE_EMITTER))
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",
[9406]141                  emitter->getClassCName(), this->getClassCName(), this->getCName());
[6823]142        delete emitter;
143      }
144    }
[6623]145    else
146    {
[9406]147      PRINTF(2)("Could not Generate Emitter for system %s::%s (wrong type in XML-format)\n", this->getClassCName(), getCName());
[6623]148    }
149  }
150  LOAD_PARAM_END_CYCLE(element);
151}
152
153/**
[7300]154 * @param maxCount the maximum count of particles that can be emitted
[4727]155 */
[7300]156void ParticleSystem::setMaxCount(unsigned int maxCount)
[4727]157{
158  this->maxCount = maxCount;
[9406]159  PRINTF(4)("MAXCOUNT of %s::%s is %d\n", this->getClassCName(), this->getCName(), maxCount);
[4727]160}
161
[3932]162// setting properties
[4478]163/**
[7300]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).
[4597]167*/
[3932]168void ParticleSystem::setLifeSpan(float lifeSpan, float randomLifeSpan)
169{
[3934]170  this->lifeSpan = lifeSpan;
171  this->randomLifeSpan = randomLifeSpan;
[9406]172  PRINTF(4)("LifeTime of %s::%s is %f\n", this->getClassCName(), this->getCName(), lifeSpan);
[3932]173}
174
[3945]175/**
[7300]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 */
[4430]181void ParticleSystem::setConserve(float conserve)
[3932]182{
[4430]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;
[7300]189
[9406]190  PRINTF(4)("Conserve of %s::%s is %f\n", this->getClassCName(), this->getCName(), conserve);
[3932]191}
192
[4430]193/////////////////////////////
194/* Per-Particle Attributes */
195/////////////////////////////
[3945]196/**
[7300]197 * @brief sets a key in the radius-animation on a per-particle basis
[4836]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
[4378]201*/
[4430]202void ParticleSystem::setRadius(float lifeCycleTime, float radius, float randRadius)
[4378]203{
[7333]204  this->radiusAnim.changeValue(lifeCycleTime, radius);
205  this->randRadiusAnim.changeValue(lifeCycleTime, randRadius);
[7300]206
207  PRINTF(4)("Radius of %s::%s at timeSlice %f is %f with a Random of %f\n",
[9406]208    this->getClassCName(), this->getCName(), lifeCycleTime, radius, randRadius);
[4378]209}
210
211/**
[7300]212 * @brief sets a key in the mass-animation on a per-particle basis
[4836]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
[3945]216*/
[4430]217void ParticleSystem::setMass(float lifeCycleTime, float mass, float randMass)
[3932]218{
[7333]219  this->massAnim.changeValue(lifeCycleTime, mass);
220  this->randMassAnim.changeValue(lifeCycleTime, randMass);
[3932]221}
222
[3945]223/**
[7300]224 * @brief sets a key in the color-animation on a per-particle basis
[4836]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
[4338]230*/
[4725]231void ParticleSystem::setColor(float lifeCycleTime, float red, float green, float blue, float alpha)
[4338]232{
[7333]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);
[7300]237
238  PRINTF(4)("Color of %s::%s on timeslice %f is r:%f g:%f b:%f a:%f\n",
[9406]239    this->getClassCName(), this->getCName(), lifeCycleTime, red, green, blue, alpha);
[4338]240}
241
[6629]242/**
[9656]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/**
[6629]254 * @brief adds an Emitter to this System.
255 * @param emitter the Emitter to add.
256 */
[6612]257void ParticleSystem::addEmitter(ParticleEmitter* emitter)
258{
[6619]259  assert (emitter != NULL);
260  if (emitter->getSystem() != NULL)
261    emitter->getSystem()->removeEmitter(emitter);
[6625]262  emitter->system = this;
[6612]263  this->emitters.push_back(emitter);
264}
265
[6629]266/**
267 * @brief removes a ParticleEmitter from this System
268 * @param emitter the Emitter to remove
269 */
[6612]270void ParticleSystem::removeEmitter(ParticleEmitter* emitter)
271{
[6625]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);
[6612]276  if (it != this->emitters.end())
[6625]277    this->emitters.erase(it);*/
[6612]278}
279
[4338]280/**
[6629]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{
[6630]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
[9406]291  PRINTF(4)("Precaching %s::%s %d seconds %d timesPerSecond\n", this->getClassCName(), this->getCName(), seconds, ticksPerSecond);
[6629]292  for (unsigned int i = 0; i < seconds*ticksPerSecond; i++)
293    this->tick(1.0/(float)ticksPerSecond);
294}
295
296
297/**
[7300]298 * @brief ticks the system.
[4836]299 * @param dt the time to tick all the Particles of the System
[3932]300
[3945]301   this is used to get all the particles some motion
302*/
[3931]303void ParticleSystem::tick(float dt)
304{
[3934]305  Particle* tickPart = particles;  // the particle to Tick
[4692]306  Particle* prevPart = NULL;
[3934]307  while (likely(tickPart != NULL))
[6625]308  {
309    // applying force to the System.
310    if (likely (tickPart->mass > 0.0))
311      tickPart->velocity += tickPart->extForce / tickPart->mass * dt;
[4687]312
[6625]313    tickPart->radius = radiusAnim.getValue(tickPart->lifeCycle)
314                       + randRadiusAnim.getValue(tickPart->lifeCycle) * tickPart->radiusRand;
[4434]315
[6625]316    tickPart->mass = massAnim.getValue(tickPart->lifeCycle)
317                     + randMassAnim.getValue(tickPart->lifeCycle) * tickPart->massRand;
[4597]318
[6625]319    tickPart->extForce = Vector(0,0,0);
[4338]320
[6625]321    // applying Color
[7333]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);
[4431]326
[6757]327    // rendering new position.
328    tickPart->position += tickPart->velocity * dt;
[7027]329    tickPart->orientation *= tickPart->momentum *dt;
[6757]330
[6625]331    // many more to come
[3932]332
[6625]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))
[4691]343      {
[6625]344        prevPart->next = tickPart->next;
345        tickPart->next = this->deadList;
346        this->deadList = tickPart;
347        tickPart = prevPart->next;
[4691]348      }
[3934]349      else
[6625]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;
[3932]358    }
[6625]359    else
360    {
361      prevPart = tickPart;
362      tickPart = tickPart->next;
363    }
364  }
[6612]365
[6625]366  std::list<ParticleEmitter*>::iterator emitter;
367  for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
368    (*emitter)->tick(dt);
[3932]369}
370
[4597]371/**
[4836]372  *  applies some force to a Particle.
373  * @param field the Field to apply.
[4338]374 */
[7300]375void ParticleSystem::applyField(const Field* field)
[4338]376{
377  Particle* tickPart = particles;
378  while (tickPart)
[6625]379  {
380    tickPart->extForce += field->calcForce(tickPart->position);
381    tickPart = tickPart->next;
382  }
[4338]383}
384
385
[3945]386/**
[4836]387 * @returns the count of Faces of this ParticleSystem
[4677]388 */
[4746]389unsigned int ParticleSystem::getFaceCount() const
[4677]390{
[6621]391  return this->count;
[4677]392}
393
394/**
[7300]395 * @brief adds a new Particle to the System
[4836]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
[3945]401*/
[4690]402void ParticleSystem::addParticle(const Vector& position, const Vector& velocity, const Quaternion& orientation, const Quaternion& momentum, unsigned int data)
[3932]403{
[3934]404  if (this->count <= this->maxCount)
[6625]405  {
406    // if it is the first Particle
407    if (unlikely(particles == NULL))
[3932]408    {
[6625]409      if (likely(deadList != NULL))
410      {
411        this->particles = this->deadList;
412        deadList = deadList->next;
413      }
[3934]414      else
[6625]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;
[3951]442
[6625]443    particles->orientation = orientation;
444    particles->momentum = momentum;
[4687]445
[6625]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;
[3934]451
[6625]452    ++this->count;
453  }
[3932]454  else
[7300]455    PRINTF(4)("maximum count of particles reached not adding any more\n");
[3932]456}
[3931]457
[3944]458/**
[4836]459 *  outputs some nice debug information
[3944]460*/
[4746]461void ParticleSystem::debug() const
[3944]462{
[7300]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));
[9656]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))
[6625]476  {
477    PRINT(0)("  - ParticleDeadList is used: ");
478    int i = 1;
479    Particle* tmpPart = this->deadList;
[9406]480    while ((tmpPart = tmpPart->next) != NULL) { ++i; }
[6625]481    PRINT(0)("count: %d\n", i);
482  }
[3944]483}
Note: See TracBrowser for help on using the repository browser.