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.
837 lines
26 KiB
837 lines
26 KiB
//========= 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 );
|
|
|