Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: more load-params for Particles

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