Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 4818 was 4762, checked in by bensch, 19 years ago

orxonox/trunk: PhysicsInterface should now make a usefull cast: PhysicsInterface→BaseObject→PNode()

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   \brief 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   \brief 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   \brief 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   \brief 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   \brief 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   \brief 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   \brief 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   \brief 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   \brief 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    \brief 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   \brief 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      //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
407
408
409      while (likely(drawPart != NULL))
410      {
411        glColor4fv(drawPart->color);
412          //! \todo implement a faster code for the look-at Camera algorithm.
413
414        const PNode* camera = State::getInstance()->getCamera();  //!< \todo MUST be different
415        Vector cameraPos = camera->getAbsCoor();
416        Vector cameraTargetPos = State::getInstance()->getCameraTarget()->getAbsCoor();
417        Vector view = cameraTargetPos - cameraPos;
418        Vector up = Vector(0, 1, 0);
419        up = camera->getAbsDir().apply(up);
420        Vector h = up.cross(view);
421        Vector v = h.cross(view);
422        h.normalize();
423        v.normalize();
424        v *= .5 * drawPart->radius;
425        h *= .5 * drawPart->radius;
426
427        glBegin(GL_TRIANGLE_STRIP);
428        glTexCoord2i(1, 1);
429        glVertex3f(drawPart->position.x - h.x - v.x,
430                   drawPart->position.y - h.y - v.y,
431                   drawPart->position.z - h.z - v.z);
432        glTexCoord2i(0, 1);
433        glVertex3f(drawPart->position.x - h.x + v.x,
434                   drawPart->position.y - h.y + v.y,
435                   drawPart->position.z - h.z + v.z);
436        glTexCoord2i(1, 0);
437        glVertex3f(drawPart->position.x + h.x - v.x,
438                   drawPart->position.y + h.y - v.y,
439                   drawPart->position.z + h.z - v.z);
440        glTexCoord2i(0, 0);
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
445        glEnd();
446
447        drawPart = drawPart->next;
448      }
449      glDepthMask(GL_TRUE);
450      break;
451
452    case PARTICLE_SPARK:
453      glDisable(GL_LIGHTING);
454      glDepthMask(GL_FALSE);
455      //glEnable(GL_LINE_SMOOTH);
456      glEnable(GL_BLEND);
457
458      glBegin(GL_LINES);
459      while (likely(drawPart != NULL))
460      {
461        glColor4fv(drawPart->color);
462        glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
463        glVertex3f(drawPart->position.x - drawPart->velocity.x * drawPart->radius,
464                   drawPart->position.y - drawPart->velocity.y * drawPart->radius,
465                   drawPart->position.z - drawPart->velocity.z * drawPart->radius);
466        drawPart = drawPart->next;
467      }
468      glEnd();
469      break;
470
471    case PARTICLE_MODEL:
472      {
473        GLfloat matrix[4][4];
474
475        if (likely(this->model != NULL))
476          while (likely(drawPart != NULL))
477        {
478          glPushMatrix();
479          glMatrixMode(GL_MODELVIEW);
480          /* move */
481          glTranslatef(drawPart->position.x, drawPart->position.y, drawPart->position.z);
482          /* scale */
483          glScalef(drawPart->radius, drawPart->radius, drawPart->radius);
484          /* rotate */
485          drawPart->orientation.matrix (matrix);
486          glMultMatrixf((float*)matrix);
487
488          this->model->draw();
489
490          glPopMatrix();
491          drawPart = drawPart->next;
492        }
493        else
494          PRINTF(2)("no model loaded onto ParticleSystem-%s\n", this->getName());
495      }
496      break;
497
498    case PARTICLE_DOT:
499      glDisable(GL_LIGHTING);
500      glBegin(GL_POINTS);
501      while (likely(drawPart != NULL))
502      {
503        glColor4fv(drawPart->color);
504
505        glLineWidth(drawPart->radius);
506
507        glVertex3f(drawPart->position.x, drawPart->position.y, drawPart->position.z);
508        drawPart = drawPart->next;
509      }
510      glEnd();
511      break;
512  }
513  glPopAttrib();
514}
515
516/**
517   \brief adds a new Particle to the System
518   \param position the initial position, where the particle gets emitted.
519   \param velocity the initial velocity of the particle.
520   \param orientation the initial orientation of the Paritcle.
521   \param momentum the initial momentum of the Particle (the speed of its rotation).
522   \param data some more data given by the emitter
523*/
524void ParticleSystem::addParticle(const Vector& position, const Vector& velocity, const Quaternion& orientation, const Quaternion& momentum, unsigned int data)
525{
526  if (this->count <= this->maxCount)
527    {
528      // if it is the first Particle
529      if (unlikely(particles == NULL))
530        {
531          if (likely(deadList != NULL))
532            {
533              this->particles = this->deadList;
534              deadList = deadList->next;
535            }
536          else
537            {
538              PRINTF(5)("Generating new Particle\n");
539              this->particles = new Particle;
540            }
541          this->particles->next = NULL;
542        }
543      // filling the List from the beginning
544      else
545        {
546          Particle* tmpPart;
547          if (likely(deadList != NULL))
548            {
549              tmpPart = this->deadList;
550              deadList = deadList->next;
551            }
552          else
553            {
554              PRINTF(5)("Generating new Particle\n");
555              tmpPart = new Particle;
556            }
557          tmpPart->next = this->particles;
558          this->particles = tmpPart;
559        }
560      particles->lifeTime = this->lifeSpan + (float)(rand()/RAND_MAX)* this->randomLifeSpan;
561      particles->lifeCycle = 0.0;
562      particles->position = position;
563      particles->velocity = velocity;
564
565      particles->orientation = orientation;
566      particles->momentum = momentum;
567
568      //  particle->rotation = ; //! \todo rotation is once again something to be done.
569      particles->massRand = 2*(float)rand()/RAND_MAX -1;
570      particles->radiusRand = 2* (float)rand()/RAND_MAX -1;
571      particles->mass = this->massAnim.getValue(0.0) + this->randMassAnim.getValue(0.0)*particles->massRand;
572      particles->radius = this->radiusAnim.getValue(0.0) + this->randRadiusAnim.getValue(0.0)*particles->radiusRand;
573
574      ++this->count;
575    }
576  else
577    PRINTF(5)("maximum count of particles reached not adding any more\n");
578}
579
580/**
581   \brief outputs some nice debug information
582*/
583void ParticleSystem::debug() const
584{
585  PRINT(0)("  ParticleSystem %s, type: ", this->getName());
586  {
587      if (this->particleType == PARTICLE_MODEL)
588        PRINT(0)("model");
589      else if (this->particleType == PARTICLE_SPRITE)
590        PRINT(0)("sprite");
591      else if (this->particleType == PARTICLE_DOT)
592        PRINT(0)("dot");
593      else if (this->particleType == PARTICLE_SPARK)
594        PRINT(0)("spark");
595
596      PRINT(0)("\n");
597  }
598
599  PRINT(0)("  ParticleCount: %d, maximumCount: %d :: filled %d%%\n", this->count, this->maxCount, 100*this->count/this->maxCount);
600  if (deadList)
601    {
602      PRINT(0)("  - ParticleDeadList is used: ");
603      int i = 1;
604      Particle* tmpPart = this->deadList;
605      while (tmpPart = tmpPart->next) ++i;
606      PRINT(0)("count: %d\n", i);
607    }
608}
Note: See TracBrowser for help on using the repository browser.