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.
500 lines
14 KiB
500 lines
14 KiB
//========= Copyright 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" |
|
|
|
// 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 ¶ms, 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 = (1200.0f*1200.0f) / (dist*dist); |
|
fadeOut = clamp( fadeOut, 0.0f, 1.0f ); |
|
|
|
if (rendermode != kRenderWorldGlow) |
|
{ |
|
// 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 ) |
|
{ |
|
int group = ent->GetRenderGroup(); |
|
if ( group == RENDER_GROUP_VIEW_MODEL_TRANSLUCENT || group == RENDER_GROUP_VIEW_MODEL_OPAQUE ) |
|
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 = dynamic_cast< C_BaseViewModel* >( 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; |
|
} |
|
|
|
// 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 = m_nRenderFX; |
|
state.m_Color.SetColor( m_clrRender.GetR(), m_clrRender.GetG(), m_clrRender.GetB(), GetRenderBrightness() ); |
|
|
|
msg->SetPtr( "sprite", &state ); |
|
}
|
|
|