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.
477 lines
14 KiB
477 lines
14 KiB
//===== Copyright � 1996-2006, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: sheet code for particles and other sprite functions
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "bitmap/psheet.h"
|
|
#include "tier1/UtlStringMap.h"
|
|
#include "tier1/utlbuffer.h"
|
|
#include "tier2/fileutils.h"
|
|
|
|
// MOC_TODO: These probably shouldn't be here - maybe we should put CSheetExtended somewhere else, like materialsystem?
|
|
#include "materialsystem/imesh.h"
|
|
#include "materialsystem/imaterial.h"
|
|
#include "materialsystem/imaterialvar.h"
|
|
#include "materialsystem/itexture.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
CSheet::CSheet( void )
|
|
{
|
|
}
|
|
|
|
CSheet::CSheet( CUtlBuffer &buf )
|
|
{
|
|
// lets read a sheet
|
|
buf.ActivateByteSwappingIfBigEndian();
|
|
int nVersion = buf.GetInt(); // version#
|
|
int nNumCoordsPerFrame = (nVersion)?MAX_IMAGES_PER_FRAME_ON_DISK:1;
|
|
|
|
int nNumSequences = buf.GetInt();
|
|
int nNumToAllocate = nNumSequences;
|
|
// The old hardcoded arrays were 64.
|
|
// We will allocate 64 for now to handle content issues with
|
|
// rendering system trying to get at empty sequences.
|
|
if ( nNumToAllocate < 64 )
|
|
nNumToAllocate = 64;
|
|
|
|
m_SheetInfo.EnsureCapacity( nNumSequences );
|
|
for ( int i = 0; i < nNumToAllocate; ++i )
|
|
{
|
|
m_SheetInfo.AddToTail();
|
|
m_SheetInfo[i].m_pSamples = NULL;
|
|
m_SheetInfo[i].m_SeqFlags = 0;
|
|
m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence = 0;
|
|
m_SheetInfo[i].m_nNumFrames = 0;
|
|
m_SheetInfo[i].m_flFrameSpan = 0.0f;
|
|
}
|
|
|
|
while ( nNumSequences-- )
|
|
{
|
|
int nSequenceIndex = buf.GetInt();
|
|
if ( nSequenceIndex < 0 )
|
|
{
|
|
Warning( "Invalid sequence number (%d)!!!\n", nSequenceIndex );
|
|
return;
|
|
}
|
|
else if ( nSequenceIndex >= m_SheetInfo.Count() )
|
|
{
|
|
// This can happen if users delete intermediate sequences.
|
|
// For example you can wind up with n sequences if you delete a sequence between 0 and n
|
|
// In this case we will pad out the vector.
|
|
int i = -1;
|
|
while ( i < nSequenceIndex )
|
|
{
|
|
i = m_SheetInfo.AddToTail();
|
|
m_SheetInfo[i].m_pSamples = NULL;
|
|
m_SheetInfo[i].m_SeqFlags = 0;
|
|
m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence = 0;
|
|
m_SheetInfo[i].m_nNumFrames = 0;
|
|
m_SheetInfo[i].m_flFrameSpan = 0.0f;
|
|
}
|
|
}
|
|
|
|
m_SheetInfo[nSequenceIndex].m_SeqFlags = (uint8)(0xFF & buf.GetInt()); // reading an int, but not worth storing it all
|
|
int nFrameCount = buf.GetInt();
|
|
// Save off how many frames we have for this sequence
|
|
m_SheetInfo[nSequenceIndex].m_nNumFrames = nFrameCount;
|
|
Assert( nFrameCount >= 0 );
|
|
bool bSingleFrameSequence = ( nFrameCount == 1 );
|
|
|
|
int nTimeSamples = bSingleFrameSequence ? 1 : SEQUENCE_SAMPLE_COUNT;
|
|
|
|
if ( m_SheetInfo[nSequenceIndex].m_pSamples )
|
|
{
|
|
Warning( "Invalid particle sheet sequence index. There are more than one items with a sequence index of %d. We are only using the last one we found..\n", nSequenceIndex );
|
|
delete[] m_SheetInfo[nSequenceIndex].m_pSamples;
|
|
}
|
|
m_SheetInfo[nSequenceIndex].m_pSamples = new SheetSequenceSample_t[ nTimeSamples ];
|
|
|
|
int fTotalSequenceTime = ( int )buf.GetFloat();
|
|
float InterpKnot[SEQUENCE_SAMPLE_COUNT];
|
|
float InterpValue[SEQUENCE_SAMPLE_COUNT];
|
|
SheetSequenceSample_t Samples[SEQUENCE_SAMPLE_COUNT];
|
|
float fCurTime = 0.;
|
|
for( int nFrm = 0 ; nFrm < nFrameCount; nFrm++ )
|
|
{
|
|
float fThisDuration = buf.GetFloat();
|
|
InterpValue[ nFrm ] = nFrm;
|
|
InterpKnot [ nFrm ] = SEQUENCE_SAMPLE_COUNT*( fCurTime/ fTotalSequenceTime );
|
|
SheetSequenceSample_t &seq = Samples[ nFrm ];
|
|
seq.m_fBlendFactor = 0.0f;
|
|
for(int nImage = 0 ; nImage< nNumCoordsPerFrame; nImage++ )
|
|
{
|
|
SequenceSampleTextureCoords_t &s=seq.m_TextureCoordData[nImage];
|
|
s.m_fLeft_U0 = buf.GetFloat();
|
|
s.m_fTop_V0 = buf.GetFloat();
|
|
s.m_fRight_U0 = buf.GetFloat();
|
|
s.m_fBottom_V0 = buf.GetFloat();
|
|
}
|
|
if ( nNumCoordsPerFrame == 1 )
|
|
seq.CopyFirstFrameToOthers();
|
|
fCurTime += fThisDuration;
|
|
m_SheetInfo[nSequenceIndex].m_flFrameSpan = fCurTime;
|
|
}
|
|
// now, fill in the whole table
|
|
for( int nIdx = 0; nIdx < nTimeSamples; nIdx++ )
|
|
{
|
|
float flIdxA, flIdxB, flInterp;
|
|
GetInterpolationData( InterpKnot, InterpValue, nFrameCount,
|
|
SEQUENCE_SAMPLE_COUNT,
|
|
nIdx,
|
|
! ( m_SheetInfo[nSequenceIndex].m_SeqFlags & SEQ_FLAG_CLAMP ),
|
|
&flIdxA, &flIdxB, &flInterp );
|
|
SheetSequenceSample_t sA = Samples[(int) flIdxA];
|
|
SheetSequenceSample_t sB = Samples[(int) flIdxB];
|
|
SheetSequenceSample_t &oseq = m_SheetInfo[nSequenceIndex].m_pSamples[nIdx];
|
|
|
|
oseq.m_fBlendFactor = flInterp;
|
|
for(int nImage = 0 ; nImage< MAX_IMAGES_PER_FRAME_IN_MEMORY; nImage++ )
|
|
{
|
|
SequenceSampleTextureCoords_t &src0=sA.m_TextureCoordData[nImage];
|
|
SequenceSampleTextureCoords_t &src1=sB.m_TextureCoordData[nImage];
|
|
SequenceSampleTextureCoords_t &o=oseq.m_TextureCoordData[nImage];
|
|
o.m_fLeft_U0 = src0.m_fLeft_U0;
|
|
o.m_fTop_V0 = src0.m_fTop_V0;
|
|
o.m_fRight_U0 = src0.m_fRight_U0;
|
|
o.m_fBottom_V0 = src0.m_fBottom_V0;
|
|
o.m_fLeft_U1 = src1.m_fLeft_U0;
|
|
o.m_fTop_V1 = src1.m_fTop_V0;
|
|
o.m_fRight_U1 = src1.m_fRight_U0;
|
|
o.m_fBottom_V1 = src1.m_fBottom_V0;
|
|
}
|
|
}
|
|
}
|
|
// now, fill in all unseen sequences with copies of the first seen sequence to prevent crashes
|
|
// while editing
|
|
int nFirstSequence = -1;
|
|
for(int i= 0 ; i < m_SheetInfo.Count(); i++)
|
|
{
|
|
if ( m_SheetInfo[i].m_pSamples )
|
|
{
|
|
nFirstSequence = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( nFirstSequence != -1 )
|
|
{
|
|
for(int i=0 ; i < m_SheetInfo.Count(); i++)
|
|
{
|
|
if ( m_SheetInfo[i].m_pSamples == NULL )
|
|
{
|
|
m_SheetInfo[i].m_pSamples = m_SheetInfo[nFirstSequence].m_pSamples;
|
|
m_SheetInfo[i].m_SeqFlags = m_SheetInfo[nFirstSequence].m_SeqFlags;
|
|
m_SheetInfo[i].m_nNumFrames = m_SheetInfo[nFirstSequence].m_nNumFrames;
|
|
Assert( m_SheetInfo[i].m_nNumFrames >= 1 );
|
|
m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CSheet::~CSheet( void )
|
|
{
|
|
for( int i = 0; i < m_SheetInfo.Count(); i++ )
|
|
{
|
|
if ( m_SheetInfo[i].m_pSamples && ( !m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence ) )
|
|
{
|
|
delete[] m_SheetInfo[i].m_pSamples;
|
|
}
|
|
}
|
|
}
|
|
|
|
const SheetSequenceSample_t *CSheet::GetSampleForSequence( float flAge, float flAgeScale, int nSequence, bool bForceLoop )
|
|
{
|
|
if ( m_SheetInfo[nSequence].m_nNumFrames == 1 )
|
|
return (const SheetSequenceSample_t *) &m_SheetInfo[nSequence].m_pSamples[0];
|
|
|
|
flAge *= flAgeScale;
|
|
unsigned int nFrame = ( int )flAge;
|
|
|
|
if ( ( m_SheetInfo[nSequence].m_SeqFlags & SEQ_FLAG_CLAMP ) && !bForceLoop )
|
|
{
|
|
nFrame = MIN( nFrame, SEQUENCE_SAMPLE_COUNT-1 );
|
|
}
|
|
else
|
|
{
|
|
nFrame &= SEQUENCE_SAMPLE_COUNT-1;
|
|
}
|
|
|
|
return (const SheetSequenceSample_t *) &m_SheetInfo[nSequence].m_pSamples[nFrame];
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CSheetExtended::CSheetExtended( IMaterial* pMaterial )
|
|
{
|
|
m_Material.Init( pMaterial );
|
|
m_pSheetData = NULL;
|
|
LoadFromMaterial(pMaterial);
|
|
}
|
|
|
|
CSheetExtended::~CSheetExtended()
|
|
{
|
|
delete m_pSheetData;
|
|
}
|
|
|
|
void CSheetExtended::LoadFromMaterial( IMaterial* pMaterial )
|
|
{
|
|
if ( pMaterial == NULL )
|
|
return;
|
|
|
|
bool bFound = false;
|
|
IMaterialVar *pVar = pMaterial->FindVar( "$basetexture", &bFound );
|
|
if ( !pVar || !bFound || !pVar->IsDefined() )
|
|
return;
|
|
|
|
ITexture *pTex = pVar->GetTextureValue();
|
|
if ( !pTex || pTex->IsError() )
|
|
return;
|
|
|
|
size_t nBytes;
|
|
void const *pSheetData = pTex->GetResourceData( VTF_RSRC_SHEET, &nBytes );
|
|
|
|
if ( pSheetData )
|
|
{
|
|
CUtlBuffer bufLoad( pSheetData, nBytes, CUtlBuffer::READ_ONLY );
|
|
LoadFromBuffer( bufLoad );
|
|
}
|
|
}
|
|
|
|
void CSheetExtended::LoadFromBuffer( CUtlBuffer& buf )
|
|
{
|
|
m_pSheetData = new CSheet(buf);
|
|
}
|
|
|
|
int CSheetExtended::GetSheetSequenceCount()
|
|
{
|
|
if ( m_pSheetData == NULL )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int nUniqueSequences = 0;
|
|
|
|
for ( int i = 0; i < m_pSheetData->m_SheetInfo.Count(); ++i )
|
|
{
|
|
if ( !m_pSheetData->m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence )
|
|
{
|
|
nUniqueSequences++;
|
|
}
|
|
}
|
|
|
|
return nUniqueSequences;
|
|
}
|
|
|
|
int CSheetExtended::GetNthSequenceIndex( int nSequenceNumber )
|
|
{
|
|
if ( m_pSheetData == NULL )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int nCountValidSequences = 0;
|
|
for ( int i = 0; i < m_pSheetData->m_SheetInfo.Count(); ++i )
|
|
{
|
|
if ( !m_pSheetData->m_SheetInfo[i].m_bSequenceIsCopyOfAnotherSequence )
|
|
{
|
|
if ( nCountValidSequences == nSequenceNumber )
|
|
{
|
|
return i;
|
|
}
|
|
else
|
|
{
|
|
nCountValidSequences++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static SheetSequenceSample_t s_DefaultSheetSequence =
|
|
{
|
|
0.0f, 0.0f, 1.0f, 1.0f,
|
|
0.0f, 0.0f, 1.0f, 1.0f,
|
|
0.0f, 0.0f, 1.0f, 1.0f,
|
|
0.0f, 0.0f, 1.0f, 1.0f,
|
|
|
|
1.0f,
|
|
};
|
|
|
|
const SheetSequenceSample_t *CSheetExtended::GetSampleForSequence( float flAge, float flAgeScale, int nSequence, bool bForceLoop )
|
|
{
|
|
if ( m_pSheetData == NULL )
|
|
return &s_DefaultSheetSequence;
|
|
return m_pSheetData->GetSampleForSequence( flAge, flAgeScale, nSequence, bForceLoop );
|
|
}
|
|
|
|
float CSheetExtended::GetSequenceTimeSpan( int nSequenceIndex )
|
|
{
|
|
if ( m_pSheetData == NULL )
|
|
{
|
|
return 0.f;
|
|
}
|
|
|
|
return m_pSheetData->m_SheetInfo[nSequenceIndex].m_flFrameSpan;
|
|
}
|
|
|
|
bool CSheetExtended::ValidSheetData()
|
|
{
|
|
return (m_pSheetData != NULL);
|
|
}
|
|
|
|
bool CSheetExtended::SequenceHasAlphaData( int nSequenceIndex )
|
|
{
|
|
return !(m_pSheetData->m_SheetInfo[nSequenceIndex].m_SeqFlags & SEQ_FLAG_NO_ALPHA);
|
|
}
|
|
|
|
bool CSheetExtended::SequenceHasColorData( int nSequenceIndex )
|
|
{
|
|
return !(m_pSheetData->m_SheetInfo[nSequenceIndex].m_SeqFlags & SEQ_FLAG_NO_COLOR);
|
|
}
|
|
|
|
inline void TexCoords0( CMeshBuilder& meshBuilder, int nChannel, const SequenceSampleTextureCoords_t* pSample )
|
|
{
|
|
meshBuilder.TexCoord4f( nChannel, pSample->m_fLeft_U0, pSample->m_fTop_V0, pSample->m_fRight_U0, pSample->m_fBottom_V0 );
|
|
}
|
|
|
|
inline void TexCoords1( CMeshBuilder& meshBuilder, int nChannel, const SequenceSampleTextureCoords_t* pSample )
|
|
{
|
|
meshBuilder.TexCoord4f( nChannel, pSample->m_fLeft_U1, pSample->m_fTop_V1, pSample->m_fRight_U1, pSample->m_fBottom_V1 );
|
|
}
|
|
|
|
inline void SpriteCardVert( CMeshBuilder& meshBuilder,
|
|
const Vector &vCenter,
|
|
const float flRadius,
|
|
const SheetSequenceSample_t * pSample,
|
|
const SequenceSampleTextureCoords_t *pSample0,
|
|
const SequenceSampleTextureCoords_t *pSecondTexture0,
|
|
const SheetSequenceSample_t *pSample1Data,
|
|
const SequenceSampleTextureCoords_t *pSample1,
|
|
float flChannel3U, float flChannel3V )
|
|
{
|
|
meshBuilder.Position3fv( vCenter.Base() );
|
|
TexCoords0( meshBuilder, 0, pSample0 );
|
|
TexCoords1( meshBuilder, 1, pSample0 );
|
|
meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, 0.0f, flRadius, 0.0f );
|
|
meshBuilder.TexCoord2f( 3, flChannel3U, flChannel3V );
|
|
TexCoords0( meshBuilder, 4, pSecondTexture0 );
|
|
|
|
if ( pSample1 )
|
|
{
|
|
TexCoords0( meshBuilder, 5, pSample1 );
|
|
TexCoords1( meshBuilder, 6, pSample1 );
|
|
meshBuilder.TexCoord4f( 7, pSample1Data->m_fBlendFactor, 0, 0, 0 );
|
|
}
|
|
}
|
|
|
|
void CSheetExtended::DrawSheet( IMesh *pMesh, const Vector &vCenter, float flRadius, int nSheetSequence, float flAge, float flSheetPreviewSpeed, bool bLoopSheetPreview, int nSecondarySequence, bool bOverrideSpriteCard )
|
|
{
|
|
// nSecondarySequence
|
|
bool bSpriteCardMaterial = false;
|
|
|
|
bSpriteCardMaterial = !bOverrideSpriteCard && m_Material && m_Material->IsSpriteCard();
|
|
|
|
const SheetSequenceSample_t *pSample = GetSampleForSequence( flAge, flSheetPreviewSpeed, nSheetSequence, bLoopSheetPreview );
|
|
const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
|
|
const SequenceSampleTextureCoords_t *pSecondTexture0 = &(pSample->m_TextureCoordData[1]);
|
|
const SheetSequenceSample_t *pSample1Data = NULL;
|
|
const SequenceSampleTextureCoords_t *pSample1 = NULL;
|
|
|
|
if ( (nSecondarySequence != -1) && IsMaterialDualSequence( m_Material ) )
|
|
{
|
|
const float SECONDARY_AGE_MULTIPLIER = 0.1f; // hardcoded 'best guess' at relative speed, since we don't want a whole UI for a second speed
|
|
float flSecondaryAge = flAge * SECONDARY_AGE_MULTIPLIER;
|
|
pSample1Data = GetSampleForSequence( flSecondaryAge, flSheetPreviewSpeed, nSecondarySequence, bLoopSheetPreview );
|
|
pSample1 = &(pSample1Data->m_TextureCoordData[0]);
|
|
}
|
|
|
|
CMeshBuilder meshBuilder;
|
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
|
|
|
|
if ( bSpriteCardMaterial )
|
|
{
|
|
SpriteCardVert( meshBuilder, vCenter, flRadius, pSample, pSample0, pSecondTexture0, pSample1Data, pSample1, 0, 0 );
|
|
}
|
|
else
|
|
{
|
|
meshBuilder.Position3fv( (vCenter + Vector(-flRadius,-flRadius,0)).Base() );
|
|
meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
|
|
}
|
|
meshBuilder.Color4ub( 255, 255, 255, 255 );
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
if ( bSpriteCardMaterial )
|
|
{
|
|
SpriteCardVert( meshBuilder, vCenter, flRadius, pSample, pSample0, pSecondTexture0, pSample1Data, pSample1, 1, 0 );
|
|
}
|
|
else
|
|
{
|
|
meshBuilder.Position3fv( (vCenter + Vector(+flRadius,-flRadius,0)).Base() );
|
|
meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
|
|
}
|
|
meshBuilder.Color4ub( 255, 255, 255, 255 );
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
if ( bSpriteCardMaterial )
|
|
{
|
|
SpriteCardVert( meshBuilder, vCenter, flRadius, pSample, pSample0, pSecondTexture0, pSample1Data, pSample1, 1, 1 );
|
|
}
|
|
else
|
|
{
|
|
meshBuilder.Position3fv( (vCenter + Vector(+flRadius,+flRadius,0)).Base() );
|
|
meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
|
|
}
|
|
meshBuilder.Color4ub( 255, 255, 255, 255 );
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
if ( bSpriteCardMaterial )
|
|
{
|
|
SpriteCardVert( meshBuilder, vCenter, flRadius, pSample, pSample0, pSecondTexture0, pSample1Data, pSample1, 0, 1 );
|
|
}
|
|
else
|
|
{
|
|
meshBuilder.Position3fv( (vCenter + Vector(-flRadius,+flRadius,0)).Base() );
|
|
meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
|
|
}
|
|
meshBuilder.Color4ub( 255, 255, 255, 255 );
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.End();
|
|
pMesh->Draw();
|
|
}
|
|
|
|
bool CSheetExtended::IsMaterialDualSequence( IMaterial* pMat )
|
|
{
|
|
if ( !pMat )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bFound = false;
|
|
IMaterialVar *pVar = pMat->FindVar( "$DUALSEQUENCE", &bFound );
|
|
|
|
return ( pVar && bFound && pVar->IsDefined() && pVar->GetIntValue() );
|
|
}
|
|
|
|
bool CSheetExtended::IsMaterialSeparateAlphaColorMaterial( IMaterial* pMat )
|
|
{
|
|
if ( !pMat || !IsMaterialDualSequence(pMat) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bFound = false;
|
|
IMaterialVar *pVar = pMat->FindVar( "$SEQUENCE_BLEND_MODE", &bFound );
|
|
|
|
if ( !pVar || !bFound || !pVar->IsDefined() )
|
|
return false;
|
|
|
|
return (pVar->GetIntValue() == 1);
|
|
}
|