summaryrefslogtreecommitdiff
path: root/game/client/tf2/c_shield.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf2/c_shield.cpp')
-rw-r--r--game/client/tf2/c_shield.cpp837
1 files changed, 837 insertions, 0 deletions
diff --git a/game/client/tf2/c_shield.cpp b/game/client/tf2/c_shield.cpp
new file mode 100644
index 0000000..b404a92
--- /dev/null
+++ b/game/client/tf2/c_shield.cpp
@@ -0,0 +1,837 @@
+//========= 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<C_Shield*>(pEntity);
+ if (!pShield)
+ return;
+
+ SetFloatResult( pShield->GetPowerLevel() );
+}
+
+EXPOSE_INTERFACE( CShieldPowerLevelProxy, IMaterialProxy, "ShieldPowerLevel" IMATERIAL_PROXY_INTERFACE_VERSION );