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.
542 lines
14 KiB
542 lines
14 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//===========================================================================// |
|
#include "cbase.h" |
|
#include "enginesprite.h" |
|
#include "hud.h" |
|
#include "materialsystem/imesh.h" |
|
#include "materialsystem/imaterial.h" |
|
#include "materialsystem/imaterialvar.h" |
|
#include "c_sprite.h" |
|
#include "tier1/callqueue.h" |
|
#include "tier1/KeyValues.h" |
|
#include "tier2/tier2.h" |
|
#include "filesystem.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
// Sprites are clipped to this rectangle (x,y,width,height) if ScissorTest is enabled |
|
static int scissor_x = 0; |
|
static int scissor_y = 0; |
|
static int scissor_width = 0; |
|
static int scissor_height = 0; |
|
static bool giScissorTest = false; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Set the scissor |
|
// the coordinate system for gl is upsidedown (inverted-y) as compared to software, so the |
|
// specified clipping rect must be flipped |
|
// Input : x - |
|
// y - |
|
// width - |
|
// height - |
|
//----------------------------------------------------------------------------- |
|
void EnableScissorTest( int x, int y, int width, int height ) |
|
{ |
|
x = clamp( x, 0, ScreenWidth() ); |
|
y = clamp( y, 0, ScreenHeight() ); |
|
width = clamp( width, 0, ScreenWidth() - x ); |
|
height = clamp( height, 0, ScreenHeight() - y ); |
|
|
|
scissor_x = x; |
|
scissor_width = width; |
|
scissor_y = y; |
|
scissor_height = height; |
|
|
|
giScissorTest = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void DisableScissorTest( void ) |
|
{ |
|
scissor_x = 0; |
|
scissor_width = 0; |
|
scissor_y = 0; |
|
scissor_height = 0; |
|
|
|
giScissorTest = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Verify that this is a valid, properly ordered rectangle. |
|
// Input : *prc - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
static int ValidateWRect(const wrect_t *prc) |
|
{ |
|
|
|
if (!prc) |
|
return false; |
|
|
|
if ((prc->left >= prc->right) || (prc->top >= prc->bottom)) |
|
{ |
|
//!!!UNDONE Dev only warning msg |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: classic interview question |
|
// Input : *prc1 - |
|
// *prc2 - |
|
// *prc - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
static int IntersectWRect(const wrect_t *prc1, const wrect_t *prc2, wrect_t *prc) |
|
{ |
|
wrect_t rc; |
|
|
|
if (!prc) |
|
prc = &rc; |
|
|
|
prc->left = MAX(prc1->left, prc2->left); |
|
prc->right = MIN(prc1->right, prc2->right); |
|
|
|
if (prc->left < prc->right) |
|
{ |
|
prc->top = MAX(prc1->top, prc2->top); |
|
prc->bottom = MIN(prc1->bottom, prc2->bottom); |
|
|
|
if (prc->top < prc->bottom) |
|
return 1; |
|
|
|
} |
|
|
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : x - |
|
// y - |
|
// width - |
|
// height - |
|
// u0 - |
|
// v0 - |
|
// u1 - |
|
// v1 - |
|
// Output : static bool |
|
//----------------------------------------------------------------------------- |
|
static bool Scissor( int& x, int& y, int& width, int& height, float& u0, float& v0, float& u1, float& v1 ) |
|
{ |
|
// clip sub rect to sprite |
|
if ((width == 0) || (height == 0)) |
|
return false; |
|
|
|
if ((x + width <= scissor_x) || (x >= scissor_x + scissor_width) || |
|
(y + height <= scissor_y) || (y >= scissor_y + scissor_height)) |
|
return false; |
|
|
|
float dudx = (u1-u0) / width; |
|
float dvdy = (v1-v0) / height; |
|
if (x < scissor_x) |
|
{ |
|
u0 += (scissor_x - x) * dudx; |
|
width -= scissor_x - x; |
|
x = scissor_x; |
|
} |
|
|
|
if (x + width > scissor_x + scissor_width) |
|
{ |
|
u1 -= (x + width - (scissor_x + scissor_width)) * dudx; |
|
width = scissor_x + scissor_width - x; |
|
} |
|
|
|
if (y < scissor_y) |
|
{ |
|
v0 += (scissor_y - y) * dvdy; |
|
height -= scissor_y - y; |
|
y = scissor_y; |
|
} |
|
|
|
if (y + height > scissor_y + scissor_height) |
|
{ |
|
v1 -= (y + height - (scissor_y + scissor_height)) * dvdy; |
|
height = scissor_y + scissor_height - y; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pSprite - |
|
// frame - |
|
// *pfLeft - |
|
// *pfRight - |
|
// *pfTop - |
|
// *pfBottom - |
|
// *pw - |
|
// *ph - |
|
// *prcSubRect - |
|
// Output : static void |
|
//----------------------------------------------------------------------------- |
|
static void AdjustSubRect(CEngineSprite *pSprite, int frame, float *pfLeft, float *pfRight, float *pfTop, |
|
float *pfBottom, int *pw, int *ph, const wrect_t *prcSubRect) |
|
{ |
|
wrect_t rc; |
|
float f; |
|
|
|
if (!ValidateWRect(prcSubRect)) |
|
return; |
|
|
|
// clip sub rect to sprite |
|
|
|
rc.top = rc.left = 0; |
|
rc.right = *pw; |
|
rc.bottom = *ph; |
|
|
|
if (!IntersectWRect(prcSubRect, &rc, &rc)) |
|
return; |
|
|
|
*pw = rc.right - rc.left; |
|
*ph = rc.bottom - rc.top; |
|
|
|
f = 1.0 / (float)pSprite->GetWidth();; |
|
*pfLeft = ((float)rc.left + 0.5) * f; |
|
*pfRight = ((float)rc.right - 0.5) * f; |
|
|
|
f = 1.0 / (float)pSprite->GetHeight(); |
|
*pfTop = ((float)rc.top + 0.5) * f; |
|
*pfBottom = ((float)rc.bottom - 0.5) * f; |
|
|
|
return; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
static unsigned int spriteOriginCache = 0; |
|
static unsigned int spriteOrientationCache = 0; |
|
bool CEngineSprite::Init( const char *pName ) |
|
{ |
|
m_VideoMaterial = NULL; |
|
for ( int i = 0; i < kRenderModeCount; ++i ) |
|
{ |
|
m_material[ i ] = NULL; |
|
} |
|
|
|
m_width = m_height = m_numFrames = 1; |
|
|
|
Assert( g_pVideo != NULL ); |
|
|
|
if ( g_pVideo != NULL && g_pVideo->LocateVideoSystemForPlayingFile( pName ) != VideoSystem::NONE ) |
|
{ |
|
m_VideoMaterial = g_pVideo->CreateVideoMaterial( pName, pName, "GAME", VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS, VideoSystem::DETERMINE_FROM_FILE_EXTENSION, false ); |
|
|
|
if ( m_VideoMaterial == NULL ) |
|
return false; |
|
|
|
IMaterial *pMaterial = m_VideoMaterial->GetMaterial(); |
|
m_VideoMaterial->GetVideoImageSize( &m_width, &m_height ); |
|
m_numFrames = m_VideoMaterial->GetFrameCount(); |
|
for ( int i = 0; i < kRenderModeCount; ++i ) |
|
{ |
|
m_material[i] = pMaterial; |
|
pMaterial->IncrementReferenceCount(); |
|
} |
|
} |
|
else |
|
{ |
|
char pTemp[MAX_PATH]; |
|
char pMaterialName[MAX_PATH]; |
|
char pMaterialPath[MAX_PATH]; |
|
Q_StripExtension( pName, pTemp, sizeof(pTemp) ); |
|
Q_strlower( pTemp ); |
|
Q_FixSlashes( pTemp, '/' ); |
|
|
|
// Check to see if this is a UNC-specified material name |
|
bool bIsUNC = pTemp[0] == '/' && pTemp[1] == '/' && pTemp[2] != '/'; |
|
if ( !bIsUNC ) |
|
{ |
|
Q_strncpy( pMaterialName, "materials/", sizeof(pMaterialName) ); |
|
Q_strncat( pMaterialName, pTemp, sizeof(pMaterialName), COPY_ALL_CHARACTERS ); |
|
} |
|
else |
|
{ |
|
Q_strncpy( pMaterialName, pTemp, sizeof(pMaterialName) ); |
|
} |
|
Q_strncpy( pMaterialPath, pMaterialName, sizeof(pMaterialPath) ); |
|
Q_SetExtension( pMaterialPath, ".vmt", sizeof(pMaterialPath) ); |
|
|
|
KeyValues *kv = new KeyValues( "vmt" ); |
|
if ( !kv->LoadFromFile( g_pFullFileSystem, pMaterialPath, "GAME" ) ) |
|
{ |
|
Warning( "Unable to load sprite material %s!\n", pMaterialPath ); |
|
return false; |
|
} |
|
|
|
for ( int i = 0; i < kRenderModeCount; ++i ) |
|
{ |
|
if ( i == kRenderNone || i == kRenderEnvironmental ) |
|
{ |
|
m_material[i] = NULL; |
|
continue; |
|
} |
|
|
|
Q_snprintf( pMaterialPath, sizeof(pMaterialPath), "%s_rendermode_%d", pMaterialName, i ); |
|
KeyValues *pMaterialKV = kv->MakeCopy(); |
|
pMaterialKV->SetInt( "$spriteRenderMode", i ); |
|
m_material[i] = g_pMaterialSystem->FindProceduralMaterial( pMaterialPath, TEXTURE_GROUP_CLIENT_EFFECTS, pMaterialKV ); |
|
m_material[ i ]->IncrementReferenceCount(); |
|
} |
|
|
|
kv->deleteThis(); |
|
|
|
m_width = m_material[0]->GetMappingWidth(); |
|
m_height = m_material[0]->GetMappingHeight(); |
|
m_numFrames = m_material[0]->GetNumAnimationFrames(); |
|
} |
|
|
|
for ( int i = 0; i < kRenderModeCount; ++i ) |
|
{ |
|
if ( i == kRenderNone || i == kRenderEnvironmental ) |
|
continue; |
|
|
|
if ( !m_material[i] ) |
|
return false; |
|
} |
|
|
|
IMaterialVar *orientationVar = m_material[0]->FindVarFast( "$spriteorientation", &spriteOrientationCache ); |
|
m_orientation = orientationVar ? orientationVar->GetIntValue() : C_SpriteRenderer::SPR_VP_PARALLEL_UPRIGHT; |
|
|
|
IMaterialVar *originVar = m_material[0]->FindVarFast( "$spriteorigin", &spriteOriginCache ); |
|
Vector origin, originVarValue; |
|
if( !originVar || ( originVar->GetType() != MATERIAL_VAR_TYPE_VECTOR ) ) |
|
{ |
|
origin[0] = -m_width * 0.5f; |
|
origin[1] = m_height * 0.5f; |
|
} |
|
else |
|
{ |
|
originVar->GetVecValue( &originVarValue[0], 3 ); |
|
origin[0] = -m_width * originVarValue[0]; |
|
origin[1] = m_height * originVarValue[1]; |
|
} |
|
|
|
up = origin[1]; |
|
down = origin[1] - m_height; |
|
left = origin[0]; |
|
right = m_width + origin[0]; |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEngineSprite::Shutdown( void ) |
|
{ |
|
if ( g_pVideo != NULL && m_VideoMaterial != NULL ) |
|
{ |
|
g_pVideo->DestroyVideoMaterial( m_VideoMaterial ); |
|
m_VideoMaterial = NULL; |
|
} |
|
|
|
UnloadMaterial(); |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Is the sprite a video sprite? |
|
//----------------------------------------------------------------------------- |
|
bool CEngineSprite::IsVideo() |
|
{ |
|
return ( m_VideoMaterial != NULL ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the texture coordinate range used to draw the sprite |
|
//----------------------------------------------------------------------------- |
|
void CEngineSprite::GetTexCoordRange( float *pMinU, float *pMinV, float *pMaxU, float *pMaxV ) |
|
{ |
|
*pMaxU = 1.0f; |
|
*pMaxV = 1.0f; |
|
if ( IsVideo() ) |
|
{ |
|
m_VideoMaterial->GetVideoTexCoordRange( pMaxU, pMaxV ); |
|
} |
|
|
|
float flOOWidth = ( m_width != 0 ) ? 1.0f / m_width : 1.0f; |
|
float flOOHeight = ( m_height!= 0 ) ? 1.0f / m_height : 1.0f; |
|
|
|
*pMinU = 0.5f * flOOWidth; |
|
*pMinV = 0.5f * flOOHeight; |
|
*pMaxU = (*pMaxU) - (*pMinU); |
|
*pMaxV = (*pMaxV) - (*pMinV); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEngineSprite::SetColor( float r, float g, float b ) |
|
{ |
|
Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) ); |
|
Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) ); |
|
m_hudSpriteColor[0] = r; |
|
m_hudSpriteColor[1] = g; |
|
m_hudSpriteColor[2] = b; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEngineSprite::GetHUDSpriteColor( float* color ) |
|
{ |
|
VectorCopy( m_hudSpriteColor, color ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the material |
|
//----------------------------------------------------------------------------- |
|
static unsigned int frameCache = 0; |
|
IMaterial *CEngineSprite::GetMaterial( RenderMode_t nRenderMode, int nFrame ) |
|
{ |
|
if ( nRenderMode == kRenderNone || nRenderMode == kRenderEnvironmental ) |
|
return NULL; |
|
|
|
if ( IsVideo() ) |
|
{ |
|
m_VideoMaterial->SetFrame( nFrame ); |
|
} |
|
|
|
|
|
IMaterial *pMaterial = m_material[nRenderMode]; |
|
IMaterialVar* pFrameVar = pMaterial->FindVarFast( "$frame", &frameCache ); |
|
if ( pFrameVar ) |
|
{ |
|
pFrameVar->SetIntValue( nFrame ); |
|
} |
|
|
|
return pMaterial; |
|
} |
|
|
|
void CEngineSprite::SetFrame( RenderMode_t nRenderMode, int nFrame ) |
|
{ |
|
if ( IsVideo() ) |
|
{ |
|
m_VideoMaterial->SetFrame( nFrame ); |
|
return; |
|
} |
|
|
|
|
|
IMaterial *pMaterial = m_material[nRenderMode]; |
|
if ( !pMaterial ) |
|
return; |
|
|
|
IMaterialVar* pFrameVar = pMaterial->FindVarFast( "$frame", &frameCache ); |
|
if ( pFrameVar ) |
|
{ |
|
pFrameVar->SetIntValue( nFrame ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CEngineSprite::GetOrientation( void ) |
|
{ |
|
return m_orientation; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEngineSprite::UnloadMaterial( void ) |
|
{ |
|
for ( int i = 0; i < kRenderModeCount; ++i ) |
|
{ |
|
if( m_material[i] ) |
|
{ |
|
m_material[i]->DecrementReferenceCount(); |
|
m_material[i] = NULL; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEngineSprite::DrawFrame( RenderMode_t nRenderMode, int frame, int x, int y, const wrect_t *prcSubRect ) |
|
{ |
|
DrawFrameOfSize( nRenderMode, frame, x, y, GetWidth(), GetHeight(), prcSubRect ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : frame - |
|
// x - |
|
// y - |
|
// *prcSubRect - |
|
//----------------------------------------------------------------------------- |
|
void CEngineSprite::DrawFrameOfSize( RenderMode_t nRenderMode, int frame, int x, int y, int iWidth, int iHeight, const wrect_t *prcSubRect ) |
|
{ |
|
// FIXME: If we ever call this with AVIs, need to have it call GetTexCoordRange and make that work |
|
Assert( !IsVideo() ); |
|
float fLeft = 0; |
|
float fRight = 1; |
|
float fTop = 0; |
|
float fBottom = 1; |
|
|
|
if ( prcSubRect ) |
|
{ |
|
AdjustSubRect( this, frame, &fLeft, &fRight, &fTop, &fBottom, &iWidth, &iHeight, prcSubRect ); |
|
} |
|
|
|
if ( giScissorTest && !Scissor( x, y, iWidth, iHeight, fLeft, fTop, fRight, fBottom ) ) |
|
return; |
|
|
|
SetFrame( nRenderMode, frame ); |
|
|
|
CMatRenderContextPtr pRenderContext( materials ); |
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, GetMaterial( nRenderMode ) ); |
|
|
|
CMeshBuilder meshBuilder; |
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); |
|
|
|
float color[3]; |
|
GetHUDSpriteColor( color ); |
|
|
|
meshBuilder.Color3fv( color ); |
|
meshBuilder.TexCoord2f( 0, fLeft, fTop ); |
|
meshBuilder.Position3f( x, y, 0.0f ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.Color3fv( color ); |
|
meshBuilder.TexCoord2f( 0, fRight, fTop ); |
|
meshBuilder.Position3f( x + iWidth, y, 0.0f ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.Color3fv( color ); |
|
meshBuilder.TexCoord2f( 0, fRight, fBottom ); |
|
meshBuilder.Position3f( x + iWidth, y + iHeight, 0.0f ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.Color3fv( color ); |
|
meshBuilder.TexCoord2f( 0, fLeft, fBottom ); |
|
meshBuilder.Position3f( x, y + iHeight, 0.0f ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.End(); |
|
pMesh->Draw(); |
|
} |