Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: now one should be able to create entities on the Fly

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