Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

renamed newclassid to classid and newobjectlist to objectlist

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