//*******************************************************************
//
// File: Asura_PS2_Carpet_Mesh_Store.h
//
// Created: 06 March 2006 by Jake Turner
//
// Description: 
// PS2 specific capret mesh storage class
// Manager class for PS2 carpet mesh data
//
//*******************************************************************

#include "Core/Asura.h"

#if defined( ASURA_USE_CARPET )

#include "Carpet/Asura_PS2_Carpet_Mesh_Store.h"

#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Interface/Asura_CommandConsole.h"
#include "Rendering/Asura_Colour.h"

// Platform includes.
#include "Carpet/Asura_PS2_Carpet_Mesh_Shared.h"
#include "Core/Asura_PS2_DMA.h"
//#include "Debugging/Asura_DebugText.h"
#include "Profiling/Asura_PS2_Profiling.h"
#include "Rendering/Asura_Module.h"
#include "Rendering/Asura_PS2_VU1.h"
#include "Rendering/Asura_PS2_GS.h"
#include "Rendering/Asura_PlatformRendering.h"
#include "Rendering/Asura_PlatformRenderStates.h"
#include "Rendering/Asura_PlatformTextureManagement.h"
#include "Rendering/Asura_PS2_DeferredRendering.h"
#include "Rendering/Asura_PS2_CarpetMeshDefines.h"
#include "Rendering/Asura_PS2_RenderingOptions.h"

// See  "PS2/Carpet/Asura_PS2_Carpet_Mesh_Shared.h" for details of the file format
// General.
#define g_uMeshFlags         (*reinterpret_cast<u_int*       >(0x70000000))
#define g_uPrevMeshFlags     (*reinterpret_cast<u_int*       >(0x70000004)) // Mesh flags from previous GSMSendPendingData().
#define g_uPendingFlags      (*reinterpret_cast<u_int*       >(0x70000008))

// Camera.
#define g_uCamVisFlags       (*reinterpret_cast<u_int*       >(0x70000010))
#define g_uCamFlagsForVU     (*reinterpret_cast<u_int*       >(0x70000014))
#define g_uNumCamsVisFor     (*reinterpret_cast<u_int*       >(0x70000018))
#define g_bO2WIsIdentity     (*reinterpret_cast<bool*        >(0x7000001C))
#define g_uCurVUCamBfr       (*reinterpret_cast<u_int*       >(0x70000020))

// Texture.
#define g_bTexUploaded       (*reinterpret_cast<bool*        >(0x70000024))
#define g_iCurBaseTex        (*reinterpret_cast<int*         >(0x70000028))
#define g_uCurVUStyleBfr     (*reinterpret_cast<u_int*       >(0x7000002C))
#define g_pxCurStyle         (*reinterpret_cast<const Asura_PS2_Carpet_Style_DMA_Data**> (0x70000030))

// Renderstate
#define g_uCurVURSBfr        (*reinterpret_cast<u_int*       >(0x70000214))
#define g_fGlobalFade		 (*reinterpret_cast<float*       >(0x70000218))
#define g_fGlobalViewScale	 (*reinterpret_cast<float*       >(0x7000021C))
#define g_pxRSBfr            ( reinterpret_cast<Asura_PS2_QW*>(0x70000220)) // Up to 5 QWs of renderstate.
#define g_pxSTOvParams       ( reinterpret_cast<Asura_PS2_QW*>(0x700002C0))

#define g_pxModulePosAndSeed ( reinterpret_cast<Asura_PS2_QW*>(0x700002F0)) // 1 QW

// Lighting.
#define g_pxOutLightBfr      ( reinterpret_cast<Asura_PS2_QW*>(0x700003B0)) // 2 QWs per light for up to 7 lights 
#define g_uNumLightQWs       (*reinterpret_cast<u_int*       >(0x700004D0))
#define g_uCurVULightBfr     (*reinterpret_cast<u_int*       >(0x700004D4))

// Camera data used during rendering process
#define g_pxCamPos			 ( reinterpret_cast<Asura_Vector_3*>(0x70000500)) // assumes 4 cameras (12*4 bytes = 48 = 0x30)
#define g_pxCamFwdVector	 ( reinterpret_cast<Asura_Vector_3*>(0x70000530)) // assumes 4 cameras (12*4 bytes = 48 = 0x30)
#define g_pxCamHorizVector   ( reinterpret_cast<Asura_Vector_3*>(0x70000560)) // assumes 4 cameras (12*4 bytes = 48 = 0x30)
#define g_pxCamVertVector    ( reinterpret_cast<Asura_Vector_3*>(0x70000590)) // assumes 4 cameras (12*4 bytes = 48 = 0x30)
#define g_pfCamHorizScale    ( reinterpret_cast<float*         >(0x700005C0)) // assumes 4 cameras (4*4 bytes = 16 = 0x10)
#define g_pfCamVertScale     ( reinterpret_cast<float*         >(0x700005D0)) // assumes 4 cameras (4*4 bytes = 16 = 0x10)
#define g_pfWaveX			 ( reinterpret_cast<float*         >(0x700005E0)) // 16-floats (16*4 bytes = 64 = 0x40)

// next available address 0x70000620+uNumModules
#define g_puVisAndClipFlags  ( reinterpret_cast<u_int*       >(0x70000620))

// Total.
#define TOTAL_SPR_QWS_REQD                                    (0x0000062)

// Special case for light count. Only required for point lighting.
#define g_uNumLights ((g_pxOutLightBfr + ASURA_PS2_CARPET_MESH__OFFSET__QW_CONTAINING_QTY)->u32s[3])

#define MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT(puExtern, uNumMSCALs) (++(uNumMSCALs), ASURA_PS2_MAKE_MSCAL_FROM_VU_PTR(puExtern))

static inline void FillQWWithNOPs( Asura_PS2_QW* pxOut )
{
	pxOut->u128 = 0;
}

// T Y P E S /////////////////////////////////////////////////////////////////

enum PENDING
{
	PENDING_UPLOAD_TEX_SYNC            =  1 <<  0,
	PENDING_UPLOAD_RENDERSTATE         =  1 <<  1,
	PENDING_UPLOAD_CAM                 =  1 <<  2,
	PENDING_UPLOAD_LIGHT               =  1 <<  3,
	PENDING_UPLOAD_MESH_FLAGS          =  1 <<  4,
	PENDING_UPLOAD_CAM_FLAGS           =  1 <<  5,
	PENDING_UPLOAD_STYLE               =  1 <<  6,
    PENDING_UPLOAD_MODULE_POS_AND_SEED =  1 <<  7,
};


////////////////////////////////////////////////////////////////////////////
//
// Global variables
//
////////////////////////////////////////////////////////////////////////////

static Asura_PS2_Carpet_Mesh_Header g_xHeader;
static void* g_pvStyleAndModuleHdrs;
static void* g_pvModuleDMAChains;
static Asura_PS2_Carpet_Style_DMA_Data* g_pxStyles;

// These point into some of the above; they are not allocated.
static const Asura_PS2_Carpet_Mesh_Style_Header*  g_pxStyleHdrs;
static const Asura_PS2_Carpet_Mesh_Module_Header* g_pxModuleHdrs;

extern u_int VU1_CARPET_MESH_INIT[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_BEGIN_STRIP[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_BEGIN_STRIP_BREAK[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_SWITCH_RENDERSTATE_BFR[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_LOAD_SEED_FOR_NEW_MODULE[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_SWITCH_STYLE_DATA_BFR[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_SWITCH_CAM_DATA_BFR[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_SWITCH_LIGHT_DATA_BFR[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_LOAD_MESH_FLAGS[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_LOAD_CAM_FLAGS[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_BEGIN_STRIP_BREAK[] __attribute__(( section(".vudata") ));
extern u_int VU1_CARPET_MESH_NOP[] __attribute__(( section(".vudata") ));

static float g_fCarpetMaxDist = 1000.0f;

static float g_fCarpetViewHorizScale = 1.0f;

bool bCarpetBotanicalsDebugTexReal = false;
bool bCarpetBotanicalsDebugTex = false;

DVSVAR bool cnsl_bUseCarpetMipMapBias = true;
DVSVAR u_int cnsl_uCarpetMipMapBias = 6;

#define DEBUG_PS2_CARPET

#if defined( DEBUG_PS2_CARPET )

DEBUGVAR float dbg_fCarpetX0 = -666.0f;
DEBUGVAR float dbg_fCarpetX1 = -666.0f;

DEBUGVAR float dbg_fCarpetX2 = -666.0f;
DEBUGVAR float dbg_fCarpetX3 = -666.0f;

DEBUGVAR bool dbg_bCarpetMeshFillSPRInBegin = false;
DEBUGVAR bool dbg_bCarpetMeshFillVU1MemInBegin = false;
DEBUGVAR float dbg_fMaxNumQuads = -1.0f;
static bool bCarpetMeshVU1Debug = false;

#endif // #if defined( DEBUG_PS2_CARPET )

void Asura_PS2_Carpet_Mesh_Store::Initialize( void )
{
	g_xHeader.uNumStyles = 0;
	g_xHeader.uTotalNumTris = 0;

    g_pvStyleAndModuleHdrs = NULL;
    g_pvModuleDMAChains    = NULL;
    g_pxStyles             = NULL;

    g_pxStyleHdrs  = NULL;
    g_pxModuleHdrs = NULL;

#ifdef ASURA_DEBUG_VARIABLE_SYSTEM
	Asura_CommandConsole::AddVar( L"Render.PS2.CarpetMipBiasToggle", &cnsl_bUseCarpetMipMapBias, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar( L"Render.PS2.CarpetMipBiasValue", &cnsl_uCarpetMipMapBias, 0, 255, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif	
}

void Asura_PS2_Carpet_Mesh_Store::Shutdown( void )
{
	Reset();
}

void Asura_PS2_Carpet_Mesh_Store::Reset( void )
{
	g_xHeader.uNumStyles = 0;
	g_xHeader.uTotalNumTris = 0;

    delete[] g_pxStyles;
    Asura_DeallocateAlign( g_pvModuleDMAChains );
    Asura_Deallocate( g_pvStyleAndModuleHdrs );

    g_pvStyleAndModuleHdrs = NULL;
    g_pvModuleDMAChains    = NULL;
    g_pxStyles             = NULL;

    g_pxStyleHdrs  = NULL;
    g_pxModuleHdrs = NULL;
}

void Asura_PS2_Carpet_Mesh_Store::Render( const float fGlobalFade )
{
	if( Asura_PS2_RenderingOptions::GetOption( Asura_PS2_RenderingOptions::OPTION_DRAW_CARPET ) == false )
	{
		return;
	}

    if( !g_xHeader.uTotalNumTris ) { return; }

    const u_int uNumStyles = g_xHeader.uNumStyles;
	if ( uNumStyles == 0 )
	{
		return;
	}

	ASURA_PS2_PROFILE__ADD_VIF_START( ASURA_PS2_PROFILE__CARPET_MARKER );

	const float fFade = fGlobalFade;
	// The input alpha image is 0 - transparent 128->255 for solid but with noise on it however
	// on the PS2 the alpha range is 64->128 (because alpha values are halved when making PS2 textures)
	// Formula is (remember on PS2 alpha scale operation is ( alpha_texture * scale_value / 128 )
	//			cut_value = 128.0f - 64.0f * fFade;
	//          scale_value = 128.0f * ( 64.0f / cut_value );
	//			scale_value = 128.0f / ( 2.0f - fFade ) );
	// But because of integer maths used it has been found that the following forumla gives better resuls:
	//			scale_value = ( 128.0f / ( 2.0f - fFade ) ) + fFudge;
	// With fFudge = 0.914f gives the least average square error (0.51%)
	// 
	// So: 
	//   fFade = 0.0 (0% visible) scale = 64 = 0.5
	//   fFade = 1.0 (100% visible) scale = 128 = 1.0
	// This value is a scaler so make it be in the 0-1 range (using 128 = 1)
	g_fGlobalFade = ( ( 128.0f / ( 2.0f - fFade ) ) + 0.914f ) / 128.0f;
	g_fGlobalViewScale = 1.0f;

	const u_int uNumCams = Asura_Camera::GetNumberOfCameras();
	for ( u_int uI = 0; uI < uNumCams; uI++ )
	{
		Asura_Camera::SetCameraIndex( uI );
		const Asura_Matrix_3x3* const xView = Asura_Camera::GetOrientation();
		g_pxCamHorizVector[uI].x = xView->e11;
		g_pxCamHorizVector[uI].y = xView->e21;
		g_pxCamHorizVector[uI].z = xView->e31;
		g_pxCamVertVector[uI].x = xView->e12;
		g_pxCamVertVector[uI].y = xView->e22;
		g_pxCamVertVector[uI].z = xView->e32;
		g_pxCamFwdVector[uI].x = xView->e13;
		g_pxCamFwdVector[uI].y = xView->e23;
		g_pxCamFwdVector[uI].z = xView->e33;

		g_pxCamPos[uI] = *(Asura_Camera::GetPosition());

		g_pfCamHorizScale[uI] = Asura_Maths::Tangent( Asura_Camera::GetHorizontalFieldOfView() * 0.5f );
	}
	Asura_Camera::SetCameraIndex( 0 );

	//static float s_fElapsedTime = 0.0f;
	//const float fDeltaTime = Asura_Timers::GetFrameTime();
	//const float fTimeScale = Asura_Timers::GetGameTimeScale();
	//const float fDeltaGameTime = fDeltaTime* fTimeScale;
	//const float fTime = s_fElapsedTime + fDeltaGameTime;
	//s_fElapsedTime = fTime;
	const float fTime = Asura_Timers::GetLocalScaledTime();

	// Setup the movement vectors
	const u_int uSizeWaveX = 16;
	for ( u_int i = 0; i < uSizeWaveX; i++)
	{		
		const float fS = static_cast<float>(i);
		g_pfWaveX[i] =  0.1f * Asura_Maths::Sine( (1.0f * fS) + fTime * (1.0f + (fS * 0.05f)) )
			+ 0.05f * Asura_Maths::Sine( (2.0f * fS) + fTime * (1.0f + ((16.0f - fS) * 0.05f)));
	}

#if defined( DEBUG_PS2_CARPET )
	bCarpetBotanicalsDebugTex = bCarpetBotanicalsDebugTexReal;
	if ( bCarpetBotanicalsDebugTex ) 
	{
		Asura_OutputToDebugger_Retail( ( "**** CARPET START ****\n" ) );
	}
#endif // #if defined( DEBUG_PS2_CARPET )

	Begin();

	// Compute the vis & clip flags for each module - need to share this between env renderer, carpet_mesh and botanicals
    const u_int uNumModules = Asura_Module_System::GetNumberOfModules();
	for( u_int uModule = 0; uModule < uNumModules; ++uModule )
	{
		const u_int uVisFlags = Asura_Module_System::GetModuleVisFlags( uModule );
		if ( uVisFlags )
		{
			const Asura_Vector_3& xBSCentre = Asura_Module_System::GetCentreOfModule( uModule );
			const float fBSRadius = Asura_Module_System::GetRadiusOfModule( uModule );

			u_int uClipFlags = 0;
			u_int uClipBit = 1 << 6;
			for( u_int uCam = 0; uCam < uNumCams; ++uCam )
			{
				if ( uVisFlags & ( 1 << uCam ) )
				{
					Asura_Camera::SetCameraIndex( uCam );
					if( !Asura_PS2_VU1::IsSphereEntirelyWithinGuardbandFrustum(xBSCentre, fBSRadius) )
					{
						uClipFlags |= uClipBit;
					}
				}
				uClipBit >>= 2;
			}
			g_puVisAndClipFlags[uModule] = uVisFlags | (uClipFlags << ASURA_MAX_NO_OF_CAMERAS);
		}
		else
		{
			g_puVisAndClipFlags[uModule] = 0;
		}
	}

	Asura_PS2_TextureManagement::SetBufferLayout( ASURA_PS2_TEXBFRLAYOUT_2_LARGE );

	// Draw the triangles
	const Asura_PS2_Carpet_Style_DMA_Data* pxStyle = g_pxStyles;
	u_int uTotalBillboards = 0;

    const Asura_PS2_Carpet_Mesh_Module_Header* pxModuleHdrsForThisStyle = g_pxModuleHdrs;

	for( u_int uStyle = 0; uStyle < uNumStyles; ++uStyle )
	{
		// Get the style header
		SetStyle( pxStyle );
		u_int uNumTrisPerStyle = 0;

		const Asura_PS2_Carpet_Mesh_Style_Header& xStyleHeader = g_pxStyleHdrs[uStyle];

		const u_int uNumModulesInStyle = xStyleHeader.usNumModules;
		for( u_int u = 0; u < uNumModulesInStyle; u++ )
		{
			const Asura_PS2_Carpet_Mesh_Module_Header& xModuleHdr = pxModuleHdrsForThisStyle[u];

			const u_int uModuleIdx = xModuleHdr.usModuleID;
			const u_int uVisAndClipFlags = g_puVisAndClipFlags[uModuleIdx];
            u_int uVisFlags  = uVisAndClipFlags & ((1 << ASURA_MAX_NO_OF_CAMERAS) - 1);
            u_int uClipFlags = uVisAndClipFlags >> ASURA_MAX_NO_OF_CAMERAS;

            // Visible to any camera?
            if( !uVisFlags ) { continue; }

            u_int uNumCamsInRange = 0;

            // Also check that it's in range for one of the cameras that can
            // see it. (We can't do this as part of the module visibility
            // checks above because the range is style-dependent.)
            {
                float fLoDFadeFar = Asura_Maths::Min( pxStyle->xData[10].f32s[3], g_fCarpetMaxDist );

			    const Asura_Vector_3& xModuleCentre = Asura_Module_System::GetCentreOfModule( uModuleIdx );
			    float fModuleBSRadius = Asura_Module_System::GetRadiusOfModule( uModuleIdx );

                for( u_int uCam = 0; uCam < uNumCams; ++uCam )
                {
                    // Linked in in the first place?
                    if( !(uVisFlags & (1 << uCam)) ) { continue; }

                    // Within range?
                    if( (xModuleCentre - g_pxCamPos[uCam]).Magnitude() <= fModuleBSRadius + fLoDFadeFar )
                    {
                        ++uNumCamsInRange;
                        continue;
                    }

                    // Out of range. Modify the cam bits accordingly.
                    uVisFlags &= ~(1 << uCam);
                    uClipFlags &= ~(1 << (6 - (uCam << 1)));
                }

                // Did we eliminate all cameras on range?
                if( !uVisFlags ) { continue; }
            }

            // Setup renderstate for this module.
		    SetCamFlags( uVisFlags, uClipFlags );
            SetModulePosAndSeed( Asura_Module_System::GetPositionOfModule(uModuleIdx), xModuleHdr.uSeed );
		    SendPendingData( true );

            // And send this chain.
			Asura_PS2_DmaTag* pxCALLTag = reinterpret_cast<Asura_PS2_DmaTag*>( Asura_PS2_DeferredRendering::GetVifBuffer(1) );
			pxCALLTag->SetWithNOPs( 0,
				                    Asura_PS2_DmaTag::PCE_NOCHANGE,
				                    Asura_PS2_DmaTag::ID_CALL,
				                    false,
				                    xModuleHdr.uDMAChainSizeOrPtr,
				                    false );

            uTotalBillboards += uNumCamsInRange * xModuleHdr.usExpectedNumBillboards;
		}

        pxModuleHdrsForThisStyle += uNumModulesInStyle;
		pxStyle++;
	}

	End();

    Asura_PS2_Render::UpdateTriStats( uTotalBillboards * 2, 1, Asura_PS2_Render::TRICOUNT_CARPET );

#if defined( DEBUG_PS2_CARPET )
	if ( bCarpetBotanicalsDebugTex ) 
	{
		Asura_OutputToDebugger_Retail( ( "**** CARPET END ****\n" ) );
		bCarpetBotanicalsDebugTex = true;
	}
#endif // #if defined( DEBUG_PS2_CARPET )

	ASURA_PS2_PROFILE__ADD_VIF_END( ASURA_PS2_PROFILE__CARPET_MARKER );
}

bool Asura_PS2_Carpet_Mesh_Store::Load( Asura_Handle xFile )
{
	// Remove memory
	Reset();

	Asura_FileAccess::ReadData( xFile, &g_xHeader, sizeof( Asura_PS2_Carpet_Mesh_Header ) );
	if ( g_xHeader.uNumStyles == 0 )
	{
		return true;
	}

    g_pvStyleAndModuleHdrs = Asura_Allocate( g_xHeader.uTotalHeadersSizeInBytes );
	Asura_FileAccess::ReadData( xFile, g_pvStyleAndModuleHdrs, g_xHeader.uTotalHeadersSizeInBytes );

    Asura_Assert( !(g_xHeader.uTotalDMAChainSize & 15), "" );
    g_pvModuleDMAChains = Asura_AllocateAlign( g_xHeader.uTotalDMAChainSize, ASURA_ALIGN_16_BYTE );
	Asura_FileAccess::ReadData( xFile, g_pvModuleDMAChains, g_xHeader.uTotalDMAChainSize );

	g_pxStyles = new Asura_PS2_Carpet_Style_DMA_Data[g_xHeader.uNumStyles];
	Asura_FileAccess::ReadData( xFile, g_pxStyles, g_xHeader.uNumStyles * sizeof( Asura_PS2_Carpet_Style_DMA_Data ) );

	// Loop over the styles setting the texture ID
	for ( u_int uI = 0; uI < g_xHeader.uNumStyles; uI++ )
	{
		Asura_PS2_Carpet_Style_DMA_Data* pxStyle = &g_pxStyles[uI];

		const Asura_Hash_ID xTextureHash( pxStyle->iTextureID );
        pxStyle->iTextureID = Asura_Texture_Management::GetIndexFromHashID( xTextureHash );

		const float fLoDFadeNear = pxStyle->xData[9].f32s[3];
		const float fLoDFadeFar = pxStyle->xData[10].f32s[3];
		float fSub = fLoDFadeFar - fLoDFadeNear;
		if ( fSub < 1.0e-4f )
		{
			fSub = 1.0e-4f;
		}
		const float fScale1 = 0.5f * ( ( 0.0f - 1.0f ) / fSub );
		// QW[13].f32s[13] = fK1 = fX3 * fScale1
		pxStyle->xData[13].f32s[3] = fLoDFadeFar * fScale1 - 0.5f;
		// QW[14].f32s[14] = fScale1 = ( 0 - 1 ) / ( fX3 - fX2 )
		pxStyle->xData[14].f32s[3] = fScale1;

		// Disable any near fading
		// QW[13].f32s[13] = fK1 = fX3 * fScale1
		pxStyle->xData[11].f32s[3] = -1.0f;
		// QW[14].f32s[14] = fScale1 = ( 0 - 1 ) / ( fX3 - fX2 )
		pxStyle->xData[12].f32s[3] = 1.0f;
	}

    Asura_PS2_Carpet_Mesh_Style_Header* pxStyleHdrs = static_cast<Asura_PS2_Carpet_Mesh_Style_Header*>( g_pvStyleAndModuleHdrs );
    Asura_PS2_Carpet_Mesh_Module_Header* pxModuleHdrs = reinterpret_cast<Asura_PS2_Carpet_Mesh_Module_Header*>( pxStyleHdrs + g_xHeader.uNumStyles );

    // Patch up the module headers to contain DMA chain addresses instead of
    // just sizes.
    {
        Asura_PS2_Carpet_Mesh_Module_Header* pxNextModuleHdr = pxModuleHdrs;
        u_int uNextChainAddr = reinterpret_cast<u_int>( g_pvModuleDMAChains );

        for( u_int uStyle = 0; uStyle < g_xHeader.uNumStyles; ++uStyle )
        {
            const Asura_PS2_Carpet_Mesh_Style_Header& xStyleHdr = pxStyleHdrs[uStyle];
            for( u_int uModule = 0; uModule < xStyleHdr.usNumModules; ++uModule )
            {
                u_int uChainSize = pxNextModuleHdr->uDMAChainSizeOrPtr;
                Asura_Assert( !(uChainSize & 15), "" );

                pxNextModuleHdr->uDMAChainSizeOrPtr = uNextChainAddr;

                uNextChainAddr += uChainSize;
                ++pxNextModuleHdr;
            }
        }

        Asura_Assert( uNextChainAddr == reinterpret_cast<u_int>(g_pvModuleDMAChains) + g_xHeader.uTotalDMAChainSize, "" );
    }

    g_pxStyleHdrs = pxStyleHdrs;
    g_pxModuleHdrs = pxModuleHdrs;

	return true;
}

// Movement is height * wave value
//	pxVertices[0].x += fHeight * fWave;
//	pxVertices[1].x += fHeight * fWave;

void Asura_PS2_Carpet_Mesh_Store::SetStyle( const Asura_PS2_Carpet_Style_DMA_Data* pxStyle )
{
	Asura_Assert(pxStyle, "");
	// Is the style valid?
	if (!pxStyle) return;	

	int iTexID = pxStyle->iTextureID;
	int iMMAG = SCE_GS_LINEAR;
	int iMMIN = SCE_GS_LINEAR_MIPMAP_NEAREST; // bilinear
	//int iMMIN = SCE_GS_LINEAR_MIPMAP_LINEAR; // trilinear
	u_int uBfr = Asura_PS2_TextureManagement::BFR_AUTO;

	const u_int uCurPendingFlags = g_uPendingFlags;
	u_int uNewPendingFlags = uCurPendingFlags;

	bool bUploaded = false;

	{
		bool bUpload = true;

		// If there's another texture sync pending, say from another pass's
		// texture upload, then sync with that first.
		if( uCurPendingFlags & PENDING_UPLOAD_TEX_SYNC )
		{
			if( bUpload )
			{
				Asura_PS2_TextureManagement::SetupTextureEnvironment( g_bTexUploaded, ASURA_PS2_TEXREGFLAG_TEXFLUSH );
			}
		}

		if( bUpload )
		{
			bUploaded = Asura_PS2_TextureManagement::UploadTexture( iTexID, uBfr );

			g_bTexUploaded = bUploaded;
			uNewPendingFlags |= PENDING_UPLOAD_TEX_SYNC;
		}
	}

	// Refresh tex setup stuff if necessary. Note that a check on whether
	// or not the setup has already been done for the specified texture ID
	// breaks in two cases: (i) if they're requesting the same texture but
	// with different filtering settings; (ii) if texture caching isn't
	// working, so that we've uploaded the texture again despite it being
	// resident. I don't think (i) is likely to happen, mainly because we
	// use bilinear everywhere on meshes, and we can workaround (ii) by
	// forcing a refresh if we found the texture needed to be uploaded above.
	int& iCurTex = g_iCurBaseTex;
	if( bUploaded || ((iTexID != iCurTex) && (iTexID != -1)) )
	{
		const u_int uMIPTBP1Offset = ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_MIPTBP1;
		Asura_PS2_TextureManagement::GetMIPTBP1Setting( &(g_pxRSBfr + uMIPTBP1Offset)->u64s[0], iTexID, uBfr );

		const u_int uTEX1Offset = ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_TEX1;
		Asura_PS2_TextureManagement::GetTEX1Setting( &(g_pxRSBfr + uTEX1Offset)->u64s[0], iTexID, iMMAG, iMMIN, false, cnsl_bUseCarpetMipMapBias ? cnsl_uCarpetMipMapBias : 0);

		const u_int uTEX0Offset = ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_TEX0;
		Asura_PS2_TextureManagement::GetTEX0Setting( &(g_pxRSBfr + uTEX0Offset)->u64s[0], iTexID, uBfr );

		iCurTex = iTexID;
		uNewPendingFlags |= PENDING_UPLOAD_RENDERSTATE;
#if defined( DEBUG_PS2_CARPET )
		if ( bCarpetBotanicalsDebugTex ) 
		{
			Asura_OutputToDebugger_Retail( ( "iTexID %d TBP 0x%X DW %d DH %d\n", 
										   iTexID,
										   g_pxRSBfr[uTEX0Offset].u32s[0] & 0x3FFF,
										   ( g_pxRSBfr[uTEX0Offset].u32s[0] >> 26 ) & 0xF,
										   ( g_pxRSBfr[uTEX0Offset].u64s[0] >> 30 ) & 0xF ) );
		}
		if ( bCarpetBotanicalsDebugTex ) 
		{
			Asura_OutputToDebugger_Retail( ( "TEX1 0x%X\n",  g_pxRSBfr[uTEX1Offset].u32s[0] ) );
			Asura_OutputToDebugger_Retail( ( "MIPTBP1 0x%X\n",  g_pxRSBfr[uMIPTBP1Offset].u32s[0] ) );
			Asura_OutputToDebugger_Retail( ( "TEX0 0x%X\n",  g_pxRSBfr[uTEX0Offset].u32s[0] ) );
		}
#endif // #if defined( DEBUG_PS2_CARPET )
	}

	uNewPendingFlags |= PENDING_UPLOAD_STYLE;
	g_uPendingFlags = uNewPendingFlags;
	g_pxCurStyle = pxStyle;
}

void Asura_PS2_Carpet_Mesh_Store::Begin( void )
{
	const bool bNeedFullSetup = Asura_PS2_VU1::LoadRenderer( Asura_PS2_VU1::RENDERER_CARPET );

    // Reserve some of the beginning of SPR.
    const u_int uNumModules = Asura_Module_System::GetNumberOfModules();
    const u_int uQWsForVisAndClipFlags = ( uNumModules + 3 ) >> 2;
	const u_int uTotalSPRQWs = TOTAL_SPR_QWS_REQD + uQWsForVisAndClipFlags;

    (void) Asura_PS2_SPR_Alloc( 0, uTotalSPRQWs ); // The cast fixes warning in release.

	// Couple of bits for debugging purposes, for checking everything's
	// correctly initialised, etc.
#if defined( DEBUG_PS2_CARPET )
	if( dbg_bCarpetMeshFillSPRInBegin )
	{
		const u_long128 uFillValue = ASURA_PS2_INIT_ULONG128( 0xCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDuLL );
		u_long128* puOut = reinterpret_cast<u_long128*>( 0x70000000 );
		for( u_int u = uTotalSPRQWs; u; --u )
		{
			*puOut++ = uFillValue;
		}
	}
	if( dbg_bCarpetMeshFillVU1MemInBegin )
	{
		Asura_PS2_VU1::FillMem( ASURA_PS2_INIT_ULONG128(0xCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDuLL) );
	}
#endif // #if defined( DEBUG_PS2_CARPET )

	const u_int uNumCams = Asura_Camera::GetNumberOfCameras();

	const u_int uNumQWsForCamUNPACK    = uNumCams * 5;
	const u_int uNumQWsForGIFTagUNPACK = ASURA_PS2_CARPET_MESH__SIZE__GIF_TAGS;

	const u_int uNumQWsForDMA = uNumQWsForCamUNPACK
		+ 1 + uNumQWsForGIFTagUNPACK
		+ 1
		+ 1 + Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS;

	const u_int uTotalQWs = 1 + uNumQWsForDMA;

	Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer( uTotalQWs );
	Asura_PS2_QW* pxOut = pxBase;

	(reinterpret_cast<Asura_PS2_DmaTag*>(pxOut++))->Set( uNumQWsForDMA,
		Asura_PS2_DmaTag::PCE_NOCHANGE,
		Asura_PS2_DmaTag::ID_CNT,
		false,
		0,
		false,
		SCE_VIF1_SET_STCYCL(1, 1, false),
		SCE_VIF1_SET_UNPACK(ASURA_PS2_CARPET_MESH__ADDR__CONST_CAM_DATA, uNumQWsForCamUNPACK, Asura_PS2_VU1::VIFPK_V4_32, false) );

	// Const cam data.
	for( u_int uCam = 0; uCam < uNumCams; ++uCam )
	{
		pxOut = Asura_PS2_GS::GetViewportSetupGifCmds( uCam, Asura_PS2_GS::CONTEXT_1, pxOut );

		(pxOut++)->u128 = reinterpret_cast<const u_long128&>( Asura_PS2_VU1::GetGBClipToScrScaleVec(uCam) );
		(pxOut++)->u128 = reinterpret_cast<const u_long128&>( Asura_PS2_VU1::GetGBClipToScrTransVec(uCam) );
		(pxOut++)->u128 = reinterpret_cast<const u_long128&>( Asura_PS2_VU1::GetGBClipToScrClipScaleVec(uCam) );
	}

	// Setup BASE and OFFSET and start UNPACK of GIF tags.
	pxOut->u32s[0] = SCE_VIF1_SET_BASE( ASURA_PS2_CARPET_MESH__ADDR__INPUT_BFR_0, false );
	pxOut->u32s[1] = SCE_VIF1_SET_OFFSET( ASURA_PS2_CARPET_MESH__SIZE__INPUT_BFR, false );
	pxOut->u32s[2] = SCE_VIF1_SET_NOP( false );
	pxOut->u32s[3] = SCE_VIF1_SET_UNPACK( ASURA_PS2_CARPET_MESH__ADDR__GIF_TAGS, uNumQWsForGIFTagUNPACK, Asura_PS2_VU1::VIFPK_V4_32, false );
	++pxOut;

	// Geometry setup GIF tag. The default NLOOP should be for the base
	// pass; we'll fix it up on VU1 for anything else.
	{
		Asura_PS2_GifTag* pxGeomSetupGifTag = reinterpret_cast<Asura_PS2_GifTag*>( pxOut++ );
		pxGeomSetupGifTag->SetExceptRegs( ASURA_PS2_CARPET_MESH__RS_OUT_0_NLOOP, true, false, 0, SCE_GIF_PACKED, 1 );
		pxGeomSetupGifTag->uAllRegs = SCE_GIF_PACKED_AD;
	}

	// Geometry draw GIF tag. Note that NLOOP is setup in µcode.
	{
		Asura_PS2_GifTag* pxGeomDrawGifTag = reinterpret_cast<Asura_PS2_GifTag*>( pxOut++ );

		u_int uPrimType = SCE_GS_PRIM_TRISTRIP;
		bool  bTME      = true;
#ifndef ASURA_PS2_SONY_SUBMISSION
		if( Asura_Render::GetWireframe() )
		{
			uPrimType = SCE_GS_PRIM_LINESTRIP;
			bTME      = false;
		}
#endif
		const u_int uPRIM = SCE_GS_SET_PRIM( uPrimType, 1, bTME, false, true, false, 0, 0, 0 );
		pxGeomDrawGifTag->SetExceptRegs( 0, true, true, uPRIM, SCE_GIF_PACKED, 3 );

		pxGeomDrawGifTag->uAllRegs = (SCE_GS_ST << 0) | (SCE_GS_RGBAQ << 4) | (SCE_GS_XYZ2 << 8);
	}

	// Ignore the next 2 GIFtags
	++pxOut;
	++pxOut;

	// Run INIT µprogram and start UNPACK of shadow constant data.
	pxOut->u32s[0] = ASURA_PS2_MAKE_MSCAL_FROM_VU_PTR( VU1_CARPET_MESH_INIT );
	pxOut->u32s[1] = SCE_VIF1_SET_NOP( false );
	pxOut->u32s[2] = SCE_VIF1_SET_NOP( false );
	pxOut->u32s[3] = SCE_VIF1_SET_NOP( false );
	++pxOut;

	// DIRECT for default renderstate setup.
	pxOut->u32s[0] = SCE_VIF1_SET_NOP( false );
	pxOut->u32s[1] = SCE_VIF1_SET_NOP( false );
	pxOut->u32s[2] = SCE_VIF1_SET_NOP( false );
	pxOut->u32s[3] = SCE_VIF1_SET_DIRECT( Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS, false );
	++pxOut;

	Asura_PlatformRenderStates::CreateDefaultRenderStatePacket( Asura_PlatformRenderStates::CONTEXT_1, pxOut, true, false );
	pxOut += Asura_PlatformRenderStates::NUM_RENDERSTATE_PACKET_QWS;

	Asura_Assert( ( pxOut == ( pxBase + uTotalQWs ) ), "" );

	// Reset buffer indecies. We assume these are still in sync if
	// renderer was already present on VU; the only time they'll change
	// is when we run one of the VU1_CARPET_MESH_SWITCH... subroutines.
	g_uCurVURSBfr     = 0;
	g_uCurVUCamBfr    = 0;
	g_uCurVUStyleBfr  = 0;
	g_uCurVULightBfr  = 0;
	g_uPendingFlags	  = 0;
    g_iCurBaseTex = ASURA_TEXTURE_NONE;
	g_pxCurStyle  = NULL;
	g_uMeshFlags       = 0;
	g_uPrevMeshFlags   = 0;
	g_uNumLightQWs = 1; 
    g_uNumLights    = 0;

	// Fill in constant elements of renderstate bfr.
	(g_pxRSBfr + ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_ALPHA      )->u64s[1] = SCE_GS_ALPHA_1;
	(g_pxRSBfr + ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_TEX1       )->u64s[1] = SCE_GS_TEX1_1;
	(g_pxRSBfr + ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_TEX0       )->u64s[1] = SCE_GS_TEX0_1;
	(g_pxRSBfr + ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_MIPTBP1    )->u64s[1] = SCE_GS_MIPTBP1_1;
	(g_pxRSBfr + ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_TEST       )->u64s[1] = SCE_GS_TEST_1;

	// Just output colour source
	( g_pxRSBfr + ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_ALPHA )->u64s[0] =
		SCE_GS_SET_ALPHA( SCE_GS_ALPHA_ZERO, SCE_GS_ALPHA_ZERO, SCE_GS_ALPHA_FIX, SCE_GS_ALPHA_CS, 0 );
	// Only pass with alpha > 63 pass alpha test, so ignore pixels with alpha <= 63
	( g_pxRSBfr + ASURA_PS2_CARPET_MESH__OFFSET__RS_IN_BASE_TEST )->u64s[0] = 
		SCE_GS_SET_TEST( true, SCE_GS_ALPHA_GREATER, 63, SCE_GS_AFAIL_KEEP, false, 0, true, SCE_GS_ZGEQUAL );

    g_uPendingFlags    = PENDING_UPLOAD_RENDERSTATE | PENDING_UPLOAD_CAM | PENDING_UPLOAD_MESH_FLAGS | PENDING_UPLOAD_CAM_FLAGS | PENDING_UPLOAD_LIGHT;

    u_int uNewMeshFlags = 0;
    //uNewMeshFlags |= ASURA_PS2_CARPET_MESH__FLAG__POINT_LIT;
    g_uMeshFlags = uNewMeshFlags;
}

void Asura_PS2_Carpet_Mesh_Store::End( void )
{
	// Send any pending things e.g. set various states but not actually rendered a mesh
	// because the mesh was out of view
	if( g_uPendingFlags )
	{
		SendPendingData( false );
	}

	// FLUSH to prevent subsequent rendering trashing stuff in VU memory that
	// we're still in the middle of using. 
	Asura_PS2_QW* pxQWForTag = Asura_PS2_DeferredRendering::GetVifBuffer( 1 );
	(reinterpret_cast<Asura_PS2_DmaTag*>(pxQWForTag))->Set( 0,
		Asura_PS2_DmaTag::PCE_NOCHANGE,
		Asura_PS2_DmaTag::ID_CNT,
		false,
		0,
		false,
		SCE_VIF1_SET_NOP(false),
		SCE_VIF1_SET_FLUSH(false) );

	// Return region of SPR that we reserved in Begin().
	Asura_PS2_SPR_Dealloc( reinterpret_cast<Asura_PS2_QW*>(0x70000000) );
}

void Asura_PS2_Carpet_Mesh_Store::SendPendingData( const bool bWillMSCNTForNextStrip )
{
	const u_int uMeshFlags = g_uMeshFlags;
	u_int uPendingFlags = g_uPendingFlags;

	// Turn on need-to-send-mesh-flags flag if required.
	if( uMeshFlags != g_uPrevMeshFlags )
	{
		uPendingFlags |= PENDING_UPLOAD_MESH_FLAGS;
		g_uPrevMeshFlags = uMeshFlags;
	}

	// Early out if nothing's changed since the previous strip.
	if( !uPendingFlags ) 
	{ 
		return; 
	}

	// Anything to UNPACK? Do this first, so we can leave it as long as
	// possible before FLUSHing.
	if ( uPendingFlags & (PENDING_UPLOAD_RENDERSTATE | PENDING_UPLOAD_CAM | PENDING_UPLOAD_LIGHT | PENDING_UPLOAD_MODULE_POS_AND_SEED) )
	{
		u_int uEstimatedQWC = 0;
		if( uPendingFlags & PENDING_UPLOAD_RENDERSTATE		   ) { uEstimatedQWC += 1 + ASURA_PS2_CARPET_MESH__SIZE__RENDERSTATE_BFR;		}
		if( uPendingFlags & PENDING_UPLOAD_CAM				   ) { uEstimatedQWC += 1 + ASURA_PS2_CARPET_MESH__SIZE__PER_OBJ_CAM_DATA_BFR;	}
		if( uPendingFlags & PENDING_UPLOAD_LIGHT			   ) { uEstimatedQWC += 1 + ASURA_PS2_CARPET_MESH__SIZE__LIGHT_DATA_BFR;		}
		if( uPendingFlags & PENDING_UPLOAD_MODULE_POS_AND_SEED ) { uEstimatedQWC += 2 + ASURA_PS2_CARPET_MESH__SIZE__MODULE_POS_BFR;		}

		const u_int uTotalQWs = 1 + uEstimatedQWC;
		Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer( uTotalQWs );
		Asura_PS2_QW* pxOut = pxBase;

		Asura_PS2_DmaTag* const pxCNTTag = reinterpret_cast<Asura_PS2_DmaTag*>( pxOut );
		pxCNTTag->Set( 0, // Will be fixed up below as necessary.
			Asura_PS2_DmaTag::PCE_NOCHANGE,
			Asura_PS2_DmaTag::ID_CNT,
			false,
			0,
			false );

		pxCNTTag->m_uWord2 = SCE_VIF1_SET_STCYCL( 1, 1, false );

		bool bNeedNOPs = false;

		if( uPendingFlags & PENDING_UPLOAD_RENDERSTATE )
		{
			const u_int uBfr  = g_uCurVURSBfr ^ 1;
			const u_int uAddr = ( uBfr ? ASURA_PS2_CARPET_MESH__ADDR__RENDERSTATE_BFR_1 : ASURA_PS2_CARPET_MESH__ADDR__RENDERSTATE_BFR_0 );

			if( bNeedNOPs )
			{
				FillQWWithNOPs( pxOut );
			}
			pxOut->u32s[3] = SCE_VIF1_SET_UNPACK( uAddr, ASURA_PS2_CARPET_MESH__SIZE__RENDERSTATE_BFR, Asura_PS2_VU1::VIFPK_V4_32, false );
			++pxOut;

			const u_long128* puSrc = &g_pxRSBfr->u128;
			for( u_int u = ASURA_PS2_CARPET_MESH__SIZE__RENDERSTATE_BFR; u; --u )
			{
				(pxOut++)->u128 = *puSrc++;
			}

			bNeedNOPs     = true;
			g_uCurVURSBfr = uBfr;
		}

		// Send the camera data
		if( uPendingFlags & PENDING_UPLOAD_CAM )
		{
			const u_int uCamVisFlags = g_uCamVisFlags;
			const u_int uBfr         = g_uCurVUCamBfr ^ 1;
			const u_int uSize        = ASURA_PS2_CARPET_MESH_SIZE__PER_CAM_DATA_BFR;
			u_int uAddr = ( uBfr ? ASURA_PS2_CARPET_MESH__ADDR__PER_OBJ_CAM_DATA_BFR_1 : ASURA_PS2_CARPET_MESH__ADDR__PER_OBJ_CAM_DATA_BFR_0 );
			const u_int uNumCams = Asura_Camera::GetNumberOfCameras();

			// Send the identity transforms down
			for( u_int uCam = 0; uCam < uNumCams; uAddr += ASURA_PS2_CARPET_MESH__OFFSET__CAM_PER_OBJ_NEXT_CAM, ++uCam )
			{
				if( bNeedNOPs )
				{
					FillQWWithNOPs( pxOut );
				}
				bNeedNOPs = true;
				(pxOut++)->u32s[3] = SCE_VIF1_SET_UNPACK( uAddr, uSize, Asura_PS2_VU1::VIFPK_V4_32, false );
				// Send identity matrix down
				const Asura_PS2_QW* const pxStart = pxOut;
				sceVu0FMATRIX& xW2GBC = Asura_PS2_VU1::GetWorldToClipMatGuardband( uCam );
				Asura_PS2_VU1::CopyPS2Mat( pxOut, xW2GBC );
				pxOut += 4;

				// Send the view width vector
				pxOut->f32s[0] = g_pxCamHorizVector[uCam].x;
				pxOut->f32s[1] = g_pxCamHorizVector[uCam].y;
				pxOut->f32s[2] = g_pxCamHorizVector[uCam].z;
				pxOut->f32s[3] = 0.0f;
				pxOut++;

				// Send the view height vector
				pxOut->f32s[0] = 0.0f;
				pxOut->f32s[1] = 1.0f;
				pxOut->f32s[2] = 0.0f;
				pxOut->f32s[3] = 0.0f;
				pxOut++;

				// Send the view forward vector
				pxOut->f32s[0] = g_pxCamFwdVector[uCam].x;
				pxOut->f32s[1] = g_pxCamFwdVector[uCam].y;
				pxOut->f32s[2] = g_pxCamFwdVector[uCam].z;
				pxOut->f32s[3] = g_pxCamFwdVector[uCam] * g_pxCamPos[uCam];	// Dot-product less memory than sending camPos ( pos - camPos )
				pxOut++;

				Asura_Assert( static_cast<u_int>(pxOut - pxStart) == uSize, "" );
			}
			g_uCurVUCamBfr = uBfr;
		}
		if( uPendingFlags & PENDING_UPLOAD_LIGHT )
		{
			u_int uQWs = g_uNumLightQWs;
			Asura_Assert( uQWs, "" );

			const u_int uBfr  = g_uCurVULightBfr ^ 1;
			const u_int uAddr = ( uBfr ? ASURA_PS2_CARPET_MESH__ADDR__LIGHT_DATA_BFR_1 : ASURA_PS2_CARPET_MESH__ADDR__LIGHT_DATA_BFR_0 );

			if( bNeedNOPs )
			{
				FillQWWithNOPs( pxOut );
			}
			(pxOut++)->u32s[3] = SCE_VIF1_SET_UNPACK( uAddr, uQWs, Asura_PS2_VU1::VIFPK_V4_32, false );

			const u_long128* puSrc = &g_pxOutLightBfr->u128;
			for( ; uQWs; --uQWs )
			{
				(pxOut++)->u128 = *puSrc++;
			}

			bNeedNOPs        = true;
			g_uCurVULightBfr = uBfr;
		}
        if( uPendingFlags & PENDING_UPLOAD_MODULE_POS_AND_SEED )
        {
            if( bNeedNOPs )
            {
                FillQWWithNOPs( pxOut );
            }

            pxOut->u32s[3] = SCE_VIF1_SET_FLUSHE( false ); // Not double-buffering this so ensure we don't upset whatever's currently running.
            ++pxOut;
            pxOut->u32s[0] = SCE_VIF1_SET_UNPACK( ASURA_PS2_CARPET_MESH__ADDR__MODULE_POS_BFR, 1, Asura_PS2_VU1::VIFPK_V4_32, false );
            pxOut->f32s[1] = g_pxModulePosAndSeed->f32s[0];
            pxOut->f32s[2] = g_pxModulePosAndSeed->f32s[1];
            pxOut->f32s[3] = g_pxModulePosAndSeed->f32s[2];
            ++pxOut;
            FillQWWithNOPs( pxOut );
            pxOut->u32s[0] = g_pxModulePosAndSeed->u32s[3];
            ++pxOut;

            bNeedNOPs = true;
        }

		if( !bNeedNOPs ) { (pxOut++)->u32s[3] = SCE_VIF1_SET_NOP( false ); }

		// Verify that we didn't underestimate.
		Asura_Assert( pxOut <= pxBase + uTotalQWs, "" );

		// Fix up tag.
		u_int uActualQWC = pxOut - reinterpret_cast<Asura_PS2_QW*>(pxCNTTag + 1);
		pxCNTTag->m_uQWC = uActualQWC;

		// Return remainder of buffer if necessary.
		u_int uDiff = (pxBase + uTotalQWs) - pxOut;
		if( uDiff ) { Asura_PS2_DeferredRendering::ReturnVifBuffer( pxOut, uDiff ); }
	}
	// Send the style data
	if( uPendingFlags & PENDING_UPLOAD_STYLE )
	{
		// WARNING: changing the style data per frame means CPU is changing data which is being DMA'ed - could 
		// have problems because it isn't double-buffered
        // (MP - 21.05.2008) You don't say... Changed it to copy the style
        // data into the VIF buffer instead.

		const u_int uDstBfr = g_uCurVUStyleBfr ^ 1;
		u_int uDstAddr = ( uDstBfr ? ASURA_PS2_CARPET_MESH__ADDR__STYLE_DATA_BFR_1 : ASURA_PS2_CARPET_MESH__ADDR__STYLE_DATA_BFR_0 );

        ASURA_STATIC_ASSERT( (sizeof(Asura_PS2_Carpet_Style_DMA_Data::xData) / 16) == ASURA_PS2_CARPET_MESH__SIZE__PER_STYLE_DATA_BFR );

		Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer( 1 + ASURA_PS2_CARPET_MESH__SIZE__PER_STYLE_DATA_BFR );
		Asura_PS2_QW* pxOut = pxBase;

        Asura_PS2_DmaTag* pxDMATag = reinterpret_cast<Asura_PS2_DmaTag*>( pxOut );
        pxDMATag->Set( ASURA_PS2_CARPET_MESH__SIZE__PER_STYLE_DATA_BFR,
                       Asura_PS2_DmaTag::PCE_NOCHANGE,
                       Asura_PS2_DmaTag::ID_CNT,
                       false,
                       0,
                       false,
                       SCE_VIF1_SET_STCYCL(1, 1, false),
                       SCE_VIF1_SET_UNPACK(uDstAddr, ASURA_PS2_CARPET_MESH__SIZE__PER_STYLE_DATA_BFR, Asura_PS2_VU1::VIFPK_V4_32, false) );
        ++pxOut;

        // Copy base data.
		const Asura_PS2_Carpet_Style_DMA_Data* pxStyle = g_pxCurStyle;
        for( u_int u = 0; u < ASURA_PS2_CARPET_MESH__SIZE__PER_STYLE_DATA_BFR; ++u )
        {
            (pxOut + u)->u128 = *reinterpret_cast<const u_long128*>(&pxStyle->xData[u]);
        }

        // Go back and fix up whatever needs fixing up.
        {
		    // QW[i].f32s[1] = movement[i]
		    for ( u_int uI = 0; uI < 16; uI++ )
		    {
			    (pxOut + uI)->f32s[1] = g_pfWaveX[uI];
		    }
		    // QW[15].f32s[3] = global fade
		    (pxOut + 15)->f32s[3] = g_fGlobalFade;

//#if defined( DEBUG_PS2_CARPET )
//		    if ( ( dbg_fCarpetX0 != -666.0f ) && ( dbg_fCarpetX1 != -666.0f ) )
//		    {
//			    // Curves are: fScale0 * x - fK0
//			    // Curves are: fScale1 * x - fK1
//			    float fSub = ( dbg_fCarpetX1 - dbg_fCarpetX0 );
//			    if ( fSub < 1.0e-4f )
//			    {
//				    fSub = 1.0e-4f;
//			    }
//			    const float fScale0 = 0.5f * ( ( 1.0f - 0.0f ) / fSub );
//			    // QW[11].f32s[11] = fK0 = fX0 * fScale0
//			    pxStyle->xData[11].f32s[3] = dbg_fCarpetX0 * fScale0 - 0.5f;
//			    // QW[12].f32s[12] = fScale0 = ( 1 - 0 ) / ( fX1 - fX0 )
//			    pxStyle->xData[12].f32s[3] = fScale0;
//		    }
//		    else
//		    {
//			    pxStyle->xData[11].f32s[3] = -1.0f;
//			    pxStyle->xData[12].f32s[3] = 1.0f;
//		    }
//		    float fLoDFadeNear;
//		    float fLoDFadeFar;
//		    if ( ( dbg_fCarpetX2 != -666.0f ) && ( dbg_fCarpetX3 != -666.0f ) )
//		    {
//			    fLoDFadeNear = dbg_fCarpetX2;
//			    fLoDFadeFar = dbg_fCarpetX3;
//		    }
//		    else
//		    {
//			    // QW[9].f32s[3] = fLoDFadeNear
//			    fLoDFadeNear = pxStyle->xData[9].f32s[3];
//			    // QW[10].f32s[3] = fLoDFadeFar
//			    fLoDFadeFar = pxStyle->xData[10].f32s[3];
//		    }
//
//		    const float fScale1 = 0.5f * ( ( 0.0f - 1.0f ) / ( fLoDFadeFar - fLoDFadeNear ) );
//		    // QW[13].f32s[13] = fK1 = fX3 * fScale1
//		    pxStyle->xData[13].f32s[3] = fLoDFadeFar * fScale1 - 0.5f;
//		    // QW[14].f32s[14] = fScale1 = ( 0 - 1 ) / ( fX3 - fX2 )
//		    pxStyle->xData[14].f32s[3] = fScale1;
//#endif // #if defined( DEBUG_PS2_CARPET )
        }

		g_uCurVUStyleBfr = uDstBfr;
	}

	if( uPendingFlags & PENDING_UPLOAD_TEX_SYNC )
	{
		Asura_PS2_TextureManagement::SetupTextureEnvironment( g_bTexUploaded, ASURA_PS2_TEXREGFLAG_TEXFLUSH );
	}

	// Add any necessary MSCALs.
	{
		u_int uEstimatedQWC = 0;
		// MSCALs to update ptrs after UNPACKs above.
		if( uPendingFlags & (PENDING_UPLOAD_STYLE | PENDING_UPLOAD_RENDERSTATE | PENDING_UPLOAD_CAM | PENDING_UPLOAD_LIGHT | PENDING_UPLOAD_MODULE_POS_AND_SEED) ) { uEstimatedQWC += 2; }
		// ITOPs and MSCALs for mesh/cam flags.
		if( uPendingFlags & (PENDING_UPLOAD_MESH_FLAGS | PENDING_UPLOAD_CAM_FLAGS) ) { ++uEstimatedQWC; }

		const u_int uTotalQWs = 1 + uEstimatedQWC;
		Asura_PS2_QW* const pxBase = Asura_PS2_DeferredRendering::GetVifBuffer( uTotalQWs );
		Asura_PS2_QW* pxOut = pxBase;

		Asura_PS2_DmaTag* const pxCNTTag = reinterpret_cast<Asura_PS2_DmaTag*>( pxOut );
		pxCNTTag->Set( 0, // Will be fixed up below as necessary.
			Asura_PS2_DmaTag::PCE_NOCHANGE,
			Asura_PS2_DmaTag::ID_CNT,
			false,
			0,
			false );

		u_int uNumMSCALs = 0;
		u_int uWordOut   = 2;

		if( uPendingFlags & PENDING_UPLOAD_STYLE )
		{
			pxOut->u32s[uWordOut++] = MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT( VU1_CARPET_MESH_SWITCH_STYLE_DATA_BFR, uNumMSCALs );
		}   
		if( uPendingFlags & PENDING_UPLOAD_RENDERSTATE )
		{
			pxOut->u32s[uWordOut++] = MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT( VU1_CARPET_MESH_SWITCH_RENDERSTATE_BFR, uNumMSCALs );
		}   
		if( uPendingFlags & PENDING_UPLOAD_CAM )
		{
			pxOut->u32s[uWordOut++] = MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT( VU1_CARPET_MESH_SWITCH_CAM_DATA_BFR, uNumMSCALs );
		}
		if( uPendingFlags & PENDING_UPLOAD_LIGHT )
		{
			pxOut->u32s[uWordOut++] = MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT( VU1_CARPET_MESH_SWITCH_LIGHT_DATA_BFR, uNumMSCALs );
		}
		if( uPendingFlags & PENDING_UPLOAD_MESH_FLAGS )
		{
			pxOut->u32s[uWordOut++] = SCE_VIF1_SET_ITOP( uMeshFlags, false );
			pxOut->u32s[uWordOut++] = MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT( VU1_CARPET_MESH_LOAD_MESH_FLAGS, uNumMSCALs );
		}
		if( uPendingFlags & PENDING_UPLOAD_CAM_FLAGS )
		{
			pxOut->u32s[uWordOut++] = SCE_VIF1_SET_ITOP( g_uCamFlagsForVU, false );
			pxOut->u32s[uWordOut++] = MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT( VU1_CARPET_MESH_LOAD_CAM_FLAGS, uNumMSCALs );
		}
        if( uPendingFlags & PENDING_UPLOAD_MODULE_POS_AND_SEED )
        {
			pxOut->u32s[uWordOut++] = MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT( VU1_CARPET_MESH_LOAD_SEED_FOR_NEW_MODULE, uNumMSCALs );
        }

		// We need to run the NOP µprogram up to two times:
		//     (i) If we're transferring a whole chain of strips and so will
		//         be MSCNTing to render the next strip, we need a NOP to get
		//         the VU1 PC to point to the right place.
		//    (ii) If we've written an odd number of MSCALs above, including
		//         (i), we need a dummy one to get the TOP register back to
		//         the correct value, ie. pointing to the strip data that the
		//         caller UNPACKed.
		if( bWillMSCNTForNextStrip ) { pxOut->u32s[uWordOut++] = MAKE_MSCAL_FROM_VU_PTR_WITH_COUNT( VU1_CARPET_MESH_NOP, uNumMSCALs ); }
		if( uNumMSCALs & 1         ) { pxOut->u32s[uWordOut++] = ASURA_PS2_MAKE_MSCAL_FROM_VU_PTR( VU1_CARPET_MESH_NOP ); }

		// Pad to end of QW.
		if( uWordOut & 3 )
		{
			for( u_int u = 4 - (uWordOut & 3); u--; )
			{
				pxOut->u32s[uWordOut++] = SCE_VIF1_SET_NOP( false );
			}
		}

		// Fix up output ptr.
		pxOut += (uWordOut >> 2);

		// Verify that we didn't underestimate.
		Asura_Assert( pxOut <= pxBase + uTotalQWs, "" );

		// Fix up tag.
		u_int uActualQWC = pxOut - reinterpret_cast<Asura_PS2_QW*>(pxCNTTag + 1);
		pxCNTTag->m_uQWC = uActualQWC;

		// Return remainder of buffer if necessary.
		u_int uDiff = (pxBase + uTotalQWs) - pxOut;
		if( uDiff ) { Asura_PS2_DeferredRendering::ReturnVifBuffer( pxOut, uDiff ); }
	}

	// Clear pending flags.
	g_uPendingFlags = 0;
}

void Asura_PS2_Carpet_Mesh_Store::SetCamFlags( u_int uVisFlags, u_int uClipFlags )
{
	// Callers should deal with meshes that aren't visible to any cameras.
	Asura_Assert( uVisFlags, "" );

	u_int uNumCamsThatCanSee = 0;
	u_int uVisFlagsForVU     = 0;

	// See notes in Asura_PS2_GSMDefines.h for what's going on here.
	{
		u_int uEEVisBit = 1 << 0;
		u_int uVUVisBit = 1 << 7;

		for( u_int u = Asura_Camera::GetNumberOfCameras(); u; --u )
		{
			if( uVisFlags & uEEVisBit )
			{
				uVisFlagsForVU |= uVUVisBit;
				++uNumCamsThatCanSee;
			}

			uEEVisBit <<= 1;
			uVUVisBit >>= 2;
		}
	}

	// Ensure that no clip flags are set where the corresponding vis flag
	// isn't. This breaks some µcode that assumes the flags will be zero once
	// there are no more cameras to render for.
	Asura_Assert( !(uClipFlags & ~(uVisFlagsForVU >> 1)), "" );

	u_int uNewFlagsForVU = uVisFlagsForVU | uClipFlags;

	// If more than one camera can see the mesh, let the µprogram know.
	if( uNumCamsThatCanSee > 1 )
	{
		uNewFlagsForVU |= ASURA_PS2_CARPET_MESH__FLAG__VIS_FOR_MORE_THAN_ONE_CAM;
	}

	if( uNewFlagsForVU != g_uCamFlagsForVU )
	{
		g_uCamVisFlags = uVisFlags;
		g_uCamFlagsForVU = uNewFlagsForVU;
		g_uNumCamsVisFor = uNumCamsThatCanSee;
		g_uPendingFlags |= PENDING_UPLOAD_CAM_FLAGS;
	}
}


void Asura_PS2_Carpet_Mesh_Store::SetModulePosAndSeed( const Asura_Vector_3& xPos, u_int uSeed )
{
    g_pxModulePosAndSeed->f32s[0] = xPos.x;
    g_pxModulePosAndSeed->f32s[1] = xPos.y;
    g_pxModulePosAndSeed->f32s[2] = xPos.z;
    g_pxModulePosAndSeed->u32s[3] = uSeed;

    g_uPendingFlags |= PENDING_UPLOAD_MODULE_POS_AND_SEED;
}


#endif // #if defined( ASURA_USE_CARPET )