//
// Asura_PFX_Templates.h
//
// Created by KOR - 27/08/2004
//
// When adding a member to any of the templates, remember to add it to all of:
// Default constructor
// Copy constructor
// = operator
// SetupGraphClamps (if applicable)
// Chunk size (Asura_Chunk_PFX.cpp, top of chunk create)
// Chunk fill in (Asura_Chunk_PFX.cpp)
// Chunk process (Asura_Chunk_PFX.cpp)
// Data Block code (CreateDataBlock, FillInFromDataBlock)
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif

#ifndef __ASURA_PFX_TEMPLATES_H__
#define __ASURA_PFX_TEMPLATES_H__

#include "Core/Asura_Hash.h"
#include "Core/Asura_Graph.h"
#ifdef _DEBUG
#include "Core/Asura_String.h"
#endif
#ifdef ASURA_TOOLS
#include "Core/Asura_TextureManagement.h"
#endif //ASURA_TOOLS
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_RenderStates.h"
#include "SpecialFX/PFX/Asura_PFX_Volume.h"

/// DEFINES ///////////////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_HDR
	const float fASURA_PFX_MAX_PARTICLE_COLOUR = 100.0f;
	const float fASURA_PFX_MAX_PHOTON_COLOUR   = 100.0f;
#else
	const float fASURA_PFX_MAX_PARTICLE_COLOUR = 1.0f;
	const float fASURA_PFX_MAX_PHOTON_COLOUR   = 1.0f;
#endif

/// CLASSES ///////////////////////////////////////////////////////////////////////////////////////

/// Asura_PFX_Particle_Template ///////////////////////////////////////////////////////////////////
// Defines how the particle looks
///////////////////////////////////////////////////////////////////////////////////////////////////

struct Asura_PFX_TextureFrame
{
	float	m_afTextureUVs[4];
	float	m_fTime;//time between 0 and 1 for frame
};

class Asura_PFX_Effect;
class Asura_PFX_Particle;


class Asura_PFX_Particle_Template
{
public:

#ifdef ASURA_TOOLS

	// Default constructor (fill out with default values)
	Asura_PFX_Particle_Template()
	: m_xColour(16, Asura_RGBA(1.f, 0.8f, 0.6f, 0.4f), Asura_RGBA(.5f, .4f, .3f, .2f))
	, m_xSize(16, 0.1f, 0.1f)
	, m_xRotation(16, 1.f, 0.f)
	, m_xSizeVariation(16, 0.f, 0.f)
	, m_eTranslucency(ASURA_TRANSLUCENCY_NORMAL)
	, m_fMinLifetime(1.f)
	, m_fMaxLifetimeDiff(0.f)
	, m_fBlurLength(.1f)
	, m_fFixedRotation(0.f)
	, m_fLightingScale(1.f)
	, m_uNumFixedRotations(1)
	, m_fNearFadeDistance(1.f)
	, m_fNearZeroDistance(.5f)
	, m_fFarFadeDistance(100.0f)
	, m_fFarZeroDistance(200.0f)
	, m_fFPS(1.f)
	, m_iTextureHandle(ASURA_TEXTURE_INVALID)
	, m_xTextureName("\\specialfx\\partclfx.tga") // default particle texture
	, m_uFlags(0)
#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
	, m_uDataBlockSize(0)
	, m_pDataBlock(NULL)
#endif
	, m_fVolumetricDepthScale(0.0f)
	, m_uSoundEvent(0)
	, m_bModified(true)
	{
		Asura_ResourcePushType(ASURA_RESOURCE_PFX);
		{
			m_uNumTextureFrames = 1;
			Asura_ResourceAllocateArray(m_pxTextureFrames, 1);

			m_pxTextureFrames[0].m_afTextureUVs[0] = m_pxTextureFrames[0].m_afTextureUVs[1] = 0.f;
			m_pxTextureFrames[0].m_afTextureUVs[2] = m_pxTextureFrames[0].m_afTextureUVs[3] = 0.125f;
			m_pxTextureFrames[0].m_fTime = 0.0f;

			SetupGraphClamps();

			// Patch up the texture handle
			m_iTextureHandle = Asura_Texture_Management::GetTextureIndex(m_xTextureName);
		}
	}

#endif // #ifdef ASURA_TOOLS

	// Copy constructor
	Asura_PFX_Particle_Template(const Asura_PFX_Particle_Template& xOriginal)
	: m_xColour(xOriginal.m_xColour)
	, m_xSize(xOriginal.m_xSize)
	, m_xRotation(xOriginal.m_xRotation)
	, m_xSizeVariation(xOriginal.m_xSizeVariation)
	, m_eTranslucency(xOriginal.m_eTranslucency)
	, m_fMinLifetime(xOriginal.m_fMinLifetime)
	, m_fMaxLifetimeDiff(xOriginal.m_fMaxLifetimeDiff)
	, m_fBlurLength(xOriginal.m_fBlurLength)
	, m_fFixedRotation(xOriginal.m_fFixedRotation)
	, m_fLightingScale(xOriginal.m_fLightingScale)
	, m_uNumFixedRotations(xOriginal.m_uNumFixedRotations)
	, m_fNearFadeDistance(xOriginal.m_fNearFadeDistance)
	, m_fNearZeroDistance(xOriginal.m_fNearZeroDistance)
	, m_fFarFadeDistance(xOriginal.m_fFarFadeDistance)
	, m_fFarZeroDistance(xOriginal.m_fFarZeroDistance)
	, m_fFPS(xOriginal.m_fFPS)
	, m_iTextureHandle(xOriginal.m_iTextureHandle)
#if _DEBUG || defined(ASURA_TOOLS)
	, m_xTextureName(xOriginal.m_xTextureName)
#endif
	, m_uFlags(xOriginal.m_uFlags)
	, m_fVolumetricDepthScale(xOriginal.m_fVolumetricDepthScale)
	, m_uSoundEvent(xOriginal.m_uSoundEvent)
#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
	, m_uDataBlockSize(0)
	, m_pDataBlock(NULL)
#endif
#ifdef ASURA_TOOLS
	, m_bModified(true)
#endif
	{
		Asura_ResourcePushType(ASURA_RESOURCE_PFX);
		{
			m_uNumTextureFrames = xOriginal.m_uNumTextureFrames;
			Asura_ResourceAllocateArray(m_pxTextureFrames, m_uNumTextureFrames);
			for(u_int u = 0; u < m_uNumTextureFrames; ++u)
			{
				m_pxTextureFrames[u] = xOriginal.m_pxTextureFrames[u];
			}
		}
		Asura_ResourcePopType();
	}

#if defined(ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK) && defined(ASURA_GRAPH_LEAN_AND_MEAN)
	// we have a function to fill out from datablock in this case.
	Asura_PFX_Particle_Template() {}
#endif
	// File constructor
	Asura_PFX_Particle_Template(Asura_Handle iFileHandle, int iVersion);


	~Asura_PFX_Particle_Template()
	{
		Asura_ResourcePushType(ASURA_RESOURCE_PFX);
		{
			Asura_ResourceDeallocateArray(m_pxTextureFrames);
#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
			Asura_Deallocate( m_pDataBlock );
#endif
		}
		Asura_ResourcePopType();
	}

	Asura_PFX_Particle_Template& operator = (const Asura_PFX_Particle_Template& xTemplate)
	{
		if(this != &xTemplate) 
		{
			Asura_ResourcePushType(ASURA_RESOURCE_PFX);
			{
				m_xColour = xTemplate.m_xColour;
				m_xSize = xTemplate.m_xSize;
				m_xRotation = xTemplate.m_xRotation;
				m_xSizeVariation = xTemplate.m_xSizeVariation;
				m_eTranslucency = xTemplate.m_eTranslucency;
				m_fMinLifetime = xTemplate.m_fMinLifetime;
				m_fMaxLifetimeDiff = xTemplate.m_fMaxLifetimeDiff;
				m_fBlurLength = xTemplate.m_fBlurLength;
				m_fFixedRotation = xTemplate.m_fFixedRotation;
				m_fLightingScale = xTemplate.m_fLightingScale;
				m_uNumFixedRotations = xTemplate.m_uNumFixedRotations;
				m_fNearFadeDistance = xTemplate.m_fNearFadeDistance;
				m_fNearZeroDistance = xTemplate.m_fNearZeroDistance;
				m_fFarFadeDistance = xTemplate.m_fFarFadeDistance;
				m_fFarZeroDistance = xTemplate.m_fFarZeroDistance;
				m_fFPS = xTemplate.m_fFPS;
				m_uFlags = xTemplate.m_uFlags;
				m_fVolumetricDepthScale = xTemplate.m_fVolumetricDepthScale;
				m_uSoundEvent = xTemplate.m_uSoundEvent;

				m_uNumTextureFrames = xTemplate.m_uNumTextureFrames;
				Asura_ResourceDeallocateArray(m_pxTextureFrames);
				Asura_ResourceAllocateArray(m_pxTextureFrames, m_uNumTextureFrames);
				for(u_int u = 0; u < m_uNumTextureFrames; ++u)
				{
					m_pxTextureFrames[u] = xTemplate.m_pxTextureFrames[u];
				}
			}
			Asura_ResourcePopType();
		}
		return *this;
	}


	//Get texture coordinates for particle at a given time
	const Asura_PFX_TextureFrame* GetTextureFrameAtTime(float fTime) const
	{
		if(m_bUseFPS)
		{
			// Use frames per second
			// TODO - handling for random lifetime, if desired
			const u_int uFrame = Asura_Maths::FloatToInt_Chop(m_fMinLifetime * fTime * m_fFPS);
			if(m_bLoopAnimation)
			{
				return &m_pxTextureFrames[uFrame % m_uNumTextureFrames];
			}
			else
			{
				return &m_pxTextureFrames[Asura_Maths::ClampToRange(uFrame, 0u, (m_uNumTextureFrames - 1))];
			}
		}
		// Use time offsets
		// Work backwards to find correct frame
		for(u_int uIndex = m_uNumTextureFrames-1;uIndex;uIndex--)
		{
			if(m_pxTextureFrames[uIndex].m_fTime <= fTime)
			{
				return &m_pxTextureFrames[uIndex];
			}
		}
		// Default fallback.
		return &m_pxTextureFrames[0];
	}

	//Get texture coordinates for current and next texture frame with weight of first one.
	// This is to allow 2-pass rendering with alpha blend between frames.
	void GetTextureFramesForCrossfadeAtTime(float fTime, 
											const Asura_PFX_TextureFrame*& pxFrame0, 
											const Asura_PFX_TextureFrame*& pxFrame1, 
											float& fWeight) const;

#ifdef ASURA_TOOLS
	void SetupGraphClamps()
	{
		m_xColour.SetClampingValues(true, true, 0.0f, fASURA_PFX_MAX_PARTICLE_COLOUR);
		m_xColour.SetSampleTolerance(ASURA_GRAPH_DEFAULT_SAMPLE_TOLERANCE, false);//make sample tolerance non-proportional to get decent results with HDR graphs
		m_xSize.SetClampingValues(true, false, 0.0f, Asura_Maths::LargeFloat);
		m_xSizeVariation.SetClampingValues(true, true, 0.0f, 1.0f);
		m_xRotation.SetSampleTolerance(0.0f);//Rotation is saved as integrated samples , so the sample reduction stuff won't behave correctly
	}
#endif // ASURA_TOOLS

#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK

#ifdef ASURA_GRAPH_LEAN_AND_MEAN

	void FillInFromDataBlock(const void* const pInputData, u_int uSize);

#else // ASURA_GRAPH_LEAN_AND_MEAN

	// to assist with processing on PS3's SPUs, each template maintains a sort of chunk
	// data block from which the entire template can be reconstructed. This data block
	// is DMA'd in one go and the template is reconstructed at the other end.
	u_int GetDataBlockSize() const { return m_uDataBlockSize; }
	void* GetDataBlock() const { return m_pDataBlock; }
	void CreateDataBlock();	// allocate mem and fill out

#endif // ASURA_GRAPH_LEAN_AND_MEAN

#endif // #ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK


	// Members
	Asura_Graph<Asura_RGBA>	m_xColour;		// Colour over particle's lifetime  
	Asura_Graph<float>		m_xSize;		// Size over particle's lifetime  
	Asura_Graph<float>		m_xRotation;	// Rotation speed over particle's lifetime
	Asura_Graph<float>		m_xSizeVariation; // Variation in size of particle's lifetime
//	Asura_Handle			m_iTexture;		// Texture handle, which maps to a particular UV pair  
	ASURA_TRANSLUCENCY_MODE m_eTranslucency;
	float					m_fMinLifetime;
	float					m_fMaxLifetimeDiff;
	u_int					m_uNumTextureFrames;
	float					m_fBlurLength;
	float					m_fFixedRotation;
	float					m_fLightingScale;
	u_int					m_uNumFixedRotations;
	float					m_fNearFadeDistance;
	float					m_fNearZeroDistance;
	float					m_fFarFadeDistance;//distance at which particle starts fading when far from camera
	float					m_fFarZeroDistance;//distance at which particle is completely faded when far from camera
	float					m_fFPS;// frames per second for looping animation
	// KOR - 09/05/2006 - members for textures.
	// The texture name is stored in tools and in the chunk.
	// The name is used to look up a handle on load for non-tools.
	int						m_iTextureHandle;
#if _DEBUG || defined(ASURA_TOOLS)
	Asura_String			m_xTextureName;
#endif
	Asura_PFX_TextureFrame*	m_pxTextureFrames;
	union
	{
		struct
		{
			// NOTE: These bit's are written out, adding any new bits means
			// ~~~~~ that you will need to add those bits to the bitfield
			//       preparation code in "Asura_Chunk_PFX_ParticleTemp::Create".
			unsigned int	m_bUseLighting					:1;
			unsigned int	m_bUseMotionBlur				:1;
			unsigned int	m_bUseRandomRotation			:1;
			unsigned int	m_bLoopAnimation				:1;
			unsigned int	m_bUseFPS						:1;
			unsigned int	m_bSpriteRotation				:1;
			unsigned int	m_bAlwaysVertical				:1; // Deprecated (replaced by fixed rotation)
			unsigned int	m_bAlwaysHorizontal				:1; // Deprecated
			unsigned int	m_bRandomTextureOrient			:1;
			unsigned int	m_b3DRotation					:1;
			unsigned int	m_bMirrorRotation				:1;
			unsigned int	m_bAlwaysExport					:1;//Obsolete
			unsigned int	m_bAlignEffectX					:1;
			unsigned int	m_bAlignEffectY					:1;
			unsigned int	m_bAlignEffectZ					:1;
			unsigned int	m_bFixedRotation				:1;
			unsigned int	m_bNearCameraFade				:1;
			unsigned int	m_bRandomLifetime				:1;
			unsigned int	m_bUseSizeScale					:1;
			unsigned int	m_bPreLighting					:1;
			unsigned int	m_bFarCameraFade				:1;
			unsigned int	m_bCrossfadeTextureFrames		:1;
			unsigned int	m_bCanUseExpensiveClipping		:1;
			unsigned int	m_bBillboardJustX				:1;
			unsigned int	m_bBillboardJustY				:1; 
			unsigned int	m_bUseUVAsAspect				:1;
			unsigned int	m_bLooped						:1;
			unsigned int    m_bUseFogging					:1;
			unsigned int	m_bRenderEarly					:1; // 29 flags

		};
		unsigned int		m_uFlags;
	};

	float                   m_fVolumetricDepthScale;  // for soft/volumetric PFX on next-gen
	Asura_Hash_ID			m_uSoundEvent;

#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
	u_int					m_uDataBlockSize;
	void*					m_pDataBlock;	// data block similar to a chunk from which entire template can be reconstructed.
#endif //#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK

#ifdef ASURA_TOOLS
	bool					m_bModified; // Whether the template has been edited in tools
#endif

	// Use fSeed = 1.f to get the maximum lifetime
	inline float GetLifetimeFromSeed(float fSeed) const
	{
		if(m_bRandomLifetime)
		{
			// Remove the first significant figure so that we don't get lifetime synching with other random properties
			const float fSeed10 = fSeed * 10.f;
			return m_fMinLifetime + (fSeed10 - Asura_Maths::FloatToInt_Chop(fSeed10)) * m_fMaxLifetimeDiff;
		}
		else
		{
			return m_fMinLifetime;
		}
	}
	inline float GetMaxLifetime() const
	{
		return m_fMinLifetime + (m_bRandomLifetime ? m_fMaxLifetimeDiff : 0.f);
	}
};

/// Asura_PFX_Source_Template /////////////////////////////////////////////////////////////////////
// Defines how the particles move
///////////////////////////////////////////////////////////////////////////////////////////////////

class Asura_PFX_Source_Template
{
public:

#ifdef ASURA_TOOLS
	// Default constructor
	Asura_PFX_Source_Template()
	: m_xSpeed(16, 1.f, 1.f)
	, m_xAngle(16, Asura_Vector_3(0.f, 0.f, 0.f), Asura_Vector_3(0.f, 0.f, 0.f))
	, m_xSpeedSpread(16, 0.f, 0.f)
	, m_xAngleSpread(16, Asura_Vector_3(0.f, 0.f, 0.f), Asura_Vector_3(0.f, 0.f, 0.f))
	, m_xReleaseRate(16, 10.f, 10.f)
	, m_xReleaseRand(16, 1.f, 1.f)
	, m_xGravityStrength(16, 9.81f, 9.81f)
	, m_xTurbulenceStrength(16, 1.f, 1.f)
	, m_xGravityVector(Asura_Vector_3(0.f, 1.f, 0.f))
	, m_uParticleTemplateHash(0)
	//, m_pxParticleTemplate(NULL)
	, m_fDuration(1.f)
	, m_fAirResistance(0.f)
	, m_fParticleBounce(0.5f)
	, m_fSourceBounce(0.5f)
	, m_fParticleFriction(1.f)
	, m_fTimeScale(1.f)
	, m_fVelocityInheritance(1.f)
	, m_fTurbulenceFrequency(1.f)
	, m_fRangeMin(0.f)
	, m_fRangeMax(100.f)
	, m_fRangeDelayTime(5.f)
	, m_uMaxParticles(32)
	, m_iSpawnSourceIndex(-1)
	, m_uFlags(0)
#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
	, m_uDataBlockSize(0)
	, m_pDataBlock(NULL)
#endif
	, m_bModified(true)
	, m_bMaxParticleWarning(false)
	{
		SetupGraphClamps();
	}
#endif // ASURA_TOOLS

	// Copy constructor
	Asura_PFX_Source_Template(const Asura_PFX_Source_Template& xOriginal)
	: m_xSpeed(xOriginal.m_xSpeed)
	, m_xAngle(xOriginal.m_xAngle)
	, m_xSpeedSpread(xOriginal.m_xSpeedSpread)
	, m_xAngleSpread(xOriginal.m_xAngleSpread)
	, m_xReleaseRate(xOriginal.m_xReleaseRate)
	, m_xReleaseRand(xOriginal.m_xReleaseRand)
	, m_xGravityStrength(xOriginal.m_xGravityStrength)
	, m_xTurbulenceStrength(xOriginal.m_xTurbulenceStrength)
	, m_xGravityVector(xOriginal.m_xGravityVector)
	, m_uParticleTemplateHash(xOriginal.m_uParticleTemplateHash)
	//, m_pxParticleTemplate(xOriginal.m_pxParticleTemplate)
	, m_fDuration(xOriginal.m_fDuration)
	, m_fAirResistance(xOriginal.m_fAirResistance)
	, m_fParticleBounce(xOriginal.m_fParticleBounce)
	, m_fSourceBounce(xOriginal.m_fSourceBounce)
	, m_fParticleFriction(xOriginal.m_fParticleFriction)
	, m_fTimeScale(xOriginal.m_fTimeScale)
	, m_fVelocityInheritance(xOriginal.m_fVelocityInheritance)
	, m_fTurbulenceFrequency(xOriginal.m_fTurbulenceFrequency)
	, m_fRangeMin(xOriginal.m_fRangeMin)
	, m_fRangeMax(xOriginal.m_fRangeMax)
	, m_fRangeDelayTime(xOriginal.m_fRangeDelayTime)
	, m_uMaxParticles(xOriginal.m_uMaxParticles)
	, m_iSpawnSourceIndex(xOriginal.m_iSpawnSourceIndex)
	, m_uFlags(xOriginal.m_uFlags)
#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
	, m_uDataBlockSize(0)
	, m_pDataBlock(NULL)
#endif
#ifdef ASURA_TOOLS
	, m_bModified(true)
	, m_bMaxParticleWarning(false)
#endif
	{
#if !defined(ASURA_TOOLS) && defined(_DEBUG)
		m_xName = xOriginal.m_xName;
		m_xName += "Copy";
#endif
	}

#if defined(ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK) && defined(ASURA_GRAPH_LEAN_AND_MEAN)
	// we have a function to fill out from datablock in this case.
	Asura_PFX_Source_Template() {}
#endif
	// File constructor
	Asura_PFX_Source_Template(Asura_Handle iFileHandle, int iVersion);

#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
	// destructor
	~Asura_PFX_Source_Template()
	{
		Asura_Deallocate( m_pDataBlock );
	}
#endif

	Asura_PFX_Source_Template& operator = (const Asura_PFX_Source_Template& xTemplate)
	{
		if(this != &xTemplate) 
		{
			m_xSpeed = xTemplate.m_xSpeed;
			m_xAngle = xTemplate.m_xAngle;
			m_xSpeedSpread = xTemplate.m_xSpeedSpread;
			m_xAngleSpread = xTemplate.m_xAngleSpread;
			m_xReleaseRate = xTemplate.m_xReleaseRate;
			m_xReleaseRand = xTemplate.m_xReleaseRand;
			m_xGravityStrength = xTemplate.m_xGravityStrength;
			m_xTurbulenceStrength = xTemplate.m_xTurbulenceStrength;
			m_xGravityVector = xTemplate.m_xGravityVector;
			m_uParticleTemplateHash = xTemplate.m_uParticleTemplateHash;
			m_fDuration = xTemplate.m_fDuration;
			m_fAirResistance = xTemplate.m_fAirResistance;
			m_fParticleBounce = xTemplate.m_fParticleBounce;
			m_fSourceBounce = xTemplate.m_fSourceBounce;
			m_fParticleFriction = xTemplate.m_fParticleFriction;
			m_fTimeScale = xTemplate.m_fTimeScale;
			m_fVelocityInheritance = xTemplate.m_fVelocityInheritance;
			m_fTurbulenceFrequency = xTemplate.m_fTurbulenceFrequency;
			m_fRangeMin = xTemplate.m_fRangeMin;
			m_fRangeMax = xTemplate.m_fRangeMax;
			m_fRangeDelayTime = xTemplate.m_fRangeDelayTime;
			m_uMaxParticles = xTemplate.m_uMaxParticles;
			m_iSpawnSourceIndex = xTemplate.m_iSpawnSourceIndex;
			m_uFlags = xTemplate.m_uFlags;
		}
		return *this;
	}

#ifdef ASURA_TOOLS
	void SetupGraphClamps()
	{
		m_xSpeed.SetClampingValues(true, false, 0.0f, Asura_Maths::LargeFloat);
		m_xAngle.SetClampingValues(true, true, -1.0f, 1.0f);
		m_xSpeedSpread.SetClampingValues(true, true, 0.0f, 1.0f);
		m_xAngleSpread.SetClampingValues(true, true, 0.0f, 1.0f);
		m_xReleaseRate.SetClampingValues(true, false, 0.0f, Asura_Maths::LargeFloat);
		m_xReleaseRand.SetClampingValues(true, true, 0.0f, 1.0f);
		m_xGravityStrength.SetClampingValues(false, false, -Asura_Maths::LargeFloat, Asura_Maths::LargeFloat);
		m_xTurbulenceStrength.SetClampingValues(true, false, 0.0f, Asura_Maths::LargeFloat);
	}

	Asura_Hash_ID GetTemplateHash() const;

#endif //ASURA_TOOLS

#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
	// to assist with processing on PS3's SPUs, each template maintains a sort of chunk
	// data block from which the entire template can be reconstructed. This data block
	// is DMA'd in one go and the template is reconstructed at the other end.
	u_int GetDataBlockSize() const { return m_uDataBlockSize; }
	void* GetDataBlock() const { return m_pDataBlock; }
	void CreateDataBlock();	// allocate mem and fill out
#ifdef ASURA_GRAPH_LEAN_AND_MEAN
	void FillInFromDataBlock(const void* const pInputData, u_int uSize);
#endif
#endif // #ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK

	// Members
	Asura_Graph<float>				m_xSpeed; // Speed with which to fire particles  
	Asura_Graph<Asura_Vector_3>		m_xAngle; // Direction in which to fire particles  
	Asura_Graph<float>				m_xSpeedSpread; // Randomness in speed
	Asura_Graph<Asura_Vector_3>		m_xAngleSpread; // Randomness in direction
	Asura_Graph<float>				m_xReleaseRate; // Release rate
	Asura_Graph<float>				m_xReleaseRand; // Release probability
	Asura_Graph<float>				m_xGravityStrength; // Effect of gravity over particle's lifetime
	Asura_Graph<float>				m_xTurbulenceStrength; // Effect of turbulence of particle's lifetime
	// Either a direction or a point relative to the source, according to the flags
	Asura_Vector_3					m_xGravityVector;
	Asura_Hash_ID					m_uParticleTemplateHash;
	//Asura_PFX_Particle_Template*	m_pxParticleTemplate;
	float							m_fDuration;
	float							m_fAirResistance;
	float							m_fParticleBounce;
	float							m_fSourceBounce;
	float							m_fParticleFriction;
	float							m_fTimeScale;
	float							m_fVelocityInheritance;
	float							m_fTurbulenceFrequency;
	float							m_fRangeMin;
	float							m_fRangeMax;
	float							m_fRangeDelayTime;
	u_int							m_uMaxParticles;
	int								m_iSpawnSourceIndex;
	union
	{
		struct 
		{
			// NOTE: These bit's are written out, adding any new bits means
			// ~~~~~ that you will need to add those bits to the bitfield
			//       preparation code in "WriteToChunkStream".
			// Affected by wind?
			unsigned int	m_bUseWind				:1; // Not currently used - may prove useful if we ever get a decent wind system...
			// Use dynamics on particles. This will be against a plane or perhaps
			// collision mesh, not full dynamics, due speed considerations
			unsigned int	m_bUseParticleDynamics	:1;
			// Give the generator itself dynamics.
			// This is intended for doing things like trails, fireballs, etc.  
			unsigned int	m_bUseSourceDynamics	:1; 
			unsigned int	m_bUseTimeScale			:1;
			unsigned int	m_bMovePclesWithSource	:1;
			// Fire all particles at once  
			unsigned int	m_bBurst				:1;
			// Loop source
			unsigned int	m_bLooping				:1; // Not currently used - looping is done at the effect level. This may change.
			// Move with turbulence
			unsigned int	m_bUseTurbulence		:1;
			// Slow down more the bigger the particle is (Air Resistance Proportional To Size)
			unsigned int	m_bARPTS				:1;
			// Fixed air resistance
			unsigned int	m_bFixedAirResistance	:1;
			// Gravity
			unsigned int	m_bDirectionalGravity	:1;
			unsigned int	m_bPointGravity			:1;
			// Inherit velocity from moving effects
			unsigned int	m_bInheritVelocity		:1;
			// Generate particles when camera is in range
			unsigned int	m_bUseRange				:1;
			// Run particle generator for particle's lifetime on first update
			unsigned int	m_bPregenerate			:1; // Deprecated - now in effect flags
			// Scale factor set in code
			unsigned int	m_bUseSpeedScale		:1;
			// Particles in this source should be killed instantly when effect stops
			unsigned int	m_bStopInstantly		:1; 
			// Rotate particles with source (when 'move particles with source' is enabled)
			unsigned int	m_bRotatePclesWithSource:1;
			// Scale the release rate proportional to volume. Given value is for 1m^3. 
			unsigned int	m_bScaleReleaseRateWithVolume:1;
			unsigned int	m_bUseSourceBBForVisibility:1; // 20 flags
		};
		unsigned int		m_uFlags;
	};


#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
	u_int					m_uDataBlockSize;
	void*					m_pDataBlock;	// data block similar to a chunk from which entire template can be reconstructed.
#endif //#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK

#if (defined ASURA_TOOLS || defined _DEBUG)
	Asura_String			m_xName;//name of template , because i'm bored of the effort required to get hold of it. RAR 22/08/2005
#endif
#ifdef ASURA_TOOLS
	bool					m_bModified; // Whether the template has been edited in tools
	bool					m_bMaxParticleWarning;//tracks whether max particle warning has been given for this source yet
#endif // ASURA_TOOLS
};

/// Asura_PFX_Effect_Template /////////////////////////////////////////////////////////////////////
// Defines which source types are used in the effect
///////////////////////////////////////////////////////////////////////////////////////////////////

struct Asura_PFX_Effect_SourceProperties
{
	Asura_Hash_ID		m_uTemplate;
	Asura_Vector_3		m_xOffset;
	Asura_Matrix_3x3	m_xOrient;
	Asura_Vector_3		m_xVelocity;
	float				m_fTimeOffset;
	Asura_PFX_Volume	m_xVolume;
	// If this is zero, behaviour is as normal.
	// Otherwise the orientation can be set using Asura_PFX_Entity_Effect::SetOrientation using the corresponding ID
	int					m_iOrientationID;

	#ifdef ASURA_TOOLS
	bool				m_bDisabled;//For temporarily disabling the source in tools
	#endif

	void SetToDefault()
	{
		m_uTemplate = 0;
		m_xOffset = Asura_Vector_3(0.f, 0.f, 0.f);
		m_xOrient = Asura_Matrix_3x3(1.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f);
		m_xVelocity = Asura_Vector_3(0.f, 0.f, 0.f);
		m_fTimeOffset = 0.f;
		m_iOrientationID = 0;
		#ifdef ASURA_TOOLS
		m_bDisabled = false;
		#endif
	}
};


//for platform specific overrides for pfx effects
struct Asura_PFX_Effect_PlatformOverride
{
	int m_iPlatform;//uses Tools_Level_Export platform enumeration
	Asura_Hash_ID m_uEffect;//replacement effect to use
};

class Asura_PFX_Effect_Template
{
public:

#ifdef ASURA_TOOLS
	// Default constructor
	Asura_PFX_Effect_Template()
	: m_uNumSources(1)
	, m_xPhotonColour(16, Asura_RGBA(1.f, .8f, .6f, .4f), Asura_RGBA(.5f, .4f, .3f, .2f))
	, m_xPhotonRadius(16, 10.f, 0.f)
	, m_iPhotonPerParticleSource(-1)
	, m_fLifeTime(10.f)
	, m_uSoundEvent(0)
	, m_uFlags(0)
	, m_uNumInstances(0)
	, m_iIsolatedSource(-1)
	, m_bModified(true)
	, m_bDoneAutosave(false)
	, m_bDoneBackup(false)
	, m_uLastBackupTime(0)
	, m_uNumPlatformOverrides(0)
	, m_pxPlatformOverrides(0)
	{
		Asura_ResourcePushType(ASURA_RESOURCE_PFX);
		{
			Asura_ResourceAllocateArray(m_pxSources, m_uNumSources);
			for(u_int u = 0; u < m_uNumSources; ++u)
			{
				m_pxSources[u].SetToDefault();
			}
			SetupGraphClamps();
		}
		Asura_ResourcePopType();
	}
#endif // ASURA_TOOLS

	// Copy constructor
	Asura_PFX_Effect_Template(const Asura_PFX_Effect_Template& xOriginal)
	: m_uNumSources(xOriginal.m_uNumSources)
	, m_xPhotonColour(xOriginal.m_xPhotonColour)
	, m_xPhotonRadius(xOriginal.m_xPhotonRadius)
	, m_iPhotonPerParticleSource(xOriginal.m_iPhotonPerParticleSource)
	, m_fLifeTime(xOriginal.m_fLifeTime)
	, m_uSoundEvent(xOriginal.m_uSoundEvent)
	, m_uFlags(xOriginal.m_uFlags)
	, m_uNumInstances(0)
#ifdef ASURA_TOOLS
	, m_iIsolatedSource(-1)
	, m_bModified(true)
	, m_bDoneAutosave(false)
	, m_bDoneBackup(false)
	, m_uLastBackupTime(0)
	, m_uNumPlatformOverrides(xOriginal.m_uNumPlatformOverrides)
	, m_xName(xOriginal.m_xName)
#endif // ASURA_TOOLS
	{
		Asura_ResourcePushType(ASURA_RESOURCE_PFX);
		{
			Asura_ResourceAllocateArray(m_pxSources, m_uNumSources);
			for(u_int u = 0; u < m_uNumSources; ++u)
			{
				CopySourceData(u, &xOriginal, u);
			}

#ifdef ASURA_TOOLS
			Asura_ResourceAllocateArray(m_pxPlatformOverrides, m_uNumPlatformOverrides);
			for(u_int u=0;u<m_uNumPlatformOverrides;u++)
			{
				m_pxPlatformOverrides[u] = xOriginal.m_pxPlatformOverrides[u];
			}
#endif // ASURA_TOOLS
#if !defined(ASURA_TOOLS) && defined(_DEBUG)
			m_xName = xOriginal.m_xName;
			m_xName += "Copy";
#endif
		}
		Asura_ResourcePopType();
	}

	// File constructor
	Asura_PFX_Effect_Template(Asura_Handle iFileHandle, int iVersion);

	~Asura_PFX_Effect_Template()
	{
		Asura_ResourcePushType(ASURA_RESOURCE_PFX);
		{
			Asura_ResourceDeallocateArray(m_pxSources);

#ifdef ASURA_TOOLS
			Asura_ResourceDeallocateArray(m_pxPlatformOverrides);
#endif
		}
		Asura_ResourcePopType();
	}

#ifdef ASURA_TOOLS
	Asura_PFX_Effect_Template& operator = (const Asura_PFX_Effect_Template& xTemplate)
	{
		if(this != &xTemplate) 
		{
			Asura_ResourcePushType(ASURA_RESOURCE_PFX);
			{
				m_uNumSources = xTemplate.m_uNumSources;
				m_xPhotonColour = xTemplate.m_xPhotonColour;
				m_xPhotonRadius = xTemplate.m_xPhotonRadius;
				m_iPhotonPerParticleSource = xTemplate.m_iPhotonPerParticleSource;
				m_fLifeTime = xTemplate.m_fLifeTime;
				m_uFlags = xTemplate.m_uFlags;

				Asura_ResourceDeallocateArray(m_pxSources);
				Asura_ResourceAllocateArray(m_pxSources, m_uNumSources);
				for(u_int u = 0; u < m_uNumSources; ++u)
				{
					CopySourceData(u, &xTemplate, u);
				}

				Asura_ResourceDeallocateArray(m_pxPlatformOverrides);
				m_uNumPlatformOverrides = xTemplate.m_uNumPlatformOverrides;

				Asura_ResourceAllocateArray(m_pxPlatformOverrides, m_uNumPlatformOverrides);
				for(u_int u=0;u<m_uNumPlatformOverrides;u++)
				{
					m_pxPlatformOverrides[u] = xTemplate.m_pxPlatformOverrides[u];
				}

				m_xName = xTemplate.m_xName;
			}
			Asura_ResourcePopType();
		}
		return *this;
	}

	void SetupGraphClamps()
	{
		Asura_Assert(m_xPhotonColour.GetControlPoints(), "Photon colour graph has no control points. Should have some by now!");
		if (m_xPhotonColour.GetControlPoints())
		{
			m_xPhotonColour.SetClampingValues(true, true, 0.0f, fASURA_PFX_MAX_PHOTON_COLOUR);
		}
		Asura_Assert(m_xPhotonRadius.GetControlPoints(), "Photon radius graph has no control points. Should have some by now!");
		if (m_xPhotonRadius.GetControlPoints())
		{
			m_xPhotonRadius.SetClampingValues(true, false, 0.0f, Asura_Maths::LargeFloat);
		}
	}

	//Gets the platfrom override template if needed , or returns this if there isn't any
	const Asura_PFX_Effect_Template* GetTemplateForPlatform(int iPlatform) const;
	Asura_PFX_Effect_Template* GetTemplateForPlatform(int iPlatform);

	Asura_Hash_ID GetTemplateHash() const;
#endif // ASURA_TOOLS

	void CopySourceData(u_int uTargetIndex, const Asura_PFX_Effect_Template *pxSourceTemplate, u_int uSourceIndex)
	{
		m_pxSources[uTargetIndex] = pxSourceTemplate->m_pxSources[uSourceIndex];
	}

	// accessors for instance count.
	u_int GetNumInstances() const { return m_uNumInstances; }
	void IncrementNumInstances() { ++m_uNumInstances; }
	void DecrementNumInstances() { --m_uNumInstances; }


	u_int GetNumOfSources() const	{	return m_uNumSources;	}
	Asura_Hash_ID GetSourceHash( u_int uSourceNum ) const
	{
		return uSourceNum<GetNumOfSources() ? m_pxSources[uSourceNum].m_uTemplate : ASURA_HASH_ID_UNSET;
	}

	#ifdef ASURA_TOOLS
	//Get/set modified flag.
	//This will also check the source and particle templates
	bool IsModified() const;
	void SetModified(bool bModified);
	#endif //ASURA_TOOLS

#ifndef ASURA_GRAPH_LEAN_AND_MEAN
    static void ColouriseEffectTemplate( Asura_PFX_Effect_Template* pxTemplate, const Asura_RGBA& xColour );
#endif

	// Members
	// Source properties
	unsigned int				m_uNumSources;  
	
	Asura_PFX_Effect_SourceProperties* m_pxSources;
	// Photon properties
	Asura_Graph<Asura_RGBA>		m_xPhotonColour; // Photon colour over time. Alpha component ignored  
	Asura_Graph<float>			m_xPhotonRadius;
	int							m_iPhotonPerParticleSource;//for limiting photon per particle to a single source

	float						m_fLifeTime;
	Asura_Hash_ID				m_uSoundEvent;
	union
	{
		struct 
		{
			// NOTE: These bit's are written out, adding any new bits means
			// ~~~~~ that you will need to add those bits to the bitfield
			//       preparation code in "WriteToChunkStream".
			unsigned int		m_bHasPhoton			:1;
			unsigned int		m_bLooping				:1;
			unsigned int		m_bOrderedRender		:1;
			unsigned int		m_bMoveWithEffect		:1;
			unsigned int		m_bAlwaysExport			:1;//Obsolete
			unsigned int		m_bHasSound				:1;//Obsolete (Indicates prescence of old sound system sounds. Will be false if there is a sound event instead.)
			// Run all sources on first update, looping if necessary
			unsigned int		m_bPregenerate			:1;
			unsigned int		m_bSoundLoops			:1;//Ignored if using sound events
			unsigned int		m_bParticlePhotons		:1;
			unsigned int		m_bAlwaysOnTop			:1;
			unsigned int		m_bUseSoundRadiusAndVolume :1;
			unsigned int		m_bForceRenderOnMainThread :1;
			unsigned int		m_bHudEffect			:1; // Rendered after FSFX but stil with depth testing with world
			unsigned int		m_uUnused				: 11; // padding to make the project flags sit at the end of the bitfield
			unsigned int		m_bProjectFlag0			: 1; // data reserved for project specific use
			unsigned int		m_bProjectFlag1			: 1; // data reserved for project specific use
			unsigned int		m_bProjectFlag2			: 1; // data reserved for project specific use
			unsigned int		m_bProjectFlag3			: 1; // data reserved for project specific use
			unsigned int		m_bProjectFlag4			: 1; // data reserved for project specific use
			unsigned int		m_bProjectFlag5			: 1; // data reserved for project specific use
			unsigned int		m_bProjectFlag6			: 1; // data reserved for project specific use
			unsigned int		m_bProjectFlag7			: 1; // data reserved for project specific use
		};
		unsigned int			m_uFlags;
	};
	
	unsigned int				m_uNumInstances;	// num of Asura_PFX_Entity_Effect instances using this template.
	
#ifdef ASURA_TOOLS
	int							m_iIsolatedSource;
	bool						m_bModified; // Whether the template has been edited in tools
	bool						m_bDoneAutosave;//true if an autosave has been done for this template since last being modified
	bool						m_bDoneBackup;//true if at least one backup has been done of this file
	u_int						m_uLastBackupTime;//time of last backup using windows tick counter

	u_int 						m_uNumPlatformOverrides;
	Asura_PFX_Effect_PlatformOverride* m_pxPlatformOverrides;
#endif // ASURA_TOOLS

	// SNH: keep this around in debug too, so we can track down problems a bit easier.
#if (defined ASURA_TOOLS || defined _DEBUG)
	Asura_String				m_xName;//name of template , because i'm bored of the effort required to get hold of it. RAR 04/04/2005
#endif
#ifdef ASURA_TOOLS
	Asura_String				m_xOriginalNameForDeletion;//Original name gets set after renaming an effect , and is used for deleting the original file once the renamed version is sucessfully saved
#endif
};



#endif//__ASURA_PFX_TEMPLATES_H__
