source-engine/public/particles/particles.h
2022-03-01 23:00:42 +03:00

2265 lines
75 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: particle system definitions
//
//===========================================================================//
#ifndef PARTICLES_H
#define PARTICLES_H
#ifdef _WIN32
#pragma once
#endif
#include "mathlib/mathlib.h"
#include "mathlib/vector.h"
#include "mathlib/ssemath.h"
#include "materialsystem/imaterialsystem.h"
#include "dmxloader/dmxelement.h"
#include "tier1/utlintrusivelist.h"
#include "vstdlib/random.h"
#include "tier1/utlobjectreference.h"
#include "tier1/UtlStringMap.h"
#include "tier1/utlmap.h"
#include "materialsystem/MaterialSystemUtil.h"
#include "trace.h"
#include "tier1/utlsoacontainer.h"
#if defined( CLIENT_DLL )
#include "c_pixel_visibility.h"
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
struct DmxElementUnpackStructure_t;
class CParticleSystemDefinition;
class CParticleCollection;
class CParticleOperatorInstance;
class CParticleSystemDictionary;
class CUtlBuffer;
class IParticleOperatorDefinition;
class CSheet;
class CMeshBuilder;
extern float s_pRandomFloats[];
//-----------------------------------------------------------------------------
// Random numbers
//-----------------------------------------------------------------------------
#define MAX_RANDOM_FLOATS 4096
#define RANDOM_FLOAT_MASK ( MAX_RANDOM_FLOATS - 1 )
//-----------------------------------------------------------------------------
// Particle attributes
//-----------------------------------------------------------------------------
#define MAX_PARTICLE_ATTRIBUTES 32
#define DEFPARTICLE_ATTRIBUTE( name, bit ) \
const int PARTICLE_ATTRIBUTE_##name##_MASK = (1 << bit); \
const int PARTICLE_ATTRIBUTE_##name = bit;
// required
DEFPARTICLE_ATTRIBUTE( XYZ, 0 );
// particle lifetime (duration) of particle as a float.
DEFPARTICLE_ATTRIBUTE( LIFE_DURATION, 1 );
// prev coordinates for verlet integration
DEFPARTICLE_ATTRIBUTE( PREV_XYZ, 2 );
// radius of particle
DEFPARTICLE_ATTRIBUTE( RADIUS, 3 );
// rotation angle of particle
DEFPARTICLE_ATTRIBUTE( ROTATION, 4 );
// rotation speed of particle
DEFPARTICLE_ATTRIBUTE( ROTATION_SPEED, 5 );
// tint of particle
DEFPARTICLE_ATTRIBUTE( TINT_RGB, 6 );
// alpha tint of particle
DEFPARTICLE_ATTRIBUTE( ALPHA, 7 );
// creation time stamp (relative to particle system creation)
DEFPARTICLE_ATTRIBUTE( CREATION_TIME, 8 );
// sequnece # (which animation sequence number this particle uses )
DEFPARTICLE_ATTRIBUTE( SEQUENCE_NUMBER, 9 );
// length of the trail
DEFPARTICLE_ATTRIBUTE( TRAIL_LENGTH, 10 );
// unique particle identifier
DEFPARTICLE_ATTRIBUTE( PARTICLE_ID, 11 );
// unique rotation around up vector
DEFPARTICLE_ATTRIBUTE( YAW, 12 );
// second sequnece # (which animation sequence number this particle uses )
DEFPARTICLE_ATTRIBUTE( SEQUENCE_NUMBER1, 13 );
// hit box index
DEFPARTICLE_ATTRIBUTE( HITBOX_INDEX, 14 );
DEFPARTICLE_ATTRIBUTE( HITBOX_RELATIVE_XYZ, 15 );
DEFPARTICLE_ATTRIBUTE( ALPHA2, 16 );
// particle trace caching fields
DEFPARTICLE_ATTRIBUTE( TRACE_P0, 17 ); // start pnt of trace
DEFPARTICLE_ATTRIBUTE( TRACE_P1, 18 ); // end pnt of trace
DEFPARTICLE_ATTRIBUTE( TRACE_HIT_T, 19 ); // 0..1 if hit
DEFPARTICLE_ATTRIBUTE( TRACE_HIT_NORMAL, 20 ); // 0 0 0 if no hit
#define MAX_PARTICLE_CONTROL_POINTS 64
#define ATTRIBUTES_WHICH_ARE_VEC3S_MASK ( PARTICLE_ATTRIBUTE_TRACE_P0_MASK | PARTICLE_ATTRIBUTE_TRACE_P1_MASK | \
PARTICLE_ATTRIBUTE_TRACE_HIT_NORMAL | PARTICLE_ATTRIBUTE_XYZ_MASK | \
PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_TINT_RGB_MASK | \
PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ_MASK )
#define ATTRIBUTES_WHICH_ARE_0_TO_1 (PARTICLE_ATTRIBUTE_ALPHA_MASK | PARTICLE_ATTRIBUTE_ALPHA2_MASK)
#define ATTRIBUTES_WHICH_ARE_ANGLES (PARTICLE_ATTRIBUTE_ROTATION_MASK | PARTICLE_ATTRIBUTE_YAW_MASK )
#define ATTRIBUTES_WHICH_ARE_INTS (PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK | PARTICLE_ATTRIBUTE_HITBOX_INDEX_MASK )
#if defined( _X360 )
#define MAX_PARTICLES_IN_A_SYSTEM 2000
#else
#define MAX_PARTICLES_IN_A_SYSTEM 5000
#endif
// Set this to 1 or 0 to enable or disable particle profiling.
// Note that this profiling is expensive on Linux, and some anti-virus
// products can make this *extremely* expensive on Windows.
#define MEASURE_PARTICLE_PERF 0
//-----------------------------------------------------------------------------
// Particle function types
//-----------------------------------------------------------------------------
enum ParticleFunctionType_t
{
FUNCTION_RENDERER = 0,
FUNCTION_OPERATOR,
FUNCTION_INITIALIZER,
FUNCTION_EMITTER,
FUNCTION_CHILDREN, // NOTE: This one is a fake function type, only here to help eliminate a ton of duplicated code in the editor
FUNCTION_FORCEGENERATOR,
FUNCTION_CONSTRAINT,
PARTICLE_FUNCTION_COUNT
};
struct CParticleVisibilityInputs
{
float m_flCameraBias;
float m_flInputMin;
float m_flInputMax;
float m_flAlphaScaleMin;
float m_flAlphaScaleMax;
float m_flRadiusScaleMin;
float m_flRadiusScaleMax;
float m_flProxyRadius;
float m_flBBoxScale;
bool m_bUseBBox;
int m_nCPin;
};
struct ModelHitBoxInfo_t
{
Vector m_vecBoxMins;
Vector m_vecBoxMaxes;
matrix3x4_t m_Transform;
};
class CModelHitBoxesInfo
{
public:
float m_flLastUpdateTime;
float m_flPrevLastUpdateTime;
int m_nNumHitBoxes;
int m_nNumPrevHitBoxes;
ModelHitBoxInfo_t *m_pHitBoxes;
ModelHitBoxInfo_t *m_pPrevBoxes;
bool CurAndPrevValid( void ) const
{
return ( m_nNumHitBoxes && ( m_nNumPrevHitBoxes == m_nNumHitBoxes ) );
}
CModelHitBoxesInfo( void )
{
m_flLastUpdateTime = -1;
m_nNumHitBoxes = 0;
m_nNumPrevHitBoxes = 0;
m_pHitBoxes = NULL;
m_pPrevBoxes = NULL;
}
~CModelHitBoxesInfo( void )
{
if ( m_pHitBoxes )
delete[] m_pHitBoxes;
if ( m_pPrevBoxes )
delete[] m_pPrevBoxes;
}
};
//-----------------------------------------------------------------------------
// Interface to allow the particle system to call back into the client
//-----------------------------------------------------------------------------
#define PARTICLE_SYSTEM_QUERY_INTERFACE_VERSION "VParticleSystemQuery001"
class IParticleSystemQuery : public IAppSystem
{
public:
virtual void GetLightingAtPoint( const Vector& vecOrigin, Color &tint ) = 0;
virtual void TraceLine( const Vector& vecAbsStart,
const Vector& vecAbsEnd, unsigned int mask,
const class IHandleEntity *ignore,
int collisionGroup,
CBaseTrace *ptr ) = 0;
// given a possible spawn point, tries to movie it to be on or in the source object. returns
// true if it succeeded
virtual bool MovePointInsideControllingObject( CParticleCollection *pParticles,
void *pObject,
Vector *pPnt )
{
return true;
}
virtual bool IsPointInControllingObjectHitBox(
CParticleCollection *pParticles,
int nControlPointNumber, Vector vecPos, bool bBBoxOnly = false )
{
return true;
}
virtual int GetCollisionGroupFromName( const char *pszCollisionGroupName )
{
return 0; // == COLLISION_GROUP_NONE
}
virtual void GetRandomPointsOnControllingObjectHitBox(
CParticleCollection *pParticles,
int nControlPointNumber,
int nNumPtsOut,
float flBBoxScale,
int nNumTrysToGetAPointInsideTheModel,
Vector *pPntsOut,
Vector vecDirectionBias,
Vector *pHitBoxRelativeCoordOut = NULL,
int *pHitBoxIndexOut = NULL ) = 0;
virtual int GetControllingObjectHitBoxInfo(
CParticleCollection *pParticles,
int nControlPointNumber,
int nBufSize, // # of output slots available
ModelHitBoxInfo_t *pHitBoxOutputBuffer )
{
// returns number of hit boxes output
return 0;
}
virtual Vector GetLocalPlayerPos( void )
{
return vec3_origin;
}
virtual void GetLocalPlayerEyeVectors( Vector *pForward, Vector *pRight = NULL, Vector *pUp = NULL )
{
*pForward = vec3_origin;
*pRight = vec3_origin;
*pUp = vec3_origin;
}
virtual float GetPixelVisibility( int *pQueryHandle, const Vector &vecOrigin, float flScale ) = 0;
virtual void SetUpLightingEnvironment( const Vector& pos )
{
}
};
//-----------------------------------------------------------------------------
//
// Particle system manager. Using a class because tools need it that way
// so the SFM and PET tools can share managers despite being linked to
// separate particle system .libs
//
//-----------------------------------------------------------------------------
typedef int ParticleSystemHandle_t;
class CParticleSystemMgr
{
public:
// Constructor, destructor
CParticleSystemMgr();
~CParticleSystemMgr();
// Initialize the particle system
bool Init( IParticleSystemQuery *pQuery );
// methods to add builtin operators. If you don't call these at startup, you won't be able to sim or draw. These are done separately from Init, so that
// the server can omit the code needed for rendering/simulation, if desired.
void AddBuiltinSimulationOperators( void );
void AddBuiltinRenderingOperators( void );
// Registration of known operators
void AddParticleOperator( ParticleFunctionType_t nOpType, IParticleOperatorDefinition *pOpFactory );
// Read a particle config file, add it to the list of particle configs
bool ReadParticleConfigFile( const char *pFileName, bool bPrecache, bool bDecommitTempMemory = true );
bool ReadParticleConfigFile( CUtlBuffer &buf, bool bPrecache, bool bDecommitTempMemory = true, const char *pFileName = NULL );
void DecommitTempMemory();
// For recording, write a specific particle system to a CUtlBuffer in DMX format
bool WriteParticleConfigFile( const char *pParticleSystemName, CUtlBuffer &buf, bool bPreventNameBasedLookup = false );
bool WriteParticleConfigFile( const DmObjectId_t& id, CUtlBuffer &buf, bool bPreventNameBasedLookup = false );
// create a particle system by name. returns null if one of that name does not exist
CParticleCollection *CreateParticleCollection( const char *pParticleSystemName, float flDelay = 0.0f, int nRandomSeed = 0 );
// create a particle system given a particle system id
CParticleCollection *CreateParticleCollection( const DmObjectId_t &id, float flDelay = 0.0f, int nRandomSeed = 0 );
// Is a particular particle system defined?
bool IsParticleSystemDefined( const char *pParticleSystemName );
bool IsParticleSystemDefined( const DmObjectId_t &id );
// Returns the index of the specified particle system.
ParticleSystemHandle_t GetParticleSystemIndex( const char *pParticleSystemName );
// Returns the name of the specified particle system.
const char *GetParticleSystemNameFromIndex( ParticleSystemHandle_t iIndex );
// Return the number of particle systems in our dictionary
int GetParticleSystemCount( void );
// call to get available particle operator definitions
// NOTE: FUNCTION_CHILDREN will return a faked one, for ease of writing the editor
CUtlVector< IParticleOperatorDefinition *> &GetAvailableParticleOperatorList( ParticleFunctionType_t nWhichList );
// Returns the unpack structure for a particle system definition
const DmxElementUnpackStructure_t *GetParticleSystemDefinitionUnpackStructure();
// Particle sheet management
void ShouldLoadSheets( bool bLoadSheets );
CSheet *FindOrLoadSheet( char const *pszFname, ITexture *pTexture );
CSheet *FindOrLoadSheet( IMaterial *pMaterial );
void FlushAllSheets( void );
// Render cache used to render opaque particle collections
void ResetRenderCache( void );
void AddToRenderCache( CParticleCollection *pParticles );
void DrawRenderCache( bool bShadowDepth );
IParticleSystemQuery *Query( void ) { return m_pQuery; }
// return the particle field name
const char* GetParticleFieldName( int nParticleField ) const;
// WARNING: the pointer returned by this function may be invalidated
// *at any time* by the editor, so do not ever cache it.
CParticleSystemDefinition* FindParticleSystem( const char *pName );
CParticleSystemDefinition* FindParticleSystem( const DmObjectId_t& id );
void CommitProfileInformation( bool bCommit ); // call after simulation, if you want
// sim time recorded. if oyu pass
// flase, info will be thrown away and
// uncomitted time reset. Having this
// function lets you only record
// profile data for slow frames if
// desired.
void DumpProfileInformation( void ); // write particle_profile.csv
// Cache/uncache materials used by particle systems
void PrecacheParticleSystem( const char *pName );
void UncacheAllParticleSystems();
// Sets the last simulation time, used for particle system sleeping logic
void SetLastSimulationTime( float flTime );
float GetLastSimulationTime() const;
int Debug_GetTotalParticleCount() const;
bool Debug_FrameWarningNeededTestAndReset();
float ParticleThrottleScaling() const; // Returns 1.0 = not restricted, 0.0 = fully restricted (i.e. don't draw!)
bool ParticleThrottleRandomEnable() const; // Retruns a randomish bool to say if you should draw this particle.
void TallyParticlesRendered( int nVertexCount, int nIndexCount = 0 );
private:
struct RenderCache_t
{
IMaterial *m_pMaterial;
CUtlVector< CParticleCollection * > m_ParticleCollections;
};
struct BatchStep_t
{
CParticleCollection *m_pParticles;
CParticleOperatorInstance *m_pRenderer;
void *m_pContext;
int m_nFirstParticle;
int m_nParticleCount;
int m_nVertCount;
};
struct Batch_t
{
int m_nVertCount;
int m_nIndexCount;
CUtlVector< BatchStep_t > m_BatchStep;
};
// Unserialization-related methods
bool ReadParticleDefinitions( CUtlBuffer &buf, const char *pFileName, bool bPrecache, bool bDecommitTempMemory );
void AddParticleSystem( CDmxElement *pParticleSystem );
// Serialization-related methods
CDmxElement *CreateParticleDmxElement( const DmObjectId_t &id );
CDmxElement *CreateParticleDmxElement( const char *pParticleSystemName );
bool WriteParticleConfigFile( CDmxElement *pParticleSystem, CUtlBuffer &buf, bool bPreventNameBasedLookup );
// Builds a list of batches to render
void BuildBatchList( int iRenderCache, IMatRenderContext *pRenderContext, CUtlVector< Batch_t >& batches );
// Known operators
CUtlVector<IParticleOperatorDefinition *> m_ParticleOperators[PARTICLE_FUNCTION_COUNT];
// Particle system dictionary
CParticleSystemDictionary *m_pParticleSystemDictionary;
// typedef CUtlMap< ITexture *, CSheet* > SheetsCache;
typedef CUtlStringMap< CSheet* > SheetsCache_t;
SheetsCache_t m_SheetList;
// attaching and dtaching killlists. when simulating, a particle system gets a kill list. after
// simulating, the memory for that will be used for the next particle system. This matters for
// threaded particles, because we don't want to share the same kill list between simultaneously
// simulating particle systems.
void AttachKillList( CParticleCollection *pParticles);
void DetachKillList( CParticleCollection *pParticles);
// For visualization (currently can only visualize one operator at a time)
CParticleCollection *m_pVisualizedParticles;
DmObjectId_t m_VisualizedOperatorId;
IParticleSystemQuery *m_pQuery;
CUtlVector< RenderCache_t > m_RenderCache;
IMaterial *m_pShadowDepthMaterial;
float m_flLastSimulationTime;
bool m_bDidInit;
bool m_bUsingDefaultQuery;
bool m_bShouldLoadSheets;
int m_nNumFramesMeasured;
enum { c_nNumFramesTracked = 10 };
int m_nParticleVertexCountHistory[c_nNumFramesTracked];
float m_fParticleCountScaling;
int m_nParticleIndexCount;
int m_nParticleVertexCount;
bool m_bFrameWarningNeeded;
friend class CParticleSystemDefinition;
friend class CParticleCollection;
};
extern CParticleSystemMgr *g_pParticleSystemMgr;
//-----------------------------------------------------------------------------
// A particle system can only have 1 operator using a particular ID
//-----------------------------------------------------------------------------
enum ParticleOperatorId_t
{
// Generic IDs
OPERATOR_GENERIC = -2, // Can have as many of these as you want
OPERATOR_SINGLETON = -1, // Can only have 1 operator with the same name as this one
// Renderer operator IDs
// Operator IDs
// Initializer operator IDs
OPERATOR_PI_POSITION, // Particle initializer: position (can only have 1 position setter)
OPERATOR_PI_RADIUS,
OPERATOR_PI_ALPHA,
OPERATOR_PI_TINT_RGB,
OPERATOR_PI_ROTATION,
OPERATOR_PI_YAW,
// Emitter IDs
OPERATOR_ID_COUNT,
};
//-----------------------------------------------------------------------------
// Class factory for particle operators
//-----------------------------------------------------------------------------
class IParticleOperatorDefinition
{
public:
virtual const char *GetName() const = 0;
virtual CParticleOperatorInstance *CreateInstance( const DmObjectId_t &id ) const = 0;
// virtual void DestroyInstance( CParticleOperatorInstance *pInstance ) const = 0;
virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const = 0;
virtual ParticleOperatorId_t GetId() const = 0;
virtual bool IsObsolete() const = 0;
virtual size_t GetClassSize() const = 0;
#if MEASURE_PARTICLE_PERF
// performance monitoring
float m_flMaxExecutionTime;
float m_flTotalExecutionTime;
float m_flUncomittedTime;
FORCEINLINE void RecordExecutionTime( float flETime )
{
m_flUncomittedTime += flETime;
m_flMaxExecutionTime = MAX( m_flMaxExecutionTime, flETime );
}
FORCEINLINE float TotalRecordedExecutionTime( void ) const
{
return m_flTotalExecutionTime;
}
FORCEINLINE float MaximumRecordedExecutionTime( void ) const
{
return m_flMaxExecutionTime;
}
#endif
};
//-----------------------------------------------------------------------------
// Particle operators
//-----------------------------------------------------------------------------
class CParticleOperatorInstance
{
public:
// custom allocators so we can be simd aligned
void *operator new( size_t nSize );
void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine );
void operator delete( void *pData );
void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine );
// unpack structure will be applied by creator. add extra initialization needed here
virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
{
}
virtual size_t GetRequiredContextBytes( ) const
{
return 0;
}
virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
{
}
virtual uint32 GetWrittenAttributes( void ) const = 0;
virtual uint32 GetReadAttributes( void ) const = 0;
virtual uint64 GetReadControlPointMask() const
{
return 0;
}
// Used when an operator needs to read the attributes of a particle at spawn time
virtual uint32 GetReadInitialAttributes( void ) const
{
return 0;
}
// a particle simulator does this
virtual void Operate( CParticleCollection *pParticles, float flOpStrength, void *pContext ) const
{
}
// a renderer overrides this
virtual void Render( IMatRenderContext *pRenderContext,
CParticleCollection *pParticles, void *pContext ) const
{
}
virtual bool IsBatchable() const
{
return true;
}
virtual void RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
{
}
// Returns the number of verts + indices to render
virtual int GetParticlesToRender( CParticleCollection *pParticles, void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int *pVertsUsed, int *pIndicesUsed ) const
{
*pVertsUsed = 0;
*pIndicesUsed = 0;
return 0;
}
// emitters over-ride this. Return a mask of what fields you initted
virtual uint32 Emit( CParticleCollection *pParticles, float flOpCurStrength,
void *pContext ) const
{
return 0;
}
// emitters over-ride this.
virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly = false ) const
{
}
virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly = false ) const
{
}
virtual void Restart( CParticleCollection *pParticles, void *pContext ) {}
// initters over-ride this
virtual void InitParticleSystem( CParticleCollection *pParticles, void *pContext ) const
{
}
// a force generator does this. It accumulates in the force array
virtual void AddForces( FourVectors *AccumulatedForces,
CParticleCollection *pParticles,
int nBlocks,
float flCurStrength,
void *pContext ) const
{
}
// this is called for each constarint every frame. It can set up data like nearby world traces,
// etc
virtual void SetupConstraintPerFrameData( CParticleCollection *pParticles,
void *pContext ) const
{
}
// a constraint overrides this. It shold return a true if it did anything
virtual bool EnforceConstraint( int nStartBlock,
int nNumBlocks,
CParticleCollection *pParticles,
void *pContext,
int nNumValidParticlesInLastChunk ) const
{
return false;
}
// should the constraint be run only once after all other constraints?
virtual bool IsFinalConstraint( void ) const
{
return false;
}
// determines if a mask needs to be initialized multiple times.
virtual bool InitMultipleOverride()
{
return false;
}
// Indicates if this initializer is scrub-safe (initializers don't use random numbers, for example)
virtual bool IsScrubSafe()
{
return false;
}
// particle-initters over-ride this
virtual void InitNewParticlesScalar( CParticleCollection *pParticles, int nFirstParticle, int n_particles, int attribute_write_mask, void *pContext ) const
{
}
// init new particles in blocks of 4. initters that have sse smarts should over ride this. the scalar particle initter will still be cllaed for head/tail.
virtual void InitNewParticlesBlock( CParticleCollection *pParticles, int start_block, int n_blocks, int attribute_write_mask, void *pContext ) const
{
// default behaviour is to call the scalar one 4x times
InitNewParticlesScalar( pParticles, 4*start_block, 4*n_blocks, attribute_write_mask, pContext );
}
// splits particle initialization up into scalar and block sections, callingt he right code
void InitNewParticles( CParticleCollection *pParticles, int nFirstParticle, int n_particles, int attribute_write_mask , void *pContext) const;
// this function is queried to determine if a particle system is over and doen with. A particle
// system is done with when it has noparticles and no operators intend to create any more
virtual bool MayCreateMoreParticles( CParticleCollection *pParticles, void *pContext ) const
{
return false;
}
// Returns the operator definition that spawned this operator
const IParticleOperatorDefinition *GetDefinition()
{
return m_pDef;
}
virtual bool ShouldRunBeforeEmitters( void ) const
{
return false;
}
// Does this operator require that particles remain in the order they were emitted?
virtual bool RequiresOrderInvariance( void ) const
{
return false;
}
// Called when the SFM wants to skip forward in time
virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const {}
// Returns a unique ID for this definition
const DmObjectId_t& GetId() { return m_Id; }
// Used for editing + debugging to visualize the operator in 3D
virtual void Render( CParticleCollection *pParticles ) const {}
// Used as a debugging mechanism to prevent bogus calls to RandomInt or RandomFloat inside operators
// Use CParticleCollection::RandomInt/RandomFloat instead
int RandomInt( int nMin, int nMax )
{
// NOTE: Use CParticleCollection::RandomInt!
Assert(0);
return 0;
}
float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f )
{
// NOTE: Use CParticleCollection::RandomFloat!
Assert(0);
return 0.0f;
}
float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f )
{
// NOTE: Use CParticleCollection::RandomFloatExp!
Assert(0);
return 0.0f;
}
float m_flOpStartFadeInTime;
float m_flOpEndFadeInTime;
float m_flOpStartFadeOutTime;
float m_flOpEndFadeOutTime;
float m_flOpFadeOscillatePeriod;
virtual ~CParticleOperatorInstance( void )
{
// so that sheet references, etc can be cleaned up
}
protected:
// utility function for initting a scalar attribute to a random range in an sse fashion
void InitScalarAttributeRandomRangeBlock( int nAttributeId, float fMinValue, float fMaxValue,
CParticleCollection *pParticles, int nStartBlock, int nBlockCount ) const;
void InitScalarAttributeRandomRangeExpBlock( int nAttributeId, float fMinValue, float fMaxValue, float fExp,
CParticleCollection *pParticles, int nStartBlock, int nBlockCount ) const;
void AddScalarAttributeRandomRangeBlock( int nAttributeId, float fMinValue, float fMaxValue, float fExp,
CParticleCollection *pParticles, int nStartBlock, int nBlockCount, bool bRandomlyInvert ) const;
private:
friend class CParticleCollection;
const IParticleOperatorDefinition *m_pDef;
void SetDefinition( const IParticleOperatorDefinition * pDef, const DmObjectId_t &id )
{
m_pDef = pDef;
CopyUniqueId( id, &m_Id );
}
DmObjectId_t m_Id;
template <typename T> friend class CParticleOperatorDefinition;
};
class CParticleRenderOperatorInstance : public CParticleOperatorInstance
{
public:
CParticleVisibilityInputs VisibilityInputs;
};
//-----------------------------------------------------------------------------
// Helper macro for creating particle operator factories
//-----------------------------------------------------------------------------
template < class T >
class CParticleOperatorDefinition : public IParticleOperatorDefinition
{
public:
CParticleOperatorDefinition( const char *pFactoryName, ParticleOperatorId_t id, bool bIsObsolete ) : m_pFactoryName( pFactoryName ), m_Id( id )
{
#if MEASURE_PARTICLE_PERF
m_flTotalExecutionTime = 0.0f;
m_flMaxExecutionTime = 0.0f;
m_flUncomittedTime = 0.0f;
#endif
m_bIsObsolete = bIsObsolete;
}
virtual const char *GetName() const
{
return m_pFactoryName;
}
virtual ParticleOperatorId_t GetId() const
{
return m_Id;
}
virtual CParticleOperatorInstance *CreateInstance( const DmObjectId_t &id ) const
{
CParticleOperatorInstance *pOp = new T;
pOp->SetDefinition( this, id );
return pOp;
}
virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const
{
return m_pUnpackParams;
}
// Editor won't display obsolete operators
virtual bool IsObsolete() const
{
return m_bIsObsolete;
}
virtual size_t GetClassSize() const
{
return sizeof( T );
}
private:
const char *m_pFactoryName;
ParticleOperatorId_t m_Id;
bool m_bIsObsolete;
static DmxElementUnpackStructure_t *m_pUnpackParams;
};
#define DECLARE_PARTICLE_OPERATOR( _className ) \
DECLARE_DMXELEMENT_UNPACK() \
friend class CParticleOperatorDefinition<_className >
#define DEFINE_PARTICLE_OPERATOR( _className, _operatorName, _id ) \
static CParticleOperatorDefinition<_className> s_##_className##Factory( _operatorName, _id, false )
#define DEFINE_PARTICLE_OPERATOR_OBSOLETE( _className, _operatorName, _id ) \
static CParticleOperatorDefinition<_className> s_##_className##Factory( _operatorName, _id, true )
#define BEGIN_PARTICLE_OPERATOR_UNPACK( _className ) \
BEGIN_DMXELEMENT_UNPACK( _className ) \
DMXELEMENT_UNPACK_FIELD( "operator start fadein","0", float, m_flOpStartFadeInTime ) \
DMXELEMENT_UNPACK_FIELD( "operator end fadein","0", float, m_flOpEndFadeInTime ) \
DMXELEMENT_UNPACK_FIELD( "operator start fadeout","0", float, m_flOpStartFadeOutTime ) \
DMXELEMENT_UNPACK_FIELD( "operator end fadeout","0", float, m_flOpEndFadeOutTime ) \
DMXELEMENT_UNPACK_FIELD( "operator fade oscillate","0", float, m_flOpFadeOscillatePeriod )
#define END_PARTICLE_OPERATOR_UNPACK( _className ) \
END_DMXELEMENT_UNPACK_TEMPLATE( _className, CParticleOperatorDefinition<_className>::m_pUnpackParams )
#define BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( _className ) \
BEGIN_PARTICLE_OPERATOR_UNPACK( _className ) \
DMXELEMENT_UNPACK_FIELD( "Visibility Proxy Input Control Point Number", "-1", int, VisibilityInputs.m_nCPin ) \
DMXELEMENT_UNPACK_FIELD( "Visibility Proxy Radius", "1.0", float, VisibilityInputs.m_flProxyRadius ) \
DMXELEMENT_UNPACK_FIELD( "Visibility input minimum","0", float, VisibilityInputs.m_flInputMin ) \
DMXELEMENT_UNPACK_FIELD( "Visibility input maximum","1", float, VisibilityInputs.m_flInputMax ) \
DMXELEMENT_UNPACK_FIELD( "Visibility Alpha Scale minimum","0", float, VisibilityInputs.m_flAlphaScaleMin ) \
DMXELEMENT_UNPACK_FIELD( "Visibility Alpha Scale maximum","1", float, VisibilityInputs.m_flAlphaScaleMax ) \
DMXELEMENT_UNPACK_FIELD( "Visibility Radius Scale minimum","1", float, VisibilityInputs.m_flRadiusScaleMin ) \
DMXELEMENT_UNPACK_FIELD( "Visibility Radius Scale maximum","1", float, VisibilityInputs.m_flRadiusScaleMax ) \
DMXELEMENT_UNPACK_FIELD( "Visibility Camera Depth Bias", "0", float, VisibilityInputs.m_flCameraBias )
// DMXELEMENT_UNPACK_FIELD( "Visibility Use Bounding Box for Proxy", "0", bool, VisibilityInputs.m_bUseBBox )
// DMXELEMENT_UNPACK_FIELD( "Visibility Bounding Box Scale", "1.0", float, VisibilityInputs.m_flBBoxScale )
#define REGISTER_PARTICLE_OPERATOR( _type, _className ) \
g_pParticleSystemMgr->AddParticleOperator( _type, &s_##_className##Factory )
// need to think about particle constraints in terms of segregating affected particles so as to
// run multi-pass constraints on only a subset
//-----------------------------------------------------------------------------
// flags for particle systems
//-----------------------------------------------------------------------------
enum
{
PCFLAGS_FIRST_FRAME = 0x1,
PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED = 0x2,
};
#define DEBUG_PARTICLE_SORT 0
// sorting functionality for rendering. Call GetRenderList( bool bSorted ) to get the list of
// particles to render (sorted or not, including children).
// **do not casually change this structure**. The sorting code treats it interchangably as an SOA
// and accesses it using sse. Any changes to this struct need the sort code updated.**
struct ParticleRenderData_t
{
float m_flSortKey; // what we sort by
int m_nIndex; // index or fudged index (for child particles)
float m_flRadius; // effective radius, using visibility
#if VALVE_LITTLE_ENDIAN
uint8 m_nAlpha; // effective alpha, combining alpha and alpha2 and vis. 0 - 255
uint8 m_nAlphaPad[3]; // this will be written to
#else
uint8 m_nAlphaPad[3]; // this will be written to
uint8 m_nAlpha; // effective alpha, combining alpha and alpha2 and vis. 0 - 255
#endif
};
struct ExtendedParticleRenderData_t : ParticleRenderData_t
{
float m_flX;
float m_flY;
float m_flZ;
float m_flPad;
};
typedef struct ALIGN16 _FourInts
{
int32 m_nValue[4];
} ALIGN16_POST FourInts;
// structure describing the parameter block used by operators which use the path between two points to
// control particles.
struct CPathParameters
{
int m_nStartControlPointNumber;
int m_nEndControlPointNumber;
int m_nBulgeControl;
float m_flBulge;
float m_flMidPoint;
void ClampControlPointIndices( void )
{
m_nStartControlPointNumber = MAX(0, MIN( MAX_PARTICLE_CONTROL_POINTS-1, m_nStartControlPointNumber ) );
m_nEndControlPointNumber = MAX(0, MIN( MAX_PARTICLE_CONTROL_POINTS-1, m_nEndControlPointNumber ) );
}
};
struct CParticleVisibilityData
{
float m_flAlphaVisibility;
float m_flRadiusVisibility;
float m_flCameraBias;
bool m_bUseVisibility;
};
struct CParticleControlPoint
{
Vector m_Position;
Vector m_PrevPosition;
// orientation
Vector m_ForwardVector;
Vector m_UpVector;
Vector m_RightVector;
// reference to entity or whatever this control point comes from
void *m_pObject;
// parent for hierarchies
int m_nParent;
};
// struct for simd xform to transform a point from an identitiy coordinate system to that of the control point
struct CParticleSIMDTransformation
{
FourVectors m_v4Origin;
FourVectors m_v4Fwd;
FourVectors m_v4Up;
FourVectors m_v4Right;
FORCEINLINE void VectorRotate( FourVectors &InPnt )
{
fltx4 fl4OutX = SubSIMD( AddSIMD( MulSIMD( InPnt.x, m_v4Fwd.x ), MulSIMD( InPnt.z, m_v4Up.x ) ), MulSIMD( InPnt.y, m_v4Right.x ) );
fltx4 fl4OutY = SubSIMD( AddSIMD( MulSIMD( InPnt.x, m_v4Fwd.y ), MulSIMD( InPnt.z, m_v4Up.y ) ), MulSIMD( InPnt.y, m_v4Right.y ) );
InPnt.z = SubSIMD( AddSIMD( MulSIMD( InPnt.x, m_v4Fwd.z ), MulSIMD( InPnt.z, m_v4Up.z ) ), MulSIMD( InPnt.y, m_v4Right.z ) );
InPnt.x = fl4OutX;
InPnt.y = fl4OutY;
}
FORCEINLINE void VectorTransform( FourVectors &InPnt )
{
VectorRotate( InPnt );
InPnt.x = AddSIMD( InPnt.x, m_v4Origin.x );
InPnt.y = AddSIMD( InPnt.y, m_v4Origin.y );
InPnt.z = AddSIMD( InPnt.z, m_v4Origin.z );
}
};
#define NUM_COLLISION_CACHE_MODES 4
//-----------------------------------------------------------------------------
//
// CParticleCollection
//
//-----------------------------------------------------------------------------
class CParticleCollection
{
public:
~CParticleCollection( void );
// Restarts the particle collection, stopping all non-continuous emitters
void Restart();
// compute bounds from particle list
void RecomputeBounds( void );
void SetControlPoint( int nWhichPoint, const Vector &v );
void SetControlPointObject( int nWhichPoint, void *pObject );
void SetControlPointOrientation( int nWhichPoint, const Vector &forward,
const Vector &right, const Vector &up );
void SetControlPointOrientation( int nWhichPoint, const Quaternion &q );
void SetControlPointForwardVector( int nWhichPoint, const Vector &v );
void SetControlPointUpVector( int nWhichPoint, const Vector &v );
void SetControlPointRightVector( int nWhichPoint, const Vector &v );
void SetControlPointParent( int nWhichPoint, int n );
// get the pointer to an attribute for a given particle.
// !!speed!! if you find yourself calling this anywhere that matters,
// you're not handling the simd-ness of the particle system well
// and will have bad perf.
const float *GetFloatAttributePtr( int nAttribute, int nParticleNumber ) const;
const int *GetIntAttributePtr( int nAttribute, int nParticleNumber ) const;
const fltx4 *GetM128AttributePtr( int nAttribute, size_t *pStrideOut ) const;
const FourVectors *Get4VAttributePtr( int nAttribute, size_t *pStrideOut ) const;
const FourInts *Get4IAttributePtr( int nAttribute, size_t *pStrideOut ) const;
const int *GetIntAttributePtr( int nAttribute, size_t *pStrideOut ) const;
int *GetIntAttributePtrForWrite( int nAttribute, int nParticleNumber );
float *GetFloatAttributePtrForWrite( int nAttribute, int nParticleNumber );
fltx4 *GetM128AttributePtrForWrite( int nAttribute, size_t *pStrideOut );
FourVectors *Get4VAttributePtrForWrite( int nAttribute, size_t *pStrideOut );
const float *GetInitialFloatAttributePtr( int nAttribute, int nParticleNumber ) const;
const fltx4 *GetInitialM128AttributePtr( int nAttribute, size_t *pStrideOut ) const;
const FourVectors *GetInitial4VAttributePtr( int nAttribute, size_t *pStrideOut ) const;
float *GetInitialFloatAttributePtrForWrite( int nAttribute, int nParticleNumber );
fltx4 *GetInitialM128AttributePtrForWrite( int nAttribute, size_t *pStrideOut );
void Simulate( float dt, bool updateBboxOnly = false );
void SkipToTime( float t );
// the camera objetc may be compared for equality against control point objects
void Render( IMatRenderContext *pRenderContext, bool bTranslucentOnly = false, void *pCameraObject = NULL );
bool IsValid( void ) const;
const char *GetName() const;
// IsFinished returns true when a system has no particles and won't be creating any more
bool IsFinished( void );
// Used to make sure we're accessing valid memory
bool IsValidAttributePtr( int nAttribute, const void *pPtr ) const;
void SwapPosAndPrevPos( void );
void SetNActiveParticles( int nCount );
void KillParticle(int nPidx);
void StopEmission( bool bInfiniteOnly = false, bool bRemoveAllParticles = false, bool bWakeOnStop = false );
void StartEmission( bool bInfiniteOnly = false );
void SetDormant( bool bDormant );
const Vector& GetControlPointAtCurrentTime( int nControlPoint ) const;
void GetControlPointOrientationAtCurrentTime( int nControlPoint, Vector *pForward, Vector *pRight, Vector *pUp ) const;
void GetControlPointTransformAtCurrentTime( int nControlPoint, matrix3x4_t *pMat );
void GetControlPointTransformAtCurrentTime( int nControlPoint, VMatrix *pMat );
int GetControlPointParent( int nControlPoint ) const;
// Used to retrieve the position of a control point
// somewhere between m_fCurTime and m_fCurTime - m_fPreviousDT
void GetControlPointAtTime( int nControlPoint, float flTime, Vector *pControlPoint ) const;
void GetControlPointAtPrevTime( int nControlPoint, Vector *pControlPoint ) const;
void GetControlPointOrientationAtTime( int nControlPoint, float flTime, Vector *pForward, Vector *pRight, Vector *pUp );
void GetControlPointTransformAtTime( int nControlPoint, float flTime, matrix3x4_t *pMat );
void GetControlPointTransformAtTime( int nControlPoint, float flTime, VMatrix *pMat );
void GetControlPointTransformAtTime( int nControlPoint, float flTime, CParticleSIMDTransformation *pXForm );
int GetHighestControlPoint( void ) const;
// Has this particle moved recently (since the last simulation?)
bool HasMoved() const;
// Control point accessed:
// NOTE: Unlike the definition's version of these methods,
// these OR-in the masks of their children.
bool ReadsControlPoint( int nPoint ) const;
// Used by particle systems to generate random numbers. Do not call these methods - use sse
// code
int RandomInt( int nMin, int nMax );
float RandomFloat( float flMin, float flMax );
float RandomFloatExp( float flMin, float flMax, float flExponent );
void RandomVector( float flMin, float flMax, Vector *pVector );
void RandomVector( const Vector &vecMin, const Vector &vecMax, Vector *pVector );
float RandomVectorInUnitSphere( Vector *pVector ); // Returns the length sqr of the vector
// NOTE: These versions will produce the *same random numbers* if you give it the same random
// sample id. do not use these methods.
int RandomInt( int nRandomSampleId, int nMin, int nMax );
float RandomFloat( int nRandomSampleId, float flMin, float flMax );
float RandomFloatExp( int nRandomSampleId, float flMin, float flMax, float flExponent );
void RandomVector( int nRandomSampleId, float flMin, float flMax, Vector *pVector );
void RandomVector( int nRandomSampleId, const Vector &vecMin, const Vector &vecMax, Vector *pVector );
float RandomVectorInUnitSphere( int nRandomSampleId, Vector *pVector ); // Returns the length sqr of the vector
fltx4 RandomFloat( const FourInts &ParticleID, int nRandomSampleOffset );
// Random number offset (for use in getting Random #s in operators)
int OperatorRandomSampleOffset() const;
// Returns the render bounds
void GetBounds( Vector *pMin, Vector *pMax );
// Visualize operators (for editing/debugging)
void VisualizeOperator( const DmObjectId_t *pOpId = NULL );
// Does the particle system use the power of two frame buffer texture (refraction?)
bool UsesPowerOfTwoFrameBufferTexture( bool bThisFrame ) const;
// Does the particle system use the full frame buffer texture (soft particles)
bool UsesFullFrameBufferTexture( bool bThisFrame ) const;
// Is the particle system translucent?
bool IsTranslucent() const;
// Is the particle system two-pass?
bool IsTwoPass() const;
// Is the particle system batchable?
bool IsBatchable() const;
// Renderer iteration
int GetRendererCount() const;
CParticleOperatorInstance *GetRenderer( int i );
void *GetRendererContext( int i );
bool CheckIfOperatorShouldRun( CParticleOperatorInstance const * op, float *pflCurStrength = NULL );
Vector TransformAxis( const Vector &SrcAxis, bool bLocalSpace, int nControlPointNumber = 0);
// return backwards-sorted particle list. use --addressing
const ParticleRenderData_t *GetRenderList( IMatRenderContext *pRenderContext, bool bSorted, int *pNparticles, CParticleVisibilityData *pVisibilityData );
// calculate the points of a curve for a path
void CalculatePathValues( CPathParameters const &PathIn,
float flTimeStamp,
Vector *pStartPnt,
Vector *pMidPnt,
Vector *pEndPnt
);
int GetGroupID() const;
void InitializeNewParticles( int nFirstParticle, int nParticleCount, uint32 nInittedMask );
// update hit boxes for control point if not updated yet for this sim step
void UpdateHitBoxInfo( int nControlPointNumber );
// Used by particle system definitions to manage particle collection lists
void UnlinkFromDefList( );
CParticleCollection *GetNextCollectionUsingSameDef() { return m_pNextDef; }
CUtlReference< CSheet > m_Sheet;
protected:
CParticleCollection( );
// Used by client code
bool Init( const char *pParticleSystemName );
bool Init( CParticleSystemDefinition *pDef );
// Bloat the bounding box by bounds around the control point
void BloatBoundsUsingControlPoint();
private:
void GenerateSortedIndexList( Vector vecCameraPos, CParticleVisibilityData *pVisibilityData, bool bSorted );
void Init( CParticleSystemDefinition *pDef, float flDelay, int nRandomSeed );
void InitStorage( CParticleSystemDefinition *pDef );
void InitParticleCreationTime( int nFirstParticle, int nNumToInit );
void CopyInitialAttributeValues( int nStartParticle, int nNumParticles );
void ApplyKillList( void );
void SetAttributeToConstant( int nAttribute, float fValue );
void SetAttributeToConstant( int nAttribute, float fValueX, float fValueY, float fValueZ );
void InitParticleAttributes( int nStartParticle, int nNumParticles, int nAttrsLeftToInit );
// initialize this attribute for all active particles
void FillAttributeWithConstant( int nAttribute, float fValue );
// Updates the previous control points
void UpdatePrevControlPoints( float dt );
// Returns the memory for a particular constant attribute
float *GetConstantAttributeMemory( int nAttribute );
// Swaps two particles in the particle list
void SwapAdjacentParticles( int hParticle );
// Unlinks a particle from the list
void UnlinkParticle( int hParticle );
// Inserts a particle before another particle in the list
void InsertParticleBefore( int hParticle, int hBefore );
// Move a particle from one index to another
void MoveParticle( int nInitialIndex, int nNewIndex );
// Computes the sq distance to a particle position
float ComputeSqrDistanceToParticle( int hParticle, const Vector &vecPosition ) const;
// Grows the dist sq range for all particles
void GrowDistSqrBounds( float flDistSqr );
// Simulates the first frame
void SimulateFirstFrame( );
bool SystemContainsParticlesWithBoolSet( bool CParticleCollection::*pField ) const;
// Does the particle collection contain opaque particle systems
bool ContainsOpaqueCollections();
bool ComputeUsesPowerOfTwoFrameBufferTexture();
bool ComputeUsesFullFrameBufferTexture();
bool ComputeIsTranslucent();
bool ComputeIsTwoPass();
bool ComputeIsBatchable();
bool ComputeRequiresOrderInvariance();
void LabelTextureUsage( void );
void LinkIntoDefList( );
public:
fltx4 m_fl4CurTime; // accumulated time
int m_nPaddedActiveParticles; // # of groups of 4 particles
float m_flCurTime; // accumulated time
int m_nActiveParticles; // # of active particles
float m_flDt;
float m_flPreviousDt;
float m_flNextSleepTime; // time to go to sleep if not drawn
CUtlReference< CParticleSystemDefinition > m_pDef;
int m_nAllocatedParticles;
int m_nMaxAllowedParticles;
bool m_bDormant;
bool m_bEmissionStopped;
bool m_bRequiresOrderInvariance;
int m_LocalLightingCP;
Color m_LocalLighting;
// control point data. Don't set these directly, or they won't propagate down to children
// particle control points can act as emitter centers, repulsions points, etc. what they are
// used for depends on what operators and parameters your system has.
CParticleControlPoint m_ControlPoints[MAX_PARTICLE_CONTROL_POINTS];
CModelHitBoxesInfo m_ControlPointHitBoxes[MAX_PARTICLE_CONTROL_POINTS];
// public so people can call methods
uint8 *m_pOperatorContextData;
CParticleCollection *m_pNext; // for linking children together
CParticleCollection *m_pPrev; // for linking children together
struct CWorldCollideContextData *m_pCollisionCacheData[NUM_COLLISION_CACHE_MODES]; // children can share collision caches w/ parent
CParticleCollection *m_pParent;
CUtlIntrusiveDList<CParticleCollection> m_Children; // list for all child particle systems
void *operator new(size_t nSize);
void *operator new( size_t size, int nBlockUse, const char *pFileName, int nLine );
void operator delete(void *pData);
void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine );
protected:
// current bounds for the particle system
bool m_bBoundsValid;
Vector m_MinBounds;
Vector m_MaxBounds;
int m_nHighestCP; //Highest CP set externally. Needs to assert if a system calls to an unassigned CP.
private:
unsigned char *m_pParticleMemory; // fixed size at initialization. Must be aligned for SSE
unsigned char *m_pParticleInitialMemory; // fixed size at initialization. Must be aligned for SSE
unsigned char *m_pConstantMemory;
int m_nPerParticleInitializedAttributeMask;
int m_nPerParticleUpdatedAttributeMask;
int m_nPerParticleReadInitialAttributeMask; // What fields do operators want to see initial attribute values for?
float *m_pParticleAttributes[MAX_PARTICLE_ATTRIBUTES];
float *m_pParticleInitialAttributes[MAX_PARTICLE_ATTRIBUTES];
size_t m_nParticleFloatStrides[MAX_PARTICLE_ATTRIBUTES];
size_t m_nParticleInitialFloatStrides[MAX_PARTICLE_ATTRIBUTES];
float *m_pConstantAttributes;
uint64 m_nControlPointReadMask; // Mask indicating which control points have been accessed
int m_nParticleFlags; // PCFLAGS_xxx
bool m_bIsScrubbable : 1;
bool m_bIsRunningInitializers : 1;
bool m_bIsRunningOperators : 1;
bool m_bIsTranslucent : 1;
bool m_bIsTwoPass : 1;
bool m_bAnyUsesPowerOfTwoFrameBufferTexture : 1; // whether or not we or any children use this
bool m_bAnyUsesFullFrameBufferTexture : 1;
bool m_bIsBatchable : 1;
bool m_bUsesPowerOfTwoFrameBufferTexture; // whether or not we use this, _not_ our children
bool m_bUsesFullFrameBufferTexture;
// How many frames have we drawn?
int m_nDrawnFrames;
int m_nSimulatedFrames;
Vector m_Center; // average of particle centers
// Used to assign unique ids to each particle
int m_nUniqueParticleId;
// Used to generate random numbers
int m_nRandomQueryCount;
int m_nRandomSeed;
int m_nOperatorRandomSampleOffset;
float m_flMinDistSqr;
float m_flMaxDistSqr;
float m_flOOMaxDistSqr;
Vector m_vecLastCameraPos;
float m_flLastMinDistSqr;
float m_flLastMaxDistSqr;
// Particle collection kill list. set up by particle system mgr
int m_nNumParticlesToKill;
int *m_pParticleKillList;
// Used to build a list of all particle collections that have the same particle def
CParticleCollection *m_pNextDef;
CParticleCollection *m_pPrevDef;
void LoanKillListTo( CParticleCollection *pBorrower ) const;
bool HasAttachedKillList( void ) const;
// For debugging
CParticleOperatorInstance *m_pRenderOp;
friend class CParticleSystemMgr;
friend class CParticleOperatorInstance;
};
class CM128InitialAttributeIterator : public CStridedConstPtr<fltx4>
{
public:
FORCEINLINE CM128InitialAttributeIterator( int nAttribute, CParticleCollection *pParticles )
{
m_pData = pParticles->GetInitialM128AttributePtr( nAttribute, &m_nStride );
}
};
class CM128AttributeIterator : public CStridedConstPtr<fltx4>
{
public:
FORCEINLINE CM128AttributeIterator( int nAttribute, CParticleCollection *pParticles )
{
m_pData = pParticles->GetM128AttributePtr( nAttribute, &m_nStride );
}
};
class C4IAttributeIterator : public CStridedConstPtr<FourInts>
{
public:
FORCEINLINE C4IAttributeIterator( int nAttribute, CParticleCollection *pParticles )
{
m_pData = pParticles->Get4IAttributePtr( nAttribute, &m_nStride );
}
};
class CM128AttributeWriteIterator : public CStridedPtr<fltx4>
{
public:
FORCEINLINE CM128AttributeWriteIterator( void )
{
}
FORCEINLINE void Init ( int nAttribute, CParticleCollection *pParticles )
{
m_pData = pParticles->GetM128AttributePtrForWrite( nAttribute, &m_nStride );
}
FORCEINLINE CM128AttributeWriteIterator( int nAttribute, CParticleCollection *pParticles )
{
Init( nAttribute, pParticles );
}
};
class C4VAttributeIterator : public CStridedConstPtr<FourVectors>
{
public:
FORCEINLINE C4VAttributeIterator( int nAttribute, CParticleCollection *pParticles )
{
m_pData = pParticles->Get4VAttributePtr( nAttribute, &m_nStride );
}
};
class C4VInitialAttributeIterator : public CStridedConstPtr<FourVectors>
{
public:
FORCEINLINE C4VInitialAttributeIterator( int nAttribute, CParticleCollection *pParticles )
{
m_pData = pParticles->GetInitial4VAttributePtr( nAttribute, &m_nStride );
}
};
class C4VAttributeWriteIterator : public CStridedPtr<FourVectors>
{
public:
FORCEINLINE C4VAttributeWriteIterator( int nAttribute, CParticleCollection *pParticles )
{
m_pData = pParticles->Get4VAttributePtrForWrite( nAttribute, &m_nStride );
}
};
//-----------------------------------------------------------------------------
// Inline methods of CParticleCollection
//-----------------------------------------------------------------------------
inline bool CParticleCollection::HasAttachedKillList( void ) const
{
return m_pParticleKillList != NULL;
}
inline bool CParticleCollection::ReadsControlPoint( int nPoint ) const
{
return ( m_nControlPointReadMask & ( 1ULL << nPoint ) ) != 0;
}
inline void CParticleCollection::SetNActiveParticles( int nCount )
{
Assert( nCount <= m_nMaxAllowedParticles );
m_nActiveParticles = nCount;
m_nPaddedActiveParticles = ( nCount+3 )/4;
}
inline void CParticleCollection::SwapPosAndPrevPos( void )
{
// strides better be the same!
Assert( m_nParticleFloatStrides[PARTICLE_ATTRIBUTE_XYZ] == m_nParticleFloatStrides[ PARTICLE_ATTRIBUTE_PREV_XYZ ] );
V_swap( m_pParticleAttributes[ PARTICLE_ATTRIBUTE_XYZ ], m_pParticleAttributes[ PARTICLE_ATTRIBUTE_PREV_XYZ ] );
}
inline void CParticleCollection::LoanKillListTo( CParticleCollection *pBorrower ) const
{
Assert(! pBorrower->m_pParticleKillList );
pBorrower->m_nNumParticlesToKill = 0;
pBorrower->m_pParticleKillList = m_pParticleKillList;
}
inline void CParticleCollection::SetAttributeToConstant( int nAttribute, float fValue )
{
float *fconst = m_pConstantAttributes + 4*3*nAttribute;
fconst[0] = fconst[1] = fconst[2] = fconst[3] = fValue;
}
inline void CParticleCollection::SetAttributeToConstant( int nAttribute, float fValueX, float fValueY, float fValueZ )
{
float *fconst = m_pConstantAttributes + 4*3*nAttribute;
fconst[0] = fconst[1] = fconst[2] = fconst[3] = fValueX;
fconst[4] = fconst[5] = fconst[6] = fconst[7] = fValueY;
fconst[8] = fconst[9] = fconst[10] = fconst[11] = fValueZ;
}
inline void CParticleCollection::SetControlPoint( int nWhichPoint, const Vector &v )
{
Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) );
m_nHighestCP = MAX( m_nHighestCP, nWhichPoint );
m_ControlPoints[ nWhichPoint ].m_Position = v;
for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
{
i->SetControlPoint( nWhichPoint, v );
}
}
inline void CParticleCollection::SetControlPointObject( int nWhichPoint, void *pObject )
{
Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) );
m_ControlPoints[ nWhichPoint ].m_pObject = pObject;
for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
{
i->SetControlPointObject( nWhichPoint, pObject );
}
}
inline void CParticleCollection::SetControlPointOrientation( int nWhichPoint, const Vector &forward,
const Vector &right, const Vector &up )
{
Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) );
// check perpendicular
if ( fabs( DotProduct( forward, up ) ) <= 0.1f
&& fabs( DotProduct( forward, right ) ) <= 0.1f
&& fabs( DotProduct( right, up ) ) <= 0.1f )
{
m_ControlPoints[ nWhichPoint ].m_ForwardVector = forward;
m_ControlPoints[ nWhichPoint ].m_UpVector = up;
m_ControlPoints[ nWhichPoint ].m_RightVector = right;
// make sure all children are finished
for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
{
i->SetControlPointOrientation( nWhichPoint, forward, right, up );
}
}
else
{
Warning( "Attempt to set particle collection %s to invalid orientation matrix\n", GetName() );
}
}
inline Vector CParticleCollection::TransformAxis( const Vector &SrcAxis, bool bLocalSpace,
int nControlPointNumber)
{
if ( bLocalSpace )
{
return // mxmul
( SrcAxis.x*m_ControlPoints[nControlPointNumber].m_RightVector )+
( SrcAxis.y*m_ControlPoints[nControlPointNumber].m_ForwardVector )+
( SrcAxis.z*m_ControlPoints[nControlPointNumber].m_UpVector );
}
else
return SrcAxis;
}
inline void CParticleCollection::SetControlPointOrientation( int nWhichPoint, const Quaternion &q )
{
matrix3x4_t mat;
Vector vecForward, vecUp, vecRight;
QuaternionMatrix( q, mat );
MatrixVectors( mat, &vecForward, &vecRight, &vecUp );
SetControlPointOrientation( nWhichPoint, vecForward, vecRight, vecUp );
}
inline void CParticleCollection::SetControlPointForwardVector( int nWhichPoint, const Vector &v)
{
Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) );
m_ControlPoints[ nWhichPoint ].m_ForwardVector = v;
for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
{
i->SetControlPointForwardVector( nWhichPoint, v );
}
}
inline void CParticleCollection::SetControlPointUpVector( int nWhichPoint, const Vector &v)
{
Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) );
m_ControlPoints[ nWhichPoint ].m_UpVector = v;
for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
{
i->SetControlPointUpVector( nWhichPoint, v );
}
}
inline void CParticleCollection::SetControlPointRightVector( int nWhichPoint, const Vector &v)
{
Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) );
m_ControlPoints[ nWhichPoint ].m_RightVector = v;
for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
{
i->SetControlPointRightVector( nWhichPoint, v );
}
}
inline void CParticleCollection::SetControlPointParent( int nWhichPoint, int n )
{
Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) );
m_ControlPoints[ nWhichPoint ].m_nParent = n;
for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
{
i->SetControlPointParent( nWhichPoint, n );
}
}
// Returns the memory for a particular constant attribute
inline float *CParticleCollection::GetConstantAttributeMemory( int nAttribute )
{
return m_pConstantAttributes + 3 * 4 * nAttribute;
}
// Random number offset (for use in getting Random #s in operators)
inline int CParticleCollection::OperatorRandomSampleOffset() const
{
return m_nOperatorRandomSampleOffset;
}
// Used by particle systems to generate random numbers
inline int CParticleCollection::RandomInt( int nRandomSampleId, int nMin, int nMax )
{
// do not call
float flRand = s_pRandomFloats[ ( m_nRandomSeed + nRandomSampleId ) & RANDOM_FLOAT_MASK ];
flRand *= ( nMax + 1 - nMin );
int nRand = (int)flRand + nMin;
return nRand;
}
inline float CParticleCollection::RandomFloat( int nRandomSampleId, float flMin, float flMax )
{
// do not call
float flRand = s_pRandomFloats[ ( m_nRandomSeed + nRandomSampleId ) & RANDOM_FLOAT_MASK ];
flRand *= ( flMax - flMin );
flRand += flMin;
return flRand;
}
inline fltx4 CParticleCollection::RandomFloat( const FourInts &ParticleID, int nRandomSampleOffset )
{
fltx4 Retval;
int nOfs=m_nRandomSeed+nRandomSampleOffset;
SubFloat( Retval, 0 ) = s_pRandomFloats[ ( nOfs + ParticleID.m_nValue[0] ) & RANDOM_FLOAT_MASK ];
SubFloat( Retval, 1 ) = s_pRandomFloats[ ( nOfs + ParticleID.m_nValue[1] ) & RANDOM_FLOAT_MASK ];
SubFloat( Retval, 2 ) = s_pRandomFloats[ ( nOfs + ParticleID.m_nValue[2] ) & RANDOM_FLOAT_MASK ];
SubFloat( Retval, 3 ) = s_pRandomFloats[ ( nOfs + ParticleID.m_nValue[3] ) & RANDOM_FLOAT_MASK ];
return Retval;
}
inline float CParticleCollection::RandomFloatExp( int nRandomSampleId, float flMin, float flMax, float flExponent )
{
// do not call
float flRand = s_pRandomFloats[ ( m_nRandomSeed + nRandomSampleId ) & RANDOM_FLOAT_MASK ];
flRand = powf( flRand, flExponent );
flRand *= ( flMax - flMin );
flRand += flMin;
return flRand;
}
inline void CParticleCollection::RandomVector( int nRandomSampleId, float flMin, float flMax, Vector *pVector )
{
// do not call
float flDelta = flMax - flMin;
int nBaseId = m_nRandomSeed + nRandomSampleId;
pVector->x = s_pRandomFloats[ nBaseId & RANDOM_FLOAT_MASK ];
pVector->x *= flDelta;
pVector->x += flMin;
pVector->y = s_pRandomFloats[ ( nBaseId + 1 ) & RANDOM_FLOAT_MASK ];
pVector->y *= flDelta;
pVector->y += flMin;
pVector->z = s_pRandomFloats[ ( nBaseId + 2 ) & RANDOM_FLOAT_MASK ];
pVector->z *= flDelta;
pVector->z += flMin;
}
inline void CParticleCollection::RandomVector( int nRandomSampleId, const Vector &vecMin, const Vector &vecMax, Vector *pVector )
{
// do not call
int nBaseId = m_nRandomSeed + nRandomSampleId;
pVector->x = RandomFloat( nBaseId, vecMin.x, vecMax.x );
pVector->y = RandomFloat( nBaseId + 1, vecMin.y, vecMax.y );
pVector->z = RandomFloat( nBaseId + 2, vecMin.z, vecMax.z );
}
// Used by particle systems to generate random numbers
inline int CParticleCollection::RandomInt( int nMin, int nMax )
{
// do not call
return RandomInt( m_nRandomQueryCount++, nMin, nMax );
}
inline float CParticleCollection::RandomFloat( float flMin, float flMax )
{
// do not call
return RandomFloat( m_nRandomQueryCount++, flMin, flMax );
}
inline float CParticleCollection::RandomFloatExp( float flMin, float flMax, float flExponent )
{
// do not call
return RandomFloatExp( m_nRandomQueryCount++, flMin, flMax, flExponent );
}
inline void CParticleCollection::RandomVector( float flMin, float flMax, Vector *pVector )
{
// do not call
RandomVector( m_nRandomQueryCount++, flMin, flMax, pVector );
}
inline void CParticleCollection::RandomVector( const Vector &vecMin, const Vector &vecMax, Vector *pVector )
{
// do not call
RandomVector( m_nRandomQueryCount++, vecMin, vecMax, pVector );
}
inline float CParticleCollection::RandomVectorInUnitSphere( Vector *pVector )
{
// do not call
return RandomVectorInUnitSphere( m_nRandomQueryCount++, pVector );
}
// get the pointer to an attribute for a given particle. !!speed!! if you find yourself
// calling this anywhere that matters, you're not handling the simd-ness of the particle system
// well and will have bad perf.
inline const float *CParticleCollection::GetFloatAttributePtr( int nAttribute, int nParticleNumber ) const
{
Assert( nParticleNumber < m_nAllocatedParticles );
int block_ofs = nParticleNumber/4;
return m_pParticleAttributes[ nAttribute ] +
m_nParticleFloatStrides[ nAttribute ] * block_ofs +
( nParticleNumber & 3 );
}
inline int *CParticleCollection::GetIntAttributePtrForWrite( int nAttribute, int nParticleNumber )
{
return reinterpret_cast< int* >( GetFloatAttributePtrForWrite( nAttribute, nParticleNumber ) );
}
inline const int *CParticleCollection::GetIntAttributePtr( int nAttribute, int nParticleNumber ) const
{
return (int*)GetFloatAttributePtr( nAttribute, nParticleNumber );
}
inline const fltx4 *CParticleCollection::GetM128AttributePtr( int nAttribute, size_t *pStrideOut ) const
{
*(pStrideOut) = m_nParticleFloatStrides[ nAttribute ]/4;
return reinterpret_cast<fltx4 *>( m_pParticleAttributes[ nAttribute ] );
}
inline const FourInts *CParticleCollection::Get4IAttributePtr( int nAttribute, size_t *pStrideOut ) const
{
*(pStrideOut) = m_nParticleFloatStrides[ nAttribute ]/4;
return reinterpret_cast<FourInts *>( m_pParticleAttributes[ nAttribute ] );
}
inline const int32 *CParticleCollection::GetIntAttributePtr( int nAttribute, size_t *pStrideOut ) const
{
*(pStrideOut) = m_nParticleFloatStrides[ nAttribute ];
return reinterpret_cast<int32 *>( m_pParticleAttributes[ nAttribute ] );
}
inline const FourVectors *CParticleCollection::Get4VAttributePtr( int nAttribute, size_t *pStrideOut ) const
{
*(pStrideOut) = m_nParticleFloatStrides[ nAttribute ]/12;
return reinterpret_cast<const FourVectors *>( m_pParticleAttributes[ nAttribute ] );
}
inline FourVectors *CParticleCollection::Get4VAttributePtrForWrite( int nAttribute, size_t *pStrideOut )
{
*(pStrideOut) = m_nParticleFloatStrides[ nAttribute ]/12;
return reinterpret_cast<FourVectors *>( m_pParticleAttributes[ nAttribute ] );
}
inline const FourVectors *CParticleCollection::GetInitial4VAttributePtr( int nAttribute, size_t *pStrideOut ) const
{
*(pStrideOut) = m_nParticleInitialFloatStrides[ nAttribute ]/12;
return reinterpret_cast<FourVectors *>( m_pParticleInitialAttributes[ nAttribute ] );
}
inline float *CParticleCollection::GetFloatAttributePtrForWrite( int nAttribute, int nParticleNumber )
{
// NOTE: If you hit this assertion, it means your particle operator isn't returning
// the appropriate fields in the RequiredAttributesMask call
Assert( !m_bIsRunningInitializers || ( m_nPerParticleInitializedAttributeMask & (1 << nAttribute) ) );
Assert( !m_bIsRunningOperators || ( m_nPerParticleUpdatedAttributeMask & (1 << nAttribute) ) );
Assert( m_nParticleFloatStrides[nAttribute] != 0 );
Assert( nParticleNumber < m_nAllocatedParticles );
int block_ofs = nParticleNumber/4;
return m_pParticleAttributes[ nAttribute ] +
m_nParticleFloatStrides[ nAttribute ] * block_ofs +
( nParticleNumber & 3 );
}
inline fltx4 *CParticleCollection::GetM128AttributePtrForWrite( int nAttribute, size_t *pStrideOut )
{
// NOTE: If you hit this assertion, it means your particle operator isn't returning
// the appropriate fields in the RequiredAttributesMask call
if ( !HushAsserts() )
{
Assert( !m_bIsRunningInitializers || ( m_nPerParticleInitializedAttributeMask & (1 << nAttribute) ) );
Assert( !m_bIsRunningOperators || ( m_nPerParticleUpdatedAttributeMask & (1 << nAttribute) ) );
Assert( m_nParticleFloatStrides[nAttribute] != 0 );
}
*(pStrideOut) = m_nParticleFloatStrides[ nAttribute ]/4;
return reinterpret_cast<fltx4 *>( m_pParticleAttributes[ nAttribute ] );
}
inline const float *CParticleCollection::GetInitialFloatAttributePtr( int nAttribute, int nParticleNumber ) const
{
Assert( nParticleNumber < m_nAllocatedParticles );
int block_ofs = nParticleNumber / 4;
return m_pParticleInitialAttributes[ nAttribute ] + m_nParticleInitialFloatStrides[ nAttribute ] * block_ofs + ( nParticleNumber & 3 );
}
inline const fltx4 *CParticleCollection::GetInitialM128AttributePtr( int nAttribute, size_t *pStrideOut ) const
{
*(pStrideOut) = m_nParticleInitialFloatStrides[ nAttribute ]/4;
return reinterpret_cast<fltx4 *>( m_pParticleInitialAttributes[ nAttribute ] );
}
inline float *CParticleCollection::GetInitialFloatAttributePtrForWrite( int nAttribute, int nParticleNumber )
{
Assert( nParticleNumber < m_nAllocatedParticles );
Assert( m_nPerParticleReadInitialAttributeMask & ( 1 << nAttribute ) );
int block_ofs = nParticleNumber / 4;
return m_pParticleInitialAttributes[ nAttribute ] + m_nParticleInitialFloatStrides[ nAttribute ] * block_ofs + ( nParticleNumber & 3 );
}
inline fltx4 *CParticleCollection::GetInitialM128AttributePtrForWrite( int nAttribute, size_t *pStrideOut )
{
Assert( m_nPerParticleReadInitialAttributeMask & ( 1 << nAttribute ) );
*(pStrideOut) = m_nParticleInitialFloatStrides[ nAttribute ] / 4;
return reinterpret_cast<fltx4 *>( m_pParticleInitialAttributes[ nAttribute ] );
}
// Used to make sure we're accessing valid memory
inline bool CParticleCollection::IsValidAttributePtr( int nAttribute, const void *pPtr ) const
{
if ( pPtr < m_pParticleAttributes[nAttribute] )
return false;
size_t nArraySize = m_nParticleFloatStrides[nAttribute] * m_nAllocatedParticles / 4;
void *pMaxPtr = m_pParticleAttributes[nAttribute] + nArraySize;
return ( pPtr <= pMaxPtr );
}
FORCEINLINE void CParticleCollection::KillParticle( int nPidx )
{
// add a particle to the sorted kill list. entries must be added in sorted order.
// within a particle operator, this is safe to call. Outside of one, you have to call
// the ApplyKillList() method yourself. The storage for the kill list is global between
// all particle systems, so you can't kill a particle in 2 different CParticleCollections
// w/o calling ApplyKillList
// That said, we only expect the particle index to be at most more than 3 larger than the
// particle count
Assert( nPidx < m_nActiveParticles + 4 );
// note that it is permissible to kill particles with indices>the number of active
// particles, in order to faciliate easy sse coding
Assert( m_nNumParticlesToKill < MAX_PARTICLES_IN_A_SYSTEM );
m_pParticleKillList[ m_nNumParticlesToKill++ ] = nPidx;
}
// initialize this attribute for all active particles
inline void CParticleCollection::FillAttributeWithConstant( int nAttribute, float fValue )
{
size_t stride;
fltx4 *pAttr = GetM128AttributePtrForWrite( nAttribute, &stride );
fltx4 fill=ReplicateX4( fValue );
for( int i = 0; i < m_nPaddedActiveParticles; i++ )
{
*(pAttr) = fill;
pAttr += stride;
}
}
//-----------------------------------------------------------------------------
// Helper to set vector attribute values
//-----------------------------------------------------------------------------
FORCEINLINE void SetVectorAttribute( float *pAttribute, float x, float y, float z )
{
pAttribute[0] = x;
pAttribute[4] = y;
pAttribute[8] = z;
}
FORCEINLINE void SetVectorAttribute( float *pAttribute, const Vector &v )
{
pAttribute[0] = v.x;
pAttribute[4] = v.y;
pAttribute[8] = v.z;
}
FORCEINLINE void SetVectorFromAttribute( Vector &v, const float *pAttribute )
{
v.x = pAttribute[0];
v.y = pAttribute[4];
v.z = pAttribute[8];
}
//-----------------------------------------------------------------------------
// Computes the sq distance to a particle position
//-----------------------------------------------------------------------------
FORCEINLINE float CParticleCollection::ComputeSqrDistanceToParticle( int hParticle, const Vector &vecPosition ) const
{
const float *xyz = GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, hParticle );
Vector vecParticlePosition( xyz[0], xyz[4], xyz[8] );
return vecParticlePosition.DistToSqr( vecPosition );
}
//-----------------------------------------------------------------------------
// Grows the dist sq range for all particles
//-----------------------------------------------------------------------------
FORCEINLINE void CParticleCollection::GrowDistSqrBounds( float flDistSqr )
{
if ( m_flLastMinDistSqr > flDistSqr )
{
m_flLastMinDistSqr = flDistSqr;
}
else if ( m_flLastMaxDistSqr < flDistSqr )
{
m_flLastMaxDistSqr = flDistSqr;
}
}
//-----------------------------------------------------------------------------
// Data associated with children particle systems
//-----------------------------------------------------------------------------
struct ParticleChildrenInfo_t
{
DmObjectId_t m_Id;
CUtlString m_Name;
bool m_bUseNameBasedLookup;
float m_flDelay; // How much to delay this system after the parent starts
};
//-----------------------------------------------------------------------------
// A template describing how a particle system will function
//-----------------------------------------------------------------------------
class CParticleSystemDefinition
{
DECLARE_DMXELEMENT_UNPACK();
DECLARE_REFERENCED_CLASS( CParticleSystemDefinition );
public:
CParticleSystemDefinition( void );
~CParticleSystemDefinition( void );
// Serialization, unserialization
void Read( CDmxElement *pElement );
CDmxElement *Write();
const char *MaterialName() const;
IMaterial *GetMaterial() const;
const char *GetName() const;
const DmObjectId_t& GetId() const;
// Does the particle system use the power of two frame buffer texture (refraction?)
bool UsesPowerOfTwoFrameBufferTexture();
// Does the particle system use the full frame buffer texture (soft particles)
bool UsesFullFrameBufferTexture();
// Should we always precache this?
bool ShouldAlwaysPrecache() const;
// Should we batch particle collections using this definition up?
bool ShouldBatch() const;
// Is the particle system rendered on the viewmodel?
bool IsViewModelEffect() const;
// Used to iterate over all particle collections using the same def
CParticleCollection *FirstCollection();
// What's the effective cull size + fill cost?
// Used for early retirement
float GetCullRadius() const;
float GetCullFillCost() const;
int GetCullControlPoint() const;
const char *GetCullReplacementDefinition() const;
// Retirement
bool HasRetirementBeenChecked( int nFrame ) const;
void MarkRetirementCheck( int nFrame );
// Control point read
void MarkReadsControlPoint( int nPoint );
bool ReadsControlPoint( int nPoint ) const;
private:
void Precache();
void Uncache();
bool IsPrecached() const;
void UnlinkAllCollections();
void SetupContextData( );
void ParseChildren( CDmxElement *pElement );
void ParseOperators( const char *pszName, ParticleFunctionType_t nFunctionType,
CDmxElement *pElement, CUtlVector<CParticleOperatorInstance *> &out_list );
void WriteChildren( CDmxElement *pElement );
void WriteOperators( CDmxElement *pElement, const char *pOpKeyName,
const CUtlVector<CParticleOperatorInstance *> &inList );
CUtlVector<CParticleOperatorInstance *> *GetOperatorList( ParticleFunctionType_t type );
CParticleOperatorInstance *FindOperatorById( ParticleFunctionType_t type, const DmObjectId_t &id );
private:
int m_nInitialParticles;
int m_nPerParticleUpdatedAttributeMask;
int m_nPerParticleInitializedAttributeMask;
int m_nInitialAttributeReadMask;
int m_nAttributeReadMask;
uint64 m_nControlPointReadMask;
Vector m_BoundingBoxMin;
Vector m_BoundingBoxMax;
char m_pszMaterialName[MAX_PATH];
CMaterialReference m_Material;
CParticleCollection *m_pFirstCollection;
char m_pszCullReplacementName[128];
float m_flCullRadius;
float m_flCullFillCost;
int m_nCullControlPoint;
int m_nRetireCheckFrame;
// Default attribute values
Color m_ConstantColor;
float m_flConstantRadius;
float m_flConstantRotation;
float m_flConstantRotationSpeed;
int m_nConstantSequenceNumber;
int m_nConstantSequenceNumber1;
int m_nGroupID;
float m_flMaximumTimeStep;
float m_flMaximumSimTime; // maximum time to sim before drawing first frame.
float m_flMinimumSimTime; // minimum time to sim before drawing first frame - prevents all
// capped particles from drawing at 0 time.
int m_nMinimumFrames; // number of frames to apply max/min simulation times
// Is the particle system rendered on the viewmodel?
bool m_bViewModelEffect;
size_t m_nContextDataSize;
DmObjectId_t m_Id;
public:
float m_flMaxDrawDistance; // distance at which to not draw.
float m_flNoDrawTimeToGoToSleep; // after not beeing seen for this long, the system will sleep
int m_nMaxParticles;
int m_nSkipRenderControlPoint; // if the camera is attached to the
// object associated with this control
// point, don't render the system
CUtlString m_Name;
CUtlVector<CParticleOperatorInstance *> m_Operators;
CUtlVector<CParticleOperatorInstance *> m_Renderers;
CUtlVector<CParticleOperatorInstance *> m_Initializers;
CUtlVector<CParticleOperatorInstance *> m_Emitters;
CUtlVector<CParticleOperatorInstance *> m_ForceGenerators;
CUtlVector<CParticleOperatorInstance *> m_Constraints;
CUtlVector<ParticleChildrenInfo_t> m_Children;
CUtlVector<size_t> m_nOperatorsCtxOffsets;
CUtlVector<size_t> m_nRenderersCtxOffsets;
CUtlVector<size_t> m_nInitializersCtxOffsets;
CUtlVector<size_t> m_nEmittersCtxOffsets;
CUtlVector<size_t> m_nForceGeneratorsCtxOffsets;
CUtlVector<size_t> m_nConstraintsCtxOffsets;
// profiling information
float m_flTotalSimTime;
float m_flUncomittedTotalSimTime;
float m_flMaxMeasuredSimTime;
int m_nMaximumActiveParticles;
bool m_bShouldSort;
bool m_bShouldBatch;
bool m_bIsPrecached : 1;
bool m_bAlwaysPrecache : 1;
friend class CParticleCollection;
friend class CParticleSystemMgr;
};
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline CParticleSystemDefinition::CParticleSystemDefinition( void )
{
m_nControlPointReadMask = 0;
m_nInitialAttributeReadMask = 0;
m_nPerParticleInitializedAttributeMask = 0;
m_nPerParticleUpdatedAttributeMask = 0;
m_nAttributeReadMask = 0;
m_flTotalSimTime = 0.0;
m_flMaxMeasuredSimTime = 0.0;
m_nMaximumActiveParticles = 0;
m_bIsPrecached = false;
m_bAlwaysPrecache = false;
m_bShouldBatch = false;
m_bShouldSort = true;
m_pFirstCollection = NULL;
m_flCullRadius = 0.0f;
m_flCullFillCost = 1.0f;
m_nRetireCheckFrame = 0;
}
inline CParticleSystemDefinition::~CParticleSystemDefinition( void )
{
UnlinkAllCollections();
m_Operators.PurgeAndDeleteElements();
m_Renderers.PurgeAndDeleteElements();
m_Initializers.PurgeAndDeleteElements();
m_Emitters.PurgeAndDeleteElements();
m_ForceGenerators.PurgeAndDeleteElements();
m_Constraints.PurgeAndDeleteElements();
}
// Used to iterate over all particle collections using the same def
inline CParticleCollection *CParticleSystemDefinition::FirstCollection()
{
return m_pFirstCollection;
}
inline float CParticleSystemDefinition::GetCullRadius() const
{
return m_flCullRadius;
}
inline float CParticleSystemDefinition::GetCullFillCost() const
{
return m_flCullFillCost;
}
inline const char *CParticleSystemDefinition::GetCullReplacementDefinition() const
{
return m_pszCullReplacementName;
}
inline int CParticleSystemDefinition::GetCullControlPoint() const
{
return m_nCullControlPoint;
}
inline void CParticleSystemDefinition::MarkReadsControlPoint( int nPoint )
{
m_nControlPointReadMask |= ( 1ULL << nPoint );
}
inline bool CParticleSystemDefinition::ReadsControlPoint( int nPoint ) const
{
return ( m_nControlPointReadMask & ( 1ULL << nPoint ) ) != 0;
}
// Retirement
inline bool CParticleSystemDefinition::HasRetirementBeenChecked( int nFrame ) const
{
return m_nRetireCheckFrame == nFrame;
}
inline void CParticleSystemDefinition::MarkRetirementCheck( int nFrame )
{
m_nRetireCheckFrame = nFrame;
}
inline bool CParticleSystemDefinition::ShouldBatch() const
{
return m_bShouldBatch;
}
inline bool CParticleSystemDefinition::IsViewModelEffect() const
{
return m_bViewModelEffect;
}
inline const char *CParticleSystemDefinition::MaterialName() const
{
return m_pszMaterialName;
}
inline const DmObjectId_t& CParticleSystemDefinition::GetId() const
{
return m_Id;
}
inline int CParticleCollection::GetGroupID( void ) const
{
return m_pDef->m_nGroupID;
}
FORCEINLINE const Vector& CParticleCollection::GetControlPointAtCurrentTime( int nControlPoint ) const
{
Assert( nControlPoint <= GetHighestControlPoint() );
Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
return m_ControlPoints[nControlPoint].m_Position;
}
FORCEINLINE void CParticleCollection::GetControlPointOrientationAtCurrentTime( int nControlPoint, Vector *pForward, Vector *pRight, Vector *pUp ) const
{
Assert( nControlPoint <= GetHighestControlPoint() );
Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
// FIXME: Use quaternion lerp to get control point transform at time
*pForward = m_ControlPoints[nControlPoint].m_ForwardVector;
*pRight = m_ControlPoints[nControlPoint].m_RightVector;
*pUp = m_ControlPoints[nControlPoint].m_UpVector;
}
FORCEINLINE int CParticleCollection::GetControlPointParent( int nControlPoint ) const
{
Assert( nControlPoint <= GetHighestControlPoint() );
Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
return m_ControlPoints[nControlPoint].m_nParent;
}
FORCEINLINE bool CParticleCollection::IsValid( void ) const
{
return ( m_pDef != NULL && m_pDef->GetMaterial() );
}
#endif // PARTICLES_H