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