#ifndef PARTICLES_H
#define PARTICLES_H
#ifdef _WIN32
#pragma once
#endif

#include "mathlib.h"
#include "vector.h"
#include "ssemath.h"
#include "imaterialsystem.h"
#include "dmxelement.h"
#include "utlintrusivelist.h"
#include "random.h"
#include "utlobjectreference.h"
#include "UtlStringMap.h"
#include "utlmap.h"
#include "MaterialSystemUtil.h"
#include "trace.h"
#include "utlsoacontainer.h"

#if defined( CLIENT_DLL )
#include "c_pixel_visibility.h"
#endif

struct DmxElementUnpackStructure_t;
class CParticleSystemDefinition;
class CParticleCollection;
class CParticleOperatorInstance;
class CParticleSystemDictionary;
class CUtlBuffer;
class IParticleOperatorDefinition;
class CSheet;
class CMeshBuilder;
extern float s_pRandomFloats[];


#define MAX_RANDOM_FLOATS 4096
#define RANDOM_FLOAT_MASK ( MAX_RANDOM_FLOATS - 1 )


#define MAX_PARTICLE_ATTRIBUTES 32

#define DEFPARTICLE_ATTRIBUTE( name, bit )						\
	const int PARTICLE_ATTRIBUTE_##name##_MASK = (1 << bit);	\
	const int PARTICLE_ATTRIBUTE_##name = bit;

DEFPARTICLE_ATTRIBUTE(XYZ, 0);

DEFPARTICLE_ATTRIBUTE(LIFE_DURATION, 1);

DEFPARTICLE_ATTRIBUTE(PREV_XYZ, 2);

DEFPARTICLE_ATTRIBUTE(RADIUS, 3);

DEFPARTICLE_ATTRIBUTE(ROTATION, 4);

DEFPARTICLE_ATTRIBUTE(ROTATION_SPEED, 5);

DEFPARTICLE_ATTRIBUTE(TINT_RGB, 6);

DEFPARTICLE_ATTRIBUTE(ALPHA, 7);

DEFPARTICLE_ATTRIBUTE(CREATION_TIME, 8);

DEFPARTICLE_ATTRIBUTE(SEQUENCE_NUMBER, 9);

DEFPARTICLE_ATTRIBUTE(TRAIL_LENGTH, 10);

DEFPARTICLE_ATTRIBUTE(PARTICLE_ID, 11);

DEFPARTICLE_ATTRIBUTE(YAW, 12);

DEFPARTICLE_ATTRIBUTE(SEQUENCE_NUMBER1, 13);

DEFPARTICLE_ATTRIBUTE(HITBOX_INDEX, 14);

DEFPARTICLE_ATTRIBUTE(HITBOX_RELATIVE_XYZ, 15);

DEFPARTICLE_ATTRIBUTE(ALPHA2, 16);

DEFPARTICLE_ATTRIBUTE(TRACE_P0, 17);						    
DEFPARTICLE_ATTRIBUTE(TRACE_P1, 18);						    
DEFPARTICLE_ATTRIBUTE(TRACE_HIT_T, 19);					   
DEFPARTICLE_ATTRIBUTE(TRACE_HIT_NORMAL, 20);				      


#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

#define MEASURE_PARTICLE_PERF 0


enum ParticleFunctionType_t
{
	FUNCTION_RENDERER = 0,
	FUNCTION_OPERATOR,
	FUNCTION_INITIALIZER,
	FUNCTION_EMITTER,
	FUNCTION_CHILDREN,	                     
	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;
	}

};




#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;

	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;											  
	}

	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,										     
		ModelHitBoxInfo_t* pHitBoxOutputBuffer)
	{
		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)
	{
	}
};


typedef int ParticleSystemHandle_t;

class CParticleSystemMgr
{
public:
	CParticleSystemMgr();
	~CParticleSystemMgr();

	bool Init(IParticleSystemQuery* pQuery);

	void AddBuiltinSimulationOperators(void);
	void AddBuiltinRenderingOperators(void);



	void AddParticleOperator(ParticleFunctionType_t nOpType, IParticleOperatorDefinition* pOpFactory);

	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();

	bool WriteParticleConfigFile(const char* pParticleSystemName, CUtlBuffer& buf, bool bPreventNameBasedLookup = false);
	bool WriteParticleConfigFile(const DmObjectId_t& id, CUtlBuffer& buf, bool bPreventNameBasedLookup = false);

	CParticleCollection* CreateParticleCollection(const char* pParticleSystemName, float flDelay = 0.0f, int nRandomSeed = 0);

	CParticleCollection* CreateParticleCollection(const DmObjectId_t& id, float flDelay = 0.0f, int nRandomSeed = 0);

	bool IsParticleSystemDefined(const char* pParticleSystemName);
	bool IsParticleSystemDefined(const DmObjectId_t& id);

	ParticleSystemHandle_t GetParticleSystemIndex(const char* pParticleSystemName);

	const char* GetParticleSystemNameFromIndex(ParticleSystemHandle_t iIndex);

	int GetParticleSystemCount(void);

	CUtlVector< IParticleOperatorDefinition*>& GetAvailableParticleOperatorList(ParticleFunctionType_t nWhichList);

	const DmxElementUnpackStructure_t* GetParticleSystemDefinitionUnpackStructure();

	void ShouldLoadSheets(bool bLoadSheets);
	CSheet* FindOrLoadSheet(char const* pszFname, ITexture* pTexture);
	CSheet* FindOrLoadSheet(IMaterial* pMaterial);
	void FlushAllSheets(void);


	void ResetRenderCache(void);
	void AddToRenderCache(CParticleCollection* pParticles);
	void DrawRenderCache(bool bShadowDepth);

	IParticleSystemQuery* Query(void) { return m_pQuery; }

	const char* GetParticleFieldName(int nParticleField) const;

	CParticleSystemDefinition* FindParticleSystem(const char* pName);
	CParticleSystemDefinition* FindParticleSystem(const DmObjectId_t& id);

	void CommitProfileInformation(bool bCommit);			      

	void DumpProfileInformation(void);					  

	void PrecacheParticleSystem(const char* pName);
	void UncacheAllParticleSystems();

	void SetLastSimulationTime(float flTime);
	float GetLastSimulationTime() const;

	int Debug_GetTotalParticleCount() const;
	bool Debug_FrameWarningNeededTestAndReset();
	float ParticleThrottleScaling() const;		            
	bool ParticleThrottleRandomEnable() const;	            

	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;
	};

	bool ReadParticleDefinitions(CUtlBuffer& buf, const char* pFileName, bool bPrecache, bool bDecommitTempMemory);
	void AddParticleSystem(CDmxElement* pParticleSystem);

	CDmxElement* CreateParticleDmxElement(const DmObjectId_t& id);
	CDmxElement* CreateParticleDmxElement(const char* pParticleSystemName);

	bool WriteParticleConfigFile(CDmxElement* pParticleSystem, CUtlBuffer& buf, bool bPreventNameBasedLookup);

	void BuildBatchList(int iRenderCache, IMatRenderContext* pRenderContext, CUtlVector< Batch_t >& batches);

	CUtlVector<IParticleOperatorDefinition*> m_ParticleOperators[PARTICLE_FUNCTION_COUNT];

	CParticleSystemDictionary* m_pParticleSystemDictionary;

	typedef CUtlStringMap< CSheet* > SheetsCache_t;
	SheetsCache_t m_SheetList;

	void AttachKillList(CParticleCollection* pParticles);
	void DetachKillList(CParticleCollection* pParticles);

	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;


enum ParticleOperatorId_t
{
	OPERATOR_GENERIC = -2,		         
	OPERATOR_SINGLETON = -1,	            

	OPERATOR_PI_POSITION,		         
	OPERATOR_PI_RADIUS,
	OPERATOR_PI_ALPHA,
	OPERATOR_PI_TINT_RGB,
	OPERATOR_PI_ROTATION,
	OPERATOR_PI_YAW,

	OPERATOR_ID_COUNT,
};


class IParticleOperatorDefinition
{
public:
	virtual const char* GetName() const = 0;
	virtual CParticleOperatorInstance* CreateInstance(const DmObjectId_t& id) 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
	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
};


class CParticleOperatorInstance
{
public:
	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);

	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;
	}

	virtual uint32 GetReadInitialAttributes(void) const
	{
		return 0;
	}

	virtual void Operate(CParticleCollection* pParticles, float flOpStrength, void* pContext) const
	{
	}

	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
	{
	}

	virtual int GetParticlesToRender(CParticleCollection* pParticles, void* pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int* pVertsUsed, int* pIndicesUsed) const
	{
		*pVertsUsed = 0;
		*pIndicesUsed = 0;
		return 0;
	}


	virtual uint32 Emit(CParticleCollection* pParticles, float flOpCurStrength,
		void* pContext) const
	{
		return 0;
	}

	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) {}

	virtual void InitParticleSystem(CParticleCollection* pParticles, void* pContext) const
	{
	}


	virtual void AddForces(FourVectors* AccumulatedForces,
		CParticleCollection* pParticles,
		int nBlocks,
		float flCurStrength,
		void* pContext) const
	{
	}


	virtual void SetupConstraintPerFrameData(CParticleCollection* pParticles,
		void* pContext) const
	{
	}


	virtual bool EnforceConstraint(int nStartBlock,
		int nNumBlocks,
		CParticleCollection* pParticles,
		void* pContext,
		int nNumValidParticlesInLastChunk) const
	{
		return false;
	}

	virtual bool IsFinalConstraint(void) const
	{
		return false;
	}

	virtual bool InitMultipleOverride()
	{
		return false;
	}


	virtual bool IsScrubSafe()
	{
		return false;
	}

	virtual void InitNewParticlesScalar(CParticleCollection* pParticles, int nFirstParticle, int n_particles, int attribute_write_mask, void* pContext) const
	{
	}

	virtual void InitNewParticlesBlock(CParticleCollection* pParticles, int start_block, int n_blocks, int attribute_write_mask, void* pContext) const
	{
		InitNewParticlesScalar(pParticles, 4 * start_block, 4 * n_blocks, attribute_write_mask, pContext);
	}

	void InitNewParticles(CParticleCollection* pParticles, int nFirstParticle, int n_particles, int attribute_write_mask, void* pContext) const;


	virtual bool MayCreateMoreParticles(CParticleCollection* pParticles, void* pContext) const
	{
		return false;
	}

	const IParticleOperatorDefinition* GetDefinition()
	{
		return m_pDef;
	}

	virtual bool ShouldRunBeforeEmitters(void) const
	{
		return false;
	}

	virtual bool RequiresOrderInvariance(void) const
	{
		return false;
	}

	virtual void SkipToTime(float flTime, CParticleCollection* pParticles, void* pContext) const {}

	const DmObjectId_t& GetId() { return m_Id; }

	virtual void Render(CParticleCollection* pParticles) const {}

	int RandomInt(int nMin, int nMax)
	{
		Assert(0);
		return 0;
	}

	float RandomFloat(float flMinVal = 0.0f, float flMaxVal = 1.0f)
	{
		Assert(0);
		return 0.0f;
	}

	float RandomFloatExp(float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f)
	{
		Assert(0);
		return 0.0f;
	}

	float m_flOpStartFadeInTime;
	float m_flOpEndFadeInTime;
	float m_flOpStartFadeOutTime;
	float m_flOpEndFadeOutTime;
	float m_flOpFadeOscillatePeriod;

	virtual ~CParticleOperatorInstance(void)
	{
	}

protected:
	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;
};

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;
	}

	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 )

#define REGISTER_PARTICLE_OPERATOR( _type, _className )	\
	g_pParticleSystemMgr->AddParticleOperator( _type, &s_##_className##Factory )



enum
{
	PCFLAGS_FIRST_FRAME = 0x1,
	PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED = 0x2,
};



#define DEBUG_PARTICLE_SORT 0

struct ParticleRenderData_t
{
	float m_flSortKey;										    
	int   m_nIndex;									        
	float m_flRadius;					       
#if VALVE_LITTLE_ENDIAN
	uint8 m_nAlpha;							           
	uint8 m_nAlphaPad[3];										     
#else
	uint8 m_nAlphaPad[3];										     
	uint8 m_nAlpha;							           
#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;



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;

	Vector m_ForwardVector;
	Vector m_UpVector;
	Vector m_RightVector;

	void* m_pObject;

	int m_nParent;
};


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


struct CUtlString_simple {
	char* buffer;
	int capacity;
	int grow_size;
	int length;
};

class CParticleSystemDefinition {
	BYTE pad_0[308];
public:
	CUtlString_simple m_Name;
};

struct CParticleAttributeAddressTable {
	float* m_pAttributes[MAX_PARTICLE_ATTRIBUTES];
	size_t m_nFloatStrides[MAX_PARTICLE_ATTRIBUTES];

	FORCEINLINE float* FloatAttributePtr(int nAttribute, int nParticleNumber) const {
		int block_ofs = nParticleNumber / 4;
		return m_pAttributes[nAttribute] +
			m_nFloatStrides[nAttribute] * block_ofs +
			(nParticleNumber & 3);
	}

};

class CParticleCollection {
	BYTE pad_0[48];
public:
	int m_nActiveParticles;
private:
	BYTE pad_1[12];
public:
	CUtlReference<CParticleSystemDefinition> m_pDef;
private:
	BYTE pad_2[60];
public:
	CParticleCollection* m_pParent;
private:
	BYTE pad_3[84];
public:
	CParticleAttributeAddressTable m_ParticleAttributes;
};




























#endif	 
