csgo-2018-source/bitmap/psheet.cpp
2021-07-24 21:11:47 -07:00

478 lines
14 KiB
C++
Raw Permalink Blame History

//===== Copyright <20> 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);
}