csgo-2018-source/game/client/view_beams.cpp
2021-07-24 21:11:47 -07:00

2273 lines
63 KiB
C++

//===== 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"
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;
}
//-----------------------------------------------------------------------------
// 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, const RenderableInstance_t &instance, 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, C_Beam *pcbeam = NULL );
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 );
// special case clipping to geometry behavior
void ClipBeam( C_Beam *pcbeam, Beam_t *pbeamt );
// 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
{
// default max # of particles at one time
DEFAULT_PARTICLES = 2048,
// no fewer than this no matter what's on the command line
MIN_PARTICLES = 512,
// Maximum length of the free list.
BEAM_FREELIST_MAX = 32
};
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 )
{
if ( pEnt->GetAttachment( nAttachment, pt ) )
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 ) )
{
int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
mstudiobbox_t *pHitbox = set->pHitbox( nAttachment - 1 );
Vector vecViewPt = MainViewOrigin(nSlot);
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()
{
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;
}
extern bool g_bRenderingScreenshot;
extern ConVar r_drawviewmodel;
int Beam_t::DrawModel( int flags, const RenderableInstance_t &instance )
{
// 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 = ToBaseViewModel(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, false, RENDERABLE_IS_TRANSLUCENT, RENDERABLE_MODEL_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 = beamInfo.m_nFlags;
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, C_Beam *pcbeam )
{
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;
// clip if requested
if ( pcbeam && pcbeam->GetClipStyle() != CBeam::kNOCLIP )
{
ClipBeam( pcbeam, pbeam );
}
// 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[3];
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;
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( color, 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;
}
#include "debugoverlay_shared.h"
#ifdef VPROF_ENABLED
ConVar cl_beam_test_traces( "cl_beam_test_traces", "0", FCVAR_DEVELOPMENTONLY, "Enable debug overlay on traces that determine where the client-side visible env_beam is drawn. Has no bearing on the server-side damage-causing part of the beam." );
static inline bool BeamDebugOverlay() { return cl_beam_test_traces.GetBool(); }
#else
static inline bool BeamDebugOverlay() { return false; }
#endif
void CViewRenderBeams::ClipBeam( C_Beam * RESTRICT pcbeam, Beam_t * RESTRICT pbeam )
{
// Assert( pbeam->GetClipStyle() != C_Beam::kNOCLIP );
int colmask = 0;
int colgroup = COLLISION_GROUP_NONE;
switch ( pcbeam->GetClipStyle() )
{
case C_Beam::kGEOCLIP:
colmask = CONTENTS_SOLID; // lasers go through gates and windows.
break;
case C_Beam::kMODELCLIP:
colmask = MASK_SOLID;
break;
default:
AssertMsg1(false, "Unknown beam clipping type %d\n", pcbeam->GetClipStyle() );
return;
}
trace_t tr;
Vector &vstart = pbeam->attachment[0];
Vector &vend = pbeam->attachment[1];
// start the trace from a few inches ahead of the start position (in case of coplanarity)
// use a fast estimated normalize ( i'll push this into mathlib later )
Vector delta = vend - vstart;
delta *= 8.0f * FastRSqrtFast((delta).LengthSqr()) ;
if ( BeamDebugOverlay() )
{
NDebugOverlay::Line( vstart + delta, vend, 255, 255, 0, true, 0.2f );
}
UTIL_TraceLine( vstart + delta , vend, colmask, NULL, colgroup, &tr );
if ( tr.fraction < 1.0f )
{
// move the endpoint to wherever the trace stopped
// if ( test_spam.GetBool() ) Msg( "(%s) %s\n", tr.startsolid ? "x" : " ", tr.m_pEnt->GetDebugName() );
if ( BeamDebugOverlay() )
NDebugOverlay::Cross( tr.endpos, 8, 255, 255, 0, false, 0.2f );
vend = tr.endpos;
}
}
//-----------------------------------------------------------------------------
// Draws a single beam
//-----------------------------------------------------------------------------
void CViewRenderBeams::DrawBeam( C_Beam* pbeam, const RenderableInstance_t &instance, ITraceFilter *pEntityBeamTraceFilter )
{
if ( !r_DrawBeams.GetInt() )
return;
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 = instance.m_nAlpha * (pbeam->GetRenderAlpha() / 255.0f);
beamInfo.m_flSpeed = pbeam->GetScrollRate();
beamInfo.m_nFlags = pbeam->GetBeamFlags();
if ( pbeam->GetBeamFlags() & FBEAM_REVERSED )
{
V_swap( beamInfo.m_vecStart, beamInfo.m_vecEnd );
V_swap( beamInfo.m_flWidth, beamInfo.m_flEndWidth );
}
SetupBeam( &beam, beamInfo );
beamInfo.m_nStartFrame = pbeam->m_fStartFrame;
beamInfo.m_flFrameRate = pbeam->m_flFrameRate;
beamInfo.m_flRed = pbeam->GetRenderColorR();
beamInfo.m_flGreen = pbeam->GetRenderColorG();
beamInfo.m_flBlue = pbeam->GetRenderColorB();
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 )
{
if ( g_pClientLeafSystem->IsRenderingWithViewModels( beam.entity[0]->RenderHandle() ) )
return;
}
}
beam.m_flHDRColorScale = pbeam->GetHDRColorScale();
// per-frame update
UpdateBeam( &beam, gpGlobals->frametime, pbeam );
// draw!
DrawBeam( &beam );
}