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.
388 lines
15 KiB
388 lines
15 KiB
//===== Copyright � 1996-2007, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: see CParticleSnapshot declaration (in particles.h)
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "tier0/platform.h"
|
|
#include "particles/particles.h"
|
|
#include "filesystem.h"
|
|
#include "tier2/tier2.h"
|
|
#include "tier2/fileutils.h"
|
|
#include "tier1/utlbuffer.h"
|
|
#include "tier1/UtlStringMap.h"
|
|
#include "tier1/strtools.h"
|
|
#include "dmxloader/dmxloader.h"
|
|
#include "dmxloader/utlsoacontainer_serialization.h"
|
|
#include "tier1/lzmaDecoder.h"
|
|
#include "tier0/vprof.h"
|
|
#include "particles_internal.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
// This macro is used to process the attribute mapping parameters, for both vararg versions of Init():
|
|
#define ATTRIBUTE_MAPPING_LOOP( _lastarg_ ) \
|
|
va_list args; \
|
|
va_start( args, _lastarg_ ); \
|
|
for(;;) \
|
|
{ \
|
|
int nFieldNumber = va_arg( args, int ); \
|
|
if ( nFieldNumber == -1 ) \
|
|
break; \
|
|
/* Associate nParticleAttribute with nFieldNumber */ \
|
|
int nParticleAttribute = va_arg( args, int );
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purge, clear the container back to its initial state
|
|
//-----------------------------------------------------------------------------
|
|
void CParticleSnapshot::Purge( void )
|
|
{
|
|
m_Container.Purge();
|
|
m_pContainer = NULL;
|
|
for ( int i = 0; i < ARRAYSIZE( m_ParticleAttributeToContainerAttribute ); i++ ) m_ParticleAttributeToContainerAttribute[ i ] = -1;
|
|
for ( int i = 0; i < ARRAYSIZE( m_ContainerAttributeToParticleAttribute ); i++ ) m_ContainerAttributeToParticleAttribute[ i ] = -1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// AddAttributeMapping, add a new field/attribute pair
|
|
//-----------------------------------------------------------------------------
|
|
bool CParticleSnapshot::AddAttributeMapping( int nFieldNumber, int nParticleAttribute, const char *pFunc )
|
|
{
|
|
if ( ( ( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] != -1 ) && ( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] != nFieldNumber ) ) ||
|
|
( ( m_ContainerAttributeToParticleAttribute[ nFieldNumber ] != -1 ) && ( m_ContainerAttributeToParticleAttribute[ nFieldNumber ] != nParticleAttribute ) ) )
|
|
{
|
|
Warning( "CParticleSnapshot::%s - Invalid attribute mapping specified (must be one-to-one)!\n", pFunc );
|
|
Assert( 0 );
|
|
Purge();
|
|
return false;
|
|
}
|
|
m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] = nFieldNumber;
|
|
m_ContainerAttributeToParticleAttribute[ nFieldNumber ] = nParticleAttribute;
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ValidateAttributeMapping, check datatypes for a field/attribute pair
|
|
//-----------------------------------------------------------------------------
|
|
bool CParticleSnapshot::ValidateAttributeMapping( int nFieldNumber, int nParticleAttribute, const char *pFunc )
|
|
{
|
|
// Check the presence/datatype of each specified container field
|
|
// TODO: support unallocated attributes (requires different 'copy' implementations in operators - can't use memcpy!)
|
|
EAttributeDataType nExpectedDataType = g_pParticleSystemMgr->GetParticleAttributeDataType( nParticleAttribute );
|
|
if ( ( m_pContainer->GetAttributeType( nFieldNumber ) != nExpectedDataType ) || !m_pContainer->HasAllocatedMemory( nFieldNumber ) )
|
|
{
|
|
Warning( "CParticleSnapshot::%s - Invalid attribute mapping specified for the provided container!\n", pFunc );
|
|
if ( m_pContainer->GetAttributeType( nFieldNumber ) != nExpectedDataType )
|
|
Warning( " (data type of container field %d does not match particle attribute %s)\n", nFieldNumber, g_pParticleSystemMgr->GetParticleAttributeName( nParticleAttribute ) );
|
|
if ( !m_pContainer->HasAllocatedMemory( nFieldNumber ) )
|
|
Warning( " (container field %d has no allocated data)\n", nFieldNumber );
|
|
Assert( 0 );
|
|
Purge();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Init, creating a new container with the specified attribute mapping
|
|
//-----------------------------------------------------------------------------
|
|
bool CParticleSnapshot::Init( int nX, int nY, int nZ, const AttributeMapVector &attributeMaps )
|
|
{
|
|
Assert( attributeMaps.Count() > 0 );
|
|
if ( attributeMaps.Count() <= 0 )
|
|
return false;
|
|
Assert( ( nX >= 1 ) && ( nY >= 1 ) && ( nZ >= 1 ) );
|
|
if ( ( nX < 1 ) || ( nY < 1 ) || ( nZ < 1 ) )
|
|
return false;
|
|
|
|
Purge();
|
|
m_pContainer = &m_Container;
|
|
|
|
for ( int i = 0; i < attributeMaps.Count(); i++ )
|
|
{
|
|
int nFieldNumber = attributeMaps[ i ].m_nContainerAttribute;
|
|
int nParticleAttribute = attributeMaps[ i ].m_nParticleAttribute;
|
|
|
|
if ( !AddAttributeMapping( nFieldNumber, nParticleAttribute, "Init" ) )
|
|
return false;
|
|
|
|
// Set the datatype of each specified container field
|
|
EAttributeDataType nDataType = g_pParticleSystemMgr->GetParticleAttributeDataType( nParticleAttribute );
|
|
m_Container.SetAttributeType( nFieldNumber, nDataType );
|
|
}
|
|
m_Container.AllocateData( nX, nY, nZ );
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Init, creating a new container with the specified attribute mapping
|
|
//-----------------------------------------------------------------------------
|
|
bool CParticleSnapshot::Init( int nX, int nY, int nZ, ... )
|
|
{
|
|
AttributeMapVector attributeMaps;
|
|
ATTRIBUTE_MAPPING_LOOP( nZ )
|
|
//{
|
|
attributeMaps.AddToTail( AttributeMap( nFieldNumber, nParticleAttribute ) );
|
|
}
|
|
return Init( nX, nY, nZ, attributeMaps );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Init using an existing container, with the specified attribute mapping
|
|
//-----------------------------------------------------------------------------
|
|
bool CParticleSnapshot::InitExternal( CSOAContainer *pContainer, const AttributeMapVector &attributeMaps )
|
|
{
|
|
Assert( attributeMaps.Count() > 0 );
|
|
if ( attributeMaps.Count() <= 0 )
|
|
return false;
|
|
|
|
Purge();
|
|
m_pContainer = pContainer;
|
|
|
|
for ( int i = 0; i < attributeMaps.Count(); i++ )
|
|
{
|
|
int nFieldNumber = attributeMaps[ i ].m_nContainerAttribute;
|
|
int nParticleAttribute = attributeMaps[ i ].m_nParticleAttribute;
|
|
|
|
if ( !AddAttributeMapping( nFieldNumber, nParticleAttribute, "InitExternal" ) ||
|
|
!ValidateAttributeMapping( nFieldNumber, nParticleAttribute, "InitExternal" ) )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Init using an existing container, with the specified attribute mapping
|
|
//-----------------------------------------------------------------------------
|
|
bool CParticleSnapshot::InitExternal( CSOAContainer *pContainer, ... )
|
|
{
|
|
AttributeMapVector attributeMaps;
|
|
ATTRIBUTE_MAPPING_LOOP( pContainer )
|
|
//{
|
|
attributeMaps.AddToTail( AttributeMap( nFieldNumber, nParticleAttribute ) );
|
|
}
|
|
return InitExternal( pContainer, attributeMaps );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Unpack structure for CParticleSnapshot
|
|
//-----------------------------------------------------------------------------
|
|
BEGIN_DMXELEMENT_UNPACK( CParticleSnapshot )
|
|
DMXELEMENT_UNPACK_FIELD_ARRAY( "particle_attribute_to_container_attribute", "-1", int, m_ParticleAttributeToContainerAttribute )
|
|
DMXELEMENT_UNPACK_FIELD_ARRAY( "container_attribute_to_particle_attribute", "-1", int, m_ContainerAttributeToParticleAttribute )
|
|
END_DMXELEMENT_UNPACK( CParticleSnapshot, s_pParticleSnapshotUnpack )
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Check whether the particle system's defined attributes have changed
|
|
//-----------------------------------------------------------------------------
|
|
void CParticleSnapshot::CheckParticleAttributesForChanges( void )
|
|
{
|
|
// If this doesn't compile, then we need to bump the file version and perform fixup on files saved w/ the old attribute definitions:
|
|
// TODO: store out an array of attribute names (g_pParticleSystemMgr->GetParticleAttributeName()) with the data so the fixup can be automatic and general
|
|
COMPILE_TIME_ASSERT( MAX_PARTICLE_ATTRIBUTES == 24 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_XYZ == 0 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_LIFE_DURATION == 1 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PREV_XYZ == 2 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_RADIUS == 3 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ROTATION == 4 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ROTATION_SPEED == 5 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_TINT_RGB == 6 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ALPHA == 7 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_CREATION_TIME == 8 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER == 9 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_TRAIL_LENGTH == 10 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PARTICLE_ID == 11 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_YAW == 12 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1 == 13 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_HITBOX_INDEX == 14 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ == 15 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ALPHA2 == 16 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SCRATCH_VEC == 17 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SCRATCH_FLOAT == 18 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_UNUSED == 19 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PITCH == 20 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_NORMAL == 21 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_GLOW_RGB == 22 );
|
|
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_GLOW_ALPHA == 23 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Init from a DMX (.psf) file
|
|
//-----------------------------------------------------------------------------
|
|
bool CParticleSnapshot::Unserialize( const char *pFullPath )
|
|
{
|
|
DECLARE_DMX_CONTEXT();
|
|
|
|
CheckParticleAttributesForChanges();
|
|
|
|
CDmxElement *pRootElement = NULL;
|
|
bool bTextMode = true, bBinaryMode = false;
|
|
if ( !UnserializeDMX( pFullPath, "GAME", bTextMode, &pRootElement ) &&
|
|
!UnserializeDMX( pFullPath, "GAME", bBinaryMode, &pRootElement ) ) // TODO: shouldn't UnserializeDMX automatically detect text mode?
|
|
{
|
|
Warning( "ERROR: CParticleSnapshot::Unserialize - could not load file %s!\n", pFullPath );
|
|
return false;
|
|
}
|
|
|
|
bool bSuccess = true;
|
|
|
|
CDmxElement *pParticleSnapshotElement = pRootElement->GetValue< CDmxElement * >( "particle_snapshot" );
|
|
if ( !pParticleSnapshotElement )
|
|
{
|
|
Warning( "ERROR: CParticleSnapshot::Unserialize - %s is not a particle snapshot (.psf) file!\n", pFullPath );
|
|
bSuccess = false;
|
|
}
|
|
|
|
int nVersion = -1;
|
|
if ( bSuccess )
|
|
{
|
|
// Read the version number
|
|
nVersion = pParticleSnapshotElement->GetValue( "version", -1 );
|
|
if ( nVersion == -1 )
|
|
{
|
|
Warning( "ERROR: CParticleSnapshot::Unserialize - missing version field in file %s\n", pFullPath );
|
|
bSuccess = false;
|
|
}
|
|
}
|
|
|
|
if ( bSuccess )
|
|
{
|
|
// Read the CParticleSnapshot structure
|
|
Purge();
|
|
pParticleSnapshotElement->UnpackIntoStructure( this, s_pParticleSnapshotUnpack );
|
|
// Unserialize the embedded CSOAContainer:
|
|
CDmxElement *pContainerElement = pParticleSnapshotElement->GetValue< CDmxElement * >( "container" );
|
|
if ( !UnserializeCSOAContainer( &m_Container, pContainerElement ) )
|
|
{
|
|
Warning( "ERROR: CParticleSnapshot::Unserialize - error reading embedded CSOAContainer in file %s\n", pFullPath );
|
|
bSuccess = false;
|
|
}
|
|
}
|
|
|
|
if ( bSuccess )
|
|
{
|
|
// Update files saved in old versions
|
|
switch( nVersion )
|
|
{
|
|
case PARTICLE_SNAPSHOT_DMX_VERSION:
|
|
// Up to date - nothing to do.
|
|
break;
|
|
default:
|
|
// The DMX unpack structure will set reasonable defaults or flag stuff that needs fixing up
|
|
// TODO: add code when versions are bumped and fixup needs to happen
|
|
bSuccess = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bSuccess )
|
|
{
|
|
m_pContainer = &m_Container;
|
|
|
|
// Validate the attribute mapping (re-'add' it, in both directions, to be paranoid)
|
|
int nForwardMaps = 0, nReverseMaps = 0;
|
|
for ( int i = 0; i < ARRAYSIZE( m_ParticleAttributeToContainerAttribute ); i++ )
|
|
{
|
|
int nFieldNumber = m_ParticleAttributeToContainerAttribute[ i ];
|
|
if ( nFieldNumber == -1 )
|
|
continue;
|
|
if ( !AddAttributeMapping( nFieldNumber, i, "Unserialize" ) ||
|
|
!ValidateAttributeMapping( nFieldNumber, i, "Unserialize" ) )
|
|
{
|
|
bSuccess = false;
|
|
break;
|
|
}
|
|
nForwardMaps++;
|
|
}
|
|
for ( int i = 0; i < ARRAYSIZE( m_ContainerAttributeToParticleAttribute ); i++ )
|
|
{
|
|
int nParticleAttribute = m_ContainerAttributeToParticleAttribute[ i ];
|
|
if ( nParticleAttribute == -1 )
|
|
continue;
|
|
if ( !AddAttributeMapping( i, nParticleAttribute, "Unserialize" ) ||
|
|
!ValidateAttributeMapping( i, nParticleAttribute, "Unserialize" ) )
|
|
{
|
|
bSuccess = false;
|
|
break;
|
|
}
|
|
nReverseMaps++;
|
|
}
|
|
|
|
if ( bSuccess && ( !nForwardMaps || !nReverseMaps ) )
|
|
{
|
|
bSuccess = false;
|
|
Warning( "ERROR: CParticleSnapshot::Unserialize - error in data in file %s (no attribute mapping specified)\n", pFullPath );
|
|
Assert( 0 );
|
|
}
|
|
}
|
|
|
|
if ( !bSuccess )
|
|
{
|
|
// Leave a beautiful corpse
|
|
Purge();
|
|
}
|
|
|
|
CleanupDMX( pRootElement );
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Write out to a DMX (.psf) file
|
|
//-----------------------------------------------------------------------------
|
|
bool CParticleSnapshot::Serialize( const char *pFullPath, bool bTextMode )
|
|
{
|
|
DECLARE_DMX_CONTEXT();
|
|
|
|
if ( !IsValid() )
|
|
{
|
|
Warning( "ERROR: CParticleSnapshot::Serialize - cannot serialize an uninitialized CParticleSnapshot! (%s)\n", pFullPath );
|
|
return false;
|
|
}
|
|
const char *pExtension = V_GetFileExtension( pFullPath );
|
|
if ( !pExtension || Q_stricmp( pExtension, "psf" ) )
|
|
{
|
|
Warning( "ERROR: CParticleSnapshot::Serialize - file extension should be '.psf' (%s)\n", pFullPath );
|
|
return false;
|
|
}
|
|
|
|
bool bSuccess = true;
|
|
CDmxElement *pRootElement = CreateDmxElement( "CDmeElement" );
|
|
{
|
|
CDmxElementModifyScope modifyRoot( pRootElement );
|
|
|
|
// Write the version number first
|
|
CDmxElement *pParticleSnapshotElement = CreateDmxElement( "CDmeParticleSnapshot" );
|
|
pRootElement->SetValue( "particle_snapshot", pParticleSnapshotElement );
|
|
|
|
int nDmxVersion = PARTICLE_SNAPSHOT_DMX_VERSION;
|
|
pParticleSnapshotElement->SetValue( "version", nDmxVersion );
|
|
|
|
// Then all our member variables
|
|
pParticleSnapshotElement->AddAttributesFromStructure( this, s_pParticleSnapshotUnpack );
|
|
|
|
// Then the embedded container
|
|
CDmxElement *pContainerElement = CreateDmxElement( "CDmeSOAContainer" );
|
|
pParticleSnapshotElement->SetValue( "container", pContainerElement );
|
|
if ( !SerializeCSOAContainer( m_pContainer, pContainerElement ) )
|
|
{
|
|
Warning( "ERROR: CParticleSnapshot::Serialize - error serializing embedded CSOAContainer for file %s\n", pFullPath );
|
|
bSuccess = false;
|
|
}
|
|
}
|
|
|
|
if ( bSuccess )
|
|
{
|
|
// Write out the file
|
|
bSuccess = SerializeDMX( pFullPath, "GAME", bTextMode, pRootElement );
|
|
}
|
|
|
|
CleanupDMX( pRootElement );
|
|
|
|
return bSuccess;
|
|
}
|