/* 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; CREATE_FACTORY(ParticleEmitter); /** \brief standard constructor */ ParticleEmitter::ParticleEmitter(const Vector& direction, float angle, float emissionRate, float velocity) { this->setClassID(CL_PARTICLE_EMITTER, "ParticleEmitter"); this->type = EMITTER_DOT; this->emitterSize = 1.0; this->direction = direction; this->setInheritSpeed(0); this->setSpread(angle); this->setEmissionRate(emissionRate); this->setEmissionVelocity(velocity); ParticleEngine::getInstance()->addEmitter(this); } /** \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 removes the EmitterSystem from the ParticleEngine */ 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, "inherit-speed", this, &ParticleEmitter::setInheritSpeed) .describe("the extent, the speed of the emitter has on the particles"); LoadParam(root, "emission-velocity", this, &ParticleEmitter::setEmissionVelocity) .describe("How fast the particles are emittet (their initial speed)"); LoadParam(root, "emission-momentum", this, &ParticleEmitter::setEmissionMomentum) .describe("How fast the particles rotation is at emissiontime (their initial momentum)"); 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() {} /** \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->setType(EMITTER_PLANE); else if (!strcmp(type, "cube")) this->setType(EMITTER_CUBE); else if (!strcmp(type, "sphere")) this->setType(EMITTER_SPHERE); else this->setType(EMITTER_DOT); } const char* ParticleEmitter::getTypeC(void) const { if (this->type == EMITTER_PLANE) return "EMITTER_PLANE"; else if (this->type == EMITTER_CUBE) return "EMITTER_CUBE"; else if (this->type == EMITTER_SPHERE) return "EMITTER_SPHERE"; else return "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 initial 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 sets the initial Momentum of all particles emitted \param momentum the new Momentum (just a float for being not too complicated). \param randomMomentum variation from the given value. */ void ParticleEmitter::setEmissionMomentum(float momentum, float randomMomentum) { this->momentum = momentum; this->momentumRandom = randomMomentum; } /** \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 (particles only partly emitted in this timestep) 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 timestep\n", count, this->saveTime); if (likely(count > 0)) { Vector inheritVelocity = this->getVelocity() * this->inheritSpeed; for (int i = 0; i < (int)count; i++) { 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 & EMITTER_PLANE) { 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 & EMITTER_CUBE) { extension = Vector((float)rand()/RAND_MAX -.5, (float)rand()/RAND_MAX -.5, (float)rand()/RAND_MAX -.5) * this->emitterSize; } // ROTATIONAL CALCULATION (this must not be done for all types of particles.) randDir = Vector(rand()-RAND_MAX/2, rand()-RAND_MAX/2, rand()-RAND_MAX/2); randDir.normalize(); Quaternion orient = Quaternion(M_PI, randDir); Quaternion moment = Quaternion(this->momentum + this->momentumRandom, randDir); system->addParticle(this->getAbsCoor() + extension - equalSpread, velocityV, orient, moment); } } } } /** \brief outputs some nice debug information */ void ParticleEmitter::debug(void) const { PRINT(0)(" Emitter %s\n", this->getName()); PRINT(0)(" EmissionRate: %f, Speed: %f, SpreadAngle: %f\n", this->getEmissionRate(), this->getEmissionVelocity(), this->getSpread()); PRINT(0)(" Size %f, Type: %s\n", this->getSize(), this->getTypeC()); }