/*----------------------------------------------------------------------*
 *                                                                      *
 * Module  :                                                            *
 *                                                                      *
 * Purpose :                                                            *
 *                                                                      *
 * FX      :                                                            *
 *----------------------------------------------------------------------*/

/**
 * \defgroup rpmatfx310xbox RpMatFx on Xbox
 * \ingroup rpmatfx310
 * 
 * Material Effects on Xbox
 */

/** 
 * \ingroup rpmatfx310xbox
 *
 * \page rpmatfx310xboxoverview Overview of RpMatFX on Xbox
 * 
 * RpMatFX on Xbox is provided for PS2 compatibility. It is in no way a Xbox
 * optimized pipeline. The effects provided use the fixed function pipeline
 * exclusively and no pixel shaders.
 * No rework of art resources is needed to run RpMatFX on the Xbox.
 *
 * \par Environment Mapping
 *
 * Environment mapping is done in one pass using using Hardware generated 
 * texture coordinates. 
 *
 * \par Bump Mapping
 *
 * Bump mapping is done using an emboss method as on the PS2. It is done in two 
 * passes and results in a graphical output close to the PS2.
 * \par
 * Known bug : A bump mapped object can not be alpha blended. This is due to the 
 * two passes approach and the use of the final blending unit.
 *
 * \par Bump Environment Mapping
 *
 * Bump environment mapping is a combination of both bump mapping and environment
 * mapping. This results in a three passes approach. It has the same shortcomings
 * as bump mapping.
 *
 * \par Dual Pass
 *
 * Dual pass, as the name implies, is done using a dual pass approach. All the 
 * RenderWare blending modes are supported. As it uses two passes, it can not be 
 * alpha blended.
 *
 * \see \ref fixedfunctionpipeline
 *
 */

/*----------------------------------------------------------------------*
 *-   Includes                                                         -*
 *----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*==== Xbox includes ====*/
#include <xtl.h>
#include <d3d8.h>

/*==== RW libs includes ====*/
#include "rpplugin.h"
#include <rpdbgerr.h>
#include <rwcore.h>
#include <archive/rpmatfx310.h>

#ifdef RWMETRICS
#include "xbmetric.h"
#endif /* RWMETRICS */

#include "effectpipes.h"
#include "rpmfxxbx.h"

/*----------------------------------------------------------------------*
 *-   Local Types                                                      -*
 *----------------------------------------------------------------------*/

/*----------------------------------------------------------------------*
 *-   Local/static Globals                                             -*
 *----------------------------------------------------------------------*/

static RxPipeline   *MatFXAtomicPipe = NULL,
					*MatFXWorldSectorPipe = NULL;
static _rxXboxAllInOneInstanceCallBack instancePipeline,
			worldInstancePipeline;

RxPipeline * _rpMatFXXboxAtomicPipeGet()
/* naughty little function for the benefit of rpteamxbox */
{
    return MatFXAtomicPipe;
}


static DWORD bumpDecl[] =
{
	D3DVSD_STREAM(0),
	D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3),
	D3DVSD_REG(D3DVSDE_NORMAL, D3DVSDT_FLOAT3),
	D3DVSD_REG(D3DVSDE_TEXCOORD0, D3DVSDT_FLOAT2),
	D3DVSD_STREAM(1),
	D3DVSD_REG(D3DVSDE_TEXCOORD1, D3DVSDT_FLOAT2),
	D3DVSD_END()
};


static DWORD litBumpDecl[] =
{
	D3DVSD_STREAM(0),
	D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3),
	D3DVSD_REG(D3DVSDE_NORMAL, D3DVSDT_FLOAT3),
	D3DVSD_REG(D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR),
	D3DVSD_REG(D3DVSDE_TEXCOORD0, D3DVSDT_FLOAT2),
	D3DVSD_STREAM(1),
	D3DVSD_REG(D3DVSDE_TEXCOORD1, D3DVSDT_FLOAT2),
	D3DVSD_END()
};

static DWORD bumpFFPShader;
static DWORD litBumpFFPShader;

D3DBLEND _RwXbDualPassConvTable[NUMBLENDMODES] =
{
    0,
    D3DBLEND_ZERO,
    D3DBLEND_ONE,
    D3DBLEND_SRCCOLOR,
    D3DBLEND_INVSRCCOLOR,
    D3DBLEND_SRCALPHA,
    D3DBLEND_INVSRCALPHA,
    D3DBLEND_DESTALPHA,
    D3DBLEND_INVDESTALPHA,
    D3DBLEND_DESTCOLOR,
    D3DBLEND_INVDESTCOLOR,
    D3DBLEND_SRCALPHASAT
};

#define NUMADDRESSMODES 5
static D3DTEXTUREADDRESS _RwXbMatFXAddressConvTable[NUMADDRESSMODES] =
{
    0,
    D3DTADDRESS_WRAP,
    D3DTADDRESS_MIRROR,
    D3DTADDRESS_CLAMP,
    D3DTADDRESS_BORDER
};

typedef struct __rwXbMatFXFilterParams _rwXbMatFXFilterParams;
struct __rwXbMatFXFilterParams
{
    D3DTEXTUREFILTERTYPE min;
    D3DTEXTUREFILTERTYPE mag;
    D3DTEXTUREFILTERTYPE filter;
};

#define NUMFILTERMODES  7
static _rwXbMatFXFilterParams _RwXbMatFXFilterModeConvTable[NUMFILTERMODES] =
{
    {D3DTEXF_NONE,   D3DTEXF_NONE,   D3DTEXF_NONE},
    {D3DTEXF_POINT,  D3DTEXF_POINT,  D3DTEXF_NONE},
    {D3DTEXF_LINEAR, D3DTEXF_LINEAR, D3DTEXF_NONE},
    {D3DTEXF_POINT,  D3DTEXF_POINT,  D3DTEXF_POINT},
    {D3DTEXF_LINEAR, D3DTEXF_LINEAR, D3DTEXF_POINT},
    {D3DTEXF_POINT,  D3DTEXF_POINT,  D3DTEXF_LINEAR},
    {D3DTEXF_LINEAR, D3DTEXF_LINEAR, D3DTEXF_LINEAR},
};

#define MAXXBOXSTAGES 4

static D3DTRANSFORMSTATETYPE _rxXbTexTrxTable[MAXXBOXSTAGES] = 
{
    D3DTS_TEXTURE0,
    D3DTS_TEXTURE1,
    D3DTS_TEXTURE2,
    D3DTS_TEXTURE3
};


/*----------------------------------------------------------------------*
 *-   Globals across program                                           -*
 *----------------------------------------------------------------------*/

/*----------------------------------------------------------------------*
 *-   Functions                                                        -*
 *----------------------------------------------------------------------*/

/****************************************************************************
 _rwXbMatFXRenderStateTextureStageRaster

 On entry   : A RwRaster pointer for the desired texture.
 On exit    :
 */
static void
_rwXbMatFXRenderStateTextureStageRaster(RwRaster *raster, RwInt32 stage)
{
    RWFUNCTION(RWSTRING("_rwXbMatFXRenderStateTextureStageRaster"));

    if (raster)
    {
        RwXboxRasterExtension  *rasExt;

        rasExt = RwXboxRasterGetExtension(raster);

        /* Set the texture */
        D3DDevice_SetTexture(stage,
                             (LPDIRECT3DBASETEXTURE8)(rasExt->texture));

        /* Set the palette if it is a palettized texture */
        if (rasExt->palette)
        {
            D3DDevice_SetPalette(stage, rasExt->palette);
        }

        /*
         * Enable Alpha Blending if the texture has alpha
         */
        if (rasExt->alpha)
        {
            D3DDevice_SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
            D3DDevice_SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
        }
        else
        {
            D3DDevice_SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
            D3DDevice_SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
        }
    }
    else
    {
        D3DDevice_SetTexture(stage, NULL);

        /*
         * Disable alpha blending if it has not been explicity set
         */
        D3DDevice_SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
        D3DDevice_SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
    }

    RWRETURNVOID();
}

/****************************************************************************
 _rwXbMatFXRenderStateTexture

 On entry   : A RwTexture pointer to set all texture related renderstate for.
 On exit    :
 */
void
_rwXbMatFXRenderStateTexture(RwTexture *texture, RwInt32 stage)
{
    RWFUNCTION(RWSTRING("_rwXbMatFXRenderStateTexture"));

    if (texture)
    {
        RwTextureFilterMode     filtering = texture->filtering;
        RwTextureAddressMode    addressingU = texture->addressingU;
        RwTextureAddressMode    addressingV = texture->addressingV;

        /* Addressing mode */
        D3DDevice_SetTextureStageState(stage, D3DTSS_ADDRESSU,
                                       _RwXbMatFXAddressConvTable[addressingU]);

        D3DDevice_SetTextureStageState(stage, D3DTSS_ADDRESSV,
                                       _RwXbMatFXAddressConvTable[addressingV]);

        /* Filter mode */
        D3DDevice_SetTextureStageState(stage, D3DTSS_MINFILTER,
                                _RwXbMatFXFilterModeConvTable[filtering].min);
        D3DDevice_SetTextureStageState(stage, D3DTSS_MAGFILTER,
                                _RwXbMatFXFilterModeConvTable[filtering].mag);
        D3DDevice_SetTextureStageState(stage, D3DTSS_MIPFILTER,
                                _RwXbMatFXFilterModeConvTable[filtering].filter);

        /* Raster */
        _rwXbMatFXRenderStateTextureStageRaster(texture->raster, stage);
    }
    else
    {
        /* Raster */
        _rwXbMatFXRenderStateTextureStageRaster(NULL, stage);
    }

    RWRETURNVOID();
}

/*
 * New instance callback for the bump mapping - creates the second stream 
 */
static RwBool
rxXbMatFXInstanceCallback(void *object,
                          RxXboxResEntryHeader *instancedHeader,
                          RwBool reinstance,
                          _rxXboxAllInOneInstanceCallBack instancePipe )
{
	rpMatFXMaterialData     *matFXData;
	MatFXBumpMapData *bumpMapData;
	RwUInt16 vbSize;
    RxXboxInstanceData *mesh;

    RWFUNCTION( RWSTRING( "rxXbMatFXInstanceCallback" ) );

	if( FALSE == instancePipe( object, instancedHeader, reinstance ))
	{
		RWRETURN( FALSE );
	}

    for (mesh = instancedHeader->begin; mesh != instancedHeader->end; mesh++)
    {
	/* Now I can create my extra data if we're bump mapping */
    	matFXData = *MATFXMATERIALGETDATA( mesh->material );
	if( matFXData )
	{
		switch( matFXData->flags )
		{
			case rpMATFXEFFECTENVMAP:
				break;

			case rpMATFXEFFECTBUMPMAP:
    			case rpMATFXEFFECTBUMPENVMAP:

    				bumpMapData = MATFXXBOXBUMPMAPGETDATA(mesh->material);
    				/*
    				 * Set the vertex shader to the bump FFP shader
    				 */				
    				bumpMapData->bumpVertexShader = bumpFFPShader;

    				/*
    				 * Create the extra vertex buffer with padding for preceding meshes
    				 * (have to have match between vertex buffers from each stream)
    				 */				

    				vbSize = (RwUInt16) (mesh->numVertices + mesh->minVert) * ( sizeof ( RwReal ) * 2 );

    				bumpMapData->vbStride = sizeof ( RwReal ) * 2;

    				if (D3D_OK != D3DDevice_CreateVertexBuffer( vbSize, IGNORED_XBOX,
    											 IGNORED_XBOX, IGNORED_XBOX, (D3DVertexBuffer**) &bumpMapData->tex2 ))
                    {
                		RWRETURN( FALSE );
                    }
    				break;

    			case rpMATFXEFFECTDUAL:
    				break;
    		}
		}
	}

    RWRETURN(TRUE);
}

static RwBool
rxXbAtomicMatFXInstanceCallback(void *object,
                               RxXboxResEntryHeader *instancedData,
                               RwBool reinstance)
{
    RWFUNCTION( RWSTRING( "_rxXBAtomicMatFXInstanceCallback" ) );

    RWRETURN( rxXbMatFXInstanceCallback( object, instancedData, reinstance, instancePipeline ) );
}

static RwBool
rxXbWorldSectorMatFXInstanceCallback(void *object,
                               RxXboxResEntryHeader *instancedData,
                               RwBool reinstance)
{
    RWFUNCTION( RWSTRING( "_rxXbWorldSectorMatFXInstanceCallback" ) );

    RWRETURN( rxXbMatFXInstanceCallback( object, instancedData, reinstance, worldInstancePipeline ) );
}

RwTexture          *
_rpMatFXSetupBumpMapTexture(const RwTexture * baseTexture,
                            const RwTexture * effectTexture)
{
	RwTexture *texture;

    RWFUNCTION(RWSTRING("_rpMatFXSetupBumpMapTexture"));
    
	texture = _rpMatFXTextureMaskCreate(baseTexture, effectTexture);
	
    RWRETURN(texture);
}



/*--- Create and destory pipelines ------------------------------------------*/

/****************************************************************************
 rxXBAtomicMatFXDefaultRender
 
 Purpose:
 
 On entry:
                
 On exit:
 */
void
rxXbAtomicMatFXDefaultRender(RxXboxResEntryHeader *resEntryHeader,
                             RxXboxInstanceData *mesh,
                             rpMatFXMaterialData *matFXData,
                             RwUInt32 flags)
{
    RwTexture   *texture;
    RwBool      alpha;

	
	RWFUNCTION(RWSTRING("rxXBAtomicMatFXDefaultRender"));

    texture = mesh->material->texture;
//	RWASSERT(NULL != texture);
	_rwXbRenderStateTexture(texture);

    alpha = (mesh->material->color.alpha == 0xFF) ? FALSE : TRUE;
    RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)alpha);

	D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);

	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE);
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

    /*
     * Pixel shader
     */
    RwXboxSetCurrentPixelShader(mesh->pixelShader);

    /*
     * Vertex shader
     */
    RwXboxSetCurrentVertexShader(mesh->vertexShader);

    /*
     * Draw the indexed primitive
     */
    D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
                                  mesh->numIndices,
                                  mesh->indexBuffer);

	RWRETURNVOID();
}

/****************************************************************************
 rxXBAtomicMatFXBumpMapRender
 
 Purpose:
 
 On entry:
                
 On exit:
 */
void
rxXbAtomicMatFXBumpMapRender(RxXboxResEntryHeader *resEntryHeader,
                             RxXboxInstanceData *mesh,
                             rpMatFXMaterialData *matFXData,
                             RwUInt32 flags,
							 RwMatrix *mat)
{
    MatFXBumpMapData *bumpMapData;
    RwBool          alpha;
	RwUInt8			*lockedVB;
	RwUInt8			*lockedTexVB;
	RwUInt32		offset;
	RwV2d			*texCoords;
	RwV2d			*bumpCoords;
	D3DVECTOR		*normal;
	RwUInt32		i;
	RwFrame			*bumpFrame;
	RwV3d			*lightPos;
	D3DXVECTOR4		bumpDir;
	DWORD			cacheSrc;
	DWORD			cacheDest;

	D3DCOLOR tFactor = 0xFF000000;

    RWFUNCTION(RWSTRING("rxXBAtomicMatFXBumpMapRender"));

	/*
	 * Cache the blending mode to avoid screwing the logo
	 */
	D3DDevice_GetRenderState(D3DRS_SRCBLEND, &cacheSrc);
	D3DDevice_GetRenderState(D3DRS_DESTBLEND, &cacheDest);

    alpha = (mesh->material->color.alpha == 0xFF) ? FALSE : TRUE;
    RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)alpha);

	/*
	 * First pass, render the object with TFACTOR only
	 */
	D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR );

    /*
     * Pixel shader
     */
    RwXboxSetCurrentPixelShader(mesh->pixelShader);

    /*
     * Vertex shader
     */
	if( flags & rxGEOMETRY_PRELIT )
	{
		RwXboxSetCurrentVertexShader(litBumpFFPShader);
	}
	else
	{
		RwXboxSetCurrentVertexShader(bumpFFPShader);
	}

    /*
     * Draw the indexed primitive
     */
    D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
                                  mesh->numIndices,
                                  mesh->indexBuffer);


    bumpMapData = MATFXXBOXBUMPMAPGETDATA(mesh->material);

	// If the geometry is textured, we can copy the texCoords
	if( flags & rxGEOMETRY_TEXTURED )
	{
		D3DVertexBuffer_Lock((D3DVertexBuffer *)resEntryHeader->vertexBuffer, 0, 
			0, &lockedVB, D3DLOCK_READONLY);

        lockedVB += resEntryHeader->stride * mesh->minVert;

		D3DVertexBuffer_Lock((D3DVertexBuffer *)bumpMapData->tex2, 0, 
			0, &lockedTexVB, 0);

        lockedTexVB += bumpMapData->vbStride * mesh->minVert;

		for( i = 0; i < (RwUInt32) mesh->numVertices; i++ )
		{
			D3DVECTOR trxNormal;
			float xx,yy;

			offset = sizeof(RwV3d);

			normal = (D3DVECTOR*) (lockedVB + offset);

			RwV3dTransformPoints( (RwV3d*) &trxNormal, (RwV3d*) normal, 1, mat );

			_rwV3dNormalize((RwV3d*) &trxNormal, (RwV3d*) &trxNormal);

			bumpFrame = bumpMapData->frame;
	        if (bumpFrame == NULL)
	        {
		        bumpFrame = RwCameraGetFrame(RwCameraGetCurrentCamera());
			    RWASSERT(bumpFrame);
			}

			/*lightPos = RwMatrixGetPos(RwFrameGetLTM(bumpFrame));*/
			lightPos = RwMatrixGetAt(RwFrameGetLTM(bumpFrame));

			bumpDir.x = lightPos->x + ( trxNormal.x * 5.0f );
			bumpDir.y = lightPos->y + ( trxNormal.y * 5.0f );
			bumpDir.z = lightPos->z + ( trxNormal.z * 5.0f );

			_rwV3dNormalize((RwV3d*) &bumpDir, (RwV3d*) &bumpDir);

			offset += sizeof(RwV3d);
			if( flags & rxGEOMETRY_PRELIT )
			{
				offset += sizeof(RwRGBA);
			}

			texCoords = (RwV2d*) (lockedVB + offset);

			bumpCoords = (RwV2d *)lockedTexVB;

			xx = bumpDir.x * MAX_BUMP_TEXTURE_SHIFT * bumpMapData->coef;
			/*if( xx > MAX_SHIFT ) xx = MAX_SHIFT;
			if( xx < -MAX_SHIFT ) xx = -MAX_SHIFT;*/
			yy = bumpDir.y * MAX_BUMP_TEXTURE_SHIFT * bumpMapData->coef;
			/*if( yy > MAX_SHIFT ) yy = MAX_SHIFT;
			if( yy < -MAX_SHIFT ) yy = -MAX_SHIFT;*/

			bumpCoords->x = texCoords->x + xx;
			bumpCoords->y = texCoords->y + yy;

			lockedVB += resEntryHeader->stride;
			lockedTexVB += bumpMapData->vbStride;
		}

		D3DVertexBuffer_Unlock( ( D3DVertexBuffer *) bumpMapData->tex2 );

		D3DVertexBuffer_Unlock( ( D3DVertexBuffer *) resEntryHeader->vertexBuffer );
	}


	D3DDevice_SetRenderState(D3DRS_TEXTUREFACTOR, tFactor );
        
	RWASSERT(NULL != bumpMapData->texture);
    _rwXbRenderStateTexture(bumpMapData->texture);
	_rwXbMatFXRenderStateTexture(bumpMapData->texture, 1);

	D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    
	D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1 );
	D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
	D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );
	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_ADDSIGNED);
	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE | D3DTA_COMPLEMENT);
	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT );

	D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	D3DDevice_SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	D3DDevice_SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
    /*
     * Pixel shader
     */
    RwXboxSetCurrentPixelShader(mesh->pixelShader);

    /*
     * Vertex shader
     */
	if( flags & rxGEOMETRY_PRELIT )
	{
		RwXboxSetCurrentVertexShader(litBumpFFPShader);
	}
	else
	{
		RwXboxSetCurrentVertexShader(bumpFFPShader);
	}

    /*
     * Set the stream source
     */

    D3DDevice_SetStreamSource(1,
                              (D3DVertexBuffer *)bumpMapData->tex2,
                              bumpMapData->vbStride);

    /*
     * Draw the indexed primitive
     */
    D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
                                  mesh->numIndices,
                                  mesh->indexBuffer);

	D3DDevice_SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
	D3DDevice_SetRenderState(D3DRS_SRCBLEND, cacheSrc);
	D3DDevice_SetRenderState(D3DRS_DESTBLEND, cacheDest);

	D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
	D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0 );

	D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
	D3DDevice_SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);

    D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

	D3DDevice_SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE);

    RWRETURNVOID();
}

/****************************************************************************
 rxXBAtomicMatFXDualPassRender
 
 Purpose:
 
 On entry:
                
 On exit:
 */
void
rxXbAtomicMatFXDualPassRender(RxXboxResEntryHeader *resEntryHeader,
                             RxXboxInstanceData *mesh,
                             rpMatFXMaterialData *matFXData,
                             RwUInt32 flags)
{
	MatFXDualData *dualData;
    RwTexture   *texture;
    RwBool      alpha;
	DWORD		cacheSrc;
	DWORD		cacheDest;
	DWORD		cacheCmpFunc;

	RWFUNCTION(RWSTRING("rxXBAtomicMatFXDualPassRender"));
    texture = mesh->material->texture;

	/*
	 * Cache the blending mode to avoid screwing the logo
	 */
	D3DDevice_GetRenderState(D3DRS_SRCBLEND, &cacheSrc);
	D3DDevice_GetRenderState(D3DRS_DESTBLEND, &cacheDest);
	D3DDevice_GetRenderState(D3DRS_ALPHAFUNC, &cacheCmpFunc);

    alpha = (mesh->material->color.alpha == 0xFF) ? FALSE : TRUE;
    RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)alpha);

	dualData = MATFXXBOXDUALPASSGETDATA(mesh->material);

    if (((flags & rxGEOMETRY_TEXTURED) && texture) ||
        ((flags & rpGEOMETRYTEXTURED2) && texture))
    {
		RWASSERT(NULL != dualData->texture);
		_rwXbRenderStateTexture(texture);
	}
	else
	{
		_rwXbRenderStateTexture(NULL);
	}

	/*
	 * First pass, render the base texture
	 */

	D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);	
    D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);

	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
    D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);

	/*
	 * Pixel shader
	 */
	RwXboxSetCurrentPixelShader(mesh->pixelShader);

	/*
	 * Vertex shader
	 */	
	RwXboxSetCurrentVertexShader(mesh->vertexShader);

	/*
	 * Draw the indexed primitive
	 */	
	D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
		                          mesh->numIndices,
			                      mesh->indexBuffer);

	/*
	 * Now we can draw the second pass
	 */
	if(flags & rpGEOMETRYTEXTURED2)
	{
		D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 1);
	}
	else
	{
		D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
	}

	D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );

	D3DDevice_SetRenderState( D3DRS_SRCBLEND, _RwXbDualPassConvTable[dualData->srcBlendMode] );
	D3DDevice_SetRenderState( D3DRS_DESTBLEND, _RwXbDualPassConvTable[dualData->dstBlendMode] );
	D3DDevice_SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_ALWAYS );

	D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);	
    D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);

	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
    D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);

    /*
     * Pixel shader
     */
    RwXboxSetCurrentPixelShader(mesh->pixelShader);

    /*
     * Vertex shader
     */
    RwXboxSetCurrentVertexShader(mesh->vertexShader);


	_rwXbRenderStateTexture(dualData->texture);

    /*
     * Draw the indexed primitive
     */
    D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
                                  mesh->numIndices,
                                  mesh->indexBuffer);

	D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
	D3DDevice_SetRenderState(D3DRS_SRCBLEND, cacheSrc);
	D3DDevice_SetRenderState(D3DRS_DESTBLEND, cacheDest);
	D3DDevice_SetRenderState( D3DRS_ALPHAFUNC, cacheCmpFunc );

    D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

	RWRETURNVOID();
}


/****************************************************************************
 rxXBAtomicMatFXBumpEnvMapRender
 
 Purpose:
 
 On entry:
                
 On exit:
 */
void
rxXbAtomicMatFXBumpEnvMapRender( RxXboxResEntryHeader *resEntryHeader,
                             RxXboxInstanceData *mesh,
                             rpMatFXMaterialData *matFXData,
                             RwUInt32 flags,
							 RwMatrix *mat)
{
    MatFXBumpMapData *bumpMapData;
	MatFXEnvMapData *envMapData;
    RwBool          alpha;
	RwUInt8			*lockedVB;
	RwUInt8			*lockedTexVB;
	RwUInt32		offset;
	RwV2d			*texCoords;
	RwV2d			*bumpCoords;
	D3DVECTOR		*normal;
	RwUInt32		i;
	RwFrame			*bumpFrame;
	RwV3d			*lightPos;
	D3DXVECTOR4		bumpDir;
	RwUInt32        shinny;
	DWORD			cacheSrc;
	DWORD			cacheDest;

    static D3DXMATRIX texMat =
    {
         0.5f, 0.0f, 0.0f, 0.0f,
         0.0f,-0.5f, 0.0f, 0.0f,
         0.0f, 0.0f, 1.0f, 0.0f,
         0.5f, 0.5f, 0.0f, 1.0f
    };


	D3DCOLOR tFactor = 0xFF000000;

    RWFUNCTION(RWSTRING("rxXBAtomicMatFXBumpMapRender"));

	/*
	 * Cache the blending mode to avoid screwing the logo
	 */
	D3DDevice_GetRenderState(D3DRS_SRCBLEND, &cacheSrc);
	D3DDevice_GetRenderState(D3DRS_DESTBLEND, &cacheDest);


    alpha = (mesh->material->color.alpha == 0xFF) ? FALSE : TRUE;
    RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)alpha);

	/*
	 * First pass, render the object with TFACTOR only
	 */
	D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR );

	D3DDevice_SetRenderState(D3DRS_TEXTUREFACTOR, tFactor);

    /*
     * Pixel shader
     */
    RwXboxSetCurrentPixelShader(mesh->pixelShader);

    /*
     * Vertex shader
     */
	if( flags & rxGEOMETRY_PRELIT )
	{
		RwXboxSetCurrentVertexShader(litBumpFFPShader);
	}
	else
	{
		RwXboxSetCurrentVertexShader(bumpFFPShader);
	}

    /*
     * Draw the indexed primitive
     */
    D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
                                  mesh->numIndices,
                                  mesh->indexBuffer);


    bumpMapData = MATFXXBOXBUMPMAPGETDATA(mesh->material);

	// If the geometry is textured, we can copy the texCoords
	if( flags & rxGEOMETRY_TEXTURED )
	{
		D3DVertexBuffer_Lock((D3DVertexBuffer *)resEntryHeader->vertexBuffer, 0, 
			0, &lockedVB, D3DLOCK_READONLY);
			
        lockedVB += resEntryHeader->stride * mesh->minVert;

		D3DVertexBuffer_Lock((D3DVertexBuffer *)bumpMapData->tex2, 0, 
			0, &lockedTexVB, 0);
    
        lockedTexVB += bumpMapData->vbStride * mesh->minVert;
        
		for( i = 0; i < (RwUInt32) mesh->numVertices; i++ )
		{
			D3DVECTOR trxNormal;
			float xx,yy;

			offset = sizeof(RwV3d);
			normal = (D3DVECTOR*) (lockedVB + offset);

			RwV3dTransformPoints( (RwV3d*) &trxNormal, (RwV3d*) normal, 1, mat );

			_rwV3dNormalize((RwV3d*) &trxNormal, (RwV3d*) &trxNormal);

			bumpFrame = bumpMapData->frame;
	        if (bumpFrame == NULL)
	        {
		        bumpFrame = RwCameraGetFrame(RwCameraGetCurrentCamera());
			    RWASSERT(bumpFrame);
			}

			/*lightPos = RwMatrixGetPos(RwFrameGetLTM(bumpFrame));*/
			lightPos = RwMatrixGetAt(RwFrameGetLTM(bumpFrame));

			bumpDir.x = lightPos->x + ( trxNormal.x * 5.0f );
			bumpDir.y = lightPos->y + ( trxNormal.y * 5.0f );
			bumpDir.z = lightPos->z + ( trxNormal.z * 5.0f );

			_rwV3dNormalize((RwV3d*) &bumpDir, (RwV3d*) &bumpDir);

			offset += sizeof(RwV3d);
			if( flags & rxGEOMETRY_PRELIT )
			{
				offset += sizeof(RwRGBA);
			}

			texCoords = (RwV2d*) (lockedVB + offset);

			bumpCoords = (RwV2d *)lockedTexVB;

			xx = bumpDir.x * MAX_BUMP_TEXTURE_SHIFT * bumpMapData->coef;
			/*if( xx > MAX_SHIFT ) xx = MAX_SHIFT; 
			if( xx < -MAX_SHIFT ) xx = -MAX_SHIFT;*/
			yy = bumpDir.y * MAX_BUMP_TEXTURE_SHIFT * bumpMapData->coef;
			/*if( yy > MAX_SHIFT ) yy = MAX_SHIFT;
			if( yy < -MAX_SHIFT ) yy = -MAX_SHIFT;*/

			bumpCoords->x = texCoords->x + xx;
			bumpCoords->y = texCoords->y + yy;

			lockedVB += resEntryHeader->stride;
			lockedTexVB += bumpMapData->vbStride;
		}

		D3DVertexBuffer_Unlock( ( D3DVertexBuffer *) bumpMapData->tex2 );

		D3DVertexBuffer_Unlock( ( D3DVertexBuffer *) resEntryHeader->vertexBuffer );
	}


	D3DDevice_SetRenderState(D3DRS_TEXTUREFACTOR, tFactor ); 
        
	RWASSERT(NULL != bumpMapData->texture);
    _rwXbRenderStateTexture(bumpMapData->texture);
	_rwXbMatFXRenderStateTexture(bumpMapData->texture, 1);

	D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    
	D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1 );
	D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
	D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );
	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_ADDSIGNED);
	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE | D3DTA_COMPLEMENT);
	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT );

	D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	D3DDevice_SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	D3DDevice_SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
    /*
     * Pixel shader
     */
    RwXboxSetCurrentPixelShader(mesh->pixelShader);

    /*
     * Vertex shader
     */
	if( flags & rxGEOMETRY_PRELIT )
	{
		RwXboxSetCurrentVertexShader(litBumpFFPShader);
	}
	else
	{
		RwXboxSetCurrentVertexShader(bumpFFPShader);
	}

    D3DDevice_SetStreamSource(1,
                              (D3DVertexBuffer *)bumpMapData->tex2,
                              bumpMapData->vbStride);

    /*
     * Draw the indexed primitive
     */
    D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
                                  mesh->numIndices,
                                  mesh->indexBuffer);

	/*
	 * Now we can do the second pass for the envMap
	 */
	envMapData = MATFXXBOXBUMPENVMAPGETDATA(mesh->material);

    if (envMapData->frame)
    {
        D3DMATRIX   matrix;
        D3DMATRIX   result;
        RwMatrix    *camMtx;
        RwMatrix    *envMtx;
        RwMatrix    *invMtx;
        RwMatrix    *tmpMtx;

        invMtx = RwMatrixCreate();
        tmpMtx = RwMatrixCreate();

        /* Transform the normals back into world space */
        camMtx = RwFrameGetLTM(RwCameraGetFrame(RwCameraGetCurrentCamera()));

        /* Transfrom the normals by the inverse of the env maps frame */
        envMtx = RwFrameGetLTM(envMapData->frame);
        
        RwMatrixInvert(invMtx, envMtx);

        RwMatrixMultiply(tmpMtx, invMtx, camMtx);

        matrix.m[0][0] = -tmpMtx->right.x;
        matrix.m[0][1] = -tmpMtx->right.y;
        matrix.m[0][2] = -tmpMtx->right.z;
        matrix.m[0][3] = 0.0f;

        matrix.m[1][0] = tmpMtx->up.x;
        matrix.m[1][1] = tmpMtx->up.y;
        matrix.m[1][2] = tmpMtx->up.z;
        matrix.m[1][3] = 0.0f;

        matrix.m[2][0] = tmpMtx->at.x;
        matrix.m[2][1] = tmpMtx->at.y;
        matrix.m[2][2] = tmpMtx->at.z;
        matrix.m[2][3] = 0.0f;

        matrix.m[3][0] = 0.0f;
        matrix.m[3][1] = 0.0f;
        matrix.m[3][2] = 0.0f;
        matrix.m[3][3] = 1.0f;

        RwMatrixDestroy(tmpMtx);
        RwMatrixDestroy(invMtx);

        D3DXMatrixMultiply(&result, &matrix, &texMat);

        D3DDevice_SetTransform(D3DTS_TEXTURE0, &result);
    }
    else
    {
        D3DDevice_SetTransform(D3DTS_TEXTURE0, &texMat);
    } 

    /* Generate spheremap texture coords from the position */
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL); 	

	RWASSERT(NULL != envMapData->texture);
    _rwXbRenderStateTexture(envMapData->texture);    
	_rwXbMatFXRenderStateTexture(NULL, 1);	

	/* Scale envmap by coef */
	D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
	D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);

	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);

	/* Modulate with material color */
    D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_MODULATE);
    D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
    D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_DIFFUSE);

	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);

	D3DDevice_SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
	D3DDevice_SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE);

	/* Set the alpha blend */
	D3DDevice_SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	D3DDevice_SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR);
	D3DDevice_SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);

    /* Set the shinnyness */
    shinny = (RwUInt32)(envMapData->coef * 255);
    shinny = (((shinny & 0xFF) << 24) |
              ((shinny & 0xFF) << 16) |
              ((shinny & 0xFF) << 8) |
               (shinny & 0xFF));	

    D3DDevice_SetRenderState(D3DRS_TEXTUREFACTOR, shinny);

    /*
     * Pixel shader
     */
    RwXboxSetCurrentPixelShader(mesh->pixelShader);

    /*
     * Vertex shader
     */
    RwXboxSetCurrentVertexShader(mesh->vertexShader);

    /*
     * Draw the indexed primitive
     */
    D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
                                  mesh->numIndices,
                                  mesh->indexBuffer);

	D3DDevice_SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
	D3DDevice_SetRenderState(D3DRS_SRCBLEND, cacheSrc);
	D3DDevice_SetRenderState(D3DRS_DESTBLEND, cacheDest);

	
	D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
	D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0 );

	D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
	D3DDevice_SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);

    D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    RWRETURNVOID();
}

/****************************************************************************
 rxXBAtomicMatFXEnvRender
 
 Purpose:
 
 On entry:
                
 On exit:
 */

void
rxXbAtomicMatFXEnvRender(RxXboxResEntryHeader *resEntryHeader,
                         RxXboxInstanceData *mesh,
                         rpMatFXMaterialData *matFXData,
                         RwUInt32 flags)
{
    static D3DXMATRIX texMat =
    {
         0.5f, 0.0f, 0.0f, 0.0f,
         0.0f,-0.5f, 0.0f, 0.0f,
         0.0f, 0.0f, 1.0f, 0.0f,
         0.5f, 0.5f, 0.0f, 1.0f
    };
    MatFXEnvMapData *envMapData;
    RwUInt32        shinny;
    RwBool          alpha;

    RWFUNCTION(RWSTRING("rxXBAtomicMatFXEnvRender"));

    alpha = (mesh->material->color.alpha == 0xFF) ? FALSE : TRUE;
    RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)alpha);

	D3DDevice_SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
	D3DDevice_SetRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
	D3DDevice_SetRenderState(D3DRS_COLORVERTEX, TRUE);

    envMapData = MATFXXBOXENVMAPGETDATA(mesh->material);
    if (envMapData->frame)
    {
        D3DMATRIX   matrix;
        D3DMATRIX   result;
        RwMatrix    *camMtx;
        RwMatrix    *envMtx;
        RwMatrix    *invMtx;
        RwMatrix    *tmpMtx;

        invMtx = RwMatrixCreate();
        tmpMtx = RwMatrixCreate();

        /* Transform the normals back into world space */
        camMtx = RwFrameGetLTM(RwCameraGetFrame(RwCameraGetCurrentCamera()));

        /* Transfrom the normals by the inverse of the env maps frame */
        envMtx = RwFrameGetLTM(envMapData->frame);
        
        RwMatrixInvert(invMtx, envMtx);

        RwMatrixMultiply(tmpMtx, invMtx, camMtx);

        matrix.m[0][0] = -tmpMtx->right.x;
        matrix.m[0][1] = -tmpMtx->right.y;
        matrix.m[0][2] = -tmpMtx->right.z;
        matrix.m[0][3] = 0.0f;

        matrix.m[1][0] = tmpMtx->up.x;
        matrix.m[1][1] = tmpMtx->up.y;
        matrix.m[1][2] = tmpMtx->up.z;
        matrix.m[1][3] = 0.0f;

        matrix.m[2][0] = tmpMtx->at.x;
        matrix.m[2][1] = tmpMtx->at.y;
        matrix.m[2][2] = tmpMtx->at.z;
        matrix.m[2][3] = 0.0f;

        matrix.m[3][0] = 0.0f;
        matrix.m[3][1] = 0.0f;
        matrix.m[3][2] = 0.0f;
        matrix.m[3][3] = 1.0f;

        RwMatrixDestroy(tmpMtx);
        RwMatrixDestroy(invMtx);

        D3DXMatrixMultiply(&result, &matrix, &texMat);

        D3DDevice_SetTransform(D3DTS_TEXTURE0, &result);
    }
    else
    {
        D3DDevice_SetTransform(D3DTS_TEXTURE0, &texMat);
    }
        
    /* Generate spheremap texture coords from the position */
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL);

    if ((flags & rxGEOMETRY_TEXTURED) && mesh->material->texture)
    {
        RWASSERT(NULL != envMapData->texture);
        _rwXbRenderStateTexture(envMapData->texture);
        _rwXbMatFXRenderStateTexture(mesh->material->texture, 1);
		_rwXbMatFXRenderStateTexture(mesh->material->texture, 2);

        /* Scale envmap by coef */
        D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
        D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
        D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);

        D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
        D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
        D3DDevice_SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);

        /* Add to base texture */
		D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_ADD);		
        D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
        D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);

        D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG2);
		D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
        D3DDevice_SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);

        /* Modulate with material color */
		D3DDevice_SetTextureStageState(2, D3DTSS_COLOROP,   D3DTOP_MODULATE);
		D3DDevice_SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);
		D3DDevice_SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    }
    else
    {
        _rwXbRenderStateTexture(envMapData->texture);

        /* Scale envmap by coef */
        D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
        D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
        D3DDevice_SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);

        /* Modulate with material color */
        D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_MODULATE);
        D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
        D3DDevice_SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    }

    /* Set the shinnyness */
    shinny = (RwUInt32)(envMapData->coef * 255);
    shinny = (((shinny & 0xFF) << 24) |
              ((shinny & 0xFF) << 16) |
              ((shinny & 0xFF) << 8) |
               (shinny & 0xFF));

    D3DDevice_SetRenderState(D3DRS_TEXTUREFACTOR, shinny);

    /*
     * Pixel shader
     */
    RwXboxSetCurrentPixelShader(mesh->pixelShader);

    /*
     * Vertex shader
     */
    RwXboxSetCurrentVertexShader(mesh->vertexShader);

    /*
     * Draw the indexed primitive
     */
    D3DDevice_DrawIndexedVertices((D3DPRIMITIVETYPE)resEntryHeader->primType,
                                  mesh->numIndices,
                                  mesh->indexBuffer);

    D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(3, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(3, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    RWRETURNVOID();
}

void _rxXbResetStages( void )
{
    D3DDevice_SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_RESULTARG, D3DTA_CURRENT);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_RESULTARG, D3DTA_CURRENT);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_RESULTARG, D3DTA_CURRENT);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);

    D3DDevice_SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE);
    D3DDevice_SetTextureStageState(3, D3DTSS_RESULTARG, D3DTA_CURRENT);
    D3DDevice_SetTextureStageState(3, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    D3DDevice_SetTextureStageState(3, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);
}

void _rxXbSetCachedStates( MatFXXboxStage* stValue, RwInt32 stage )
{
    RWFUNCTION(RWSTRING("_rxXbSetCachedStates"));
    RWASSERT( NULL != stValue );
    RWASSERT( MAXXBOXSTAGES > stage );

    /* alpha */
    if( ( stValue->alphaOp != rpD3DTOP_DISABLE )  && (stValue->colorOp != rpD3DTOP_DISABLE ) )
    {
        /* alpha */
        D3DDevice_SetTextureStageState( stage, D3DTSS_ALPHAOP, stValue->alphaOp );
        D3DDevice_SetTextureStageState( stage, D3DTSS_ALPHAARG0, stValue->alphaArg0 );
        D3DDevice_SetTextureStageState( stage, D3DTSS_ALPHAARG1, stValue->alphaArg1 );
        D3DDevice_SetTextureStageState( stage, D3DTSS_ALPHAARG2, stValue->alphaArg2 );

        /* color */
        D3DDevice_SetTextureStageState( stage, D3DTSS_COLOROP, stValue->colorOp );
        D3DDevice_SetTextureStageState( stage, D3DTSS_COLORARG0, stValue->colorArg0 );
        D3DDevice_SetTextureStageState( stage, D3DTSS_COLORARG1, stValue->colorArg1 );
        D3DDevice_SetTextureStageState( stage, D3DTSS_COLORARG2, stValue->colorArg2 );

        D3DDevice_SetTextureStageState( stage, D3DTSS_RESULTARG, stValue->resultArg );

        D3DDevice_SetTextureStageState( stage, D3DTSS_TEXTURETRANSFORMFLAGS, stValue->textureTransformFlags );

        D3DDevice_SetTextureStageState( stage, D3DTSS_COLORSIGN, stValue->colorSign );

        D3DDevice_SetTextureStageState( stage, D3DTSS_ALPHAKILL, stValue->alphaKill );

        /* bump env factors */
        D3DDevice_SetTextureStageState( stage, D3DTSS_BUMPENVMAT00, (*(RwInt32*) &stValue->bumpEnvMat00 ) );
        D3DDevice_SetTextureStageState( stage, D3DTSS_BUMPENVMAT01, (*(RwInt32*) &stValue->bumpEnvMat01 ) );
        D3DDevice_SetTextureStageState( stage, D3DTSS_BUMPENVMAT10, (*(RwInt32*) &stValue->bumpEnvMat10 ) );
        D3DDevice_SetTextureStageState( stage, D3DTSS_BUMPENVMAT11, (*(RwInt32*) &stValue->bumpEnvMat11 ) );

        D3DDevice_SetTextureStageState( stage, D3DTSS_TEXCOORDINDEX, stValue->texCoordIndex );

        D3DDevice_SetTextureStageState( stage, D3DTSS_BORDERCOLOR, stValue->borderColor );

        /* Set the tex transform matrix */

        D3DDevice_SetTransform( _rxXbTexTrxTable[stage],(const D3DMATRIX*) &stValue->texTransformMatrix );
    }

    RWRETURNVOID();
}

void _rxXbSetCachedMiscStates( MatFXXboxMiscState* miscState )
{
   RWFUNCTION(RWSTRING("_rxXbSetCachedMiscStates"));
   RWASSERT( NULL != miscState );

   D3DDevice_SetRenderState( D3DRS_TEXTUREFACTOR, miscState->textureFactor );
   RWRETURNVOID();
}

void _rxXbSetCachedFinalBlend( MatFXXboxFinalBlend* finalBlend )
{
    RWFUNCTION(RWSTRING("_rxXbSetCachedFinalBlend"));
    RWASSERT( NULL != finalBlend );

    D3DDevice_SetRenderState( D3DRS_SRCBLEND, finalBlend->srcBlend );
    D3DDevice_SetRenderState( D3DRS_DESTBLEND, finalBlend->dstBlend );

    RWRETURNVOID();
}

/****************************************************************************
 rxXBAtomicMatFXRenderCallback
 
 Purpose:
 
 On entry:
                
 On exit:
 */
void
rxXbAtomicMatFXRenderCallback(RxXboxResEntryHeader *resEntryHeader,
                              void *object,
                              RwUInt8 type,
                              RwUInt32 flags)
{
    RxXboxInstanceData      *mesh;
    rpMatFXMaterialData     *matFXData;
	RwMatrix				transform;
	DWORD					effect;

    RWFUNCTION(RWSTRING("rxXbAtomicMatFXRenderCallback"));

    if (type == rpATOMIC)
    {
    	transform = *RwFrameGetLTM(RpAtomicGetFrame(object));
    }
    else
    {
        RwMatrixSetIdentity( &transform );
    }

    if (flags & rxGEOMETRY_PRELIT)
    {
        /* Emmisive color from the vertex colors */
        D3DDevice_SetRenderState(D3DRS_COLORVERTEX, TRUE);
        D3DDevice_SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
    }
    else
    {
        /* Emmisive color from material, set to black in the submit node */
        D3DDevice_SetRenderState(D3DRS_COLORVERTEX, FALSE);
        D3DDevice_SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
    }

    /*
     * Set the stream source - all meshes in object are bunged in the vertex buffer
     */
    D3DDevice_SetStreamSource(0,
                              (D3DVertexBuffer *)resEntryHeader->vertexBuffer,
                              resEntryHeader->stride);

    for (mesh = resEntryHeader->begin; mesh != resEntryHeader->end; mesh++)
    {
        if (flags & rxGEOMETRY_LIGHT)
        {
            static D3DMATERIAL8 material =
            {
                {0.0f, 0.0f, 0.0f, 1.0f}, /* Diffuse */
                {0.0f, 0.0f, 0.0f, 1.0f}, /* Ambient */
                {0.0f, 0.0f, 0.0f, 1.0f}, /* Specular */
                {0.0f, 0.0f, 0.0f, 1.0f}, /* Emissive */
                0.0f /* Power */
            };

            if (flags & rxGEOMETRY_MODULATE)
            {
                RwReal  ambScale;
                RwReal  diffScale;
                RwRGBA  *color;

                ambScale = mesh->material->surfaceProps.ambient * COLORSCALAR;
                diffScale = mesh->material->surfaceProps.diffuse * COLORSCALAR;
                color = &mesh->material->color;

                material.Diffuse.r = color->red * diffScale;
                material.Diffuse.g = color->green * diffScale;
                material.Diffuse.b = color->blue * diffScale;
                material.Diffuse.a = color->alpha * diffScale;

                material.Ambient.r = color->red * ambScale;
                material.Ambient.g = color->green * ambScale;
                material.Ambient.b = color->blue * ambScale;
                material.Ambient.a = color->alpha * ambScale;
            }
            else
            {
                material.Diffuse.r = mesh->material->surfaceProps.diffuse;
                material.Diffuse.g = material.Diffuse.r;
                material.Diffuse.b = material.Diffuse.r;
                material.Diffuse.a = material.Diffuse.r;

                material.Ambient.r = mesh->material->surfaceProps.ambient;
                material.Ambient.g = material.Ambient.r;
                material.Ambient.b = material.Ambient.r;
                material.Ambient.a = material.Ambient.r;
            }

            D3DDevice_SetMaterial(&material);
        }

        /*
         * Render
         */
        matFXData = *MATFXMATERIALGETDATA(mesh->material);
        if (matFXData == NULL)
		{
			effect = rpMATFXEFFECTNULL;
		}
		else
		{
			effect = matFXData->flags;
		}
        switch (effect)
        {
        case rpMATFXEFFECTENVMAP:
			rxXbAtomicMatFXEnvRender(resEntryHeader, mesh, matFXData, flags);
            break;

		case rpMATFXEFFECTBUMPMAP:
			rxXbAtomicMatFXBumpMapRender(resEntryHeader, mesh, matFXData, flags, &transform);
			break;

		case rpMATFXEFFECTDUAL:
			rxXbAtomicMatFXDualPassRender(resEntryHeader, mesh, matFXData, flags);
			break;

		case rpMATFXEFFECTBUMPENVMAP:
			rxXbAtomicMatFXBumpEnvMapRender(resEntryHeader, mesh, matFXData, flags, &transform);
			break;

        default:
            rxXbAtomicMatFXDefaultRender(resEntryHeader, mesh, matFXData, flags);
            break;
        }

        #ifdef RWMETRICS        
        _rwXbMetricsInc( mesh->numVertices, mesh->numIndices, resEntryHeader->primType );
        #endif /* RWMETRICS */
    }

    /* Count up things drawn for metrics builds */
    #ifdef RWMETRICS
    {
        RwInt32 geometricTris = 0;
        if (type == rpATOMIC)
        {
            RpAtomic *atom = (RpAtomic *)object;
            geometricTris = RpGeometryGetNumTriangles( RpAtomicGetGeometry( atom ) );
        }
        else
        {
            RpWorldSector *sector = (RpWorldSector *)object;
            geometricTris = RpWorldSectorGetNumPolygons( sector );
        }

        RWSRCGLOBAL(metrics)->numTriangles += geometricTris;
    }
    #endif /* RWMETRICS */

    RWRETURNVOID();
}

/****************************************************************************
 rpAtomicMatFxPipelineCreate
 
 Purpose:
 
 On entry:
                
 On exit:
 */
RxPipeline *
rpAtomicMatFxPipelineCreate(void)
{
    RxPipeline  *pipe;

    RWFUNCTION(RWSTRING("rpAtomicMatFxPipelineCreate"));

    pipe = RxPipelineCreate();
    if (pipe)
    {
        RxLockedPipe    *lpipe;

        pipe->pluginId = rwID_MATERIALEFFECTSPLUGIN;

        lpipe = RxPipelineLock(pipe);
        if (NULL != lpipe)
        {
            RxNodeDefinition    *instanceNode;

            /*
             * Get the instance node definition
             */
            instanceNode = RxNodeDefinitionGetXboxAtomicAllInOne();

            /*
             * Add the node to the pipeline
             */
            lpipe = RxLockedPipeAddFragment(lpipe, NULL, instanceNode, NULL);

            /*
             * Unlock the pipeline
             */
            lpipe = RxLockedPipeUnlock(lpipe);

            RWRETURN(pipe);
        }

        RxPipelineDestroy(pipe);
    }

    RWRETURN(NULL);
}


/****************************************************************************
 rpWorldSectorMatFxPipelineCreate
 
 Purpose:
 
 On entry:
                
 On exit:
 */
RxPipeline *
rpWorldSectorMatFxPipelineCreate(void)
{
    RxPipeline  *pipe;

    RWFUNCTION(RWSTRING("rpWorldSectorMatFxPipelineCreate"));

    pipe = RxPipelineCreate();
    if (pipe)
    {
        RxLockedPipe    *lpipe;

        pipe->pluginId = rwID_MATERIALEFFECTSPLUGIN;

        lpipe = RxPipelineLock(pipe);
        if (NULL != lpipe)
        {
            RxNodeDefinition    *instanceNode;

            /*
             * Get the instance node definition
             */
            instanceNode = RxNodeDefinitionGetXboxWorldSectorAllInOne();

            /*
             * Add the node to the pipeline
             */
            lpipe = RxLockedPipeAddFragment(lpipe, NULL, instanceNode, NULL);

            /*
             * Unlock the pipeline
             */
            lpipe = RxLockedPipeUnlock(lpipe);

            RWRETURN(pipe);
        }

        RxPipelineDestroy(pipe);
    }

    RWRETURN(NULL);
}

RwBool
_rpMatFXPipelinesCreate(void)
{
    RxNodeDefinition    *instanceNode;
    RxPipelineNode      *node;

    RWFUNCTION(RWSTRING("_rpMatFXPipelinesCreate"));

    MatFXAtomicPipe = rpAtomicMatFxPipelineCreate();
    RWASSERT(NULL != MatFXAtomicPipe);

    /*
     * Get the instance node definition
     */
    instanceNode = RxNodeDefinitionGetXboxAtomicAllInOne();
    RWASSERT(NULL != instanceNode);

    /*
     * Set the pipeline specific data
     */
    node = RxPipelineFindNodeByName(MatFXAtomicPipe, instanceNode->name, NULL, NULL);
    RWASSERT(NULL != node);

	/*
	 * Save the object pipeline for later use
	 */
	instancePipeline = _rxXboxAllInOneGetInstanceCallBack(node);
	RWASSERT( NULL != instancePipeline );

    /*
     * Set the bumpmap object pipeline
     */
    _rxXboxAllInOneSetInstanceCallBack(node, rxXbAtomicMatFXInstanceCallback);

    /*
     * Set the MatFX render callback
     */
    RxXboxAllInOneSetRenderCallBack(node, rxXbAtomicMatFXRenderCallback);

    /* 
     * And likewise for world sectors:
     */
    MatFXWorldSectorPipe = rpWorldSectorMatFxPipelineCreate();
    RWASSERT(NULL != MatFXWorldSectorPipe);

    instanceNode = RxNodeDefinitionGetXboxWorldSectorAllInOne();
    RWASSERT(NULL != instanceNode);

    node = RxPipelineFindNodeByName(MatFXWorldSectorPipe, instanceNode->name, NULL, NULL);
    RWASSERT(NULL != node);

 	worldInstancePipeline = _rxXboxAllInOneGetInstanceCallBack(node);
	RWASSERT( NULL != worldInstancePipeline );

    _rxXboxAllInOneSetInstanceCallBack(node, rxXbWorldSectorMatFXInstanceCallback);

    RxXboxAllInOneSetRenderCallBack(node, rxXbAtomicMatFXRenderCallback);

	/*
	 * Create the bump map FFP shaders
	 */
	D3DDevice_CreateVertexShader( bumpDecl, NULL, &bumpFFPShader, 0 );
	D3DDevice_CreateVertexShader( litBumpDecl, NULL, &litBumpFFPShader, 0 );
    RWRETURN(TRUE);
}

RwBool
_rpMatFXPipelinesDestroy(void)
{
    RWFUNCTION(RWSTRING("_rpMatFXPipelinesDestroy"));

	/*
	 * Delete the vertex shaders
	 */
	D3DDevice_DeleteVertexShader( litBumpFFPShader );
	D3DDevice_DeleteVertexShader( bumpFFPShader );

    if (MatFXAtomicPipe)
    {
        RxPipelineDestroy(MatFXAtomicPipe);
        MatFXAtomicPipe = NULL;
    }

    if (MatFXWorldSectorPipe)
    {
        RxPipelineDestroy(MatFXWorldSectorPipe);
        MatFXWorldSectorPipe = NULL;
    }

    RWRETURN(TRUE);
}

RpWorldSector *
_rpMatFXPipelineWorldSectorSetup(RpWorldSector *worldSector)
{
    RWFUNCTION(RWSTRING("_rpMatFXPipelineWorldSectorSetup"));
    RWASSERT(worldSector);

    RpWorldSectorSetPipeline( worldSector, MatFXWorldSectorPipe );

    RWRETURN(worldSector);
}

/*--- Enable effects --------------------------------------------------------*/
RpMaterial *
_rpMatFXEnvMapEnable(RpMaterial *material)
{
    RWFUNCTION(RWSTRING("_rpMatFXEnvMapEnable"));

    RWRETURN(material);
}

RpMaterial *
_rpMatFXBumpMapEnable(RpMaterial *material)
{
    RWFUNCTION(RWSTRING("_rpMatFXBumpMapEnable"));

    RWRETURN(material);
}

RpMaterial *
_rpMatFXDualEnable(RpMaterial *material)
{
    RWFUNCTION(RWSTRING("_rpMatFXDualEnable"));

    RWRETURN(material);
}

/*--- Upload texture --------------------------------------------------------*/

/*--- Device data fucntions -------------------------------------------------*/
RwBool
_rpMatFXSetupDualRenderState(MatFXDualData *dualData,
                             RwRenderState __RWUNUSED__ nState)
{
    RWFUNCTION(RWSTRING("_rpMatFXSetupDualRenderState"));
    RWASSERT(dualData);
    RWRETURN(TRUE);
}

/*--- Attach pipelines ------------------------------------------------------*/
RpAtomic *
_rpMatFXPipelineAtomicSetup(RpAtomic *atomic)
{
    RWFUNCTION(RWSTRING("_rpMatFXPipelineAtomicSetup"));
    RWASSERT(atomic);

    RpAtomicSetPipeline(atomic, MatFXAtomicPipe);

    RWRETURN(atomic);
}

