//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Client's sheild entity // // $Workfile: $ // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "C_Shield.h" #include "clienteffectprecachesystem.h" #include "clientmode.h" #include "materialsystem/imesh.h" #include "mapdata.h" #include "ivrenderview.h" #include "tf_shareddefs.h" #include "collisionutils.h" #include "functionproxy.h" // Precache the effects CLIENTEFFECT_REGISTER_BEGIN( Shield ) CLIENTEFFECT_MATERIAL( "shadertest/wireframevertexcolor" ) CLIENTEFFECT_MATERIAL( "effects/shield/shield" ) CLIENTEFFECT_MATERIAL( "effects/shieldhit" ) CLIENTEFFECT_MATERIAL( "effects/shieldpass" ) CLIENTEFFECT_MATERIAL( "effects/shieldpass2" ) CLIENTEFFECT_REGISTER_END() //----------------------------------------------------------------------------- // Stores a list of all active shields //----------------------------------------------------------------------------- CUtlVector< C_Shield* > C_Shield::s_Shields; //----------------------------------------------------------------------------- // Various important constants: //----------------------------------------------------------------------------- #define SHIELD_DAMAGE_CHANGE_FIRST_PASS_TIME 0.3f #define SHIELD_DAMAGE_CHANGE_TRANSITION_TIME 0.5f #define SHIELD_DAMAGE_CHANGE_TRANSITION_START_TIME (SHIELD_DAMAGE_CHANGE_TIME - SHIELD_DAMAGE_CHANGE_TRANSITION_TIME) #define SHIELD_DAMAGE_CHANGE_TOTAL_TIME (SHIELD_DAMAGE_CHANGE_TRANSITION_START_TIME + SHIELD_DAMAGE_CHANGE_TRANSITION_TIME) #define SHIELD_TRANSITION_MAX_BLEND_AMT 0.2f //----------------------------------------------------------------------------- // Data table //----------------------------------------------------------------------------- //EXTERN_RECV_TABLE(DT_BaseEntity); IMPLEMENT_CLIENTCLASS_DT(C_Shield, DT_Shield, CShield) RecvPropInt( RECVINFO(m_nOwningPlayerIndex) ), RecvPropFloat( RECVINFO(m_flPowerLevel) ), RecvPropInt( RECVINFO(m_bIsEMPed) ), END_RECV_TABLE() //----------------------------------------------------------------------------- // Shield color for the various protection types //----------------------------------------------------------------------------- static unsigned char s_ImpactDecalColor[3] = { 0, 0, 255 }; // ---------------------------------------------------------------------------- // Functions. // ---------------------------------------------------------------------------- C_Shield::C_Shield() { m_pWireframe.Init( "shadertest/wireframevertexcolor", TEXTURE_GROUP_OTHER ); m_pShield.Init( "effects/shield/shield", TEXTURE_GROUP_CLIENT_EFFECTS ); m_pHitDecal.Init( "effects/shieldhit", TEXTURE_GROUP_CLIENT_EFFECTS ); m_pPassDecal.Init( "effects/shieldpass", TEXTURE_GROUP_CLIENT_EFFECTS ); m_pPassDecal2.Init( "effects/shieldpass2", TEXTURE_GROUP_CLIENT_EFFECTS ); m_FadeValue = 1.0f; m_CurveValue = 1.0f; m_bCollisionsActive = true; s_Shields.AddToTail(this); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- C_Shield::~C_Shield() { int i = s_Shields.Find(this); if ( i >= 0 ) { s_Shields.FastRemove(i); } } //----------------------------------------------------------------------------- // Inherited classes should call this in their constructor to indicate size... //----------------------------------------------------------------------------- void C_Shield::InitShield( int w, int h, int subdivisions ) { m_SplinePatch.Init( w, h, 2 ); m_SubdivisionCount = subdivisions; Assert( m_SubdivisionCount > 1 ); m_InvSubdivisionCount = 1.0f / (m_SubdivisionCount - 1); } //----------------------------------------------------------------------------- // This is called after a network update //----------------------------------------------------------------------------- void C_Shield::OnDataChanged( DataUpdateType_t updateType ) { if (updateType == DATA_UPDATE_CREATED) { m_StartTime = engine->GetLastTimeStamp(); } BaseClass::OnDataChanged( updateType ); } //----------------------------------------------------------------------------- // Purpose: // Input : collisionGroup - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool C_Shield::ShouldCollide( int collisionGroup, int contentsMask ) const { return m_bCollisionsActive && ((collisionGroup == TFCOLLISION_GROUP_WEAPON) || (collisionGroup == TFCOLLISION_GROUP_GRENADE)); } //----------------------------------------------------------------------------- // Should I draw? //----------------------------------------------------------------------------- bool C_Shield::ShouldDraw() { // Let the client mode (like commander mode) reject drawing entities. if (g_pClientMode && !g_pClientMode->ShouldDrawEntity(this) ) return false; return true; } //----------------------------------------------------------------------------- // Activates/deactivates a shield for collision purposes //----------------------------------------------------------------------------- void C_Shield::ActivateCollisions( bool activate ) { m_bCollisionsActive = activate; } //----------------------------------------------------------------------------- // Activates all shields //----------------------------------------------------------------------------- void C_Shield::ActivateShields( bool activate, int team ) { for (int i = s_Shields.Count(); --i >= 0; ) { // Activate all shields on the same team if ( (team == -1) || (team == s_Shields[i]->GetTeamNumber()) ) { s_Shields[i]->ActivateCollisions( activate ); } } } //----------------------------------------------------------------------------- // Helper method for collision testing //----------------------------------------------------------------------------- #pragma warning ( disable : 4701 ) bool C_Shield::TestCollision( const Ray_t& ray, unsigned int mask, trace_t& trace ) { // Can't block anything if we're EMPed, or we've got no power left to block if ( m_bIsEMPed ) return false; if ( m_flPowerLevel <= 0 ) return false; // Here, we're gonna test for collision. // If we don't stop this kind of bullet, we'll generate an effect here // but we won't change the trace to indicate a collision. // It's just polygon soup... int hitgroup; bool firstTri; int v1[2], v2[2], v3[2]; float ihit, jhit; float mint = FLT_MAX; float t; int h = Height(); int w = Width(); for (int i = 0; i < h - 1; ++i) { for (int j = 0; j < w - 1; ++j) { // Don't test if this panel ain't active... if (!IsPanelActive( j, i )) continue; // NOTE: Structure order of points so that our barycentric // axes for each triangle are along the (u,v) directions of the mesh // The barycentric coords we'll need below // Two triangles per quad... t = IntersectRayWithTriangle( ray, GetPoint( j, i + 1 ), GetPoint( j + 1, i + 1 ), GetPoint( j, i ), true ); if ((t >= 0.0f) && (t < mint)) { mint = t; v1[0] = j; v1[1] = i + 1; v2[0] = j + 1; v2[1] = i + 1; v3[0] = j; v3[1] = i; ihit = i; jhit = j; firstTri = true; } t = IntersectRayWithTriangle( ray, GetPoint( j + 1, i ), GetPoint( j, i ), GetPoint( j + 1, i + 1 ), true ); if ((t >= 0.0f) && (t < mint)) { mint = t; v1[0] = j + 1; v1[1] = i; v2[0] = j; v2[1] = i; v3[0] = j + 1; v3[1] = i + 1; ihit = i; jhit = j; firstTri = false; } } } if (mint == FLT_MAX) return false; // Stuff the barycentric coordinates of the triangle hit into the hit group // For the first triangle, the first edge goes along u, the second edge goes // along -v. For the second triangle, the first edge goes along -u, // the second edge goes along v. const Vector& v1vec = GetPoint(v1[0], v1[1]); const Vector& v2vec = GetPoint(v2[0], v2[1]); const Vector& v3vec = GetPoint(v3[0], v3[1]); float u, v; bool ok = ComputeIntersectionBarycentricCoordinates( ray, v1vec, v2vec, v3vec, u, v ); Assert( ok ); if ( !ok ) { return false; } if (firstTri) v = 1.0 - v; else u = 1.0 - u; v += ihit; u += jhit; v /= (h - 1); u /= (w - 1); // Compress (u,v) into 1 dot 15, v in top bits hitgroup = (((int)(v * (1 << 15))) << 16) + (int)(u * (1 << 15)); Vector normal; float intercept; ComputeTrianglePlane( v1vec, v2vec, v3vec, normal, intercept ); UTIL_SetTrace( trace, ray, this, mint, hitgroup, CONTENTS_SOLID, normal, intercept ); return true; } #pragma warning ( default : 4701 ) //----------------------------------------------------------------------------- // Called when we hit something that we deflect... //----------------------------------------------------------------------------- void C_Shield::RegisterDeflection(const Vector& vecDir, int bitsDamageType, trace_t *ptr) { Vector normalDir; VectorCopy( vecDir, normalDir ); VectorNormalize( normalDir ); CreateShieldDeflection( ptr->hitgroup, normalDir, false ); } //----------------------------------------------------------------------------- // This is required to get all the decals to animate correctly //----------------------------------------------------------------------------- void C_Shield::SetCurrentDecal( int idx ) { m_CurrentDecal = idx; } //----------------------------------------------------------------------------- // returns the address of a variable that stores the material animation frame //----------------------------------------------------------------------------- float C_Shield::GetTextureAnimationStartTime() { if( m_CurrentDecal == -1 ) return m_StartTime; return m_Decals[m_CurrentDecal].m_StartTime; } //----------------------------------------------------------------------------- // Indicates that a texture animation has wrapped //----------------------------------------------------------------------------- void C_Shield::TextureAnimationWrapped() { if( m_CurrentDecal != -1 ) { m_Decals[m_CurrentDecal].m_StartTime = -1.0f; } } //----------------------------------------------------------------------------- // Indicates a collision occurred: //----------------------------------------------------------------------------- void C_Shield::ReceiveMessage( int classID, bf_read &msg ) { if ( classID != GetClientClass()->m_ClassID ) { // message is for subclass BaseClass::ReceiveMessage( classID, msg ); return; } int hitgroup; Vector dir; unsigned char partialBlock; hitgroup = msg.ReadLong( ); msg.ReadBitVec3Normal( dir ); partialBlock = msg.ReadByte( ); CreateShieldDeflection( hitgroup, dir, partialBlock ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_Shield::CreateShieldDeflection( int hitgroup, const Vector &dir, bool partialBlock ) { float hitU = (float)(hitgroup & 0xFFFF) / (float)(1 << 15); float hitV = (float)(hitgroup >> 16) / (float)(1 << 15); Ripple_t ripple; ripple.m_RippleU = hitU; ripple.m_RippleV = hitV; ripple.m_Amplitude = partialBlock ? 4 : 30; ripple.m_Radius = 0.08f; ripple.m_StartTime = engine->GetLastTimeStamp(); ripple.m_Direction = dir; m_Ripples.AddToTail(ripple); Decal_t decal; decal.m_RippleU = hitU; decal.m_RippleV = hitV; decal.m_Radius = partialBlock ? 0.03f : 0.08f; decal.m_StartTime = engine->GetLastTimeStamp(); m_Decals.AddToTail(decal); } //----------------------------------------------------------------------------- // Draws the control points in wireframe //----------------------------------------------------------------------------- void C_Shield::DrawWireframeModel( Vector const** ppPositions ) { IMesh* pMesh = materials->GetDynamicMesh( true, NULL, NULL, m_pWireframe ); int numLines = (Height() - 1) * Width() + Height() * (Width() - 1); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_LINES, numLines ); Vector const* tmp; for (int i = 0; i < Height(); ++i) { for (int j = 0; j < Width(); ++j) { if ( i > 0 ) { tmp = ppPositions[j + Width() * i]; meshBuilder.Position3fv( tmp->Base() ); meshBuilder.Color4ub( 255, 255, 255, 128 ); meshBuilder.AdvanceVertex(); tmp = ppPositions[j + Width() * (i-1)]; meshBuilder.Position3fv( tmp->Base() ); meshBuilder.Color4ub( 255, 255, 255, 128 ); meshBuilder.AdvanceVertex(); } if (j > 0) { tmp = ppPositions[j + Width() * i]; meshBuilder.Position3fv( tmp->Base() ); meshBuilder.Color4ub( 255, 255, 255, 128 ); meshBuilder.AdvanceVertex(); tmp = ppPositions[j - 1 + Width() * i]; meshBuilder.Position3fv( tmp->Base() ); meshBuilder.Color4ub( 255, 255, 255, 128 ); meshBuilder.AdvanceVertex(); } } } meshBuilder.End(); pMesh->Draw(); } //----------------------------------------------------------------------------- // Draws the base shield //----------------------------------------------------------------------------- #define TRANSITION_REGION_WIDTH 0.5f extern ConVar mat_wireframe; void C_Shield::DrawShieldPoints(Vector* pt, Vector* normal, float* opacity) { SetCurrentDecal( -1 ); if (mat_wireframe.GetInt() == 0) materials->Bind( m_pShield, (IClientRenderable*)this ); else materials->Bind( m_pWireframe, (IClientRenderable*)this ); IMesh* pMesh = materials->GetDynamicMesh( true, NULL, NULL ); int numTriangles = (m_SubdivisionCount - 1) * (m_SubdivisionCount - 1) * 2; CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numTriangles ); float du = 1.0f * m_InvSubdivisionCount; float dv = du; unsigned char color[3]; color[0] = 255; color[1] = 255; color[2] = 255; for ( int i = 0; i < m_SubdivisionCount - 1; ++i) { float v = i * dv; for (int j = 0; j < m_SubdivisionCount - 1; ++j) { int idx = i * m_SubdivisionCount + j; float u = j * du; meshBuilder.Position3fv( pt[idx].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx] ); meshBuilder.Normal3fv( normal[idx].Base() ); meshBuilder.TexCoord2f( 0, u, v ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+m_SubdivisionCount] ); meshBuilder.Normal3fv( normal[idx + m_SubdivisionCount].Base() ); meshBuilder.TexCoord2f( 0, u, v + dv ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + 1].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+1] ); meshBuilder.Normal3fv( normal[idx+1].Base() ); meshBuilder.TexCoord2f( 0, u + du, v ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + 1].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+1] ); meshBuilder.Normal3fv( normal[idx+1].Base() ); meshBuilder.TexCoord2f( 0, u + du, v ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+m_SubdivisionCount] ); meshBuilder.Normal3fv( normal[idx + m_SubdivisionCount].Base() ); meshBuilder.TexCoord2f( 0, u, v + dv ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount + 1].Base() ); meshBuilder.Color4ub( color[0], color[1], color[2], opacity[idx+m_SubdivisionCount+1] ); meshBuilder.Normal3fv( normal[idx + m_SubdivisionCount + 1].Base() ); meshBuilder.TexCoord2f( 0, u + du, v + dv ); meshBuilder.AdvanceVertex(); } } meshBuilder.End(); pMesh->Draw(); } //----------------------------------------------------------------------------- // Draws shield decals //----------------------------------------------------------------------------- void C_Shield::DrawShieldDecals( Vector* pt, bool hitDecals ) { if (m_Decals.Size() == 0) return; // Compute ripples: for ( int r = m_Decals.Size(); --r >= 0; ) { // At the moment, nothing passes! bool passDecal = false; if ((!hitDecals) && (passDecal == hitDecals)) continue; SetCurrentDecal( r ); // We have to force a flush here because we're changing the proxy state if (!hitDecals) materials->Bind( m_pPassDecal, (IClientRenderable*)this ); else materials->Bind( passDecal ? m_pPassDecal2 : m_pHitDecal, (IClientRenderable*)this ); float dtime = gpGlobals->curtime - m_Decals[r].m_StartTime; float decay = exp( -( 2 * dtime) ); // Retire the animation if it wraps // This gets set by TextureAnimatedWrapped above if ((m_Decals[r].m_StartTime < 0.0f) || (decay < 1e-3)) { m_Decals.Remove(r); continue; } IMesh* pMesh = materials->GetDynamicMesh(); // Figure out the quads we must mod2x.... float u0 = m_Decals[r].m_RippleU - m_Decals[r].m_Radius; float u1 = m_Decals[r].m_RippleU + m_Decals[r].m_Radius; float v0 = m_Decals[r].m_RippleV - m_Decals[r].m_Radius; float v1 = m_Decals[r].m_RippleV + m_Decals[r].m_Radius; float du = u1 - u0; float dv = v1 - v0; int i0 = Floor2Int( v0 * (m_SubdivisionCount - 1) ); int i1 = Ceil2Int( v1 * (m_SubdivisionCount - 1) ); int j0 = Floor2Int( u0 * (m_SubdivisionCount - 1) ); int j1 = Ceil2Int( u1 * (m_SubdivisionCount - 1) ); if (i0 < 0) i0 = 0; if (i1 >= m_SubdivisionCount) i1 = m_SubdivisionCount - 1; if (j0 < 0) j0 = 0; if (j1 >= m_SubdivisionCount) j1 = m_SubdivisionCount - 1; int numTriangles = (i1 - i0) * (j1 - j0) * 2; CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numTriangles ); float decalDu = m_InvSubdivisionCount / du; float decalDv = m_InvSubdivisionCount / dv; unsigned char color[3]; color[0] = s_ImpactDecalColor[0] * decay; color[1] = s_ImpactDecalColor[1] * decay; color[2] = s_ImpactDecalColor[2] * decay; for ( int i = i0; i < i1; ++i) { float t = (float)i * m_InvSubdivisionCount; for (int j = j0; j < j1; ++j) { float s = (float)j * m_InvSubdivisionCount; int idx = i * m_SubdivisionCount + j; // Compute (u,v) into the decal float decalU = (s - u0) / du; float decalV = (t - v0) / dv; meshBuilder.Position3fv( pt[idx].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU, decalV ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU, decalV + decalDv ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + 1].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU + decalDu, decalV ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + 1].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU + decalDu, decalV ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU, decalV + decalDv ); meshBuilder.AdvanceVertex(); meshBuilder.Position3fv( pt[idx + m_SubdivisionCount + 1].Base() ); meshBuilder.Color3ubv( color ); meshBuilder.TexCoord2f( 0, decalU + decalDu, decalV + decalDv ); meshBuilder.AdvanceVertex(); } } meshBuilder.End(); pMesh->Draw(); } } //----------------------------------------------------------------------------- // Computes a single point //----------------------------------------------------------------------------- void C_Shield::ComputePoint( float s, float t, Vector& pt, Vector& normal, float& opacity ) { // Precache some computations for the point on the spline at (s, t). m_SplinePatch.SetupPatchQuery( s, t ); // Get the position + normal m_SplinePatch.GetPointAndNormal( pt, normal ); // From here on down is all futzing with opacity // Check neighbors for activity... bool active = IsPanelActive(m_SplinePatch.m_is, m_SplinePatch.m_it); if (m_SplinePatch.m_fs == 0.0f) active = active || IsPanelActive(m_SplinePatch.m_is - 1, m_SplinePatch.m_it); if (m_SplinePatch.m_ft == 0.0f) active = active || IsPanelActive(m_SplinePatch.m_is, m_SplinePatch.m_it - 1); if (!active) { // If the panel's not active, it's transparent. opacity = 0.0f; } else { if ((s == 0.0f) || (t == 0.0f) || (s == (Width() - 1.0f)) || (t == (Height() - 1.0f)) ) { // If it's on the edge, it's max opacity opacity = 192.0f; } else { // Channel zero is the opacity data opacity = m_SplinePatch.GetChannel( 0 ); // Make the shield translucent if the owner is the local player... // Also don't mess with the edges.. if (m_ShieldOwnedByLocalPlayer) { // Channel 1 is the opacity blend float blendFactor = m_SplinePatch.GetChannel( 1 ); blendFactor = clamp( blendFactor, 0.0f, 1.0f ); float blendValue = 1.0f; Vector delta; VectorSubtract( pt, GetAbsOrigin(), delta ); float dist = VectorLength( delta ); if (dist != 0.0f) { delta *= 1.0f / dist; float dot = DotProduct( m_ViewDir, delta ); float angle = acos( dot ); float fov = M_PI * render->GetFieldOfView() / 180.0f; if (angle < fov * .2f) blendValue = 0.1f; else if (angle < fov * 0.4f) { // Want a cos falloff between .2 and .4 // 0.1 at .2 and 1.0 at .4 angle -= fov * 0.2f; blendValue = 1.0f - 0.9f * 0.5f * (cos ( M_PI * angle / (fov * 0.2f) ) + 1.0f); } } // Interpolate between 1 and the blend value based on the blend factor... opacity *= (1.0f - blendFactor) + blendFactor * blendValue; } opacity = clamp( opacity, 0.0f, 192.0f ); } } opacity *= m_FadeValue; } //----------------------------------------------------------------------------- // Compute the shield points using catmull-rom //----------------------------------------------------------------------------- void C_Shield::ComputeShieldPoints( Vector* pt, Vector* normal, float* opacity ) { int i; for ( i = 0; i < m_SubdivisionCount; ++i) { float t = (Height() - 1) * (float)i * m_InvSubdivisionCount; for (int j = 0; j < m_SubdivisionCount; ++j) { float s = (Width() - 1) * (float)j * m_InvSubdivisionCount; int idx = i * m_SubdivisionCount + j; ComputePoint( s, t, pt[idx], normal[idx], opacity[idx] ); } } } //----------------------------------------------------------------------------- // Compute the shield ripples from being hit //----------------------------------------------------------------------------- void C_Shield::RippleShieldPoints( Vector* pt, float* opacity ) { // Compute ripples: for ( int r = m_Ripples.Size(); --r >= 0; ) { float dtime = gpGlobals->curtime - m_Ripples[r].m_StartTime; float decay = exp( -( 2 * dtime) ); float amplitude = m_Ripples[r].m_Amplitude * decay; for ( int i = 0; i < m_SubdivisionCount; ++i) { float t = i * m_InvSubdivisionCount; for (int j = 0; j < m_SubdivisionCount; ++j) { float s = j * m_InvSubdivisionCount; int idx = i * m_SubdivisionCount + j; float ds = s - m_Ripples[r].m_RippleU; float dt = t - m_Ripples[r].m_RippleV; float dr = sqrt( ds * ds + dt * dt ); if (dr < m_Ripples[r].m_Radius) { // need to apply ripple float diff = amplitude * cos( 0.5f * M_PI * dr / m_Ripples[r].m_Radius ); VectorMA( pt[idx], diff, m_Ripples[r].m_Direction, pt[idx] ); // Compute opacity at this point... float impactopacity = 192.0f * decay * dr / m_Ripples[r].m_Radius; if (impactopacity > opacity[idx]) opacity[idx] = impactopacity; } } } if (amplitude < 0.1) m_Ripples.Remove(r); } } //----------------------------------------------------------------------------- // Main draw entry point //----------------------------------------------------------------------------- int C_Shield::DrawModel( int flags ) { if ( !m_bReadyToDraw ) return 0; if (m_FadeValue == 0.0f) return 1; // If I have no power, don't draw if ( m_flPowerLevel <= 0 ) return 1; // Make it curvy or not!! m_SplinePatch.SetLinearBlend( m_CurveValue ); // Set up the patch with all the data it's going to need int count = Width() * Height(); Vector const** pControlPoints = (Vector const**)stackalloc(count * sizeof(Vector*)); float* pControlOpacity = (float*)stackalloc(count * sizeof(float)); float* pControlBlend = (float*)stackalloc(count * sizeof(float)); GetShieldData( pControlPoints, pControlOpacity, pControlBlend ); m_SplinePatch.SetControlPositions( pControlPoints ); m_SplinePatch.SetChannelData( 0, pControlOpacity ); m_SplinePatch.SetChannelData( 1, pControlBlend ); // DrawWireframeModel( pControlPoints ); // Allocate space for temporary data int numSubdivisions = m_SubdivisionCount * m_SubdivisionCount; Vector* pt = (Vector*)stackalloc(numSubdivisions * sizeof(Vector)); Vector* normal = (Vector*)stackalloc(numSubdivisions * sizeof(Vector)); float* opacity = (float*)stackalloc(numSubdivisions * sizeof(float)); // Do something a little special if this shield is owned by the local player C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); m_ShieldOwnedByLocalPlayer = (player->entindex() == m_nOwningPlayerIndex); if (m_ShieldOwnedByLocalPlayer) { QAngle viewAngles; engine->GetViewAngles(viewAngles); AngleVectors( viewAngles, &m_ViewDir ); } ComputeShieldPoints( pt, normal, opacity ); RippleShieldPoints( pt, opacity ); // Commented out because it causes things to not be drawn behind it // DrawShieldDecals( pt, false ); DrawShieldPoints( pt, normal, opacity ); DrawShieldDecals( pt, true ); return 1; } //============================================================================================================ // SHIELD POWERLEVEL PROXY //============================================================================================================ class CShieldPowerLevelProxy : public CResultProxy { public: void OnBind( void *pC_BaseEntity ); }; void CShieldPowerLevelProxy::OnBind( void *pRenderable ) { IClientRenderable *pRend = (IClientRenderable *)pRenderable; C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity(); C_Shield *pShield = dynamic_cast(pEntity); if (!pShield) return; SetFloatResult( pShield->GetPowerLevel() ); } EXPOSE_INTERFACE( CShieldPowerLevelProxy, IMaterialProxy, "ShieldPowerLevel" IMATERIAL_PROXY_INTERFACE_VERSION );