
//
// Asura_Platform_PFX_FastPath.cpp
//
// Created by DMR 15/11/06
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
// Faster renderer for most PFX types, using hardware instancing
//

#include "Core/Asura.h"


#ifdef ASURA_USE_FASTPATH_PFX

// This file's header.
#include "SpecialFX/Asura_Platform_PFX_System_FastPath.h"

// Asura platform-independent headers
#include "Camera/Asura_Camera.h"
#include "Core/Asura_HalfFloat.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"
#include "Interface/Asura_CommandConsole.h"
#include "Light/Asura_GI_Solution.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"
#include "SpecialFX/PFX/Asura_PFX_Classes.h"
#include "SpecialFX/PFX/Asura_PFX_Effect.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "SpecialFX/PFX/Asura_PFX_Templates.h"

// Asura Xbox 360 headers
#include "Debugging/Asura_Platform_GPUProfile.h"
#include "Rendering/Asura_Xbox360_Graphics.h"
#include "Rendering/Asura_Xbox360_Effect_System.h"
#include "Rendering/Asura_Xbox360_TextureManagement.h"
#include "Rendering/Asura_Xbox360_Fog.h"

// Shader Include Files
#include "PFX_FastPath.h"

// Misc
#include <fxl.h>
#include <xtl.h>

bool								 Asura_Platform_PFX_System_FastPath::s_bIsEnabled = true;
Direct3D_VertexDeclaration*		     Asura_Platform_PFX_System_FastPath::s_pxPFXVertexDecl = NULL;
Asura_Xbox360_Effect*			     Asura_Platform_PFX_System_FastPath::s_pxPFXEffect = NULL;

Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xTextureHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xDepthTextureHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xFirstInstanceHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xZReadEnableHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xXDeltaHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xYDeltaHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xLightColourHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xVolumetricDepthScaleHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xPosOffsetAndSizeScaleHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xNearFarFadeHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xTwoOverScreenWidthHandle = 0;
Asura_Xbox360_Effect_System::Handle  Asura_Platform_PFX_System_FastPath::s_xTwoOverScreenHeightHandle = 0;

static Asura_Xbox360_FogEffectParameters g_xFogEffectParameters;

Asura_Xbox360_VertexBuffer			 Asura_Platform_PFX_System_FastPath::s_axVertexBuffers[VERTEXBUFFER_COUNT];
int									 Asura_Platform_PFX_System_FastPath::s_iCurrentVertexBuffer = 0;
uint32							 	 Asura_Platform_PFX_System_FastPath::s_uFastPathParticlesRenderedThisFrame;
bool								 Asura_Platform_PFX_System_FastPath::s_bVertexBufferLocked = false;
bool								 Asura_Platform_PFX_System_FastPath::s_bVertexBufferOverflow = false;
int								 	 Asura_Platform_PFX_System_FastPath::s_iCurrentVertexBufferPosition = 0;
Asura_Platform_PFXInstanceData	    *Asura_Platform_PFX_System_FastPath::s_pxCurrentVertexBuffer=0;


// Decls/Effects/Handles for the new (fast path) instanced PFX renderer
// ------------------------------------------------------------------------------------------

// This is for the geometry data. Just 4 verts that will be instanced.
//
// This vertex format looks bloated - but the cost of converting floats->ints and floats->halfs 
// was very severe - at the moment, this seems to give better results.
//
const Direct3D_VertexElement axINSTANCED_VERTEX_DECLARATION[] =
{
	// INSTANCE STREAM
	{ 0,  0, D3DDECLTYPE_FLOAT4,    D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },	// Position (Orientation in W)
	{ 0, 16, D3DDECLTYPE_FLOAT4,    D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },	// UVset 0 (U0,V0,U1,V1)
	{ 0, 32, D3DDECLTYPE_FLOAT4,	D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },  // UVset 1 (U0,V0,U1,V1)
	{ 0, 48, D3DDECLTYPE_FLOAT2,	D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 },	// Size
	{ 0, 56, D3DDECLTYPE_FLOAT2,	D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3 },	// TextureAlignment and CrossfadeBlendWeight
	{ 0, 64, D3DDECLTYPE_FLOAT4,    D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    0 },	// Colour+Alpha (without near-far fadeout)

	D3DDECL_END()
};

//////////////////////////////////////////////////////////////////////////
// Local enumerated types...
//////////////////////////////////////////////////////////////////////////

enum PFXTechnique
{
	PFXTechnique_Point,
	PFXTechnique_Volumetric,

	Max_PFXTechniques
};

enum PFXPass
{
	PFXPass_SingleTexture,
	PFXPass_DualTexture,

	Max_PFXPasses
};


void Asura_Platform_PFX_System_FastPath::Initialise( void )
{
#if defined ASURA_ASSERT
	const HRESULT xResult =
#endif
		Asura_Xbox360_Graphics::GetDevice()->CreateVertexDeclaration( axINSTANCED_VERTEX_DECLARATION, &s_pxPFXVertexDecl );

	Asura_Assert( SUCCEEDED( xResult ) && s_pxPFXVertexDecl, "Failed to create PFX (fast path) vertex declaration." );

	s_pxPFXEffect = Asura_Xbox360_Effect_System::CreateEffectFromMemory(g_auPFX_FastPath);
	s_xTextureHandle				= s_pxPFXEffect->GetParameterHandle( "g_xTexture" );
	s_xDepthTextureHandle			= s_pxPFXEffect->GetParameterHandle( "g_xDepthTexture" );
	s_xFirstInstanceHandle			= s_pxPFXEffect->GetParameterHandle( "g_fFirstInstance" );
	s_xZReadEnableHandle			= s_pxPFXEffect->GetParameterHandle( "g_bZReadEnable" );	
	s_xXDeltaHandle					= s_pxPFXEffect->GetParameterHandle( "g_xXDelta" );
	s_xYDeltaHandle					= s_pxPFXEffect->GetParameterHandle( "g_xYDelta" );
	s_xNearFarFadeHandle			= s_pxPFXEffect->GetParameterHandle( "g_xNearFarFade" );
	s_xLightColourHandle			= s_pxPFXEffect->GetParameterHandle( "g_xLightColour" );
	s_xVolumetricDepthScaleHandle	= s_pxPFXEffect->GetParameterHandle( "g_fVolumetricDepthScale" );
	s_xPosOffsetAndSizeScaleHandle  = s_pxPFXEffect->GetParameterHandle( "g_xPosOffsetAndSizeScale" );
	s_xNearFarFadeHandle			= s_pxPFXEffect->GetParameterHandle( "g_xNearFarFade" );
	s_xTwoOverScreenWidthHandle		= s_pxPFXEffect->GetParameterHandle( "g_fTwoOverScreenWidth" );
	s_xTwoOverScreenHeightHandle	= s_pxPFXEffect->GetParameterHandle( "g_fTwoOverScreenHeight" );

	g_xFogEffectParameters.Capture(s_pxPFXEffect);

	s_pxPFXEffect->GetInterface()->SetSampler(s_xDepthTextureHandle,Asura_Xbox360_Graphics::GetDepthMtlTexture());

	for(int i=0;i<VERTEXBUFFER_COUNT;i++)
	{
		s_axVertexBuffers[i].CreateStatic(sizeof(Asura_Platform_PFXInstanceData), VERTEXBUFFER_SIZE);
	}	
 
	s_iCurrentVertexBuffer			= 0;
	s_bVertexBufferLocked			= false;

#if defined ASURA_SUPPORT_COMMAND_CONSOLE
	Asura_CommandConsole::AddVar( L"Render.PFX.EnableFastPath", &s_bIsEnabled,
		Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif
}



void Asura_Platform_PFX_System_FastPath::Shutdown( void )
{
	s_pxPFXVertexDecl->Release();
	s_pxPFXVertexDecl = NULL;

	Asura_Xbox360_Effect_System::Destroy( s_pxPFXEffect );
	s_pxPFXEffect = NULL;

	for(int i=0;i<VERTEXBUFFER_COUNT;i++)
	{	
		s_axVertexBuffers[i].Destroy();
	}
}




void Asura_Platform_PFX_System_FastPath::UpdateInstanceBuffersForNewFrame()
{
	if (s_bVertexBufferLocked)
	{
		s_bVertexBufferLocked=false;
		s_axVertexBuffers[s_iCurrentVertexBuffer].Unlock();		
	}
	
	s_iCurrentVertexBuffer++;

	if (s_iCurrentVertexBuffer>=VERTEXBUFFER_COUNT)
	{
		s_iCurrentVertexBuffer=0;
	}

	{
		// Obtain device ownership
		SCOPED_DEVICE_OWNERSHIP();
		Asura_Xbox360_Graphics::GetDevice()->SetStreamSource(0,NULL,0,0);
	}

	s_iCurrentVertexBufferPosition = 0;
	s_pxCurrentVertexBuffer = (Asura_Platform_PFXInstanceData *)s_axVertexBuffers[s_iCurrentVertexBuffer].Lock();	
	s_bVertexBufferLocked=true;

	s_uFastPathParticlesRenderedThisFrame = 0;
	s_bVertexBufferOverflow = false;
}

void *Asura_Platform_PFX_System_FastPath::GetInstanceBufferArea(uint32 uNumParticles)
{
	if (s_iCurrentVertexBufferPosition+uNumParticles<VERTEXBUFFER_SIZE)
	{
		int iPos = s_iCurrentVertexBufferPosition;
		s_iCurrentVertexBufferPosition+=uNumParticles;
		return (void *)&s_pxCurrentVertexBuffer[iPos];
	}
	else
	{		
		s_bVertexBufferOverflow = true;
	}

	return NULL;
}



void Asura_Platform_PFX_System_FastPath::RenderParticles(				
	const  Asura_PFX_Effect *pxEffect,
	const  Asura_PFX_Particle* pxParticles,
	u_int  uNumParticles,
	const  Asura_PFX_Particle_Template* pxTemplate,
	const  Asura_RGBA& xLightColour,	
	const  uint16 usPFXFlags,		
	const  uint16 usCameraFlags, 
	void* pPlatformRenderData
	)
{
	const PFXTechnique	eTechnique	= (pxTemplate->m_fVolumetricDepthScale > 0.0f) ? PFXTechnique_Volumetric : PFXTechnique_Point;
	const PFXPass		ePass		= (pxTemplate->m_bCrossfadeTextureFrames) ? PFXPass_DualTexture : PFXPass_SingleTexture;

	// (Calling function has just checked that pxTemplate,pxParticles are valid

	if (s_bVertexBufferLocked)
	{
		s_bVertexBufferLocked=false;
		s_axVertexBuffers[s_iCurrentVertexBuffer].Unlock();		
	}		


	s_axVertexBuffers[s_iCurrentVertexBuffer].Bind(0);
	
	Asura_Platform_PFXInstanceData *pxCurrentPos = (Asura_Platform_PFXInstanceData *)pPlatformRenderData;

	uint32 uFirstInstance = pxCurrentPos-s_pxCurrentVertexBuffer;
	s_pxPFXEffect->GetInterface()->SetFloat(s_xFirstInstanceHandle,(float)uFirstInstance);
	

	// Set the blending mode
	if ( usPFXFlags & ASURA_PFX_EFFECT_WATER_DISTORTER )
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);		
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA);
	}
	else
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(pxTemplate->m_eTranslucency);
	}


	// Set the vertex decl. Todo: Avoid redundant changes of this
	Asura_Xbox360_Graphics::GetDevice()->SetVertexDeclaration( s_pxPFXVertexDecl );

	// SHADER CONSTANT SETUP - Could be further optimized by batching these
	// ---------------------------------------------------------------------------------
	Asura_Xbox360_Texture* const pxTexture = Asura_Xbox360_TextureManagement::GetTexture( pxTemplate->m_iTextureHandle );
	Asura_Assert( NULL != pxTexture, "Particle Texture not found in texture Asura_Xbox360_TextureManagement");

	if( NULL != pxTexture)
	{
		s_pxPFXEffect->GetInterface()->SetSampler( s_xTextureHandle, pxTexture->GetD3DTexture() );
	}

	bool bAlwaysOnTop = (usPFXFlags & ASURA_PFX_EFFECT_ALWAYS_ON_TOP) != 0;

	FXLEffect *pxFXLEffect = s_pxPFXEffect->GetInterface();

	
	pxFXLEffect ->SetBool( s_xZReadEnableHandle, !bAlwaysOnTop );

	if(pxTemplate->m_bUseLighting)
	{
		pxFXLEffect->SetVectorF(s_xLightColourHandle,(float *)&xLightColour);
	}
	else
	{
		float afOne[4] = {1.0f,1.0f,1.0f,1.0f};
		pxFXLEffect->SetVectorF(s_xLightColourHandle,afOne);
	}	
	
	pxFXLEffect->SetFloat(s_xVolumetricDepthScaleHandle,pxTemplate->m_fVolumetricDepthScale);	
	pxFXLEffect->SetFloat(s_xTwoOverScreenWidthHandle,  (2.0f / Asura_Viewport::GetWidth()));
	pxFXLEffect->SetFloat(s_xTwoOverScreenHeightHandle, (2.0f / Asura_Viewport::GetHeight()));

	// Set the fog parameters
	g_xFogEffectParameters.Apply(pxTemplate->m_bUseFogging, pxTemplate->m_eTranslucency );	 

	Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HIGHPRECISIONBLENDENABLE, TRUE );

	for( int iCam=0; iCam<Asura_Camera::GetNumberOfCameras(); ++iCam )
	{
		if(!(usCameraFlags & (1<<iCam))) continue;
		Asura_Camera::SetCameraIndex( iCam );		

		// Cull if all particles are faded out.
		// A bit late really, as we've already done the update and VB filling...
		if (pxTemplate->m_bFarCameraFade)
		{	
			Asura_Vector_3 xCentre = pxEffect->GetBoundingBox().CalculateCentre();
			Asura_Vector_3 xCam = *Asura_Camera::GetPosition();			
			xCentre = xCentre - xCam; 
			float fR = pxEffect->GetBoundingRadius();

			if (xCentre.MagnitudeSquared() > 
				(pxTemplate->m_fFarZeroDistance+fR)*(pxTemplate->m_fFarZeroDistance+fR))
			{			
				// Effect is completely faded out in it's current position
				continue;
			}						
		}


		// Camera-specific shader constants
		// ------------------------------------------------------------------------------------------

		float axPositionOffsetAndSizeScale[4];
		if ( usPFXFlags & ASURA_PFX_EFFECT_SKYBOX )
		{
			const	Asura_Vector_3 *pxCameraPos	= Asura_Camera::GetPosition();
			axPositionOffsetAndSizeScale[0] = pxCameraPos->x;
			axPositionOffsetAndSizeScale[1] = pxCameraPos->y;
			axPositionOffsetAndSizeScale[2] = pxCameraPos->z;
		}
		else
		{
			axPositionOffsetAndSizeScale[0] =
			axPositionOffsetAndSizeScale[1] =
			axPositionOffsetAndSizeScale[2] = 0.0f;
		}

		if( pxEffect && pxTemplate->m_bUseSizeScale )
		{		
			axPositionOffsetAndSizeScale[3] = pxEffect->GetSizeScale();
		}
		else
		{
			axPositionOffsetAndSizeScale[3] = 1.0f;
		}

		pxFXLEffect->SetVectorF(s_xPosOffsetAndSizeScaleHandle, (float *)axPositionOffsetAndSizeScale);

		float axNearFarFade[4];
		if(pxTemplate->m_bNearCameraFade || pxTemplate->m_bFarCameraFade)
		{
			axNearFarFade[0] = pxTemplate->m_fNearFadeDistance;
			axNearFarFade[1] = pxTemplate->m_fNearZeroDistance;
			axNearFarFade[2] = pxTemplate->m_fFarFadeDistance;
			axNearFarFade[3] = pxTemplate->m_fFarZeroDistance;
		}
		else
		{
			axNearFarFade[0] = -10000.0f;
			axNearFarFade[1] = -10001.0f;
			axNearFarFade[2] = 10000.0f;
			axNearFarFade[3] = 10001.0f;
		}
		
		pxFXLEffect->SetVectorF(s_xNearFarFadeHandle, (float *)axNearFarFade);


		Asura_Vector_3		xDeltaX;
		Asura_Vector_3		xDeltaY;
		const Asura_Matrix_3x3* pxOrientation = pxEffect->GetOrientation();

		if(pxTemplate->m_bAlignEffectX && pxOrientation)
		{
			xDeltaX = Asura_Vector_3(pxOrientation->e12, pxOrientation->e22, pxOrientation->e32);
			xDeltaY = Asura_Vector_3(pxOrientation->e13, pxOrientation->e23, pxOrientation->e33);
		}
		else if(pxTemplate->m_bAlignEffectY && pxOrientation)
		{
			xDeltaX = Asura_Vector_3(pxOrientation->e11, pxOrientation->e21, pxOrientation->e31);
			xDeltaY = Asura_Vector_3(pxOrientation->e13, pxOrientation->e23, pxOrientation->e33);
		}
		else if(pxTemplate->m_bAlignEffectZ && pxOrientation)
		{
			xDeltaX = Asura_Vector_3(pxOrientation->e11, pxOrientation->e21, pxOrientation->e31);
			xDeltaY = Asura_Vector_3(pxOrientation->e12, pxOrientation->e22, pxOrientation->e32);
		}
		else if(pxTemplate->m_bBillboardJustY && pxOrientation)
		{
			const Asura_Matrix_3x3* pxCameraOrientation = Asura_Camera::GetOrientation();
			xDeltaX = Asura_Vector_3(pxCameraOrientation->e11,pxCameraOrientation->e21,pxCameraOrientation->e31);
			xDeltaY = Asura_Vector_3(pxOrientation->e13, pxOrientation->e23, pxOrientation->e33);
		}
		else if(pxTemplate->m_bBillboardJustX && pxOrientation)
		{
			const Asura_Matrix_3x3* pxCameraOrientation = Asura_Camera::GetOrientation();
			xDeltaX = Asura_Vector_3(pxOrientation->e12, pxOrientation->e22, pxOrientation->e32);
			xDeltaY = Asura_Vector_3(pxCameraOrientation->e12,pxCameraOrientation->e22,pxCameraOrientation->e32);
		}
		else
		{
			// Normal particles - screen-aligned
			const Asura_Matrix_3x3* pxCameraOrientation = Asura_Camera::GetOrientation();
			xDeltaX = Asura_Vector_3(pxCameraOrientation->e11,pxCameraOrientation->e21,pxCameraOrientation->e31);
			xDeltaY = Asura_Vector_3(pxCameraOrientation->e12,pxCameraOrientation->e22,pxCameraOrientation->e32);
		}

		pxFXLEffect->SetVectorF(s_xXDeltaHandle,(float *)&xDeltaX);
		pxFXLEffect->SetVectorF(s_xYDeltaHandle,(float *)&xDeltaY);

		// Render it	
		s_pxPFXEffect->Begin(eTechnique, ePass);
		for (int iTiling = 0; iTiling < Asura_Xbox360_Graphics::GetNumberOfPredicatedTilePasses(); ++iTiling)
		{
			if (Asura_Xbox360_Graphics::IsTiling2())
			{
				Asura_Xbox360_Graphics::GetDevice()->SetPredication(D3DPRED_TILE(iTiling));
				Asura_Xbox360_Graphics::CorrectVPOSForTiling(iTiling);
			}
			s_pxPFXEffect->Commit();		
			Asura_Xbox360_Graphics::GetDevice()->DrawVertices(D3DPT_QUADLIST, 0, 4*uNumParticles);
		}
		s_pxPFXEffect->End();
		Asura_Render::AddToParticlesRendered( uNumParticles );		
		s_uFastPathParticlesRenderedThisFrame += uNumParticles;
		

	}  // for each camera

	if (Asura_Xbox360_Graphics::IsTiling2())
	{
		Asura_Xbox360_Graphics::GetDevice()->SetPredication(0);
		Asura_Xbox360_Graphics::CorrectVPOSForTiling(0);
	}


	Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);				

	if ( usPFXFlags & ASURA_PFX_EFFECT_WATER_DISTORTER )
	{		
		Asura_Xbox360_Graphics::GetDevice()->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA);
	}

	Asura_Xbox360_Graphics::GetDevice()->SetRenderState_Inline( D3DRS_HIGHPRECISIONBLENDENABLE, TRUE );
}





// UpdateInstanceBuffer - This has been separated out of the render call, as it can be 
// run multithreaded
// ------------------------------------------------------------------------------------------				

void Asura_Platform_PFX_System_FastPath::UpdateInstanceBuffer(const Asura_PFX_Particle_Template *pxTemplate, 
															  const Asura_PFX_Particle			*pxParticles, 
															  Asura_Platform_PFXInstanceData	*pxParticleInstanceData, 
															  uint32 uNumParticles)
{		
	SCOPEDPIXEVENT((0xff8800,  "Asura_Platform_PFX_System_FastPath::UpdateInstanceBuffer"));



	for(u_int uParticle = 0 ; uParticle < uNumParticles ; ++uParticle)
	{
		const	Asura_PFX_Particle*	pxParticle = &pxParticles[uParticle];
		Asura_Vector_3		xPosition	= pxParticle->m_xPosition;

		Asura_RGBA xColour = pxTemplate->m_xColour.GetValueAtTime_Discrete(pxParticle->m_fAge);			

		float fSize = pxTemplate->m_xSize.GetValueAtTime_Discrete(pxParticle->m_fAge);

		fSize *= (1.f - pxParticle->m_fSeed * pxTemplate->m_xSizeVariation.GetValueAtTime_Discrete(pxParticle->m_fAge));


		// All of this lot just calculates fTheta - the rotation angle
		float fTheta = 0.0f;
		if(pxTemplate->m_bFixedRotation)
		{
			fTheta = Asura_Maths::DegToRad(pxTemplate->m_fFixedRotation);
			if(pxTemplate->m_uNumFixedRotations > 1)
			{
				int iRotation = Asura_Maths::FloatToInt_Chop(pxParticle->m_fSeed * pxTemplate->m_uNumFixedRotations);
				fTheta += (iRotation * Asura_Maths::Pi * 2.f) / pxTemplate->m_uNumFixedRotations;
			}
		}
		else 
		{
			// Need a rotation
			if(pxTemplate->m_bUseRandomRotation)
			{
				fTheta = ((Asura_Timers::GetGameTimeScale() == 0.0f) ? 0.0f : Asura_Random::GetFloat()) * Asura_Maths::Pi * 2.0f;
			}
			else
			{
				fTheta = (pxParticle->m_fSeed + pxTemplate->m_xRotation.GetValueAtTime_Discrete(pxParticle->m_fAge)) * Asura_Maths::Pi * 2.f;
			}

			if(pxTemplate->m_bMirrorRotation)
			{
				const int* piRandom = reinterpret_cast<const int*>(&pxParticle->m_fSeed);
				if(*piRandom & 1)
				{
					fTheta = -fTheta;
				}
			}
		}



		// are we changing texture orient?
		u_int uTexOrient = 0;
		float fCrossFadeBlendWeight = 0.0f;

		if(pxTemplate->m_bRandomTextureOrient)
		{
			uTexOrient = Asura_Maths::FloatToInt_Chop(pxParticle->m_fSeed * 4.f);
		}

		// Get the texture frame(s) for this particle
		const Asura_PFX_TextureFrame* pxTextureFrame;
		const Asura_PFX_TextureFrame* pxTextureFrame2;
		if (pxTemplate->m_bCrossfadeTextureFrames)
		{
			pxTemplate->GetTextureFramesForCrossfadeAtTime(pxParticle->m_fAge, pxTextureFrame, pxTextureFrame2, fCrossFadeBlendWeight);
		}
		else
		{
			fCrossFadeBlendWeight = 0.0f;
			pxTextureFrame = pxTemplate->GetTextureFrameAtTime(pxParticle->m_fAge);
			pxTextureFrame2 = pxTextureFrame; 
		}


		pxParticleInstanceData->X = xPosition.x;
		pxParticleInstanceData->Y = xPosition.y;
		pxParticleInstanceData->Z = xPosition.z;
		pxParticleInstanceData->Orientation = fTheta;
		pxParticleInstanceData->TextureAlignment = (float)uTexOrient;

		pxParticleInstanceData->Width  = fSize;
		pxParticleInstanceData->Height = fSize;

		if ( pxTemplate->m_bUseUVAsAspect )
		{
			const float fDU = pxTextureFrame->m_afTextureUVs[ 2 ] - pxTextureFrame->m_afTextureUVs[ 0 ];
			const float fDV = pxTextureFrame->m_afTextureUVs[ 3 ] - pxTextureFrame->m_afTextureUVs[ 1 ];
			const float fAspect = ( fDV ) ? fDU / fDV : 1.0f;
			pxParticleInstanceData->Width *= fAspect;
		}

		for(int i=0;i<4;i++)
		{				
			pxParticleInstanceData->UVSet0[i] = pxTextureFrame->m_afTextureUVs[i];
			pxParticleInstanceData->UVSet1[i] = pxTextureFrame2->m_afTextureUVs[i];
		}

		pxParticleInstanceData->CrossFadeBlendWeight = fCrossFadeBlendWeight;
		pxParticleInstanceData->Diffuse[0] = xColour.m_fR;
		pxParticleInstanceData->Diffuse[1] = xColour.m_fG;
		pxParticleInstanceData->Diffuse[2] = xColour.m_fB;
		pxParticleInstanceData->Diffuse[3] = xColour.m_fA;
		pxParticleInstanceData++;				
	}
}


#endif // ASURA_USE_FASTPATH_PFX

