2286 lines
65 KiB
C++
2286 lines
65 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
|
|
{
|
|
|
|
#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 ) )
|
|
{
|
|
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 = 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, 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[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;
|
|
}
|
|
|
|
|
|
|
|
#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 )
|
|
{
|
|
|
|
AssertMsg( pEntityBeamTraceFilter == NULL, "pEntityBeamTraceFilter is only meaningful in Portal!" );
|
|
|
|
|
|
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;
|
|
beamInfo.m_flSpeed = pbeam->GetScrollRate();
|
|
|
|
|
|
|
|
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 );
|
|
}
|