
//
// Asura_Platform_PFX.cpp
//
// Created by KOR - 16/07/2004
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//
#include "Core/Asura.h"


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

// Asura headers
#include "Core/Asura_HalfFloat.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_Viewport.h"
#include "Rendering/Asura_Render.h"
#include "Light/Asura_GI_Solution.h"
#include "SpecialFX/PFX/Asura_PFX_Classes.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "SpecialFX/PFX/Asura_PFX_Templates.h"

#include "Rendering/Asura_Xbox360_Graphics.h"
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_TextureManagement.h"
#include "Rendering/Asura_Xbox360_Effect_System.h"
#include "Rendering/Asura_Xbox360_TextureManagement.h"
#include "Rendering/Asura_Xbox360_Fog.h"
#include "Debugging/Asura_Platform_GPUProfile.h"

#ifdef ASURA_USE_FASTPATH_PFX
#include "SpecialFX/Asura_Platform_PFX_System_FastPath.h"
#endif

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

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

#ifndef ASURA_PFX_SYSTEM_SUSPICIOUS_POSITION_THRESHOLD
    // The position beyond which a particle position is considered to be suspicious.
    #define	ASURA_PFX_SYSTEM_SUSPICIOUS_POSITION_THRESHOLD  ( 6000.0f )
#endif 

struct PFXVertex
{
	float X, Y, Z, CrossFadeBlendWeight;
	Asura_HalfFloat R,G,B,A;
	Asura_HalfFloat U, V;
	Asura_HalfFloat U2, V2;
};

enum PFXTechnique
{
	PFXTechnique_Standard,
	PFXTechnique_HeatHaze,

	Max_PFXTechniques
};

enum PFXPass
{
	PFXPass_SingleTexture,
	PFXPass_DualTexture,

	Max_PFXPasses
};


// Decls/Effects/Handles for the old (slow path) PFX renderer
// ------------------------------------------------------------------------------------------
const Direct3D_VertexElement axVERTEX_DECLARATION[] =
{
	{ 0,  0, D3DDECLTYPE_FLOAT4,    D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	{ 0, 16, D3DDECLTYPE_FLOAT16_4,  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    0 },
	{ 0, 24, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
	{ 0, 28, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
	D3DDECL_END()
};

static Direct3D_VertexDeclaration* g_pxPFXVertexDecl = NULL;

static Asura_Xbox360_Effect* g_pxPFXEffect = NULL;

static Asura_Xbox360_Effect_System::Handle g_xTextureHandle = 0;
static Asura_Xbox360_Effect_System::Handle g_xZReadEnableHandle = 0;
static Asura_Xbox360_FogEffectParameters   g_xFogEffectParameters;

static Asura_Xbox360_Effect_System::Handle g_xOneOverScreenDimensionsHandle = 0;
static Asura_Xbox360_Effect_System::Handle g_xHeatHazeCaptureTextureHandle = 0;

static void Platform_Initialise();
static void Platform_Shutdown();


void Asura_PFX_System::Platform_Initialise( void )
{
#ifdef ASURA_USE_FASTPATH_PFX
	Asura_Platform_PFX_System_FastPath::Initialise();
#endif

	#if defined ASURA_ASSERT
		const HRESULT xResult =
	#endif
		Asura_Xbox360_Graphics::GetDevice()->CreateVertexDeclaration( axVERTEX_DECLARATION, &g_pxPFXVertexDecl );

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

	g_pxPFXEffect = Asura_Xbox360_Effect_System::CreateEffectFromMemory(g_auPFX_Temp);

	g_xTextureHandle = g_pxPFXEffect->GetParameterHandle( "g_xTexture" );
	g_xZReadEnableHandle = g_pxPFXEffect->GetParameterHandle( "g_bZReadEnable" );	

	g_xOneOverScreenDimensionsHandle = g_pxPFXEffect->GetParameterHandle( "g_xOneOverScreenDimensions" );	
	g_xHeatHazeCaptureTextureHandle  = g_pxPFXEffect->GetParameterHandle( "g_xHeatHazeCaptureTexture" );	

	g_xFogEffectParameters.Capture(g_pxPFXEffect);
}

void Asura_PFX_System::Platform_Reset( void )
{

}

void Asura_PFX_System::Platform_Shutdown( void )
{
#ifdef ASURA_USE_FASTPATH_PFX
	Asura_Platform_PFX_System_FastPath::Shutdown();
#endif
	g_pxPFXVertexDecl->Release();
	g_pxPFXVertexDecl = NULL;

	Asura_Xbox360_Effect_System::Destroy( g_pxPFXEffect );
	g_pxPFXEffect = NULL;
}

void Asura_PFX_System::Platform_BeginRender()
{
	#if defined(ASURA_XBOX360_ENABLE_PIX) && defined(USE_PIX)
		const char* const szHeatHazeString = (s_bIsRenderingHeatHazeSources ? " Heat Haze" : "");
	#endif

	switch(Asura_PFX_System::s_eCurrentRenderType)
	{
		case ASURA_PFX_RENDER_HUD:
		{
			PIXBeginNamedEvent(0xff885577, "PFX Render (HUD%s)", szHeatHazeString);
			break;
		}

		case ASURA_PFX_RENDER_SKYBOX:
		{
			PIXBeginNamedEvent(0xff885577, "PFX Render (Skybox%s)", szHeatHazeString);
			break;
		}

		case ASURA_PFX_RENDER_PROJECT:
		{
			PIXBeginNamedEvent(0xff885577, "PFX Render (Project%s)", szHeatHazeString);
			break;
		}

		case ASURA_PFX_RENDER_WATER_DISTORTER:
		{
			PIXBeginNamedEvent(0xff885577, "PFX Render (Water Distorter%s)", szHeatHazeString);
			break;
		}

		default:
		{
			PIXBeginNamedEvent(0xff885577, "PFX Render%s", szHeatHazeString);
			break;
		}
	}		// End of switch.

	if(Asura_PFX_System::s_eCurrentRenderType == ASURA_PFX_RENDER_SKYBOX)
	{
		Asura_Xbox360_Graphics::SetSkyboxDepthBias();
	}

	if( s_bIsRenderingHeatHazeSources )
	{
		// Assume that the backbuffer capture is dirty - we just rendered a bunch of particles to it
		Asura_Xbox360_Graphics::Resolve( Asura_Xbox360_Graphics::GetSceneResolveTexture() );
		Asura_Render::AllowFullScreenRendering();
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
	}

	// Resolve above has its own GPU profile section
	ASURA_GPU_PROFILE_START(ASURA_GPUPROFILE_PFX);
}

void Asura_PFX_System::Platform_EndRender()
{
	if(Asura_PFX_System::s_eCurrentRenderType == ASURA_PFX_RENDER_SKYBOX)
	{
		Asura_Xbox360_Graphics::ResetDepthBias();
	}

	PIXEndNamedEvent();
	ASURA_GPU_PROFILE_END(ASURA_GPUPROFILE_PFX);
}

bool Asura_PFX_System::Platform_SupportsHeatHaze()
{
	return true;
}




// DH ( 20:Feb:2006 )
// This change calls "DrawPrimitiveUP" for a batch of particles rather than one call per particle.
// This can improve particle performance by 20-40% if many particles are being rendered.
//
// Numerous buffer sizes were tested and it was found the a list just 16*6 ( 96) verts was the 
// most optimal, due to "DrawPrimitiveUP" internal workings.
//
// DMR 15-11-06 - The above comment was copied from the PC version? Trying bigger batches for 360 for now...

static int const iParticleVertsNumOrig = 128;
static PFXVertex xParticleVertsOrig[ iParticleVertsNumOrig*4 ];


// Old PFX renderer (at least with some batch now). No hardware instancing, and everything
// done on the CPU - but supports everything, including the motion blur, 3d rotation, etc
// ------------------------------------------------------------------------------------------
void Asura_PFX_Effect::Platform_RenderParticles( const Asura_PFX_Particle* pxParticles,
                                                 u_int uNumParticles,
                                                 const Asura_PFX_Particle_Template* pxTemplate,
                                                 const Asura_RGBA& xLightColour
#ifdef ASURA_SUPPORT_MULTITHREADED_JOB_QUEUE
												 ,void* pPlatformRenderData
#endif
												) const
{
	// It is valid to pass in zero particles (source could just have started or ended. Simply return)
	if(!uNumParticles) return;


	Asura_Assert(pxParticles, "Null particle pointer given");
	Asura_Assert(pxTemplate, "Null particle template pointer given");
	if(!pxTemplate || !pxParticles) return;



#ifdef ASURA_USE_FASTPATH_PFX

	#ifdef ASURA_SUPPORT_MULTITHREADED_JOB_QUEUE
		if (pPlatformRenderData == NULL)
		{			
			pPlatformRenderData = Asura_Platform_PFX_System_FastPath::GetInstanceBufferArea(uNumParticles);			

			if (pPlatformRenderData)
			{
				Asura_Platform_PFX_System_FastPath::UpdateInstanceBuffer(pxTemplate, pxParticles, (Asura_Platform_PFXInstanceData *)pPlatformRenderData, uNumParticles);				
			}
		}
	#endif

	// Can we use the fast path renderer? 	
	// DMR - 02/08/07 - Skybox particles forced through the slow path, as they're rendered early, before we're finished with MRT rendering
		if  (!pxTemplate->m_bUseMotionBlur &&
			!pxTemplate->m_b3DRotation &&
			!pxTemplate->m_bSpriteRotation && // not that these are implemented on 360 yet but they probably don't want to be fast path if they are
			!(m_usPFXFlags & ASURA_PFX_EFFECT_SKYBOX) &&
			!(m_usPFXFlags & ASURA_PFX_EFFECT_FORCE_RENDER_ON_MAIN_THREAD ) &&
			!(pxTemplate->m_eTranslucency == ASURA_TRANSLUCENCY_SPECIAL_HEATHAZE) &&
			pPlatformRenderData)
	{
		Asura_Platform_PFX_System_FastPath::RenderParticles(this,
															   pxParticles,
															   uNumParticles,
															   pxTemplate,
															   xLightColour,
															   m_usPFXFlags,
															   m_ucCameraFlags,
															   pPlatformRenderData);
		return;	
	}
#endif

	const bool			bHeatHaze	= Asura_PFX_System::IsRenderingHeatHazeSources();
	const PFXTechnique	eTechnique	= (bHeatHaze) ? PFXTechnique_HeatHaze : PFXTechnique_Standard;
	const PFXPass		ePass		= (pxTemplate->m_bCrossfadeTextureFrames) ? PFXPass_DualTexture : PFXPass_SingleTexture;

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

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

	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)
	{
		g_pxPFXEffect->GetInterface()->SetSampler( g_xTextureHandle, pxTexture->GetD3DTexture() );
	}

	if (bHeatHaze)
	{	
		FXLVECTOR xVec;
		xVec.x = 1.0f / static_cast<float>(Asura_Viewport::GetWidth());
		xVec.y = 1.0f / static_cast<float>(Asura_Viewport::GetHeight());
		g_pxPFXEffect->SetVector(  g_xOneOverScreenDimensionsHandle, xVec);
		g_pxPFXEffect->SetTexture( g_xHeatHazeCaptureTextureHandle, Asura_Xbox360_Graphics::GetSceneResolveTexture());
	}


	const bool bAlwaysOnTop = (m_usPFXFlags & ASURA_PFX_EFFECT_ALWAYS_ON_TOP) != 0;
	g_pxPFXEffect->GetInterface()->SetBool( g_xZReadEnableHandle, !bAlwaysOnTop );

	// Set the fog parameters
	if (pxTemplate->m_eTranslucency== ASURA_TRANSLUCENCY_SPECIAL_HEATHAZE)
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
	}
	else
	{
		g_xFogEffectParameters.Apply(pxTemplate->m_bUseFogging, pxTemplate->m_eTranslucency);	 
		Asura_Render::CurrentStates.RequestTranslucencyMode(pxTemplate->m_eTranslucency);
	}	


	Asura_Vector_3 axVertices[4];

	for( int iCam=0; iCam<Asura_Camera::GetNumberOfCameras(); ++iCam )
	{
		if(!(m_ucCameraFlags & (1<<iCam))) continue;
		Asura_Camera::SetCameraIndex( iCam );
		const	Asura_Vector_3*		pxCameraPos		= Asura_Camera::GetPosition();
				PFXVertex*			pxParticleVerts;
				u_int				uParticlesLeft	= uNumParticles;
				u_int				uParticlesToRender;
				u_int				uParticleCount;

		while ( uParticlesLeft )
		{
			uParticleCount = uParticlesLeft;
			
			if ( uParticleCount > iParticleVertsNumOrig )
			{
				uParticleCount	= iParticleVertsNumOrig;
			}

			uParticlesToRender	= uParticleCount;
			pxParticleVerts		= &xParticleVertsOrig[0];

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

				if ( m_usPFXFlags & ASURA_PFX_EFFECT_SKYBOX ) xPosition += *pxCameraPos;
            
				Asura_RGBA xColour = pxTemplate->m_xColour.GetValueAtTime_Discrete(pxParticle->m_fAge);
	            if(pxTemplate->m_bUseLighting)
				{
					xColour.m_fR *= xLightColour.m_fR;
					xColour.m_fG *= xLightColour.m_fG;
					xColour.m_fB *= xLightColour.m_fB;
				}
	

				if(pxTemplate->m_bNearCameraFade || pxTemplate->m_bFarCameraFade)
				{
					float fDist = (*pxCameraPos - xPosition).Magnitude();
						//fade when near camera
					if(fDist < pxTemplate->m_fNearFadeDistance && pxTemplate->m_bNearCameraFade)
					{
						if( (pxTemplate->m_fNearFadeDistance - pxTemplate->m_fNearZeroDistance) == 0.0f )
						{
							 xColour.m_fA = 0.f;
						}
						else
						{
							xColour.m_fA *= (fDist - pxTemplate->m_fNearZeroDistance) / (pxTemplate->m_fNearFadeDistance - pxTemplate->m_fNearZeroDistance);
						}						
					}
					//fade when far from camera
					if(fDist > pxTemplate->m_fFarFadeDistance && pxTemplate->m_bFarCameraFade)
					{
						if( (pxTemplate->m_fFarFadeDistance - pxTemplate->m_fFarZeroDistance) == 0.0f ) 
						{
							xColour.m_fA = 0.f;
						}
						else
						{
							xColour.m_fA *= (fDist - pxTemplate->m_fFarZeroDistance) / (pxTemplate->m_fFarFadeDistance - pxTemplate->m_fFarZeroDistance);
						}						
					}
				}
				// KOR - 15/11/2005 - early out on zero alpha
				if(xColour.m_fA <= 0.f)
				{
					--uParticlesToRender;
					continue;
				}

				float fSize = pxTemplate->m_xSize.GetValueAtTime_Discrete(pxParticle->m_fAge);
				if( pxTemplate->m_bUseSizeScale )
				{
					fSize *= GetSizeScale();
				}

				fSize *= (1.f - pxParticle->m_fSeed * pxTemplate->m_xSizeVariation.GetValueAtTime_Discrete(pxParticle->m_fAge));
				const float fAgeInSeconds = pxParticle->m_fAge * pxTemplate->GetLifetimeFromSeed(pxParticle->m_fSeed);
				const float fLength = Asura_Maths::Min(pxTemplate->m_fBlurLength, fAgeInSeconds);
				const Asura_Vector_3 xPrevPosition = xPosition - (pxParticle->m_xVelocity * fLength);

				Asura_Vector_3 xDeltaX;
				Asura_Vector_3 xDeltaY;

				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 if(pxTemplate->m_b3DRotation || !pxTemplate->m_bUseMotionBlur)
				{
					// Need a rotation
					if(pxTemplate->m_bUseRandomRotation)
					{
						fTheta = ((Asura_Timers::GetGameTimeScale() == 0.0f) ? 0.0f : Asura_Random::GetFloat()) * Asura_Maths::Pi * 2.0f;
					}
					else
					{
		#ifdef ASURA_TOOLS
						fTheta = (pxParticle->m_fSeed + pxTemplate->m_xRotation.GetAreaUnderGraph(pxParticle->m_fAge)) * Asura_Maths::Pi * 2.f;
		#else
						fTheta = (pxParticle->m_fSeed + pxTemplate->m_xRotation.GetValueAtTime_Discrete(pxParticle->m_fAge)) * Asura_Maths::Pi * 2.f;
		#endif
					}
				
					if(pxTemplate->m_bMirrorRotation)
					{
						const int* piRandom = reinterpret_cast<const int*>(&pxParticle->m_fSeed);
						if(*piRandom % 2)
						{
							fTheta = -fTheta;
						}
					}
				}

				const Asura_Matrix_3x3* pxOrientation = GetOrientation();
				if(pxTemplate->m_b3DRotation)
				{
					// KOR - 9/9/2004 - not optimal, and possibly not the way we want to do 3D rotation
					// (intended for effects such as glass shards or scattering debris). Need artist feedback to improve.
					const Asura_Euler_3 xEuler(fTheta, fTheta, 0.f);
					Asura_Matrix_3x3 xMatrix;
					xEuler.ToMatrix(xMatrix);
					xDeltaX = Asura_Vector_3(xMatrix.e11, xMatrix.e21, xMatrix.e31) * fSize;
					xDeltaY = Asura_Vector_3(xMatrix.e12, xMatrix.e22, xMatrix.e32) * fSize;
				}
				else if(pxTemplate->m_bAlignEffectX && pxOrientation)
				{
					xDeltaX = Asura_Vector_3(pxOrientation->e12, pxOrientation->e22, pxOrientation->e32) * fSize;
					xDeltaY = Asura_Vector_3(pxOrientation->e13, pxOrientation->e23, pxOrientation->e33) * fSize;
				}
				else if(pxTemplate->m_bAlignEffectY && pxOrientation)
				{
					xDeltaX = Asura_Vector_3(pxOrientation->e11, pxOrientation->e21, pxOrientation->e31) * fSize;
					xDeltaY = Asura_Vector_3(pxOrientation->e13, pxOrientation->e23, pxOrientation->e33) * fSize;
				}
				else if(pxTemplate->m_bAlignEffectZ && pxOrientation)
				{
					xDeltaX = Asura_Vector_3(pxOrientation->e11, pxOrientation->e21, pxOrientation->e31) * fSize;
					xDeltaY = Asura_Vector_3(pxOrientation->e12, pxOrientation->e22, pxOrientation->e32) * fSize;
				}
				else
				{
					const Asura_Matrix_3x3* pxCameraOrientation = Asura_Camera::GetOrientation();
					xDeltaX = Asura_Vector_3(pxCameraOrientation->e11,pxCameraOrientation->e21,pxCameraOrientation->e31) * fSize;
					xDeltaY = Asura_Vector_3(pxCameraOrientation->e12,pxCameraOrientation->e22,pxCameraOrientation->e32) * fSize;
				}

                Asura_Assert( Asura_Maths::Abs( xPosition.x ) < ASURA_PFX_SYSTEM_SUSPICIOUS_POSITION_THRESHOLD,
                    "Asura_PFX_Effect::Platform_RenderParticles - possible particle position problem" );
                Asura_Assert( Asura_Maths::Abs( xPosition.y ) < ASURA_PFX_SYSTEM_SUSPICIOUS_POSITION_THRESHOLD,
                    "Asura_PFX_Effect::Platform_RenderParticles - possible particle position problem" );
                Asura_Assert( Asura_Maths::Abs( xPosition.z ) < ASURA_PFX_SYSTEM_SUSPICIOUS_POSITION_THRESHOLD,
                    "Asura_PFX_Effect::Platform_RenderParticles - possible particle position problem" );


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

				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;
					xDeltaX *= fAspect;
				}

				/// Kevin Lea - 27/03/2002 11:43:15 - code to draw particle stretched along its path
				/// ucky and needs rewriting
				if (pxTemplate->m_bUseMotionBlur)
				{
					Asura_Vector_3 xBeta;

					xBeta = xPosition - xPrevPosition;
					if( xBeta.MagnitudeSquared() > (0.1f*0.1f) )
					{
						xBeta.Normalise();
						xBeta*=0.1f;
					}
					
                    Asura_Assert( Asura_Maths::Abs( xPrevPosition.x ) < ASURA_PFX_SYSTEM_SUSPICIOUS_POSITION_THRESHOLD,
                        "Asura_PFX_Effect::Platform_RenderParticles - possible particle previous position problem" );
                    Asura_Assert( Asura_Maths::Abs( xPrevPosition.y ) < ASURA_PFX_SYSTEM_SUSPICIOUS_POSITION_THRESHOLD,
                        "Asura_PFX_Effect::Platform_RenderParticles - possible particle previous position problem\n" );
                    Asura_Assert( Asura_Maths::Abs( xPrevPosition.z ) < ASURA_PFX_SYSTEM_SUSPICIOUS_POSITION_THRESHOLD,
                        "Asura_PFX_Effect::Platform_RenderParticles - possible particle previous position problem\n" );

					if (Asura_Maths::Abs(xBeta * xDeltaX) > Asura_Maths::Abs(xBeta*xDeltaY))
					{
						if (xBeta * xDeltaX > 0)
						{
							axVertices[0] = xPrevPosition	- xBeta - xDeltaX - xDeltaY; 
							axVertices[1] = xPosition		+ xBeta + xDeltaX - xDeltaY;
							axVertices[2] = xPosition		+ xBeta + xDeltaX + xDeltaY;
							axVertices[3] = xPrevPosition	- xBeta - xDeltaX + xDeltaY;
						}
						else
						{
							axVertices[0] = xPosition		+ xBeta - xDeltaX - xDeltaY; 
							axVertices[1] = xPrevPosition	- xBeta + xDeltaX - xDeltaY;
							axVertices[2] = xPrevPosition	- xBeta + xDeltaX + xDeltaY;
							axVertices[3] = xPosition		+ xBeta - xDeltaX + xDeltaY;
						}
					}
					else
					{
						if (xBeta * xDeltaY > 0)
						{
							axVertices[0] = xPrevPosition	- xBeta - xDeltaX - xDeltaY; 
							axVertices[1] = xPrevPosition	- xBeta + xDeltaX - xDeltaY;
							axVertices[2] = xPosition		+ xBeta + xDeltaX + xDeltaY;
							axVertices[3] = xPosition		+ xBeta - xDeltaX + xDeltaY;
						}
						else
						{
							axVertices[0] = xPosition		+ xBeta - xDeltaX - xDeltaY; 
							axVertices[1] = xPosition		+ xBeta + xDeltaX - xDeltaY;
							axVertices[2] = xPrevPosition	- xBeta + xDeltaX + xDeltaY;
							axVertices[3] = xPrevPosition	- xBeta - xDeltaX + xDeltaY;
						}
					}
				}
				else 
				{
					if (fTheta!=0.f)
					{
						const float fSin = Asura_Maths::Sine(fTheta);
						const float fCos = Asura_Maths::Cosine(fTheta);
			
						Asura_Vector_3 xCosDeltaX = xDeltaX * fCos;
						Asura_Vector_3 xSinDeltaX = xDeltaX * fSin;
						Asura_Vector_3 xCosDeltaY = xDeltaY * fCos;
						Asura_Vector_3 xSinDeltaY = xDeltaY * fSin;
					
						axVertices[0] = xPosition + (-xCosDeltaX + xSinDeltaX) + (-xSinDeltaY - xCosDeltaY);		
						axVertices[1] = xPosition + ( xCosDeltaX + xSinDeltaX) + ( xSinDeltaY - xCosDeltaY);
						axVertices[2] = xPosition + ( xCosDeltaX - xSinDeltaX) + ( xSinDeltaY + xCosDeltaY);
						axVertices[3] = xPosition + (-xCosDeltaX - xSinDeltaX) + (-xSinDeltaY + xCosDeltaY);
					}
					else
					{
						axVertices[0] = xPosition - xDeltaX - xDeltaY; 
						axVertices[1] = xPosition + xDeltaX - xDeltaY;
						axVertices[2] = xPosition + xDeltaX + xDeltaY;
						axVertices[3] = xPosition - xDeltaX + xDeltaY;
					}
				}


				{		
					// are we changing texture orient?
					u_int uTexOrient = 0;
					if(pxTemplate->m_bRandomTextureOrient)
					{
						uTexOrient = Asura_Maths::FloatToInt_Chop(pxParticle->m_fSeed * 4.f);
					}

				

					switch(uTexOrient)
					{
						case 0:
						case 1:
							// Normal							
							pxParticleVerts[0].U = pxParticleVerts[3].U = pxTextureFrame->m_afTextureUVs[0];
							pxParticleVerts[1].U = pxParticleVerts[2].U	= pxTextureFrame->m_afTextureUVs[2];								

							if (pxTextureFrame2)
							{
								pxParticleVerts[0].U2 = pxParticleVerts[3].U2	= pxTextureFrame2->m_afTextureUVs[0];
								pxParticleVerts[1].U2 = pxParticleVerts[2].U2 = pxTextureFrame2->m_afTextureUVs[2];								
							}							
							break;
						case 2:
						case 3:
						default:
							// Mirrored
							pxParticleVerts[1].U = pxParticleVerts[2].U = pxTextureFrame->m_afTextureUVs[0];								
							pxParticleVerts[0].U = pxParticleVerts[3].U = pxTextureFrame->m_afTextureUVs[2];

							if (pxTextureFrame2)
							{
								pxParticleVerts[0].U2 = pxParticleVerts[3].U2 = pxTextureFrame2->m_afTextureUVs[2];
								pxParticleVerts[1].U2 = pxParticleVerts[2].U2 = pxTextureFrame2->m_afTextureUVs[0];								
							}
							
							break;
					}
					switch(uTexOrient)
					{
						case 0:
						case 3:
							pxParticleVerts[0].V = pxParticleVerts[1].V = pxTextureFrame->m_afTextureUVs[1];
							pxParticleVerts[2].V = pxParticleVerts[3].V = pxTextureFrame->m_afTextureUVs[3];

							if (pxTextureFrame2)
							{
								pxParticleVerts[0].V2 = pxParticleVerts[1].V2 = pxTextureFrame2->m_afTextureUVs[1];
								pxParticleVerts[2].V2 = pxParticleVerts[3].V2 = pxTextureFrame2->m_afTextureUVs[3];
							}
							
							break;
						case 1:
						case 2:
						default:
							pxParticleVerts[0].V = pxParticleVerts[1].V = pxTextureFrame->m_afTextureUVs[3];
							pxParticleVerts[2].V = pxParticleVerts[3].V = pxTextureFrame->m_afTextureUVs[1];

							// Mirrored
							if (pxTextureFrame2)
							{
								pxParticleVerts[0].V2 = pxParticleVerts[1].V2 = pxTextureFrame2->m_afTextureUVs[3];
								pxParticleVerts[2].V2 = pxParticleVerts[3].V2 = pxTextureFrame2->m_afTextureUVs[1];
							}

							break;
					}

					for(int i=0;i<4;i++)
					{					
						pxParticleVerts[i].X	   = axVertices[i].x;
						pxParticleVerts[i].Y	   = axVertices[i].y;
						pxParticleVerts[i].Z	   = axVertices[i].z;
						pxParticleVerts[i].R = xColour.m_fR;
						pxParticleVerts[i].G = xColour.m_fG;
						pxParticleVerts[i].B = xColour.m_fB;
						pxParticleVerts[i].A = xColour.m_fA;
						pxParticleVerts[i].CrossFadeBlendWeight = fCrossFadeBlendWeight;
					}

					pxParticleVerts += 4;
				}
			}

			if ( uParticlesToRender )
			{
				g_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);
					}
					g_pxPFXEffect->Commit();
					Asura_Xbox360_Graphics::GetDevice()->DrawPrimitiveUP(D3DPT_QUADLIST, uParticlesToRender, (LPVOID)xParticleVertsOrig, sizeof(PFXVertex) );
				}
				g_pxPFXEffect->End();
				Asura_Render::AddToParticlesRendered( uParticlesToRender );								
			}
			pxParticles		+= uParticleCount;
			uParticlesLeft	-= uParticleCount;
		}
	}
	if (Asura_Xbox360_Graphics::IsTiling2())
	{
		Asura_Xbox360_Graphics::GetDevice()->SetPredication(0);
		Asura_Xbox360_Graphics::CorrectVPOSForTiling(0);
	}

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






#ifdef ASURA_SUPPORT_MULTITHREADED_JOB_QUEUE

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_PFX_System::Platform_BeginUpdate
// Author       : Dave Reed
// Date         : 14-Nov-2006
//
// Description  : Prepares vertex buffers for updating during Update.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////
void Asura_PFX_System::Platform_BeginUpdate (void)
{
#ifdef ASURA_USE_FASTPATH_PFX
	Asura_Platform_PFX_System_FastPath::UpdateInstanceBuffersForNewFrame();
#endif
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_PFX_System::Platform_GetRenderData
// Author       : Dave Reed
// Date         : 14-Nov-2006
//
// Description  : returns area of vertex buffer big enough for given number of 
//                particles - for multithrefaded updates.
//
//			      Returns NULL if we're not doing this via the job queue 
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////
void* Asura_PFX_System::Platform_GetRenderData(u_int uNumParticles)
{
#ifdef ASURA_USE_FASTPATH_PFX
	// Todo - Don't call this with slow-path effects - this is wasting buffer space at the moment!
	if (Asura_Platform_PFX_System_FastPath::IsEnabled())
	{	
		return Asura_Platform_PFX_System_FastPath::GetInstanceBufferArea(uNumParticles);
	}
#endif	

	return NULL;
	
}

#endif // ASURA_SUPPORT_MULTITHREADED_JOB_QUEUE



/// End of file. ///

