| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917 |
- #include "Base.h"
- #include "ParticleEmitter.h"
- #include "Game.h"
- #include "Node.h"
- #include "Scene.h"
- #include "Quaternion.h"
- #include "Properties.h"
- #define PARTICLE_COUNT_MAX 100
- #define PARTICLE_EMISSION_RATE 10
- #define PARTICLE_EMISSION_RATE_TIME_INTERVAL 1000.0f / (float)PARTICLE_EMISSION_RATE
- namespace gameplay
- {
- ParticleEmitter::ParticleEmitter(SpriteBatch* batch, unsigned int particleCountMax) :
- _particleCountMax(particleCountMax), _particleCount(0), _particles(NULL),
- _emissionRate(PARTICLE_EMISSION_RATE), _started(false), _ellipsoid(false),
- _sizeStartMin(1.0f), _sizeStartMax(1.0f), _sizeEndMin(1.0f), _sizeEndMax(1.0f),
- _energyMin(1000L), _energyMax(1000L),
- _colorStart(Vector4::zero()), _colorStartVar(Vector4::zero()), _colorEnd(Vector4::one()), _colorEndVar(Vector4::zero()),
- _position(Vector3::zero()), _positionVar(Vector3::zero()),
- _velocity(Vector3::zero()), _velocityVar(Vector3::one()),
- _acceleration(Vector3::zero()), _accelerationVar(Vector3::zero()),
- _rotationPerParticleSpeedMin(0.0f), _rotationPerParticleSpeedMax(0.0f),
- _rotationSpeedMin(0.0f), _rotationSpeedMax(0.0f),
- _rotationAxis(Vector3::zero()), _rotation(Matrix::identity()),
- _spriteBatch(batch), _spriteTextureBlending(BLEND_TRANSPARENT), _spriteTextureWidth(0), _spriteTextureHeight(0), _spriteTextureWidthRatio(0), _spriteTextureHeightRatio(0), _spriteTextureCoords(NULL),
- _spriteAnimated(false), _spriteLooped(false), _spriteFrameCount(1), _spriteFrameRandomOffset(0),_spriteFrameDuration(0L), _spriteFrameDurationSecs(0.0f), _spritePercentPerFrame(0.0f),
- _node(NULL), _orbitPosition(false), _orbitVelocity(false), _orbitAcceleration(false),
- _timePerEmission(PARTICLE_EMISSION_RATE_TIME_INTERVAL), _timeLast(0L), _timeRunning(0L)
- {
- _particles = new Particle[particleCountMax];
- _spriteBatch->getStateBlock()->setDepthWrite(false);
- _spriteBatch->getStateBlock()->setDepthTest(true);
- }
- ParticleEmitter::~ParticleEmitter()
- {
- SAFE_DELETE(_spriteBatch);
- SAFE_DELETE_ARRAY(_particles);
- SAFE_DELETE_ARRAY(_spriteTextureCoords);
- }
- ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlending textureBlending, unsigned int particleCountMax)
- {
- assert(textureFile);
- Texture* texture = NULL;
- texture = Texture::create(textureFile, true);
- if (!texture)
- {
- LOG_ERROR_VARG("Error creating ParticleEmitter: Could not read texture file: %s", textureFile);
- return NULL;
- }
- // Use default SpriteBatch material.
- SpriteBatch* batch = SpriteBatch::create(texture, NULL, particleCountMax);
- texture->release(); // batch owns the texture.
- assert(batch);
- ParticleEmitter* emitter = new ParticleEmitter(batch, particleCountMax);
- assert(emitter);
- // By default assume only one frame which uses the entire texture.
- emitter->setTextureBlending(textureBlending);
- emitter->_spriteTextureWidth = texture->getWidth();
- emitter->_spriteTextureHeight = texture->getHeight();
- emitter->_spriteTextureWidthRatio = 1.0f / (float)texture->getWidth();
- emitter->_spriteTextureHeightRatio = 1.0f / (float)texture->getHeight();
- Rectangle texCoord((float)texture->getWidth(), (float)texture->getHeight());
- emitter->setSpriteFrameCoords(1, &texCoord);
- return emitter;
- }
- ParticleEmitter* ParticleEmitter::create(const char* particleFile)
- {
- assert(particleFile);
- Properties* properties = Properties::create(particleFile);
- if (!properties)
- {
- LOG_ERROR_VARG("Error loading ParticleEmitter: Could not load file: %s", particleFile);
- return NULL;
- }
- ParticleEmitter* particle = create(properties->getNextNamespace());
- SAFE_DELETE(properties);
- return particle;
- }
- ParticleEmitter* ParticleEmitter::create(Properties* properties)
- {
- if (!properties || strcmp(properties->getNamespace(), "particle") != 0)
- {
- LOG_ERROR("Error loading ParticleEmitter: No 'particle' namespace found");
- return NULL;
- }
- Properties* sprite = properties->getNextNamespace();
- if (!sprite || strcmp(sprite->getNamespace(), "sprite") != 0)
- {
- LOG_ERROR("Error loading ParticleEmitter: No 'sprite' namespace found");
- return NULL;
- }
- // Load sprite properties.
- // Path to image file is required.
- const char* texturePath = sprite->getString("path");
- if (strlen(texturePath) == 0)
- {
- LOG_ERROR_VARG("Error loading ParticleEmitter: No texture path specified: %s", texturePath);
- return NULL;
- }
- const char* blendingString = sprite->getString("blending");
- TextureBlending textureBlending = getTextureBlendingFromString(blendingString);
- int spriteWidth = sprite->getInt("width");
- int spriteHeight = sprite->getInt("height");
- bool spriteAnimated = sprite->getBool("animated");
- bool spriteLooped = sprite->getBool("looped");
- int spriteFrameCount = sprite->getInt("frameCount");
- int spriteFrameRandomOffset = sprite->getInt("frameRandomOffset");
- float spriteFrameDuration = sprite->getFloat("frameDuration");
- // Emitter properties.
- unsigned int particleCountMax = (unsigned int)properties->getInt("particleCountMax");
- if (particleCountMax == 0)
- {
- // Set sensible default.
- particleCountMax = PARTICLE_COUNT_MAX;
- }
- unsigned int emissionRate = (unsigned int)properties->getInt("emissionRate");
- if (emissionRate == 0)
- {
- emissionRate = PARTICLE_EMISSION_RATE;
- }
- bool ellipsoid = properties->getBool("ellipsoid");
- float sizeStartMin = properties->getFloat("sizeStartMin");
- float sizeStartMax = properties->getFloat("sizeStartMax");
- float sizeEndMin = properties->getFloat("sizeEndMin");
- float sizeEndMax = properties->getFloat("sizeEndMax");
- long energyMin = properties->getLong("energyMin");
- long energyMax = properties->getLong("energyMax");
- Vector4 colorStart;
- Vector4 colorStartVar;
- Vector4 colorEnd;
- Vector4 colorEndVar;
- properties->getVector4("colorStart", &colorStart);
- properties->getVector4("colorStartVar", &colorStartVar);
- properties->getVector4("colorEnd", &colorEnd);
- properties->getVector4("colorEndVar", &colorEndVar);
- Vector3 position;
- Vector3 positionVar;
- Vector3 velocity;
- Vector3 velocityVar;
- Vector3 acceleration;
- Vector3 accelerationVar;
- Vector3 rotationAxis;
- Vector3 rotationAxisVar;
- properties->getVector3("position", &position);
- properties->getVector3("positionVar", &positionVar);
- properties->getVector3("velocity", &velocity);
- properties->getVector3("velocityVar", &velocityVar);
- properties->getVector3("acceleration", &acceleration);
- properties->getVector3("accelerationVar", &accelerationVar);
- float rotationPerParticleSpeedMin = properties->getFloat("rotationPerParticleSpeedMin");
- float rotationPerParticleSpeedMax = properties->getFloat("rotationPerParticleSpeedMax");
- float rotationSpeedMin = properties->getFloat("rotationSpeedMin");
- float rotationSpeedMax = properties->getFloat("rotationSpeedMax");
- properties->getVector3("rotationAxis", &rotationAxis);
- properties->getVector3("rotationAxisVar", &rotationAxisVar);
- bool orbitPosition = properties->getBool("orbitPosition");
- bool orbitVelocity = properties->getBool("orbitVelocity");
- bool orbitAcceleration = properties->getBool("orbitAcceleration");
- // Apply all properties to a newly created ParticleEmitter.
- ParticleEmitter* emitter = ParticleEmitter::create(texturePath, textureBlending, particleCountMax);
- emitter->setEmissionRate(emissionRate);
- emitter->setEllipsoid(ellipsoid);
- emitter->setSize(sizeStartMin, sizeStartMax, sizeEndMin, sizeEndMax);
- emitter->setEnergy(energyMin, energyMax);
- emitter->setColor(colorStart, colorStartVar, colorEnd, colorEndVar);
- emitter->setPosition(position, positionVar);
- emitter->setVelocity(velocity, velocityVar);
- emitter->setAcceleration(acceleration, accelerationVar);
- emitter->setRotationPerParticle(rotationPerParticleSpeedMin, rotationPerParticleSpeedMax);
- emitter->setRotation(rotationSpeedMin, rotationSpeedMax, rotationAxis, rotationAxisVar);
- emitter->setSpriteAnimated(spriteAnimated);
- emitter->setSpriteLooped(spriteLooped);
- emitter->setSpriteFrameRandomOffset(spriteFrameRandomOffset);
- emitter->setSpriteFrameDuration(spriteFrameDuration);
- emitter->setSpriteFrameCoords(spriteFrameCount, spriteWidth, spriteHeight);
- emitter->setOrbit(orbitPosition, orbitVelocity, orbitAcceleration);
- return emitter;
- }
- unsigned int ParticleEmitter::getEmissionRate() const
- {
- return _emissionRate;
- }
- void ParticleEmitter::setEmissionRate(unsigned int rate)
- {
- _emissionRate = rate;
- _timePerEmission = 1000.0f / (float)_emissionRate;
- }
- void ParticleEmitter::start()
- {
- _started = true;
- _timeLast = Game::getGameTime();
- }
- void ParticleEmitter::stop()
- {
- _started = false;
- }
- bool ParticleEmitter::isStarted() const
- {
- return _started;
- }
- bool ParticleEmitter::isActive() const
- {
- if (_started)
- return true;
- if (!_node)
- return false;
- bool active = false;
- for (unsigned int i = 0; i < _particleCount; i++)
- {
- if (_particles[i]._energy > 0)
- {
- active = true;
- break;
- }
- }
- return active;
- }
- void ParticleEmitter::emit(unsigned int particleCount)
- {
- // Limit particleCount so as not to go over _particleCountMax.
- if (particleCount + _particleCount > _particleCountMax)
- {
- particleCount = _particleCountMax - _particleCount;
- }
- Vector3 translation;
- Matrix world = _node->getWorldMatrix();
- world.getTranslation(&translation);
- // Take translation out of world matrix so it can be used to rotate orbiting properties.
- world.m[12] = 0.0f;
- world.m[13] = 0.0f;
- world.m[14] = 0.0f;
- // Emit the new particles.
- for (unsigned int i = 0; i < particleCount; i++)
- {
- Particle* p = &_particles[_particleCount];
- generateColor(_colorStart, _colorStartVar, &p->_colorStart);
- generateColor(_colorEnd, _colorEndVar, &p->_colorEnd);
- p->_color.set(p->_colorStart);
- p->_energy = p->_energyStart = generateScalar(_energyMin, _energyMax);
- p->_size = p->_sizeStart = generateScalar(_sizeStartMin, _sizeStartMax);
- p->_sizeEnd = generateScalar(_sizeEndMin, _sizeEndMax);
- p->_rotationPerParticleSpeed = generateScalar(_rotationPerParticleSpeedMin, _rotationPerParticleSpeedMax);
- p->_angle = generateScalar(0.0f, p->_rotationPerParticleSpeed);
- p->_rotationSpeed = generateScalar(_rotationSpeedMin, _rotationSpeedMax);
- // Only initial position can be generated within an ellipsoidal domain.
- generateVector(_position, _positionVar, &p->_position, _ellipsoid);
- generateVector(_velocity, _velocityVar, &p->_velocity, false);
- generateVector(_acceleration, _accelerationVar, &p->_acceleration, false);
- generateVector(_rotationAxis, _rotationAxisVar, &p->_rotationAxis, false);
- // Initial position, velocity and acceleration can all be relative to the emitter's transform.
- // Rotate specified properties by the node's rotation.
- if (_orbitPosition)
- {
- world.transformPoint(p->_position, &p->_position);
- }
- if (_orbitVelocity)
- {
- world.transformPoint(p->_velocity, &p->_velocity);
- }
- if (_orbitAcceleration)
- {
- world.transformPoint(p->_acceleration, &p->_acceleration);
- }
- // The rotation axis always orbits the node.
- if (p->_rotationSpeed != 0.0f && !p->_rotationAxis.isZero())
- {
- world.transformPoint(p->_rotationAxis, &p->_rotationAxis);
- }
- // Translate position relative to the node's world space.
- p->_position.add(translation);
- // Initial sprite frame.
- if (_spriteFrameRandomOffset > 0)
- {
- p->_frame = rand() % _spriteFrameRandomOffset;
- }
- else
- {
- p->_frame = 0;
- }
- p->_timeOnCurrentFrame = 0.0f;
- ++_particleCount;
- }
- }
- unsigned int ParticleEmitter::getParticlesCount() const
- {
- return _particleCount;
- }
- void ParticleEmitter::setEllipsoid(bool ellipsoid)
- {
- _ellipsoid = ellipsoid;
- }
- void ParticleEmitter::setSize(float startMin, float startMax, float endMin, float endMax)
- {
- _sizeStartMin = startMin;
- _sizeStartMax = startMax;
- _sizeEndMin = endMin;
- _sizeEndMax = endMax;
- }
- float ParticleEmitter::getSizeStartMin() const
- {
- return _sizeStartMin;
- }
- float ParticleEmitter::getSizeStartMax() const
- {
- return _sizeStartMax;
- }
- float ParticleEmitter::getSizeEndMin() const
- {
- return _sizeEndMin;
- }
- float ParticleEmitter::getSizeEndMax() const
- {
- return _sizeEndMax;
- }
- void ParticleEmitter::setEnergy(long energyMin, long energyMax)
- {
- _energyMin = energyMin;
- _energyMax = energyMax;
- }
- long ParticleEmitter::getEnergyMin() const
- {
- return _energyMin;
- }
- long ParticleEmitter::getEnergyMax() const
- {
- return _energyMax;
- }
- void ParticleEmitter::setColor(const Vector4& startColor, const Vector4& startColorVar, const Vector4& endColor, const Vector4& endColorVar)
- {
- _colorStart.set(startColor);
- _colorStartVar.set(startColorVar);
- _colorEnd.set(endColor);
- _colorEndVar.set(endColorVar);
- }
- const Vector4& ParticleEmitter::getColorStart() const
- {
- return _colorStart;
- }
- const Vector4& ParticleEmitter::getColorStartVariance() const
- {
- return _colorStartVar;
- }
- const Vector4& ParticleEmitter::getColorEnd() const
- {
- return _colorEnd;
- }
- const Vector4& ParticleEmitter::getColorEndVariance() const
- {
- return _colorEndVar;
- }
- void ParticleEmitter::setPosition(const Vector3& position, const Vector3& positionVar)
- {
- _position.set(position);
- _positionVar.set(positionVar);
- }
- const Vector3& ParticleEmitter::getPosition() const
- {
- return _position;
- }
- const Vector3& ParticleEmitter::getPositionVariance() const
- {
- return _positionVar;
- }
- const Vector3& ParticleEmitter::getVelocity() const
- {
- return _velocity;
- }
- const Vector3& ParticleEmitter::getVelocityVariance() const
- {
- return _velocityVar;
- }
- void ParticleEmitter::setVelocity(const Vector3& velocity, const Vector3& velocityVar)
- {
- _velocity.set(velocity);
- _velocityVar.set(velocityVar);
- }
- const Vector3& ParticleEmitter::getAcceleration() const
- {
- return _acceleration;
- }
- const Vector3& ParticleEmitter::getAccelerationVariance() const
- {
- return _accelerationVar;
- }
- void ParticleEmitter::setAcceleration(const Vector3& acceleration, const Vector3& accelerationVar)
- {
- _acceleration.set(acceleration);
- _accelerationVar.set(accelerationVar);
- }
- void ParticleEmitter::setRotationPerParticle(float speedMin, float speedMax)
- {
- _rotationPerParticleSpeedMin = speedMin;
- _rotationPerParticleSpeedMax = speedMax;
- }
- float ParticleEmitter::getRotationPerParticleSpeedMin() const
- {
- return _rotationPerParticleSpeedMin;
- }
- float ParticleEmitter::getRotationPerParticleSpeedMax() const
- {
- return _rotationPerParticleSpeedMax;
- }
- void ParticleEmitter::setRotation(float speedMin, float speedMax, const Vector3& axis, const Vector3& axisVariance)
- {
- _rotationSpeedMin = speedMin;
- _rotationSpeedMax = speedMax;
- _rotationAxis.set(axis);
- _rotationAxisVar.set(axisVariance);
- }
- float ParticleEmitter::getRotationSpeedMin() const
- {
- return _rotationSpeedMin;
- }
- float ParticleEmitter::getRotationSpeedMax() const
- {
- return _rotationSpeedMax;
- }
- const Vector3& ParticleEmitter::getRotationAxis() const
- {
- return _rotationAxis;
- }
- const Vector3& ParticleEmitter::getRotationAxisVariance() const
- {
- return _rotationAxisVar;
- }
- void ParticleEmitter::setTextureBlending(TextureBlending textureBlending)
- {
- switch (textureBlending)
- {
- case BLEND_OPAQUE:
- _spriteBatch->getStateBlock()->setBlend(false);
- break;
- case BLEND_TRANSPARENT:
- _spriteBatch->getStateBlock()->setBlend(true);
- _spriteBatch->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
- _spriteBatch->getStateBlock()->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
- break;
- case BLEND_ADDITIVE:
- _spriteBatch->getStateBlock()->setBlend(true);
- _spriteBatch->getStateBlock()->setBlendSrc(RenderState::BLEND_ONE);
- _spriteBatch->getStateBlock()->setBlendDst(RenderState::BLEND_ONE);
- break;
- case BLEND_MULTIPLIED:
- _spriteBatch->getStateBlock()->setBlend(true);
- _spriteBatch->getStateBlock()->setBlendSrc(RenderState::BLEND_ZERO);
- _spriteBatch->getStateBlock()->setBlendDst(RenderState::BLEND_SRC_COLOR);
- break;
- }
- }
- void ParticleEmitter::setSpriteAnimated(bool animated)
- {
- _spriteAnimated = animated;
- }
- bool ParticleEmitter::isSpriteAnimated() const
- {
- return _spriteAnimated;
- }
- void ParticleEmitter::setSpriteLooped(bool looped)
- {
- _spriteLooped = looped;
- }
- bool ParticleEmitter::isSpriteLooped() const
- {
- return _spriteLooped;
- }
- void ParticleEmitter::setSpriteFrameRandomOffset(int maxOffset)
- {
- _spriteFrameRandomOffset = maxOffset;
- }
- int ParticleEmitter::getSpriteFrameRandomOffset() const
- {
- return _spriteFrameRandomOffset;
- }
- void ParticleEmitter::setSpriteFrameDuration(long duration)
- {
- _spriteFrameDuration = duration;
- _spriteFrameDurationSecs = (float)duration / 1000.0f;
- }
- long ParticleEmitter::getSpriteFrameDuration() const
- {
- return _spriteFrameDuration;
- }
- void ParticleEmitter::setSpriteTexCoords(unsigned int frameCount, float* texCoords)
- {
- _spriteFrameCount = frameCount;
- _spritePercentPerFrame = 1.0f / (float)frameCount;
- SAFE_DELETE_ARRAY(_spriteTextureCoords);
- _spriteTextureCoords = new float[frameCount * 4];
- memcpy(_spriteTextureCoords, texCoords, frameCount * 4 * sizeof(float));
- }
- void ParticleEmitter::setSpriteFrameCoords(unsigned int frameCount, Rectangle* frameCoords)
- {
- _spriteFrameCount = frameCount;
- _spritePercentPerFrame = 1.0f / (float)frameCount;
- float* texCoords = new float[frameCount * 4];
- // Pre-compute texture coordinates from rects.
- for (unsigned int i = 0; i < frameCount; i++)
- {
- float u1 = _spriteTextureWidthRatio * frameCoords[i].x;
- float v1 = 1.0f - _spriteTextureHeightRatio * frameCoords[i].y;
- float u2 = u1 + _spriteTextureWidthRatio * frameCoords[i].width;
- float v2 = v1 - _spriteTextureHeightRatio * frameCoords[i].height;
- texCoords[i*4] = u1;
- texCoords[i*4 + 1] = v1;
- texCoords[i*4 + 2] = u2;
- texCoords[i*4 + 3] = v2;
- }
- SAFE_DELETE_ARRAY(_spriteTextureCoords);
- _spriteTextureCoords = new float[frameCount * 4];
- memcpy(_spriteTextureCoords, texCoords, frameCount * 4 * sizeof(float));
- SAFE_DELETE_ARRAY(texCoords);
- }
- void ParticleEmitter::setSpriteFrameCoords(unsigned int frameCount, int width, int height)
- {
- int x;
- int y;
- Rectangle* frameCoords = new Rectangle[frameCount];
- unsigned int cols = _spriteTextureWidth / width;
- unsigned int rows = _spriteTextureHeight / height;
- unsigned int n = 0;
- for (unsigned int i = 0; i < rows; ++i)
- {
- y = i * height;
- for (unsigned int j = 0; j < cols; ++j)
- {
- x = j * width;
- frameCoords[i*cols + j] = Rectangle(x, y, width, height);
- if (++n == frameCount)
- {
- break;
- }
- }
- if (n == frameCount)
- {
- break;
- }
- }
- setSpriteFrameCoords(frameCount, frameCoords);
- SAFE_DELETE_ARRAY(frameCoords);
- }
- Node* ParticleEmitter::getNode() const
- {
- return _node;
- }
- void ParticleEmitter::setNode(Node* node)
- {
- // Connect the new node.
- _node = node;
- }
- void ParticleEmitter::setOrbit(bool orbitPosition, bool orbitVelocity, bool orbitAcceleration)
- {
- _orbitPosition = orbitPosition;
- _orbitVelocity = orbitVelocity;
- _orbitAcceleration = orbitAcceleration;
- }
- long ParticleEmitter::generateScalar(long min, long max)
- {
- // Note: this is not a very good RNG, but it should be suitable for our purposes.
- long r = 0;
- for (unsigned int i = 0; i < sizeof(long)/sizeof(int); i++)
- {
- r = r << 8; // sizeof(int) * CHAR_BITS
- r |= rand();
- }
- // Now we have a random long between 0 and MAX_LONG. We need to clamp it between min and max.
- r %= max - min;
- r += min;
- return r;
- }
- float ParticleEmitter::generateScalar(float min, float max)
- {
- return min + (max - min) * MATH_RANDOM_0_1();
- }
- void ParticleEmitter::generateVectorInRect(const Vector3& base, const Vector3& variance, Vector3* dst)
- {
- // Scale each component of the variance vector by a random float
- // between -1 and 1, then add this to the corresponding base component.
- dst->x = base.x + variance.x * MATH_RANDOM_MINUS1_1();
- dst->y = base.y + variance.y * MATH_RANDOM_MINUS1_1();
- dst->z = base.z + variance.z * MATH_RANDOM_MINUS1_1();
- }
- void ParticleEmitter::generateVectorInEllipsoid(const Vector3& center, const Vector3& scale, Vector3* dst)
- {
- // Generate a point within a unit cube, then reject if the point is not in a unit sphere.
- do
- {
- dst->x = MATH_RANDOM_MINUS1_1();
- dst->y = MATH_RANDOM_MINUS1_1();
- dst->z = MATH_RANDOM_MINUS1_1();
- } while (dst->length() > 1.0f);
-
- // Scale this point by the scaling vector.
- dst->x *= scale.x;
- dst->y *= scale.y;
- dst->z *= scale.z;
- // Translate by the center point.
- dst->add(center);
- }
- void ParticleEmitter::generateVector(const Vector3& base, const Vector3& variance, Vector3* dst, bool ellipsoid)
- {
- if (ellipsoid)
- {
- generateVectorInEllipsoid(base, variance, dst);
- }
- else
- {
- generateVectorInRect(base, variance, dst);
- }
- }
- void ParticleEmitter::generateColor(const Vector4& base, const Vector4& variance, Vector4* dst)
- {
- // Scale each component of the variance color by a random float
- // between -1 and 1, then add this to the corresponding base component.
- dst->x = base.x + variance.x * MATH_RANDOM_MINUS1_1();
- dst->y = base.y + variance.y * MATH_RANDOM_MINUS1_1();
- dst->z = base.z + variance.z * MATH_RANDOM_MINUS1_1();
- dst->w = base.w + variance.w * MATH_RANDOM_MINUS1_1();
- }
- ParticleEmitter::TextureBlending ParticleEmitter::getTextureBlendingFromString(const char* str)
- {
- if (strcmp(str, "BLEND_OPAQUE") == 0 || strcmp(str, "OPAQUE") == 0)
- {
- return BLEND_OPAQUE;
- }
- else if (strcmp(str, "BLEND_TRANSPARENT") == 0 || strcmp(str, "TRANSPARENT") == 0)
- {
- return BLEND_TRANSPARENT;
- }
- else if (strcmp(str, "BLEND_ADDITIVE") == 0 || strcmp(str, "ADDITIVE") == 0)
- {
- return BLEND_ADDITIVE;
- }
- else if (strcmp(str, "BLEND_MULTIPLIED") == 0 || strcmp(str, "MULTIPLIED") == 0)
- {
- return BLEND_MULTIPLIED;
- }
- return BLEND_TRANSPARENT;
- }
- void ParticleEmitter::update(long elapsedTime)
- {
- if (!isActive())
- {
- return;
- }
- // Calculate the time passed since last update.
- float elapsedSecs = (float)elapsedTime / 1000.0f;
- if (_started && _emissionRate)
- {
- // Calculate how much time has passed since we last emitted particles.
- _timeRunning += elapsedTime;
- // How many particles should we emit this frame?
- unsigned int emitCount = _timeRunning / _timePerEmission;
-
- if ((int)_timePerEmission > 0)
- {
- _timeRunning %= (int)_timePerEmission;
- }
- emit(emitCount);
- }
- // Now update all currently living particles.
- for (unsigned int particlesIndex = 0; particlesIndex < _particleCount; ++particlesIndex)
- {
- Particle* p = &_particles[particlesIndex];
- p->_energy -= elapsedTime;
- if (p->_energy > 0L)
- {
- if (p->_rotationSpeed != 0.0f && !p->_rotationAxis.isZero())
- {
- Matrix::createRotation(p->_rotationAxis, p->_rotationSpeed * elapsedSecs, &_rotation);
- _rotation.transformPoint(p->_velocity, &p->_velocity);
- _rotation.transformPoint(p->_acceleration, &p->_acceleration);
- }
- // Particle is still alive.
- p->_velocity.x += p->_acceleration.x * elapsedSecs;
- p->_velocity.y += p->_acceleration.y * elapsedSecs;
- p->_velocity.z += p->_acceleration.z * elapsedSecs;
- p->_position.x += p->_velocity.x * elapsedSecs;
- p->_position.y += p->_velocity.y * elapsedSecs;
- p->_position.z += p->_velocity.z * elapsedSecs;
- p->_angle += p->_rotationPerParticleSpeed * elapsedSecs;
- // Simple linear interpolation of color and size.
- float percent = 1.0f - ((float)p->_energy / (float)p->_energyStart);
- p->_color.x = p->_colorStart.x + (p->_colorEnd.x - p->_colorStart.x) * percent;
- p->_color.y = p->_colorStart.y + (p->_colorEnd.y - p->_colorStart.y) * percent;
- p->_color.z = p->_colorStart.z + (p->_colorEnd.z - p->_colorStart.z) * percent;
- p->_color.w = p->_colorStart.w + (p->_colorEnd.w - p->_colorStart.w) * percent;
- p->_size = p->_sizeStart + (p->_sizeEnd - p->_sizeStart) * percent;
- // Handle sprite animations.
- if (_spriteAnimated)
- {
- if (!_spriteLooped)
- {
- // The last frame should finish exactly when the particle dies.
- float percentSpent = 0.0f;
- for (unsigned int i = 0; i < p->_frame; i++)
- {
- percentSpent += _spritePercentPerFrame;
- }
- p->_timeOnCurrentFrame = percent - percentSpent;
- if (p->_frame < _spriteFrameCount - 1 &&
- p->_timeOnCurrentFrame >= _spritePercentPerFrame)
- {
- ++p->_frame;
- }
- }
- else
- {
- // _spriteFrameDurationSecs is an absolute time measured in seconds,
- // and the animation repeats indefinitely.
- p->_timeOnCurrentFrame += elapsedSecs;
- if (p->_timeOnCurrentFrame >= _spriteFrameDurationSecs)
- {
- p->_timeOnCurrentFrame -= _spriteFrameDurationSecs;
- ++p->_frame;
- if (p->_frame == _spriteFrameCount)
- {
- p->_frame = 0;
- }
- }
- }
- }
- }
- else
- {
- // Particle is dead. Move the particle furthest from the start of the array
- // down to take its place, and re-use the slot at the end of the list of living particles.
- if (particlesIndex != _particleCount - 1)
- {
- _particles[particlesIndex] = _particles[_particleCount - 1];
- }
- --_particleCount;
- }
- }
- }
- void ParticleEmitter::draw()
- {
- if (!isActive())
- {
- return;
- }
- if (_particleCount > 0)
- {
- // Set our node's view projection matrix to this emitter's effect.
- if (_node)
- {
- _spriteBatch->setProjectionMatrix(_node->getViewProjectionMatrix());
- }
- // Begin sprite batch drawing
- _spriteBatch->begin();
- // 2D Rotation.
- Vector2 pivot(0.5f, 0.5f);
- // 3D Rotation so that particles always face the camera.
- const Matrix& cameraWorldMatrix = _node->getScene()->getActiveCamera()->getNode()->getWorldMatrix();
- Vector3 right;
- cameraWorldMatrix.getRightVector(&right);
- Vector3 up;
- cameraWorldMatrix.getUpVector(&up);
- for (unsigned int i = 0; i < _particleCount; i++)
- {
- Particle* p = &_particles[i];
- _spriteBatch->draw(p->_position, right, up, p->_size, p->_size,
- _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3],
- p->_color, pivot, p->_angle);
- }
- // Render.
- _spriteBatch->end();
- }
- }
- }
|