//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $Workfile:     $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "iviewrender_beams.h"
#include "tempentity.h"
#include "beam_shared.h"
#include "ivieweffects.h"
#include "beamdraw.h"
#include "engine/IVDebugOverlay.h"
#include "engine/ivmodelinfo.h"
#include "view.h"
#include "fx.h"
#include "tier0/icommandline.h"
#include "tier0/vprof.h"
#include "c_pixel_visibility.h"
#include "iviewrender.h"
#include "view_shared.h"
#include "viewrender.h"

#ifdef PORTAL
	#include "prop_portal_shared.h"
#endif

ConVar r_DrawBeams( "r_DrawBeams", "1", FCVAR_CHEAT, "0=Off, 1=Normal, 2=Wireframe" );

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

bool g_BeamCreationAllowed = false;

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : state - 
//-----------------------------------------------------------------------------
void SetBeamCreationAllowed( bool state )
{
	g_BeamCreationAllowed = state;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool BeamCreationAllowed( void )
{
	return g_BeamCreationAllowed;
}

extern IViewEffects *vieweffects;

//-----------------------------------------------------------------------------
// Purpose: Implements beam rendering apis
//-----------------------------------------------------------------------------
class CViewRenderBeams : public IViewRenderBeams
{
// Construction
public:
						CViewRenderBeams( void );
	virtual				~CViewRenderBeams( void );

// Implement IViewRenderBeams
public:
	virtual	void		InitBeams( void );
	virtual	void		ShutdownBeams( void );
	virtual	void		ClearBeams( void );

	// Updates the state of the temp ent beams
	virtual void		UpdateTempEntBeams();

	virtual void		DrawBeam( C_Beam* pbeam, ITraceFilter *pEntityBeamTraceFilter = NULL );
	virtual void		DrawBeam( Beam_t *pbeam );

	virtual	void		KillDeadBeams( C_BaseEntity *pDeadEntity );

	virtual Beam_t		*CreateBeamEnts( BeamInfo_t &beamInfo );
	virtual Beam_t		*CreateBeamEntPoint( BeamInfo_t &beamInfo );
	virtual	Beam_t		*CreateBeamPoints( BeamInfo_t &beamInfo );
	virtual Beam_t		*CreateBeamRing( BeamInfo_t &beamInfo );
	virtual Beam_t		*CreateBeamRingPoint( BeamInfo_t &beamInfo );
	virtual Beam_t		*CreateBeamCirclePoints( BeamInfo_t &beamInfo );
	virtual Beam_t		*CreateBeamFollow( BeamInfo_t &beamInfo );

	virtual	void		CreateBeamEnts( int startEnt, int endEnt, int modelIndex, int haloIndex, float haloScale, 
							float life, float width, float endWidth, float fadeLength, float amplitude, 
							float brightness, float speed, int startFrame, 
							float framerate, float r, float g, float b, int type = -1 );
	virtual	void		CreateBeamEntPoint( int	nStartEntity, const Vector *pStart, int nEndEntity, const Vector* pEnd,
							int modelIndex, int haloIndex, float haloScale, 
							float life, float width, float endWidth, float fadeLength, float amplitude, 
							float brightness, float speed, int startFrame, 
							float framerate, float r, float g, float b );
	virtual	void		CreateBeamPoints( Vector& start, Vector& end, int modelIndex, int haloIndex, float haloScale,   
							float life, float width, float endWidth, float fadeLength, float amplitude, 
							float brightness, float speed, int startFrame, 
							float framerate, float r, float g, float b );
	virtual	void		CreateBeamRing( int startEnt, int endEnt, int modelIndex, int haloIndex, float haloScale,   
							float life, float width, float endWidth, float fadeLength, float amplitude, 
							float brightness, float speed, int startFrame, 
							float framerate, float r, float g, float b, int flags );
	virtual void		CreateBeamRingPoint( const Vector& center, float start_radius, float end_radius, int modelIndex, int haloIndex, float haloScale,   
							float life, float width, float m_nEndWidth, float m_nFadeLength, float amplitude, 
							float brightness, float speed, int startFrame, 
							float framerate, float r, float g, float b, int flags );
	virtual	void		CreateBeamCirclePoints( int type, Vector& start, Vector& end, 
							int modelIndex,  int haloIndex,  float haloScale, float life, float width, 
							float endWidth, float fadeLength, float amplitude, float brightness, float speed, 
							int startFrame, float framerate, float r, float g, float b );
	virtual	void		CreateBeamFollow( int startEnt, int modelIndex, int haloIndex, float haloScale,   
							float life, float width, float endWidth, float fadeLength, float r, float g, float b, 
							float brightness );

	virtual void		FreeBeam( Beam_t *pBeam ) { BeamFree( pBeam ); }
	virtual void		UpdateBeamInfo( Beam_t *pBeam, BeamInfo_t &beamInfo );

private:
	void					FreeDeadTrails( BeamTrail_t **trail );
	void					UpdateBeam( Beam_t *pbeam, float frametime );
	void					DrawBeamWithHalo( Beam_t* pbeam,int frame,int rendermode,float *color, float *srcColor, const model_t *sprite,const model_t *halosprite, float flHDRColorScale );
	void					DrawBeamFollow( const model_t* pSprite, Beam_t *pbeam, int frame, int rendermode, float frametime, const float* color, float flHDRColorScale = 1.0f );
	void					DrawLaser( Beam_t* pBeam, int frame, int rendermode, float* color, model_t const* sprite, model_t const* halosprite, float flHDRColorScale = 1.0f );
	void					DrawTesla( Beam_t* pBeam, int frame, int rendermode, float* color, model_t const* sprite, float flHDRColorScale = 1.0f );

	bool					RecomputeBeamEndpoints( Beam_t *pbeam );

	int						CullBeam( const Vector &start, const Vector &end, int pvsOnly );

	// Creation
	Beam_t					*CreateGenericBeam( BeamInfo_t &beamInfo );
	void					SetupBeam( Beam_t *pBeam, const BeamInfo_t &beamInfo );
	void					SetBeamAttributes( Beam_t *pBeam, const BeamInfo_t &beamInfo );
	
	// Memory Alloc/Free
	Beam_t*					BeamAlloc( bool bRenderable );
	void					BeamFree( Beam_t* pBeam );

// DATA
private:
	enum
	{

#ifndef _XBOX
		// default max # of particles at one time
		DEFAULT_PARTICLES	= 2048,
#else
		DEFAULT_PARTICLES   = 1024,
#endif

		// no fewer than this no matter what's on the command line
		MIN_PARTICLES		= 512,	

#ifndef _XBOX
		// Maximum length of the free list.
		BEAM_FREELIST_MAX	= 32
#else
		BEAM_FREELIST_MAX   = 4
#endif

	};

	Beam_t					*m_pActiveBeams;
	Beam_t					*m_pFreeBeams;
	int						m_nBeamFreeListLength;

	BeamTrail_t				*m_pBeamTrails;
	BeamTrail_t				*m_pActiveTrails;
	BeamTrail_t				*m_pFreeTrails;
	int						m_nNumBeamTrails;
};

// Expose interface to rest of client .dll
static CViewRenderBeams s_ViewRenderBeams;
IViewRenderBeams *beams = ( IViewRenderBeams * )&s_ViewRenderBeams;

CUniformRandomStream beamRandom;
//-----------------------------------------------------------------------------
// Global methods
//-----------------------------------------------------------------------------

//		freq2 += step * 0.1;
// Fractal noise generator, power of 2 wavelength
static void Noise( float *noise, int divs, float scale )
{
	int div2;
	
	div2 = divs >> 1;

	if ( divs < 2 )
		return;

	// Noise is normalized to +/- scale
	noise[ div2 ] = (noise[0] + noise[divs]) * 0.5 + scale * beamRandom.RandomFloat(-1, 1);
	if ( div2 > 1 )
	{
		Noise( &noise[div2], div2, scale * 0.5 );
		Noise( noise, div2, scale * 0.5 );
	}
}

static void SineNoise( float *noise, int divs )
{
	int i;
	float freq;
	float step = M_PI / (float)divs;

	freq = 0;
	for ( i = 0; i < divs; i++ )
	{
		noise[i] = sin( freq );
		freq += step;
	}
}

bool ComputeBeamEntPosition( C_BaseEntity *pEnt, int nAttachment, bool bInterpretAttachmentIndexAsHitboxIndex, Vector& pt )
{
	// NOTE: This will *leave* the pt at its current value, essential for
	// beam follow ents what want to stick around a little after their ent has died
	if (!pEnt)
		return false;

	if ( !bInterpretAttachmentIndexAsHitboxIndex )
	{
		QAngle angles;
		if ( pEnt->GetAttachment( nAttachment, pt, angles ) )
			return true;
	}
	else
	{
		C_BaseAnimating *pAnimating = pEnt->GetBaseAnimating();
		if ( pAnimating )
		{
			studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pAnimating->GetModel() );
			if (pStudioHdr)
			{
				mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnimating->GetHitboxSet() );
				if ( set && (set->numhitboxes >= nAttachment) && (nAttachment > 0) )
				{
					matrix3x4_t	*hitboxbones[MAXSTUDIOBONES];
					if ( pAnimating->HitboxToWorldTransforms( hitboxbones ) )
					{
						mstudiobbox_t *pHitbox = set->pHitbox( nAttachment - 1 );
						Vector vecViewPt = MainViewOrigin();
						Vector vecLocalViewPt;
						VectorITransform( vecViewPt, *hitboxbones[ pHitbox->bone ], vecLocalViewPt );

						Vector vecLocalClosestPt;
						CalcClosestPointOnAABB( pHitbox->bbmin, pHitbox->bbmax, vecLocalViewPt, vecLocalClosestPt );

						VectorTransform( vecLocalClosestPt, *hitboxbones[ pHitbox->bone ], pt );

//						MatrixGetColumn( *hitboxbones[ pHitbox->bone ], 3, pt );
						return true;
					}
				}
			}
		}
	}

	// Player origins are at their feet
	if ( pEnt->IsPlayer() )
	{
		pt = pEnt->WorldSpaceCenter();
	}
	else
	{
		VectorCopy( pEnt->GetRenderOrigin(), pt );
	}

	return true;
}


//-----------------------------------------------------------------------------
//
// Methods of Beam_t
//
//-----------------------------------------------------------------------------

Beam_t::Beam_t()
{
#ifdef PORTAL
	m_bDrawInMainRender = true;
	m_bDrawInPortalRender = true;
#endif

	Reset();
}


void Beam_t::Reset()
{
	m_Mins.Init(0,0,0);
	m_Maxs.Init(0,0,0);
	type = 0;
	flags = 0;
	trail = 0;
	m_hRenderHandle = INVALID_CLIENT_RENDER_HANDLE;
	m_bCalculatedNoise = false;
	m_queryHandleHalo = NULL;
	m_flHDRColorScale = 1.0f;
}


const Vector& Beam_t::GetRenderOrigin( void )
{
	if ((type == TE_BEAMRING) || (type == TE_BEAMRINGPOINT))
	{
		// return the center of the ring
		static Vector org;
		VectorMA( attachment[0], 0.5f, delta, org );
		return org;
	}

	return attachment[0];
}

const QAngle& Beam_t::GetRenderAngles( void )
{
	return vec3_angle;
}

const matrix3x4_t &Beam_t::RenderableToWorldTransform()
{
	static matrix3x4_t mat;
	SetIdentityMatrix( mat );
	PositionMatrix( GetRenderOrigin(), mat );
	return mat;
}


void Beam_t::GetRenderBounds( Vector& mins, Vector& maxs )
{
	VectorCopy( m_Mins, mins );
	VectorCopy( m_Maxs, maxs );
}


void Beam_t::ComputeBounds( )
{
	switch( type )
	{
	case TE_BEAMSPLINE:
		{
			// Here, we gotta look at all the attachments....
			Vector attachmentDelta;
			m_Mins.Init( 0,0,0 );
			m_Maxs.Init( 0,0,0 );

			for (int i=1; i < numAttachments; i++)
			{
				VectorSubtract( attachment[i], attachment[0], attachmentDelta );
				m_Mins = m_Mins.Min( attachmentDelta );
				m_Maxs = m_Maxs.Max( attachmentDelta );
			}
		}
		break;

	case TE_BEAMDISK:
	case TE_BEAMCYLINDER:
		{
			// FIXME: This isn't quite right for the cylinder

			// Here, delta[2] is the radius
			int radius = delta[2];
			m_Mins.Init( -radius, -radius, -radius );
			m_Maxs.Init( radius, radius, radius );
		}
		break;

	case TE_BEAMRING:
	case TE_BEAMRINGPOINT:
		{
			int radius = delta.Length() * 0.5f;
			m_Mins.Init( -radius, -radius, -radius );
			m_Maxs.Init( radius, radius, radius );
		}
		break;

	case TE_BEAMPOINTS:
	default:
		{
			// Just use the delta
			for (int i = 0; i < 3; ++i)
			{
				if (delta[i] > 0.0f)
				{
					m_Mins[i] = 0.0f;
					m_Maxs[i] = delta[i];
				}
				else
				{
					m_Mins[i] = delta[i];
					m_Maxs[i] = 0.0f;
				}
			}
		}
		break;
	}

	// Deal with beam follow
	Vector org = GetRenderOrigin();
	Vector followDelta;
	BeamTrail_t* pFollow = trail;
	while (pFollow)
	{
		VectorSubtract( pFollow->org, org, followDelta );
		m_Mins = m_Mins.Min( followDelta );
		m_Maxs = m_Maxs.Max( followDelta );

		pFollow = pFollow->next;
	}
}

bool Beam_t::ShouldDraw( void )
{
	return true;
}

bool Beam_t::IsTransparent( void )
{
	return true;
}

void Beam_t::ComputeFxBlend( )
{
	// Do nothing, they're always 255
}

int	Beam_t::GetFxBlend( )
{
	return 255;
}

extern bool g_bRenderingScreenshot;
extern ConVar r_drawviewmodel;

int Beam_t::DrawModel( int flags )
{
#ifdef PORTAL
	if ( ( !g_pPortalRender->IsRenderingPortal() && !m_bDrawInMainRender ) || 
		( g_pPortalRender->IsRenderingPortal() && !m_bDrawInPortalRender ) )
	{
		return 0;
	}
#endif //#ifdef PORTAL

	// Tracker 16432:  If rendering a savegame screenshot don't draw beams 
	//   who have viewmodels as their attached entity
	if ( g_bRenderingScreenshot || !r_drawviewmodel.GetBool() )
	{
		// If the beam is attached
		for (int i=0;i<MAX_BEAM_ENTS;i++)
		{
			C_BaseViewModel *vm = dynamic_cast<C_BaseViewModel *>(entity[i].Get());
			if ( vm )
			{
				return 0;
			}
		}
	}

	s_ViewRenderBeams.DrawBeam( this );
	return 0;
}


//-----------------------------------------------------------------------------
//
// Implementation of CViewRenderBeams
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constructor, destructor: 
//-----------------------------------------------------------------------------

CViewRenderBeams::CViewRenderBeams( void ) : m_pBeamTrails(0)
{
	m_pFreeBeams = NULL;
	m_pActiveBeams = NULL;
	m_nBeamFreeListLength = 0;
}

CViewRenderBeams::~CViewRenderBeams( void )
{
	ClearBeams();
}


//-----------------------------------------------------------------------------
// Purpose: Initialize beam system and beam trails for follow beams
//-----------------------------------------------------------------------------
void CViewRenderBeams::InitBeams( void )
{
	int p = CommandLine()->ParmValue("-particles", -1);
	if ( p >= 0 )
	{
		m_nNumBeamTrails = max( p, MIN_PARTICLES );
	}
	else
	{
		m_nNumBeamTrails = DEFAULT_PARTICLES;
	}
	
	m_pBeamTrails = (BeamTrail_t *)new BeamTrail_t[ m_nNumBeamTrails ];
	Assert( m_pBeamTrails );

	// Clear them out
	ClearBeams();
}


//-----------------------------------------------------------------------------
// Purpose: Clear out all beams
//-----------------------------------------------------------------------------
void CViewRenderBeams::ClearBeams( void )
{
	Beam_t *next = NULL;
	for( ; m_pActiveBeams; m_pActiveBeams = next )
	{
		next = m_pActiveBeams->next;
		delete m_pActiveBeams;
	}

	for( ; m_pFreeBeams; m_pFreeBeams = next )
	{
		next = m_pFreeBeams->next;
		delete m_pFreeBeams;
	}

	m_nBeamFreeListLength = 0;

	if ( m_nNumBeamTrails )
	{
		// Also clear any particles used by beams
		m_pFreeTrails = &m_pBeamTrails[0];
		m_pActiveTrails = NULL;

		for (int i=0 ;i<m_nNumBeamTrails ; i++)
		{
			m_pBeamTrails[i].next = &m_pBeamTrails[i+1];
		}
		m_pBeamTrails[m_nNumBeamTrails-1].next = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Shut down beam system
//-----------------------------------------------------------------------------

void CViewRenderBeams::ShutdownBeams( void )
{
	if (m_pBeamTrails)
	{
		delete[] m_pBeamTrails;
		m_pActiveTrails = NULL;
		m_pBeamTrails = NULL;
		m_pFreeTrails = NULL;
		m_nNumBeamTrails = 0;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Try and allocate a free beam
// Output : Beam_t
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::BeamAlloc( bool bRenderable )
{
	Beam_t*	pBeam = NULL; 

	if ( m_pFreeBeams )
	{
		pBeam = m_pFreeBeams;
		m_pFreeBeams  = pBeam->next;
		m_nBeamFreeListLength--;
	}
	else
	{
		pBeam = new Beam_t();
		if( !pBeam )
		{
			DevMsg( "ERROR: failed to alloc Beam_t!\n" );
			Assert( pBeam );
		}
	}
	pBeam->next		= m_pActiveBeams;
	m_pActiveBeams	= pBeam;

	if ( bRenderable )
	{
		// Hook it into the rendering system...
		ClientLeafSystem()->AddRenderable( pBeam, RENDER_GROUP_TRANSLUCENT_ENTITY );
	}
	else
	{
		pBeam->m_hRenderHandle = INVALID_CLIENT_RENDER_HANDLE;
	}

	return pBeam;
}

//-----------------------------------------------------------------------------
// Purpose: Free the beam.
//-----------------------------------------------------------------------------
void CViewRenderBeams::BeamFree( Beam_t* pBeam )
{
	// Free particles that have died off.
	FreeDeadTrails( &pBeam->trail );

	// Remove it from the rendering system...
	ClientLeafSystem()->RemoveRenderable( pBeam->m_hRenderHandle );

	// Clear us out
	pBeam->Reset();

	if( m_nBeamFreeListLength < BEAM_FREELIST_MAX )
	{
		m_nBeamFreeListLength++;

		// Now link into free list;
		pBeam->next = m_pFreeBeams;
		m_pFreeBeams = pBeam;
	}
	else
	{
		delete pBeam;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Iterates through active list and kills beams associated with deadEntity
// Input  : deadEntity - 
//-----------------------------------------------------------------------------
void CViewRenderBeams::KillDeadBeams( C_BaseEntity *pDeadEntity )
{
	Beam_t *pbeam;
	Beam_t *pnewlist;
	Beam_t *pnext;
	BeamTrail_t *pHead;  // Build a new list to replace m_pActiveBeams.

	pbeam    = m_pActiveBeams;  // Old list.
	pnewlist = NULL;           // New list.
	
	while (pbeam)
	{
		pnext = pbeam->next;
		if (pbeam->entity[0] != pDeadEntity )   // Link into new list.
		{
			pbeam->next = pnewlist;
			pnewlist = pbeam;

			pbeam = pnext;
			continue;
		}

		pbeam->flags &= ~(FBEAM_STARTENTITY | FBEAM_ENDENTITY);
		if ( pbeam->type != TE_BEAMFOLLOW )
		{
			// Die Die Die!
			pbeam->die = gpGlobals->curtime - 0.1;  

			// Kill off particles
			pHead = pbeam->trail;
			while (pHead)
			{
				pHead->die = gpGlobals->curtime - 0.1;
				pHead = pHead->next;
			}

			// Free the beam
			BeamFree( pbeam );
		}
		else
		{
			// Stay active
			pbeam->next = pnewlist;
			pnewlist = pbeam;
		}
		pbeam = pnext;
	}

	// We now have a new list with the bogus stuff released.
	m_pActiveBeams = pnewlist;
}

//-----------------------------------------------------------------------------
// Purpose: Fill in values to beam structure.
//   Input: pBeam -
//          beamInfo -
//-----------------------------------------------------------------------------
void CViewRenderBeams::SetupBeam( Beam_t *pBeam, const BeamInfo_t &beamInfo )
{
	const model_t *pSprite = modelinfo->GetModel( beamInfo.m_nModelIndex );
	if ( !pSprite )
		return;

	pBeam->type				= ( beamInfo.m_nType < 0 ) ? TE_BEAMPOINTS : beamInfo.m_nType;
	pBeam->modelIndex		= beamInfo.m_nModelIndex;
	pBeam->haloIndex		= beamInfo.m_nHaloIndex;
	pBeam->haloScale		= beamInfo.m_flHaloScale;
	pBeam->frame			= 0;
	pBeam->frameRate		= 0;
	pBeam->frameCount		= modelinfo->GetModelFrameCount( pSprite );
	pBeam->freq				= gpGlobals->curtime * beamInfo.m_flSpeed;
	pBeam->die				= gpGlobals->curtime + beamInfo.m_flLife;
	pBeam->width			= beamInfo.m_flWidth;
	pBeam->endWidth			= beamInfo.m_flEndWidth;
	pBeam->fadeLength		= beamInfo.m_flFadeLength;
	pBeam->amplitude		= beamInfo.m_flAmplitude;
	pBeam->brightness		= beamInfo.m_flBrightness;
	pBeam->speed			= beamInfo.m_flSpeed;
	pBeam->life				= beamInfo.m_flLife;
	pBeam->flags			= 0;

	VectorCopy( beamInfo.m_vecStart, pBeam->attachment[0] );
	VectorCopy( beamInfo.m_vecEnd, pBeam->attachment[1] );
	VectorSubtract( beamInfo.m_vecEnd, beamInfo.m_vecStart, pBeam->delta );
	Assert( pBeam->delta.IsValid() );

	if ( beamInfo.m_nSegments == -1 )
	{
		if ( pBeam->amplitude >= 0.50 )
		{
			pBeam->segments = VectorLength( pBeam->delta ) * 0.25 + 3; // one per 4 pixels
		}
		else
		{
			pBeam->segments = VectorLength( pBeam->delta ) * 0.075 + 3; // one per 16 pixels
		}
	}
	else
	{
		pBeam->segments = beamInfo.m_nSegments;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Set beam color and frame data.
//	 Input: pBeam -
//          beamInfo -
//-----------------------------------------------------------------------------
void CViewRenderBeams::SetBeamAttributes( Beam_t *pBeam, const BeamInfo_t &beamInfo )
{
	pBeam->frame	 = ( float )beamInfo.m_nStartFrame;
	pBeam->frameRate = beamInfo.m_flFrameRate;
	pBeam->flags |= beamInfo.m_nFlags;

	pBeam->r = beamInfo.m_flRed;
	pBeam->g = beamInfo.m_flGreen;
	pBeam->b = beamInfo.m_flBlue;
}

//-----------------------------------------------------------------------------
// Purpose: Cull beam by bbox
// Input  : *start - 
//			*end - 
//			pvsOnly - 
// Output : int
//-----------------------------------------------------------------------------

int CViewRenderBeams::CullBeam( const Vector &start, const Vector &end, int pvsOnly )
{
	Vector mins, maxs;
	int i;

	for ( i = 0; i < 3; i++ )
	{
		if ( start[i] < end[i] )
		{
			mins[i] = start[i];
			maxs[i] = end[i];
		}
		else
		{
			mins[i] = end[i];
			maxs[i] = start[i];
		}
		
		// Don't let it be zero sized
		if ( mins[i] == maxs[i] )
		{
			maxs[i] += 1;
		}
	}

	// Check bbox
	if ( engine->IsBoxVisible( mins, maxs ) )
	{
		if ( pvsOnly || !engine->CullBox( mins, maxs ) )
		{
			// Beam is visible
			return 1;	
		}
	}

	// Beam is not visible
	return 0;
}

//-----------------------------------------------------------------------------
// Purpose: Allocate and setup a generic beam.
//   Input: beamInfo -
//  Output: Beam_t
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::CreateGenericBeam( BeamInfo_t &beamInfo )
{
#if 0
	if ( BeamCreationAllowed() == false )
	{
		//NOTENOTE: If you've hit this, you may not add a beam where you have attempted to.
		//			Most often this means that you have added it in an entity's DrawModel function.
		//			Move this to the ClientThink function instead!

		DevMsg( "ERROR: Beam created too late in frame!\n" );
		Assert(0);
		return NULL;
	}
#endif

	Beam_t *pBeam = BeamAlloc( beamInfo.m_bRenderable );
	if ( !pBeam )
		return NULL;

	// In case we fail.
	pBeam->die = gpGlobals->curtime;

	// Need a valid model.
	if ( beamInfo.m_nModelIndex < 0 )
		return NULL;

	// Set it up
	SetupBeam( pBeam, beamInfo );

	return pBeam;	
}

//-----------------------------------------------------------------------------
// Purpose: Create a beam between two ents
// Input  : startEnt - 
//			endEnt - 
//			modelIndex - 
//			life - 
//			width - 
//			amplitude - 
//			brightness - 
//			speed - 
//			startFrame - 
//			framerate - 
//			BEAMENT_ENTITY(startEnt - 
// Output : Beam_t
//-----------------------------------------------------------------------------
void CViewRenderBeams::CreateBeamEnts( int startEnt, int endEnt, int modelIndex, 
	int haloIndex, float haloScale, float life, float width, float endWidth, 
	float fadeLength, float amplitude, float brightness, float speed, 
	int startFrame, float framerate, float r, float g, float b, int type )
{
	BeamInfo_t beamInfo;

	beamInfo.m_nType = type;
	beamInfo.m_pStartEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( startEnt ) );
	beamInfo.m_nStartAttachment = BEAMENT_ATTACHMENT( startEnt );
	beamInfo.m_pEndEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( endEnt ) );
	beamInfo.m_nEndAttachment = BEAMENT_ATTACHMENT( endEnt );
	beamInfo.m_nModelIndex = modelIndex;
	beamInfo.m_nHaloIndex = haloIndex;
	beamInfo.m_flHaloScale = haloScale;
	beamInfo.m_flLife = life;
	beamInfo.m_flWidth = width;
	beamInfo.m_flEndWidth = endWidth;
	beamInfo.m_flFadeLength = fadeLength;
	beamInfo.m_flAmplitude = amplitude;
	beamInfo.m_flBrightness = brightness;
	beamInfo.m_flSpeed = speed;
	beamInfo.m_nStartFrame = startFrame;
	beamInfo.m_flFrameRate = framerate;
	beamInfo.m_flRed = r;
	beamInfo.m_flGreen = g;
	beamInfo.m_flBlue = b;

	CreateBeamEnts( beamInfo );
}

//-----------------------------------------------------------------------------
// Purpose: Create a beam between two entities.
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::CreateBeamEnts( BeamInfo_t &beamInfo )
{
	// Don't start temporary beams out of the PVS
	if ( beamInfo.m_flLife != 0 && 
		 ( !beamInfo.m_pStartEnt || beamInfo.m_pStartEnt->GetModel() == NULL || 
		   !beamInfo.m_pEndEnt || beamInfo.m_pEndEnt->GetModel() == NULL) )
	{
		return NULL;
	}

	beamInfo.m_vecStart = vec3_origin;
	beamInfo.m_vecEnd = vec3_origin;

	Beam_t *pBeam = CreateGenericBeam( beamInfo );
	if ( !pBeam )
		return NULL;

	pBeam->type = ( beamInfo.m_nType < 0 ) ? TE_BEAMPOINTS : beamInfo.m_nType;
	pBeam->flags = FBEAM_STARTENTITY | FBEAM_ENDENTITY;

	pBeam->entity[0] = beamInfo.m_pStartEnt;
	pBeam->attachmentIndex[0] = beamInfo.m_nStartAttachment;
	pBeam->entity[1] = beamInfo.m_pEndEnt;
	pBeam->attachmentIndex[1] = beamInfo.m_nEndAttachment;

	// Attributes.
	SetBeamAttributes( pBeam, beamInfo );
	if ( beamInfo.m_flLife == 0 )
	{
		pBeam->flags |= FBEAM_FOREVER;
	}

	UpdateBeam( pBeam, 0 );

	return pBeam;
}

//-----------------------------------------------------------------------------
// Purpose: Creates a beam between an entity and a point
// Input  : startEnt - 
//			*end - 
//			modelIndex - 
//			life - 
//			width - 
//			amplitude - 
//			brightness - 
//			speed - 
//			startFrame - 
//			framerate - 
//			r - 
//			g - 
//			b - 
// Output : Beam_t
//-----------------------------------------------------------------------------
void CViewRenderBeams::CreateBeamEntPoint( int nStartEntity, const Vector *pStart, int nEndEntity, const Vector* pEnd,
										   int modelIndex, int haloIndex, float haloScale, float life,  float width, 
										   float endWidth, float fadeLength, float amplitude, float brightness, float speed, int startFrame, 
										   float framerate, float r, float g, float b )
{
	BeamInfo_t beamInfo;

	if ( nStartEntity <= 0 )
	{
		beamInfo.m_vecStart = pStart ? *pStart : vec3_origin;
		beamInfo.m_pStartEnt = NULL;
	}
	else
	{
		beamInfo.m_pStartEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( nStartEntity ) );
		beamInfo.m_nStartAttachment = BEAMENT_ATTACHMENT( nStartEntity );

		// Don't start beams out of the PVS
		if ( !beamInfo.m_pStartEnt )
			return;
	}

	if ( nEndEntity <= 0 )
	{
		beamInfo.m_vecEnd = pEnd ? *pEnd : vec3_origin;
		beamInfo.m_pEndEnt = NULL;
	}
	else
	{
		beamInfo.m_pEndEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( nEndEntity ) );
		beamInfo.m_nEndAttachment = BEAMENT_ATTACHMENT( nEndEntity );

		// Don't start beams out of the PVS
		if ( !beamInfo.m_pEndEnt )
			return;
	}

	beamInfo.m_nModelIndex = modelIndex;
	beamInfo.m_nHaloIndex = haloIndex;
	beamInfo.m_flHaloScale = haloScale;
	beamInfo.m_flLife = life;
	beamInfo.m_flWidth = width;
	beamInfo.m_flEndWidth = endWidth;
	beamInfo.m_flFadeLength = fadeLength;
	beamInfo.m_flAmplitude = amplitude;
	beamInfo.m_flBrightness = brightness;
	beamInfo.m_flSpeed = speed;
	beamInfo.m_nStartFrame = startFrame;
	beamInfo.m_flFrameRate = framerate;
	beamInfo.m_flRed = r;
	beamInfo.m_flGreen = g;
	beamInfo.m_flBlue = b;

	CreateBeamEntPoint( beamInfo );
}

//-----------------------------------------------------------------------------
// Purpose: Creates a beam between an entity and a point.
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::CreateBeamEntPoint( BeamInfo_t &beamInfo )
{
	if ( beamInfo.m_flLife != 0 )
	{
		if ( beamInfo.m_pStartEnt && beamInfo.m_pStartEnt->GetModel() == NULL )
			return NULL;

		if ( beamInfo.m_pEndEnt && beamInfo.m_pEndEnt->GetModel() == NULL )
			return NULL;
	}

	// Model index.
	if ( ( beamInfo.m_pszModelName ) && ( beamInfo.m_nModelIndex == -1 ) )
	{
		beamInfo.m_nModelIndex = modelinfo->GetModelIndex( beamInfo.m_pszModelName );
	}

	if ( ( beamInfo.m_pszHaloName ) && ( beamInfo.m_nHaloIndex == -1 ) )
	{
		beamInfo.m_nHaloIndex = modelinfo->GetModelIndex( beamInfo.m_pszHaloName );
	}

	Beam_t *pBeam = CreateGenericBeam( beamInfo );
	if ( !pBeam )
		return NULL;

	pBeam->type = TE_BEAMPOINTS;
	pBeam->flags = 0;

	if ( beamInfo.m_pStartEnt )
	{
		pBeam->flags |= FBEAM_STARTENTITY;
		pBeam->entity[0] = beamInfo.m_pStartEnt;
		pBeam->attachmentIndex[0] = beamInfo.m_nStartAttachment;
		beamInfo.m_vecStart = vec3_origin;
	}
	if ( beamInfo.m_pEndEnt )
	{
		pBeam->flags |= FBEAM_ENDENTITY;
		pBeam->entity[1] = beamInfo.m_pEndEnt;
		pBeam->attachmentIndex[1] = beamInfo.m_nEndAttachment;
		beamInfo.m_vecEnd = vec3_origin;
	}

	SetBeamAttributes( pBeam, beamInfo );
	if ( beamInfo.m_flLife == 0 )
	{
		pBeam->flags |= FBEAM_FOREVER;
	}

	UpdateBeam( pBeam, 0 );
	return pBeam;
}

//-----------------------------------------------------------------------------
// Purpose: Creates a beam between two points
// Input  : *start - 
//			*end - 
//			modelIndex - 
//			life - 
//			width - 
//			amplitude - 
//			brightness - 
//			speed - 
//			startFrame - 
//			framerate - 
//			r - 
//			g - 
//			b - 
// Output : Beam_t
//-----------------------------------------------------------------------------
void CViewRenderBeams::CreateBeamPoints( Vector& start, Vector& end, int modelIndex, int haloIndex, float haloScale, float life,  float width, 
										 float endWidth, float fadeLength,float amplitude, float brightness, float speed, int startFrame, 
										 float framerate, float r, float g, float b )
{
	BeamInfo_t beamInfo;
	
	beamInfo.m_vecStart = start;
	beamInfo.m_vecEnd = end;
	beamInfo.m_nModelIndex = modelIndex;
	beamInfo.m_nHaloIndex = haloIndex;
	beamInfo.m_flHaloScale = haloScale;
	beamInfo.m_flLife = life;
	beamInfo.m_flWidth = width;
	beamInfo.m_flEndWidth = endWidth;
	beamInfo.m_flFadeLength = fadeLength;
	beamInfo.m_flAmplitude = amplitude;
	beamInfo.m_flBrightness = brightness;
	beamInfo.m_flSpeed = speed;
	beamInfo.m_nStartFrame = startFrame;
	beamInfo.m_flFrameRate = framerate;
	beamInfo.m_flRed = r;
	beamInfo.m_flGreen = g;
	beamInfo.m_flBlue = b;

	CreateBeamPoints( beamInfo );
}

//-----------------------------------------------------------------------------
// Purpose: Creates a beam between two points.
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::CreateBeamPoints( BeamInfo_t &beamInfo )
{
	// Don't start temporary beams out of the PVS
	if ( beamInfo.m_flLife != 0 && !CullBeam( beamInfo.m_vecStart, beamInfo.m_vecEnd, 1 ) )
		return NULL;

	// Model index.
	if ( ( beamInfo.m_pszModelName ) && ( beamInfo.m_nModelIndex == -1 ) )
	{
		beamInfo.m_nModelIndex = modelinfo->GetModelIndex( beamInfo.m_pszModelName );
	}

	if ( ( beamInfo.m_pszHaloName ) && ( beamInfo.m_nHaloIndex == -1 ) )
	{
		beamInfo.m_nHaloIndex = modelinfo->GetModelIndex( beamInfo.m_pszHaloName );
	}

	// Create the new beam.
	Beam_t *pBeam = CreateGenericBeam( beamInfo );
	if ( !pBeam )
		return NULL;

	// Set beam initial state.
	SetBeamAttributes( pBeam, beamInfo );
	if ( beamInfo.m_flLife == 0 )
	{
		pBeam->flags |= FBEAM_FOREVER;
	}

	return pBeam;
}

//-----------------------------------------------------------------------------
// Purpose: Creates a circular beam between two points
// Input  : type - 
//			*start - 
//			*end - 
//			modelIndex - 
//			life - 
//			width - 
//			amplitude - 
//			brightness - 
//			speed - 
//			startFrame - 
//			framerate - 
//			r - 
//			g - 
//			b - 
// Output : Beam_t
//-----------------------------------------------------------------------------
void CViewRenderBeams::CreateBeamCirclePoints( int type, Vector& start, Vector& end, int modelIndex, int haloIndex, float haloScale, float life, float width, 
											   float endWidth, float fadeLength,float amplitude, float brightness, float speed, int startFrame, 
											   float framerate, float r, float g, float b )
{
	BeamInfo_t beamInfo;
	
	beamInfo.m_nType = type;
	beamInfo.m_vecStart = start;
	beamInfo.m_vecEnd = end;
	beamInfo.m_nModelIndex = modelIndex;
	beamInfo.m_nHaloIndex = haloIndex;
	beamInfo.m_flHaloScale = haloScale;
	beamInfo.m_flLife = life;
	beamInfo.m_flWidth = width;
	beamInfo.m_flEndWidth = endWidth;
	beamInfo.m_flFadeLength = fadeLength;
	beamInfo.m_flAmplitude = amplitude;
	beamInfo.m_flBrightness = brightness;
	beamInfo.m_flSpeed = speed;
	beamInfo.m_nStartFrame = startFrame;
	beamInfo.m_flFrameRate = framerate;
	beamInfo.m_flRed = r;
	beamInfo.m_flGreen = g;
	beamInfo.m_flBlue = b;

	CreateBeamCirclePoints( beamInfo );
}

//-----------------------------------------------------------------------------
// Purpose: Creates a circular beam between two points.
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::CreateBeamCirclePoints( BeamInfo_t &beamInfo )
{
	Beam_t *pBeam = CreateGenericBeam( beamInfo );
	if ( !pBeam )
		return NULL;

	pBeam->type = beamInfo.m_nType;

	SetBeamAttributes( pBeam, beamInfo );
	if ( beamInfo.m_flLife == 0 )
	{
		pBeam->flags |= FBEAM_FOREVER;
	}

	return pBeam;
}

//-----------------------------------------------------------------------------
// Purpose: Create a beam which follows an entity
// Input  : startEnt - 
//			modelIndex - 
//			life - 
//			width - 
//			r - 
//			g - 
//			b - 
//			brightness - 
// Output : Beam_t
//-----------------------------------------------------------------------------
void CViewRenderBeams::CreateBeamFollow( int startEnt, int modelIndex, int haloIndex, float haloScale, float life, float width, float endWidth, 
										 float fadeLength, float r, float g, float b, float brightness )
{
	BeamInfo_t beamInfo;

	beamInfo.m_pStartEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( startEnt ) );
	beamInfo.m_nStartAttachment = BEAMENT_ATTACHMENT( startEnt );
	beamInfo.m_nModelIndex = modelIndex;
	beamInfo.m_nHaloIndex = haloIndex;
	beamInfo.m_flHaloScale = haloScale;
	beamInfo.m_flLife = life;
	beamInfo.m_flWidth = width;
	beamInfo.m_flEndWidth = endWidth;
	beamInfo.m_flFadeLength = fadeLength;
	beamInfo.m_flBrightness = brightness;
	beamInfo.m_flRed = r;
	beamInfo.m_flGreen = g;
	beamInfo.m_flBlue = b;
	beamInfo.m_flAmplitude = life;

	CreateBeamFollow( beamInfo );
}

//-----------------------------------------------------------------------------
// Purpose: Create a beam which follows an entity.
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::CreateBeamFollow( BeamInfo_t &beamInfo )
{
	beamInfo.m_vecStart = vec3_origin;
	beamInfo.m_vecEnd = vec3_origin;
	beamInfo.m_flSpeed = 1.0f;
	Beam_t *pBeam = CreateGenericBeam( beamInfo );
	if ( !pBeam )
		return NULL;

	pBeam->type = TE_BEAMFOLLOW;
	pBeam->flags = FBEAM_STARTENTITY;
	pBeam->entity[0] = beamInfo.m_pStartEnt;
	pBeam->attachmentIndex[0] = beamInfo.m_nStartAttachment;

	beamInfo.m_flFrameRate = 1.0f;
	beamInfo.m_nStartFrame = 0;

	SetBeamAttributes( pBeam, beamInfo );

	UpdateBeam( pBeam, 0 );

	return pBeam;
}

//-----------------------------------------------------------------------------
// Purpose: Create a beam ring between two entities
// Input  : startEnt - 
//			endEnt - 
//			modelIndex - 
//			life - 
//			width - 
//			amplitude - 
//			brightness - 
//			speed - 
//			startFrame - 
//			framerate - 
//			startEnt - 
// Output : Beam_t
//-----------------------------------------------------------------------------
void CViewRenderBeams::CreateBeamRingPoint( const Vector& center, float start_radius, float end_radius, 
					   int modelIndex, int haloIndex, float haloScale, float life, float width, float endWidth, 
					   float fadeLength, float amplitude, float brightness, float speed, int startFrame, float framerate, 
					   float r, float g, float b, int nFlags )
{
	BeamInfo_t beamInfo;
	
	beamInfo.m_nModelIndex = modelIndex;
	beamInfo.m_nHaloIndex = haloIndex;
	beamInfo.m_flHaloScale = haloScale;
	beamInfo.m_flLife = life;
	beamInfo.m_flWidth = width;
	beamInfo.m_flEndWidth = endWidth;
	beamInfo.m_flFadeLength = fadeLength;
	beamInfo.m_flAmplitude = amplitude;
	beamInfo.m_flBrightness = brightness;
	beamInfo.m_flSpeed = speed;
	beamInfo.m_nStartFrame = startFrame;
	beamInfo.m_flFrameRate = framerate;
	beamInfo.m_flRed = r;
	beamInfo.m_flGreen = g;
	beamInfo.m_flBlue = b;
	beamInfo.m_vecCenter = center;
	beamInfo.m_flStartRadius = start_radius;
	beamInfo.m_flEndRadius = end_radius;
	beamInfo.m_nFlags = nFlags;

	CreateBeamRingPoint( beamInfo );
}

//-----------------------------------------------------------------------------
// Purpose: Create a beam ring between two entities
//   Input: beamInfo -
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::CreateBeamRingPoint( BeamInfo_t &beamInfo )
{
	// ??
	Vector endpos = beamInfo.m_vecCenter;

	beamInfo.m_vecStart = beamInfo.m_vecCenter;
	beamInfo.m_vecEnd = beamInfo.m_vecCenter;

	Beam_t *pBeam = CreateGenericBeam( beamInfo );
	if ( !pBeam )
		return NULL;

	pBeam->type = TE_BEAMRINGPOINT;
	pBeam->start_radius = beamInfo.m_flStartRadius;
	pBeam->end_radius = beamInfo.m_flEndRadius;
	pBeam->attachment[2] = beamInfo.m_vecCenter;

	SetBeamAttributes( pBeam, beamInfo );
	if ( beamInfo.m_flLife == 0 )
	{
		pBeam->flags |= FBEAM_FOREVER;
	}

	return pBeam;
}

//-----------------------------------------------------------------------------
// Purpose: Create a beam ring between two entities
// Input  : startEnt - 
//			endEnt - 
//			modelIndex - 
//			life - 
//			width - 
//			amplitude - 
//			brightness - 
//			speed - 
//			startFrame - 
//			framerate - 
//			startEnt - 
// Output : Beam_t
//-----------------------------------------------------------------------------
void CViewRenderBeams::CreateBeamRing( int startEnt, int endEnt, int modelIndex, int haloIndex, float haloScale, float life, float width, float endWidth, float fadeLength, 
					   float amplitude, float brightness, float speed, int startFrame, float framerate, 
					   float r, float g, float b, int flags )
{
	BeamInfo_t beamInfo;

	beamInfo.m_pStartEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( startEnt ) );
	beamInfo.m_nStartAttachment = BEAMENT_ATTACHMENT( startEnt );
	beamInfo.m_pEndEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( endEnt ) );
	beamInfo.m_nEndAttachment = BEAMENT_ATTACHMENT( endEnt );
	beamInfo.m_nModelIndex = modelIndex;
	beamInfo.m_nHaloIndex = haloIndex;
	beamInfo.m_flHaloScale = haloScale;
	beamInfo.m_flLife = life;
	beamInfo.m_flWidth = width;
	beamInfo.m_flEndWidth = endWidth;
	beamInfo.m_flFadeLength = fadeLength;
	beamInfo.m_flAmplitude = amplitude;
	beamInfo.m_flBrightness = brightness;
	beamInfo.m_flSpeed = speed;
	beamInfo.m_nStartFrame = startFrame;
	beamInfo.m_flFrameRate = framerate;
	beamInfo.m_flRed = r;
	beamInfo.m_flGreen = g;
	beamInfo.m_flBlue = b;
	beamInfo.m_nFlags = flags;

	CreateBeamRing( beamInfo );
}

//-----------------------------------------------------------------------------
// Purpose: Create a beam ring between two entities.
//   Input: beamInfo -
//-----------------------------------------------------------------------------
Beam_t *CViewRenderBeams::CreateBeamRing( BeamInfo_t &beamInfo )
{
	// Don't start temporary beams out of the PVS
	if ( beamInfo.m_flLife != 0 && 
		 ( !beamInfo.m_pStartEnt || beamInfo.m_pStartEnt->GetModel() == NULL || 
		   !beamInfo.m_pEndEnt || beamInfo.m_pEndEnt->GetModel() == NULL ) )
	{
		return NULL;
	}

	beamInfo.m_vecStart = vec3_origin;
	beamInfo.m_vecEnd = vec3_origin;
	Beam_t *pBeam = CreateGenericBeam( beamInfo );
	if ( !pBeam )
		return NULL;

	pBeam->type = TE_BEAMRING;
	pBeam->flags = FBEAM_STARTENTITY | FBEAM_ENDENTITY;
	pBeam->entity[0] = beamInfo.m_pStartEnt;
	pBeam->attachmentIndex[0] = beamInfo.m_nStartAttachment;
	pBeam->entity[1] = beamInfo.m_pEndEnt;
	pBeam->attachmentIndex[1] = beamInfo.m_nEndAttachment;

	SetBeamAttributes( pBeam, beamInfo );
	if ( beamInfo.m_flLife == 0 )
	{
		pBeam->flags |= FBEAM_FOREVER;
	}

	UpdateBeam( pBeam, 0 );

	return pBeam;
}

//-----------------------------------------------------------------------------
// Purpose: Free dead trails associated with beam
// Input  : **ppparticles - 
//-----------------------------------------------------------------------------
void CViewRenderBeams::FreeDeadTrails( BeamTrail_t **trail )
{
	BeamTrail_t *kill;
	BeamTrail_t *p;

	// kill all the ones hanging direcly off the base pointer
	for ( ;; ) 
	{
		kill = *trail;
		if (kill && kill->die < gpGlobals->curtime)
		{
			*trail = kill->next;
			kill->next = m_pFreeTrails;
			m_pFreeTrails = kill;
			continue;
		}
		break;
	}

	// kill off all the others
	for (p=*trail ; p ; p=p->next)
	{
		for ( ;; )
		{
			kill = p->next;
			if (kill && kill->die < gpGlobals->curtime)
			{
				p->next = kill->next;
				kill->next = m_pFreeTrails;
				m_pFreeTrails = kill;
				continue;
			}
			break;
		}
	}
}



//-----------------------------------------------------------------------------
// Updates beam state
//-----------------------------------------------------------------------------
void CViewRenderBeams::UpdateBeam( Beam_t *pbeam, float frametime )
{
	if ( pbeam->modelIndex < 0 )
	{
		pbeam->die = gpGlobals->curtime;
		return;
	}

	// if we are paused, force random numbers used by noise to generate the same value every frame
	if ( frametime == 0.0f )
	{
		beamRandom.SetSeed( (int)gpGlobals->curtime );
	}

	// If FBEAM_ONLYNOISEONCE is set, we don't want to move once we've first calculated noise
	if ( !(pbeam->flags & FBEAM_ONLYNOISEONCE ) )
	{
		pbeam->freq += frametime;
	}
	else
	{
		pbeam->freq += frametime * beamRandom.RandomFloat(1,2);
	}

	// OPTIMIZE: Do this every frame?
	// UNDONE: Do this differentially somehow?
	// Generate fractal noise
	pbeam->rgNoise[0] = 0;
	pbeam->rgNoise[NOISE_DIVISIONS] = 0;
	if ( pbeam->amplitude != 0 )
	{
		if ( !(pbeam->flags & FBEAM_ONLYNOISEONCE ) || !pbeam->m_bCalculatedNoise )
		{
			if ( pbeam->flags & FBEAM_SINENOISE )
			{
				SineNoise( pbeam->rgNoise, NOISE_DIVISIONS );
			}
			else
			{
				Noise( pbeam->rgNoise, NOISE_DIVISIONS, 1.0 );
			}

			pbeam->m_bCalculatedNoise = true;
		}
	}

	// update end points
	if ( pbeam->flags & (FBEAM_STARTENTITY|FBEAM_ENDENTITY) )
	{
		// Makes sure attachment[0] + attachment[1] are valid
		if (!RecomputeBeamEndpoints( pbeam ))
			return;

		// Compute segments from the new endpoints
		VectorSubtract( pbeam->attachment[1], pbeam->attachment[0], pbeam->delta );
		if ( pbeam->amplitude >= 0.50 )
			pbeam->segments = VectorLength( pbeam->delta ) * 0.25 + 3; // one per 4 pixels
		else
			pbeam->segments = VectorLength( pbeam->delta ) * 0.075 + 3; // one per 16 pixels
	}

	// Get position data for spline beam
	switch ( pbeam->type )
	{
	case TE_BEAMSPLINE:
		{
			// Why isn't attachment[0] being computed?
			for (int i=1; i < pbeam->numAttachments; i++)
			{
				if (!ComputeBeamEntPosition( pbeam->entity[i], pbeam->attachmentIndex[i], (pbeam->flags & FBEAM_USE_HITBOXES) != 0, pbeam->attachment[i] ))
				{
					// This should never happen, but if for some reason the attachment doesn't exist, 
					// as a safety measure copy in the location of the previous attachment point (rather than bailing)
					VectorCopy( pbeam->attachment[i-1], pbeam->attachment[i] );
				}
			}
		}
		break;

	case TE_BEAMRINGPOINT:
		{
			// 
			float dr = pbeam->end_radius - pbeam->start_radius;
			if ( dr != 0.0f )
			{
				float frac = 1.0f;
				// Go some portion of the way there based on life
				float remaining = pbeam->die - gpGlobals->curtime;
				if ( remaining < pbeam->life && pbeam->life > 0.0f )
				{
					frac = remaining / pbeam->life;
				}
				frac = min( 1.0f, frac );
				frac = max( 0.0f, frac );

				frac = 1.0f - frac;

				// Start pos
				Vector endpos = pbeam->attachment[ 2 ];
				endpos.x += ( pbeam->start_radius + frac * dr ) / 2.0f;
				Vector startpos = pbeam->attachment[ 2 ];
				startpos.x -= ( pbeam->start_radius + frac * dr ) / 2.0f;

				pbeam->attachment[ 0 ] = startpos;
				pbeam->attachment[ 1 ] = endpos;

				VectorSubtract( pbeam->attachment[1], pbeam->attachment[0], pbeam->delta );
				if (pbeam->amplitude >= 0.50)
					pbeam->segments = VectorLength( pbeam->delta ) * 0.25 + 3; // one per 4 pixels
				else
					pbeam->segments = VectorLength( pbeam->delta ) * 0.075 + 3; // one per 16 pixels

			}
		}
		break;

	case TE_BEAMPOINTS:
		// UNDONE: Build culling volumes for other types of beams
		if ( !CullBeam( pbeam->attachment[0], pbeam->attachment[1], 0 ) )
			return;
		break;
	}

	// update life cycle
	pbeam->t = pbeam->freq + (pbeam->die - gpGlobals->curtime);
	if (pbeam->t != 0)
	{
		pbeam->t = pbeam->freq / pbeam->t;
	}
	else
	{
		pbeam->t = 1.0f;
	}

	// ------------------------------------------
	// check for zero fadeLength (means no fade)
	// ------------------------------------------
	if (pbeam->fadeLength == 0)
	{
		Assert( pbeam->delta.IsValid() );
		pbeam->fadeLength = pbeam->delta.Length();
	}
}


//-----------------------------------------------------------------------------
// Purpose: Update beams created by temp entity system
//-----------------------------------------------------------------------------
void CViewRenderBeams::UpdateTempEntBeams( void )
{
	VPROF_("UpdateTempEntBeams", 2, VPROF_BUDGETGROUP_CLIENT_SIM, false, BUDGETFLAG_CLIENT);
	if ( !m_pActiveBeams )
		return;

	// Get frame time
	float frametime = gpGlobals->frametime;

	if ( frametime == 0.0f )
		return;

	// Draw temporary entity beams
	Beam_t* pPrev = 0;
	Beam_t* pNext;
	for ( Beam_t* pBeam = m_pActiveBeams; pBeam ; pBeam = pNext )
	{
		// Need to store the next one since we may delete this one
		pNext = pBeam->next;

		// Retire old beams
		if ( !(pBeam->flags & FBEAM_FOREVER) && 
			pBeam->die <= gpGlobals->curtime )
		{
			// Reset links
			if ( pPrev )
			{
				pPrev->next = pNext;
			}
			else
			{
				m_pActiveBeams = pNext;
			}

			// Free the beam
			BeamFree( pBeam );

			pBeam = NULL;
			continue;
		}

		// Update beam state
		UpdateBeam( pBeam, frametime );

		// Compute bounds for the beam
		pBeam->ComputeBounds();

		// Indicates the beam moved
		if ( pBeam->m_hRenderHandle != INVALID_CLIENT_RENDER_HANDLE )
		{
			ClientLeafSystem()->RenderableChanged( pBeam->m_hRenderHandle );
		}

		pPrev = pBeam;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Draw helper for beam follow beams
// Input  : *pbeam - 
//			frametime - 
//			*color - 
//-----------------------------------------------------------------------------
void CViewRenderBeams::DrawBeamFollow( const model_t* pSprite, Beam_t *pbeam, 
	int frame, int rendermode, float frametime, const float* color, float flHDRColorScale )
{
	BeamTrail_t		*particles;
	BeamTrail_t		*pnew;
	float			div;
	Vector			delta;
	
	Vector			screenLast;
	Vector			screen;

	FreeDeadTrails( &pbeam->trail );

	particles = pbeam->trail;
	pnew = NULL;

	div = 0;
	if ( pbeam->flags & FBEAM_STARTENTITY )
	{
		if (particles)
		{
			VectorSubtract( particles->org, pbeam->attachment[0], delta );
			div = VectorLength( delta );
			if (div >= 32 && m_pFreeTrails)
			{
				pnew = m_pFreeTrails;
				m_pFreeTrails = pnew->next;
			}
		}
		else if (m_pFreeTrails)
		{
			pnew = m_pFreeTrails;
			m_pFreeTrails = pnew->next;
			div = 0;
		}
	}

	if (pnew)
	{
		VectorCopy( pbeam->attachment[0], pnew->org );
		pnew->die = gpGlobals->curtime + pbeam->amplitude;
		VectorCopy( vec3_origin, pnew->vel );

		pbeam->die = gpGlobals->curtime + pbeam->amplitude;
		pnew->next = particles;
		pbeam->trail = pnew;
		particles = pnew;
	}

	if (!particles)
	{
		return;
	}
	if (!pnew && div != 0)
	{
		VectorCopy( pbeam->attachment[0], delta );
		debugoverlay->ScreenPosition( pbeam->attachment[0], screenLast );
		debugoverlay->ScreenPosition( particles->org, screen );
	}
	else if (particles && particles->next)
	{
		VectorCopy( particles->org, delta );
		debugoverlay->ScreenPosition( particles->org, screenLast );
		debugoverlay->ScreenPosition( particles->next->org, screen );
		particles = particles->next;
	}
	else
	{
		return;
	}

	// Draw it
	::DrawBeamFollow( pSprite, pbeam->trail, frame, rendermode, delta, screen, screenLast, 
		pbeam->die, pbeam->attachment[0], pbeam->flags, pbeam->width, 
		pbeam->amplitude, pbeam->freq, (float*)color );
	
	// Drift popcorn trail if there is a velocity
	particles = pbeam->trail;
	while (particles)
	{
		VectorMA( particles->org, frametime, particles->vel, particles->org );
		particles = particles->next;
	}
}

//------------------------------------------------------------------------------
// Purpose : Draw beam with a halo
// Input   :
// Output  :
//------------------------------------------------------------------------------
void CViewRenderBeams::DrawBeamWithHalo(	Beam_t*			pbeam, 
											int				frame,
											int				rendermode,
											float*			color,
											float*			srcColor,
											const model_t	*sprite,
											const model_t	*halosprite,
											float			flHDRColorScale )
{
	Vector beamDir = pbeam->attachment[1] - pbeam->attachment[0];
	VectorNormalize( beamDir );
	
	Vector localDir = CurrentViewOrigin() - pbeam->attachment[0];
	VectorNormalize( localDir );
	
	float dotpr = DotProduct( beamDir, localDir );
	float fade;

	if ( dotpr < 0.0f )
	{
		fade = 0;
	}
	else
	{
		fade = dotpr * 2.0f;
	}

	float	distToLine;
	Vector	out;

	// Find out how close we are to the "line" of the spotlight
	CalcClosestPointOnLine( CurrentViewOrigin(), pbeam->attachment[0], pbeam->attachment[0] + ( beamDir * 2 ), out, &distToLine );

	distToLine = ( CurrentViewOrigin() - out ).Length();

	float scaleColor[4];
	float dotScale = 1.0f;
	
	// Use beam width
	float distThreshold = pbeam->width * 4.0f;

	if ( distToLine < distThreshold )
	{
		dotScale = RemapVal( distToLine, distThreshold, pbeam->width, 1.0f, 0.0f );
		dotScale = clamp( dotScale, 0, 1 );
	}

	scaleColor[0] = color[0] * dotScale;
	scaleColor[1] = color[1] * dotScale;
	scaleColor[2] = color[2] * dotScale;
	scaleColor[3] = color[3] * dotScale;

	if( pbeam->flags & FBEAM_HALOBEAM )
	{
		DrawSegs( NOISE_DIVISIONS, pbeam->rgNoise, sprite, frame, rendermode, pbeam->attachment[0],
			pbeam->delta, pbeam->width, pbeam->endWidth, pbeam->amplitude, pbeam->freq, pbeam->speed,
			pbeam->segments, pbeam->flags, scaleColor, pbeam->fadeLength, flHDRColorScale );
	}
	else
	{
		// Draw primary beam just shy of its end so it doesn't clip
		DrawSegs( NOISE_DIVISIONS, pbeam->rgNoise, sprite, frame, rendermode, pbeam->attachment[0], 
			pbeam->delta, pbeam->width, pbeam->width, pbeam->amplitude, pbeam->freq, pbeam->speed, 
			2, pbeam->flags, scaleColor, pbeam->fadeLength, flHDRColorScale );
	}

	Vector vSource = pbeam->attachment[0];

	pixelvis_queryparams_t params;
	params.Init( vSource, pbeam->m_haloProxySize );

	float haloFractionVisible = PixelVisibility_FractionVisible( params, pbeam->m_queryHandleHalo );
	if ( fade && haloFractionVisible > 0.0f )
	{
		//NOTENOTE: This is kinda funky when moving away and to the backside -- jdw
		float haloScale = RemapVal( distToLine, distThreshold, pbeam->width*0.5f, 1.0f, 2.0f );

		haloScale = clamp( haloScale, 1.0f, 2.0f );

		haloScale *= pbeam->haloScale;
		
		float colorFade = fade*fade;
		colorFade = clamp( colorFade, 0, 1 );

		float haloColor[3];
		VectorScale( srcColor, colorFade * haloFractionVisible, haloColor );

		BeamDrawHalo( halosprite, frame, kRenderGlow, vSource, haloScale, haloColor, flHDRColorScale );
	}
}


//------------------------------------------------------------------------------
// Purpose : Draw a beam based upon the viewpoint
//------------------------------------------------------------------------------
void CViewRenderBeams::DrawLaser( Beam_t *pbeam, int frame, int rendermode, float *color, const model_t *sprite, const model_t *halosprite, float flHDRColorScale )
{
	float	color2[3];
	VectorCopy( color, color2 );

	Vector vecForward;
	Vector	beamDir	= pbeam->attachment[1] - pbeam->attachment[0];
	VectorNormalize( beamDir );
	AngleVectors( CurrentViewAngles(), &vecForward );
	float flDot = DotProduct(beamDir, vecForward);

	// abort if the player's looking along it away from the source
	if ( flDot > 0 )
	{
		return;
	}
	else
	{
		// Fade the beam if the player's not looking at the source
		float flFade = pow( flDot, 10 );

		// Fade the beam based on the player's proximity to the beam
		Vector localDir = CurrentViewOrigin() - pbeam->attachment[0];
		flDot = DotProduct( beamDir, localDir );
		Vector vecProjection = flDot * beamDir;
		float flDistance = ( localDir - vecProjection ).Length();

		if ( flDistance > 30 )
		{
			flDistance = 1 - ((flDistance - 30) / 64);
			if ( flDistance <= 0 )
			{
				flFade = 0;
			}
			else
			{
				flFade *= pow( flDistance, 3 );
			}
		}

		if (flFade < (1.0f / 255.0f))
			return;

		VectorScale( color2, flFade, color2 );

		//engine->Con_NPrintf( 6, "Fade: %f", flFade );
		//engine->Con_NPrintf( 7, "Dist: %f", flDistance );
	}

	DrawSegs( NOISE_DIVISIONS, pbeam->rgNoise, sprite, frame, rendermode, pbeam->attachment[0], pbeam->delta, pbeam->width, pbeam->endWidth, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments, pbeam->flags, color2, pbeam->fadeLength);
}

//------------------------------------------------------------------------------
// Purpose : Draw a fibrous tesla beam
//------------------------------------------------------------------------------
void CViewRenderBeams::DrawTesla( Beam_t *pbeam, int frame, int rendermode, float *color, const model_t *sprite, float flHDRColorScale )
{
	DrawTeslaSegs( NOISE_DIVISIONS, pbeam->rgNoise, sprite, frame, rendermode, pbeam->attachment[0], pbeam->delta, pbeam->width, pbeam->endWidth, pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments, pbeam->flags, color, pbeam->fadeLength, flHDRColorScale );
}

//-----------------------------------------------------------------------------
// Purpose: Draw all beam entities
// Input  : *pbeam - 
//			frametime - 
//-----------------------------------------------------------------------------
void CViewRenderBeams::DrawBeam( Beam_t *pbeam )
{
	Assert( pbeam->delta.IsValid() );

	if ( !r_DrawBeams.GetInt() )
		return;

	// Don't draw really short beams
	if (pbeam->delta.Length() < 0.1)
	{
		return;
	}

	const model_t	*sprite;
	const model_t	*halosprite = NULL;

	if ( pbeam->modelIndex < 0 )
	{
		pbeam->die = gpGlobals->curtime;
		return;
	}
	
	sprite = modelinfo->GetModel( pbeam->modelIndex );
	if ( !sprite )
	{
		return;
	}
	
	halosprite = modelinfo->GetModel( pbeam->haloIndex );

	int frame = ( ( int )( pbeam->frame + gpGlobals->curtime * pbeam->frameRate) % pbeam->frameCount );
	int rendermode = ( pbeam->flags & FBEAM_SOLID ) ? kRenderNormal : kRenderTransAdd;

	// set color
	float srcColor[3];
	float color[3];

	srcColor[0] = pbeam->r;
	srcColor[1] = pbeam->g;
	srcColor[2] = pbeam->b;
	if ( pbeam->flags & FBEAM_FADEIN )
	{
		VectorScale( srcColor, pbeam->t, color );
	}
	else if ( pbeam->flags & FBEAM_FADEOUT )
	{
		VectorScale( srcColor, ( 1.0f - pbeam->t ), color );
	}
	else
	{
		VectorCopy( srcColor, color );
	}

	VectorScale( color, (1/255.0), color );
	VectorCopy( color, srcColor );
	VectorScale( color, ((float)pbeam->brightness / 255.0), color );

	switch( pbeam->type )
	{
	case TE_BEAMDISK:
		DrawDisk( NOISE_DIVISIONS, pbeam->rgNoise, sprite, frame, rendermode, 
			pbeam->attachment[0], pbeam->delta, pbeam->width, pbeam->amplitude, 
			pbeam->freq, pbeam->speed, pbeam->segments, color, pbeam->m_flHDRColorScale );
		break;

	case TE_BEAMCYLINDER:
		DrawCylinder( NOISE_DIVISIONS, pbeam->rgNoise, sprite, frame, rendermode, 
			pbeam->attachment[0], pbeam->delta, pbeam->width, pbeam->amplitude, 
			pbeam->freq, pbeam->speed, pbeam->segments, color, pbeam->m_flHDRColorScale );
		break;

	case TE_BEAMPOINTS:
		if (halosprite)
		{	
			DrawBeamWithHalo( pbeam, frame, rendermode, color, srcColor, sprite, halosprite, pbeam->m_flHDRColorScale );
		}
		else
		{
			DrawSegs( NOISE_DIVISIONS, pbeam->rgNoise, sprite, frame, rendermode, 
				pbeam->attachment[0], pbeam->delta, pbeam->width, pbeam->endWidth, 
				pbeam->amplitude, pbeam->freq, pbeam->speed, pbeam->segments, 
				pbeam->flags, color, pbeam->fadeLength, pbeam->m_flHDRColorScale );
		}
		break;

	case TE_BEAMFOLLOW:
		DrawBeamFollow( sprite, pbeam, frame, rendermode, gpGlobals->frametime, color, pbeam->m_flHDRColorScale );
		break;

	case TE_BEAMRING:
	case TE_BEAMRINGPOINT:
		DrawRing( NOISE_DIVISIONS, pbeam->rgNoise, Noise, sprite, frame, rendermode, 
			pbeam->attachment[0], pbeam->delta, pbeam->width, pbeam->amplitude, 
			pbeam->freq, pbeam->speed, pbeam->segments, color, pbeam->m_flHDRColorScale );
		break;

	case TE_BEAMSPLINE:
		DrawSplineSegs( NOISE_DIVISIONS, pbeam->rgNoise, sprite, halosprite,
			pbeam->haloScale, frame, rendermode, pbeam->numAttachments,
			pbeam->attachment, pbeam->width, pbeam->endWidth, pbeam->amplitude,
			pbeam->freq, pbeam->speed, pbeam->segments, pbeam->flags, color, pbeam->fadeLength, pbeam->m_flHDRColorScale );
		break;

	case TE_BEAMLASER:
		DrawLaser( pbeam, frame, rendermode, color, sprite, halosprite, pbeam->m_flHDRColorScale );
		break;

	case TE_BEAMTESLA:
		DrawTesla( pbeam, frame, rendermode, color, sprite, pbeam->m_flHDRColorScale );
		break;

	default:
		DevWarning( 1, "CViewRenderBeams::DrawBeam:  Unknown beam type %i\n", pbeam->type );
		break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Update the beam 
//-----------------------------------------------------------------------------
void CViewRenderBeams::UpdateBeamInfo( Beam_t *pBeam, BeamInfo_t &beamInfo )
{
	pBeam->attachment[0] = beamInfo.m_vecStart;
	pBeam->attachment[1] = beamInfo.m_vecEnd;
	pBeam->delta = beamInfo.m_vecEnd - beamInfo.m_vecStart;

	Assert( pBeam->delta.IsValid() );

	SetBeamAttributes( pBeam, beamInfo );
}


//-----------------------------------------------------------------------------
// Recomputes beam endpoints..
//-----------------------------------------------------------------------------
bool CViewRenderBeams::RecomputeBeamEndpoints( Beam_t *pbeam )
{
	if ( pbeam->flags & FBEAM_STARTENTITY )
	{
		if (ComputeBeamEntPosition( pbeam->entity[0], pbeam->attachmentIndex[0], (pbeam->flags & FBEAM_USE_HITBOXES) != 0, pbeam->attachment[0] ))
		{
			pbeam->flags |= FBEAM_STARTVISIBLE;
		}
		else if (! (pbeam->flags & FBEAM_FOREVER))
		{
			pbeam->flags &= ~(FBEAM_STARTENTITY);
		}
		else
		{
			// DevWarning( 1,"can't find start entity\n");
//			return false;
		}

		// If we've never seen the start entity, don't display
		if ( !(pbeam->flags & FBEAM_STARTVISIBLE) )
			return false;
	}

	if ( pbeam->flags & FBEAM_ENDENTITY )
	{
		if (ComputeBeamEntPosition( pbeam->entity[1], pbeam->attachmentIndex[1], (pbeam->flags & FBEAM_USE_HITBOXES) != 0, pbeam->attachment[1] ))
		{
			pbeam->flags |= FBEAM_ENDVISIBLE;
		}
		else if (! (pbeam->flags & FBEAM_FOREVER))
		{
			pbeam->flags &= ~(FBEAM_ENDENTITY);
			pbeam->die = gpGlobals->curtime;
			return false;
		}
		else
		{
			return false;
		}

		// If we've never seen the end entity, don't display
		if ( !(pbeam->flags & FBEAM_ENDVISIBLE) )
			return false;
	}

	return true;
}

#ifdef PORTAL
	bool bBeamDrawingThroughPortal = false;
#endif

//-----------------------------------------------------------------------------
// Draws a single beam
//-----------------------------------------------------------------------------
void CViewRenderBeams::DrawBeam( C_Beam* pbeam, ITraceFilter *pEntityBeamTraceFilter )
{
	Beam_t beam;

	// Set up the beam.
	int beamType = pbeam->GetType();

	BeamInfo_t beamInfo;
	beamInfo.m_vecStart = pbeam->GetAbsStartPos();
	beamInfo.m_vecEnd = pbeam->GetAbsEndPos();
	beamInfo.m_pStartEnt = beamInfo.m_pEndEnt = NULL;
	beamInfo.m_nModelIndex = pbeam->GetModelIndex();
	beamInfo.m_nHaloIndex = pbeam->m_nHaloIndex;
	beamInfo.m_flHaloScale = pbeam->m_fHaloScale;
	beamInfo.m_flLife = 0;
	beamInfo.m_flWidth = pbeam->GetWidth();
	beamInfo.m_flEndWidth = pbeam->GetEndWidth();
	beamInfo.m_flFadeLength = pbeam->GetFadeLength();
	beamInfo.m_flAmplitude = pbeam->GetNoise();
	beamInfo.m_flBrightness = pbeam->GetFxBlend();
	beamInfo.m_flSpeed = pbeam->GetScrollRate();

#ifdef PORTAL	// Beams need to recursively draw through portals
	// Trace to see if we've intersected a portal
	float fEndFraction;
	Ray_t rayBeam;

	bool bIsReversed = ( pbeam->GetBeamFlags() & FBEAM_REVERSED ) != 0x0;

	Vector vRayStartPoint, vRayEndPoint;

	vRayStartPoint = beamInfo.m_vecStart;
	vRayEndPoint = beamInfo.m_vecEnd;

	if ( beamType == BEAM_ENTPOINT || beamType == BEAM_ENTS || beamType == BEAM_LASER )
	{
		ComputeBeamEntPosition( pbeam->m_hAttachEntity[0], pbeam->m_nAttachIndex[0], false, vRayStartPoint );
		ComputeBeamEntPosition( pbeam->m_hAttachEntity[1], pbeam->m_nAttachIndex[1], false, vRayEndPoint );
	}

	if ( !bIsReversed )
		rayBeam.Init( vRayStartPoint, vRayEndPoint );
	else
		rayBeam.Init( vRayEndPoint, vRayStartPoint );

	CBaseEntity *pStartEntity = pbeam->GetStartEntityPtr();

	CTraceFilterSkipClassname traceFilter( pStartEntity, "prop_energy_ball", COLLISION_GROUP_NONE );

	if ( !pEntityBeamTraceFilter && pStartEntity )
		pEntityBeamTraceFilter = pStartEntity->GetBeamTraceFilter();

	CTraceFilterChain traceFilterChain( &traceFilter, pEntityBeamTraceFilter );

	C_Prop_Portal *pPortal = UTIL_Portal_TraceRay_Beam( rayBeam, MASK_SHOT, &traceFilterChain, &fEndFraction );

	// Get the point that we hit a portal or wall
	Vector vEndPoint = rayBeam.m_Start + rayBeam.m_Delta * fEndFraction;

	if ( pPortal )
	{
		// Prevent infinite recursion by lower the brightness each call
		int iOldBrightness = pbeam->GetBrightness();

		if ( iOldBrightness > 16 )
		{
			// Remember the old values of the beam before changing it for the next call
			Vector vOldStart = pbeam->GetAbsStartPos();
			Vector vOldEnd = pbeam->GetAbsEndPos();
			//float fOldWidth = pbeam->GetEndWidth();
			C_BaseEntity *pOldStartEntity = pbeam->GetStartEntityPtr();
			C_BaseEntity *pOldEndEntity = pbeam->GetEndEntityPtr();
			int iOldStartAttachement = pbeam->GetStartAttachment();
			int iOldEndAttachement = pbeam->GetEndAttachment();
			int iOldType = pbeam->GetType();

			// Get the transformed positions of the sub beam in the other portal's space
			Vector vTransformedStart, vTransformedEnd;
			VMatrix matThisToLinked = pPortal->MatrixThisToLinked();
			UTIL_Portal_PointTransform( matThisToLinked, vEndPoint, vTransformedStart );
			UTIL_Portal_PointTransform( matThisToLinked, rayBeam.m_Start + rayBeam.m_Delta, vTransformedEnd );

			// Set up the sub beam for the next call
			pbeam->SetBrightness( iOldBrightness - 16 );
			if ( bIsReversed )
				pbeam->PointsInit( vTransformedEnd, vTransformedStart );
			else
				pbeam->PointsInit( vTransformedStart, vTransformedEnd );
			if ( bIsReversed )
				pbeam->SetEndWidth( pbeam->GetWidth() );
			pbeam->SetStartEntity( pPortal->m_hLinkedPortal );

			// Draw the sub beam
			bBeamDrawingThroughPortal = true;
			DrawBeam( pbeam, pEntityBeamTraceFilter );
			bBeamDrawingThroughPortal = true;

			// Restore the original values
			pbeam->SetBrightness( iOldBrightness );
			pbeam->SetStartPos( vOldStart );
			pbeam->SetEndPos( vOldEnd );
			//if ( bIsReversed )
			//	pbeam->SetEndWidth( fOldWidth );
			if ( pOldStartEntity )
				pbeam->SetStartEntity( pOldStartEntity );
			if ( pOldEndEntity )
				pbeam->SetEndEntity( pOldEndEntity );
			pbeam->SetStartAttachment( iOldStartAttachement );
			pbeam->SetEndAttachment( iOldEndAttachement );
			pbeam->SetType( iOldType );

			// Doesn't use a hallow or taper the beam because we recursed
			beamInfo.m_nHaloIndex = 0;
			if ( !bIsReversed )
				beamInfo.m_flEndWidth = beamInfo.m_flWidth;
		}
	}

	// Clip to the traced end point (portal or wall)
	if ( bBeamDrawingThroughPortal )
	{
		if ( bIsReversed )
			beamInfo.m_vecStart = vEndPoint;
		else
			beamInfo.m_vecEnd = vEndPoint;
	}

	bBeamDrawingThroughPortal = false;
#endif

	SetupBeam( &beam, beamInfo );

	beamInfo.m_nStartFrame = pbeam->m_fStartFrame;
	beamInfo.m_flFrameRate = pbeam->m_flFrameRate;
	beamInfo.m_flRed = pbeam->m_clrRender->r;
	beamInfo.m_flGreen = pbeam->m_clrRender->g;
	beamInfo.m_flBlue = pbeam->m_clrRender->b;

	SetBeamAttributes( &beam, beamInfo );

	if ( pbeam->m_nHaloIndex > 0 )
	{
		// HACKHACK: heuristic to estimate proxy size.  Revisit this!
		float size = 1.0f + (pbeam->m_fHaloScale * pbeam->m_fWidth / pbeam->m_fEndWidth);
		size = clamp( size, 1.0f, 8.0f );
		beam.m_queryHandleHalo = &pbeam->m_queryHandleHalo;
		beam.m_haloProxySize = size;
	}
	else
	{
		beam.m_queryHandleHalo = NULL;
	}

	// Handle code from relinking.
	switch( beamType )
	{
		case BEAM_ENTS:
		{
			beam.type			= TE_BEAMPOINTS;
			beam.flags			= FBEAM_STARTENTITY | FBEAM_ENDENTITY;
			beam.entity[0]		= pbeam->m_hAttachEntity[0];
			beam.attachmentIndex[0]	= pbeam->m_nAttachIndex[0];
			beam.entity[1]		= pbeam->m_hAttachEntity[1];
			beam.attachmentIndex[1]	= pbeam->m_nAttachIndex[1];
			beam.numAttachments	= pbeam->m_nNumBeamEnts;
			break;
		}
		case BEAM_LASER:
		{
			beam.type			= TE_BEAMLASER;
			beam.flags			= FBEAM_STARTENTITY | FBEAM_ENDENTITY;
			beam.entity[0]		= pbeam->m_hAttachEntity[0];
			beam.attachmentIndex[0]	= pbeam->m_nAttachIndex[0];
			beam.entity[1]		= pbeam->m_hAttachEntity[1];
			beam.attachmentIndex[1]	= pbeam->m_nAttachIndex[1];
			beam.numAttachments	= pbeam->m_nNumBeamEnts;
			break;
		}
		case BEAM_SPLINE:
		{
			beam.type			= TE_BEAMSPLINE;
			beam.flags			= FBEAM_STARTENTITY | FBEAM_ENDENTITY;
			beam.numAttachments	= pbeam->m_nNumBeamEnts;
			for (int i=0;i<beam.numAttachments;i++)
			{
				beam.entity[i]		= pbeam->m_hAttachEntity[i];
				beam.attachmentIndex[i]	= pbeam->m_nAttachIndex[i];
			}
			break;
		}
		case BEAM_ENTPOINT:
		{
			beam.type			= TE_BEAMPOINTS;
			beam.flags = 0;
			beam.entity[0]		= pbeam->m_hAttachEntity[0];
			beam.attachmentIndex[0]	= pbeam->m_nAttachIndex[0];
			beam.entity[1]		= pbeam->m_hAttachEntity[1];
			beam.attachmentIndex[1]	= pbeam->m_nAttachIndex[1];
			if ( beam.entity[0].Get() )
			{
				beam.flags |= FBEAM_STARTENTITY;
			}
			if ( beam.entity[1].Get() )
			{
				beam.flags |= FBEAM_ENDENTITY;
			}
			beam.numAttachments	= pbeam->m_nNumBeamEnts;
			break;
		}
		case BEAM_POINTS:
			// Already set up
			break;
	}

	beam.flags |= pbeam->GetBeamFlags() & (FBEAM_SINENOISE|FBEAM_SOLID|FBEAM_SHADEIN|FBEAM_SHADEOUT|FBEAM_NOTILE);

	if ( beam.entity[0] )
	{
		// don't draw viewmodel effects in reflections
		if ( CurrentViewID() == VIEW_REFLECTION )
		{
			int group = beam.entity[0]->GetRenderGroup();
			if (group == RENDER_GROUP_VIEW_MODEL_TRANSLUCENT || group == RENDER_GROUP_VIEW_MODEL_OPAQUE)
				return;
		}
	}

	beam.m_flHDRColorScale = pbeam->GetHDRColorScale();

	// Draw it
	UpdateBeam( &beam, gpGlobals->frametime );
	DrawBeam( &beam );
}
