diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf2/c_shield.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf2/c_shield.cpp')
| -rw-r--r-- | game/client/tf2/c_shield.cpp | 837 |
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 ); |