
/* There is no documentation in here as this will all shortly change as
 * nodePS2Manager is modified and moved from RpWorld to the core */

/*
 * fastim3d
 * Using PS2Manager.csl to render Im3D primitives quickly (platform-specific)
 *
 * Copyright (c) Criterion Software Limited
 */

#include <string.h> /* To aid compilation under CW */

#include <rwcore.h>

#include "p2stdclsw.h"
#include "ps2clusterattribs.h"

#include "nodeps2manager.h"

#include "nodeps2all.h"
#include "ps2allim3d.h"

#include "fastim3d.h"

#if (!defined(DOXYGEN))
static const char rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: fastim3d.c,v 1.49 2001/09/27 18:44:12 iestynb Exp $";
#endif /* (!defined(DOXYGEN)) */


/****************************************************************************
 Local defines
 */

#define NUMCLUSTERSOFINTEREST 0

/* Grab immediglobal from baim3d.c until this stuff goes in there
 * (when nodePS2Manager moves into the core) */

#define RWIMMEDIGLOBAL(var)                             \
    (RWPLUGINOFFSET(rwImmediGlobals,                    \
                    RwEngineInstance,                   \
                    _rwIm3DModule.globalsOffset)->var)


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

   Functions

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

/****************************************************************************
 PS2Im3DFastTransformNodeBody()

 Fakes doing the transform and fills the stash similar to nodeImmStash
  - there are no camVerts, meshState, etc though
  - it has to copy the source vertices [:o/] given the separation of
    RwIM3DTransform and RwIM3DRender*
  - it stores the transformation matrix (used during the render pipe now)
    and the flags also (rwIM3D_VERTEXUV and rwIM3DNOCLIP are used)
 */

static              RwBool
PS2Im3DFastTransformNodeBody(RxPipelineNode * __RWUNUSED__ self,
                             const RxPipelineNodeParam * params)
{
    rwIm3DPool          *pool;
    RxHeap              *heap;

    RWFUNCTION(RWSTRING("PS2Im3DFastTransformNodeBody"));

    RWASSERT(self != NULL);

    RWASSERT(params != NULL);
    pool = (rwIm3DPool *)RxPipelineNodeParamGetData(params);

    heap = RxPipelineNodeParamGetHeap(params);
    RWASSERT(NULL != heap);

    /* For PS2Manager Im3D, no need to allow zero vertices */
    if (pool->numElements <= 0)
    {
        RWRETURN(FALSE);
    }

    RWASSERT(NULL != pool->elements);
    /* All other platforms reference the verts (so customer code must dead
     * with this already and not edit verts before RwIm3DEnd) and copies
     * hurt more on PS2 than anything else, so we'll do it here too.*/
    pool->stash.objVerts = (RxObjSpace3DVertex*) (pool->elements);

    if (!(pool->stash.flags & rwIM3D_VERTEXUV))
    {
        /* We won't set up UVs but (for now) junk ones will be
         * uploaded, so turn off texturing so it doesn't matter... */
        RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
    }

    /* TODO: need a different render pipe so the
     *       UV cluster isn't generated/uploaded */

#if (defined(RWMETRICS))
    /* This isn't a true representation of what occurs; we
     * really submit and transform these verts multiple
     * times if there are multiple Im3DRender###s, should we
     * move this? Or keep it consistent with other platforms? */
    RWSRCGLOBAL(metrics)->numVertices += pool->numElements;
#endif /* (defined(RWMETRICS)) */

    RWRETURN(TRUE);
}

/****************************************************************************
 NodeDefinitionGetPS2Im3DFastTransform()
 */

static RxNodeDefinition *
NodeDefinitionGetPS2Im3DFastTransform(void)
{

    static RwChar       _PS2Im3DFastTransform_csl[] =
        RWSTRING("PS2Im3DFastTransform.csl");

    static RxNodeDefinition nodePS2Im3DFastTransform = {
        _PS2Im3DFastTransform_csl, /* Name */
        {                      /* nodemethods */
            PS2Im3DFastTransformNodeBody, /* +-- nodebody */
            (RxNodeInitFn) NULL,  /* +-- nodeinit */
            (RxNodeTermFn) NULL,  /* +-- nodeterm */
            (RxPipelineNodeInitFn) NULL, /* +-- pipelinenodeinit */
            (RxPipelineNodeTermFn) NULL, /* +-- pipelineNodeTerm */
            (RxPipelineNodeConfigFn) NULL, /* +--  pipelineNodeConfig */
            (RxConfigMsgHandlerFn) NULL /* +--  configMsgHandler */
        },
        {                      /* Io */
            NUMCLUSTERSOFINTEREST, /* +-- NumClustersOfInterest */
            (RxClusterRef *)NULL,                 /* +-- ClustersOfInterest */
            (RxClusterValidityReq *)NULL,                 /* +-- InputRequirements */
            0,                    /* +-- NumOutputs */
            (RxOutputSpec *)NULL                  /* +-- Outputs */
        },
        (RwInt32) 0,           /* PipelineNodePrivateDataSize */
        (RxNodeDefEditable)FALSE,   /* editable */
        (RwInt32) 0            /* inPipes */
    };

    RWFUNCTION(RWSTRING("NodeDefinitionGetPS2Im3DFastTransform"));

    RWRETURN(&nodePS2Im3DFastTransform);

}

/****************************************************************************
 fastIm3DTransformPipeCreate()
 */

static RxPipeline  *
fastIm3DTransformPipeCreate(void)
{
    RxPipeline         *pipe;

    RWFUNCTION(RWSTRING("fastIm3DTransformPipeCreate"));

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

        lpipe = RxPipelineLock(pipe);

        if (NULL != lpipe)
        {
            RxNodeDefinition   *im3dnode =
                NodeDefinitionGetPS2Im3DFastTransform();

            lpipe = RxLockedPipeAddFragment(lpipe,
                                            (RwUInt32 *)NULL,
                                            im3dnode,
                                            (RxNodeDefinition *)NULL);

            if (lpipe != NULL)
            {
                lpipe = RxLockedPipeUnlock(lpipe);

                if (lpipe != NULL)
                {
                    RWRETURN(pipe);
                }
            }
        }

        RxPipelineDestroy(pipe);
    }

    RWERROR((E_RW_DEFAULTPIPELINECREATION, "FastIm3DTransform"));

    RWRETURN((RxPipeline *)NULL);
}

/****************************************************************************
 CreateIm3DPS2ManagerRenderPipeline()
 */

static RxPipeline  *
CreateIm3DPS2ManagerRenderPipeline(void)
{
    RxPipeline         *pipe;

    RWFUNCTION(RWSTRING("CreateIm3DPS2ManagerRenderPipeline"));

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

        lpipe = RxPipelineLock(pipe);

        if (NULL != lpipe)
        {
            RxNodeDefinition   *manager =

                RxNodeDefinitionGetPS2Manager(rxOBJTYPE_IM3D);
            RxPipelineNode     *plnode, *result;

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

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

            /* get manager node to generate cluster "xyz" */
            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2xyz,
                                                             CL_XYZ);
            RWASSERT(result != NULL);

            /* get manager node to generate cluster "uv"
             * - yes this should be optional, but we'd need two pipes
             *   for that which is a bit crap, so I'm just uploading
             *   junk UVs and turning texturing off */
            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2uv,
                                                             CL_UV);
            RWASSERT(result != NULL);

            /* get manager node to generate cluster "rgba" */
            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2rgba,
                                                             CL_RGBA);
            RWASSERT(result != NULL);

            /* get manager node to generate cluster "normal"
             * - we never fill it for Im3D verts though!
             * should fix this with a stddata.h or something
             * and have two pipes, one w/ UVs, one w/out,
             * neither with normals */
            result = RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                             &RxClPS2normal,
                                                             CL_NORMAL);
            RWASSERT(result != NULL);

            RxPipelineNodePS2ManagerSetVUBufferSizes(plnode,
                                              _rwskyStrideOfInputCluster,
                                              _rwskyTSVertexCount,
                                              _rwskyTLTriCount);

            lpipe = RxLockedPipeUnlock(lpipe);

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

                RxPipelineNodePS2ManagerSetVIFOffset(plnode,
                                                     _rwskyVIFOffset);

                /* Hack to change VU1 code array index calculation - due
                 * to our new default VU1 code array being smaller since
                 * putting in backface culling support. Deliberately no
                 * wrapper function for this. All other pipes should
                 * work exactly as before. */
                ((RxPipelineNodePS2ObjAllInOneData *)plnode->privateData)
                    ->genericVU1Index = TRUE;

                RWRETURN(pipe);
            }
        }

        RxPipelineDestroy(pipe);
    }

    RWERROR((E_RW_DEFAULTPIPELINECREATION, "FastIm3DRender"));

    RWRETURN((RxPipeline *)NULL);
}

/**
 * \ingroup rwim3dps2
 * \ref RwIm3DSkyGetPS2ManagerTransformPipeline
 * returns a pointer to the default immediate mode transform PS2Manager pipeline.
 *
 * The PS2Manager (see \ref RxNodeDefinitionGetPS2Manager) Im3D transform
 * pipeline does very little work, it just sets up a copy of the source
 * vertices for the RwIm3DRender*() functions to work with and also caches
 * the flags and transformation matrix (since they are used in the render
 * pipeline in the PS2Manager version). In fact, this same pipeline is
 * used by the PS2All-based RwIm3D render pipelines
 * (see \ref RwIm3DSkyGetPS2AllTransformPipeline)
 *
 * If you wish to submit 2D triangles through Submit.csl (i.e you want to
 * do transformation and lighting and whatever else CPU-side - which will
 * of course be very slow) then rather than use this Im3D transform
 * pipeline, you should use a generic (non-PS2-specific) one. N.B. the
 * PS2Manager Im3D transform pipe must be paired with PS2Manager (and now
 * PS2All, see \ref RwIm3DSkyGetPS2AllRenderPipeline) render pipes and
 * vice versa! Do not change one and not the other.
 *
 * The world plugin must be attached before using this function (for now,
 * this is an RpWorld overload of the Im3D functionality, using RpWorld
 * technology - PS2Manager.csl - to speed it up).
 *
 * The PS2Manager Im3D transform pipeline:
 * \verbatim
    PS2Im3DFastTransform.csl
   \endverbatim
 *
 * \return Returns pointer to the pipeline if successful or NULL if there
 * is an error.
 *
 * \see RwIm3DSkyGetPS2ManagerRenderPipeline
 * \see RwIm3DSkyGetPS2AllRenderPipeline
 * \see RwIm3DSkyGetPS2AllMatPipeline
 * \see RwIm3DGetGenericTransformPipeline
 * \see RwIm3DGetGenericRenderPipeline
 * \see RwIm3DTransform
 * \see RwIm3DEnd
 * \see RwIm3DRenderIndexedPrimitive
 * \see RwIm3DRenderPrimitive
 * \see RwIm3DRenderTriangle
 * \see RwIm3DRenderLine
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DSetTransformPipeline
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DSetRenderPipeline
 */
RxPipeline *
RwIm3DSkyGetPS2ManagerTransformPipeline(void)
{
    RWAPIFUNCTION(RWSTRING("RwIm3DSkyGetPS2ManagerTransformPipeline"));

    RWRETURN(RWIMMEDIGLOBAL(platformIm3DTransformPipeline));
}

/**
 * \ingroup rwim3dps2
 * \ref RwIm3DSkyGetPS2ManagerRenderPipeline
 * returns a pointer to the default immediate mode PS2Manager render pipeline.
 *
 * The PS2Manager (see \ref RxNodeDefinitionGetPS2Manager) Im3D render
 * pipeline for PS2 is shown below.
 *
 * The Im3D render pipelines (in fact one pipeline can render all supported
 * primitive types except rwPRIMTYPEPOINTLIST for which you will need to
 * supply your own vector code) are not currently based on the PS2Manager
 * node, but rather the PS2All node (see \ref RwIm3DSkyGetPS2AllRenderPipeline).
 *
 * If you wish to submit 2D triangles through Submit.csl (i.e you want to
 * do transformation and lighting and whatever else CPU-side - which will
 * of course be very slow) then rather than use this Im3D transform
 * pipeline, you should use a generic (non-PS2-specific) one. N.B. the
 * PS2Manager Im3D transform pipe must be paired with PS2Manager (and now
 * PS2All, see \ref RwIm3DSkyGetPS2AllRenderPipeline) render pipes and vice
 * versa! Do not change one and not the other.
 *
 * The PS2Manager Im3D render pipeline (for all primitive types):
 *
 *   PS2Manager.csl
 *
 * \param  type     \ref RwPrimitiveType of the pipeline to fetch.
 *
 * \return Returns pointer to the pipeline if successful or NULL if there
 * is an error.
 *
 * \see RwIm3DSkyGetPS2ManagerTransformPipeline
 * \see RwIm3DSkyGetPS2AllRenderPipeline
 * \see RwIm3DSkyGetPS2AllMatPipeline
 * \see RwIm3DGetGenericTransformPipeline
 * \see RwIm3DGetGenericRenderPipeline
 * \see RwIm3DRenderIndexedPrimitive
 * \see RwIm3DRenderPrimitive
 * \see RwIm3DRenderTriangle
 * \see RwIm3DRenderLine
 * \see RwIm3DTransform
 * \see RwIm3DEnd
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DSetRenderPipeline
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DSetTransformPipeline
 */
RxPipeline *
RwIm3DSkyGetPS2ManagerRenderPipeline(RwPrimitiveType type)
{
    RWAPIFUNCTION(RWSTRING("RwIm3DSkyGetPS2ManagerRenderPipeline"));

    /* The same pipe handles all primitive types... except
     * pointLists which'd require non-default vector code */
    if (type == rwPRIMTYPEPOINTLIST) RWRETURN((RxPipeline *)NULL);

    RWRETURN(RWIMMEDIGLOBAL(ps2ManagerIm3DRenderPipeline));
}

/**
 * \ingroup rwim3dps2
 * \ref RwIm3DSkyGetPS2AllTransformPipeline
 * returns a pointer to the default immediate mode transform PS2All pipeline.
 *
 * The PS2All (see \ref RxNodeDefinitionGetPS2All) Im3D transform
 * pipeline does very little work, it just sets up a copy of the source
 * vertices for the RwIm3DRender*() functions to work with and also caches
 * the flags and transformation matrix (since they are used in the render
 * pipeline in the PS2Manager version). In fact, this same pipeline is
 * used by the PS2Manager-based RwIm3D render pipelines
 * (see \ref RwIm3DSkyGetPS2ManagerTransformPipeline)
 *
 * If you wish to submit 2D triangles through Submit.csl (i.e you want to
 * do transformation and lighting and whatever else CPU-side - which will
 * of course be very slow) then rather than use this Im3D transform
 * pipeline, you should use a generic (non-PS2-specific) one. N.B. the
 * PS2All Im3D transform pipe must be paired with PS2All (or PS2Manager,
 * see \ref RwIm3DSkyGetPS2ManagerRenderPipeline) render pipes and
 * vice versa! Do not change one and not the other.
 *
 * The world plugin must be attached before using this function (for now,
 * this is an RpWorld overload of the Im3D functionality, using RpWorld
 * technology - PS2All.csl - to speed it up).
 *
 * The PS2All Im3D transform pipeline:
 * \verbatim
    PS2Im3DFastTransform.csl
   \endverbatim
 *
 * \return Returns pointer to the pipeline if successful or NULL if there
 * is an error.
 *
 * \see RwIm3DSkyGetPS2AllRenderPipeline
 * \see RwIm3DSkyGetPS2AllMatPipeline
 * \see RwIm3DSkyGetPS2ManagerRenderPipeline
 * \see RwIm3DGetGenericTransformPipeline
 * \see RwIm3DGetGenericRenderPipeline
 * \see RwIm3DTransform
 * \see RwIm3DEnd
 * \see RwIm3DRenderIndexedPrimitive
 * \see RwIm3DRenderPrimitive
 * \see RwIm3DRenderTriangle
 * \see RwIm3DRenderLine
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DSetTransformPipeline
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DSetRenderPipeline
 */
RxPipeline *
RwIm3DSkyGetPS2AllTransformPipeline(void)
{
    RWAPIFUNCTION(RWSTRING("RwIm3DSkyGetPS2AllTransformPipeline"));

    RWRETURN(RWIMMEDIGLOBAL(platformIm3DTransformPipeline));
}

/**
 * \ingroup rwim3dps2
 * \ref RwIm3DSkyGetPS2AllRenderPipeline
 * returns a pointer to the default immediate mode PS2All render pipeline.
 *
 * The PS2All (see \ref RxNodeDefinitionGetPS2All) Im3D render pipeline
 * for PS2 is shown below.
 *
 * The Im3D render pipelines (in fact one pipeline can render all supported
 * primitive types except rwPRIMTYPEPOINTLIST for which you will need to
 * supply your own vector code) are by default based on the PS2All.csl node.
 *
 * Note that this pipeline uses mesh grouping (see
 * \ref RxPipelineNodePS2AllGroupMeshes), which means that it always calls
 * the same material pipeline - this can be retrieved through
 * \ref RwIm3DSkyGetPS2AllMatPipeline. Object pipelines (and we're treating
 * an Im3D render pipeline as an object instance+render pipeline) based on
 * PS2All.csl must be paired with material pipelines based on PS2AllMat.csl.
 * See \ref RxNodeDefinitionGetPS2All and \ref RxNodeDefinitionGetPS2AllMat
 * for further details.
 *
 * This pipeline is intended for use with the PS2Manager Im3D transform
 * pipeline (see \ref RwIm3DSkyGetPS2ManagerTransformPipeline).
 *
 * If you wish to submit 2D triangles through Submit.csl (i.e you want to
 * do transformation and lighting and whatever else CPU-side - which will
 * of course be very slow) then rather than use this Im3D transform
 * pipeline, you should use a generic (non-PS2-specific) one. N.B. the
 * PS2Manager Im3D transform pipe must be paired with PS2Manager (and now
 * PS2All) render pipes and vice versa! Do not change one and not the other.
 *
 * The PS2All Im3D render pipeline (for all primitive types):
 *
 *   PS2All.csl
 *
 * The callbacks used for the PS2All.csl node are:
 *
 * RwIm3DPS2AllObjectSetupCallBack (\ref RxPipelineNodePS2AllObjectSetupCallBack)
 *
 *
 * \param  type     \ref RwPrimitiveType of the pipeline to fetch.
 *
 * \return Returns pointer to the pipeline if successful or NULL if there
 * is an error.
 *
 * \see RxNodeDefinitionGetPS2All
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 * \see RxNodeDefinitionGetPS2AllMat
 * \see RwIm3DSkyGetPS2AllMatPipeline
 * \see RwIm3DSkyGetPS2ManagerTransformPipeline
 * \see RwIm3DGetGenericTransformPipeline
 * \see RwIm3DGetGenericRenderPipeline
 * \see RwIm3DRenderIndexedPrimitive
 * \see RwIm3DRenderPrimitive
 * \see RwIm3DRenderTriangle
 * \see RwIm3DRenderLine
 * \see RwIm3DTransform
 * \see RwIm3DEnd
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DSetRenderPipeline
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DSetTransformPipeline
 */
RxPipeline *
RwIm3DSkyGetPS2AllRenderPipeline(RwPrimitiveType type)
{
    RWAPIFUNCTION(RWSTRING("RwIm3DSkyGetPS2AllRenderPipeline"));

    /* The same pipe handles all primitive types... except
     * pointLists which'd require non-default vector code */
    if (type == rwPRIMTYPEPOINTLIST) RWRETURN((RxPipeline *)NULL);

    RWRETURN(RWIMMEDIGLOBAL(platformIm3DRenderPipelines).triList);
}

/****************************************************************************
 CreateIm3DPS2AllRenderPipeline
 */
static RxPipeline *
CreateIm3DPS2AllRenderPipeline(RxPipeline *ps2AllMatPipe)
{
    RxPipeline         *pipe;

    RWFUNCTION(RWSTRING("CreateIm3DPS2AllRenderPipeline"));

    RWASSERT(NULL != ps2AllMatPipe);

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

        lpipe = RxPipelineLock(pipe);

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

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

            RWASSERT(lpipe != NULL);

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

            lpipe = RxLockedPipeUnlock(lpipe);

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

                /* Set up the necesary callbacks */
                RxPipelineNodePS2AllSetCallBack(
                    plnode, rxPS2ALLCALLBACKOBJECTSETUP,
                    RwIm3DPS2AllObjectSetupCallBack);

                /* There are no materials to get pipes from, so group */
                RxPipelineNodePS2AllGroupMeshes(plnode, ps2AllMatPipe);

                RWRETURN(pipe);
            }
        }

        RxPipelineDestroy(pipe);
    }

    RWERROR((E_RW_DEFAULTPIPELINECREATION, "Im3DPS2AllRender"));

    RWRETURN((RxPipeline *)NULL);
}

/****************************************************************************
 CreateIm3DPS2AllMatPipeline
 */
static RxPipeline *
CreateIm3DPS2AllMatPipeline(void)
{
    RxPipeline         *pipe;

    RWFUNCTION(RWSTRING("CreateIm3DPS2AllMatPipeline"));

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

        lpipe = RxPipelineLock(pipe);

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

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

            RWASSERT(lpipe != NULL);

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

            /* Get ps2allmat node to generate cluster "xyz" */
            result = RxPipelineNodePS2AllMatGenerateCluster(
                         plnode, &RxClPS2xyz, CL_XYZ);
            RWASSERT(result != NULL);

            /* Get ps2allmat node to generate cluster "uv"
             * Yes this should be optional, but we'd need two pipes for that which is
             * a bit crap, so I'm just uploading junk UVs and turning texturing off */
            result = RxPipelineNodePS2AllMatGenerateCluster(
        /* TODO[2][3][5]: AIEE!! SHOULDN'T WE HAVE PLACEHOLDER FOR THESE UNUSED
         *               CLUSTERS IN IM3D PIPES?? THAT'D IMPROVE DMA PERFORMANCE
         *               (HEH, HARDLY THE BOTTLENECK :) ) */
                         plnode, &RxClPS2uv, CL_UV);
            RWASSERT(result != NULL);

            /* Get ps2allmat node to generate cluster "rgba" */
            result = RxPipelineNodePS2AllMatGenerateCluster(
                         plnode, &RxClPS2rgba, CL_RGBA);
            RWASSERT(result != NULL);

            /* Get ps2allmat node to generate cluster "normal"
             * We never use it for Im3D verts though! Should fix this with a new stddata.h
             * or two and have two pipes, one w/ UVs, one w/out, neither with normals */
            result = RxPipelineNodePS2AllMatGenerateCluster(
                          plnode, &RxClPS2normal, CL_NORMAL);
            RWASSERT(result != NULL);


            /* Set ps2allmat buffer sizes */
            result = RxPipelineNodePS2AllMatSetVUBufferSizes(plnode,
                                              _rwskyStrideOfInputCluster,
                                              _rwskyTSVertexCount,
                                              _rwskyTLTriCount);
            RWASSERT(result != NULL);


            lpipe = RxLockedPipeUnlock(lpipe);

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

                /* set ps2allmat VIFOffset */
                RxPipelineNodePS2AllMatSetVIFOffset(
                    plnode, _rwskyVIFOffset);

                /* Set up the necessary callbacks */
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKIM3DPREMESH,
                    _rwIm3DPS2AllIm3DPreMeshCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKRESENTRYALLOC,
                    RwIm3DPS2AllResEntryAllocCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKINSTANCE,
                    RwIm3DPS2AllInstanceCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKBRIDGE,
                    RwIm3DPS2AllBridgeCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKIM3DPOSTMESH,
                    _rwIm3DPS2AllIm3DPostMeshCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKIM3DMESHSPLIT,
                    _rwIm3DPS2AllMeshSplitCallBack);
                RxPipelineNodePS2AllMatSetCallBack(
                    plnode, rxPS2ALLMATCALLBACKPOSTMESH,
                    RwIm3DPS2AllPostMeshCallBack);

                RWRETURN(pipe);
            }
        }

        RxPipelineDestroy(pipe);
    }

    RWERROR((E_RW_DEFAULTPIPELINECREATION, "Im3DPS2AllMat"));

    RWRETURN((RxPipeline *)NULL);
}

/**
 * \ingroup rwim3dps2
 * \ref RwIm3DSkyGetPS2AllMatPipeline
 * returns a pointer to the default immediate mode PS2AllMat material
 * render pipeline.
 *
 * The PS2AllMat (see \ref RxNodeDefinitionGetPS2AllMat) Im3D material
 * render pipeline for PS2 is shown below.
 *
 * This pipeline is used behind the scenes from within the default Im3D
 * PS2All render pipeline. See \ref RwIm3DSkyGetPS2AllRenderPipeline
 * for details.
 *
 * Due to a limitation (which will be removed at some point in the future)
 * in the size of allocations possible with the allocator used for
 * allocating RwIm3D instance data, large primitives (ones for which
 * instance data comes to more than 64KB) have to be split into smaller
 * primitives. This happens automatically, behind the scenes. For this
 * reason, there are many callbacks and helper macros used within in
 * default RwIm3D pipeline which are temporary and undocumented. It is
 * thus not advised to create RwIm3D pipelines significantly different
 * from the defaults without assistance.
 *
 * The PS2AllMat Im3D render pipeline (for all primitive types):
 *
 *   PS2AllMat.csl
 *
 * The callbacks used for the PS2AllMat.csl node are:
 *
 *  \li _rwIm3DPS2AllIm3DPreMeshCallBack (undocumented),
 *  \li \ref RwIm3DPS2AllResEntryAllocCallBack (see also \ref RxPipelineNodePS2AllMatResEntryAllocCallBack),
 *  \li \ref RwIm3DPS2AllInstanceCallBack (see also \ref RxPipelineNodePS2AllMatInstanceCallBack),
 *  \li \ref RwIm3DPS2AllBridgeCallBack (see also \ref RxPipelineNodePS2AllMatBridgeCallBack),
 *  \li _rwIm3DPS2AllIm3DPostMeshCallBack (undocumented),
 *  \li _rwIm3DPS2AllMeshSplitCallBack (undocumented),
 *  \li \ref RwIm3DPS2AllPostMeshCallBack (see also \ref RxPipelineNodePS2AllMatPostMeshCallBack)
 *
 *
 * \param  type     \ref RwPrimitiveType of the pipeline to fetch.
 *
 * \return Returns pointer to the pipeline if successful or NULL if there
 * is an error.
 *
 * \see RxNodeDefinitionGetPS2AllMat
 * \see RxPipelineNodePS2AllMatResEntryAllocCallBack
 * \see RxPipelineNodePS2AllMatInstanceCallBack
 * \see RxPipelineNodePS2AllMatBridgeCallBack
 * \see RxPipelineNodePS2AllMatPostMeshCallBack
 * \see RxNodeDefinitionGetPS2All
 * \see RwIm3DSkyGetPS2AllRenderPipeline
 * \see RwIm3DSkyGetPS2ManagerTransformPipeline
 * \see RwIm3DGetGenericTransformPipeline
 * \see RwIm3DGetGenericRenderPipeline
 * \see RwIm3DRenderIndexedPrimitive
 * \see RwIm3DRenderPrimitive
 * \see RwIm3DRenderTriangle
 * \see RwIm3DRenderLine
 * \see RwIm3DTransform
 * \see RwIm3DEnd
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DGetRenderPipeline
 * \see RwIm3DSetRenderPipeline
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DGetTransformPipeline
 * \see RwIm3DSetTransformPipeline
 */
RxPipeline *
RwIm3DSkyGetPS2AllMatPipeline(RwPrimitiveType type)
{
    RWAPIFUNCTION(RWSTRING("RwIm3DSkyGetPS2AllMatPipeline"));

    /* The same pipe handles all primitive types... except
     * pointLists which'd require non-default vector code */
    if (type == rwPRIMTYPEPOINTLIST) RWRETURN((RxPipeline *)NULL);

    RWRETURN(RWIMMEDIGLOBAL(ps2AllMatIm3DPipeline));
}


/* Yes, this is an RPWORLD file trying to overload the operation
 * of Im3D - it is a temporary fix to get Im3D working with nodePS2All
 * (or PS2Manager) and is simply the fastest way to get it working.
 *
 * This will be put into rwIm3D[Create|Destroy]TransformPipeline and
 * rwIm3D[Create|Destroy]RenderPipelines in src/pipe/p2/sky2/im3dpipe.c
 * once nodePS2All is in the core.
 *
 * This gist is that the Im3D transform pipe is replaced with a 'fake'
 * (virtually empty) transform node and all render pipes are replaced with
 * the same nodePS2All pipeline - which instances into DMA chains and
 * transforms on VU1 for much speedliness */

RwBool
_rwfastIm3DPipesOpen(void)
{
    RxPipeline *transformPipe, *ps2AllRenderPipe;
    RxPipeline *ps2ManagerRenderPipe, *ps2AllMatRenderPipe;

    RWFUNCTION(RWSTRING("_rwfastIm3DPipesOpen"));

    /* The same transform pipeline works with both the
     * PS2All and the PS2Manager render pipelines */
    transformPipe = fastIm3DTransformPipeCreate();
    if (transformPipe != NULL)
    {
        ps2ManagerRenderPipe = CreateIm3DPS2ManagerRenderPipeline();
        if (ps2ManagerRenderPipe != NULL)
        {
            ps2AllMatRenderPipe = CreateIm3DPS2AllMatPipeline();
            if (ps2AllMatRenderPipe != NULL)
            {
                ps2AllRenderPipe = CreateIm3DPS2AllRenderPipeline(ps2AllMatRenderPipe);
                if (ps2AllRenderPipe != NULL)
                {
                    RWIMMEDIGLOBAL(platformIm3DTransformPipeline) = transformPipe;

                    /* The default pipes now use PS2All[+PS2AllMat] */
                    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).triList  = ps2AllRenderPipe;
                    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).triFan   = ps2AllRenderPipe;
                    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).triStrip = ps2AllRenderPipe;
                    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).lineList = ps2AllRenderPipe;
                    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).polyLine = ps2AllRenderPipe;
                    RWIMMEDIGLOBAL(ps2AllMatIm3DPipeline) = ps2AllMatRenderPipe;
                    /* The pipe could be used with pointLists, but you'd
                     * need new vector code matey */

                    /* PS2AllManager pipe not used by default */
                    RWIMMEDIGLOBAL(ps2ManagerIm3DRenderPipeline) = ps2ManagerRenderPipe;

                    /* This will all be thrown away and unified with
                     * worldsectors/atomics once everything's based on meshes. We
                     * now support all primitive types (triFans/polyLines faked) and
                     * we added more spaces in the VUCODEARRAY to support lines.
                     * We may need more to support culling in addition to clipping
                     * and non-clipping. */
                    RwIm3DSetTransformPipeline(transformPipe);
                    RwIm3DSetRenderPipeline(ps2AllRenderPipe, rwPRIMTYPETRILIST);
                    RwIm3DSetRenderPipeline(ps2AllRenderPipe, rwPRIMTYPETRIFAN);
                    RwIm3DSetRenderPipeline(ps2AllRenderPipe, rwPRIMTYPETRISTRIP);
                    RwIm3DSetRenderPipeline(ps2AllRenderPipe, rwPRIMTYPELINELIST);
                    RwIm3DSetRenderPipeline(ps2AllRenderPipe, rwPRIMTYPEPOLYLINE);

                    RWRETURN(TRUE);
                }
                RxPipelineDestroy(ps2AllMatRenderPipe);
            }
            RxPipelineDestroy(ps2ManagerRenderPipe);
        }
        RxPipelineDestroy(transformPipe);
    }

    RWRETURN(FALSE);
}

void
_rwfastIm3DPipesClose(void)
{
    RWFUNCTION(RWSTRING("_rwfastIm3DPipesClose"));

    /* Now these will be reset to the generic versions */
    RwIm3DSetRenderPipeline((RxPipeline *)NULL, rwPRIMTYPEPOLYLINE);
    RwIm3DSetRenderPipeline((RxPipeline *)NULL, rwPRIMTYPELINELIST);
    RwIm3DSetRenderPipeline((RxPipeline *)NULL, rwPRIMTYPETRISTRIP);
    RwIm3DSetRenderPipeline((RxPipeline *)NULL, rwPRIMTYPETRIFAN);
    RwIm3DSetRenderPipeline((RxPipeline *)NULL, rwPRIMTYPETRILIST);
    RwIm3DSetTransformPipeline((RxPipeline *)NULL);

    /* Destroy PS2All pipes first since we're going in reverse order */
    RxPipelineDestroy(RWIMMEDIGLOBAL(ps2AllMatIm3DPipeline));
    RWIMMEDIGLOBAL(ps2AllMatIm3DPipeline)                = (RxPipeline *)NULL;

    /* Same render pipe used for all primitive types */
    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).polyLine = (RxPipeline *)NULL;
    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).lineList = (RxPipeline *)NULL;
    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).triFan   = (RxPipeline *)NULL;
    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).triStrip = (RxPipeline *)NULL;
    RxPipelineDestroy(RWIMMEDIGLOBAL(platformIm3DRenderPipelines).triList);
    RWIMMEDIGLOBAL(platformIm3DRenderPipelines).triList  = (RxPipeline *)NULL;

    RxPipelineDestroy(RWIMMEDIGLOBAL(ps2ManagerIm3DRenderPipeline));
    RWIMMEDIGLOBAL(ps2ManagerIm3DRenderPipeline)         = (RxPipeline *)NULL;

    RxPipelineDestroy(RWIMMEDIGLOBAL(platformIm3DTransformPipeline));
    RWIMMEDIGLOBAL(platformIm3DTransformPipeline)        = (RxPipeline *)NULL;

    RWRETURNVOID();

}

