Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

trunk: particle-optimisation

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