/*
 * nodeps2matbridge
 * A PS2-specific node to bridge from a CPU-based pipeline  to a VU-based pipeline
 *
 * Copyright (c) Criterion Software Limited
 */

/****************************************************************************
 *                                                                          *
 * module : nodeps2matbridge.c                                              *
 *                                                                          *
 * purpose: yawn...                                                         *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 includes
 */
#include "rwcore.h"
#include "nodeps2matinstance.h"
#include "nodeps2matbridge.h"

#if (!defined(DOXYGEN))
static const char rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: nodeps2matbridge.c,v 1.127 2001/09/12 19:09:24 iestynb Exp $";
#endif /* (!defined(DOXYGEN)) */



/****************************************************************************
 local defines
 */
static RxClusterRef nodeClusters[] = {
    {&RxClPS2DMASessionRecord, rxCLALLOWABSENT, rxCLRESERVED},
    {&RxClPS2Mesh, rxCLALLOWABSENT, rxCLRESERVED}
};

#define PRIVATEDATATYPE rwPS2MatBridgePvtData

#define NUMCLUSTERSOFINTEREST \
    ((sizeof(nodeClusters))/(sizeof(nodeClusters[0])))

#define MESSAGE(_string) \
    RwDebugSendMessage(rwDEBUGMESSAGE, "PS2MatBridge.csl", (_string))

/****************************************************************************
 local (static) globals
 */

/****************************************************************************
 local (static) prototypes
 */


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

   Functions

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

/****************************************************************************
 ConstructionTimeCode
 */

static              RwBool      /* success? */
ConstructionTimeCode(RxPipelineNode * __RWUNUSED__ self,
                     rwPS2MatBridgePvtData * pvtdata)
{
    RwBool              result = FALSE; /* fail, unless explicitly set TRUE */

    RWFUNCTION(RWSTRING("ConstructionTimeCode"));

    /* At present not a lot is done here */

    pvtdata->VU1CodeArray = (void **)NULL; /* actually, wanna set to driver default */
    /* default = (1024 - 16 - 13 - 32)/2 = 481
     * [This is also used in initVU1DispatchStuff() in skyinst.c
     *  CHANGE BOTH NOT ONE - KEEP 'EM SYNCH'D!] */
    pvtdata->vifOffset = 481;

    /* MatBridge handles texture setup itself by default */
    pvtdata->noTexture = FALSE;

    result = TRUE;             /* success */

    RWRETURN(result);
}
/****************************************************************************
 _rwPS2MatBridgePipelineNodeInit()

 called at construction time
 */

RwBool
_rwPS2MatBridgePipelineNodeInit(RxPipelineNode * self)
{
    RwBool              result = FALSE; /* fail, unless explictly set TRUE */
    rwPS2MatBridgePvtData *pvtdata;

    RWFUNCTION(RWSTRING("_rwPS2MatBridgePipelineNodeInit"));

    pvtdata = (rwPS2MatBridgePvtData *) self->privateData;

    RWASSERT(pvtdata != NULL);

    /* OI!! TODO: is this memset necessary? There are three flippin' members... */
    memset(pvtdata, 0x00U, sizeof(rwPS2MatBridgePvtData));

    result = ConstructionTimeCode(self, pvtdata); /* success? */

    RWRETURN(result);
}

/****************************************************************************
 PS2MatBridgeNodeBody()
 */

static              RwBool
PS2MatBridgeNodeBody(RxPipelineNodeInstance * self,
                     const RxPipelineNodeParam * __RWUNUSED__ params)
{
    RwBool                 result = TRUE;
    RxPacket              *pk;
    rwPS2MatBridgePvtData *pvtdata;
    RxCluster             *clDMASessionRecord;
    RxCluster             *clMesh;
    RxPS2DMASessionRecord *dmaSeshRec;
    RxPS2Mesh             *ps2Mesh;

    RWFUNCTION(RWSTRING("PS2MatBridgeNodeBody"));

    RWASSERT(NULL != self);

    pvtdata = (rwPS2MatBridgePvtData *) self->privateData;
    RWASSERT(NULL != pvtdata);

    pk = (RxPacket *)RxPacketFetch(self);
    RWASSERT((RxPacket *)NULL != pk);

    clDMASessionRecord = RxClusterLockRead(pk, 0);
    RWASSERT((NULL != clDMASessionRecord) &&
             (clDMASessionRecord->numUsed > 0));
    clMesh = RxClusterLockRead(pk, 1);
    RWASSERT((NULL != clMesh) && (clMesh->numUsed > 0));

    dmaSeshRec =
        RxClusterGetCursorData(clDMASessionRecord, RxPS2DMASessionRecord);
    ps2Mesh =
        RxClusterGetCursorData(clMesh, RxPS2Mesh);

    result = _rwRabinsBridgeNodeCode(dmaSeshRec, ps2Mesh, pvtdata);

#if (defined(RWMETRICS))
    /* We don't count lines */
    if (!(dmaSeshRec->transType & TRANSLINE))
    {
        if (dmaSeshRec->transType & TRANSLIST)
        {
            RWSRCGLOBAL(metrics)->numProcTriangles +=
                ps2Mesh->mesh->numIndices / 3;
        }
        else
        {
            RWSRCGLOBAL(metrics)->numProcTriangles +=
                ps2Mesh->mesh->numIndices - 2;
        }
    }
#endif /* (defined(RWMETRICS)) */

    /* Packet will be destroyed automatically, let us not
     * bloat code by doing it here */

    RWRETURN(result);
}

/**
 * \ingroup rpworldp2sky2
 * \ref RxPipelineNodePS2MatBridgeSetVIFOffset is used to set the VIF offset that
 * is used when processing multiple batches of data. If this function is
 * not called, a value suitable for the default code fragements and static
 * data allocations will be used. For user provided VU1 code, this value
 * can be obtained from the pipeline specific header file generated by
 * processing stddata.i
 *
 * \param  self      A pointer to the current \ref RxPipelineNode
 * \param vifOffset  Value of VIF double buffer offset to be used
 * by this pipe
 *
 * \return a pointer to this node on success, otherwise NULL.
 *
 * \see RxPipelineNodePS2MatInstanceGenerateCluster
 * \see RxPipelineNodePS2MatInstanceSetVUBufferSizes
 * \see RxPipelineNodePS2MatBridgeSetVU1CodeArray
 * \see RxPipelineNodePS2MatBridgeGetVU1CodeArray
 * \see RxNodeDefinitionGetPS2MatInstance
 * \see RxNodeDefinitionGetPS2ObjAllInOne
 */

RxPipelineNode     *
RxPipelineNodePS2MatBridgeSetVIFOffset(RxPipelineNode * self, RwInt32 vifOffset)
{
    RxPipelineNode     *result = (RxPipelineNode *)NULL;
    /* failure, unless set otherwise */

    RWAPIFUNCTION(RWSTRING("RxPipelineNodePS2MatBridgeSetVIFOffset"));

    RWASSERT(self != NULL);
    if (self != NULL)
    {
        RwBool              pipelineNotLocked;

        pipelineNotLocked = (self->slotClusterRefs != NULL);

        RWASSERT(pipelineNotLocked);
        if (pipelineNotLocked)
        {
            rwPS2MatBridgePvtData *pvtdata;

            pvtdata = (rwPS2MatBridgePvtData *) self->privateData;

            RWASSERT(pvtdata != NULL);
            if (pvtdata != NULL)
            {
                pvtdata->vifOffset = vifOffset;
            }

            result = self;     /* success */
        }
    }

    RWRETURN(result);
}

/**
 * \ingroup rpworldp2sky2
 * \ref RxPipelineNodePS2MatBridgeSetVU1CodeArray is a post construction time
 * API function which permits setting of the VU code fragments to be
 * used by this pipeline.
 *
 * The bridge node connects its main CPU based pipeline to up to 16
 * different VU1 based pipelines. These are provided as an array of
 * 16 pointers to callable DMA chain mode packets that will upload
 * code to location 0 onward on VU1. The array is indexed using the
 * TRANS* bit values stored in the RxClPS2DMASessionRecord cluster
 * (there are 4 such toggles, hence 16 combinations - note that user
 * created code fragments may be used and TRANS* values set by a user
 * node if the default clip/list/iso/fog values set by
 * PS2WorldSectorObjAllInOne.csl/
 * PS2AtomicObjAllInOne.csl are not appropriate).
 *
 *      \li rxSKYTRANSTYPEFOG:  This transform fogs.
 *      \li rxSKYTRANSTYPECLIP: This transform can cope with geometry which will
 *                              project out side the overdraw area.
 *      \li rxSKYTRANSTYPELIST: This is a trilist transform.
 *      \li rxSKYTRANSTYPEISO:  This transform is used for isometric projection.
 *      \li rxSKYTRANSTYPELINE: Draw linelists/linestrips, not trilists/tristrips.
 *
 * If this function is not called, or is called with NULL, the default
 * VU1 based code fragments will be used. If an array element is NULL,
 * the default VU1 code fragment for that combination of types will be
 * used (a useful debugging procedure is to fill unused slots with some
 * simple VU1 code that ignores input data and merely serves to alert
 * you that that code slot is being used when it shouldn't - a big red
 * triangle drawn over the screen for instance, something immediately
 * recognisable)..
 *
 * NOTE: For a pointlist pipeline, the first half of the code array
 * should be used - i.e the TRANSTRI slots are reused.
 * (see \ref RxPipelineNodePS2MatInstanceSetPointListVUBufferSize)
 *
 * \param  self         A pointer to the current \ref RxPipelineNode
 * \param VU1CodeArray  An array of 16 pointers to a callable dma
 * chain which will upload code and return. This is used by reference.
 *
 * \return a pointer to this node on success, otherwise NULL.
 *
 * \see RxPipelineNodePS2MatInstanceGenerateCluster
 * \see RxPipelineNodePS2MatInstanceSetVUBufferSizes
 * \see RxPipelineNodePS2MatInstanceSetPointListVUBufferSize
 * \see RxPipelineNodePS2MatBridgeGetVU1CodeArray
 * \see RxPipelineNodePS2MatBridgeSetVIFOffset
 * \see RxNodeDefinitionGetPS2MatInstance
 * \see RxNodeDefinitionGetPS2ObjAllInOne
 */

RxPipelineNode     *
RxPipelineNodePS2MatBridgeSetVU1CodeArray(RxPipelineNode * self, void **VU1CodeArray)
{
    RxPipelineNode     *result = (RxPipelineNode *)NULL;
    /* failure, unless set otherwise */

    RWAPIFUNCTION(RWSTRING("RxPipelineNodePS2MatBridgeSetVU1CodeArray"));

    RWASSERT(self != NULL);
    RWASSERT(VU1CodeArray != NULL);
    if (self != NULL && VU1CodeArray != NULL)
    {
        RwBool              pipelineNotLocked;

        pipelineNotLocked = (self->slotClusterRefs != NULL);

        RWASSERT(pipelineNotLocked);
        if (pipelineNotLocked)
        {
            rwPS2MatBridgePvtData *pvtdata;

            pvtdata = (rwPS2MatBridgePvtData *) self->privateData;

            RWASSERT(pvtdata != NULL);
            if (pvtdata != NULL)
            {
                pvtdata->VU1CodeArray = VU1CodeArray;
            }

            result = self;     /* success */
        }
    }

    RWRETURN(result);
}

/**
 * \ingroup rpworldp2sky2
 * \ref RxPipelineNodePS2MatBridgeGetVU1CodeArray is a post construction time
 * time API function which retrieves an array of pointers to the VU
 * code fragments currently to be used by this pipeline. It mirrors
 * \ref RxPipelineNodePS2MatBridgeSetVU1CodeArray
 *
 * \param  self         A pointer to the current \ref RxPipelineNode
 *
 * \return a pointer to the current VU1 code array on success,
 * otherwise NULL.
 *
 * \see RxPipelineNodePS2MatInstanceGenerateCluster
 * \see RxPipelineNodePS2MatInstanceSetVUBufferSizes
 * \see RxPipelineNodePS2MatBridgeSetVU1CodeArray
 * \see RxPipelineNodePS2MatBridgeSetVIFOffset
 * \see RxNodeDefinitionGetPS2MatInstance
 * \see RxNodeDefinitionGetPS2ObjAllInOne
 */

const void **
RxPipelineNodePS2MatBridgeGetVU1CodeArray(RxPipelineNode * self)
{
    RWAPIFUNCTION(RWSTRING("RxPipelineNodePS2MatBridgeGetVU1CodeArray"));

    RWASSERT(self != NULL);
    if (self != NULL)
    {
        RwBool pipelineNotLocked;

        pipelineNotLocked = (self->slotClusterRefs != NULL);

        RWASSERT(pipelineNotLocked);
        if (pipelineNotLocked)
        {
            rwPS2MatBridgePvtData *pvtdata;

            pvtdata = (rwPS2MatBridgePvtData *) self->privateData;

            RWASSERT(pvtdata != NULL);
            if (pvtdata != NULL)
            {
                if (NULL != pvtdata->VU1CodeArray)
                {
                    RWRETURN((const void **)(pvtdata->VU1CodeArray));
                }
                else
                {
                    /* The default */
                    RWRETURN((const void **)(&(skyVU1Transforms[0])));
                }
            }
        }
    }

    RWRETURN(NULL);
}

/**
 * \ingroup rpworldp2sky2
 * \ref RxPipelineNodePS2MatBridgeNoTexture is a post construction time
 * API which permits preventing the bridge node from uploading
 * any texture information.
 *
 * By default, the bridge node will upload the raster for each mesh's
 * texture to video memory, just before the geometry of that mesh is
 * processed by VU1. It will also set up the appropriate filtering
 * and addressing modes for the texture. This API function can be used
 * to prevent this from happening. This is useful if a custom user
 * node (inserted in the pipeline before the bridge node) is able
 * to set up textures itself more efficiently than can the bridge
 * node (given that it is written based on knowledge of the current
 * application), if the custom node needs to choose the texture to
 * upload from somewhere other than the object's materials, etc.
 *
 * This function must be called after unlocking the pipeline
 * containing the bridge node.
 *
 * \param  self      A pointer to the current \ref RxPipelineNode
 * \param noTexture  TRUE to prevent the bridge node from
 * uploading texture information, FALSE (default) to allow it
 *
 * \return a pointer to this node on success, otherwise NULL.
 *
 * \see RxPipelineNodePS2MatInstanceGenerateCluster
 * \see RxPipelineNodePS2MatInstanceSetVUBufferSizes
 * \see RxPipelineNodePS2MatBridgeSetVU1CodeArray
 * \see RxPipelineNodePS2MatBridgeGetVU1CodeArray
 * \see RxPipelineNodePS2MatBridgeSetVIFOffset
 * \see RxNodeDefinitionGetPS2MatInstance
 * \see RxNodeDefinitionGetPS2ObjAllInOne
 */
RxPipelineNode *
RxPipelineNodePS2MatBridgeNoTexture(RxPipelineNode * self,
                            RwBool noTexture)
{
    RxPipelineNode *result = (RxPipelineNode *)NULL;
    /* failure, unless set otherwise */

    RWAPIFUNCTION(RWSTRING("RxPipelineNodePS2MatBridgeNoTexture"));

    RWASSERT(self != NULL);
    if (self != NULL)
    {
        RwBool pipelineNotLocked;

        pipelineNotLocked = (self->slotClusterRefs != NULL);

        RWASSERT(pipelineNotLocked);
        if (pipelineNotLocked)
        {
            rwPS2MatBridgePvtData *pvtdata;

            pvtdata = (rwPS2MatBridgePvtData *) self->privateData;

            RWASSERT(pvtdata != NULL);
            if (pvtdata != NULL)
            {
                pvtdata->noTexture = noTexture;
            }

            result = self;     /* success */
        }
    }

    RWRETURN(result);
}

/**
 * \ingroup rpworldp2sky2
 * \ref RxNodeDefinitionGetPS2MatBridge returns a pointer to a node
 * to bridge the bottom of a main CPU based material pipeline to a set
 * of VU1 based pipelines. This node has an API for use both during and
 * after pipeline construction.
 *
 * First, the appropriate chunk of VU1 code (see
 * \ref RxPipelineNodePS2MatBridgeSetVU1CodeArray) is uploaded (unless it is already
 * on VU1), then renderstate is specified (including management of the
 * texture cache, with respect to this mesh's material's texture). Finally,
 * material colour and surface properties are uploaded and the transfer of
 * mesh geometry data is kicked off (or queued, rather, since older DMA
 * transfers will still be executing).
 *
 * This node destroys packets after it has initiated dispatch to the VU1
 * (this may change but it is done this way currently to avoid mesh
 * data being modified before its DMA transfer has completed).
 *
 * The node has no outputs
 * The input requirements of this node:
 *
 *      \li RxClPS2DMASessionRecord  - required
 *      \li RxClPS2Mesh              - required
 *
 * \return pointer to a node to bridge packets between the CPU and VU1
 *
 * \see RxPipelineNodePS2MatInstanceGenerateCluster
 * \see RxPipelineNodePS2MatInstanceSetVUBufferSizes
 * \see RxPipelineNodePS2MatBridgeSetVIFOffset
 * \see RxPipelineNodePS2MatBridgeSetVU1CodeArray
 * \see RxPipelineNodePS2MatBridgeGetVU1CodeArray
 * \see RxNodeDefinitionGetPS2MatInstance
 * \see RxNodeDefinitionGetPS2ObjAllInOne
 */

RxNodeDefinition   *
RxNodeDefinitionGetPS2MatBridge(void)
{

    /*******************************************
     **                                       **
     **  PS2MATBRIDGE.CSL NODE SPECIFICATION  **
     **                                       **
     *******************************************/

    /* input requirements (this array parallel to ClusterRefs) */
    static RxClusterValidityReq nodeReqs[NUMCLUSTERSOFINTEREST] = {
        rxCLREQ_REQUIRED, rxCLREQ_REQUIRED
    };

    static RwChar       _PS2MatBridge_csl[] =
        RWSTRING("PS2MatBridge.csl");

    static RxNodeDefinition nodePS2MatBridgeCSL = {
        _PS2MatBridge_csl,      /* Name */
        {                      /* nodemethods */
         PS2MatBridgeNodeBody, /* +-- nodebody */
         (RxNodeInitFn) NULL,  /* +-- nodeinit */
         (RxNodeTermFn) NULL,  /* +-- nodeterm */
         _rwPS2MatBridgePipelineNodeInit, /* +-- pipelinenodeinit */
         (RxPipelineNodeTermFn) NULL, /* +-- pipelineNodeTerm */
         (RxPipelineNodeConfigFn) NULL, /* +-- pipelineNodeConfig */
         (RxConfigMsgHandlerFn) NULL /* +-- configMsgHandler */
         },
        {                      /* Io */
         NUMCLUSTERSOFINTEREST, /* +-- NumClustersOfInterest */
         nodeClusters,         /* +-- ClustersOfInterest */
         nodeReqs,             /* +-- InputRequirements */
         0,                    /* +-- NumOutputs */
         (RxOutputSpec *)NULL  /* +-- Outputs */
         },
        sizeof(PRIVATEDATATYPE), /* PipelineNodePrivateDataSize */
        (RxNodeDefEditable)FALSE,/* editable */
        (RwInt32) 0            /* inPipes */
    };

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

    RxNodeDefinition   *result = &nodePS2MatBridgeCSL;

    RWAPIFUNCTION(RWSTRING("RxNodeDefinitionGetPS2MatBridge"));

    /*RWMESSAGE((RWSTRING("Pipeline II node"))); */

    RWRETURN(result);
}



/****************************************************************************
 _rwRabinsBridgeNodeCode
 */

#if (defined(__MWERKS__))

#if (defined(RWVERBOSE))
#pragma message (__FILE__ "/" _SKY_EXPAND(__LINE__) ": __MWERKS__ == " _SKY_EXPAND(__MWERKS__))
#endif /* (defined(RWVERBOSE)) */

#pragma optimization_level 0
#pragma message("#pragma optimization_level 0")

#endif /* (defined(__MWERKS__)) */

RwBool                         /* success? */
_rwRabinsBridgeNodeCode(const RxPS2DMASessionRecord * DMASessionRecord,
                        const RxPS2Mesh * Mesh,
                        rwPS2MatBridgePvtData * pvtdata)
{
    rwPS2ResEntryHeader *ps2ResHeader;
    const RpMeshHeader *meshHeader;
    const void         *code;
    RwBool              matModulate;
    RwReal              scale = 0.0f;
    RwBool              sync = FALSE;
#if (defined(VUCONTINUE))
    RwBool              newcode = FALSE;
#endif /* (defined(VUCONTINUE)) */

    RWFUNCTION(RWSTRING("_rwRabinsBridgeNodeCode"));

    /* the mesh header plays a small part in this function so localise it */
    if (DMASessionRecord->objType == rxOBJTYPE_WORLDSECTOR)
    {
        RpWorld *world = (RpWorld *) RWSRCGLOBAL(curWorld);
        meshHeader = DMASessionRecord->sourceObject.worldSector->mesh;
        matModulate = ( RpWorldGetFlags(world) &
                        rpWORLDMODULATEMATERIALCOLOR)?TRUE:FALSE;

        /* For now we sync d-cache here. We should do this less often */
        if (DMASessionRecord->serialNum != meshHeader->serialNum) sync = TRUE;
    }
    else if (DMASessionRecord->objType == rxOBJTYPE_ATOMIC)
    {
        RpGeometry *geom =
            RpAtomicGetGeometry(DMASessionRecord->sourceObject.atomic);
        RpInterpolator *interpolator =
           &DMASessionRecord->sourceObject.atomic->interpolator;

        meshHeader = geom->mesh;
        matModulate = ( RpGeometryGetFlags(geom) &
                        rpGEOMETRYMODULATEMATERIALCOLOR )?TRUE:FALSE;
        if (geom->numMorphTargets > 1)
        {
            scale = ((interpolator->recipTime)*(interpolator->position));
        }

        /* For now we sync d-cache here. We should do this less often */
        if ((!(geom->instanceFlags & rpGEOMETRYPERSISTENT) &&
             (DMASessionRecord->serialNum != meshHeader->serialNum)) ||
            (geom->instanceFlags & rpGEOMETRYINSTANCE))
        {
            sync = TRUE;
        }
    }
    else /* (DMASessionRecord->objType == rxOBJTYPE_IM3D) */
    {
        meshHeader = ((const RpMeshHeader *) Mesh->mesh) - 1;
        /* Always sync DCache here for Im3D
         * 'till the data's persistent... */
        sync = TRUE;
        matModulate = FALSE;
    }

    ps2ResHeader =
        (rwPS2ResEntryHeader *) ( (RwResEntry *)(*(Mesh->cacheEntryRef)) + 1);

    /* Sync d-cache here. We should do this less often. */
    if (FALSE != sync)
    {
        /* Sync from the start of the data (end of the RwResEntry and
         * rwPS2ResEntryHeader headers) to its end. We don't need to
         * use SCESYNCDCACHEROUNDUP cos our data is QW-aligned and
         * completely contained within the resEntry */
        SyncDCache(ps2ResHeader->data,
                   (RwUInt8 *) (*(Mesh->cacheEntryRef)) +
                   sizeof(RwResEntry) +
                   (*(Mesh->cacheEntryRef))->size
                   + 195);
    }


    /* We potentially have to deal with code upload here */
    /* TRANS{,N}FOG and TRANS{PER,ISO} from skyTransType */
    if (NULL != pvtdata->VU1CodeArray)
    {
        code = pvtdata->VU1CodeArray[DMASessionRecord->vu1CodeIndex];
        if (NULL == code)
        {
            code = skyVU1Transforms[DMASessionRecord->vu1CodeIndex];
        }
    }
    else
    {
        code = skyVU1Transforms[DMASessionRecord->vu1CodeIndex];
    }

    if (skyUploadedCode != code)
    {
        RwUInt64            tmp, tmp1;
        u_long128           ltmp = 0;

        sweFinaliseOpenLocalPkt(SWE_LPS_NOFIXUP |
                                SWE_PKT_DMA_MODE_CHAIN_TTE |
                                SWE_PKT_LOCAL | SWE_PKT_VU1 |
                                SWE_PKT_CIRCALLOC, -2);

        RWASSERT(NULL != sweLocalPacket);

        /* dma ref to code upload, and set base/offset */
        tmp = (5l << 28) | (RwUInt64) ((RwUInt32) code) << 32;
        tmp1 =
            (3l << 24) | 0 | ((2l << 24) | (pvtdata->vifOffset)) << 32;
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);
        skyUploadedCode = code;

        tmp = (0xfl << 28);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);

        /* Add a "CONT" packet to allow DMA packets to be continued
         * after VU code upload.
         * Without this statement, the DMA packet stream has
         * to be stopped and restarted which introduces
         * a large DMA management overhead. */
        sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
#if (defined(VUCONTINUE))
        newcode = TRUE;
#endif /* (defined(VUCONTINUE)) */
    }

    /* For now, we setup stuff like textures here.
     * Unless pvtData->noTexture is set (see RxPipelineNodePS2MatBridgeNoTexture())
     * Deal with the material if there is one */

    /* NOTE: We should probably not require materials. For now only
     *       Im3D gets away without one (uses current renderstate) */
    if ((FALSE == pvtdata->noTexture) &&
        ((NULL != Mesh->mesh->material) ||
         (DMASessionRecord->objType == rxOBJTYPE_IM3D)))
    {
        RpMaterial         *material = Mesh->mesh->material;
        RwTexture          *texture;
        RwTexture           im3DTexture;

        /* Alpha in vertices enabled */
        if (DMASessionRecord->objType == rxOBJTYPE_IM3D)
        {
            /* NOTE: skyVertexAlpha is tested inside
             *       _rwSkySetRenderState(rwRENDERSTATETEXTURERASTER,) */
            skyVertexAlpha = TRUE;

            /* For Im3D, texture modes are those set by RwRenderStateSet()
             * Skip extraneous work below, given the test:
             *  if (texture->raster != skyTextureRaster) */
            texture = &im3DTexture;
            texture->raster = skyTextureRaster;
        }
        else
        {
            /* Set up render state */
            texture = material->texture;
            skyVertexAlpha = (material->color.alpha != 255);
        }

        if (NULL != texture)
        {
            if (texture->raster != skyTextureRaster)
            {
                /* Raster to texture with */
                skyTextureRaster = texture->raster;

                skyAlphaTex = FALSE;
                if (NULL != skyTextureRaster)
                {
                    const RwUInt32           cFormat =
                        ( skyTextureRaster->cFormat &
                          (rwRASTERFORMATPIXELFORMATMASK >> 8) );

                    /* If an alpha format texture - enable alpha blending */
                    skyAlphaTex |=
                        ( ((rwRASTERFORMAT1555 >> 8) == cFormat) |
                          ((rwRASTERFORMAT8888 >> 8) == cFormat) );

                    skyPrim_State |= 0x10;

                    /* Do what it takes to get the raster selected */
                    skyTexCacheAccessRaster(skyTextureRaster, FALSE);

                }
                else
                {
                    skyPrim_State &= ~0x10l;
                }

                if (skyVertexAlpha || skyAlphaTex)
                {
                    skyPrim_State |= 0x40;
                }
                else
                {
                    skyPrim_State &= ~0x40l;
                }
                /* Finally iff skyAlphaTex we turn on Alpha test */
                if (skyAlphaTex)
                {
                    skyTest_1 |= 1;
                }
                else
                {
                    skyTest_1 &= ~1l;
                }
            }

            /* NOTE: the following is BAD.
             *       people can change texture filter/address modes
             *       behind our back, so we have to test that every damn time!
             *       As with everything else we need to introduce locking either
             *       of textures or parent objects such as materials/geometrys
             *       to avoid redundant effort here in the fast-path */

            /* For Im3D renderstate have been set already */
            if (DMASessionRecord->objType != rxOBJTYPE_IM3D)
            {

#if 0
                /* Switch statements hurt */
                if (texture->filtering == rwFILTERLINEARMIPNEAREST)
                {
                    skyTex1_1 = (skyTex1_1 & ~0x1e0l) | 0xc0;
                }
                else if (texture->filtering == rwFILTERLINEAR)
                {
                    skyTex1_1 = (skyTex1_1 & ~0x1e0l) | 0x60;
                }
                else if (texture->filtering == rwFILTERLINEARMIPLINEAR)
                {
                    skyTex1_1 = (skyTex1_1 & ~0x1e0l) | 0x160;
                }
                else if (texture->filtering == rwFILTERMIPNEAREST)
                {
                    skyTex1_1 = (skyTex1_1 & ~0x1e0l) | 0x80;
                }
                else if (texture->filtering == rwFILTERMIPLINEAR)
                {
                    skyTex1_1 = (skyTex1_1 & ~0x1e0l) | 0x120;
                }
                else
                {
                    /* must be rwFILTERNEAREST */
                    skyTex1_1 = skyTex1_1 & ~0x1e0l;
                }
#else
                /* A more efficient texture command production than a
                 * switch statement, which uses a jump table pulled into
                 * the d-cache from the text segment.
                 * ASSUMES THESE ENUM VALUES AREN'T GOING ANYWHERE  :-)
                 */
                /* *INDENT-OFF* */
                {
                    /* We try to force CW to work by using a temp var */
                    long tmp = 0;

                    asm __volatile__ (".set noreorder
                                       .set noat
                                       ori $at, $0, 0x6
                                       beql $at, %2, nps2mb0
                                       ori %0, %1, 0x160

                                       ori $at, $0, 0x5
                                       beql $at, %2, nps2mb0
                                       ori %0, %1, 0xc0

                                       ori $at, $0, 0x4
                                       beql $at, %2, nps2mb0
                                       ori %0, %1, 0x120

                                       ori $at, $0, 0x3
                                       beql $at, %2, nps2mb0
                                       ori %0, %1, 0x80

                                       ori $at, $0, 0x2
                                       beql $at, %2, nps2mb0
                                       ori %0, %1, 0x60

                                       ori %0,%1,0x0

                                      nps2mb0:
                                       .set reorder
                                       .set at
                                      " : "=r" (tmp)
                                      : "r" (skyTex1_1 & ~0x1e0l),
                                      "r" (texture->filtering));
                    skyTex1_1 = tmp;
                /* *INDENT-ON* */
                }
#endif

                /* Clamp, wrap, mirror or border.
                 * We now have two addressing modes,
                 * one for U and one for V directions.
                 * If the app has never set the V direction,
                 * then default both U and V to the setting
                 * from the U direction, which will have been set. */

                {
                    RwTextureAddressMode u;
                    RwTextureAddressMode v;

                    u = RwTextureGetAddressingU(texture);
                    v = RwTextureGetAddressingV(texture);

                    skyClamp_1 = 0; /* default to repeat in U and V */

                    if (u == rwTEXTUREADDRESSCLAMP)
                    {
                        skyClamp_1 |= 1;
                    }

                    if (v == rwTEXTUREADDRESSCLAMP)
                    {
                        skyClamp_1 |= 4;
                    }
                }
            }

            /* Now write to pkt */
            sweOpenLocalPkt(SWE_LPS_CONT, -4);

            {
                RwUInt64            tmp, tmp1;
                u_long128           ltmp = 0;

                tmp = /* NLOOP */ 3l
#if (defined(LESSEOPS))
                    | /* EOP */ (0l << 15)
#else /* (defined(LESSEOPS)) */
                    | /* EOP */ (1l << 15)
#endif /* (defined(LESSEOPS)) */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyTest_1;
                tmp1 = (GS_TEST_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);

                tmp = skyTex1_1;
                tmp1 = (GS_TEX1_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);

                tmp = skyClamp_1;
                tmp1 = (GS_CLAMP_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);
            }
        }
        else
        {
            _rwSkySetRenderState(rwRENDERSTATETEXTURERASTER, NULL);
        }
    }

    /* Start dispatch of triangles to VU1 */
#if (defined(VUCONTINUE))
    sweFinaliseOpenLocalPkt(SWE_PKT_VU1 | SWE_PKT_CIRCALLOC
                            | SWE_PKT_DMA_MODE_CHAIN_TTE |
                            SWE_LPS_NOFIXUP, -10);
#else /* (defined(VUCONTINUE)) */
    sweFinaliseOpenLocalPkt(SWE_PKT_VU1 | SWE_PKT_CIRCALLOC
                            | SWE_PKT_DMA_MODE_CHAIN_TTE |
                            SWE_LPS_NOFIXUP, -9);
#endif /* (defined(VUCONTINUE)) */

    /* Upload new gif tag and colscale and surface props */
    {
        RwUInt64            tmp, tmp1;
        u_long128           ltmp = 0;

        tmp = (1l << 28) | (6);
        tmp1 =
            (((0x6cl << 24) | (6l << 16) | (0x3fal)) << 32) |
            ((1l << 24) | (4 << 8) | (4));
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

        /* Modify the gif tag for the prim */
        /* Mask out old */
        ((RwUInt32 *) & gifTagPrim128)[1] &= ~(0x7f8 << (47 - 32));
        /* TODO: Set GS prim type here from DMASessionRecord->primType
         * NOTE: duplicated work done in openVU1SetupPkt() at end of objAllInOne code */
        ((RwUInt32 *) & gifTagPrim128)[1] |=
            (((skyPrim_State) & 0x7ff) << (47 - 32));
        /* GIF tag for 1 primitive using packed mode */
        SWEADDCONTFAST(gifTagPrim128);
    }

    /* Scale the colScale by the color to implement material color modulate
     * for free...! :-) */
    /* We have a sneaking suspicion that 128.0f should be 128.1f */
    {
        u_long128           ltmp = 0;
        RpMaterial         *material = Mesh->mesh->material;
        RwSurfaceProperties *surfProps;
        RwRGBA             *matCol;
        RwSurfaceProperties im3DSurfProps;
        RwRGBA              im3DMatCol;
        float               __floattmp1;
        float               __floattmp2;
        long                __longtmp;
#if (defined(__MWERKS__))
        #pragma unused (__floattmp1, __floattmp2, __longtmp)
#endif /* (defined(__MWERKS__)) */


        if (DMASessionRecord->objType == rxOBJTYPE_IM3D)
        {
            im3DSurfProps.ambient = ((RwReal)1);
            im3DSurfProps.diffuse = ((RwReal)1);
            im3DSurfProps.specular = ((RwReal)1);
            surfProps = &im3DSurfProps;
        }
        else
        {
            surfProps = &(material->surfaceProps);
        }

        if (matModulate == FALSE)
        {
            *(RwUInt32 *)&im3DMatCol = 0xffffffff;
            matCol = &im3DMatCol;
        }
        else
        {
            matCol = &(material->color);
        }


#if (0 && defined(__MWERKS__))
        if (skyPrim_State & 0x10)
        {
            ((RwReal *) & ltmp)[0] = ((RwReal) (128.1f / (255.0f * 255.0f)) *
                                      (RwReal) (matCol->red));
            ((RwReal *) & ltmp)[1] = ((RwReal) (128.1f / (255.0f * 255.0f)) *
                                      (RwReal) (matCol->green));
            ((RwReal *) & ltmp)[2] = ((RwReal) (128.1f / (255.0f * 255.0f)) *
                                      (RwReal) (matCol->blue));
            ((RwReal *) & ltmp)[3] = ((RwReal) (128.1f / (255.0f * 255.0f)) *
                                      (RwReal) (matCol->alpha));
        }
        else
        {
            ((RwReal *) & ltmp)[0] = ((RwReal) (255.0f / (255.0f * 255.0f)) *
                                      (RwReal) (matCol->red));
            ((RwReal *) & ltmp)[1] = ((RwReal) (255.0f / (255.0f * 255.0f)) *
                                      (RwReal) (matCol->green));
            ((RwReal *) & ltmp)[2] = ((RwReal) (255.0f / (255.0f * 255.0f)) *
                                      (RwReal) (matCol->blue));
            ((RwReal *) & ltmp)[3] = ((RwReal) (128.1f / (255.0f * 255.0f)) *
                                      (RwReal) (matCol->alpha));
        }
        SWEADDCONTFAST(ltmp);

        /* Surface prop */
        ((RwReal *) & ltmp)[0] = ( (255.00001f) *
                                   ((RwReal) (surfProps->ambient)) );
        ((RwReal *) & ltmp)[1] = ( (255.00001f) *
                                   ((RwReal) (surfProps->specular)) );
        ((RwReal *) & ltmp)[2] = ( (255.00001f) *
                                   ((RwReal) (surfProps->diffuse)) );

#else /* (0 && defined(__MWERKS__)) */

        if (skyPrim_State & 0x10)
        {
            /* *INDENT-OFF* */
            asm ("
                            mul.s %1, %7, %8
                            mul.s %2, %5, %8

                            mfc1 %0, %1
                            mfc1 %3, %2

                            pexew %0, %0
                            pexew %3, %3

                            mul.s %1, %6, %8
                            mul.s %2, %4, %8

                            mfc1 %0, %1
                            mfc1 %3, %2

                            ppacw %0, %0, %3

                    " : "=r" (ltmp),
                 "=f&" (__floattmp1),
                 "=f&" (__floattmp2),
                 "=r" (__longtmp):
                 "f" ((RwReal)(matCol->red)),
                 "f" ((RwReal)(matCol->green)),
                 "f" ((RwReal)(matCol->blue)),
                 "f" ((RwReal)(matCol->alpha)),
                 "f" ((128.1f/(255.0f*255.0f))) );
            /* *INDENT-ON* */

            /* , "f" ((((RwReal)1)/255.0f)) ); */

        }
        else
        {
            /* *INDENT-OFF* */
            asm ("
                            mul.s %1, %7, %9
                            mul.s %2, %5, %8

                            mfc1 %0, %1
                            mfc1 %3, %2

                            pexew %0, %0
                            pexew %3, %3

                            mul.s %1, %6, %8
                            mul.s %2, %4, %8

                            mfc1 %0, %1
                            mfc1 %3, %2

                            ppacw %0, %0, %3

                    " : "=r" (ltmp),
                 "=f&" (__floattmp1),
                 "=f&" (__floattmp2),
                 "=r" (__longtmp):
                 "f" ((RwReal)(matCol->red)),
                 "f" ((RwReal)(matCol->green)),
                 "f" ((RwReal)(matCol->blue)),
                 "f" ((RwReal)(matCol->alpha)),
                 "f" ((((RwReal)1)/255.0f)),
                 "f" ((128.1f/(255.0f*255.0f))));
            /* *INDENT-ON* */
        }
        SWEADDCONTFAST(ltmp);

        /* Surface prop */
        {
            /* *INDENT-OFF* */
#if (!defined(FASTMORPH))
            asm ("
                            mul.s %2, %5, %7

                            mfc1 %0, %8
                            mfc1 %3, %2

                            pexew %0, %0
                            pexew %3, %3

                            mul.s %1, %6, %7
                            mul.s %2, %4, %7

                            mfc1 %0, %1
                            mfc1 %3, %2

                            ppacw %0, %0, %3

                    " : "=r" (ltmp),
                 "=f&" (__floattmp1),
                 "=f&" (__floattmp2),
                 "=r" (__longtmp):
                 "f" ((RwReal)(surfProps->ambient)),
                 "f" ((RwReal)(surfProps->specular)),
                 "f" ((RwReal)(surfProps->diffuse)),
                 "f" (255.00001f),
                 "f" (0.0f) );
            /* *INDENT-ON* */
#else /* (!defined(FASTMORPH)) */
            asm ("
                            mul.s %2, %5, %7

                            mfc1 %0, %8
                            mfc1 %3, %2

                            pexew %0, %0
                            pexew %3, %3

                            mul.s %1, %6, %7
                            mul.s %2, %4, %7

                            mfc1 %0, %1
                            mfc1 %3, %2

                            ppacw %0, %0, %3

                    " : "=r" (ltmp),
                 "=f&" (__floattmp1),
                 "=f&" (__floattmp2),
                 "=r" (__longtmp):
                 "f" ((RwReal)(surfProps->ambient)),
                 "f" ((RwReal)(surfProps->specular)),
                 "f" ((RwReal)(surfProps->diffuse)),
                 "f" (255.00001f),
                 "f" (scale) );
#endif /* (!defined(FASTMORPH)) */
        }

#endif /* (0 && defined(__MWERKS__)) */
        SWEADDCONTFAST(ltmp);
    }

#if 1   /* Deactivate for old vu codes users */
    {
        u_long128       ltmp          = 0;
        RwUInt64        tmp1          = 0;
        RwUInt32        skySwitchFlag = (DMASessionRecord->transType & 7);

        if((skyTSClipperMode && (!(skySwitchFlag & 4))) ||
           (skyTLClipperMode && (skySwitchFlag & 4)))
        {
            skySwitchFlag = (skySwitchFlag | 8);
        }

        /* Add in a culling flag */
        skyUserSwitch1 = 0;
        if (gSkyCullState == rwCULLMODECULLFRONT)
        {
            skyUserSwitch1 = 0x20;
        }

#if (defined(VUCONTINUE))
        tmp1 = (1l<<32) | skyUserSwitch1;
#else /* (defined(VUCONTINUE)) */
        tmp1 = (((RwUInt64)skyUserSwitch2) << 32) | skyUserSwitch1;
#endif /* (defined(VUCONTINUE)) */
        MAKE128(ltmp, tmp1, skySwitchFlag);

        if (skySwitchFlag & 8)
        {
            /* Clipping. Use small frustum */
            SWEADDCONTFAST(skyCClipVect1);/* Camera Clipping info */

            SWEADDCONTFAST(skyCClipVect2);/* Camera Clipping info */
        }
        else
        {
            /* Culling. Use large frustum */
            SWEADDCONTFAST(skyClipVect1);/* Camera Clipping info */

            SWEADDCONTFAST(skyClipVect2);/* Camera Clipping info */
        }

        SWEADDCONTFAST(ltmp);        /* Dynamic Switches     */
     }

#endif

    /* "You are not expected to understand this" */
    /* The purpose is to make packets as long as possible. The mechanism
     * is described in the comment above sweFinaliseOpenLocalPkt()
     */
    {
        RwUInt64            tmp, tmp1;
        u_long128           ltmp = 0;

#if (defined(VUCONTINUE))
        if (newcode)
        {
            tmp = 1l<<28;
            tmp1 = 0x15l<<24;
            MAKE128(ltmp, tmp1, tmp);
            SWEADDCONTFAST(ltmp);
        }
#endif /* (defined(VUCONTINUE)) */
        tmp = ( (((RwUInt64) (RwUInt32) (ps2ResHeader->data)) << 32) |
                (5l << 28) );
        tmp1 = ( (3l << 24) |
                 0 |
                 ((2l << 24) |
                  (pvtdata->vifOffset)) << 32 ); /* Reset vifOffset */
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

        tmp = (0xfl << 28);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);
    }

    sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);

    /* Reference count stuff (used when objects are destroyed)
     * clrCnt improves efficiency by allowing many reference
     * increments in one packet to be performed in one go */
    ps2ResHeader = (rwPS2ResEntryHeader *)
                       ((RwResEntry *) (*(Mesh->cacheEntryRef)) + 1);
    DI();
    ps2ResHeader->refCnt += 1;
    EI();
    ps2ResHeader->clrCnt += 1;
    if (ps2ResHeader->clrCnt == 1)
    {
        ps2ResHeader = (rwPS2ResEntryHeader *)
                           ((RwResEntry *) (*(Mesh->cacheEntryRef)) + 1);
        _sweProcrastinatedAddURef((RwUInt32 *) &(ps2ResHeader->refCnt));
    }

    if (DMASessionRecord->objType == rxOBJTYPE_IM3D)
    {
        /* This gets the DMA chain memory freed automatically when DMA is done
         * with it (SWE_PKT_NULL -> freed by RwFree() hopefully) */

#if 0
        _sweAddPkt(*(Mesh->cacheEntryRef),
                   SWE_PKT_NULL | SWE_PKT_LOCAL);
#else
        _sweAddPkt(*(Mesh->cacheEntryRef),
                   SWE_PKT_NULL | SWE_PKT_CIRCALLOC | SWE_PKT_LOCAL);
#endif

    }

    RWRETURN(TRUE);
}


