/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: Patrick Boenzli */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_PARTICLE #include "particle_emitter.h" #include "particle_system.h" #include "particle_engine.h" #include "load_param.h" #include "debug.h" #include "stdlibincl.h" using namespace std; /** \brief standard constructor */ ParticleEmitter::ParticleEmitter(const Vector& direction, float angle, float emissionRate, float velocity) { this->type = EMITTER_DOT; this->emitterSize = 1.0; this->direction = direction; this->setInheritSpeed(0); this->setSpread(angle); this->setEmissionRate(emissionRate); this->setEmissionVelocity(velocity); } /** \brief constructs and loads a ParticleEmitter from a XML-element \param root the XML-element to load from */ ParticleEmitter::ParticleEmitter(const TiXmlElement* root) { this->setClassID(CL_PARTICLE_EMITTER, "ParticleEmitter"); this->saveTime = 0.0; if (root) this->loadParams(root); ParticleEngine::getInstance()->addEmitter(this); } /** \brief standard destructor */ ParticleEmitter::~ParticleEmitter () { ParticleEngine::getInstance()->removeEmitter(this); } /** \brief loads a ParticleEmitter from a XML-element \param root the XML-element to load from */ void ParticleEmitter::loadParams(const TiXmlElement* root) { static_cast(this)->loadParams(root); LoadParam(root, "type", this, &ParticleEmitter::setType) .describe("What type of emitter is this [dot, plane, cube, sphere]."); LoadParam(root, "size", this, &ParticleEmitter::setSize) .describe("How big the emitter is (no effect on dot-emitters)"); LoadParam(root, "rate", this, &ParticleEmitter::setEmissionRate) .describe("How many particles should be emittet from this emitter"); LoadParam(root, "emission-velocity", this, &ParticleEmitter::setEmissionVelocity) .describe("How fast the particles are emittet (their initial speed)"); LoadParam(root, "spread", this, &ParticleEmitter::setSpread) .describe("The angle the particles are emitted from (angle, deviation)"); } /** \brief this start the emitter */ void ParticleEmitter::start() {} /** \brief this stops the emitter */ void ParticleEmitter::stop() {} /* these are Animation interfaces: so you can change spec values as you want */ /** \param type the new Type of this emitter */ void ParticleEmitter::setType(EMITTER_TYPE type) { this->type = type; } /** \brief sets the type of emitter \param type the type as a const char* dot: EMITTER_DOT, plane: EMITTER_PLANE, cube: EMITTER_CUBE, sphere, EMITTER_SPHERE; */ void ParticleEmitter::setType(const char* type) { if (!strcmp(type, "plane")) this->type = EMITTER_PLANE; else if (!strcmp(type, "cube")) this->type = EMITTER_CUBE; else if (!strcmp(type, "sphere")) this->type = EMITTER_SPHERE; else this->type = EMITTER_DOT; } /** \brief sets a new size to the emitter */ void ParticleEmitter::setSize(float emitterSize) { if (emitterSize > 0.0) this->emitterSize = emitterSize; else emitterSize = 0.0; } /** \brief set the emission rate \param emissionRate: sets the number of particles emitted per second if you want to change the value of this variable during emission time (to make it more dynamic) you may want to use the animation class */ void ParticleEmitter::setEmissionRate(float emissionRate) { if (emissionRate > 0.0) this->emissionRate = emissionRate; else this->emissionRate = 0.0; } /** \brief how much of the speed from the ParticleEmitter should flow onto the ParticleSystem \param value a Value between zero and one if you want to change the value of this variable during emission time (to make it more dynamic) you may want to use the animation class */ void ParticleEmitter::setInheritSpeed(float value) { if (unlikely(value > 1.0)) this->inheritSpeed = 1; else if (unlikely(value < 0.0)) this->inheritSpeed = 0; else this->inheritSpeed = value; } /** \brief set the angle of the emitter \param angle around the direction in which there are particles to be emitted \param randomAngle A random spread-angle, the +- randomness of this option if you want to change the value of this variable during emission time (to make it more dynamic) you may want to use the animation class */ void ParticleEmitter::setSpread(float angle, float randomAngle) { this->angle = angle; this->randomAngle = randomAngle; } /** \brief sets the velocity of all particles emitted \param velocity The starting velocity of the emitted particles \param randomVelocity A random starting velocity, the +- randomness of this option if you want to change the value of this variable during emission time (to make it more dynamic) you may want to use the animation class */ void ParticleEmitter::setEmissionVelocity(float velocity, float randomVelocity) { this->velocity = velocity; this->randomVelocity = randomVelocity; } /** \brief this set the time to life of a particle, after which it will die \param dt: the time to live in seconds \param system: the system into which to emitt if you want to change the value of this variable during emission time (to make it more dynamic) you may want to use the animation class */ void ParticleEmitter::tick(float dt, ParticleSystem* system) { if (likely(dt > 0.0 && this->emissionRate > 0.0)) { // saving the time float count = (dt+this->saveTime) * this->emissionRate; this->saveTime = modff(count, &count) / this->emissionRate; PRINTF(5)("emitting %f particles, saving %f seconds for the next round\n", count, this->saveTime); if (likely(count > 0)) { Vector inheritVelocity = this->getVelocity() * this->inheritSpeed; for (int i = 0; i < count; i++) // emmits from EMITTER_DOT, { Vector randDir = Vector(rand()-RAND_MAX/2, rand()-RAND_MAX/2, rand()-RAND_MAX/2); randDir.normalize(); randDir = (this->getAbsDir()*Quaternion(angle + randomAngle *((float)rand()/RAND_MAX -.5), randDir)).apply(this->direction); Vector velocityV = randDir.getNormalized()*this->velocity + inheritVelocity; // this should spread the Particles evenly. if the Emitter is moved around quickly Vector equalSpread = this->getVelocity() * rand()/RAND_MAX * dt; Vector extension; // the Vector for different fields. if (this->type & 2) { extension = Vector(this->emitterSize * ((float)rand()/RAND_MAX -.5), 0, this->emitterSize * ((float)rand()/RAND_MAX - .5)); extension = this->getAbsDir().apply(extension); } else if (this->type & 8) { extension = Vector((float)rand()/RAND_MAX -.5, (float)rand()/RAND_MAX -.5, (float)rand()/RAND_MAX -.5) * this->emitterSize; } system->addParticle(this->getAbsCoor() + extension - equalSpread, velocityV); } } } } /** \brief outputs some nice debug information */ void ParticleEmitter::debug(void) { PRINT(0)(" Emitter %s\n", this->getName()); }