You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2285 lines
63 KiB
2285 lines
63 KiB
//===== 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 ); |
|
}
|
|
|