Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5678 was 5671, checked in by bensch, 19 years ago

orxonox/trunk: renamed class LoadParam to CLoadParam and Macro LoadParamNew to LoadParam

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