//===== 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;iParmValue("-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 ;inext; 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;im_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 ); }