/* 
 * Skin plug-in
 */

/****************************************************************************
 *                                                                          *
 *  Module  :   SkinGeneric.c                                               *
 *                                                                          *
 *  Purpose :   Skin Rendering pipeline for PS2                             *
 *                                                                          *
 ****************************************************************************/

/*===========================================================================
                    Includes
 */
#ifdef __R5900__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#include <float.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 "matblend.h"

#include "SKY2_SkinGeneric/stddata.h"
#include "SKY2_SkinGeneric/SkinGeneric.h"

#include "genmatbl.h"

/*===========================================================================
                        Local (static) Globals
 */
#ifdef  SKY2_DRVMODEL_H
#define SKYVU1TRANSFORMSALIGN __attribute__ ((aligned(64)))

/*------ New VU Array Structure / Back Face Culling Support ------*/
void        *skySkinVU1Transforms[VU1CODEARRAYSIZE]
    SKYVU1TRANSFORMSALIGN = {
    &SKINGenPRS,                /* TRANSTRI,  TRANSPER, TRANSNCULL */
    &SKINBFacePRS,              /* TRANSTRI,  TRANSPER, TRANSCULL  */
    &SKINGenPRL,                /* TRANSTRI,  TRANSISO, TRANSNCULL */
    &SKINnulltransform,         /* NOT DONE YET: &vu1CullFacePRL,  */
    &SKINsegPRS,                /* TRANSLINE, TRANSPER, TRANSNCULL */
    &SKINnulltransform,         /* No culling for lines            */
    &SKINsegPRL,                /* TRANSLINE, TRANSISO, TRANSNCULL */
    &SKINnulltransform,         /* No culling for lines            */
    /* unused, but left for compatibility with 32 entries array    */
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform,
    &SKINnulltransform, &SKINnulltransform
};
#endif /* SKY2_DRVMODEL_H */

/*==========================================================================*/
/*                                                                          */
/*                               Functions                                  */
/*                                                                          */
/*==========================================================================*/

/*=====================================================================*/
/*=                 Generic Skinning Core Function                    =*/
/*=====================================================================*/
static              RwBool
CoreSkinGenericFunc(RxPS2Mesh * ps2Mesh,
                    RxPS2DMASessionRecord * dmaSessionRec,
                    RwReal * destMBI)
{
    RpAtomic           *atomic = dmaSessionRec->sourceObject.atomic;
    RpSkin             *pSkin = *RPSKINATOMICGETDATA(atomic);

    RWFUNCTION(RWSTRING("CoreSkinGenericFunc"));
    RWASSERT(NULL != ps2Mesh);
    RWASSERT(NULL != dmaSessionRec);
    RWASSERT(NULL != destMBI);
    RWASSERT(NULL != atomic);
    RWASSERT(NULL != pSkin);

    /* RWASSERT(pSkin->pCurrentHierarchy); */

 /*------------ Skin weights and indices instancing -----------*/
    if (dmaSessionRec->serialNum !=
        ((RwUInt32 *) (*(ps2Mesh->cacheEntryRef) + 1))[3])
    {
        _rpSkinMBWeightsInstancing(
            (const RpMesh *)ps2Mesh->mesh, pSkin, destMBI);
    }

 /*----------------- Bones matrices uploading -----------------*/
    if (RpSkinAtomicGlobals.SkinMatrixCacheSkin != pSkin ||
        RpSkinAtomicGlobals.SkinMatrixCacheRenderFrame !=
        RWSRCGLOBAL(renderFrame))
    {
        RwMatrix           *pMBA = _rpSkinMBMatUpdating(atomic, pSkin);
        RwUInt32            numMatrices = pSkin->numBones;
        u_long128           ltmp = 0;
        RwUInt64            tmp, tmp1;
        RwInt32             i;

        RWASSERT(NULL != pMBA);

        /*---------------- DMA packet -----------------------*/
        sweFinaliseOpenLocalPkt(SWE_LPS_NOFIXUP |
                                SWE_PKT_DMA_MODE_CHAIN_TTE |
                                SWE_PKT_LOCAL | SWE_PKT_VU1 |
                                SWE_PKT_CIRCALLOC,
                                -(2 + (4 * numMatrices)));
        if (!sweLocalPacket)
        {
            RWRETURN(FALSE);
        }
        tmp = ((1l << 28) | (numMatrices * 4)); /*-- 4m QW --*/
        tmp1 = ((((0x6cl << 24) | ((numMatrices * 4) << 16) |
                                               /*-- 4m QW --*/
                  ((long) (pipeASymbStaticDataStart))) << 32) |
                ((1l << 24) | (4 << 8) | (4)));
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);
        /*---------------------------------------------------*/

     /*-------- Upload Skin matrices ---------------------*/
        for (i = 0; i < (RwInt32) numMatrices; i++)
        {
            ltmp = *((u_long128 *) & pMBA->right.x);
            SWEADDCONTFAST(ltmp);
            ltmp = *((u_long128 *) & pMBA->up.x);
            SWEADDCONTFAST(ltmp);
            ltmp = *((u_long128 *) & pMBA->at.x);
            SWEADDCONTFAST(ltmp);
            ltmp = *((u_long128 *) & pMBA->pos.x);
            SWEADDCONTFAST(ltmp);
            pMBA++;
        }
        /*---------------------------------------------------*/

        /*------- Terminate the DMA with an interrupt -------*/
        tmp = (0xfl << 28);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);
        /*---------------------------------------------------*/

        sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
    }

    RWRETURN(TRUE);
}

/*=====================================================================*/
/*=                     Manager Instance Call-Back                    =*/
/*=====================================================================*/

RwBool
RxPipelineNodePS2SkinGenericPS2ManagerInstanceCallBack(void **clusterData,
                                                       RwUInt32 __RWUNUSED__
                                                       numClusters)
{
    RWAPIFUNCTION(RWSTRING
                  ("RxPipelineNodePS2SkinGenericPS2ManagerInstanceCallBack"));

    RWRETURN(CoreSkinGenericFunc((RxPS2Mesh *) (clusterData[0]),
                                 (RxPS2DMASessionRecord
                                  *) (clusterData[1]),
                                 (RwReal *) (clusterData[2])));
}

/*=====================================================================*/
/*=             Generic Skinning PipeLine Constructor                 =*/
/*=====================================================================*/

RxPipeline         *
RpSkinGenericPipeCreate(void)
{
    RxPipeline         *newPipe = RxPipelineCreate();

    RWFUNCTION(RWSTRING("RpSkinGenericPipeCreate"));

    if (newPipe)
    {
        RxLockedPipe       *lpipe = RxPipelineLock(newPipe);

        if (NULL != lpipe)
        {
            RxNodeDefinition   *manager =
                RxNodeDefinitionGetPS2Manager(rxOBJTYPE_ATOMIC);
            RxPipelineNode     *plnode, *result;

            RWASSERT(NULL != manager);

            lpipe =
                RxLockedPipeAddFragment(lpipe, (RwUInt32 *) NULL,
                                        manager,
                                        (RxNodeDefinition *) NULL);
            RWASSERT(NULL != lpipe);

            plnode = RxPipelineFindNodeByName(lpipe, manager->name,
                                              (RxPipelineNode *) NULL,
                                              (RwInt32 *) NULL);
            RWASSERT(NULL != plnode);

            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2xyz,
                                                             CL_XYZ);
            RWASSERT(NULL != result);
            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2uv,
                                                             CL_UV);
            RWASSERT(NULL != result);
            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2rgba,
                                                             CL_RGBA);
            RWASSERT(NULL != result);
            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2normal,
                                                             CL_NORMAL);
            RWASSERT(NULL != result);
            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2user1,
                                                             CL_USER1);
            RWASSERT(NULL != result);

            RxPipelineNodePS2ManagerSetVUBufferSizes(plnode,
                                                     pipeASymbStrideOfInputCluster,
                                                     pipeASymbTSVertexCount,
                                                     pipeASymbTLTriCount);
            lpipe = RxLockedPipeUnlock(lpipe);
            if (lpipe != NULL)
            {
                plnode = RxPipelineFindNodeByName(lpipe, manager->name,
                                                  (RxPipelineNode *)
                                                  NULL,
                                                  (RwInt32 *) NULL);
                result =
                    RxPipelineNodePS2ManagerSetVIFOffset(plnode,
                                                         pipeASymbVIFOffset);
                if (!result)
                {
                    printf("Failed to set VIF offset\n");
                    RWRETURN((RxPipeline *)NULL);
                }

                /*---- New VU Array description allowing BF Culling ----*/
                ((RxPipelineNodePS2ObjAllInOneData *) plnode->privateData)->
                    genericVU1Index = TRUE;

          /*---- We now insert our own VU code -------------------*/
                result = RxPipelineNodePS2ManagerSetVU1CodeArray(plnode,
                    skySkinVU1Transforms);

                if (!result)
                {
                    printf("Failed to set vucode array\n");
                    RWRETURN((RxPipeline *)NULL);
                }

                RxPipelineNodePS2ManagerSetInstanceCallBack(
                    plnode,
                    RxPipelineNodePS2SkinGenericPS2ManagerInstanceCallBack);
                RWRETURN(lpipe);
            }
        }
    }
    RWRETURN((RxPipeline *)NULL);
}

/*=====================================================================*/
/*=                   PS2All Object Setup Call-Back                   =*/
/*=====================================================================*/

RwBool
RxPipelineNodePS2SkinPS2AllObjectSetupCallBack(
    RxPS2AllPipeData *ps2AllPipeData,
    RwMatrix **transform,
    RxWorldApplyLightFunc lightingFunc)
{
    RpAtomic           *atomic;
    RwFrustumTestResult inFrustum;

    RWFUNCTION(RWSTRING("RpAtomicPS2AllObjectSetupCallBack"));

    atomic = (RpAtomic *)(ps2AllPipeData->sourceObject);
    RWASSERT(NULL != atomic);

    /* Get the RwMeshCache from the atomic/geometry;
     * If the geometry has more than one morph target the resEntry
     * in the atomic is used else the resEntry in the geometry */
    RpAtomicPS2AllGetMeshHeaderMeshCache(atomic, ps2AllPipeData);

    /* Early out if no meshes */
    if(ps2AllPipeData->meshHeader->numMeshes <= 0)
    {
        /* Oy... we have geometry with vertices but no triangles
         * We have to test because with plugin data to compensate
         * (e.g bezier control points, or procedural vector code
         * ala particles), it might be valid... :o/ */
        RWRETURN(FALSE);
    }

    /* Gather metrics */
    RpAtomicPS2AllGatherObjMetrics(atomic);

    /* Set up numMorphTargets and spExtra */
    RpAtomicPS2AllMorphSetup(atomic, ps2AllPipeData);

    /* Decide whether this geometry needs instancing or not */
    RpAtomicPS2AllObjInstanceTest(atomic, ps2AllPipeData);

    /* We need to cache the transform */
    RpAtomicPS2AllTransformSetup(atomic, transform);

    /* We need to to a frustum test here */
    RpAtomicPS2AllFrustumTest(atomic, &inFrustum);

    /* Set up primType and transtype */
    RpAtomicPS2AllPrimTypeTransTypeSetup(ps2AllPipeData, inFrustum);

    /* Do we modulate with material colours for this geometry? */
    RpAtomicPS2AllMatModulateSetup(atomic, ps2AllPipeData);

    /* Do default lighting unless overloaded (same function
     * overloads this and PS2Manager pipelines) */
    if (MatBlend.lightingCallBack)
    {
        MatBlend.lightingCallBack(rxOBJTYPE_ATOMIC,
                                  ps2AllPipeData->sourceObject,
                                  ps2AllPipeData->surfProps,
                                  lightingFunc);
    }
    else
    {
        RpAtomicPS2AllLightingSetup(ps2AllPipeData, lightingFunc);
    }

    RWRETURN(TRUE);
}

/*=====================================================================*/
/*=                     PS2All Instance Call-Back                     =*/
/*=====================================================================*/

RwBool
RxPipelineNodePS2SkinPS2AllInstanceCallBack(RxPS2AllPipeData *ps2AllPipeData,
                                            void **clusterData,
                                            RwUInt32 numClusters __RWUNUSEDRELEASE__)
{
    RwReal           *destMBI;
    RpAtomic         *atomic;
    RpSkin           *pSkin;

    RWAPIFUNCTION(RWSTRING("RxPipelineNodePS2SkinPS2AllInstanceCallBack"));


    atomic = (RpAtomic *)ps2AllPipeData->sourceObject;
    RWASSERT(NULL != atomic);
    pSkin = *RPSKINATOMICGETDATA(atomic);
    RWASSERT(NULL != pSkin);
    RWASSERT(pSkin->pCurrentHierarchy || pSkin->pCurrentSkeleton);

    RWASSERT(1 == numClusters);

     /*------------ Standard data instancing -----------*/
    RpAtomicPS2AllInstance(ps2AllPipeData);

     /*------------ Skin weights and indices instancing -----------*/
    destMBI = (RwReal *)(clusterData[0]);
    RWASSERT(NULL != destMBI);
    _rpSkinMBWeightsInstancing(ps2AllPipeData->mesh, pSkin, destMBI);

    RWRETURN(TRUE);
}

/*=====================================================================*/
/*=                     PS2All Bridge Call-Back                       =*/
/*=====================================================================*/

RwBool
RxPipelineNodePS2SkinPS2AllMatBridgeCallBack(RxPS2AllPipeData *ps2AllPipeData)
{
    RpAtomic *atomic;
    RpSkin   *pSkin;
    RwMatrix *pMBA  = (RwMatrix *) NULL;
    RwUInt32  numMatrices = 0;
    RwUInt32  numQW;
    RwUInt32  skinNumQW;

    RWAPIFUNCTION(RWSTRING("RxPipelineNodePS2SkinPS2AllMatBridgeCallBack"));

    atomic = (RpAtomic *)ps2AllPipeData->sourceObject;
    RWASSERT(NULL != atomic);
    pSkin = *RPSKINATOMICGETDATA(atomic);
    RWASSERT(NULL != pSkin);
    RWASSERT(pSkin->pCurrentHierarchy || pSkin->pCurrentSkeleton);


    /* Are we uploading bones? */
    if (RpSkinAtomicGlobals.SkinMatrixCacheSkin != pSkin ||
        RpSkinAtomicGlobals.SkinMatrixCacheRenderFrame !=
        RWSRCGLOBAL(renderFrame))
    {
        pMBA = _rpSkinMBMatUpdating(atomic, pSkin);
        numMatrices = pSkin->numBones;
        skinNumQW = 1 + 4*numMatrices;
    }
    else
    {
        skinNumQW = 0;
    }


    /* If you exit here, the mesh won't be rendered */

    /* Asynchronously upload the texture if nec/poss */
    RpMeshPS2AllAsyncTextureUpload(ps2AllPipeData);
    /* If changed, does skyTexCacheAccessRaster and updates global renderstate */
    RpMeshPS2AllSyncTextureUpload(ps2AllPipeData);

    /* Open a VIF transfer packet, with a header DMA tag */
    numQW = rpMESHPS2ALLGIFTAGNUMQW +
            rpMESHPS2ALLMATCOLNUMQW +
            rpMESHPS2ALLSURFPROPSNUMQW +
            rpMESHPS2ALLCLIPINFONUMQW +
            rpMESHPS2ALLTEXTURESTATENUMQW +
            skinNumQW;
    RpMeshPS2AllStartVIFUploads(TRUE, numQW);

    /* Skinning VIF uploads here... VIF tag(s) necessary.
     * Would have called _rxPS2AllStartVIFUploads with FALSE if needed
     * DMA tags in the user uploads (would have had to fix up with a
     * terminal DMA tag so the following standard transfers work). */

    if (0 != skinNumQW)
    {
     /*----------------- Bones matrices uploading -----------------*/
        u_long128           ltmp = 0;
        RwUInt64            /*tmp, */tmp1;
        RwInt32             i;

        RWASSERT(NULL != pMBA);

        /* VIF tag to upload our matrices */
        tmp1 = ((((0x6CL << 24) | ((numMatrices * 4) << 16) |
                                               /*-- 4m QW --*/
                  ((long) (pipeASymbStaticDataStart))) << 32) |
                ((1L << 24) | (4 << 8) | (4)));
        MAKE128(ltmp, tmp1, 0L/*tmp*/);
        SWEADDCONTFAST(ltmp);
        /*---------------------------------------------------*/

     /*-------- Upload Skin matrices ---------------------*/
        for (i = 0; i < (RwInt32) numMatrices; i++)
        {
            ltmp = *((u_long128 *) & pMBA->right.x);
            SWEADDCONTFAST(ltmp);
            ltmp = *((u_long128 *) & pMBA->up.x);
            SWEADDCONTFAST(ltmp);
            ltmp = *((u_long128 *) & pMBA->at.x);
            SWEADDCONTFAST(ltmp);
            ltmp = *((u_long128 *) & pMBA->pos.x);
            SWEADDCONTFAST(ltmp);
            pMBA++;
        }
    }


    /* Here follow the standard VIF uploads */

    /* Upload a GIF tag for the code to submit geom to the GS under */
    RpMeshPS2AllGIFTagUpload(ps2AllPipeData);

    /* Upload material colour, dependent on whether there's a texture
     * (also does some renderstate setup based on alpha - that's why
     * this needs to be before the texture state setup func) */
    RpMeshPS2AllMatColUpload(ps2AllPipeData);

    /* Upload surface properties, including the 'extra' float in W */
    RpMeshPS2AllSurfPropsUpload(ps2AllPipeData);

    /* Sets up clipping info and J-C's switch QWs (NOTE: uses "transType&7") */
    RpMeshPS2AllClipInfoUpload(ps2AllPipeData);

    /* Upload texture renderstate to the GS (this sends stuff thru VIF
     * only, ergo can be combined with other VIF stuff! :) ) */
    RpMeshPS2AllTextureStateUpload(ps2AllPipeData);

    /* Set up vu1CodeIndex */
    RpMeshPS2AllVU1CodeIndexSetup(ps2AllPipeData);
    /* Upload the VU1 code (if it changed) last, since it uses a DMA tag (ref transfer of the code) */
    RpMeshPS2AllVU1CodeUpload(ps2AllPipeData);

    /* Kicks off geometry transfer, sets up refCnt/clrCnt and
     * closes the packet (so it can be extended later I think) */
    RpMeshPS2AllEndVIFUploads(ps2AllPipeData);

    RWRETURN(TRUE);
}

/*=====================================================================*/
/*=             PS2All Skinning PipeLine Constructor                  =*/
/*=====================================================================*/

RxPipeline         *
RpSkinPS2AllPipeCreate(RxPipeline *matPipe)
{
    RxPipeline         *newPipe = RxPipelineCreate();

    RWFUNCTION(RWSTRING("RpSkinPS2AllPipeCreate"));

    if (newPipe && matPipe)
    {
        RxLockedPipe       *lpipe = RxPipelineLock(newPipe);

        if (NULL != lpipe)
        {
            RxNodeDefinition   *ps2all = RxNodeDefinitionGetPS2All();
            RxPipelineNode     *plnode;

            RWASSERT(NULL != ps2all);

            lpipe =
                RxLockedPipeAddFragment(lpipe, (RwUInt32 *) NULL,
                                        ps2all,
                                        (RxNodeDefinition *) NULL);
            RWASSERT(NULL != lpipe);

            plnode = RxPipelineFindNodeByName(lpipe, ps2all->name,
                                              (RxPipelineNode *) NULL,
                                              (RwInt32 *) NULL);
            RWASSERT(NULL != plnode);

            lpipe = RxLockedPipeUnlock(lpipe);
            if (lpipe != NULL)
            {
                plnode = RxPipelineFindNodeByName(lpipe, ps2all->name,
                                                  (RxPipelineNode *)
                                                  NULL,
                                                  (RwInt32 *) NULL);

                RxPipelineNodePS2AllSetCallBack(
                    plnode, rxPS2ALLCALLBACKOBJECTSETUP,
                    RxPipelineNodePS2SkinPS2AllObjectSetupCallBack);
                RxPipelineNodePS2AllSetCallBack(
                    plnode, rxPS2ALLCALLBACKOBJECTFINALIZE,
                    RpAtomicPS2AllObjectFinalizeCallBack);

                /* We make the PS2All skinning pipe just like the PS2Manager
                 * one in that it's per-object.. all meshes are forced down
                 * the same material pipe. There's cope for greater
                 * flexibility with PS2All, this is just done to be compatible
                 * more easily with code that used the PS2Manager pipe.
                 *  Also, there's the lighting overloading. In order to use
                 * RpSkinSetPS2Lighting with the PS2All pipeline, we have to
                 * have a skinning objectSetupCB that uses the lighting
                 * callback passed to that function. People should really
                 * set the skinning pipeline/callbacks in their materials
                 * and overload object lighting in their own pipelines. */
                RxPipelineNodePS2AllGroupMeshes(plnode, matPipe);

                RWRETURN(lpipe);
            }
        }
    }
    RWRETURN((RxPipeline *)NULL);
}

/*=====================================================================*/
/*=				PS2AllMat Skinning PipeLine Constructor				  =*/
/*=====================================================================*/

RxPipeline         *
RpSkinPS2AllMatPipeCreate(void)
{
    RxPipeline         *newPipe = RxPipelineCreate();

    RWFUNCTION(RWSTRING("RpSkinPS2AllMatPipeCreate"));

    if (newPipe)
    {
        RxLockedPipe       *lpipe = RxPipelineLock(newPipe);

        if (NULL != lpipe)
        {
            RxNodeDefinition   *ps2allmat = RxNodeDefinitionGetPS2AllMat();
            RxPipelineNode     *plnode, *result;

            RWASSERT(NULL != ps2allmat);

            lpipe =
                RxLockedPipeAddFragment(lpipe, (RwUInt32 *) NULL,
                                        ps2allmat,
                                        (RxNodeDefinition *) NULL);
            RWASSERT(NULL != lpipe);

            plnode = RxPipelineFindNodeByName(lpipe, ps2allmat->name,
                                              (RxPipelineNode *) NULL,
                                              (RwInt32 *) NULL);
            RWASSERT(NULL != plnode);

            result = RxPipelineNodePS2AllMatGenerateCluster(
                         plnode, &RxClPS2xyz, CL_XYZ);
            RWASSERT(NULL != result);
            result = RxPipelineNodePS2AllMatGenerateCluster(
                         plnode, &RxClPS2uv, CL_UV);
            RWASSERT(NULL != result);
            result = RxPipelineNodePS2AllMatGenerateCluster(
                         plnode, &RxClPS2rgba, CL_RGBA);
            RWASSERT(NULL != result);
            result = RxPipelineNodePS2AllMatGenerateCluster(
                         plnode, &RxClPS2normal, CL_NORMAL);
            RWASSERT(NULL != result);
            result = RxPipelineNodePS2AllMatGenerateCluster(
                         plnode, &RxClPS2user1, CL_USER1);
            RWASSERT(NULL != result);

            RxPipelineNodePS2AllMatSetVUBufferSizes(plnode,
                                                 pipeASymbStrideOfInputCluster,
                                                 pipeASymbTSVertexCount,
                                                 pipeASymbTLTriCount);
            lpipe = RxLockedPipeUnlock(lpipe);
            if (lpipe != NULL)
            {
                plnode = RxPipelineFindNodeByName(lpipe, ps2allmat->name,
                                                  (RxPipelineNode *)
                                                  NULL,
                                                  (RwInt32 *) NULL);
                result = RxPipelineNodePS2AllMatSetVIFOffset(
                             plnode, pipeASymbVIFOffset);
                if (!result)
                {
                    printf("Failed to set VIF offset\n");
                    RWRETURN((RxPipeline *)NULL);
                }

                /*---- We now insert our own VU code -------------------*/
                result = RxPipelineNodePS2AllMatSetVU1CodeArray(
                             plnode, skySkinVU1Transforms, VU1CODEARRAYSIZE);

                if (!result)
                {
                    printf("Failed to set vucode array\n");
                    RWRETURN((RxPipeline *)NULL);
                }

                /* We overload the instance and bridge CBs */
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKMESHINSTANCETEST,
                    RpMeshPS2AllMeshInstanceTestCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKRESENTRYALLOC,
                    RpMeshPS2AllResEntryAllocCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKINSTANCE,
                    RxPipelineNodePS2SkinPS2AllInstanceCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKBRIDGE,
                    RxPipelineNodePS2SkinPS2AllMatBridgeCallBack);

                RWRETURN(lpipe);
            }
        }
    }
    RWRETURN((RxPipeline *)NULL);
}


#endif /* __R5900__ */
