void ParticleEmitter::update(float elapsedTime) { if (!isActive()) { return; } // Calculate the time passed since last update. float elapsedSecs = elapsedTime * 0.001f; if (_started && _emissionRate) { // Calculate how much time has passed since we last emitted particles. _timeRunning += elapsedTime; // How many particles should we emit this frame? GP_ASSERT(_timePerEmission); unsigned int emitCount = (unsigned int)(_timeRunning / _timePerEmission); if (emitCount) { if ((int)_timePerEmission > 0) { _timeRunning = fmod(_timeRunning, (double)_timePerEmission); } emitOnce(emitCount); } } GP_ASSERT(_node && _node->getScene() && _node->getScene()->getActiveCamera()); const Frustum& frustum = _node->getScene()->getActiveCamera()->getFrustum(); // Now update all currently living particles. GP_ASSERT(_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; if (!frustum.intersects(p->_position)) { p->_visible = false; continue; } 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::update(float elapsedTime) { if (!isActive()) return; // Cap particle updates at a maximum rate. This saves processing // and also improves precision since updating with very small // time increments is more lossy. static double runningTime = 0; runningTime += elapsedTime; if (runningTime < PARTICLE_UPDATE_RATE_MAX) return; float elapsedMs = runningTime; runningTime = 0; float elapsedSecs = elapsedMs * 0.001f; if (_started && _emissionRate) { // Calculate how much time has passed since we last emitted particles. _emitTime += elapsedMs; //+= elapsedTime; // How many particles should we emit this frame? GP_ASSERT(_timePerEmission); unsigned int emitCount = (unsigned int)(_emitTime / _timePerEmission); if (emitCount) { if ((int)_timePerEmission > 0) { _emitTime = fmod((double)_emitTime, (double)_timePerEmission); } emitOnce(emitCount); } } // Now update all currently living particles. GP_ASSERT(_particles); for (unsigned int particlesIndex = 0; particlesIndex < _particleCount; ++particlesIndex) { Particle* p = &_particles[particlesIndex]; p->_energy -= elapsedMs; 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; } } }