2233 lines
85 KiB
C++
2233 lines
85 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: particle system definitions
|
|
//
|
|
//===========================================================================//
|
|
|
|
#ifndef PARTICLES_H
|
|
#define PARTICLES_H
|
|
#ifdef _WIN32
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "dmxloader/dmxelement.h"
|
|
#include "materialsystem/MaterialSystemUtil.h"
|
|
#include "materialsystem/imaterialsystem.h"
|
|
#include "mathlib/mathlib.h"
|
|
#include "mathlib/ssemath.h"
|
|
#include "mathlib/vector.h"
|
|
#include "tier1/UtlStringMap.h"
|
|
#include "tier1/utlintrusivelist.h"
|
|
#include "tier1/utlmap.h"
|
|
#include "tier1/utlobjectreference.h"
|
|
#include "tier1/utlsoacontainer.h"
|
|
#include "trace.h"
|
|
#include "vstdlib/random.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);
|
|
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
|