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.
593 lines
17 KiB
593 lines
17 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//===========================================================================// |
|
|
|
#include "stdafx.h" |
|
#include "const.h" |
|
#include "Sprite.h" |
|
#include "Material.h" // FIXME: we need to work only with IEditorTexture! |
|
#include "materialsystem/imaterial.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "Render3d.h" |
|
#include "camera.h" |
|
#include "tier1/utldict.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
|
|
class CSpriteDataCache |
|
{ |
|
public: |
|
CMaterial *m_pMaterial; |
|
IMaterialVar *m_pFrameVar; |
|
IMaterialVar *m_pRenderModeVar; |
|
IMaterialVar *m_pOrientationVar; |
|
IMaterialVar *m_pOriginVar; |
|
int m_Width; |
|
int m_Height; |
|
bool m_bOriginVarFound; |
|
bool m_bOrientationVarFound; |
|
}; |
|
|
|
|
|
CUtlDict<CSpriteDataCache*, int> g_SpriteDataCache; |
|
SpriteCache_t CSpriteCache::m_Cache[SPRITE_CACHE_SIZE]; |
|
int CSpriteCache::m_nItems = 0; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns an instance of a particular studio model. If the model is |
|
// in the cache, a pointer to that model is returned. If not, a new one |
|
// is created and added to the cache. |
|
// Input : pszModelPath - Full path of the .MDL file. |
|
//----------------------------------------------------------------------------- |
|
CSpriteModel *CSpriteCache::CreateSprite(const char *pszSpritePath) |
|
{ |
|
// |
|
// First look for the sprite in the cache. If it's there, increment the |
|
// reference count and return a pointer to the cached sprite. |
|
// |
|
for (int i = 0; i < m_nItems; i++) |
|
{ |
|
if (!stricmp(pszSpritePath, m_Cache[i].pszPath)) |
|
{ |
|
m_Cache[i].nRefCount++; |
|
return(m_Cache[i].pSprite); |
|
} |
|
} |
|
|
|
// |
|
// If it isn't there, try to create one. |
|
// |
|
CSpriteModel *pSprite = new CSpriteModel; |
|
|
|
if (pSprite != NULL) |
|
{ |
|
if (!pSprite->LoadSprite(pszSpritePath)) |
|
{ |
|
delete pSprite; |
|
pSprite = NULL; |
|
} |
|
} |
|
|
|
// |
|
// If we successfully created it, add it to the cache. |
|
// |
|
if (pSprite != NULL) |
|
{ |
|
CSpriteCache::AddSprite(pSprite, pszSpritePath); |
|
} |
|
|
|
return(pSprite); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds the model to the cache, setting the reference count to one. |
|
// Input : pModel - Model to add to the cache. |
|
// pszSpritePath - The full path of the .MDL file, which is used as a |
|
// key in the sprite cache. |
|
// Output : Returns TRUE if the sprite was successfully added, FALSE if we ran |
|
// out of memory trying to add the sprite to the cache. |
|
//----------------------------------------------------------------------------- |
|
bool CSpriteCache::AddSprite(CSpriteModel *pSprite, const char *pszSpritePath) |
|
{ |
|
// |
|
// Copy the sprite pointer. |
|
// |
|
m_Cache[m_nItems].pSprite = pSprite; |
|
|
|
// |
|
// Allocate space for and copy the model path. |
|
// |
|
m_Cache[m_nItems].pszPath = new char [strlen(pszSpritePath) + 1]; |
|
if (m_Cache[m_nItems].pszPath != NULL) |
|
{ |
|
strcpy(m_Cache[m_nItems].pszPath, pszSpritePath); |
|
} |
|
else |
|
{ |
|
return(false); |
|
} |
|
|
|
m_Cache[m_nItems].nRefCount = 1; |
|
|
|
m_nItems++; |
|
|
|
return(true); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Increments the reference count on a sprite in the cache. Called by |
|
// client code when a pointer to the sprite is copied, making that |
|
// reference independent. |
|
// Input : pModel - Sprite for which to increment the reference count. |
|
//----------------------------------------------------------------------------- |
|
void CSpriteCache::AddRef(CSpriteModel *pSprite) |
|
{ |
|
for (int i = 0; i < m_nItems; i++) |
|
{ |
|
if (m_Cache[i].pSprite == pSprite) |
|
{ |
|
m_Cache[i].nRefCount++; |
|
return; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by client code to release an instance of a model. If the |
|
// model's reference count is zero, the model is freed. |
|
// Input : pModel - Pointer to the model to release. |
|
//----------------------------------------------------------------------------- |
|
void CSpriteCache::Release(CSpriteModel *pSprite) |
|
{ |
|
for (int i = 0; i < m_nItems; i++) |
|
{ |
|
if (m_Cache[i].pSprite == pSprite) |
|
{ |
|
m_Cache[i].nRefCount--; |
|
Assert(m_Cache[i].nRefCount >= 0); |
|
|
|
// |
|
// If this model is no longer referenced, free it and remove it |
|
// from the cache. |
|
// |
|
if (m_Cache[i].nRefCount <= 0) |
|
{ |
|
// |
|
// Free the path, which was allocated by AddModel. |
|
// |
|
delete [] m_Cache[i].pszPath; |
|
delete m_Cache[i].pSprite; |
|
|
|
// |
|
// Decrement the item count and copy the last element in the cache over |
|
// this element. |
|
// |
|
m_nItems--; |
|
|
|
m_Cache[i].pSprite = m_Cache[m_nItems].pSprite; |
|
m_Cache[i].pszPath = m_Cache[m_nItems].pszPath; |
|
m_Cache[i].nRefCount = m_Cache[m_nItems].nRefCount; |
|
} |
|
|
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor. |
|
//----------------------------------------------------------------------------- |
|
CSpriteModel::CSpriteModel(void) : |
|
m_pMaterial(0), m_NumFrames(-1), m_fScale(1.0), m_Origin(0,0,0), m_UL(0,0), m_LR(0,0), m_TexUL(0,1), m_TexLR(1,0), m_bInvert(false) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor. Frees the sprite image and descriptor. |
|
//----------------------------------------------------------------------------- |
|
CSpriteModel::~CSpriteModel(void) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the render mode |
|
//----------------------------------------------------------------------------- |
|
void CSpriteModel::SetRenderMode( const int mode ) |
|
{ |
|
if (m_pMaterial && m_pRenderModeVar) |
|
{ |
|
if ( mode != m_pRenderModeVar->GetIntValue() ) |
|
{ |
|
m_pRenderModeVar->SetIntValue( mode ); |
|
m_pMaterial->GetMaterial()->RecomputeStateSnapshots(); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pEntity - |
|
// type - |
|
// forward - |
|
// right - |
|
// up - |
|
//----------------------------------------------------------------------------- |
|
void CSpriteModel::GetSpriteAxes(QAngle& Angles, int type, Vector& forward, Vector& right, Vector& up, Vector& ViewUp, Vector& ViewRight, Vector& ViewForward) |
|
{ |
|
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] = -m_Origin[0]; |
|
tvec[1] = -m_Origin[1]; |
|
tvec[2] = -m_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.999848) || (dot < -0.999848)) // 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] = ViewUp[i]; |
|
right[i] = ViewRight[i]; |
|
forward[i] = ViewForward[i]; |
|
} |
|
break; |
|
} |
|
|
|
case SPR_VP_PARALLEL_UPRIGHT: |
|
{ |
|
// generate the sprite's axes, with vup 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 = ViewForward[2]; // same as DotProduct (vpn, r_spritedesc.vup) because |
|
// r_spritedesc.vup is 0, 0, 1 |
|
if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 |
|
return; |
|
|
|
up[0] = 0; |
|
up[1] = 0; |
|
up[2] = 1; |
|
|
|
right[0] = ViewForward[1]; |
|
right[1] = -ViewForward[0]; |
|
right[2] = 0; |
|
VectorNormalize (right); |
|
|
|
forward[0] = -right[1]; |
|
forward[1] = right[0]; |
|
forward[2] = 0; |
|
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 / 360); |
|
sr = sin(angle); |
|
cr = cos(angle); |
|
|
|
for (i=0 ; i<3 ; i++) |
|
{ |
|
forward[i] = ViewForward[i]; |
|
right[i] = ViewRight[i] * cr + ViewUp[i] * sr; |
|
up[i] = ViewRight[i] * -sr + ViewUp[i] * cr; |
|
} |
|
break; |
|
} |
|
|
|
default: |
|
{ |
|
//Sys_Error ("R_DrawSprite: Bad sprite type %d", type); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the sprite's scale |
|
//----------------------------------------------------------------------------- |
|
|
|
void CSpriteModel::SetScale( const float fScale ) |
|
{ |
|
m_fScale = fScale; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the sprite's origin |
|
//----------------------------------------------------------------------------- |
|
|
|
void CSpriteModel::SetOrigin( const Vector &v ) |
|
{ |
|
m_Origin = v; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the sprite's origin |
|
//----------------------------------------------------------------------------- |
|
|
|
void CSpriteModel::GetOrigin( Vector &v ) |
|
{ |
|
v = m_Origin; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the sprite's vertical inversion |
|
//----------------------------------------------------------------------------- |
|
|
|
void CSpriteModel::SetInvert( const bool b ) |
|
{ |
|
m_bInvert = b; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets the Euler angles for the model. |
|
// Input : fAngles - A pointer to engine PITCH, YAW, and ROLL angles. |
|
//----------------------------------------------------------------------------- |
|
void CSpriteModel::SetAngles( const QAngle& pfAngles ) |
|
{ |
|
m_Angles[PITCH] = pfAngles[PITCH]; |
|
m_Angles[YAW] = pfAngles[YAW]; |
|
m_Angles[ROLL] = pfAngles[ROLL]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the material's primative type |
|
//----------------------------------------------------------------------------- |
|
|
|
void CSpriteModel::SetMaterialPrimitiveType( const MaterialPrimitiveType_t type ) |
|
{ |
|
m_MaterialPrimitiveType = type; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Renders the sprite in 3D mode |
|
//----------------------------------------------------------------------------- |
|
|
|
void CSpriteModel::DrawSprite3D( CRender3D *pRender, unsigned char color[3] ) |
|
{ |
|
Vector corner, spritex, spritey, spritez; |
|
Vector ViewUp; |
|
Vector ViewRight; |
|
Vector ViewForward; |
|
|
|
pRender->GetViewUp( ViewUp ); |
|
pRender->GetViewRight( ViewRight ); |
|
pRender->GetViewForward( ViewForward ); |
|
|
|
GetSpriteAxes(m_Angles, GetType(), spritez, spritex, spritey, ViewUp, ViewRight, ViewForward); |
|
|
|
Vector2D ul, lr; |
|
Vector2DMultiply( m_UL, m_fScale, ul ); |
|
Vector2DMultiply( m_LR, m_fScale, lr ); |
|
|
|
VectorMA( m_Origin, ul.x, spritex, corner ); |
|
VectorMA( corner, lr.y, spritey, corner ); |
|
spritex *= (lr.x - ul.x); |
|
spritey *= (ul.y - lr.y); |
|
|
|
Vector2D texul, texlr; |
|
texul.x = m_TexUL.x; |
|
texul.y = m_bInvert ? m_TexLR.y : m_TexUL.y; |
|
texlr.x = m_TexLR.x; |
|
texlr.y = m_bInvert ? m_TexUL.y : m_TexLR.y; |
|
|
|
|
|
CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); |
|
pRender->BindTexture( m_pMaterial ); |
|
IMesh* pMesh = pRenderContext->GetDynamicMesh(); |
|
|
|
CMeshBuilder meshBuilder; |
|
meshBuilder.Begin( pMesh, m_MaterialPrimitiveType, 4 ); |
|
|
|
meshBuilder.Position3fv(corner.Base()); |
|
meshBuilder.TexCoord2f(0, texul.x, texul.y); |
|
meshBuilder.Color3ub( color[0], color[1], color[2] ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
corner += spritey; |
|
meshBuilder.Position3fv(corner.Base()); |
|
meshBuilder.TexCoord2f(0, texul.x, texlr.y); |
|
meshBuilder.Color3ub( color[0], color[1], color[2] ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
corner += spritex; |
|
meshBuilder.Position3fv(corner.Base()); |
|
meshBuilder.TexCoord2f(0, texlr.x, texlr.y); |
|
meshBuilder.Color3ub( color[0], color[1], color[2] ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
corner -= spritey; |
|
meshBuilder.Position3fv(corner.Base()); |
|
meshBuilder.TexCoord2f(0, texlr.x, texul.y); |
|
meshBuilder.Color3ub( color[0], color[1], color[2] ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.End(); |
|
pMesh->Draw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Binds a sprite |
|
//----------------------------------------------------------------------------- |
|
void CSpriteModel::Bind( CRender* pRender, int frame ) |
|
{ |
|
if (m_pMaterial && m_pFrameVar) |
|
{ |
|
m_pFrameVar->SetIntValue( frame ); |
|
pRender->BindTexture( m_pMaterial ); |
|
} |
|
} |
|
|
|
|
|
CSpriteDataCache* LookupSpriteDataCache( const char *pSpritePath ) |
|
{ |
|
char filename[MAX_PATH]; |
|
V_strncpy( filename, pSpritePath, sizeof( filename ) ); |
|
V_FixSlashes( filename ); |
|
|
|
CSpriteDataCache *pData; |
|
int i = g_SpriteDataCache.Find( filename ); |
|
if ( i == g_SpriteDataCache.InvalidIndex() ) |
|
{ |
|
pData = new CSpriteDataCache; |
|
memset( pData, 0, sizeof( *pData ) ); |
|
g_SpriteDataCache.Insert( filename, pData ); |
|
|
|
pData->m_pMaterial = CMaterial::CreateMaterial( filename, true ); |
|
if ( pData->m_pMaterial && pData->m_pMaterial->GetMaterial() ) |
|
{ |
|
pData->m_Width = pData->m_pMaterial->GetWidth(); |
|
pData->m_Height = pData->m_pMaterial->GetHeight(); |
|
pData->m_pFrameVar = pData->m_pMaterial->GetMaterial()->FindVar( "$spriteFrame", 0 ); |
|
pData->m_pRenderModeVar = pData->m_pMaterial->GetMaterial()->FindVar( "$spriterendermode", 0 ); |
|
|
|
pData->m_pOrientationVar = pData->m_pMaterial->GetMaterial()->FindVar( "$spriteOrientation", &pData->m_bOrientationVarFound, false ); |
|
pData->m_pOriginVar = pData->m_pMaterial->GetMaterial()->FindVar( "$spriteorigin", &pData->m_bOriginVarFound ); |
|
} |
|
} |
|
else |
|
{ |
|
pData = g_SpriteDataCache[i]; |
|
} |
|
|
|
return pData; |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Loads a sprite material. |
|
// Input : pszSpritePath - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
|
|
bool CSpriteModel::LoadSprite(const char *pszSpritePath) |
|
{ |
|
CSpriteDataCache *pCache = LookupSpriteDataCache( pszSpritePath ); |
|
|
|
m_pMaterial = pCache->m_pMaterial; |
|
if( m_pMaterial && m_pMaterial->GetMaterial() ) |
|
{ |
|
m_Width = pCache->m_Width; |
|
m_Height = pCache->m_Height; |
|
// FIXME: m_NumFrames = m_pMaterial->GetMaterial()->GetNumAnimationFrames(); |
|
m_pFrameVar = pCache->m_pFrameVar; |
|
m_pRenderModeVar = pCache->m_pRenderModeVar; |
|
|
|
IMaterialVar *orientationVar = pCache->m_pOrientationVar; |
|
bool found = pCache->m_bOrientationVarFound; |
|
if( found ) |
|
{ |
|
m_Type = orientationVar->GetIntValue(); |
|
} |
|
else |
|
{ |
|
m_Type = SPR_VP_PARALLEL_UPRIGHT; |
|
} |
|
|
|
IMaterialVar *pOriginVar = pCache->m_pOriginVar; |
|
Vector origin; |
|
found = pCache->m_bOriginVarFound; |
|
if( !found || ( pOriginVar->GetType() != MATERIAL_VAR_TYPE_VECTOR ) ) |
|
{ |
|
origin[0] = -m_Width * 0.5f; |
|
origin[1] = m_Height * 0.5f; |
|
} |
|
else |
|
{ |
|
Vector originVarValue; |
|
pOriginVar->GetVecValue( originVarValue.Base(), 3); |
|
origin[0] = -m_Width * originVarValue[0]; |
|
origin[1] = m_Height * originVarValue[1]; |
|
} |
|
|
|
m_UL.y = origin[1]; |
|
m_LR.y = origin[1] - m_Height; |
|
m_UL.x = origin[0]; |
|
m_LR.x = m_Width + origin[0]; |
|
|
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Kind of a hack... |
|
//----------------------------------------------------------------------------- |
|
int CSpriteModel::GetFrameCount() |
|
{ |
|
// FIXME: Figure out the correct time to cache in this info |
|
if ((m_NumFrames < 0) && m_pMaterial) |
|
{ |
|
m_NumFrames = m_pMaterial->GetMaterial()->GetNumAnimationFrames(); |
|
} |
|
return (m_NumFrames < 0) ? 0 : m_NumFrames; |
|
} |