void
SparkDrawer::draw(const ParticleSystem& psys) const
{
  buffer->clear();
  buffer->set_pos(Vector2f(psys.get_x_pos(), psys.get_y_pos()));

  if (width == 1.0f)
  {
    buffer->set_mode(GL_LINES);
    buffer->set_blend_func(GL_SRC_ALPHA, GL_ONE);
    for(ParticleSystem::const_iterator i = psys.begin(); i != psys.end(); ++i)
    {
      buffer->color(Color(color.r, color.g, color.b, color.a - (color.a * psys.get_progress(i->t))));
      buffer->vertex(i->x + i->v_x/10.0f, i->y + i->v_y/10.0f); 

      buffer->color(Color(0, 0, 0, 0));
      buffer->vertex(i->x, i->y);
    }
  }
  else
  {
    buffer->set_mode(GL_QUADS);
    buffer->set_blend_func(GL_SRC_ALPHA, GL_ONE);
    for(ParticleSystem::const_iterator i = psys.begin(); i != psys.end(); ++i)
    {
      const float len = sqrtf(i->v_x * i->v_x  +  i->v_y * i->v_y);

      const float o_x = i->v_y/len * width;
      const float o_y = i->v_x/len * width;

      const float x1 = i->x;
      const float y1 = i->y;
      const float x2 = i->x + i->v_x/10.0f;
      const float y2 = i->y + i->v_y/10.0f;

      buffer->color(Color(0, 0, 0, 0));
      buffer->vertex(x1 + o_x, y1 - o_y);

      buffer->color(Color(0, 0, 0, 0));
      buffer->vertex(x1 - o_x, y1 + o_y);

      buffer->color(Color(color.r, color.g, color.b, color.a - (color.a * psys.get_progress(i->t))));
      buffer->vertex(x2 - o_x, y2 + o_y); 

      buffer->color(Color(color.r, color.g, color.b, color.a - (color.a * psys.get_progress(i->t))));
      buffer->vertex(x2 + o_x, y2 - o_y); 
    }
  }

  buffer->render(~0u);
}
void MagnetPSA::execute( ParticleSystem &particleSystem, float dTime )
{
	BW_GUARD_PROFILER( MagnetPSA_execute );

	// Do nothing if the dTime passed was zero or if the particle system
	// is not quite old enough to be active.
	if ( ( age_ < delay() ) || ( dTime <= 0.0f ) )
	{
		age_ += dTime;
		return;
	}

	// If there is no source matrix provider, use the origin of the 
	// particle system itself.
	Vector3 origin;
	if ( source_ )
	{
		Matrix m;
		source_->matrix(m);
		origin = m.applyToOrigin();
	}
	else
	{
		if ( !particleSystem.pRenderer() || !particleSystem.pRenderer()->local() )
		{
			origin = particleSystem.worldTransform().applyToOrigin();
		}
		else
		{
			origin.set(0,0,0);
		}
	}

	Particles::iterator current = particleSystem.begin();
	Particles::iterator endOfParticles = particleSystem.end();
	while ( current != endOfParticles )
	{
		Particle &particle = *current++;

		if (particle.isAlive())
		{
			// Here we go: Apply acceleration to velocity based on magnet position.
			Vector3 dv (origin-particle.position());
			float separation = max(dv.length(), minDist_);
			dv /= separation;
			float factor = (dTime * strength_) / separation;
			Vector3 velocity;
			particle.getVelocity( velocity );
			particle.setVelocity( velocity + (dv * factor) );
		}
	}
}
/**
 *	This method executes the action for the given frame of time. The dTime
 *	parameter is the time elapsed since the last call.
 *
 *	@param particleSystem	The particle system on which to operate.
 *	@param dTime			Elapsed time in seconds.
 */
void ScalerPSA::execute( ParticleSystem &particleSystem, float dTime )
{
	BW_GUARD_PROFILER( ScalerPSA_execute );

	// Do nothing if the dTime passed was zero or if the particle system
	// is not quite old enough to be active.
	if ( ( age_ < delay() ) || ( dTime <= 0.0f ) )
	{
		age_ += dTime;
		return;
	}


	Particles::iterator current = particleSystem.begin();
	Particles::iterator endOfParticles = particleSystem.end();
	while ( current != endOfParticles )
	{
		Particle &particle = *current;

		if (particle.isAlive())
		{
			float size = particle.size();

			if ( size > size_ )
			{
				size -= rate_ * dTime;
				if ( size < size_ )
				{
					size = size_;
				}
			}
			else if ( size < size_ )
			{
				size += rate_ * dTime;
				if ( size > size_ )
				{
					size = size_;
				}
			}

			particle.size( size );
		}

		++current;
	}
}
/**
 *	This method executes the action for the given frame of time. The dTime
 *	parameter is the time elapsed since the last call.
 *
 *	@param particleSystem	The particle system on which to operate.
 *	@param dTime			Elapsed time in seconds.
 */
void CollidePSA::execute( ParticleSystem &particleSystem, float dTime )
{
	BW_GUARD;
	SourcePSA* pSource = static_cast<SourcePSA*>( &*particleSystem.pAction( PSA_SOURCE_TYPE_ID ) );

	if ( !pSource )
		return;

	RompColliderPtr pGS = pSource->groundSpecifier();
	if ( !pGS )
		return;

	uint64	tend = timestamp() + stampsPerSecond() / 2000;

	bool soundHit = false;
	float maxVelocity = 0;
	Vector3 soundPos;
	uint materialKind;

	Particles::iterator it = particleSystem.begin();
	Particles::iterator end = particleSystem.end();

	WorldTriangle tri;

	//bust out of the loop if we take more than 0.5 msec

	//Sprite particles don't calculate spin
	while ( it != end && timestamp()<tend )
	{
		Particle &particle = *it++;

		if (!particle.isAlive())
		{		
			continue;
		}

		//note - particles get moved after actions.
		Vector3 velocity;
		particle.getVelocity(velocity);
		Vector3 pos;
		Vector3 newPos;

		if(particleSystem.isLocal())
		{
			Matrix world = particleSystem.worldTransform();
			pos = world.applyPoint(particle.position());
			Vector3 nPos;
			particleSystem.predictPosition( particle, dTime, nPos );
			newPos = world.applyPoint(nPos);
		}
		else
		{
			pos = particle.position();
			particleSystem.predictPosition( particle, dTime, newPos );
		}


		float tValue = pGS->collide( pos, newPos, tri );
		if ( tValue >= 0.f && tValue <= 1.f )
		{
			// calc v as a dotprod of the two normalised vectors (before and after collision)
			Vector3 oldVel = velocity / velocity.length();
			tri.bounce( velocity, elasticity_ );
			particle.setVelocity( velocity );
			float newSpeed = velocity.length();
			Vector3 newVel(velocity / newSpeed);
			float severity = oldVel.dotProduct(newVel);
			//DEBUG_MSG("severity: %1.3f, speed=%1.3f\n", severity, newSpeed);
			float v = (1 - severity) * newSpeed;

			//now spin the particle ( mesh only )
			if ( !spriteBased_ )
			{
				//first, calculate the current rotation, and update the pitch/yaw value.
				Matrix currentRot;
				currentRot.setRotate( particle.yaw(), particle.pitch(), 0.f );
				Matrix spin;
                float spinSpeed = particle.meshSpinSpeed();
                Vector3 meshSpinAxis = particle.meshSpinAxis();
                // If there is no spin direction then creating a rotation 
                // matrix can create weird matrices - e.g. matrices with
                // negative scale components and a translation.  We choose the
                // velocity as the spin direction (aribitrarily choosing, for
                // example up looks weird).
                if (meshSpinAxis == Vector3::zero())
                {
                    meshSpinAxis = velocity;
                    meshSpinAxis.normalise();
                }
				
				D3DXMatrixRotationAxis
                ( 
                    &spin, 
                    &meshSpinAxis, 
                    spinSpeed * (particle.age()-particle.meshSpinAge()) 
                );
				
				currentRot.preMultiply( spin );		
				particle.pitch( currentRot.pitch() );
				particle.yaw( currentRot.yaw() );

				//now, reset the age of the spin 
				particle.meshSpinAge( particle.age() );

				//finally, update the spin ( stored in the particle's colour )
				float addedSpin = unitRand() * (maxAddedRotation_-minAddedRotation_) + minAddedRotation_;
				addedSpin *= min( newSpeed, 1.f );				
				spinSpeed = Math::clamp( 0.f, spinSpeed + addedSpin, 1.f );
                particle.meshSpinSpeed(spinSpeed);
                particle.meshSpinAxis(meshSpinAxis);
			}

			if ( soundEnabled_ && v > 0.5f )
			{
				soundHit = true;
				if (v > maxVelocity) {
					maxVelocity = v;
					soundPos = particle.position();
					materialKind = tri.materialKind();
				}
			}
		}
	}

	if ( soundHit )
	{
		SmartPointer < RompSound > rs = RompSound::getProvider();
		if (rs)
		{
			if (!soundTag_.empty())
			{
				rs->playParticleSound( soundTag_.c_str(), soundPos, maxVelocity, soundSrcIdx_, materialKind );
			}
		}
	}
}