//-----------------------------------------------------------------------------
// Purpose:
// Input  : *pParticleMgr -
//			*pArgs -
//-----------------------------------------------------------------------------
void C_EntityParticleTrail::Start( )
{
    if( ParticleMgr()->AddEffect( &m_ParticleEffect, this ) == false )
        return;

    const char *pMaterialName = GetMaterialNameFromIndex( m_iMaterialName );
    if ( !pMaterialName )
        return;

    m_hMaterial	= ParticleMgr()->GetPMaterial( pMaterialName );
    m_teParticleSpawn.Init( 150 );
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *materialName - 
//-----------------------------------------------------------------------------
void CLitSmokeEmitter::Init( const char *materialName, Vector sortOrigin )
{
	m_hSmokeMaterial = GetPMaterial( materialName );
	
	IMaterial *pMaterial = ParticleMgr()->PMaterialToIMaterial( m_hSmokeMaterial );
	if ( pMaterial )
	{
		m_Renderer.Init( ParticleMgr(), pMaterial );
	}

	SetSortOrigin( sortOrigin );
	m_bInitted = true;
}
//-----------------------------------------------------------------------------
// ParticleSystem iteration, query, modification
//-----------------------------------------------------------------------------
ParticleSystemSearchResult CClientTools::NextParticleSystem( ParticleSystemSearchResult sr )
{
	CNewParticleEffect *pParticleEffect = NULL;
	if ( sr == NULL )
	{
		pParticleEffect = ParticleMgr()->FirstNewEffect();
	}
	else
	{
		pParticleEffect = ParticleMgr()->NextNewEffect( reinterpret_cast< CNewParticleEffect* >( sr ) );
	}
	return reinterpret_cast< ParticleSystemSearchResult >( pParticleEffect );
}
Beispiel #4
0
void CTrailParticles::RenderParticles(CParticleRenderIterator *pIterator)
{
    const TrailParticle *pParticle = (const TrailParticle*) pIterator->GetFirst();
    while (pParticle)
    {
        //Get our remaining time
        float lifePerc = 1.0f - (pParticle->m_flLifetime / pParticle->m_flDieTime);
        float scale = (pParticle->m_flLength*lifePerc);

        if (scale < 0.01f)
            scale = 0.01f;

        Vector	start, delta;

        //NOTE: We need to do everything in screen space
        TransformParticle(ParticleMgr()->GetModelView(), pParticle->m_Pos, start);
        float sortKey = start.z;

        Vector3DMultiply(ParticleMgr()->GetModelView(), pParticle->m_vecVelocity, delta);

        float	color[4];
        float	ramp = 1.0;

        // Fade in for the first few frames
        if (pParticle->m_flLifetime <= 0.3 && m_fFlags & bitsPARTICLE_TRAIL_FADE_IN)
        {
            ramp = pParticle->m_flLifetime;
        }
        else if (m_fFlags & bitsPARTICLE_TRAIL_FADE)
        {
            ramp = (1.0f - (pParticle->m_flLifetime / pParticle->m_flDieTime));
        }

        color[0] = pParticle->m_color.r * ramp * (1.0f / 255.0f);
        color[1] = pParticle->m_color.g * ramp * (1.0f / 255.0f);
        color[2] = pParticle->m_color.b * ramp * (1.0f / 255.0f);
        color[3] = pParticle->m_color.a * ramp * (1.0f / 255.0f);

        float	flLength = (pParticle->m_vecVelocity * scale).Length();//( delta - pos ).Length();
        float	flWidth = (flLength < pParticle->m_flWidth) ? flLength : pParticle->m_flWidth;

        //See if we should fade
        Vector vecScaledDelta = (delta*scale);
        Tracer_Draw(pIterator->GetParticleDraw(), start, vecScaledDelta, flWidth, color);

        pParticle = (const TrailParticle*) pIterator->GetNext(sortKey);
    }
}
void CLitSmokeEmitter::RenderParticles( CParticleRenderIterator *pIterator )
{
	const LitSmokeParticle *pParticle = (const LitSmokeParticle*)pIterator->GetFirst();
	while ( pParticle )
	{
		float	tLifetime = pParticle->m_flLifetime / pParticle->m_flDieTime;

		// Transform.						   
		Vector tPos;

		TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos );
		float sortKey = tPos.z;
		
		float alpha255 = ( ( (float) pParticle->m_uchColor[3]/255.0f ) * sin( M_PI_F * tLifetime ) ) * 255.0f;

		Vector	color01 = Vector( pParticle->m_uchColor[0], pParticle->m_uchColor[1], pParticle->m_uchColor[2] ) * (tLifetime / 255.0f);

		m_Renderer.RenderParticle_AddColor (
			pIterator->GetParticleDraw(),
			pParticle->m_Pos,
			tPos,
			alpha255,
			FLerp( pParticle->m_uchStartSize, pParticle->m_uchEndSize, tLifetime ),
			color01
		);
		
		pParticle = (const LitSmokeParticle*)pIterator->GetNext( sortKey );
	}
}
void CNewParticleEffect::Construct()
{
	m_vSortOrigin.Init();

	m_bDontRemove = false;
	m_bRemove = false;
	m_bDrawn = false;
	m_bNeedsBBoxUpdate = false;
	m_bIsFirstFrame = true;
	m_bAutoUpdateBBox = false;
	m_bAllocated = true;
	m_bSimulate = true;
	m_bRecord = false;
	m_bShouldPerformCullCheck = false;
	m_bDisableAggregation = true;							// will be reset when someone creates it via CreateOrAggregate


	m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
	m_RefCount = 0;
	ParticleMgr()->AddEffect( this );
	m_LastMax = Vector( -1.0e6, -1.0e6, -1.0e6 );
	m_LastMin = Vector( 1.0e6, 1.0e6, 1.0e6 );
	m_MinBounds = Vector( 1.0e6, 1.0e6, 1.0e6 );
	m_MaxBounds = Vector( -1.0e6, -1.0e6, -1.0e6 );
	m_pDebugName = NULL;
	m_nSplitScreenUser = -1;

	SetRenderable( this );

	RecordCreation();
}
CNewParticleEffect *CParticleProperty::Create( CParticleSystemDefinition *pDef, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset, matrix3x4_t *matOffset )
{
	int nBatchMode = cl_particle_batch_mode.GetInt();
	bool bRequestedBatch = ( nBatchMode == 2 ) || ( ( nBatchMode == 1 ) && pDef && pDef->ShouldBatch() ); 
	if ( ( iAttachType == PATTACH_CUSTOMORIGIN ) && bRequestedBatch )
	{
		int iIndex = FindEffect( pDef->GetName() );
		if ( iIndex >= 0 )
		{
			CNewParticleEffect *pEffect = m_ParticleEffects[iIndex].pParticleEffect.GetObject();
			pEffect->Restart();
			return pEffect;
		}
	}

	int iIndex = m_ParticleEffects.AddToTail();
	ParticleEffectList_t *newEffect = &m_ParticleEffects[iIndex];
	newEffect->pParticleEffect = CNewParticleEffect::Create( m_pOuter, pDef, pDef->GetName() );

	if ( !newEffect->pParticleEffect->IsValid() )
	{
		// Caused by trying to spawn an unregistered particle effect. Remove it.
		ParticleMgr()->RemoveEffect( newEffect->pParticleEffect.GetObject() );
		return NULL;
	}

	AddControlPoint( iIndex, 0, GetOuter(), iAttachType, iAttachmentPoint, vecOriginOffset, matOffset );

	if ( m_pOuter )
	{
		m_pOuter->OnNewParticleEffect( pDef->GetName(), newEffect->pParticleEffect.GetObject() );
	}

	return newEffect->pParticleEffect.GetObject();
}
void C_FuncSmokeVolume::OnDataChanged( DataUpdateType_t updateType )
{		
	m_MinColor[0] = ( 1.0f / 255.0f ) * m_Color1.r;
	m_MinColor[1] = ( 1.0f / 255.0f ) * m_Color1.g;
	m_MinColor[2] = ( 1.0f / 255.0f ) * m_Color1.b;

	m_MaxColor[0] = ( 1.0f / 255.0f ) * m_Color2.r;
	m_MaxColor[1] = ( 1.0f / 255.0f ) * m_Color2.g;
	m_MaxColor[2] = ( 1.0f / 255.0f ) * m_Color2.b;

	m_ParticleRadius = m_ParticleDrawWidth * 0.5f;
	m_SpacingRadius = m_ParticleSpacingDistance * 0.5f;

	m_ParticleEffect.SetParticleCullRadius( m_ParticleRadius );

//	Warning( "m_Density: %f\n", m_Density );
//	Warning( "m_MovementSpeed: %f\n", m_MovementSpeed );
	
	if(updateType == DATA_UPDATE_CREATED)
	{
		Vector size = WorldAlignMaxs() - WorldAlignMins();
		m_xCount = 0.5f + ( size.x / ( m_SpacingRadius * 2.0f ) );
		m_yCount = 0.5f + ( size.y / ( m_SpacingRadius * 2.0f ) );
		m_zCount = 0.5f + ( size.z / ( m_SpacingRadius * 2.0f ) );
		m_CurrentDensity = m_Density;

		delete [] m_pSmokeParticleInfos;
		m_pSmokeParticleInfos = new SmokeParticleInfo[m_xCount * m_yCount * m_zCount];
		Start( ParticleMgr(), NULL );
	}
	BaseClass::OnDataChanged( updateType );
}
void CLocalSpaceEmitter::RenderParticles( CParticleRenderIterator *pIterator )
{
	const matrix3x4_t &mLocalToWorld = GetTransformMatrix();
	const VMatrix &mModelView = ParticleMgr()->GetModelView();

	const SimpleParticle *pParticle = (const SimpleParticle *)pIterator->GetFirst();
	while ( pParticle )
	{
		// Transform it
		Vector screenPos, worldPos;
		VectorTransform( pParticle->m_Pos, mLocalToWorld, worldPos );
		
		// Correct viewmodel squashing
		if ( m_fFlags & FLE_VIEWMODEL )
		{
			FormatViewModelAttachment( NULL, worldPos, false );
		}

		TransformParticle( mModelView, worldPos, screenPos );
		
		float sortKey = (int) screenPos.z;

		// Render it
		RenderParticle_ColorSizeAngle(
			pIterator->GetParticleDraw(),
			screenPos,
			UpdateColor( pParticle ),
			UpdateAlpha( pParticle ) * GetAlphaDistanceFade( screenPos, m_flNearClipMin, m_flNearClipMax ),
			UpdateScale( pParticle ),
			pParticle->m_flRoll 
			);

		pParticle = (const SimpleParticle *)pIterator->GetNext( sortKey );
	}
}
//-----------------------------------------------------------------------------
// Reload particle definitions
//-----------------------------------------------------------------------------
void CClientTools::ReloadParticleDefintions( const char *pFileName, const void *pBufData, int nLen )
{
	// Remove all new effects, because we are going to free internal structures they point to
	ParticleMgr()->RemoveAllNewEffects();

	// FIXME: Use file name to determine if we care about this data
	CUtlBuffer buf( pBufData, nLen, CUtlBuffer::READ_ONLY );
	g_pParticleSystemMgr->ReadParticleConfigFile( buf, true );
}
Beispiel #11
0
//-----------------------------------------------------------------------------
// Purpose: First sent down from the server
//-----------------------------------------------------------------------------
void C_ParticleTrail::OnDataChanged(DataUpdateType_t updateType)
{
	C_BaseEntity::OnDataChanged(updateType);

	if ( updateType == DATA_UPDATE_CREATED )
	{
		Start( ParticleMgr(), NULL );
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBasePlasmaProjectile::Activate( void )
{
	BaseClass::Activate();

#if defined( CLIENT_DLL )
	if ( IsClientCreated() && !m_pParticleMgr )
	{
		Start(ParticleMgr(), NULL);
		SetNextClientThink( CLIENT_THINK_ALWAYS );
	}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Called after a data update has occured
// Input  : bnewentity - 
//-----------------------------------------------------------------------------
void C_SmokeStack::OnDataChanged(DataUpdateType_t updateType)
{
	C_BaseEntity::OnDataChanged(updateType);

	if(updateType == DATA_UPDATE_CREATED)
	{
		Start(ParticleMgr(), NULL);
	}

	// Recalulate lifetime in case length or speed changed.
	m_InvLifetime = m_Speed / m_JetLength;
}
//-----------------------------------------------------------------------------
// Creates, destroys particles attached to an attachment
//-----------------------------------------------------------------------------
void C_EnvParticleScript::CreateParticle( const char *pAttachmentName, const char *pSpriteName )
{
	// Find the attachment
	int nAttachment = LookupAttachment( pAttachmentName );
	if ( nAttachment <= 0 )
		return;

	// Get the sprite materials
	PMaterialHandle hMat = m_ParticleEffect.FindOrAddMaterial( pSpriteName );
	ParticleScriptParticle_t *pParticle = 
		(ParticleScriptParticle_t*)m_ParticleEffect.AddParticle(sizeof(ParticleScriptParticle_t), hMat);

	if ( pParticle == NULL )
		return;
	
	// Get the sprite size from the material's materialvars
	bool bFound = false;
	IMaterialVar *pMaterialVar = NULL;
	IMaterial *pMaterial = ParticleMgr()->PMaterialToIMaterial( hMat );
	if ( pMaterial )
	{
		pMaterialVar = pMaterial->FindVar( "$spritesize", &bFound, false );
	}

	if ( bFound )
	{
		pParticle->m_flSize = pMaterialVar->GetFloatValue();
	}
	else
	{
		pParticle->m_flSize = 100.0f;
	}

	// Make sure the particle cull size reflects our particles
	if ( pParticle->m_flSize > m_flMaxParticleSize )
	{
		m_flMaxParticleSize = pParticle->m_flSize;
		m_ParticleEffect.SetParticleCullRadius( m_flMaxParticleSize );
	}

	// Place the particle on the attachment specified
	pParticle->m_nAttachment = nAttachment;
	QAngle vecAngles;
	GetAttachment( nAttachment, pParticle->m_Pos, vecAngles );

	if ( m_flSequenceScale != 1.0f )
	{
		pParticle->m_Pos -= GetAbsOrigin();
		pParticle->m_Pos *= m_flSequenceScale;
		pParticle->m_Pos += GetAbsOrigin();
	}
}
Beispiel #15
0
//-----------------------------------------------------------------------------
// Purpose: Called after a data update has occured
// Input  : bnewentity - 
//-----------------------------------------------------------------------------
void C_SteamJet::OnDataChanged(DataUpdateType_t updateType)
{
	C_BaseEntity::OnDataChanged(updateType);

	if(updateType == DATA_UPDATE_CREATED)
	{
		Start(ParticleMgr(), NULL);
	}

	// Recalulate lifetime in case length or speed changed.
	m_Lifetime = m_JetLength / m_Speed;
	m_ParticleEffect.SetParticleCullRadius( max(m_StartSize, m_EndSize) );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBasePlasmaProjectile::OnDataChanged(DataUpdateType_t updateType)
{
	BaseClass::OnDataChanged(updateType);

	if ( updateType != DATA_UPDATE_CREATED )
		return;

	if ( !m_pParticleMgr )
	{
		Start(ParticleMgr(), NULL);
		SetNextClientThink( CLIENT_THINK_ALWAYS );
	}
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CParticleEffect::CParticleEffect( const char *pName )
{
	m_pDebugName = pName;
	m_vSortOrigin.Init();
	m_Flags = FLAG_ALLOCATED;
	m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
	m_RefCount = 0;
	m_bSimulate = true;
	ParticleMgr()->AddEffect( &m_ParticleEffect, this );
#if defined( _DEBUG )
	g_ParticleEffects.AddToTail( this );
#endif
}
void C_FuncSmokeVolume::RenderParticles( CParticleRenderIterator *pIterator )
{
	if ( m_CurrentDensity == 0 )
		return;

	const SmokeGrenadeParticle *pParticle = (const SmokeGrenadeParticle*)pIterator->GetFirst();
	while ( pParticle )
	{
		Vector renderPos = pParticle->m_Pos;

		// Fade out globally.
		float alpha = m_CurrentDensity;

		// Apply the precalculated fade alpha from world geometry.
		alpha *= pParticle->m_FadeAlpha;
		
		// TODO: optimize this whole routine!
		Vector color = m_MinColor + (m_MaxColor - m_MinColor) * (pParticle->m_ColorInterp / 255.1f);
		if ( IsEmissive() )
		{
			color.x += pParticle->m_Color[0] / 255.0f;
			color.y += pParticle->m_Color[1] / 255.0f;
			color.z += pParticle->m_Color[2] / 255.0f;

			color.x = clamp( color.x, 0.0f, 1.0f );
			color.y = clamp( color.y, 0.0f, 1.0f );
			color.z = clamp( color.z, 0.0f, 1.0f );
		}
		else
		{
			color.x *= pParticle->m_Color[0] / 255.0f;
			color.y *= pParticle->m_Color[1] / 255.0f;
			color.z *= pParticle->m_Color[2] / 255.0f;
		}
		
		Vector tRenderPos;
		TransformParticle( ParticleMgr()->GetModelView(), renderPos, tRenderPos );
		float sortKey = 1;//tRenderPos.z;

		RenderParticle_ColorSizeAngle(
			pIterator->GetParticleDraw(),
			tRenderPos,
			color,
			alpha * GetAlphaDistanceFade(tRenderPos, 10, 30),	// Alpha
			m_ParticleRadius,
			pParticle->m_CurRotation
			);

		pParticle = (const SmokeGrenadeParticle*)pIterator->GetNext( sortKey );
	}
}
//-----------------------------------------------------------------------------
// Simulate the particles
//-----------------------------------------------------------------------------
void C_EnvParticleScript::RenderParticles( CParticleRenderIterator *pIterator )
{
	const ParticleScriptParticle_t* pParticle = (const ParticleScriptParticle_t*)pIterator->GetFirst();
	while ( pParticle )
	{
		Vector vecRenderPos;
		TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, vecRenderPos );
		float sortKey = vecRenderPos.z;

		Vector color( 1, 1, 1 );
		RenderParticle_ColorSize( pIterator->GetParticleDraw(), vecRenderPos, color, 1.0f, pParticle->m_flSize );
		
		pParticle = (const ParticleScriptParticle_t*)pIterator->GetNext( sortKey );
	}
}
//-----------------------------------------------------------------------------
// Starts up the particle system
//-----------------------------------------------------------------------------
void C_EnvParticleScript::OnDataChanged( DataUpdateType_t updateType )
{		
	BaseClass::OnDataChanged( updateType );

	if( updateType == DATA_UPDATE_CREATED )
	{
		ParticleMgr()->AddEffect( &m_ParticleEffect, this );
		g_pClientLeafSystem->EnableRendering( RenderHandle(), false );
	}

	if ( m_nOldSequence != GetSequence() )
	{
		DestroyAllParticles();
	}
}
CNewParticleEffect *CParticleProperty::Create( const char *pszParticleName, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset )
{
	if ( GameRules() )
	{
		pszParticleName = GameRules()->TranslateEffectForVisionFilter( "particles", pszParticleName );
	}

	int nBatchMode = cl_particle_batch_mode.GetInt();
	CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pszParticleName );
	bool bRequestedBatch = ( nBatchMode == 2 ) || ( ( nBatchMode == 1 ) && pDef && pDef->ShouldBatch() ); 
	if ( ( iAttachType == PATTACH_CUSTOMORIGIN ) && bRequestedBatch )
	{
		int iIndex = FindEffect( pszParticleName );
		if ( iIndex >= 0 )
		{
			CNewParticleEffect *pEffect = m_ParticleEffects[iIndex].pParticleEffect.GetObject();
			pEffect->Restart();
			return pEffect;
		}
	}

	if ( !pDef )
	{
		AssertMsg( 0, "Attempting to create unknown particle system" );
		Warning( "Attempting to create unknown particle system '%s' \n", pszParticleName );
		return NULL;
	}

	int iIndex = m_ParticleEffects.AddToTail();
	ParticleEffectList_t *newEffect = &m_ParticleEffects[iIndex];
	newEffect->pParticleEffect = CNewParticleEffect::Create( m_pOuter, pDef );

	if ( !newEffect->pParticleEffect->IsValid() )
	{
		// Caused by trying to spawn an unregistered particle effect. Remove it.
		ParticleMgr()->RemoveEffect( newEffect->pParticleEffect.GetObject() );
		return NULL;
	}

	AddControlPoint( iIndex, 0, GetOuter(), iAttachType, iAttachmentPoint, vecOriginOffset );

	if ( m_pOuter )
	{
		m_pOuter->OnNewParticleEffect( pszParticleName, newEffect->pParticleEffect.GetObject() );
	}
	
	return newEffect->pParticleEffect.GetObject();
}
Beispiel #22
0
void CDustEffect::RenderParticles( CParticleRenderIterator *pIterator )
{
	const CFuncDustParticle *pParticle = (const CFuncDustParticle*)pIterator->GetFirst();
	while ( pParticle )
	{
		// Velocity.
		float flAlpha;
		if( m_pDust->m_DustFlags & DUSTFLAGS_FROZEN )
		{
			flAlpha = 1;
		}
		else
		{
			// Alpha.
			float flAngle = (pParticle->m_flLifetime / pParticle->m_flDieTime) * M_PI * 2;
			flAlpha = sin( flAngle - (M_PI * 0.5f) ) * 0.5f + 0.5f;
		}

		Vector tPos;
		TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos );
		float sortKey = (int) tPos.z;

		if( -tPos.z <= m_pDust->m_DistMax )
		{
			flAlpha *= 1 + (tPos.z / m_pDust->m_DistMax);

			// Draw it.
			float flSize = pParticle->m_flSize;
			if( m_pDust->m_DustFlags & DUSTFLAGS_SCALEMOTES )
				flSize *= -tPos.z;

			RenderParticle_Color255Size(
				pIterator->GetParticleDraw(),
				tPos,
				Vector( m_pDust->m_Color.r, m_pDust->m_Color.g, m_pDust->m_Color.b ),
				flAlpha * m_pDust->m_Color.a,
				flSize
				);
		}

		pParticle = (const CFuncDustParticle*)pIterator->GetNext( sortKey );
	}
}
void CTEParticleRenderer::RenderParticles( CParticleRenderIterator *pIterator )
{
	const StandardParticle_t *pParticle = (const StandardParticle_t*)pIterator->GetFirst();
	while ( pParticle )
	{
		// Render.
		Vector tPos;
		TransformParticle(ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos);
		float sortKey = tPos.z;

		Vector vColor(pParticle->m_Color[0]/255.9f, pParticle->m_Color[1]/255.9f, pParticle->m_Color[2]/255.9f);
		RenderParticle_ColorSize(
			pIterator->GetParticleDraw(),
			tPos,
			vColor,
			pParticle->m_Color[3]/255.9f,
			m_ParticleSize);

		pParticle = (const StandardParticle_t*)pIterator->GetNext( sortKey );
	}
}
inline void C_EntityParticleTrail::RenderParticles( CParticleRenderIterator *pIterator )
{
    const SimpleParticle *pParticle = (const SimpleParticle*)pIterator->GetFirst();
    while ( pParticle )
    {
        float t = pParticle->m_flLifetime / pParticle->m_flDieTime;

        // Render
        Vector	tPos;
        TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos );
        float sortKey = tPos.z;

        Vector	color = Vector( pParticle->m_uchColor[0] / 255.0f, pParticle->m_uchColor[1] / 255.0f, pParticle->m_uchColor[2] / 255.0f );
        float alpha = Lerp( t, pParticle->m_uchStartAlpha / 255.0f, pParticle->m_uchEndAlpha / 255.0f );
        float flSize = Lerp( t, pParticle->m_uchStartSize, pParticle->m_uchEndSize );

        // Render it
        RenderParticle_ColorSize( pIterator->GetParticleDraw(), tPos, color, alpha, flSize );

        pParticle = (const SimpleParticle*)pIterator->GetNext( sortKey );
    }
}
void CNewParticleEffect::Construct()
{
	m_vSortOrigin.Init();

	m_bDontRemove = false;
	m_bRemove = false;
	m_bDrawn = false;
	m_bNeedsBBoxUpdate = false;
	m_bIsFirstFrame = true;
	m_bAutoUpdateBBox = false;
	m_bAllocated = true;
	m_bSimulate = true;
	m_bShouldPerformCullCheck = false;

	m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
	m_RefCount = 0;
	ParticleMgr()->AddEffect( this );
	m_LastMax = Vector( -1.0e6, -1.0e6, -1.0e6 );
	m_LastMin = Vector( 1.0e6, 1.0e6, 1.0e6 );
	m_MinBounds = Vector( 1.0e6, 1.0e6, 1.0e6 );
	m_MaxBounds = Vector( -1.0e6, -1.0e6, -1.0e6 );
	m_pDebugName = NULL;

	if ( IsValid() && clienttools->IsInRecordingMode() )
	{
		int nId = AllocateToolParticleEffectId();	

		static ParticleSystemCreatedState_t state;
		state.m_nParticleSystemId = nId;
		state.m_flTime = gpGlobals->curtime;
		state.m_pName = GetName();
		state.m_nOwner = m_hOwner.Get() ? m_hOwner->entindex() : -1;

		KeyValues *msg = new KeyValues( "ParticleSystem_Create" );
		msg->SetPtr( "state", &state );
		ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
	}
}
void CSimpleEmitter::RenderParticles( CParticleRenderIterator *pIterator )
{
	const SimpleParticle *pParticle = (const SimpleParticle *)pIterator->GetFirst();
	while ( pParticle )
	{
		//Render
		Vector	tPos;

		TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos );
		float sortKey = (int) tPos.z;

		//Render it
		RenderParticle_ColorSizeAngle(
			pIterator->GetParticleDraw(),
			tPos,
			UpdateColor( pParticle ),
			UpdateAlpha( pParticle ) * GetAlphaDistanceFade( tPos, m_flNearClipMin, m_flNearClipMax ),
			UpdateScale( pParticle ),
			pParticle->m_flRoll
			);

		pParticle = (const SimpleParticle *)pIterator->GetNext( sortKey );
	}
}
Beispiel #27
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flags - 
// Output : int
//-----------------------------------------------------------------------------
int C_EntityDissolve::DrawModel( int flags )
{
	// See if we should draw
	if ( gpGlobals->frametime == 0 || m_bReadyToDraw == false )
		return 0;

	C_BaseAnimating *pAnimating = GetMoveParent() ? GetMoveParent()->GetBaseAnimating() : NULL;
	if ( pAnimating == NULL )
		return 0;

	matrix3x4_t	*hitboxbones[MAXSTUDIOBONES];
	if ( pAnimating->HitboxToWorldTransforms( hitboxbones ) == false )
		return 0;

	studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pAnimating->GetModel() );
	if ( pStudioHdr == NULL )
		return false;

	mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnimating->GetHitboxSet() );
	if ( set == NULL )
		return false;

	// Make sure the emitter is setup properly
	SetupEmitter();
	
	// Get fade percentages for the effect
	float fadeInPerc = GetFadeInPercentage();
	float fadeOutPerc = GetFadeOutPercentage();

	float fadePerc = ( fadeInPerc >= 1.0f ) ? fadeOutPerc : fadeInPerc;

	Vector vecSkew = vec3_origin;

	// Do extra effects under certain circumstances
	if ( ( fadePerc < 0.99f ) && ( (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL) || (m_nDissolveType == ENTITY_DISSOLVE_ELECTRICAL_LIGHT) ) )
	{
		DoSparks( set, hitboxbones );
	}

	// Skew the particles in front or in back of their targets
	vecSkew = CurrentViewForward() * ( 8.0f - ( ( 1.0f - fadePerc ) * 32.0f ) );

	float spriteScale = ( ( gpGlobals->curtime - m_flStartTime ) / m_flFadeOutLength );
	spriteScale = clamp( spriteScale, 0.75f, 1.0f );

	// Cache off this material reference
	if ( g_Material_Spark == NULL )
	{
		g_Material_Spark = ParticleMgr()->GetPMaterial( "effects/spark" );
	}

	if ( g_Material_AR2Glow == NULL )
	{
		g_Material_AR2Glow = ParticleMgr()->GetPMaterial( "effects/combinemuzzle2" );
	}

	SimpleParticle *sParticle;

	for ( int i = 0; i < set->numhitboxes; ++i )
	{
		Vector vecAbsOrigin, xvec, yvec;
		mstudiobbox_t *pBox = set->pHitbox(i);
		ComputeRenderInfo( pBox, *hitboxbones[pBox->bone], &vecAbsOrigin, &xvec, &yvec );

		Vector offset;
		Vector	xDir, yDir;

		xDir = xvec;
		float xScale = VectorNormalize( xDir ) * 0.75f;

		yDir = yvec;
		float yScale = VectorNormalize( yDir ) * 0.75f;

		int numParticles = clamp( 3.0f * fadePerc, 0.f, 3.f );

		int iTempParts = 2;

		if ( m_nDissolveType == ENTITY_DISSOLVE_CORE )
		{
			if ( m_bCoreExplode == true )
			{
				numParticles = 15;
				iTempParts = 20;
			}
		}

		for ( int j = 0; j < iTempParts; j++ )
		{
			// Skew the origin
			offset = xDir * Helper_RandomFloat( -xScale*0.5f, xScale*0.5f ) + yDir * Helper_RandomFloat( -yScale*0.5f, yScale*0.5f );
			offset += vecSkew;

			if ( random->RandomInt( 0, 2 ) != 0 )
				continue;

			sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), g_Material_Spark, vecAbsOrigin + offset );
			
			if ( sParticle == NULL )
				return 1;

			sParticle->m_vecVelocity	= Vector( Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( 16.0f, 64.0f ) );
			
			if ( m_nDissolveType == ENTITY_DISSOLVE_CORE )
			{
				if ( m_bCoreExplode == true )
				{
					Vector vDirection = (vecAbsOrigin + offset) - m_vDissolverOrigin;
					VectorNormalize( vDirection );
					sParticle->m_vecVelocity = vDirection * m_nMagnitude;
				}
			}

			if ( sParticle->m_vecVelocity.z > 0 )
			{
				sParticle->m_uchStartSize	= random->RandomFloat( 4, 6 ) * spriteScale;
			}
			else
			{
				sParticle->m_uchStartSize	= 2 * spriteScale;
			}

			sParticle->m_flDieTime = random->RandomFloat( 0.4f, 0.5f );
			
			// If we're the last particles, last longer
			if ( numParticles == 0 )
			{
				sParticle->m_flDieTime *= 2.0f;
				sParticle->m_uchStartSize = 2 * spriteScale;
				sParticle->m_flRollDelta	= Helper_RandomFloat( -4.0f, 4.0f );

				if ( m_nDissolveType == ENTITY_DISSOLVE_CORE )
				{
					if ( m_bCoreExplode == true )
					{
						sParticle->m_flDieTime *= 2.0f;
						sParticle->m_flRollDelta	= Helper_RandomFloat( -1.0f, 1.0f );
					}
				}
			}
			else
			{
				sParticle->m_flRollDelta	= Helper_RandomFloat( -8.0f, 8.0f );
			}
			
			sParticle->m_flLifetime		= 0.0f;

			sParticle->m_flRoll			= Helper_RandomInt( 0, 360 );

			float alpha = 255;

			sParticle->m_uchColor[0]	= m_vEffectColor.x;
			sParticle->m_uchColor[1]	= m_vEffectColor.y;
			sParticle->m_uchColor[2]	= m_vEffectColor.z;
			sParticle->m_uchStartAlpha	= alpha;
			sParticle->m_uchEndAlpha	= 0;
			sParticle->m_uchEndSize		= 0;
		}
			
		for ( int j = 0; j < numParticles; j++ )
		{
			offset = xDir * Helper_RandomFloat( -xScale*0.5f, xScale*0.5f ) + yDir * Helper_RandomFloat( -yScale*0.5f, yScale*0.5f );
			offset += vecSkew;

			sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), g_Material_AR2Glow, vecAbsOrigin + offset );

			if ( sParticle == NULL )
				return 1;
			
			sParticle->m_vecVelocity	= Vector( Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( -4.0f, 4.0f ), Helper_RandomFloat( -64.0f, 128.0f ) );
			sParticle->m_uchStartSize	= random->RandomFloat( 8, 12 ) * spriteScale;
			sParticle->m_flDieTime		= 0.1f;
			sParticle->m_flLifetime		= 0.0f;

			sParticle->m_flRoll			= Helper_RandomInt( 0, 360 );
			sParticle->m_flRollDelta	= Helper_RandomFloat( -2.0f, 2.0f );

			float alpha = 255;

			sParticle->m_uchColor[0]	= m_vEffectColor.x;
			sParticle->m_uchColor[1]	= m_vEffectColor.y;
			sParticle->m_uchColor[2]	= m_vEffectColor.z;
			sParticle->m_uchStartAlpha	= alpha;
			sParticle->m_uchEndAlpha	= 0;
			sParticle->m_uchEndSize		= 0;

			if ( m_nDissolveType == ENTITY_DISSOLVE_CORE )
			{
				if ( m_bCoreExplode == true )
				{
					Vector vDirection = (vecAbsOrigin + offset) - m_vDissolverOrigin;

					VectorNormalize( vDirection );

					sParticle->m_vecVelocity = vDirection * m_nMagnitude;

					sParticle->m_flDieTime		= 0.5f;
				}
			}
		}
	}

	return 1;
}
//-----------------------------------------------------------------------------
// Indicates whether the client should render particle systems
//-----------------------------------------------------------------------------
void CClientTools::EnableParticleSystems( bool bEnable )
{
	ParticleMgr()->RenderParticleSystems( bEnable );
}
Beispiel #29
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &origin - 
//			&normal - 
//			scale - 
//			r - 
//			g - 
//			b - 
//			flags - 
//-----------------------------------------------------------------------------
void FX_BloodSpray( const Vector &origin, const Vector &normal, float scale, unsigned char r, unsigned char g, unsigned char b, int flags )
{
	if ( UTIL_IsLowViolence() )
		return;

	//debugoverlay->AddLineOverlay( origin, origin + normal * 72, 255, 255, 255, true, 10 ); 

	Vector offset;
	float spread	= 0.2f;
	
	//Find area ambient light color and use it to tint smoke
	Vector worldLight = WorldGetLightForPoint( origin, true );
	Vector color = Vector( (float)(worldLight[0] * r) / 255.0f, (float)(worldLight[1] * g) / 255.0f, (float)(worldLight[2] * b) / 255.0f );
	float colorRamp;

	int i;

	Vector	offDir;

	Vector right;
	Vector up;

	if (normal != Vector(0, 0, 1) )
	{
		right = normal.Cross( Vector(0, 0, 1) );
		up = right.Cross( normal );
	}
	else
	{
		right = Vector(0, 0, 1);
		up = right.Cross( normal );
	}

	//
	// Dump out drops
	//
	if (flags & FX_BLOODSPRAY_DROPS)
	{
		TrailParticle *tParticle;

		CSmartPtr<CTrailParticles> pTrailEmitter = CTrailParticles::Create( "blooddrops" );
		if ( !pTrailEmitter )
			return;

		pTrailEmitter->SetSortOrigin( origin );

		// Partial gravity on blood drops.
		pTrailEmitter->SetGravity( 600.0 ); 

		pTrailEmitter->GetBinding().SetBBox( origin - Vector( 32, 32, 32 ), origin + Vector( 32, 32, 32 ) );
		pTrailEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
		pTrailEmitter->SetVelocityDampen( 0.2f );

		PMaterialHandle	hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_drop" );

		//
		// Long stringy drops of blood.
		//
		for ( i = 0; i < 14; i++ )
		{
			// Originate from within a circle 'scale' inches in diameter.
			offset = origin;
			offset += right * random->RandomFloat( -0.5f, 0.5f ) * scale;
			offset += up * random->RandomFloat( -0.5f, 0.5f ) * scale;

			tParticle = (TrailParticle *) pTrailEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );

			if ( tParticle == NULL )
				break;

			tParticle->m_flLifetime	= 0.0f;

			offDir = normal + RandomVector( -0.3f, 0.3f );

			tParticle->m_vecVelocity = offDir * random->RandomFloat( 4.0f * scale, 40.0f * scale );
			tParticle->m_vecVelocity[2] += random->RandomFloat( 4.0f, 16.0f ) * scale;

			tParticle->m_flWidth		= random->RandomFloat( 0.125f, 0.275f ) * scale;
			tParticle->m_flLength		= random->RandomFloat( 0.02f, 0.03f ) * scale;
			tParticle->m_flDieTime		= random->RandomFloat( 0.5f, 1.0f );

			FloatToColor32( tParticle->m_color, color[0], color[1], color[2], 1.0f );
		}

		//
		// Shorter droplets.
		//
		for ( i = 0; i < 24; i++ )
		{
			// Originate from within a circle 'scale' inches in diameter.
			offset = origin;
			offset += right * random->RandomFloat( -0.5f, 0.5f ) * scale;
			offset += up * random->RandomFloat( -0.5f, 0.5f ) * scale;

			tParticle = (TrailParticle *) pTrailEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );

			if ( tParticle == NULL )
				break;

			tParticle->m_flLifetime	= 0.0f;

			offDir = normal + RandomVector( -1.0f, 1.0f );
			offDir[2] += random->RandomFloat(0, 1.0f);

			tParticle->m_vecVelocity = offDir * random->RandomFloat( 2.0f * scale, 25.0f * scale );
			tParticle->m_vecVelocity[2] += random->RandomFloat( 4.0f, 16.0f ) * scale;

			tParticle->m_flWidth		= random->RandomFloat( 0.25f, 0.375f ) * scale;
			tParticle->m_flLength		= random->RandomFloat( 0.0025f, 0.005f ) * scale;
			tParticle->m_flDieTime		= random->RandomFloat( 0.5f, 1.0f );

			FloatToColor32( tParticle->m_color, color[0], color[1], color[2], 1.0f );
		}
	}

	if ((flags & FX_BLOODSPRAY_GORE) || (flags & FX_BLOODSPRAY_CLOUD))
	{
		CSmartPtr<CBloodSprayEmitter> pSimple = CBloodSprayEmitter::Create( "bloodgore" );
		if ( !pSimple )
			return;

		pSimple->SetSortOrigin( origin );
		pSimple->SetGravity( 0 );

		PMaterialHandle	hMaterial;

		//
		// Tight blossom of blood at the center.
		//
		if (flags & FX_BLOODSPRAY_GORE)
		{
			hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_gore" );

			SimpleParticle *pParticle;

			for ( i = 0; i < 6; i++ )
			{
				// Originate from within a circle 'scale' inches in diameter.
				offset = origin + ( 0.5 * scale * normal );
				offset += right * random->RandomFloat( -0.5f, 0.5f ) * scale;
				offset += up * random->RandomFloat( -0.5f, 0.5f ) * scale;

				pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset );

				if ( pParticle != NULL )
				{
					pParticle->m_flLifetime = 0.0f;
					pParticle->m_flDieTime	= 0.3f;

					spread = 0.2f;
					pParticle->m_vecVelocity.Random( -spread, spread );
					pParticle->m_vecVelocity += normal * random->RandomInt( 10, 100 );
					//VectorNormalize( pParticle->m_vecVelocity );

					colorRamp = random->RandomFloat( 0.75f, 1.25f );

					pParticle->m_uchColor[0]	= MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
					pParticle->m_uchColor[1]	= MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
					pParticle->m_uchColor[2]	= MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
					
					pParticle->m_uchStartSize	= random->RandomFloat( scale * 0.25, scale );
					pParticle->m_uchEndSize		= pParticle->m_uchStartSize * 2;
					
					pParticle->m_uchStartAlpha	= random->RandomInt( 200, 255 );
					pParticle->m_uchEndAlpha	= 0;
					
					pParticle->m_flRoll			= random->RandomInt( 0, 360 );
					pParticle->m_flRollDelta	= 0.0f;
				}
			}
		}

		//
		// Diffuse cloud just in front of the exit wound.
		//
		if (flags & FX_BLOODSPRAY_CLOUD)
		{
			hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_puff" );

			SimpleParticle *pParticle;

			for ( i = 0; i < 6; i++ )
			{
				// Originate from within a circle '2 * scale' inches in diameter.
				offset = origin + ( scale * normal );
				offset += right * random->RandomFloat( -1, 1 ) * scale;
				offset += up * random->RandomFloat( -1, 1 ) * scale;

				pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset );

				if ( pParticle != NULL )
				{
					pParticle->m_flLifetime = 0.0f;
					pParticle->m_flDieTime	= random->RandomFloat( 0.5f, 0.8f);

					spread = 0.5f;
					pParticle->m_vecVelocity.Random( -spread, spread );
					pParticle->m_vecVelocity += normal * random->RandomInt( 100, 200 );

					colorRamp = random->RandomFloat( 0.75f, 1.25f );

					pParticle->m_uchColor[0]	= MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
					pParticle->m_uchColor[1]	= MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
					pParticle->m_uchColor[2]	= MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
					
					pParticle->m_uchStartSize	= random->RandomFloat( scale * 1.5f, scale * 2.0f );
					pParticle->m_uchEndSize		= pParticle->m_uchStartSize * 4;
					
					pParticle->m_uchStartAlpha	= random->RandomInt( 80, 128 );
					pParticle->m_uchEndAlpha	= 0;
					
					pParticle->m_flRoll			= random->RandomInt( 0, 360 );
					pParticle->m_flRollDelta	= 0.0f;
				}
			}
		}
	}

	// TODO: Play a sound?
	//CLocalPlayerFilter filter;
	//C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, CHAN_VOICE, "Physics.WaterSplash", 1.0, ATTN_NORM, 0, 100, &origin );
}
Beispiel #30
0
//-----------------------------------------------------------------------------
// Purpose: Used for bullets hitting bleeding surfaces
// Input  : origin - 
//			normal - 
//			scale - This parameter is not currently used
//-----------------------------------------------------------------------------
void FX_BloodBulletImpact( const Vector &origin, const Vector &normal, float scale /*NOTE: Unused!*/, unsigned char r, unsigned char g, unsigned char b )
{
	if ( UTIL_IsLowViolence() )
		return;

	Vector offset;
	
	//Find area ambient light color and use it to tint smoke
	Vector worldLight = WorldGetLightForPoint( origin, true );
	
	if ( gpGlobals->maxClients > 1 )
	{
		worldLight = Vector( 1.0, 1.0, 1.0 );
		r = 96;
		g = 0;
		b = 10;
	}

	Vector color = Vector( (float)(worldLight[0] * r) / 255.0f, (float)(worldLight[1] * g) / 255.0f, (float)(worldLight[2] * b) / 255.0f );
	float colorRamp;

	Vector	offDir;

	CSmartPtr<CBloodSprayEmitter> pSimple = CBloodSprayEmitter::Create( "bloodgore" );
	if ( !pSimple )
		return;

	pSimple->SetSortOrigin( origin );
	pSimple->SetGravity( 200 );
	
	// Setup a bounding box to contain the particles without (stops auto-updating)
	pSimple->GetBinding().SetBBox( origin - Vector( 16, 16, 16 ), origin + Vector( 16, 16, 16 ) );

	// Cache the material if we haven't already
	if ( g_Blood_Core == NULL )
	{
		g_Blood_Core = ParticleMgr()->GetPMaterial( "effects/blood_core" );
	}

	SimpleParticle *pParticle;

	Vector	dir = normal * RandomVector( -0.5f, 0.5f );

	offset = origin + ( 2.0f * normal );

	pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Blood_Core, offset );

	if ( pParticle != NULL )
	{
		pParticle->m_flLifetime = 0.0f;
		pParticle->m_flDieTime	= random->RandomFloat( 0.25f, 0.5f);

		pParticle->m_vecVelocity	= dir * random->RandomFloat( 16.0f, 32.0f );
		pParticle->m_vecVelocity[2] -= random->RandomFloat( 8.0f, 16.0f );

		colorRamp = random->RandomFloat( 0.75f, 2.0f );

		pParticle->m_uchColor[0]	= MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
		pParticle->m_uchColor[1]	= MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
		pParticle->m_uchColor[2]	= MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
		
		pParticle->m_uchStartSize	= random->RandomInt( 2, 4 );
		pParticle->m_uchEndSize		= pParticle->m_uchStartSize * 8;
	
		pParticle->m_uchStartAlpha	= 255;
		pParticle->m_uchEndAlpha	= 0;
		
		pParticle->m_flRoll			= random->RandomInt( 0, 360 );
		pParticle->m_flRollDelta	= 0.0f;
	}

	// Cache the material if we haven't already
	if ( g_Blood_Gore == NULL )
	{
		g_Blood_Gore = ParticleMgr()->GetPMaterial( "effects/blood_gore" );
	}

	for ( int i = 0; i < 4; i++ )
	{
		offset = origin + ( 2.0f * normal );

		pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Blood_Gore, offset );

		if ( pParticle != NULL )
		{
			pParticle->m_flLifetime = 0.0f;
			pParticle->m_flDieTime	= random->RandomFloat( 0.5f, 0.75f);

			pParticle->m_vecVelocity	= dir * random->RandomFloat( 16.0f, 32.0f )*(i+1);
			pParticle->m_vecVelocity[2] -= random->RandomFloat( 32.0f, 64.0f )*(i+1);

			colorRamp = random->RandomFloat( 0.75f, 2.0f );

			pParticle->m_uchColor[0]	= MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
			pParticle->m_uchColor[1]	= MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
			pParticle->m_uchColor[2]	= MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
			
			pParticle->m_uchStartSize	= random->RandomInt( 2, 4 );
			pParticle->m_uchEndSize		= pParticle->m_uchStartSize * 4;
		
			pParticle->m_uchStartAlpha	= 255;
			pParticle->m_uchEndAlpha	= 0;
			
			pParticle->m_flRoll			= random->RandomInt( 0, 360 );
			pParticle->m_flRollDelta	= 0.0f;
		}
	}

	//
	// Dump out drops
	//
	TrailParticle *tParticle;

	CSmartPtr<CTrailParticles> pTrailEmitter = CTrailParticles::Create( "blooddrops" );
	if ( !pTrailEmitter )
		return;

	pTrailEmitter->SetSortOrigin( origin );

	// Partial gravity on blood drops
	pTrailEmitter->SetGravity( 400.0 ); 
	
	// Enable simple collisions with nearby surfaces
	pTrailEmitter->Setup(origin, &normal, 1, 10, 100, 400, 0.2, 0 );

	if ( g_Blood_Drops == NULL )
	{
		g_Blood_Drops = ParticleMgr()->GetPMaterial( "effects/blood_drop" );
	}

	//
	// Shorter droplets
	//
	for ( int i = 0; i < 8; i++ )
	{
		// Originate from within a circle 'scale' inches in diameter
		offset = origin;

		tParticle = (TrailParticle *) pTrailEmitter->AddParticle( sizeof(TrailParticle), g_Blood_Drops, offset );

		if ( tParticle == NULL )
			break;

		tParticle->m_flLifetime	= 0.0f;

		offDir = RandomVector( -1.0f, 1.0f );

		tParticle->m_vecVelocity = offDir * random->RandomFloat( 64.0f, 128.0f );

		tParticle->m_flWidth		= random->RandomFloat( 0.5f, 2.0f );
		tParticle->m_flLength		= random->RandomFloat( 0.05f, 0.15f );
		tParticle->m_flDieTime		= random->RandomFloat( 0.25f, 0.5f );

		FloatToColor32( tParticle->m_color, color[0], color[1], color[2], 1.0f );
	}

	// TODO: Play a sound?
	//CLocalPlayerFilter filter;
	//C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, CHAN_VOICE, "Physics.WaterSplash", 1.0, ATTN_NORM, 0, 100, &origin );
}