/****************************************************************************
 *                                                                          *
 *  Module  :   xboxmatbl.c                                                 *
 *                                                                          *
 *  Purpose :   Matrix-blending pipeline (xbox)                             *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 Includes
 */

#include <xtl.h>
#include <d3d8.h>

#include <rpcriter.h>
#include "rpplugin.h"
#include <rpdbgerr.h>

#include <rwcore.h>
#include <rpworld.h>
#include <rprandom.h>
#include <rpspline.h>
#include <rpskin.h>

#include "skinpriv.h"
#include "genmatbl.h"

/* Pre compiled vertex shader */
#include "vertdefs.h"
#include "vertshdr.h"

#if (!defined(DOXYGEN_SHOULD_SKIP_THIS))
static const char   rcsid[] _RWUNUSED_ =
    "@@(#)$Id: xboxmatbl.c,v 1.3 2001/05/01 10:50:11 johns Exp $";
#endif /* (!defined(DOXYGEN_SHOULD_SKIP_THIS)) */

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

/****************************************************************************
 Local (Static) Prototypes
 */

/****************************************************************************
 Local Defines
 */

#define NUM_ROWS                3
#define MAX_SKIN_MATRICES       41 /* ((96 + 36 - 4 - 3 - 2) / 3) */
#define NUM_MATRICES_ELEMENTS   492 /* MAX_SKIN_MATRICES * 4 * 3 */

#define NUM_CONSTANTS_CACHED    (VSCONST_REG_BASE * -4)

#define IGNORED_XBOX            0

#define FASTSKIN

/****************************************************************************
 Globals (across program)
 */

extern RwInt32      RpSkinAtomicSkinOffset;

/****************************************************************************
 Local (static) Globals
 */

static RxPipeline  *SkinPipeline = NULL;
static RxXBoxAllInOneRenderCallBack RenderPipeline = NULL;

static RwReal       BoneWorldMatrices[NUM_MATRICES_ELEMENTS];

static DWORD        SkinVertexShaderDecl[] = {
    D3DVSD_STREAM(0),
    D3DVSD_REG(VSD_REG_POS, D3DVSDT_FLOAT3), /* Position */
    D3DVSD_REG(VSD_REG_WEIGHTS, D3DVSDT_FLOAT4), /* Weights */
    D3DVSD_REG(VSD_REG_INDICES, D3DVSDT_SHORT4), /* Indices */
    D3DVSD_REG(VSD_REG_NORMAL, D3DVSDT_FLOAT3), /* Normals */
    //D3DVSD_REG( VSD_REG_COLOR,     D3DVSDT_D3DCOLOR ),   /* Diffuse color */
    D3DVSD_REG(VSD_REG_TEXCOORDS, D3DVSDT_FLOAT2), /* Texture coordinates */
    D3DVSD_END()
};
static DWORD        SkinVertexShader;

static RwReal       ConstantDataCache[NUM_CONSTANTS_CACHED];
static RwReal       LightDirCol[8] = /* Direction & Color */
{
    0.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 0.0f
};

static RwReal       AmbientLightColor[4] = {
    0.0f, 0.0f, 0.0f, 0.0f
};

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************/

/****************************************************************************/
#if defined(FASTSKIN)

/****************************************************************************
 _rxXbAtomicSkinLightingCallback

 Purpose:

 On entry:
 .
 On exit :
*/
void
_rxXbAtomicSkinLightingCallback(void *object)
{
    RpAtomic           *atomic;
    RpGeometryFlag      flags;
    RwBool              ambient = FALSE;
    RwBool              directional = FALSE;

    RWFUNCTION(RWSTRING("_rxXbAtomicSkinLightingCallback"));

    atomic = (RpAtomic *) object;
    flags = RpGeometryGetFlags(RpAtomicGetGeometry(atomic));

    if ((flags & rxGEOMETRY_LIGHT) && (NULL != RWSRCGLOBAL(curWorld)))
    {
        RwLLLink           *cur, *end;
        RpWorld            *world;

        world = (RpWorld *) RWSRCGLOBAL(curWorld);

        cur = rwLinkListGetFirstLLLink(&world->directionalLightList);
        end = rwLinkListGetTerminator(&world->directionalLightList);
        while (cur != end)
        {
            RpLight            *light;

            light = rwLLLinkGetData(cur, RpLight, inWorld);

            /* NB light may actually be a dummyTie from a enclosing ForAll */
            if (light && (rwObjectTestFlags(light, flags)))
            {
                if ((!directional)
                    && (RpLightGetType(light) == rpLIGHTDIRECTIONAL))
                {
                    RwV3d              *at;
                    const RwRGBAReal   *color;

                    /* Set the lights direction */
                    at = RwMatrixGetAt(RwFrameGetLTM
                                       (RpLightGetFrame(light)));

                    LightDirCol[0] = at->x;
                    LightDirCol[1] = at->y;
                    LightDirCol[2] = at->z;
                    LightDirCol[3] = 0.0f; /* Use this for clamping */

                    /* Set the light color */
                    color = RpLightGetColor(light);

                    LightDirCol[4] = color->red;
                    LightDirCol[5] = color->green;
                    LightDirCol[6] = color->blue;
                    LightDirCol[7] = color->alpha;

                    directional = TRUE;
                }
                else if (!ambient)
                {
                    const RwRGBAReal   *color;

                    color = RpLightGetColor(light);

                    AmbientLightColor[0] = color->red;
                    AmbientLightColor[1] = color->green;
                    AmbientLightColor[2] = color->blue;
                    AmbientLightColor[3] = color->alpha;

                    ambient = TRUE;
                }

                if (directional && ambient)
                {
                    RWRETURNVOID();
                }
            }

            /* Next */
            cur = rwLLLinkGetNext(cur);
        }
    }

    if (!ambient)
    {
        AmbientLightColor[0] = 0.0f;
        AmbientLightColor[1] = 0.0f;
        AmbientLightColor[2] = 0.0f;
        AmbientLightColor[3] = 0.0f;
    }

    if (!directional)
    {
        LightDirCol[0] = 0.0f;
        LightDirCol[1] = 0.0f;
        LightDirCol[2] = 0.0f;
        LightDirCol[3] = 0.0f;

        LightDirCol[4] = 0.0f;
        LightDirCol[5] = 0.0f;
        LightDirCol[6] = 0.0f;
        LightDirCol[7] = 0.0f;
    }

    RWRETURNVOID();
}

/****************************************************************************
 rxXBAtomicSkinInstanceCallback

 Purpose:   To instance.

 On entry:

 On exit :
*/
static              RwBool
rxXBAtomicSkinInstanceCallback(void *object,
                               RxXBoxInstanceData * instancedData,
                               RwBool reinstance)
{
    RpAtomic           *atomic;
    RpGeometry         *geometry;
    RpSkin             *skin;
    RwUInt32            vbSize;
    RwUInt32            offset;
    RpGeometryFlag      flags;
    RwUInt32            numVertices;
    RwUInt8            *lockedVertexBuffer;
    RwUInt8            *vertexBuffer;
    RwV3d              *pos;
    RwInt32            *matrixIndexMap;
    RwInt32            *vertexIndexMap;

    RWFUNCTION(RWSTRING("rxXBAtomicSkinInstanceCallback"));

    atomic = (RpAtomic *) object;
    geometry = RpAtomicGetGeometry(atomic);
    flags = RpGeometryGetFlags(geometry);

    skin = *RPSKINATOMICGETDATA(atomic);

    vertexIndexMap = (RwInt32 *) skin->pPlatformData;
    matrixIndexMap = &vertexIndexMap[256];

    /*
     * Calculate the stride of the vertex
     */

    /* Position */
    instancedData->stride = sizeof(RwV3d);

    /* Weights */
    instancedData->stride += sizeof(RwReal) * 4;

    /* Indices */
    instancedData->stride += sizeof(RwInt16) * 4;

    /* Normals */
    if (flags & rxGEOMETRY_NORMALS)
    {
        instancedData->stride += sizeof(RwV3d);
    }

    /* Pre-lighting */
    if (flags & rxGEOMETRY_PRELIT)
    {
        instancedData->stride += sizeof(RwUInt32);
    }

    /* Texture coordinates */
    if (flags & rxGEOMETRY_TEXTURED)
    {
        instancedData->stride += sizeof(RwTexCoords);
    }

    /*
     * Create the vertex buffer
     */
    vbSize = instancedData->stride * instancedData->numVertices;

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

    /*
     * Lock the vertex buffer
     */
    D3DVertexBuffer_Lock((D3DVertexBuffer *) instancedData->
                         vertexBuffer1, 0, vbSize, &lockedVertexBuffer,
                         0);

    /*
     * Instance
     */

    /* Positions */
    pos = &geometry->morphTarget[0].verts[instancedData->minVert];

    vertexBuffer = lockedVertexBuffer;
    numVertices = instancedData->numVertices;
    while (numVertices--)
    {
        *((RwV3d *) vertexBuffer) = *pos;
        pos++;

        vertexBuffer += instancedData->stride;
    }

    offset = sizeof(RwV3d);

    /*
     * Weights
     */
    {
        RwMatrixWeights    *weights;

        weights = &skin->pMatrixWeightsMap[instancedData->minVert];

        vertexBuffer = lockedVertexBuffer + offset;
        numVertices = instancedData->numVertices;
        while (numVertices--)
        {
            *((RwMatrixWeights *) vertexBuffer) = *weights;
            weights++;

            vertexBuffer += instancedData->stride;
        }

        offset += sizeof(RwReal) * 4;
    }

    /* Indices */
    {
        RwUInt32           *indices;

        indices = &skin->pMatrixIndexMap[instancedData->minVert];

        vertexBuffer = lockedVertexBuffer + offset;
        numVertices = instancedData->numVertices;
        while (numVertices--)
        {
            RwUInt32            index;

            index = *indices;

            *(((RwInt16 *) vertexBuffer) + 0) =
                (((RwInt16) vertexIndexMap[((index) & 0xFF)]) *
                 NUM_ROWS);
            *(((RwInt16 *) vertexBuffer) + 1) =
                (((RwInt16) vertexIndexMap[((index >> 8) & 0xFF)]) *
                 NUM_ROWS);
            *(((RwInt16 *) vertexBuffer) + 2) =
                (((RwInt16) vertexIndexMap[((index >> 16) & 0xFF)]) *
                 NUM_ROWS);
            *(((RwInt16 *) vertexBuffer) + 3) =
                (((RwInt16) vertexIndexMap[((index >> 24) & 0xFF)]) *
                 NUM_ROWS);

            indices++;

            vertexBuffer += instancedData->stride;
        }

        offset += sizeof(RwInt16) * 4;
    }

    /* Normals */
    if (flags & rxGEOMETRY_NORMALS)
    {
        RwV3d              *normal;

        normal =
            &geometry->morphTarget[0].normals[instancedData->minVert];

        vertexBuffer = lockedVertexBuffer + offset;
        numVertices = instancedData->numVertices;
        while (numVertices--)
        {
            *((RwV3d *) vertexBuffer) = *normal;
            vertexBuffer += instancedData->stride;
            normal++;
        }

        offset += sizeof(RwV3d);
    }

    /* Pre-lighting */
    if (flags & rxGEOMETRY_PRELIT)
    {
        RwRGBA             *color;

        color = &geometry->preLitLum[instancedData->minVert];

        vertexBuffer = lockedVertexBuffer + offset;
        numVertices = instancedData->numVertices;
        while (numVertices--)
        {
            *((RwUInt32 *) vertexBuffer) = ((color->alpha << 24) |
                                            (color->red << 16) |
                                            (color->green << 8) |
                                            (color->blue));
            vertexBuffer += instancedData->stride;
            color++;
        }

        offset += sizeof(RwUInt32);
    }

    /* Texture coordinates */
    if (flags & rxGEOMETRY_TEXTURED)
    {
        RwTexCoords        *texCoord;

        texCoord = &geometry->texCoords[0][instancedData->minVert];

        vertexBuffer = lockedVertexBuffer + offset;
        numVertices = instancedData->numVertices;
        while (numVertices--)
        {
            *((RwTexCoords *) vertexBuffer) = *texCoord;
            texCoord++;

            vertexBuffer += instancedData->stride;
        }
    }

    /*
     * Unlock the vertex buffer
     */
    D3DVertexBuffer_Unlock((D3DVertexBuffer *) instancedData->
                           vertexBuffer1);

    /*
     * Set the vertex shader flags
     */
    instancedData->vertexShader = SkinVertexShader;

    RWRETURN(TRUE);
}
#endif /* defined(FASTSKIN) */

/****************************************************************************
 rpSkinMatrixUpdate

 Inputs :

 Outputs:
 */
static void
rpSkinMatrixUpdate(RpAtomic * atomic)
{
    RpSkin             *skin;
    RwMatrix           *matrixArray = NULL;

    RWFUNCTION(RWSTRING("rpSkinMatrixUpdate"));
    RWASSERT(NULL != atomic);

    skin = *RPSKINATOMICGETDATA(atomic);
    RWASSERT(NULL != skin);

    matrixArray = RpSkinAtomicGlobals.SkinMatrixCache;

    if (skin->pCurrentHierarchy->flags & rpHANIMHIERARCHYNOMATRICES)
    {
        RwMatrix            inverseAtomicLTM;
        RwMatrix            tempMatrix;
        RwInt32             i;

        RwMatrixInvert(&inverseAtomicLTM,
                       RwFrameGetLTM(RpAtomicGetFrame(atomic)));

        for (i = 0; i < skin->pCurrentHierarchy->numFrames; i++)
        {
            RwFrame            *pFrame =
                skin->pCurrentHierarchy->pFrameInfo[i].pFrame;
            RwMatrixMultiply(&tempMatrix,
                             &skin->pBoneInfo[i].boneToSkinMat,
                             RwFrameGetLTM(pFrame));
            RwMatrixMultiply(&matrixArray[i], &tempMatrix,
                             &inverseAtomicLTM);
        }
    }
    else
    {
        if (skin->pCurrentHierarchy->
            flags & rpHANIMHIERARCHYLOCALSPACEMATRICES)
        {
            RwInt32             i;

            for (i = 0; i < skin->pCurrentHierarchy->numFrames; i++)
            {
                RwMatrixMultiply(&matrixArray[i],
                                 &skin->pBoneInfo[i].boneToSkinMat,
                                 &skin->pCurrentHierarchy->
                                 pMatrixArray[i]);
            }
        }
        else
        {
            RwMatrix            inverseAtomicLTM;
            RwMatrix            tempMatrix;
            RwInt32             i;

            RwMatrixInvert(&inverseAtomicLTM,
                           RwFrameGetLTM(RpAtomicGetFrame(atomic)));

            for (i = 0; i < skin->pCurrentHierarchy->numFrames; i++)
            {
                RwMatrixMultiply(&tempMatrix,
                                 &skin->pBoneInfo[i].boneToSkinMat,
                                 &skin->pCurrentHierarchy->
                                 pMatrixArray[i]);
                RwMatrixMultiply(&matrixArray[i], &tempMatrix,
                                 &inverseAtomicLTM);
            }
        }
    }

    RWRETURNVOID();
}

void
_rxXBAtomicSkinRenderCallback(RwResEntry * repEntry,
                              void *object, RwUInt8 type,
                              RwUInt32 flags)
{
    RpAtomic           *atomic = (RpAtomic *) object;
    RpSkin             *skin;
    RwCamera           *camera;
    RwMatrix           *camLTM;
    RwMatrix           *atomicLTM;
    RwMatrix           *matrixArray;
    RwReal             *val;
    RwMatrix            invCamMtx;
    RwInt32             i;
    RwInt32            *matrixIndexMap;
    RwInt32            *vertexIndexMap;
    D3DMATRIX           projMatrix;
    D3DMATRIX           viewMatrix;
    D3DMATRIX           worldMatrix;
    D3DMATRIX           destMatrix;
    D3DMATRIX           tmpMatrix;

    RWFUNCTION(RWSTRING("_rxXBAtomicSkinRenderCallback"));

    camera = RwCameraGetCurrentCamera();

    /*
     * Projection matrix
     */
    projMatrix.m[0][0] = camera->recipViewWindow.x;
    projMatrix.m[0][1] = 0.0f;
    projMatrix.m[0][2] = 0.0f;
    projMatrix.m[0][3] = 0.0f;

    projMatrix.m[1][0] = 0.0f;
    projMatrix.m[1][1] = camera->recipViewWindow.y;
    projMatrix.m[1][2] = 0.0f;
    projMatrix.m[1][3] = 0.0f;

    projMatrix.m[2][0] = 0.0f;
    projMatrix.m[2][1] = 0.0f;
    projMatrix.m[2][2] =
        camera->farPlane / (camera->farPlane - camera->nearPlane);
    projMatrix.m[2][3] = -projMatrix.m[2][2] * camera->nearPlane;

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

    /*
     * View matrix - (camera matrix)
     */
    camLTM = RwFrameGetLTM(RwCameraGetFrame(camera));

    RwMatrixSetIdentity(&invCamMtx);
    RwMatrixInvert(&invCamMtx, camLTM);

    viewMatrix.m[0][0] = -invCamMtx.right.x;
    viewMatrix.m[0][1] = -invCamMtx.up.x;
    viewMatrix.m[0][2] = -invCamMtx.at.x;
    viewMatrix.m[0][3] = -invCamMtx.pos.x;

    viewMatrix.m[1][0] = invCamMtx.right.y;
    viewMatrix.m[1][1] = invCamMtx.up.y;
    viewMatrix.m[1][2] = invCamMtx.at.y;
    viewMatrix.m[1][3] = invCamMtx.pos.y;

    viewMatrix.m[2][0] = invCamMtx.right.z;
    viewMatrix.m[2][1] = invCamMtx.up.z;
    viewMatrix.m[2][2] = invCamMtx.at.z;
    viewMatrix.m[2][3] = invCamMtx.pos.z;

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

    /*
     * World space transformation matrix
     */
    atomicLTM = RwFrameGetLTM(RpAtomicGetFrame((RpAtomic *) object));

    worldMatrix.m[0][0] = atomicLTM->right.x;
    worldMatrix.m[0][1] = atomicLTM->up.x;
    worldMatrix.m[0][2] = atomicLTM->at.x;
    worldMatrix.m[0][3] = atomicLTM->pos.x;

    worldMatrix.m[1][0] = atomicLTM->right.y;
    worldMatrix.m[1][1] = atomicLTM->up.y;
    worldMatrix.m[1][2] = atomicLTM->at.y;
    worldMatrix.m[1][3] = atomicLTM->pos.y;

    worldMatrix.m[2][0] = atomicLTM->right.z;
    worldMatrix.m[2][1] = atomicLTM->up.z;
    worldMatrix.m[2][2] = atomicLTM->at.z;
    worldMatrix.m[2][3] = atomicLTM->pos.z;

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

    D3DXMatrixMultiply(&tmpMatrix, &viewMatrix, &worldMatrix);
    D3DXMatrixMultiply(&destMatrix, &projMatrix, &tmpMatrix);

    /*
     * Switch to 192 constant mode
     */
    D3DDevice_SetShaderConstantMode(D3DSCM_192CONSTANTS);

    /*
     * Cache the constant register
     */
    D3DDevice_GetVertexShaderConstant(VSCONST_REG_BASE,
                                      ConstantDataCache,
                                      -VSCONST_REG_BASE);

    /*
     * Set the constant registers with the combined
     * world, camera & projection matrix
     */
    D3DDevice_SetVertexShaderConstant(VSCONST_REG_TRANSFORM_OFFSET,
                                      (void *) &destMatrix,
                                      VSCONST_REG_TRANSFORM_SIZE);

    D3DDevice_SetVertexShaderConstant
        (VSCONST_REG_WORLD_TRANSFORM_OFFSET, (void *) &worldMatrix,
         VSCONST_REG_WORLD_TRANSFORM_SIZE);

    /* Upload the light data */
    D3DDevice_SetVertexShaderConstant(VSCONST_REG_DIR_LIGHT_OFFSET,
                                      (void *) LightDirCol,
                                      VSCONST_REG_DIR_LIGHT_SIZE);

    skin = *RPSKINATOMICGETDATA(atomic);

    /*
     * Update the vertex matrices
     */
    if (skin->pCurrentHierarchy)
    {
        rpSkinMatrixUpdate(atomic);
    }

    /*
     * Get the vertex matrices
     */
    if (skin->pCurrentSkeleton)
    {
        matrixArray = skin->pCurrentSkeleton->pMatrixArray;
    }
    else if (skin->pCurrentHierarchy)
    {
        matrixArray = RpSkinAtomicGlobals.SkinMatrixCache;
    }

    val = BoneWorldMatrices;

    vertexIndexMap = (RwInt32 *) skin->pPlatformData;
    matrixIndexMap = &vertexIndexMap[256];

    for (i = 0; i < matrixIndexMap[256]; i++)
    {
        *val = matrixArray[matrixIndexMap[i]].right.x;
        val++;
        *val = matrixArray[matrixIndexMap[i]].up.x;
        val++;
        *val = matrixArray[matrixIndexMap[i]].at.x;
        val++;
        *val = matrixArray[matrixIndexMap[i]].pos.x;
        val++;

        *val = matrixArray[matrixIndexMap[i]].right.y;
        val++;
        *val = matrixArray[matrixIndexMap[i]].up.y;
        val++;
        *val = matrixArray[matrixIndexMap[i]].at.y;
        val++;
        *val = matrixArray[matrixIndexMap[i]].pos.y;
        val++;

        *val = matrixArray[matrixIndexMap[i]].right.z;
        val++;
        *val = matrixArray[matrixIndexMap[i]].up.z;
        val++;
        *val = matrixArray[matrixIndexMap[i]].at.z;
        val++;
        *val = matrixArray[matrixIndexMap[i]].pos.z;
        val++;
    }

    D3DDevice_SetVertexShaderConstant(VSCONST_REG_MATRIX_OFFSET,
                                      (void *) BoneWorldMatrices,
                                      matrixIndexMap[256] * NUM_ROWS);

    D3DDevice_SetVertexShaderConstant(95, &AmbientLightColor, 1);

    /*
     * Render the object
     */
    RenderPipeline(repEntry, object, type, flags);

    /*
     * Cache the constant register
     */
    D3DDevice_SetVertexShaderConstant(VSCONST_REG_BASE,
                                      ConstantDataCache,
                                      -VSCONST_REG_BASE);

    /*
     * Switch to 96 constant mode
     */
    D3DDevice_SetShaderConstantMode(D3DSCM_96CONSTANTS);

    RWRETURNVOID();
}

/****************************************************************************
 _rpSkinMBPipelineDestroy

 Purpose:

 On entry:

 On exit:
 */
void
_rpSkinMBPipelineDestroy(void)
{
    RWFUNCTION(RWSTRING("_rpSkinMBPipelineDestroy"));

    D3DDevice_DeleteVertexShader(SkinVertexShader);

    RxPipelineDestroy(SkinPipeline);
    SkinPipeline = NULL;

    RWRETURNVOID();
}

/****************************************************************************
 _rpAtomicSkinPipelineCreate

 Purpose:

 On entry:

 On exit:
 */
RxPipeline         *
_rpAtomicSkinPipelineCreate(void)
{
    RxPipeline         *pipe;

    RWFUNCTION(RWSTRING("_rpAtomicSkinPipelineCreate"));

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

        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);
}

/****************************************************************************
 _rpSkinMBPluginAttach

 Purpose:

 On entry:

 On exit:
 */
RwBool
_rpSkinMBPluginAttach(void)
{
#if defined(FASTSKIN)
    RxNodeDefinition   *instanceNode;
    RxPipelineNode     *node;
#endif /* defined(FASTSKIN) */

    RWFUNCTION(RWSTRING("_rpSkinMBPluginAttach"));
#if defined(FASTSKIN)
    /*
     * Create a new atomic pipeline
     */
    SkinPipeline = _rpAtomicSkinPipelineCreate();
    if (!SkinPipeline)
    {
        RWRETURN(FALSE);
    }

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

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

    /*
     * Set the skinning instance pipeline
     */
    RxXBoxAllInOneSetInstanceCallBack(node,
                                      rxXBAtomicSkinInstanceCallback);

    /*
     * Set Lighting callback
     */
    RxXBoxAllInOneSetLightingCallBack(node,
                                      _rxXbAtomicSkinLightingCallback);

    /*
     * Get the default render pipeline
     */
    RenderPipeline = RxXBoxAllInOneGetRenderCallBack(node);

    /*
     * Set the skinning render callback
     */
    RxXBoxAllInOneSetRenderCallBack(node,
                                    _rxXBAtomicSkinRenderCallback);

    /*
     * Create the skin vertex shader
     */
    if (D3D_OK != D3DDevice_CreateVertexShader(SkinVertexShaderDecl,
                                               dwVertshdrVertexShader,
                                               &SkinVertexShader, 0))
    {
        RWRETURN(FALSE);
    }

#endif /* defined(FASTSKIN) */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rpSkinMBInitAtomic

 Initialise an atomic's matrix-blending skin data.

 Inputs :   RpAtomic *          A pointer to a skin atomic.

 Outputs:   RwBool              TRUE on success
 */

RwBool
_rpSkinMBInitAtomic(RpAtomic * atomic)
{
    RpSkin             *pSkin;
    RwInt32             i;
    RwInt32             newBoneIndex;
    RwInt32            *matrixIndexMap;
    RwInt32            *vertexIndexMap;
    RwBool              usedBones[256];
    RwUInt32            bytes;

    RWFUNCTION(RWSTRING("_rpSkinMBInitAtomic"));
#if defined(FASTSKIN)
    RpAtomicSetInstancePipeline(atomic, SkinPipeline);

    /* build a platform specific mapping for bone indices, this allows us
     * to only use the subset of bone matrices which are actually used */
    pSkin = *RPSKINATOMICGETDATA(atomic);

    if (pSkin == NULL)
    {
        RWRETURN(FALSE);
    }

    for (i = 0; i < 256; i++)
    {
        usedBones[i] = FALSE;
    }

    for (i = 0; i < pSkin->totalVertices; i++)
    {
        RwInt32             index = pSkin->pMatrixIndexMap[i];

        if (pSkin->pMatrixWeightsMap[i].w0 > 0.0f)
        {
            usedBones[index & 0xFF] = TRUE;
        }
        if (pSkin->pMatrixWeightsMap[i].w1 > 0.0f)
        {
            usedBones[(index >> 8) & 0xFF] = TRUE;
        }
        if (pSkin->pMatrixWeightsMap[i].w2 > 0.0f)
        {
            usedBones[(index >> 16) & 0xFF] = TRUE;
        }
        if (pSkin->pMatrixWeightsMap[i].w3 > 0.0f)
        {
            usedBones[(index >> 24) & 0xFF] = TRUE;
        }
    }
    
    bytes = sizeof(RwInt32) * (256 * 2 + 1);
    pSkin->pPlatformData = (RwInt32 *) RwMalloc(bytes);
    memset(pSkin->pPlatformData, 0, bytes);
    
    vertexIndexMap = (RwInt32 *) pSkin->pPlatformData;
    matrixIndexMap = &vertexIndexMap[256];
    newBoneIndex = 0;

    for (i = 0; i < 256; i++)
    {
        vertexIndexMap[i] = -1;
        matrixIndexMap[i] = -1;
    }

    for (i = 0; i < 256; i++)
    {
        if (usedBones[i])
        {
            vertexIndexMap[i] = newBoneIndex;
            newBoneIndex++;
        }
    }

    matrixIndexMap[256] = newBoneIndex;

    for (i = 0; i < 256; i++)
    {
        if (vertexIndexMap[i] != -1)
        {
            matrixIndexMap[vertexIndexMap[i]] = i;
        }
    }

#endif /* defined(FASTSKIN) */
    RWRETURN(TRUE);
}
