Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: merged the christmas branche to the trunk
merged with command:
svn merge -r6165:HEAD christmas_branche/ ../trunk/
no conflicts

File size: 17.8 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"
21#include "particle_engine.h"
[4338]22
23#include "field.h"
[5511]24#include "model.h"
[4338]25
[5155]26#include "load_param.h"
[5652]27#include "factory.h"
[3942]28#include "material.h"
[4338]29#include "state.h"
[5446]30#include "shell_command.h"
[3930]31
[5944]32#include "parser/tinyxml/tinyxml.h"
[4338]33
[5750]34CREATE_FACTORY(ParticleSystem, CL_PARTICLE_SYSTEM);
[5446]35SHELL_COMMAND(texture, ParticleSystem, setMaterialTexture)
36    ->defaultValues(1, "maps/evil-flower.png");
[4725]37
[1856]38using namespace std;
[1853]39
[3245]40/**
[4836]41 *  standard constructor
42 * @param maxCount the Count of particles in the System
43 * @param type The Type of the ParticleSystem
[3245]44*/
[4762]45ParticleSystem::ParticleSystem (unsigned int maxCount, PARTICLE_TYPE type)
[3365]46{
[4602]47  this->init();
[4597]48
[4727]49  this->setMaxCount(maxCount);
[4338]50  this->setType(type, 1);
[3365]51}
[1853]52
[4677]53/**
[5445]54 * @brief creates a Particle System out of a XML-element
55 * @param root: the XML-element to load from
[4677]56 */
[4762]57ParticleSystem::ParticleSystem(const TiXmlElement* root)
[4602]58{
59  this->init();
[4725]60
[4602]61  this->loadParams(root);
62}
[1853]63
[3245]64/**
[4836]65 *  standard deconstructor
[3245]66*/
[4176]67ParticleSystem::~ParticleSystem()
[3543]68{
69  // delete what has to be deleted here
[3935]70   ParticleEngine::getInstance()->removeSystem(this);
[4123]71
72   // deleting all the living Particles
73   while (this->particles)
74     {
75       Particle* tmpDelPart = this->particles;
76       this->particles = this->particles->next;
77       delete tmpDelPart;
78     }
79
80   // deleting all the dead particles
81   while (this->deadList)
82     {
83       Particle* tmpDelPart = this->deadList;
84       this->deadList = this->deadList->next;
85       delete tmpDelPart;
86     }
[4176]87
88   if (this->material)
89     delete this->material;
[3543]90}
[1853]91
[3945]92/**
[4602]93  \brief initializes the ParticleSystem with default values
94*/
[4746]95void ParticleSystem::init()
[4602]96{
97  this->setClassID(CL_PARTICLE_SYSTEM, "ParticleSystem");
98
99  this->material = NULL;
[4727]100  this->setMaxCount(PARTICLE_DEFAULT_MAX_COUNT);
[4602]101  this->count = 0;
102  this->particles = NULL;
103  this->deadList = NULL;
104  this->setConserve(1);
105  this->setLifeSpan(1);
106  this->glID = NULL;
107  this->setType(PARTICLE_DEFAULT_TYPE, 1);
108  ParticleEngine::getInstance()->addSystem(this);
109}
110
111
112/**
113 * loads Parameters from a TiXmlElement
114 * @param root the XML-element to load from.
115 */
116void ParticleSystem::loadParams(const TiXmlElement* root)
117{
118  static_cast<WorldEntity*>(this)->loadParams(root);
119  static_cast<PhysicsInterface*>(this)->loadParams(root);
120
[5671]121  LoadParam(root, "max-count", this, ParticleSystem, setMaxCount)
[4727]122      .describe("the maximal count of Particles, that can be emitted into this system");
123
[5671]124  LoadParam(root, "life-span", this, ParticleSystem, setLifeSpan)
[4725]125      .describe("sets the life-span of the Particles.");
[4602]126
[5671]127  LoadParam(root, "conserve", this, ParticleSystem, setConserve)
[5652]128      .describe("sets the Conserve factor of the Particles (1.0: they keep all their energy, 0.0:they keep no energy)");
[4725]129
[5671]130  LoadParam(root, "type", this, ParticleSystem, setType)
[4732]131      .describe("sets the type of the Particles, (dot, spark, sprite or model)");
132
[6222]133  LoadParam(root, "texture", this, ParticleSystem, setMaterialTexture);
134
[5654]135  LOAD_PARAM_START_CYCLE(root, element);
[4727]136  {
[5654]137    element->ToText();
[4725]138  // PER-PARTICLE-ATTRIBUTES:
[5654]139    LoadParam_CYCLE(element, "radius", this, ParticleSystem, setRadius)
[4727]140        .describe("The Radius of each particle over time (TimeIndex [0-1], radius at TimeIndex, randomRadius at TimeIndex)");
[4725]141
[5654]142    LoadParam_CYCLE(element, "mass", this, ParticleSystem, setMass)
[4727]143        .describe("The Mass of each particle over time (TimeIndex: [0-1], mass at TimeIndex, randomMass at TimeIndex)");
[4725]144
[5654]145    LoadParam_CYCLE(element, "color", this, ParticleSystem, setColor)
[4727]146        .describe("The Color of each particle over time (TimeIndex: [0-1], red: [0-1], green: [0-1], blue: [0-1], alpha: [0-1])");
147  }
[5654]148  LOAD_PARAM_END_CYCLE(element);
[4727]149
[4602]150}
[4727]151
[4725]152/**
[4836]153* @param maxCount the maximum count of particles that can be emitted
[4727]154 */
155void ParticleSystem::setMaxCount(int maxCount)
156{
157  this->maxCount = maxCount;
158}
159
160
161/**
[4836]162 * @param particleType the type of particles in this System
163 * @param count how many particles (in PARTICLE_MULTI-mode)
164   @todo this will be different
[4725]165*/
166void ParticleSystem::setType(const char* particleType)
167{
[4726]168  if (!strcmp(particleType, "dot"))
[4725]169    this->setType(PARTICLE_DOT);
[4726]170  else if(!strcmp(particleType, "spark"))
[4725]171    this->setType(PARTICLE_SPARK);
[4726]172  else if(!strcmp(particleType, "model"))
[4725]173    this->setType(PARTICLE_MODEL);
174  else // if (strcmp(particleType, "SPRITE"))
175    this->setType(PARTICLE_SPRITE);
176}
[4602]177
178/**
[4836]179 * @param particleType the type of particles in this System
180 * @param count how many particles (in PARTICLE_MULTI-mode)
[5446]181   @todo this MUST be different
[3945]182*/
[3942]183void ParticleSystem::setType(PARTICLE_TYPE particleType, int count)
184{
185  this->particleType = particleType;
186  this->dialectCount = count;
[4338]187  //  if (glID != NULL)
188  //    delete glID;
[3942]189
[4338]190  //  glID = new GLuint[count];
191  //  for (int i = 0; i< count; i++)
192  //    glID[i] = 0;
[3942]193
[4338]194  //  glID[0] = glGenLists(count);
195  if (this->material)
196    delete this->material;
197  this->material = NULL;
[3946]198
[4663]199  switch (this->particleType)
[4338]200    {
[4663]201      case PARTICLE_SPRITE:
202        this->material = new Material("transperencyMap");
[5445]203        this->material->setDiffuseMap("maps/radialTransparency.png");
[4338]204      //  material->setTransparency(.5);
[4663]205        break;
[4726]206  }
[3942]207}
208
[3932]209// setting properties
[4478]210/**
[4836]211 *  sets the material to an external material
212 * @param material: the material to set this material to.
[4478]213
214   !! important if the extern material gets deleted it MUST be unregistered here or segfault !!
215*/
[3932]216void ParticleSystem::setMaterial(Material* material)
217{
[3935]218  this->material = material;
[3932]219}
[3931]220
[5446]221void ParticleSystem::setMaterialTexture(const char* textureFile)
222{
223  if (this->material != NULL)
224    this->material->setDiffuseMap(textureFile);
225}
226
[3938]227/**
[4836]228 *  Sets the lifespan of newly created particles
[4597]229*/
[3932]230void ParticleSystem::setLifeSpan(float lifeSpan, float randomLifeSpan)
231{
[3934]232  this->lifeSpan = lifeSpan;
233  this->randomLifeSpan = randomLifeSpan;
[3932]234}
235
[3945]236/**
[4836]237 *  sets the conserve Factor of newly created particles
[3945]238*/
[4430]239void ParticleSystem::setConserve(float conserve)
[3932]240{
[4430]241  if (conserve > 1.0)
242    this->conserve = 1.0;
243  else if (conserve < 0.0)
244    this->conserve = 0.0;
245  else
246    this->conserve = conserve;
[3932]247}
248
[4430]249/////////////////////////////
250/* Per-Particle Attributes */
251/////////////////////////////
[3945]252/**
[4836]253 *  sets a key in the radius-animation on a per-particle basis
254 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
255 * @param radius the radius at this position
256 * @param randRadius the randRadius at this position
[4378]257*/
[4430]258void ParticleSystem::setRadius(float lifeCycleTime, float radius, float randRadius)
[4378]259{
[4649]260  this->radiusAnim.changeEntry(lifeCycleTime, radius);
261  this->randRadiusAnim.changeEntry(lifeCycleTime, randRadius);
[4378]262}
263
264/**
[4836]265 *  sets a key in the mass-animation on a per-particle basis
266 * @param lifeCycleTime the time (partilceLifeTime/particleAge) [0-1]
267 * @param mass the mass at this position
268 * @param randMass the randomMass at this position
[3945]269*/
[4430]270void ParticleSystem::setMass(float lifeCycleTime, float mass, float randMass)
[3932]271{
[4649]272  this->massAnim.changeEntry(lifeCycleTime, mass);
273  this->randMassAnim.changeEntry(lifeCycleTime, randMass);
[3932]274}
275
[3945]276/**
[4836]277 *  sets a key in the color-animation on a per-particle basis
278 * @param lifeCycleTime: the time (partilceLifeTime/particleAge) [0-1]
279 * @param red: red
280 * @param green: green
281 * @param blue: blue
282 * @param alpha: alpha
[4338]283*/
[4725]284void ParticleSystem::setColor(float lifeCycleTime, float red, float green, float blue, float alpha)
[4338]285{
[4649]286  this->colorAnim[0].changeEntry(lifeCycleTime, red);
287  this->colorAnim[1].changeEntry(lifeCycleTime, green);
288  this->colorAnim[2].changeEntry(lifeCycleTime, blue);
289  this->colorAnim[3].changeEntry(lifeCycleTime, alpha);
[4338]290}
291
292/**
[4836]293 *  ticks the system.
294 * @param dt the time to tick all the Particles of the System
[3932]295
[3945]296   this is used to get all the particles some motion
297*/
[3931]298void ParticleSystem::tick(float dt)
299{
[3934]300  Particle* tickPart = particles;  // the particle to Tick
[4692]301  Particle* prevPart = NULL;
[3934]302  while (likely(tickPart != NULL))
[3932]303    {
[4378]304      // applying force to the System.
[4385]305      if (likely (tickPart->mass > 0.0))
[4597]306        tickPart->velocity += tickPart->extForce / tickPart->mass * dt;
[4378]307      // rendering new position.
[4687]308      tickPart->position += tickPart->velocity * dt;
309      tickPart->orientation += tickPart->momentum *dt;
310
[4434]311      tickPart->radius = radiusAnim.getValue(tickPart->lifeCycle)
[4597]312        + randRadiusAnim.getValue(tickPart->lifeCycle) * tickPart->radiusRand;
[4434]313
314      tickPart->mass = massAnim.getValue(tickPart->lifeCycle)
[4597]315        + randMassAnim.getValue(tickPart->lifeCycle) * tickPart->massRand;
316
[4338]317      tickPart->extForce = Vector(0,0,0);
318
319      // applying Color
[4431]320      tickPart->color[0] = this->colorAnim[0].getValue(tickPart->lifeCycle);
321      tickPart->color[1] = this->colorAnim[1].getValue(tickPart->lifeCycle);
322      tickPart->color[2] = this->colorAnim[2].getValue(tickPart->lifeCycle);
323      tickPart->color[3] = this->colorAnim[3].getValue(tickPart->lifeCycle);
324
[3932]325      // many more to come
326
[3935]327      if (this->conserve < 1.0)
[4691]328      {
329        tickPart->velocity *= this->conserve;
330        tickPart->momentum *= this->conserve;
331      }
[3934]332      // find out if we have to delete tickPart
[4385]333      if (unlikely((tickPart->lifeCycle += dt/tickPart->lifeTime) >= 1.0))
[4597]334        {
335          // remove the particle from the list
336          if (likely(prevPart != NULL))
337            {
338              prevPart->next = tickPart->next;
339              tickPart->next = this->deadList;
340              this->deadList = tickPart;
341              tickPart = prevPart->next;
342            }
343          else
344            {
345              prevPart = NULL;
346              this->particles = tickPart->next;
347              tickPart->next = this->deadList;
348              this->deadList = tickPart;
349              tickPart = this->particles;
350            }
351          --this->count;
352        }
[3934]353      else
[4597]354        {
355          prevPart = tickPart;
356          tickPart = tickPart->next;
357        }
[3932]358    }
359}
360
[4597]361/**
[4836]362  *  applies some force to a Particle.
363  * @param field the Field to apply.
[4338]364 */
[4395]365void ParticleSystem::applyField(Field* field)
[4338]366{
367  Particle* tickPart = particles;
368  while (tickPart)
369    {
[4395]370      tickPart->extForce += field->calcForce(tickPart->position);
[4338]371      tickPart = tickPart->next;
372    }
373}
374
375
[3945]376/**
[4836]377 * @returns the count of Faces of this ParticleSystem
[4677]378 */
[4746]379unsigned int ParticleSystem::getFaceCount() const
[4677]380{
381  switch (this->particleType)
382  {
383    case PARTICLE_SPRITE:
384      return this->count;
385      break;
386    case PARTICLE_MODEL:
[5994]387      if (this->getModel(0))
388        return this->count * this->getModel()->getFaceCount();
[4677]389      break;
390  }
391}
392
393
394/**
[4836]395 *  draws all the Particles of this System
[4338]396
397   The Cases in this Function all do the same:
398   Drawing all the particles with the appropriate Type.
399   This is just the fastest Way to do this, but will most likely be changed in the future.
[4663]400 */
[4746]401void ParticleSystem::draw() const
[3932]402{
[4176]403  glPushAttrib(GL_ENABLE_BIT);
[4338]404
[4176]405  Particle* drawPart = particles;
[4597]406
[4176]407  switch (this->particleType)
[4663]408  {
[4338]409    default:
[4176]410    case PARTICLE_SPRITE:
[4667]411      glDisable(GL_LIGHTING);
[4176]412      glMatrixMode(GL_MODELVIEW);
[4515]413      glDepthMask(GL_FALSE);
[4338]414
[4597]415      material->select();
[4863]416      glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);
417
[4357]418      //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
[4338]419
[4597]420
[3934]421      while (likely(drawPart != NULL))
[4663]422      {
423        glColor4fv(drawPart->color);
[4836]424          //! @todo implement a faster code for the look-at Camera algorithm.
[4338]425
[4836]426        const PNode* camera = State::getCamera();  //!< @todo MUST be different
[4663]427        Vector cameraPos = camera->getAbsCoor();
[4827]428        Vector cameraTargetPos = State::getCameraTarget()->getAbsCoor();
[4663]429        Vector view = cameraTargetPos - cameraPos;
430        Vector up = Vector(0, 1, 0);
431        up = camera->getAbsDir().apply(up);
432        Vector h = up.cross(view);
433        Vector v = h.cross(view);
434        h.normalize();
435        v.normalize();
436        v *= .5 * drawPart->radius;
437        h *= .5 * drawPart->radius;
[4338]438
[4663]439        glBegin(GL_TRIANGLE_STRIP);
440        glTexCoord2i(1, 1);
441        glVertex3f(drawPart->position.x - h.x - v.x,
442                   drawPart->position.y - h.y - v.y,
443                   drawPart->position.z - h.z - v.z);
444        glTexCoord2i(0, 1);
445        glVertex3f(drawPart->position.x - h.x + v.x,
446                   drawPart->position.y - h.y + v.y,
447                   drawPart->position.z - h.z + v.z);
448        glTexCoord2i(1, 0);
449        glVertex3f(drawPart->position.x + h.x - v.x,
450                   drawPart->position.y + h.y - v.y,
451                   drawPart->position.z + h.z - v.z);
452        glTexCoord2i(0, 0);
453        glVertex3f(drawPart->position.x + h.x + v.x,
454                   drawPart->position.y + h.y + v.y,
455                   drawPart->position.z + h.z + v.z);
[4338]456
[4663]457        glEnd();
[4597]458
[4663]459        drawPart = drawPart->next;
460      }
[4515]461      glDepthMask(GL_TRUE);
[4176]462      break;
463
464    case PARTICLE_SPARK:
[4667]465      glDisable(GL_LIGHTING);
[4716]466      glDepthMask(GL_FALSE);
467      //glEnable(GL_LINE_SMOOTH);
468      glEnable(GL_BLEND);
469
[4176]470      glBegin(GL_LINES);
471      while (likely(drawPart != NULL))
[4663]472      {
473        glColor4fv(drawPart->color);
474        glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
[4716]475        glVertex3f(drawPart->position.x - drawPart->velocity.x * drawPart->radius,
476                   drawPart->position.y - drawPart->velocity.y * drawPart->radius,
477                   drawPart->position.z - drawPart->velocity.z * drawPart->radius);
[4663]478        drawPart = drawPart->next;
479      }
[4176]480      glEnd();
481      break;
[4597]482
[4663]483    case PARTICLE_MODEL:
484      {
[4687]485        GLfloat matrix[4][4];
[4663]486
[5994]487        if (likely(this->getModel() != NULL))
[4687]488          while (likely(drawPart != NULL))
489        {
490          glPushMatrix();
491          glMatrixMode(GL_MODELVIEW);
492          /* move */
493          glTranslatef(drawPart->position.x, drawPart->position.y, drawPart->position.z);
494          /* scale */
495          glScalef(drawPart->radius, drawPart->radius, drawPart->radius);
496          /* rotate */
497          drawPart->orientation.matrix (matrix);
498          glMultMatrixf((float*)matrix);
[4663]499
[5994]500          this->getModel()->draw();
[4663]501
[4687]502          glPopMatrix();
503          drawPart = drawPart->next;
504        }
[4726]505        else
[4732]506          PRINTF(2)("no model loaded onto ParticleSystem-%s\n", this->getName());
[4663]507      }
508      break;
509
[4176]510    case PARTICLE_DOT:
[4667]511      glDisable(GL_LIGHTING);
[4176]512      glBegin(GL_POINTS);
513      while (likely(drawPart != NULL))
[4663]514      {
515        glColor4fv(drawPart->color);
[4338]516
[4663]517        glLineWidth(drawPart->radius);
[4176]518
[4663]519        glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
520        drawPart = drawPart->next;
521      }
[4176]522      glEnd();
523      break;
[4663]524  }
[4176]525  glPopAttrib();
[3932]526}
527
[3945]528/**
[4836]529 *  adds a new Particle to the System
530 * @param position the initial position, where the particle gets emitted.
531 * @param velocity the initial velocity of the particle.
532 * @param orientation the initial orientation of the Paritcle.
533 * @param momentum the initial momentum of the Particle (the speed of its rotation).
534 * @param data some more data given by the emitter
[3945]535*/
[4690]536void ParticleSystem::addParticle(const Vector& position, const Vector& velocity, const Quaternion& orientation, const Quaternion& momentum, unsigned int data)
[3932]537{
[3934]538  if (this->count <= this->maxCount)
[3932]539    {
[3934]540      // if it is the first Particle
541      if (unlikely(particles == NULL))
[4597]542        {
543          if (likely(deadList != NULL))
544            {
545              this->particles = this->deadList;
546              deadList = deadList->next;
547            }
548          else
549            {
550              PRINTF(5)("Generating new Particle\n");
551              this->particles = new Particle;
552            }
553          this->particles->next = NULL;
554        }
[3934]555      // filling the List from the beginning
556      else
[4597]557        {
558          Particle* tmpPart;
559          if (likely(deadList != NULL))
560            {
561              tmpPart = this->deadList;
562              deadList = deadList->next;
563            }
564          else
565            {
566              PRINTF(5)("Generating new Particle\n");
567              tmpPart = new Particle;
568            }
569          tmpPart->next = this->particles;
570          this->particles = tmpPart;
571        }
[4338]572      particles->lifeTime = this->lifeSpan + (float)(rand()/RAND_MAX)* this->randomLifeSpan;
573      particles->lifeCycle = 0.0;
[3934]574      particles->position = position;
575      particles->velocity = velocity;
[3951]576
[4690]577      particles->orientation = orientation;
578      particles->momentum = momentum;
[4687]579
[4836]580      //  particle->rotation = ; //! @todo rotation is once again something to be done.
[4434]581      particles->massRand = 2*(float)rand()/RAND_MAX -1;
582      particles->radiusRand = 2* (float)rand()/RAND_MAX -1;
583      particles->mass = this->massAnim.getValue(0.0) + this->randMassAnim.getValue(0.0)*particles->massRand;
584      particles->radius = this->radiusAnim.getValue(0.0) + this->randRadiusAnim.getValue(0.0)*particles->radiusRand;
[3934]585
586      ++this->count;
[3932]587    }
588  else
[4017]589    PRINTF(5)("maximum count of particles reached not adding any more\n");
[3932]590}
[3931]591
[3944]592/**
[4836]593 *  outputs some nice debug information
[3944]594*/
[4746]595void ParticleSystem::debug() const
[3944]596{
[4726]597  PRINT(0)("  ParticleSystem %s, type: ", this->getName());
598  {
599      if (this->particleType == PARTICLE_MODEL)
600        PRINT(0)("model");
601      else if (this->particleType == PARTICLE_SPRITE)
602        PRINT(0)("sprite");
603      else if (this->particleType == PARTICLE_DOT)
604        PRINT(0)("dot");
605      else if (this->particleType == PARTICLE_SPARK)
606        PRINT(0)("spark");
607
608      PRINT(0)("\n");
609  }
610
[3944]611  PRINT(0)("  ParticleCount: %d, maximumCount: %d :: filled %d%%\n", this->count, this->maxCount, 100*this->count/this->maxCount);
[4123]612  if (deadList)
613    {
614      PRINT(0)("  - ParticleDeadList is used: ");
615      int i = 1;
616      Particle* tmpPart = this->deadList;
617      while (tmpPart = tmpPart->next) ++i;
618      PRINT(0)("count: %d\n", i);
619    }
[3944]620}
Note: See TracBrowser for help on using the repository browser.