Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

687 lines
18 KiB

//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "c_sprite.h"
#include "model_types.h"
#include "iviewrender.h"
#include "view.h"
#include "enginesprite.h"
#include "engine/ivmodelinfo.h"
#include "util_shared.h"
#include "tier0/vprof.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "view_shared.h"
#include "viewrender.h"
#include "tier1/keyvalues.h"
#include "toolframework/itoolframework.h"
#include "toolframework_client.h"
#include "mapentities_shared.h"
#include "gamestringpool.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar r_drawsprites( "r_drawsprites", "1", FCVAR_CHEAT );
//-----------------------------------------------------------------------------
// Purpose: Generic sprite model renderer
// Input : *baseentity -
// *psprite -
// fscale -
// frame -
// rendermode -
// r -
// g -
// b -
// a -
// forward -
// right -
// up -
//-----------------------------------------------------------------------------
static unsigned int s_nHDRColorScaleCache = 0;
void DrawSpriteModel( IClientEntity *baseentity, CEngineSprite *psprite, const Vector &origin, float fscale, float frame,
int rendermode, int r, int g, int b, int a, const Vector& forward, const Vector& right, const Vector& up, float flHDRColorScale )
{
float scale;
IMaterial *material;
// don't even bother culling, because it's just a single
// polygon without a surface cache
if ( fscale > 0 )
scale = fscale;
else
scale = 1.0f;
if ( rendermode == kRenderNormal )
{
render->SetBlend( 1.0f );
}
material = psprite->GetMaterial( (RenderMode_t)rendermode, frame );
if ( !material )
return;
CMatRenderContextPtr pRenderContext( materials );
if ( ShouldDrawInWireFrameMode() || r_drawsprites.GetInt() == 2 )
{
IMaterial *pMaterial = materials->FindMaterial( "debug/debugspritewireframe", TEXTURE_GROUP_OTHER );
pRenderContext->Bind( pMaterial, NULL );
}
else
{
pRenderContext->Bind( material, (IClientRenderable*)baseentity );
}
unsigned char color[4];
color[0] = r;
color[1] = g;
color[2] = b;
color[3] = a;
IMaterialVar *pHDRColorScaleVar = material->FindVarFast( "$HDRCOLORSCALE", &s_nHDRColorScaleCache );
if( pHDRColorScaleVar )
{
pHDRColorScaleVar->SetVecValue( flHDRColorScale, flHDRColorScale, flHDRColorScale );
}
Vector point;
IMesh* pMesh = pRenderContext->GetDynamicMesh();
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
Vector vec_a;
Vector vec_b;
Vector vec_c;
Vector vec_d;
// isolate common terms
VectorMA( origin, psprite->GetDown() * scale, up, vec_a );
VectorScale( right, psprite->GetLeft() * scale, vec_b );
VectorMA( origin, psprite->GetUp() * scale, up, vec_c );
VectorScale( right, psprite->GetRight() * scale, vec_d );
float flMinU, flMinV, flMaxU, flMaxV;
psprite->GetTexCoordRange( &flMinU, &flMinV, &flMaxU, &flMaxV );
meshBuilder.Color4ubv( color );
meshBuilder.TexCoord2f( 0, flMinU, flMaxV );
VectorAdd( vec_a, vec_b, point );
meshBuilder.Position3fv( point.Base() );
meshBuilder.AdvanceVertex();
meshBuilder.Color4ubv( color );
meshBuilder.TexCoord2f( 0, flMinU, flMinV );
VectorAdd( vec_c, vec_b, point );
meshBuilder.Position3fv( point.Base() );
meshBuilder.AdvanceVertex();
meshBuilder.Color4ubv( color );
meshBuilder.TexCoord2f( 0, flMaxU, flMinV );
VectorAdd( vec_c, vec_d, point );
meshBuilder.Position3fv( point.Base() );
meshBuilder.AdvanceVertex();
meshBuilder.Color4ubv( color );
meshBuilder.TexCoord2f( 0, flMaxU, flMaxV );
VectorAdd( vec_a, vec_d, point );
meshBuilder.Position3fv( point.Base() );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
//-----------------------------------------------------------------------------
// Purpose: Determine glow brightness/scale based on distance to render origin and trace results
// Input : entorigin -
// rendermode -
// renderfx -
// alpha -
// pscale - Pointer to the value for scale, will be changed based on distance and rendermode.
//-----------------------------------------------------------------------------
float StandardGlowBlend( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle, int rendermode, int renderfx, int alpha, float *pscale )
{
float dist;
float brightness;
brightness = PixelVisibility_FractionVisible( params, queryHandle );
if ( brightness <= 0.0f )
{
return 0.0f;
}
dist = GlowSightDistance( params.position, false );
if ( dist <= 0.0f )
{
return 0.0f;
}
if ( renderfx == kRenderFxNoDissipation )
{
return (float)alpha * (1.0f/255.0f) * brightness;
}
// UNDONE: Tweak these magic numbers (1200 - distance at full brightness)
float fadeOut = 1.0f;
if (rendermode == kRenderWorldGlow)
{
fadeOut = ( 2400.0f*2400.0f ) / ( dist*dist );
fadeOut = clamp( fadeOut, 0.0f, 1.0f );
}
else
{
fadeOut = ( 1200.0f*1200.0f ) / ( dist*dist );
fadeOut = clamp( fadeOut, 0.0f, 1.0f );
// Make the glow fixed size in screen space, taking into consideration the scale setting.
if ( *pscale == 0.0f )
{
*pscale = 1.0f;
}
*pscale *= dist * (1.0f/200.0f);
}
return fadeOut * brightness;
}
static float SpriteAspect( CEngineSprite *pSprite )
{
if ( pSprite )
{
float x = fabsf(pSprite->GetRight() - pSprite->GetLeft());
float y = fabsf(pSprite->GetDown() - pSprite->GetUp());
if ( y != 0 && x != 0 )
{
return x / y;
}
}
return 1.0f;
}
float C_SpriteRenderer::GlowBlend( CEngineSprite *psprite, const Vector& entorigin, int rendermode, int renderfx, int alpha, float *pscale )
{
pixelvis_queryparams_t params;
float aspect = SpriteAspect(psprite);
params.Init( entorigin, PIXELVIS_DEFAULT_PROXY_SIZE, aspect );
return StandardGlowBlend( params, &m_queryHandle, rendermode, renderfx, alpha, pscale );
}
// since sprites can network down a glow proxy size, handle that here
float CSprite::GlowBlend( CEngineSprite *psprite, const Vector& entorigin, int rendermode, int renderfx, int alpha, float *pscale )
{
pixelvis_queryparams_t params;
float aspect = SpriteAspect(psprite);
params.Init( entorigin, m_flGlowProxySize, aspect );
return StandardGlowBlend( params, &m_queryHandle, rendermode, renderfx, alpha, pscale );
}
//-----------------------------------------------------------------------------
// Purpose: Determine sprite orientation axes
// Input : type -
// forward -
// right -
// up -
//-----------------------------------------------------------------------------
void C_SpriteRenderer::GetSpriteAxes( SPRITETYPE type,
const Vector& origin,
const QAngle& angles,
Vector& forward,
Vector& right,
Vector& up )
{
int i;
float dot, angle, sr, cr;
Vector tvec;
// Automatically roll parallel sprites if requested
if ( angles[2] != 0 && type == SPR_VP_PARALLEL )
{
type = SPR_VP_PARALLEL_ORIENTED;
}
switch( type )
{
case SPR_FACING_UPRIGHT:
{
// generate the sprite's axes, with vup straight up in worldspace, and
// r_spritedesc.vright perpendicular to modelorg.
// This will not work if the view direction is very close to straight up or
// down, because the cross product will be between two nearly parallel
// vectors and starts to approach an undefined state, so we don't draw if
// the two vectors are less than 1 degree apart
tvec[0] = -origin[0];
tvec[1] = -origin[1];
tvec[2] = -origin[2];
VectorNormalize (tvec);
dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because
// r_spritedesc.vup is 0, 0, 1
if ((dot > 0.999848f) || (dot < -0.999848f)) // cos(1 degree) = 0.999848
return;
up[0] = 0;
up[1] = 0;
up[2] = 1;
right[0] = tvec[1];
// CrossProduct(r_spritedesc.vup, -modelorg,
right[1] = -tvec[0];
// r_spritedesc.vright)
right[2] = 0;
VectorNormalize (right);
forward[0] = -right[1];
forward[1] = right[0];
forward[2] = 0;
// CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
// r_spritedesc.vpn)
}
break;
case SPR_VP_PARALLEL:
{
// generate the sprite's axes, completely parallel to the viewplane. There
// are no problem situations, because the sprite is always in the same
// position relative to the viewer
for (i=0 ; i<3 ; i++)
{
up[i] = CurrentViewUp()[i];
right[i] = CurrentViewRight()[i];
forward[i] = CurrentViewForward()[i];
}
}
break;
case SPR_VP_PARALLEL_UPRIGHT:
{
// generate the sprite's axes, with g_vecVUp straight up in worldspace, and
// r_spritedesc.vright parallel to the viewplane.
// This will not work if the view direction is very close to straight up or
// down, because the cross product will be between two nearly parallel
// vectors and starts to approach an undefined state, so we don't draw if
// the two vectors are less than 1 degree apart
dot = CurrentViewForward()[2]; // same as DotProduct (vpn, r_spritedesc.g_vecVUp) because
// r_spritedesc.vup is 0, 0, 1
if ((dot > 0.999848f) || (dot < -0.999848f)) // cos(1 degree) = 0.999848
return;
up[0] = 0;
up[1] = 0;
up[2] = 1;
right[0] = CurrentViewForward()[1];
// CrossProduct (r_spritedesc.vup, vpn,
right[1] = -CurrentViewForward()[0]; // r_spritedesc.vright)
right[2] = 0;
VectorNormalize (right);
forward[0] = -right[1];
forward[1] = right[0];
forward[2] = 0;
// CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
// r_spritedesc.vpn)
}
break;
case SPR_ORIENTED:
{
// generate the sprite's axes, according to the sprite's world orientation
AngleVectors( angles, &forward, &right, &up );
}
break;
case SPR_VP_PARALLEL_ORIENTED:
{
// generate the sprite's axes, parallel to the viewplane, but rotated in
// that plane around the center according to the sprite entity's roll
// angle. So vpn stays the same, but vright and vup rotate
angle = angles[ROLL] * (M_PI*2.0f/360.0f);
SinCos( angle, &sr, &cr );
for (i=0 ; i<3 ; i++)
{
forward[i] = CurrentViewForward()[i];
right[i] = CurrentViewRight()[i] * cr + CurrentViewUp()[i] * sr;
up[i] = CurrentViewRight()[i] * -sr + CurrentViewUp()[i] * cr;
}
}
break;
default:
Warning( "GetSpriteAxes: Bad sprite type %d\n", type );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int C_SpriteRenderer::DrawSprite(
IClientEntity *entity,
const model_t *model,
const Vector& origin,
const QAngle& angles,
float frame,
IClientEntity *attachedto,
int attachmentindex,
int rendermode,
int renderfx,
int alpha,
int r,
int g,
int b,
float scale,
float flHDRColorScale
)
{
VPROF_BUDGET( "C_SpriteRenderer::DrawSprite", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
if ( !r_drawsprites.GetBool() || !model || modelinfo->GetModelType( model ) != mod_sprite )
{
return 0;
}
// Get extra data
CEngineSprite *psprite = (CEngineSprite *)modelinfo->GetModelExtraData( model );
if ( !psprite )
{
return 0;
}
Vector effect_origin;
VectorCopy( origin, effect_origin );
// Use attachment point
if ( attachedto )
{
C_BaseEntity *ent = attachedto->GetBaseEntity();
if ( ent )
{
// don't draw viewmodel effects in reflections
if ( CurrentViewID() == VIEW_REFLECTION )
{
if ( g_pClientLeafSystem->IsRenderingWithViewModels( ent->RenderHandle() ) )
return 0;
}
QAngle temp;
ent->GetAttachment( attachmentindex, effect_origin, temp );
}
}
if ( rendermode != kRenderNormal )
{
float blend = render->GetBlend();
// kRenderGlow and kRenderWorldGlow have a special blending function
if (( rendermode == kRenderGlow ) || ( rendermode == kRenderWorldGlow ))
{
blend *= GlowBlend( psprite, effect_origin, rendermode, renderfx, alpha, &scale );
// Fade out the sprite depending on distance from the view origin.
r *= blend;
g *= blend;
b *= blend;
}
render->SetBlend( blend );
if ( blend <= 0.0f )
{
return 0;
}
}
// Get orthonormal basis
Vector forward, right, up;
GetSpriteAxes( (SPRITETYPE)psprite->GetOrientation(), origin, angles, forward, right, up );
// Draw
DrawSpriteModel(
entity,
psprite,
effect_origin,
scale,
frame,
rendermode,
r,
g,
b,
alpha,
forward, right, up, flHDRColorScale );
return 1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSprite::GetToolRecordingState( KeyValues *msg )
{
if ( !ToolsEnabled() )
return;
VPROF_BUDGET( "CSprite::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
BaseClass::GetToolRecordingState( msg );
// Use attachment point
if ( m_hAttachedToEntity )
{
C_BaseEntity *ent = m_hAttachedToEntity->GetBaseEntity();
if ( ent )
{
BaseEntityRecordingState_t *pState = (BaseEntityRecordingState_t*)msg->GetPtr( "baseentity" );
// override position if we're driven by an attachment
QAngle temp;
pState->m_vecRenderOrigin = GetAbsOrigin();
ent->GetAttachment( m_nAttachment, pState->m_vecRenderOrigin, temp );
// override viewmodel if we're driven by an attachment
bool bViewModel = ToBaseViewModel( ent ) != NULL;
msg->SetInt( "viewmodel", bViewModel );
}
}
float renderscale = GetRenderScale();
if ( m_bWorldSpaceScale )
{
CEngineSprite *psprite = ( CEngineSprite * )modelinfo->GetModelExtraData( GetModel() );
float flMinSize = MIN( psprite->GetWidth(), psprite->GetHeight() );
renderscale /= flMinSize;
}
color24 c = GetRenderColor();
// sprite params
static SpriteRecordingState_t state;
state.m_flRenderScale = renderscale;
state.m_flFrame = m_flFrame;
state.m_flProxyRadius = m_flGlowProxySize;
state.m_nRenderMode = GetRenderMode();
state.m_nRenderFX = GetRenderFX() ? true : false;
state.m_Color.SetColor( c.r, c.g, c.b, GetRenderBrightness() );
msg->SetPtr( "sprite", &state );
}
CUtlVector< CSprite * > g_ClientsideSprites;
// ===================== For clientside spawning of sprites =====================
void CSprite::RecreateAllClientside()
{
DestroyAllClientside();
ParseAllClientsideEntities( engine->GetMapEntitiesString() );
}
void CSprite::DestroyAllClientside()
{
// This only gets called during LevelInitPostEntity and LevelShutdown so we're going to use
// Release() instead of Remove() or UTIL_Remove
while ( g_ClientsideSprites.Count() > 0 )
{
CSprite *p = g_ClientsideSprites[0];
// This will call into CSprite::~CSprite in sprite.cpp, which will FindAndRemove this sprite from the array
p->Release();
}
}
bool CSprite::InitializeClientside()
{
if ( InitializeAsClientEntity( STRING( GetModelName() ), false ) == false )
{
return false;
}
m_bClientOnly = true;
g_ClientsideSprites.AddToTail( this );
Spawn();
const model_t *mod = GetModel();
if ( mod )
{
Vector mins, maxs;
modelinfo->GetModelBounds( mod, mins, maxs );
SetCollisionBounds( mins, maxs );
}
SetBlocksLOS( false ); // this should be a small object
SetNextClientThink( CLIENT_THINK_NEVER );
return true;
}
const char *CSprite::ParseClientsideEntity( const char *pEntData )
{
CEntityMapData entData( (char*)pEntData );
char className[MAPKEY_MAXLENGTH];
MDLCACHE_CRITICAL_SECTION();
if ( !entData.ExtractValue( "classname", className ) )
{
Error( "classname missing from entity!\n" );
}
if ( !Q_strcmp( className, "env_sprite_clientside" ))
{
// always force clientside entities placed in maps
CSprite *pEntity = new CSprite();
if ( pEntity )
{
// Set up keyvalues.
pEntity->ParseMapData(&entData);
if ( !pEntity->InitializeClientside() )
pEntity->Release();
return entData.CurrentBufferPosition();
}
}
// Just skip past all the keys.
char keyName[MAPKEY_MAXLENGTH];
char value[MAPKEY_MAXLENGTH];
if ( entData.GetFirstKey(keyName, value) )
{
do
{
}
while ( entData.GetNextKey(keyName, value) );
}
//
// Return the current parser position in the data block
//
return entData.CurrentBufferPosition();
}
bool CSprite::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "scale" ) )
{
m_flSpriteScale = atof(szValue);
}
else if ( FStrEq( szKeyName, "framerate" ) )
{
m_flSpriteFramerate = atof(szValue);
}
else if ( FStrEq( szKeyName, "GlowProxySize" ) )
{
m_flGlowProxySize = atof(szValue);
}
else if ( FStrEq( szKeyName, "frame" ) )
{
m_flFrame = atof(szValue);
}
else if ( FStrEq( szKeyName, "HDRColorScale" ) )
{
m_flHDRColorScale = atof(szValue);
}
else if ( FStrEq( szKeyName, "rendermode" ) )
{
SetRenderMode( (RenderMode_t) atoi( szValue ) );
}
else if ( FStrEq( szKeyName, "model" ) )
{
SetModelName( AllocPooledString( szValue ) );
}
else
{
return BaseClass::KeyValue( szKeyName, szValue );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Only called on BSP load. Parses and spawns all the entities in the BSP.
// Input : pMapData - Pointer to the entity data block to parse.
//-----------------------------------------------------------------------------
void CSprite::ParseAllClientsideEntities(const char *pMapData)
{
int nEntities = 0;
char szTokenBuffer[MAPKEY_MAXLENGTH];
//
// Loop through all entities in the map data, creating each.
//
for ( ; true; pMapData = MapEntity_SkipToNextEntity( pMapData, szTokenBuffer ) )
{
//
// Parse the opening brace.
//
char token[MAPKEY_MAXLENGTH];
pMapData = MapEntity_ParseToken( pMapData, token );
//
// Check to see if we've finished or not.
//
if ( !pMapData )
break;
if ( token[0] != '{' )
{
Error( "CSprite::ParseAllEntities: found %s when expecting {", token);
continue;
}
//
// Parse the entity and add it to the spawn list.
//
pMapData = ParseClientsideEntity( pMapData );
nEntities++;
}
}