mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-28 09:03:01 +00:00
501 lines
14 KiB
C++
501 lines
14 KiB
C++
//========= 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 );
|
|
}
|