Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5099 was 4863, checked in by bensch, 19 years ago

orxonox/trunk: glowing particles

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