mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 09:49:36 +00:00
1
This commit is contained in:
@@ -0,0 +1,593 @@
|
||||
//========= 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;
|
||||
}
|
||||
Reference in New Issue
Block a user