Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6783 was 6757, checked in by bensch, 19 years ago

trunk: nicer burst

File size: 13.7 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
[5357]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
[5155]25#include "load_param.h"
[5652]26#include "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
[1856]34using namespace std;
[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{
[4602]42  this->init();
[4597]43
[4727]44  this->setMaxCount(maxCount);
[3365]45}
[1853]46
[4677]47/**
[4836]48 *  standard deconstructor
[3245]49*/
[4176]50ParticleSystem::~ParticleSystem()
[3543]51{
[6625]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  }
[4123]59
[6625]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  }
[4176]67
[6625]68  while(!this->emitters.empty())
69  {
70    this->removeEmitter(this->emitters.front());
71  }
[6623]72
[3543]73}
[1853]74
[3945]75/**
[6629]76 * @brief initializes the ParticleSystem with default values
77 */
[4746]78void ParticleSystem::init()
[4602]79{
80  this->setClassID(CL_PARTICLE_SYSTEM, "ParticleSystem");
81
[4727]82  this->setMaxCount(PARTICLE_DEFAULT_MAX_COUNT);
[4602]83  this->count = 0;
84  this->particles = NULL;
85  this->deadList = NULL;
86  this->setConserve(1);
87  this->setLifeSpan(1);
[6612]88
89  this->toList(OM_ENVIRON);
[4602]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{
[6512]99  WorldEntity::loadParams(root);
100  PhysicsInterface::loadParams(root);
[4602]101
[5671]102  LoadParam(root, "max-count", this, ParticleSystem, setMaxCount)
[6625]103  .describe("the maximal count of Particles, that can be emitted into this system");
[4727]104
[5671]105  LoadParam(root, "life-span", this, ParticleSystem, setLifeSpan)
[6625]106  .describe("sets the life-span of the Particles.");
[4602]107
[5671]108  LoadParam(root, "conserve", this, ParticleSystem, setConserve)
[6625]109  .describe("sets the Conserve factor of the Particles (1.0: they keep all their energy, 0.0:they keep no energy)");
[4725]110
[6623]111  LoadParamXML(root, "emitters", this, ParticleSystem, loadEmitters);
[6222]112
[5654]113  LOAD_PARAM_START_CYCLE(root, element);
[4727]114  {
[5654]115    element->ToText();
[6625]116    // PER-PARTICLE-ATTRIBUTES:
[5654]117    LoadParam_CYCLE(element, "radius", this, ParticleSystem, setRadius)
[6625]118    .describe("The Radius of each particle over time (TimeIndex [0-1], radius at TimeIndex, randomRadius at TimeIndex)");
[4725]119
[5654]120    LoadParam_CYCLE(element, "mass", this, ParticleSystem, setMass)
[6625]121    .describe("The Mass of each particle over time (TimeIndex: [0-1], mass at TimeIndex, randomMass at TimeIndex)");
[4725]122
[5654]123    LoadParam_CYCLE(element, "color", this, ParticleSystem, setColor)
[6625]124    .describe("The Color of each particle over time (TimeIndex: [0-1], red: [0-1], green: [0-1], blue: [0-1], alpha: [0-1])");
[4727]125  }
[5654]126  LOAD_PARAM_END_CYCLE(element);
[6629]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);
[4602]131}
[4727]132
[4725]133/**
[6623]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->isA(CL_PARTICLE_EMITTER))
143      this->addEmitter(dynamic_cast<ParticleEmitter*>(emitter));
144    else
145    {
146      PRINTF(2)("Tried to load an Element of type '%s' that should be a ParticleEmitter onto '%s::%s'.\n",
147                emitter->getClassName(), this->getClassName(), this->getName());
148      delete emitter;
149    }
150  }
151  LOAD_PARAM_END_CYCLE(element);
152}
153
154/**
[4836]155* @param maxCount the maximum count of particles that can be emitted
[4727]156 */
157void ParticleSystem::setMaxCount(int maxCount)
158{
159  this->maxCount = maxCount;
160}
161
[3932]162// setting properties
[4478]163/**
[4836]164 *  Sets the lifespan of newly created particles
[4597]165*/
[3932]166void ParticleSystem::setLifeSpan(float lifeSpan, float randomLifeSpan)
167{
[3934]168  this->lifeSpan = lifeSpan;
169  this->randomLifeSpan = randomLifeSpan;
[3932]170}
171
[3945]172/**
[4836]173 *  sets the conserve Factor of newly created particles
[3945]174*/
[4430]175void ParticleSystem::setConserve(float conserve)
[3932]176{
[4430]177  if (conserve > 1.0)
178    this->conserve = 1.0;
179  else if (conserve < 0.0)
180    this->conserve = 0.0;
181  else
182    this->conserve = conserve;
[3932]183}
184
[4430]185/////////////////////////////
186/* Per-Particle Attributes */
187/////////////////////////////
[3945]188/**
[4836]189 *  sets a key in the radius-animation on a per-particle basis
190 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
191 * @param radius the radius at this position
192 * @param randRadius the randRadius at this position
[4378]193*/
[4430]194void ParticleSystem::setRadius(float lifeCycleTime, float radius, float randRadius)
[4378]195{
[4649]196  this->radiusAnim.changeEntry(lifeCycleTime, radius);
197  this->randRadiusAnim.changeEntry(lifeCycleTime, randRadius);
[4378]198}
199
200/**
[4836]201 *  sets a key in the mass-animation on a per-particle basis
202 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
203 * @param mass the mass at this position
204 * @param randMass the randomMass at this position
[3945]205*/
[4430]206void ParticleSystem::setMass(float lifeCycleTime, float mass, float randMass)
[3932]207{
[4649]208  this->massAnim.changeEntry(lifeCycleTime, mass);
209  this->randMassAnim.changeEntry(lifeCycleTime, randMass);
[3932]210}
211
[3945]212/**
[4836]213 *  sets a key in the color-animation on a per-particle basis
214 * @param lifeCycleTime: the time (partilceLifeTime/particleAge) [0-1]
215 * @param red: red
216 * @param green: green
217 * @param blue: blue
218 * @param alpha: alpha
[4338]219*/
[4725]220void ParticleSystem::setColor(float lifeCycleTime, float red, float green, float blue, float alpha)
[4338]221{
[4649]222  this->colorAnim[0].changeEntry(lifeCycleTime, red);
223  this->colorAnim[1].changeEntry(lifeCycleTime, green);
224  this->colorAnim[2].changeEntry(lifeCycleTime, blue);
225  this->colorAnim[3].changeEntry(lifeCycleTime, alpha);
[4338]226}
227
[6629]228/**
229 * @brief adds an Emitter to this System.
230 * @param emitter the Emitter to add.
231 */
[6612]232void ParticleSystem::addEmitter(ParticleEmitter* emitter)
233{
[6619]234  assert (emitter != NULL);
235  if (emitter->getSystem() != NULL)
236    emitter->getSystem()->removeEmitter(emitter);
[6625]237  emitter->system = this;
[6612]238  this->emitters.push_back(emitter);
239}
240
[6629]241/**
242 * @brief removes a ParticleEmitter from this System
243 * @param emitter the Emitter to remove
244 */
[6612]245void ParticleSystem::removeEmitter(ParticleEmitter* emitter)
246{
[6625]247  assert (emitter != NULL);
248  emitter->system = NULL;
249  this->emitters.remove(emitter);
250  /*  std::list<ParticleEmitter*>::iterator it = std::find(this->emitters.begin(), this->emitters.end(), emitter);
[6612]251  if (it != this->emitters.end())
[6625]252    this->emitters.erase(it);*/
[6612]253}
254
[4338]255/**
[6629]256 * @brief does a Precaching, meaning, that the ParticleSystem(and its emitters) will be ticked force
257 * @param seconds: seconds
258 * @param ticksPerSeconds times per Second.
259 */
260void ParticleSystem::precache(unsigned int seconds, unsigned int ticksPerSecond)
261{
[6630]262
263  std::list<ParticleEmitter*>::iterator emitter;
264  for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
265    (*emitter)->updateNode(.1), (*emitter)->updateNode(.1);
266
[6629]267  PRINTF(4)("Precaching %s::%s %d seconds %d timesPerSecond\n", this->getClassName(), this->getName(), seconds, ticksPerSecond);
268  this->debug();
269  for (unsigned int i = 0; i < seconds*ticksPerSecond; i++)
270    this->tick(1.0/(float)ticksPerSecond);
271}
272
273
274/**
[4836]275 *  ticks the system.
276 * @param dt the time to tick all the Particles of the System
[3932]277
[3945]278   this is used to get all the particles some motion
279*/
[3931]280void ParticleSystem::tick(float dt)
281{
[3934]282  Particle* tickPart = particles;  // the particle to Tick
[4692]283  Particle* prevPart = NULL;
[3934]284  while (likely(tickPart != NULL))
[6625]285  {
286    // applying force to the System.
287    if (likely (tickPart->mass > 0.0))
288      tickPart->velocity += tickPart->extForce / tickPart->mass * dt;
[4687]289
[6625]290    tickPart->radius = radiusAnim.getValue(tickPart->lifeCycle)
291                       + randRadiusAnim.getValue(tickPart->lifeCycle) * tickPart->radiusRand;
[4434]292
[6625]293    tickPart->mass = massAnim.getValue(tickPart->lifeCycle)
294                     + randMassAnim.getValue(tickPart->lifeCycle) * tickPart->massRand;
[4597]295
[6625]296    tickPart->extForce = Vector(0,0,0);
[4338]297
[6625]298    // applying Color
299    tickPart->color[0] = this->colorAnim[0].getValue(tickPart->lifeCycle);
300    tickPart->color[1] = this->colorAnim[1].getValue(tickPart->lifeCycle);
301    tickPart->color[2] = this->colorAnim[2].getValue(tickPart->lifeCycle);
302    tickPart->color[3] = this->colorAnim[3].getValue(tickPart->lifeCycle);
[4431]303
[6757]304    // rendering new position.
305    tickPart->position += tickPart->velocity * dt;
306    tickPart->orientation += tickPart->momentum *dt;
307
[6625]308    // many more to come
[3932]309
[6625]310    if (this->conserve < 1.0)
311    {
312      tickPart->velocity *= this->conserve;
313      tickPart->momentum *= this->conserve;
314    }
315    // find out if we have to delete tickPart
316    if (unlikely((tickPart->lifeCycle += dt/tickPart->lifeTime) >= 1.0))
317    {
318      // remove the particle from the list
319      if (likely(prevPart != NULL))
[4691]320      {
[6625]321        prevPart->next = tickPart->next;
322        tickPart->next = this->deadList;
323        this->deadList = tickPart;
324        tickPart = prevPart->next;
[4691]325      }
[3934]326      else
[6625]327      {
328        prevPart = NULL;
329        this->particles = tickPart->next;
330        tickPart->next = this->deadList;
331        this->deadList = tickPart;
332        tickPart = this->particles;
333      }
334      --this->count;
[3932]335    }
[6625]336    else
337    {
338      prevPart = tickPart;
339      tickPart = tickPart->next;
340    }
341  }
[6612]342
[6625]343  std::list<ParticleEmitter*>::iterator emitter;
344  for (emitter = this->emitters.begin(); emitter != this->emitters.end(); emitter++)
345    (*emitter)->tick(dt);
[3932]346}
347
[4597]348/**
[4836]349  *  applies some force to a Particle.
350  * @param field the Field to apply.
[4338]351 */
[4395]352void ParticleSystem::applyField(Field* field)
[4338]353{
354  Particle* tickPart = particles;
355  while (tickPart)
[6625]356  {
357    tickPart->extForce += field->calcForce(tickPart->position);
358    tickPart = tickPart->next;
359  }
[4338]360}
361
362
[3945]363/**
[4836]364 * @returns the count of Faces of this ParticleSystem
[4677]365 */
[4746]366unsigned int ParticleSystem::getFaceCount() const
[4677]367{
[6621]368  return this->count;
[4677]369}
370
371
372/**
[4836]373 *  draws all the Particles of this System
[4338]374
375   The Cases in this Function all do the same:
376   Drawing all the particles with the appropriate Type.
377   This is just the fastest Way to do this, but will most likely be changed in the future.
[4663]378 */
[6621]379// void ParticleSystem::draw() const
380// {
381//   glPushAttrib(GL_ENABLE_BIT);
382//
383//   Particle* drawPart = particles;
384//
385//   switch (this->particleType)
386//   {
387//
388//     case PARTICLE_MODEL:
389//       {
390//       }
391//       break;
392//
393//     case PARTICLE_DOT:
394//       glDisable(GL_LIGHTING);
395//       glBegin(GL_POINTS);
396//       while (likely(drawPart != NULL))
397//       {
398//         glColor4fv(drawPart->color);
399//
400//         glLineWidth(drawPart->radius);
401//
402//         glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
403//         drawPart = drawPart->next;
404//       }
405//       glEnd();
406//       break;
407//   }
408//   glPopAttrib();
409// }
[4338]410
[3945]411/**
[4836]412 *  adds a new Particle to the System
413 * @param position the initial position, where the particle gets emitted.
414 * @param velocity the initial velocity of the particle.
415 * @param orientation the initial orientation of the Paritcle.
416 * @param momentum the initial momentum of the Particle (the speed of its rotation).
417 * @param data some more data given by the emitter
[3945]418*/
[4690]419void ParticleSystem::addParticle(const Vector& position, const Vector& velocity, const Quaternion& orientation, const Quaternion& momentum, unsigned int data)
[3932]420{
[3934]421  if (this->count <= this->maxCount)
[6625]422  {
423    // if it is the first Particle
424    if (unlikely(particles == NULL))
[3932]425    {
[6625]426      if (likely(deadList != NULL))
427      {
428        this->particles = this->deadList;
429        deadList = deadList->next;
430      }
[3934]431      else
[6625]432      {
433        PRINTF(5)("Generating new Particle\n");
434        this->particles = new Particle;
435      }
436      this->particles->next = NULL;
437    }
438    // filling the List from the beginning
439    else
440    {
441      Particle* tmpPart;
442      if (likely(deadList != NULL))
443      {
444        tmpPart = this->deadList;
445        deadList = deadList->next;
446      }
447      else
448      {
449        PRINTF(5)("Generating new Particle\n");
450        tmpPart = new Particle;
451      }
452      tmpPart->next = this->particles;
453      this->particles = tmpPart;
454    }
455    particles->lifeTime = this->lifeSpan + (float)(rand()/RAND_MAX)* this->randomLifeSpan;
456    particles->lifeCycle = 0.0;
457    particles->position = position;
458    particles->velocity = velocity;
[3951]459
[6625]460    particles->orientation = orientation;
461    particles->momentum = momentum;
[4687]462
[6625]463    //  particle->rotation = ; //! @todo rotation is once again something to be done.
464    particles->massRand = 2*(float)rand()/RAND_MAX -1;
465    particles->radiusRand = 2* (float)rand()/RAND_MAX -1;
466    particles->mass = this->massAnim.getValue(0.0) + this->randMassAnim.getValue(0.0)*particles->massRand;
467    particles->radius = this->radiusAnim.getValue(0.0) + this->randRadiusAnim.getValue(0.0)*particles->radiusRand;
[3934]468
[6625]469    ++this->count;
470  }
[3932]471  else
[4017]472    PRINTF(5)("maximum count of particles reached not adding any more\n");
[3932]473}
[3931]474
[3944]475/**
[4836]476 *  outputs some nice debug information
[3944]477*/
[4746]478void ParticleSystem::debug() const
[3944]479{
[6629]480  PRINT(0)("  ParticleCount: %d emitters: %d, maximumCount: %d :: filled %d%%\n", this->count, this->emitters.size(), this->maxCount, 100*this->count/this->maxCount);
[4123]481  if (deadList)
[6625]482  {
483    PRINT(0)("  - ParticleDeadList is used: ");
484    int i = 1;
485    Particle* tmpPart = this->deadList;
486    while (tmpPart = tmpPart->next) ++i;
487    PRINT(0)("count: %d\n", i);
488  }
[3944]489}
Note: See TracBrowser for help on using the repository browser.