|
|
//===== Copyright � 1996-2005, 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_hAVIMaterial = AVIMATERIAL_INVALID; m_hBIKMaterial = BIKMATERIAL_INVALID; m_width = m_height = m_numFrames = 1;
const char *pExt = Q_GetFileExtension( pName ); bool bIsAVI = pExt && !Q_stricmp( pExt, "avi" ); #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
bool bIsBIK = pExt && !Q_stricmp( pExt, "bik" ); #endif
if ( bIsAVI && IsPC() ) { m_hAVIMaterial = avi->CreateAVIMaterial( pName, pName, "GAME" ); if ( m_hAVIMaterial == AVIMATERIAL_INVALID ) return false;
IMaterial *pMaterial = avi->GetMaterial( m_hAVIMaterial ); avi->GetFrameSize( m_hAVIMaterial, &m_width, &m_height ); m_numFrames = avi->GetFrameCount( m_hAVIMaterial ); for ( int i = 0; i < kRenderModeCount; ++i ) { m_material[i] = pMaterial; pMaterial->IncrementReferenceCount(); } } #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
else if ( bIsBIK ) { m_hBIKMaterial = bik->CreateMaterial( pName, pName, "GAME" ); if ( m_hBIKMaterial == BIKMATERIAL_INVALID ) return false;
IMaterial *pMaterial = bik->GetMaterial( m_hBIKMaterial ); bik->GetFrameSize( m_hBIKMaterial, &m_width, &m_height ); m_numFrames = bik->GetFrameCount( m_hBIKMaterial ); for ( int i = 0; i < kRenderModeCount; ++i ) { m_material[i] = pMaterial; pMaterial->IncrementReferenceCount(); } } #endif
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) );
for ( int i = 0; i < kRenderModeCount; ++i ) { m_material[i] = NULL; }
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 ) { continue; }
// strip possible materials/
Q_snprintf( pMaterialPath, sizeof(pMaterialPath), "%s_rendermode_%d", pMaterialName + ( bIsUNC ? 0 : 10 ), 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 ( m_hAVIMaterial != AVIMATERIAL_INVALID ) { avi->DestroyAVIMaterial( m_hAVIMaterial ); m_hAVIMaterial = AVIMATERIAL_INVALID; }
#if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
if ( m_hBIKMaterial != BIKMATERIAL_INVALID ) { bik->DestroyMaterial( m_hBIKMaterial ); m_hBIKMaterial = BIKMATERIAL_INVALID; } #endif
UnloadMaterial(); }
//-----------------------------------------------------------------------------
// Is the sprite an AVI?
//-----------------------------------------------------------------------------
bool CEngineSprite::IsAVI() { return ( m_hAVIMaterial != AVIMATERIAL_INVALID ); }
//-----------------------------------------------------------------------------
// Is the sprite an BIK?
//-----------------------------------------------------------------------------
bool CEngineSprite::IsBIK() { return ( m_hBIKMaterial != BIKMATERIAL_INVALID ); }
//-----------------------------------------------------------------------------
// 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 ( IsAVI() ) { avi->GetTexCoordRange( m_hAVIMaterial, pMaxU, pMaxV ); } #if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
if ( IsBIK() ) { bik->GetTexCoordRange( m_hBIKMaterial, pMaxU, pMaxV ); } #endif
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 ( IsAVI() ) { avi->SetFrame( m_hAVIMaterial, nFrame ); return m_material[ 0 ]; // render mode is ignored for avi
}
#if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
if ( IsBIK() ) { bik->SetFrame( m_hBIKMaterial, nFrame ); return m_material[ 0 ]; // render mode is ignored for bink
} #endif
IMaterial *pMaterial = m_material[nRenderMode]; Assert( pMaterial ); if ( pMaterial == NULL ) return NULL;
IMaterialVar* pFrameVar = pMaterial->FindVarFast( "$frame", &frameCache ); if ( pFrameVar ) { pFrameVar->SetIntValue( nFrame ); }
return pMaterial; }
void CEngineSprite::SetFrame( RenderMode_t nRenderMode, int nFrame ) { if ( IsAVI() ) { avi->SetFrame( m_hAVIMaterial, nFrame ); return; }
#if !defined( _GAMECONSOLE ) || defined( BINK_ENABLED_FOR_CONSOLE )
if ( IsBIK() ) { bik->SetFrame( m_hBIKMaterial, nFrame ); return; } #endif
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( !IsAVI() && !IsBIK() );
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(); }
|