//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: particle system code
//
//===========================================================================//

#include "tier0/platform.h"
#include "particles/particles.h"
#include "filesystem.h"
#include "tier2/tier2.h"
#include "tier2/fileutils.h"
#include "tier1/UtlStringMap.h"
#include "tier1/strtools.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

extern int g_nParticle_Multiplier;

//-----------------------------------------------------------------------------
// Emits particles immediately
//-----------------------------------------------------------------------------
struct InstantaneousEmitterContext_t
{
	int m_nRemainingParticles;
	int m_ActualParticlesToEmit;
	float m_flTimeOffset;
	bool m_bReadScaleFactor;
};

class C_OP_InstantaneousEmitter : public CParticleOperatorInstance
{
	DECLARE_PARTICLE_OPERATOR( C_OP_InstantaneousEmitter );

	uint32 GetWrittenAttributes( void ) const
	{
		return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
	}

	uint32 GetReadAttributes( void ) const
	{
		return 0;
	}

	virtual uint64 GetReadControlPointMask() const 
	{
		if ( m_nScaleControlPoint >= 0 )
			return ( 1ULL << m_nScaleControlPoint );
		return 0; 
	}

	virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength, 
						 void *pContext ) const;

	// unpack structure will be applied by creator. add extra initialization needed here
	virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
	{
		if ( m_nMinParticlesToEmit >= 0 )
		{
			if ( m_nMinParticlesToEmit > m_nParticlesToEmit )
			{
				V_swap( m_nParticlesToEmit, m_nMinParticlesToEmit );
			}
		}

		if ( m_nPerFrameNum < 0 )
		{
			m_nPerFrameNum = INT_MAX;
		}
		m_nScaleControlPointField = clamp( m_nScaleControlPointField, 0, 2 );
	}

	virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
	{
		InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
		if ( !bInfiniteOnly )
		{
			pCtx->m_nRemainingParticles = 0;
		}
	}
	virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
	{
		InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
		if ( !bInfiniteOnly )
		{
			pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit;
			SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
		}
	}

	// Called when the SFM wants to skip forward in time
	virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
	{
		// NOTE: This is a bit of a hack. We're saying that if we're skipping more than two seconds, that we're
		//		 probably not going to bother emitting at all.  Really, this would have to know the maximum 
		//		 lifetime of the child particles and only skip if past that.

		InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
		float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
		if ( flTime > ( flStartTime + 2.0f ) )
		{
			pCtx->m_nRemainingParticles = 0;
		}
	}

	virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
	{
		InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
		if ( m_nMinParticlesToEmit >= 0 )
		{
			pCtx->m_ActualParticlesToEmit = pParticles->RandomInt( m_nMinParticlesToEmit, m_nParticlesToEmit );
		}
		else
		{
			pCtx->m_ActualParticlesToEmit = m_nParticlesToEmit;
		}
		pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit;
		pCtx->m_flTimeOffset = 0.0f;
		pCtx->m_bReadScaleFactor = false;
	}

	virtual void Restart( CParticleCollection *pParticles, void *pContext )
	{
		InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
		pCtx->m_nRemainingParticles = pCtx->m_ActualParticlesToEmit;
		pCtx->m_flTimeOffset = pParticles->m_flCurTime;
		pCtx->m_bReadScaleFactor = false;
	}

	size_t GetRequiredContextBytes( void ) const
	{
		return sizeof( InstantaneousEmitterContext_t );
	}

	virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
	{
		InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
		return !(pCtx->m_nRemainingParticles <= 0);
	}

	int m_nParticlesToEmit;
	int m_nMinParticlesToEmit;
	float m_flStartTime;
	int m_nPerFrameNum;
	int m_nScaleControlPoint;
	int m_nScaleControlPointField;
};

DEFINE_PARTICLE_OPERATOR( C_OP_InstantaneousEmitter, "emit_instantaneously", OPERATOR_GENERIC );

BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_InstantaneousEmitter ) 
	DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
	DMXELEMENT_UNPACK_FIELD( "num_to_emit_minimum", "-1", int, m_nMinParticlesToEmit )
	DMXELEMENT_UNPACK_FIELD( "num_to_emit", "100", int, m_nParticlesToEmit )
	DMXELEMENT_UNPACK_FIELD( "maximum emission per frame", "-1", int, m_nPerFrameNum )
	DMXELEMENT_UNPACK_FIELD( "emission count scale control point", "-1", int, m_nScaleControlPoint )
	DMXELEMENT_UNPACK_FIELD( "emission count scale control point field", "0", int, m_nScaleControlPointField )
END_PARTICLE_OPERATOR_UNPACK( C_OP_InstantaneousEmitter )


uint32 C_OP_InstantaneousEmitter::Emit( CParticleCollection *pParticles, float flCurStrength, 
										void *pContext ) const
{
	// Don't emit any more if the particle system has emitted all it's supposed to.
	InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
	if ( pCtx->m_nRemainingParticles <= 0 )
		return 0;

	// Wait until we're told to start emitting
	float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
	if ( pParticles->m_flCurTime < flStartTime )
		return 0;

	if ( pCtx->m_ActualParticlesToEmit == 0 )
		return 0;

	if ( ( m_nScaleControlPoint >= 0 ) && !pCtx->m_bReadScaleFactor )
	{
		Vector vecScale;
		if ( flStartTime <= pParticles->m_flCurTime && flStartTime >= pParticles->m_flCurTime - pParticles->m_flPreviousDt )
		{
			pParticles->GetControlPointAtTime( m_nScaleControlPoint, flStartTime, &vecScale );
		}
		else
		{
			pParticles->GetControlPointAtPrevTime( m_nScaleControlPoint, &vecScale );
		}

		pCtx->m_ActualParticlesToEmit *= vecScale[m_nScaleControlPointField];
		pCtx->m_nRemainingParticles *= vecScale[m_nScaleControlPointField];
		pCtx->m_bReadScaleFactor = true;
	}

	pCtx->m_nRemainingParticles = max( pCtx->m_nRemainingParticles, 0 );

	// NOTE: Applying the scale here because I don't believe we can sample the control point
	// values inside 
	// We're only allowed to emit so many particles, though..
	// If we run out of room, only emit the last N particles
	int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
	// Cap to the maximum emission per frame
	int nParticlesThisFrame = min( m_nPerFrameNum, pCtx->m_nRemainingParticles );
	nAllowedParticlesToEmit = min( nAllowedParticlesToEmit, nParticlesThisFrame );
	int nActualParticlesToEmit = min( nAllowedParticlesToEmit, pCtx->m_ActualParticlesToEmit * g_nParticle_Multiplier );
	pCtx->m_nRemainingParticles -= nParticlesThisFrame;
	Assert( pCtx->m_nRemainingParticles >= 0 );

	if ( nActualParticlesToEmit == 0 )
		return 0;
	
	int nStartParticle = pParticles->m_nActiveParticles;
	pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );

	// !! speed!! do sse init here
	for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
	{
		float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
		*pTimeStamp = flStartTime;
	}

	return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
}


//-----------------------------------------------------------------------------
// Emits particles over time
//-----------------------------------------------------------------------------
struct ContinuousEmitterContext_t
{
	float	m_flTotalActualParticlesSoFar;
	int		m_nTotalEmittedSoFar;
	float	m_flNextEmitTime;
	float	m_flTimeOffset;
	bool	m_bStoppedEmission;
};
	  
bool g_bDontMakeSkipToTimeTakeForever = false;


class C_OP_ContinuousEmitter : public CParticleOperatorInstance
{
	DECLARE_PARTICLE_OPERATOR( C_OP_ContinuousEmitter );

	uint32 GetWrittenAttributes( void ) const
	{
		return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
	}

	uint32 GetReadAttributes( void ) const
	{
		return 0;
	}

	virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
	{
		if ( m_flEmitRate < 0.0f )
		{
			m_flEmitRate = 0.0f;
		}
		if ( m_flEmissionDuration < 0.0f )
		{
			m_flEmissionDuration = 0.0f;
		}
		m_flEmitRate *= g_nParticle_Multiplier;
	}

	virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
						 void *pContext ) const ;

	inline bool IsInfinitelyEmitting() const
	{
		return ( m_flEmissionDuration == 0.0f );
	}

	virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
	{
		ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
		if ( !bInfiniteOnly || IsInfinitelyEmitting() )
		{
			pCtx->m_bStoppedEmission = true;
		}
	}
	virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
	{
		ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
		if ( !bInfiniteOnly || IsInfinitelyEmitting() )
		{
			pCtx->m_bStoppedEmission = false;
			SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
		}
	}

	virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
	{
		ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
		pCtx->m_flNextEmitTime = m_flStartTime;
		pCtx->m_flTotalActualParticlesSoFar = 0.0f;
		pCtx->m_nTotalEmittedSoFar = 0;
		pCtx->m_flTimeOffset = 0.0f;
		pCtx->m_bStoppedEmission = false;
	}

	virtual void Restart( CParticleCollection *pParticles, void *pContext )
	{
		if ( !IsInfinitelyEmitting() )
		{
			ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
			pCtx->m_flNextEmitTime = pParticles->m_flCurTime + m_flStartTime;
			pCtx->m_flTotalActualParticlesSoFar = 0.0f;
			pCtx->m_nTotalEmittedSoFar = 0;
			pCtx->m_flTimeOffset = pParticles->m_flCurTime;
		}
	}

	// Called when the SFM wants to skip forward in time
	// Currently hacked for save/load pre-sim - correct solution is to serialize rather 
	// than skip-to-time and simulate
	virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
	{
		ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
		float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
		if ( flTime <= flStartTime )
			return;
	
		float flControlPointScale = pParticles->GetHighestControlPoint();
		flControlPointScale *= m_flEmissionScale;
		float flEmissionRate = m_flEmitRate;
	
		float flEmitStrength;
		if ( pParticles->CheckIfOperatorShouldRun( this, &flEmitStrength ) )
		{
			flEmissionRate *= flEmitStrength;
		}

		if ( flControlPointScale != 0.0f )
		{
			flEmissionRate *= flControlPointScale;
		}

		float flPrevDrawTime = pParticles->m_flCurTime - flTime;
		float flCurrDrawTime = pParticles->m_flCurTime;

		if ( !IsInfinitelyEmitting() )
		{
			if ( flPrevDrawTime < flStartTime )
			{
				flPrevDrawTime = flStartTime;
			}
			//if ( flCurrDrawTime > flStartTime + m_flEmissionDuration )
			//{
			//	flCurrDrawTime = flStartTime + m_flEmissionDuration;
			//}
		}
		float flDeltaTime = flCurrDrawTime - flPrevDrawTime;
		flDeltaTime = min( flDeltaTime, 4.f );
		flPrevDrawTime = flCurrDrawTime - flDeltaTime;
		//disabled for now
		pCtx->m_flTotalActualParticlesSoFar = flDeltaTime * flEmissionRate;
		

		//if ( !IsInfinitelyEmitting() )
		//	pCtx->m_flTotalActualParticlesSoFar = min( pCtx->m_ActualParticlesToEmit, pCtx->m_flTotalActualParticlesSoFar );
		pCtx->m_nTotalEmittedSoFar = 0;
		//simulate a bunch
		int nActualParticlesToEmit = floor (pCtx->m_flTotalActualParticlesSoFar);
		int nStartParticle = pParticles->m_nActiveParticles;

		if ( pParticles->m_nMaxAllowedParticles < nStartParticle + nActualParticlesToEmit )
		{
			nActualParticlesToEmit = pParticles->m_nMaxAllowedParticles - nStartParticle;
		}

		pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
		
		float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit );
		float flTimeStep = flPrevDrawTime + flTimeStampStep;

		// Set the particle creation time to the exact sub-frame particle emission time
		// !! speed!! do sse init here
		for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
		{
			float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
			flTimeStep = min( flTimeStep, flCurrDrawTime );
			*pTimeStamp = flTimeStep;
			flTimeStep += flTimeStampStep;
		}

		if ( !g_bDontMakeSkipToTimeTakeForever )
		{
			flPrevDrawTime = max( flPrevDrawTime, flCurrDrawTime - pParticles->m_pDef->m_flNoDrawTimeToGoToSleep );
			pParticles->m_flCurTime = flPrevDrawTime;
			pParticles->m_fl4CurTime = ReplicateX4( flPrevDrawTime );
			for( float i = flPrevDrawTime; i < flCurrDrawTime; i += 0.1 )
			{
				pParticles->Simulate( .1, false );
			}
		}
	}

	size_t GetRequiredContextBytes( void ) const
	{
		return sizeof( ContinuousEmitterContext_t );
	}

	virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
	{
		ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
		if ( pCtx->m_bStoppedEmission )
			return false;

		if ( m_flEmitRate <= 0.0f )
			return false;

		float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
		if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( flStartTime + m_flEmissionDuration ) )
			return false;

		return true;
	}

	float m_flEmissionDuration;
	float m_flStartTime;
	float m_flEmitRate;
	float m_flTimePerEmission;
	float m_flEmissionScale;
	bool  m_bScalePerParticle;
};

DEFINE_PARTICLE_OPERATOR( C_OP_ContinuousEmitter, "emit_continuously", OPERATOR_GENERIC );

BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ContinuousEmitter ) 
	DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
	DMXELEMENT_UNPACK_FIELD( "emission_rate", "100", float, m_flEmitRate )
	DMXELEMENT_UNPACK_FIELD( "emission_duration", "0", float, m_flEmissionDuration )
	DMXELEMENT_UNPACK_FIELD( "scale emission to used control points", "0.0", float, m_flEmissionScale )
	DMXELEMENT_UNPACK_FIELD( "use parent particles for emission scaling", "0", bool, m_bScalePerParticle )
END_PARTICLE_OPERATOR_UNPACK( C_OP_ContinuousEmitter )

uint32 C_OP_ContinuousEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
									 void *pContext ) const
{
	// Have we emitted all the particles we're going to emit?
	// NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
	ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );

	//Allows for dynamic scaling via changes in number of control points.
	float flControlPointScale = pParticles->GetHighestControlPoint();
	//The emission scale here allows for a scalar value per controlpoint, like 2 or .25...
	flControlPointScale *= m_flEmissionScale;
	//Global strength scale brought in by operator fade in/fade out/oscillate 
	float flEmissionRate = m_flEmitRate * flCurStrength;
	if ( flControlPointScale != 0.0f || m_bScalePerParticle )
	{
		if ( m_bScalePerParticle )
		{ 
			if ( pParticles->m_pParent )
			{
				flControlPointScale = pParticles->m_pParent->m_nActiveParticles * m_flEmissionScale;
			}
			else
			{
				flControlPointScale = m_flEmissionScale;
			}

		}
		flEmissionRate *= flControlPointScale;
	}

	if ( flEmissionRate == 0.0f )
		return 0;

	if ( !C_OP_ContinuousEmitter::MayCreateMoreParticles( pParticles, pContext ) )
		return 0;

	float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
	if ( pParticles->m_flCurTime < flStartTime )
		return 0;

	Assert( flEmissionRate != 0.0f );

	// determine our previous and current draw times and clamp them to start time and emission duration
	float flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt;
	float flCurrDrawTime = pParticles->m_flCurTime;

	if ( !IsInfinitelyEmitting() )
	{
		if ( flPrevDrawTime < flStartTime )
		{
			flPrevDrawTime = flStartTime;
		}
		if ( flCurrDrawTime > flStartTime + m_flEmissionDuration )
		{
			flCurrDrawTime = flStartTime + m_flEmissionDuration;
		}
	}
	
	float flDeltaTime = flCurrDrawTime - flPrevDrawTime;

	//Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float
	float flActualParticlesToEmit = flEmissionRate  * flDeltaTime;

	//Add emitted particle to float counter to allow for fractional emission
	pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit;

	//Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame
	int nParticlesToEmit = 	floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar;

	//Add emitted particles to running int total.
	pCtx->m_nTotalEmittedSoFar += nParticlesToEmit;

 
	if ( nParticlesToEmit == 0 )
		return 0;

	// We're only allowed to emit so many particles, though..
	// If we run out of room, only emit the last N particles
	int nActualParticlesToEmit = nParticlesToEmit;
	int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
	if ( nAllowedParticlesToEmit < nParticlesToEmit )
	{
		nActualParticlesToEmit = nAllowedParticlesToEmit;
		//flStartEmissionTime = pCtx->m_flNextEmitTime - flTimePerEmission * nActualParticlesToEmit;
	}
	if ( nActualParticlesToEmit == 0 )
		return 0;

	int nStartParticle = pParticles->m_nActiveParticles;
	pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );


	float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit );
	float flTimeStep = flPrevDrawTime + flTimeStampStep;
	
	// Set the particle creation time to the exact sub-frame particle emission time
	// !! speed!! do sse init here
	for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
	{
		float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
		flTimeStep = min( flTimeStep, flCurrDrawTime );
		*pTimeStamp = flTimeStep;
		flTimeStep += flTimeStampStep;
	}

	return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
}


//-----------------------------------------------------------------------------
// Noise Emitter
//-----------------------------------------------------------------------------
struct NoiseEmitterContext_t
{
	float	m_flTotalActualParticlesSoFar;
	int		m_nTotalEmittedSoFar;
	float	m_flNextEmitTime;
	float	m_flTimeOffset;
	bool	m_bStoppedEmission;
};

class C_OP_NoiseEmitter : public CParticleOperatorInstance
{
	DECLARE_PARTICLE_OPERATOR( C_OP_NoiseEmitter );

	uint32 GetWrittenAttributes( void ) const
	{
		return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
	}

	uint32 GetReadAttributes( void ) const
	{
		return 0;
	}

	virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
	{
		if ( m_flEmitRate < 0.0f )
		{
			m_flEmitRate = 0.0f;
		}
		if ( m_flEmissionDuration < 0.0f )
		{
			m_flEmissionDuration = 0.0f;
		}
		m_flEmitRate *= g_nParticle_Multiplier;
	}

	virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
		void *pContext ) const ;

	inline bool IsInfinitelyEmitting() const
	{
		return ( m_flEmissionDuration == 0.0f );
	}

	virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
	{
		NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
		if ( !bInfiniteOnly || IsInfinitelyEmitting() )
		{
			pCtx->m_bStoppedEmission = true;
		}
	}
	virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
	{
		NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
		if ( !bInfiniteOnly || IsInfinitelyEmitting() )
		{
			pCtx->m_bStoppedEmission = false;
			SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
		}
	}
	 
	virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
	{
		NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
		pCtx->m_flNextEmitTime = m_flStartTime;
		pCtx->m_flTotalActualParticlesSoFar = 1.0f;
		pCtx->m_nTotalEmittedSoFar = 0;
		pCtx->m_flTimeOffset = 0.0f;
		pCtx->m_bStoppedEmission = false;
	}

	virtual void Restart( CParticleCollection *pParticles, void *pContext )
	{
		if ( !IsInfinitelyEmitting() )
		{
			NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
			pCtx->m_flNextEmitTime = m_flStartTime + pParticles->m_flCurTime;
			pCtx->m_flTotalActualParticlesSoFar = 1.0f;
			pCtx->m_nTotalEmittedSoFar = 0;
			pCtx->m_flTimeOffset = pParticles->m_flCurTime;
		}
	}

	// Called when the SFM wants to skip forward in time
	virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
	{
		NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
		float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
		if ( flTime <= flStartTime )
			return;

		float flControlPointScale = pParticles->GetHighestControlPoint();
		flControlPointScale *= m_flEmissionScale;
		float flEmissionRate = m_flEmitRate;

		float flEmitStrength;
		if ( pParticles->CheckIfOperatorShouldRun( this, &flEmitStrength ) )
		{
			flEmissionRate *= flEmitStrength;
		}

		if ( flControlPointScale != 0.0f )
		{
			flEmissionRate *= flControlPointScale;
		}
		pCtx->m_flTotalActualParticlesSoFar = ( pParticles->m_flCurTime - flStartTime ) * flEmissionRate + 1;

		//if ( !IsInfinitelyEmitting() )
		//	pCtx->m_flTotalActualParticlesSoFar = min( pCtx->m_ActualParticlesToEmit, pCtx->m_flTotalActualParticlesSoFar );
		pCtx->m_nTotalEmittedSoFar = 0;

	}

	size_t GetRequiredContextBytes( void ) const
	{
		return sizeof( NoiseEmitterContext_t );
	}

	virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
	{
		NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
		if ( pCtx->m_bStoppedEmission )
			return false;

		if ( m_flEmitRate <= 0.0f )
			return false;

		float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
		if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( flStartTime + m_flEmissionDuration ) )
			return false;

		return true;
	}

	float	m_flEmissionDuration;
	float	m_flStartTime;
	float	m_flEmitRate;
	float	m_flTimePerEmission;
	float	m_flEmissionScale;
	bool	m_bAbsVal, m_bAbsValInv;
	float	m_flOffset;
	float	m_flOutputMin;
	float	m_flOutputMax;
	float	m_flNoiseScale, m_flNoiseScaleLoc;
	Vector  m_vecOffsetLoc;
	float   m_flWorldTimeScale;
};


DEFINE_PARTICLE_OPERATOR( C_OP_NoiseEmitter, "emit noise", OPERATOR_GENERIC );

BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_NoiseEmitter ) 
	DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
	DMXELEMENT_UNPACK_FIELD( "emission_duration", "0", float, m_flEmissionDuration )
	DMXELEMENT_UNPACK_FIELD( "scale emission to used control points", "0.0", float, m_flEmissionScale )
	DMXELEMENT_UNPACK_FIELD( "time noise coordinate scale","0.1",float,m_flNoiseScale)
	//DMXELEMENT_UNPACK_FIELD( "spatial noise coordinate scale","0.001",float,m_flNoiseScaleLoc)
	DMXELEMENT_UNPACK_FIELD( "time coordinate offset","0", float, m_flOffset )
	//DMXELEMENT_UNPACK_FIELD( "spatial coordinate offset","0 0 0", Vector, m_vecOffsetLoc )
	DMXELEMENT_UNPACK_FIELD( "absolute value","0", bool, m_bAbsVal )
	DMXELEMENT_UNPACK_FIELD( "invert absolute value","0", bool, m_bAbsValInv )
	DMXELEMENT_UNPACK_FIELD( "emission minimum","0", float, m_flOutputMin )
	DMXELEMENT_UNPACK_FIELD( "emission maximum","100", float, m_flOutputMax )
	DMXELEMENT_UNPACK_FIELD( "world time noise coordinate scale","0", float, m_flWorldTimeScale )
END_PARTICLE_OPERATOR_UNPACK( C_OP_NoiseEmitter )

uint32 C_OP_NoiseEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
									void *pContext ) const
{
	// Have we emitted all the particles we're going to emit?
	// NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
	NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );

	//Allows for dynamic scaling via changes in number of control points.
	float flControlPointScale = pParticles->GetHighestControlPoint();
	//The emission scale here allows for a scalar value per controlpoint, like 2 or .25...
	flControlPointScale *= m_flEmissionScale;

	float	flAbsScale;
	int		nAbsVal;
	nAbsVal = 0xffffffff; 
	flAbsScale = 0.5;
	if ( m_bAbsVal )
	{
		nAbsVal = 0x7fffffff;
		flAbsScale = 1.0;
	}

	float fMin = m_flOutputMin;
	float fMax = m_flOutputMax;


	float CoordScale = m_flNoiseScale;
	//float CoordScaleLoc = m_flNoiseScaleLoc;


	float ValueScale, ValueBase;

	Vector Coord, CoordLoc, CoordWorldTime;
	//CoordLoc.x = pxyz[0]; 
	//CoordLoc.y = pxyz[4];
	//CoordLoc.z = pxyz[8];
	//CoordLoc += m_vecOffsetLoc;

	float Offset = m_flOffset;
	Coord = Vector ( (pParticles->m_flCurTime + Offset), (pParticles->m_flCurTime + Offset), (pParticles->m_flCurTime + Offset) );
	Coord *= CoordScale;
	//CoordLoc *= CoordScaleLoc;
	//Coord += CoordLoc;

	CoordWorldTime = Vector( (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale) );
	Coord += CoordWorldTime;

	fltx4 flNoise128;
	FourVectors fvNoise;

	fvNoise.DuplicateVector( Coord );
	flNoise128 = NoiseSIMD( fvNoise );
	float flNoise = SubFloat( flNoise128, 0 );

	*( (int *) &flNoise)  &= nAbsVal;

	ValueScale = ( flAbsScale *( fMax - fMin ) );
	ValueBase = ( fMin+ ( ( 1.0 - flAbsScale ) *( fMax - fMin ) ) );

	if ( m_bAbsValInv )
	{
		flNoise = 1.0 - flNoise;
	}

	float flInitialNoise = ( ValueBase + ( ValueScale * flNoise ) );
	flInitialNoise = clamp(flInitialNoise, 0.0f, (float) INT_MAX );

	//Global strength scale brought in by operator fade in/fade out/oscillate 
	float flEmissionRate = flInitialNoise * flCurStrength;
	if ( flControlPointScale != 0.0f )
	{
		flEmissionRate *= flControlPointScale;
	}

	if ( flEmissionRate == 0.0f )
		return 0;

	if ( !C_OP_NoiseEmitter::MayCreateMoreParticles( pParticles, pContext ) )
		return 0;

	float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
	if ( pParticles->m_flCurTime < flStartTime )
		return 0;

	Assert( flEmissionRate != 0.0f );

	// determine our previous and current draw times and clamp them to start time and emission duration
	float flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt;
	float flCurrDrawTime = pParticles->m_flCurTime;

	if ( !IsInfinitelyEmitting() )
	{
		if ( flPrevDrawTime < flStartTime )
		{
			flPrevDrawTime = flStartTime;
		}
		if ( flCurrDrawTime > flStartTime + m_flEmissionDuration )
		{
			flCurrDrawTime = flStartTime + m_flEmissionDuration;
		}
	}

	float flDeltaTime = flCurrDrawTime - flPrevDrawTime;

	//Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float
	float flActualParticlesToEmit = flEmissionRate  * flDeltaTime;

	//Add emitted particle to float counter to allow for fractional emission
	pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit;

	//Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame
	int nParticlesToEmit = 	floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar;

	//Add emitted particles to running int total.
	pCtx->m_nTotalEmittedSoFar += nParticlesToEmit;


	if ( nParticlesToEmit == 0 )
		return 0;

	// We're only allowed to emit so many particles, though..
	// If we run out of room, only emit the last N particles
	int nActualParticlesToEmit = nParticlesToEmit;
	int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
	if ( nAllowedParticlesToEmit < nParticlesToEmit )
	{
		nActualParticlesToEmit = nAllowedParticlesToEmit;
		//flStartEmissionTime = pCtx->m_flNextEmitTime - flTimePerEmission * nActualParticlesToEmit;
	}
	if ( nActualParticlesToEmit == 0 )
		return 0;

	int nStartParticle = pParticles->m_nActiveParticles;
	pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );

	float flTimeStampStep = ( flCurrDrawTime - flPrevDrawTime ) / ( nActualParticlesToEmit );
	float flTimeStep = flPrevDrawTime + flTimeStampStep;

	// Set the particle creation time to the exact sub-frame particle emission time
	// !! speed!! do sse init here
	for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
	{
		float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
		flTimeStep = min( flTimeStep, flCurrDrawTime );
		*pTimeStamp = flTimeStep;
		flTimeStep += flTimeStampStep;
	}

	return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
}


void AddBuiltInParticleEmitters( void )
{
	REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_ContinuousEmitter );
	REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_InstantaneousEmitter );
	REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_NoiseEmitter );
}