R E T I R E D






/*
 * _doc nodePS2WorldSectorInstance
 * _topic TODO: nodePS2WorldSectorInstance |
 * _index | nodePS2WorldSectorInstance
 * _normal Copyright (c) Criterion Software Limited
 */
/****************************************************************************
 *                                                                          *
 * module : nodePS2WorldSectorInstance.c                                    *
 *                                                                          *
 * purpose: yawn...                                                         *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 includes
 */

#include <math.h>
#include <eekernel.h> /* SyncDCache(), etc. */
#include "rwcore.h"
#include "baworld.h"
#include "p2stdclsw.h"
#include "skyisms.h"
#include "nodePS2WorldSectorInstance.h"

static const char rcsid[] __RWUNUSED__ = "@@(#)$Id: nodePS2WorldSectorInstance.c,v 1.31 2000/09/29 16:08:46 iestynb Exp $";


/****************************************************************************
 local types
 */

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


/****************************************************************************
 local defines
 */

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

/*****************************************************/
/**                                                 **/
/**  PS2WORLDSECTORINSTANCE.CSL NODE SPECIFICATION  **/
/**                                                 **/
/*****************************************************/

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

/****************************************************************************
 functions
 */

/****************************************************************************
 _rwSkyInstanceDestroyCallback

 On entry : resRntry
 On exit  :

 If the Instance has a reference count, wait for it to reach 0, flushing as
 required.
 */

static void
_rwSkyInstanceDestroyCallback(RwResEntry *resEntry)
{
    volatile RwUInt32 *count = SKYINSTANCEREFCNT(resEntry);
    RWFUNCTION(RWSTRING("_rwSkyInstanceDestroyCallback"));

    PFENTRY(PF_rwSkyInstanceDestroyCallback);
    while (*count != 0)
    {
        PFCALL(PF_rwSkyInstanceDestroyCallback);
        _sweFlush();
        PFRET(PF_rwSkyInstanceDestroyCallback);
    }
    PFEXIT(PF_rwSkyInstanceDestroyCallback);

    RWRETURNVOID();
}

/****************************************************************************
 _rwQueuePS2DMAChainPacket
 */

static RwBool
_rwQueuePS2DMAChainPacket(RxPipelineNodeInstance *self, RwResEntry *repEntry)
{
    RwBool result = FALSE;
    RxPacket *pk;
    RWFUNCTION(RWSTRING("_rwQueuePS2DMAChainPacket"));

    if ( (pk = RxPacketCreate(self)) != NULL )
    {
        RxCluster *cl;

        cl = RxClusterLockWrite(pk, 0 /* RxClPS2DMAChain */, self);
        result = (cl != NULL);

        if (result)
        {
            /* as a rule, we're pretty scornful of creating clusters by hand */

            cl->flags      = rxCLFLAGS_CLUSTERVALID | rxCLFLAGS_EXTERNAL;
            cl->stride     = 4; /* DWORDS */
            cl->data       = repEntry;
            cl->numUsed    = \
            cl->numAlloced = (repEntry->size + 3U) & ~3U;

            RxClusterUnlock(cl);

            RxPacketDispatch(pk, 0, self);

            RWRETURN(result);
        }

        RxPacketDestroy(pk, self);
    }

    RWRETURN(result);
}

/****************************************************************************
 _rwSkyWorldSectorInstance

 On entry   : World sector
 On exit    : TRUE on success
 */

static RwResEntry *
_rwSkyWorldSectorInstance(void *object)
{
    int                     batchVertexCount, batchTriangleCount;
    int                     debugVertSize;
    RwReal                  *instxyzw;
    RwReal                  *instuv;
    RwUInt32                *instrgb;
    RwInt8                  *instnorm;
    int                     instbl;
    RpWorldSector           *worldSector = (RpWorldSector *)object;
    RpMeshHeader            *meshHeader;
    RpWorld                 *world = (RpWorld *)RWSRCGLOBAL(curWorld);
    RwInt32                 numVerts = worldSector->numPolygons * 3;
    RwUInt16                overloadFlags = rwObjectGetFlags(world) & WORLDRENDERTYPEMASK;
    RwInstDataPolyHeader    *polyHeader;
    u_long128               *instData;
    RpMesh                  *mesh;
    const RWIMVERTEXINDEX   *meshIndices;
    RwInt32                 numTriangles, numMeshes;

    RWFUNCTION(RWSTRING("_rwSkyWorldSectorInstance"));
    PFENTRY(PF_rwSkyWorldSectorInstance);
    RWASSERT(object);

    polyHeader = SKYPOLYHEADER(worldSector->repEntry);
    meshHeader = worldSector->mesh;

    if (numVerts && worldSector->repEntry)
    {
        /* Do we need to re-instance - has the app changed the representation */
        if ((polyHeader->serialNum != meshHeader->serialNum) ||
            (polyHeader->overloadFlags != overloadFlags))
        {
            /* Release resources to force re-instance */
            PFCALL(PF_rwSkyWorldSectorInstance);
            RwResourcesFreeResEntry(worldSector->repEntry);
            PFRET(PF_rwSkyWorldSectorInstance);
        }
    }

    if (!worldSector->repEntry)
    {
        /* Allocate some resources and initialise the static elements */
        RwResEntry     *repEntry;

        if (!numVerts)
        {
#ifdef INSTEMPTYSECTORS
            /* Dummy allocation such that atomics are not destroyed */
            PFCALL(PF_rwSkyWorldSectorInstance);
            if (!RwResourcesAllocateResEntry(worldSector, &worldSector->repEntry,
                                             sizeof(SkyInstDataExt) + sizeof(RwInstDataPolyHeader),
                                             _rwSkyInstanceDestroyCallback))
            {
                PFRET(PF_rwSkyWorldSectorInstance);
                PFEXIT(PF_rwSkyWorldSectorInstance);
                RWRETURN(NULL);
            }
            PFRET(PF_rwSkyWorldSectorInstance);

            /* Set refcount to be 0 */
            *SKYINSTANCEREFCNT(worldSector->repEntry) = 0;
            *SKYINSTANCECLRCNT(worldSector->repEntry) = 0;
            *SKYINSTANCEINFRUSTUM(worldSector->repEntry) = rwSPHEREBOUNDARY;

            /* Because this implementation of the pipeline is based on polygons. */
            polyHeader = SKYPOLYHEADER(worldSector->repEntry);

            polyHeader->xForm = NULL;

            polyHeader->vertices = NULL;
            SKYINSTANCEEND(repEntry) = NULL;

            polyHeader->numVertices = 0;
            polyHeader->sizeOfVertex = 0;  /* This doesn't make a whole lot of sense with a SCATTER build */
            
            polyHeader->polygons = NULL;
            polyHeader->numPolygons = 0;
            polyHeader->sizeOfPolygon = 0;
            polyHeader->firstPolygonOffset = 0;
            
            polyHeader->overloadFlags = 0;
            polyHeader->mesh = (void *)meshHeader;

            /* Copy serial num so we know if the app changes things */
            polyHeader->serialNum = meshHeader->serialNum;

            /* Make sure we can copy the flags across - need
             * commonality between the flag sets
             */
            RWASSERT(rpGEOMETRYTEXTURED == rpWORLDTEXTURED);
            RWASSERT(rpGEOMETRYPRELIT == rpWORLDPRELIT);
            RWASSERT(rpGEOMETRYNORMALS == rpWORLDNORMALS);
            RWASSERT(rpGEOMETRYLIGHT == rpWORLDLIGHT);
            polyHeader->flags = RpWorldGetFlags(world);

            /* Nothing done in the pipe yet */
            polyHeader->pipelineOpFlags = 0;

            PFEXIT(PF_rwSkyWorldSectorInstance);
            RWRETURN(worldSector->repEntry);
#else /* INSTEMPTYSECTORS */
            PFEXIT(PF_rwSkyWorldSectorInstance);
            RWRETURN(NULL);
#endif /* INSTEMPTYSECTORS */
        }

        {
            RwInt32                 sizeVert;
            RwInt32                 size;

            /* I need to run down the meshes here. Ugly */
            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);
            sizeVert = 0;
            while (numMeshes--)
            {
                numTriangles = mesh->numIndices/3;

                sizeVert += 32                                  /* dmaHeader & mask command */
                            + (((3*3*numTriangles+3)>>2)<<4)    /* xyzw */
                            + (((3*numTriangles+1)>>1)<<4)      /* uv */
                            + (((3*numTriangles+3)>>2)<<(4))    /* rgba */
                            + (numTriangles>>MAXVU1BATCHCNTSHIFT)*(((MAXVU1BATCHCNT*3*3)+15)&~0xf) + (((3*3)*(numTriangles&(MAXVU1BATCHCNT-1))+15)&~0xf) /* normals */
                            + (((numTriangles+MAXVU1BATCHCNT-1)>>MAXVU1BATCHCNTSHIFT)<<4)*6 /* VIF commands */
#ifdef NOPSININST
                            + 11 * sizeof(u_long128)
#endif /* NOPSININST */
                            + 2 * sizeof(u_long128);
#ifdef DMAALIGN
                sizeVert = (sizeVert+DMAALIGN)&~DMAALIGN;
#endif /* DMAALIGN */

                mesh++;
            }
            debugVertSize = sizeVert;
            size = sizeof(SkyInstDataExt) + sizeof(RwInstDataPolyHeader) +
                   sizeVert
#ifndef DMAALIGN
                   + 64;
#else /* !DMAALIGN */
#if (DMAALIGN > 63)
                   + (DMAALIGN + 1);
#else /* (DMAALIGN > 63) */
                   + 64;
#endif /* (DMAALIGN > 63) */
#endif /* !DMAALIGN */

            PFCALL(PF_rwSkyWorldSectorInstance);
            if (!RwResourcesAllocateResEntry(worldSector, &worldSector->repEntry, size, _rwSkyInstanceDestroyCallback))
            {
               PFRET(PF_rwSkyWorldSectorInstance);
               PFEXIT(PF_rwSkyWorldSectorInstance);
               RWRETURN(NULL);
            }
            PFRET(PF_rwSkyWorldSectorInstance);
            /* Convenient access */
            repEntry = worldSector->repEntry;

            /* Set refcount to be 0 */
            *SKYINSTANCEREFCNT(repEntry) = 0;
            *SKYINSTANCECLRCNT(repEntry) = 0;

            /* Because this implementation of the pipeline is based one polygons. */
            polyHeader = SKYPOLYHEADER(worldSector->repEntry);

            polyHeader->vertices = (void *)(polyHeader + 1);
            /* Align the vertices on a cache line */
#ifndef DMAALIGN
            polyHeader->vertices = (void *)(((RwUInt32)polyHeader->vertices + 63) & ~63);
#else /* !DMAALIGN */
#if (DMAALIGN > 63)
            polyHeader->vertices = (void *)(((RwUInt32)polyHeader->vertices + DMAALIGN) & ~DMAALIGN);
#else /* (DMAALIGN > 63) */
            polyHeader->vertices = (void *)(((RwUInt32)polyHeader->vertices + 63) & ~63);
#endif /* (DMAALIGN > 63) */
#endif /* !DMAALIGN */
            polyHeader->numVertices = numVerts;
            polyHeader->sizeOfVertex = 0;  /* This doesn't make a whole lot of sense with a SCATTER build */

            /* Cache the size */
            SKYINSTANCEEND(repEntry) = ((RwUInt8 *)polyHeader->vertices) + sizeVert;

            polyHeader->polygons = NULL;
            polyHeader->numPolygons = 0;
            polyHeader->sizeOfPolygon = 0;
            polyHeader->firstPolygonOffset = 0;

            polyHeader->overloadFlags = overloadFlags;
            polyHeader->mesh = (void *)meshHeader;

            /* Copy serial num so we know if the app changes things */
            polyHeader->serialNum = meshHeader->serialNum;

            /* Make sure we can copy the flags across - need
             * commonality between the flag sets
             */
            RWASSERT(rpGEOMETRYTEXTURED == rpWORLDTEXTURED);
            RWASSERT(rpGEOMETRYPRELIT == rpWORLDPRELIT);
            RWASSERT(rpGEOMETRYNORMALS == rpWORLDNORMALS);
            RWASSERT(rpGEOMETRYLIGHT == rpWORLDLIGHT);
            polyHeader->flags = RpWorldGetFlags(world);

            /* Bang in the DMA and VIF tags */
            instData = (u_long128 *)polyHeader->vertices;
            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                u_long128 runCmd;

                numTriangles = mesh->numIndices/3;

                /* Ready for next mesh */
                mesh++;

                *instData++ = dma1Header128;
                *instData++ = maskCmd128;

                runCmd = runCmd128;

                while (numTriangles)
                {
                    batchTriangleCount = numTriangles&~MAXVU1BATCHCNTMASK?MAXVU1BATCHCNT:numTriangles;
                    batchVertexCount = batchTriangleCount+(batchTriangleCount<<1);
                    numTriangles -= batchTriangleCount;

                    *instData = dma1Header128;
                    /* length + 5 to include vif cmds */
                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;
                    *((RwUInt16*)instData) = instbl;
                    instData++;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);
                
                    /* Add VIF tags */

                    /* xyzw (and load ITOPS) (and mode = 0) */
                    *((RwUInt64*)instxyzw-2) = (1l<<24)|(1<<8)|(4) | (0x05l<<(24+32));
                    *((RwUInt64*)instxyzw-1) = (((0x68l<<24) | (batchVertexCount<<16) | 0x8000l)<<32) |
                                               (4l<<24) | batchTriangleCount;

                    /* uv */
                    *((RwUInt64*)instuv-2) = (0x20l<<24) | (0xf0f0f0f0l<<32);
                    *((RwUInt64*)instuv-1) = ((0x74l<<24)|(batchVertexCount<<16) | 0x8001l)<<32;
                    *((RwUInt64*)instrgb-4) = 0;
                    *((RwUInt64*)instrgb-3) = 0;

                    /* rgba */
                    *((RwUInt64*)instrgb-2) = 0;
                    *((RwUInt64*)instrgb-1) = ((0x6el<<24)|(batchVertexCount<<16) | 0xc002l)<<32;

                    /* normals */
                    *((RwUInt64*)instnorm-2) = (0x20l<<24) | (0xc0c0c0c0l<<32);
                    *((RwUInt64*)instnorm-1) = ((0x7al<<24)|(batchVertexCount<<16) | 0x8003l)<<32;

                    /* Skip to the end of the batch */
                    instData += (instbl-1);

                    /* Put in the execute command */
                    *instData++ = runCmd;

                    /* Next time use a continue */
                    runCmd = contCmd128;
                }

#ifdef NOPSININST
                *instData++ = dma10Header128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
#endif /* NOPSININST */

                *instData++ = dma1Ret128;
                *instData++ = unmaskCmd128;
#ifdef DMAALIGN
                instData = (u_long128 *)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }

        /* Texture coordinates */
        if (rwObjectTestFlags(world, rpWORLDTEXTURED))
        {
            /* Initialize the triangles (and texture coords if appropriate) */
            instData = (u_long128 *)polyHeader->vertices;

            mesh = (RpMesh *)((RwUInt8*)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);
            numMeshes = meshHeader->numMeshes;

            /* We have vertex texture coordinates */
            while (numMeshes--)
            {
                meshIndices = mesh->indices;
                numTriangles = mesh->numIndices/3;

                /* Jump over dmaHeader and mask command */
                instData += 2;

                while (numTriangles)
                {
                    /* Start a new batch */
                    batchTriangleCount = numTriangles&~MAXVU1BATCHCNTMASK?MAXVU1BATCHCNT:numTriangles;
                    batchVertexCount = batchTriangleCount+(batchTriangleCount<<1);
                    numTriangles -= batchTriangleCount;

                    /* Jump over dmaHeader */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);
                
                    /* Must process these as triangles to do the minUV thing */
                    while (batchTriangleCount--)
                    {
                        RwTexCoords tc1, tc2, tc3, minTc;

                        tc1 = worldSector->vertexTexCoords[*meshIndices++];
                        tc2 = worldSector->vertexTexCoords[*meshIndices++];
                        tc3 = worldSector->vertexTexCoords[*meshIndices++];

                        minTc = tc1;
                        if (tc2.u < minTc.u)
                        {
                            minTc.u = tc2.u;
                        }
                        if (tc2.v < minTc.v)
                        {
                            minTc.v = tc2.v;
                        }
                        if (tc3.u < minTc.u)
                        {
                            minTc.u = tc3.u;
                        }
                        if (tc3.v < minTc.v)
                        {
                            minTc.v = tc3.v;
                        }

                        minTc.u = (RwReal)((RwUInt32)minTc.u);
                        minTc.v = (RwReal)((RwUInt32)minTc.v);

                        /* Set up the tex coords */
                        *instuv++ = tc1.u - minTc.u;
                        *instuv++ = tc1.v - minTc.v;

                        *instuv++ = tc2.u - minTc.u;
                        *instuv++ = tc2.v - minTc.v;

                        *instuv++ = tc3.u - minTc.u;
                        *instuv++ = tc3.v - minTc.v;
                    }

                    /* Now we know we are at the end of a batch */
                    while((int)instuv&0xf)
                    {
                        *(int*)instuv++ = 0;
                    }

                    /* Jump over vertices and execute command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Onto the next one */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128 *)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }

        if (rwObjectTestFlags(world, rpWORLDNORMALS))
        {
            /* vertices & normals */
            RpVertexNormal     *normals = worldSector->normals;
            RwV3d              *vertices = worldSector->vertices;

            instData = (u_long128 *)polyHeader->vertices;
            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                meshIndices = mesh->indices;
                numTriangles = mesh->numIndices/3;

                /* Skip over dma header and mask cmd */
                instData += 2;

                while (numTriangles)
                {
                    batchTriangleCount = numTriangles&~MAXVU1BATCHCNTMASK?MAXVU1BATCHCNT:numTriangles;
                    batchVertexCount = batchTriangleCount+(batchTriangleCount<<1);
                    numTriangles -= batchTriangleCount;

                    /* Skip over dma header */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);
                
                    while (batchVertexCount--)
                    {
                        RwUInt32 vert = *meshIndices++;

                        *instxyzw++ = vertices[vert].x;
                        *instxyzw++ = vertices[vert].y;
                        *instxyzw++ = vertices[vert].z;

                        /* Fill in the normals (already in char space) */
                        *instnorm++ = normals[vert].x;
                        *instnorm++ = normals[vert].y;
                        *instnorm++ = normals[vert].z;
                    }

                    /* Now we know we are at the end of a batch */
                    while ((int)instxyzw&0xf)
                    {
                        *instxyzw++ = 0.0f; /* I know this is 0x00000000 */
                    }
                    while ((int)instnorm&0xf)
                    {
                        *instnorm++ = 0;
                    }

                    /* Skip over verts and exec command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Ready for next mesh */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128 *)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }
        else
        {
            /* Vertices */
            RwV3d              *vertices = worldSector->vertices;

            instData = (u_long128 *)polyHeader->vertices;
            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                meshIndices = mesh->indices;
                numTriangles = mesh->numIndices/3;

                /* Skip dma header and mask cmd */
                instData += 2;

                while (numTriangles)
                {
                    batchTriangleCount = numTriangles&~MAXVU1BATCHCNTMASK?MAXVU1BATCHCNT:numTriangles;
                    batchVertexCount = batchTriangleCount+(batchTriangleCount<<1);
                    numTriangles -= batchTriangleCount;

                    /* Skip dma header */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);
                
                    while (batchVertexCount--)
                    {
                        RwUInt32 vert = *meshIndices++;

                        *instxyzw++ = vertices[vert].x;
                        *instxyzw++ = vertices[vert].y;
                        *instxyzw++ = vertices[vert].z;

                        /* Zero the normals (we have none) */
                        *instnorm++ = 0;
                        *instnorm++ = 0;
                        *instnorm++ = 0;
                    }

                    /* Now we know we are at the end of a batch */
                    while ((int)instxyzw&0xf)
                    {
                        *instxyzw++ = 0.0f; /* I know this is 0x00000000 */
                    }
                    while ((int)instnorm&0xf)
                    {
                        *instnorm++ = 0;
                    }

                    /* Skip verts and exec command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Ready for next mesh */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128*)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }

        /* Initialise preLitLum */
        if (rwObjectTestFlags(world, rpWORLDPRELIT))
        {
            RwRGBA  *preLitLum = worldSector->preLitLum;
            instData = (u_long128 *)polyHeader->vertices;

            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                meshIndices = mesh->indices;
                numTriangles = mesh->numIndices/3;

                /* Skip dmaHeader and mask command */
                instData += 2;

                while (numTriangles)
                {
                    /* Start a new batch */
                    batchTriangleCount = numTriangles&~MAXVU1BATCHCNTMASK?MAXVU1BATCHCNT:numTriangles;
                    batchVertexCount = batchTriangleCount+(batchTriangleCount<<1);
                    numTriangles -= batchTriangleCount;

                    /* Skip dmaHeader */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);

                    while (batchVertexCount--)
                    {
#ifndef OVERRIDELIGHT
                        const RwRGBA *col;

                        col = &preLitLum[*meshIndices++];

                        *instrgb++ = (RwUInt32)((RwUInt8)col->red)
                                     |((RwUInt32)((RwUInt8)col->green)<<8)
                                     |((RwUInt32)((RwUInt8)col->blue)<<16)
                                     |0xff000000;
#else /* !OVERRIDELIGHT */
                        *instrgb++ = 0xffffffff;
#endif /* !OVERRIDELIGHT */
                    }

                    /* Now we know we are at the end of a batch */
                    while ((int)instrgb&0xf)
                    {
                        *instrgb++ = 0;
                    }

                    /* Jump over vertices and execute command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Ready for next mesh */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128*)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }
        else
        {
            instData = (u_long128 *)polyHeader->vertices;

            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                numTriangles = mesh->numIndices/3;

                /* Jump over dmaHeader and mask command */
                instData += 2;

                while (numTriangles)
                {
                    batchTriangleCount = numTriangles&~MAXVU1BATCHCNTMASK?MAXVU1BATCHCNT:numTriangles;
                    batchVertexCount = batchTriangleCount+(batchTriangleCount<<1);
                    numTriangles -= batchTriangleCount;

                    /* Jump over dmaHeader */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);

                    while (batchVertexCount--)
                    {
#ifndef SUBSISTLIGHT
                        *instrgb++ = 0xff000000;
#else /* !SUBSISTLIGHT */
                        *instrgb++ = 0xffffffff;
#endif /* !SUBSISTLIGHT */
                    }

                    /* Now we know we are at the end of a batch */
                    while ((int)instrgb&0xf)
                    {
                        *instrgb++ = 0;
                    }

                    /* Jump over vertices and execute command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Ready for next mesh */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128*)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }
        PFCALL(PF_rwSkyWorldSectorInstance);
        SyncDCache(polyHeader->vertices, SCESYNCDCACHEROUNDUP(SKYINSTANCEEND(repEntry)));
        PFRET(PF_rwSkyWorldSectorInstance);
    }
    else
    {
        PFCALL(PF_rwSkyWorldSectorInstance);
        RwResourcesUseResEntry(worldSector->repEntry);
        PFRET(PF_rwSkyWorldSectorInstance);
    }

    /* Set up the pointers to the actual data ! */
    polyHeader->xForm = (void *)&(((RwCamera *)RWSRCGLOBAL(curCamera))->viewMatrix);

    /* Do large frustum test to see if we need to clip/cull */
    {
        RwFrustumPlane     *frustPlane;
        RwUInt32           numPlanes;

        /* Assume innocent until proven guilty */
        *SKYINSTANCEINFRUSTUM(worldSector->repEntry) = rwSPHEREINSIDE;

        frustPlane = CAMERAEXTFROMCAMERA(RWSRCGLOBAL(curCamera))->largeFrustumPlanes;
        numPlanes = 6;
        while (numPlanes--)
        {
            RwV3d           vCorner;
            RwReal          dot;

            /* Check against plane */
            vCorner.x = ((RwV3d *) &worldSector->boundingBox)[1-frustPlane->closestX].x;
            vCorner.y = ((RwV3d *) &worldSector->boundingBox)[1-frustPlane->closestY].y;
            vCorner.z = ((RwV3d *) &worldSector->boundingBox)[1-frustPlane->closestZ].z;

            dot = RwV3dDotProduct(&vCorner, &frustPlane->plane.normal);
            dot -= frustPlane->plane.distance;

            if (dot > 0)
            {
                /* Its outside the plane */
                *SKYINSTANCEINFRUSTUM(worldSector->repEntry) = rwSPHEREBOUNDARY;
                break;
            }

            frustPlane++;
        }
    }

    /* Nothing done in the pipe yet */
    polyHeader->pipelineOpFlags = 0;
#if 0
{
/* Dump pkt we just built */
    int i;
    RwUInt32* inst = polyHeader->vertices;

printf("World: pkt\n");
    for (i=0; i<debugVertSize; i+=16)
    {
        printf("%x %.8x_%.8x_%.8x_%.8x\n", inst, *(inst+3), *(inst+2), *(inst+1), *(inst));
        inst+=4;
    }
printf("\n");
}
#endif

    PFEXIT(PF_rwSkyWorldSectorInstance);
    RWRETURN(worldSector->repEntry);
}

/****************************************************************************
 _rwSkyTSWorldSectorInstance

 On entry   : World sector
 On exit    : TRUE on success
 */

static RwResEntry *
_rwSkyTSWorldSectorInstance(void *object)
{
    int                     batchVertexCount, batchTriangleCount;
    int                     debugVertSize;
    RwReal                  *instxyzw;
    RwReal                  *instuv;
    RwUInt32                *instrgb;
    RwInt8                  *instnorm;
    int                     instbl;
    RpWorldSector           *worldSector = (RpWorldSector *)object;
    RpMeshHeader            *meshHeader;
    RpWorld                 *world = (RpWorld *)RWSRCGLOBAL(curWorld);
    RwInt32                 numVerts = worldSector->numPolygons * 3;
    RwUInt16                overloadFlags = rwObjectGetFlags(world) & WORLDRENDERTYPEMASK;
    RwInstDataPolyHeader    *polyHeader;
    u_long128               *instData;
    RpMesh                  *mesh;
    const RWIMVERTEXINDEX   *meshIndices;
    RwInt32                 numTriangles, numMeshes;

    RWFUNCTION(RWSTRING("_rwSkyTSWorldSectorInstance"));
    PFENTRY(PF_rwSkyTSWorldSectorInstance);
    RWASSERT(object);

    polyHeader = SKYPOLYHEADER(worldSector->repEntry);
    meshHeader = worldSector->mesh;

    if (numVerts && worldSector->repEntry)
    {
        /* Do we need to re-instance - has the app changed the representation */
        if ((polyHeader->serialNum != meshHeader->serialNum) ||
            (polyHeader->overloadFlags != overloadFlags))
        {
            /* Release resources to force re-instance */
            PFCALL(PF_rwSkyTSWorldSectorInstance);
            RwResourcesFreeResEntry(worldSector->repEntry);
            PFRET(PF_rwSkyTSWorldSectorInstance);
        }
    }

    if (!worldSector->repEntry)
    {
        /* Allocate some resources and initialise the static elements */
        RwResEntry     *repEntry;

        if (!numVerts)
        {
#ifdef INSTEMPTYSECTORS
            /* Dummy allocation such that atomics are not destroyed */
            PFCALL(PF_rwSkyTSWorldSectorInstance);
            if (!RwResourcesAllocateResEntry(worldSector, &worldSector->repEntry,
                                             sizeof(SkyInstDataExt) + sizeof(RwInstDataPolyHeader),
                                             _rwSkyInstanceDestroyCallback))
            {
                PFRET(PF_rwSkyTSWorldSectorInstance);
                PFEXIT(PF_rwSkyTSWorldSectorInstance);
                RWRETURN(NULL);
            }
            PFRET(PF_rwSkyTSWorldSectorInstance);

            /* Set refcount to be 0 */
            *SKYINSTANCEREFCNT(worldSector->repEntry) = 0;
            *SKYINSTANCECLRCNT(worldSector->repEntry) = 0;
            *SKYINSTANCEINFRUSTUM(worldSector->repEntry) = rwSPHEREBOUNDARY;

            /* Because this implementation of the pipeline is based on polygons. */
            polyHeader = SKYPOLYHEADER(worldSector->repEntry);

            polyHeader->xForm = NULL;

            polyHeader->vertices = NULL;
            SKYINSTANCEEND(repEntry) = NULL;

            polyHeader->numVertices = 0;
            polyHeader->sizeOfVertex = 0;  /* This doesn't make a whole lot of sense with a SCATTER build */
            
            polyHeader->polygons = NULL;
            polyHeader->numPolygons = 0;
            polyHeader->sizeOfPolygon = 0;
            polyHeader->firstPolygonOffset = 0;
            
            polyHeader->overloadFlags = 0;
            polyHeader->mesh = (void *)meshHeader;

            /* Copy serial num so we know if the app changes things */
            polyHeader->serialNum = meshHeader->serialNum;

            /* Make sure we can copy the flags across - need
             * commonality between the flag sets
             */
            RWASSERT(rpGEOMETRYTEXTURED == rpWORLDTEXTURED);
            RWASSERT(rpGEOMETRYPRELIT == rpWORLDPRELIT);
            RWASSERT(rpGEOMETRYNORMALS == rpWORLDNORMALS);
            RWASSERT(rpGEOMETRYLIGHT == rpWORLDLIGHT);
            polyHeader->flags = RpWorldGetFlags(world);

            /* Nothing done in the pipe yet */
            polyHeader->pipelineOpFlags = 0;

            PFEXIT(PF_rwSkyTSWorldSectorInstance);
            RWRETURN(worldSector->repEntry);
#else /* INSTEMPTYSECTORS */
            PFEXIT(PF_rwSkyTSWorldSectorInstance);
            RWRETURN(NULL);
#endif /* INSTEMPTYSECTORS */
        }

        {
            RwInt32                 sizeVert;
            RwInt32                 size;

            /* I need to run down the meshes here. Ugly */
            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);
            sizeVert = 0;
            while (numMeshes--)
            {
                int vCount;
                numTriangles = mesh->numIndices-2;
                /* Vcount is numTriangles+((numTriangles+MAXTSLEN-1)/MAXTSLEN)*2 */

                vCount = numTriangles + ((numTriangles+MAXTSLEN-1)/MAXTSLEN)*2;

                sizeVert += 32                                  /* dmaHeader & mask command */
                            + (((vCount+(vCount<<1)+3)>>2)<<4)  /* xyzw */
                            + (((vCount+1)>>1)<<4)              /* uv */
                            + (((vCount+3)>>2)<<(4))            /* RGBA */
                            + (numTriangles/MAXTSLEN)*((((MAXTSLEN+2)*3)+15)&~0xf) + (((3)*(vCount%(MAXTSLEN+2))+15)&~0xf) /* normals */
                            + (((vCount+MAXTSVERTS-1)/MAXTSVERTS)<<4)*6 /* VIF commands */
#ifdef NOPSININST
                            + 11 * sizeof(u_long128)
#endif /* NOPSININST */
                            + 2 * sizeof(u_long128);

                mesh++;
#ifdef DMAALIGN
                sizeVert = (sizeVert+DMAALIGN)&~DMAALIGN;
#endif /* DMAALIGN */
            }
            debugVertSize = sizeVert;
            size = sizeof(SkyInstDataExt) + sizeof(RwInstDataPolyHeader) +
                   sizeVert
#ifndef DMAALIGN
                   + 64;
#else /* !DMAALIGN */
#if (DMAALIGN > 63)
                   + (DMAALIGN + 1);
#else /* (DMAALIGN > 63) */
                   + 64;
#endif /* (DMAALIGN > 63) */
#endif /* !DMAALIGN */

            PFCALL(PF_rwSkyTSWorldSectorInstance);
            if (!RwResourcesAllocateResEntry(worldSector, &worldSector->repEntry, size, _rwSkyInstanceDestroyCallback))
            {
               PFRET(PF_rwSkyTSWorldSectorInstance);
               PFEXIT(PF_rwSkyTSWorldSectorInstance);
               RWRETURN(NULL);
            }
            PFRET(PF_rwSkyTSWorldSectorInstance);
            /* Convenient access */
            repEntry = worldSector->repEntry;

            /* Set refcount to be 0 */
            *SKYINSTANCEREFCNT(repEntry) = 0;
            *SKYINSTANCECLRCNT(repEntry) = 0;

            /* Because this implementation of the pipeline is based one polygons. */
            polyHeader = SKYPOLYHEADER(worldSector->repEntry);

            polyHeader->vertices = (void *)(polyHeader + 1);
            /* Align the vertices on a cache line */
#ifndef DMAALIGN
            polyHeader->vertices = (void *)(((RwUInt32)polyHeader->vertices + 63) & ~63);
#else /* !DMAALIGN */
#if (DMAALIGN > 63)
            polyHeader->vertices = (void *)(((RwUInt32)polyHeader->vertices + DMAALIGN) & ~DMAALIGN);
#else /* (DMAALIGN > 63) */
            polyHeader->vertices = (void *)(((RwUInt32)polyHeader->vertices + 63) & ~63);
#endif /* (DMAALIGN > 63) */
#endif /* !DMAALIGN */
            polyHeader->numVertices = numVerts;
            polyHeader->sizeOfVertex = 0;  /* This doesn't make a whole lot of sense with a SCATTER build */

            /* Cache the size */
            SKYINSTANCEEND(repEntry) = ((RwUInt8 *)polyHeader->vertices) + sizeVert;

            polyHeader->polygons = NULL;
            polyHeader->numPolygons = 0;
            polyHeader->sizeOfPolygon = 0;
            polyHeader->firstPolygonOffset = 0;

            polyHeader->overloadFlags = overloadFlags;
            polyHeader->mesh = (void *)meshHeader;

            /* Copy serial num so we know if the app changes things */
            polyHeader->serialNum = meshHeader->serialNum;

            /* Make sure we can copy the flags across - need
             * commonality between the flag sets
             */
            RWASSERT(rpGEOMETRYTEXTURED == rpWORLDTEXTURED);
            RWASSERT(rpGEOMETRYPRELIT == rpWORLDPRELIT);
            RWASSERT(rpGEOMETRYNORMALS == rpWORLDNORMALS);
            RWASSERT(rpGEOMETRYLIGHT == rpWORLDLIGHT);
            polyHeader->flags = RpWorldGetFlags(world);

            /* Bang in the DMA and VIF tags */
            instData = (u_long128 *)polyHeader->vertices;
            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                u_long128 runCmd;

                numTriangles = mesh->numIndices-2;

                /* Ready for next mesh */
                mesh++;

                *instData++ = dma1Header128;
                *instData++ = maskCmd128;

                runCmd = runCmd128;

                while (numTriangles)
                {
                    batchTriangleCount = numTriangles>MAXTSLEN?MAXTSLEN:numTriangles;
                    batchVertexCount = batchTriangleCount+2;
                    numTriangles -= batchTriangleCount;

                    *instData = dma1Header128;
                    /* length + 5 to include vif cmds */
                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;
                    *((RwUInt16*)instData) = instbl;
                    instData++;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);
                
                    /* Add VIF tags */

                    /* xyzw (and load ITOPS) (and mode = 0) */
                    *((RwUInt64*)instxyzw-2) = (1l<<24)|(1<<8)|(4) | (0x05l<<(24+32));
                    *((RwUInt64*)instxyzw-1) = (((0x68l<<24) | (batchVertexCount<<16) | 0x8000l)<<32) |
                                               (4l<<24) | batchVertexCount;

                    /* uv */
                    *((RwUInt64*)instuv-2) = (0x20l<<24) | (0xf0f0f0f0l<<32);
                    *((RwUInt64*)instuv-1) = ((0x74l<<24)|(batchVertexCount<<16) | 0x8001l)<<32;
                    *((RwUInt64*)instrgb-4) = 0;
                    *((RwUInt64*)instrgb-3) = 0;

                    /* rgba */
                    *((RwUInt64*)instrgb-2) = 0;
                    *((RwUInt64*)instrgb-1) = ((0x6el<<24)|(batchVertexCount<<16) | 0xc002l)<<32;

                    /* normals */
                    *((RwUInt64*)instnorm-2) = (0x20l<<24) | (0xc0c0c0c0l<<32);
                    *((RwUInt64*)instnorm-1) = ((0x7al<<24)|(batchVertexCount<<16) | 0x8003l)<<32;

                    /* Skip to the end of the batch */
                    instData += (instbl-1);

                    /* Put in the execute command */
                    *instData++ = runCmd;

                    /* Next time use a continue */
                    runCmd = contCmd128;
                }

#ifdef NOPSININST
                *instData++ = dma10Header128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
                *instData++ = zero128;
#endif /* NOPSININST */

                *instData++ = dma1Ret128;
                *instData++ = unmaskCmd128;
#ifdef DMAALIGN
                instData = (u_long128 *)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }

        /* Texture coordinates */
        if (rwObjectTestFlags(world, rpWORLDTEXTURED))
        {
            /* Initialize the triangles (and texture coords if appropriate) */
            instData = (u_long128 *)polyHeader->vertices;

            mesh = (RpMesh *)((RwUInt8*)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);
            numMeshes = meshHeader->numMeshes;

            /* We have vertex texture coordinates */
            while (numMeshes--)
            {
                meshIndices = mesh->indices;
                numTriangles = mesh->numIndices-2;

                /* Jump over dmaHeader and mask command */
                instData += 2;

                while (numTriangles)
                {
                    /* Start a new batch */
                    batchTriangleCount = numTriangles>MAXTSLEN?MAXTSLEN:numTriangles;
                    batchVertexCount = batchTriangleCount + 2;
                    numTriangles -= batchTriangleCount;

                    /* Jump over dmaHeader */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);
                
                    /* Must process these as triangles to do the minUV thing */
                    while (batchTriangleCount--)
                    {
                        RwTexCoords tc;

                        tc = worldSector->vertexTexCoords[*meshIndices++];

                        /* Set up the tex coords */
                        *instuv++ = tc.u;
                        *instuv++ = tc.v;
                    }

                    /* Back up the indices by 2 to start a new batch */
                    meshIndices -= 2;

                    /* Now we know we are at the end of a batch */
                    while((int)instuv&0xf)
                    {
                        *(int*)instuv++ = 0;
                    }

                    /* Jump over vertices and execute command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Onto the next one */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128 *)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }

        if (rwObjectTestFlags(world, rpWORLDNORMALS))
        {
            /* vertices & normals */
            RpVertexNormal     *normals = worldSector->normals;
            RwV3d              *vertices = worldSector->vertices;

            instData = (u_long128 *)polyHeader->vertices;
            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                meshIndices = mesh->indices;
                numTriangles = mesh->numIndices-2;

                /* Skip over dma header and mask cmd */
                instData += 2;

                while (numTriangles)
                {
                    batchTriangleCount = numTriangles>MAXTSLEN?MAXTSLEN:numTriangles;
                    batchVertexCount = batchTriangleCount + 2;
                    numTriangles -= batchTriangleCount;

                    /* Skip over dma header */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);
                
                    while (batchVertexCount--)
                    {
                        RwUInt32 vert = *meshIndices++;

                        *instxyzw++ = vertices[vert].x;
                        *instxyzw++ = vertices[vert].y;
                        *instxyzw++ = vertices[vert].z;

                        /* Fill in the normals (already in char space) */
                        *instnorm++ = normals[vert].x;
                        *instnorm++ = normals[vert].y;
                        *instnorm++ = normals[vert].z;
                    }

                    /* Back up the indices by 2 to start a new batch */
                    meshIndices -= 2;

                    /* Now we know we are at the end of a batch */
                    while ((int)instxyzw&0xf)
                    {
                        *instxyzw++ = 0.0f; /* I know this is 0x00000000 */
                    }
                    while ((int)instnorm&0xf)
                    {
                        *instnorm++ = 0;
                    }

                    /* Skip over verts and exec command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Ready for next mesh */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128 *)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }
        else
        {
            /* Vertices */
            RwV3d              *vertices = worldSector->vertices;

            instData = (u_long128 *)polyHeader->vertices;
            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                meshIndices = mesh->indices;
                numTriangles = mesh->numIndices/3;

                /* Skip dma header and mask cmd */
                instData += 2;

                while (numTriangles)
                {
                    batchTriangleCount = numTriangles>MAXTSLEN?MAXTSLEN:numTriangles;
                    batchVertexCount = batchTriangleCount+2;
                    numTriangles -= batchTriangleCount;

                    /* Skip dma header */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);
                
                    while (batchVertexCount--)
                    {
                        RwUInt32 vert = *meshIndices++;

                        *instxyzw++ = vertices[vert].x;
                        *instxyzw++ = vertices[vert].y;
                        *instxyzw++ = vertices[vert].z;

                        /* Zero the normals (we have none) */
                        *instnorm++ = 0;
                        *instnorm++ = 0;
                        *instnorm++ = 0;
                    }

                    /* Back up the indices by 2 to start a new batch */
                    meshIndices -= 2;

                    /* Now we know we are at the end of a batch */
                    while ((int)instxyzw&0xf)
                    {
                        *instxyzw++ = 0.0f; /* I know this is 0x00000000 */
                    }
                    while ((int)instnorm&0xf)
                    {
                        *instnorm++ = 0;
                    }

                    /* Skip verts and exec command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Ready for next mesh */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128*)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }

        /* Initialise preLitLum */
        if (rwObjectTestFlags(world, rpWORLDPRELIT))
        {
            RwRGBA  *preLitLum = worldSector->preLitLum;
            instData = (u_long128 *)polyHeader->vertices;

            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                meshIndices = mesh->indices;
                numTriangles = mesh->numIndices-2;

                /* Skip dmaHeader and mask command */
                instData += 2;

                while (numTriangles)
                {
                    /* Start a new batch */
                    batchTriangleCount = numTriangles>MAXTSLEN?MAXTSLEN:numTriangles;
                    batchVertexCount = batchTriangleCount + 2;
                    numTriangles -= batchTriangleCount;

                    /* Skip dmaHeader */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);

                    while (batchVertexCount--)
                    {
#ifndef OVERRIDELIGHT
                        const RwRGBA *col;

                        col = &preLitLum[*meshIndices++];

                        *instrgb++ = (RwUInt32)((RwUInt8)col->red)
                                     |((RwUInt32)((RwUInt8)col->green)<<8)
                                     |((RwUInt32)((RwUInt8)col->blue)<<16)
                                     |0xff000000;
#else /* !OVERRIDELIGHT */
                        *instrgb++ = 0xffffffff;
#endif /* !OVERRIDELIGHT */
                    }

                    /* Back up the indices by 2 to start a new batch */
                    meshIndices -= 2;

                    /* Now we know we are at the end of a batch */
                    while ((int)instrgb&0xf)
                    {
                        *instrgb++ = 0;
                    }

                    /* Jump over vertices and execute command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Ready for next mesh */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128*)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }
        else
        {
            instData = (u_long128 *)polyHeader->vertices;

            numMeshes = meshHeader->numMeshes;
            mesh = (RpMesh *)((RwUInt8 *)(meshHeader + 1) +
                              meshHeader->firstMeshOffset);

            while (numMeshes--)
            {
                numTriangles = mesh->numIndices-2;

                /* Jump over dmaHeader and mask command */
                instData += 2;

                while (numTriangles)
                {
                    batchTriangleCount = numTriangles>MAXTSLEN?MAXTSLEN:numTriangles;
                    batchVertexCount = batchTriangleCount + 2;
                    numTriangles -= batchTriangleCount;

                    /* Jump over dmaHeader */
                    instData++;

                    instbl = ((batchVertexCount+(batchVertexCount<<1)+3)>>2) +
                             ((batchVertexCount+1)>>1) +
                             ((batchVertexCount+3)>>2) +
                             ((batchVertexCount+(batchVertexCount<<1)+15)>>4) +
                             5;

                    instxyzw = (RwReal*)(instData+1);
                    instuv = (RwReal *)((u_long128*)instxyzw+((batchVertexCount+(batchVertexCount<<1)+3)>>2)+1);
                    instrgb = (RwUInt32 *)((u_long128*)instuv+((batchVertexCount+1)>>1)+1);
                    instnorm = (RwInt8 *)((u_long128*)instrgb+((batchVertexCount+3)>>2)+1);

                    while (batchVertexCount--)
                    {
#ifndef SUBSISTLIGHT
                        *instrgb++ = 0xff000000;
#else /* !SUBSISTLIGHT */
                        *instrgb++ = 0xffffffff;
#endif /* !SUBSISTLIGHT */
                    }

                    /* Back up the indices by 2 to start a new batch */
                    meshIndices -= 2;

                    /* Now we know we are at the end of a batch */
                    while ((int)instrgb&0xf)
                    {
                        *instrgb++ = 0;
                    }

                    /* Jump over vertices and execute command */
                    instData += instbl;
                }

#ifdef NOPSININST
                instData += 11+2;
#else /* NOPSININST */
                instData += 2;
#endif /* NOPSININST */

                /* Ready for next mesh */
                mesh++;
#ifdef DMAALIGN
                instData = (u_long128*)(((RwUInt32)instData+DMAALIGN)&~DMAALIGN);
#endif /* DMAALIGN */
            }
        }
        PFCALL(PF_rwSkyTSWorldSectorInstance);
        SyncDCache(polyHeader->vertices, SCESYNCDCACHEROUNDUP(SKYINSTANCEEND(repEntry)));
        PFRET(PF_rwSkyTSWorldSectorInstance);
    }
    else
    {
        PFCALL(PF_rwSkyTSWorldSectorInstance);
        RwResourcesUseResEntry(worldSector->repEntry);
        PFRET(PF_rwSkyTSWorldSectorInstance);
    }

    /* Set up the pointers to the actual data ! */
    polyHeader->xForm = (void *)&(((RwCamera *)RWSRCGLOBAL(curCamera))->viewMatrix);

    /* Do large frustum test to see if we need to clip/cull */
    {
        RwFrustumPlane     *frustPlane;
        RwUInt32           numPlanes;

        /* Assume innocent until proven guilty */
        *SKYINSTANCEINFRUSTUM(worldSector->repEntry) = rwSPHEREINSIDE;

        frustPlane = CAMERAEXTFROMCAMERA(RWSRCGLOBAL(curCamera))->largeFrustumPlanes;
        numPlanes = 6;
        while (numPlanes--)
        {
            RwV3d           vCorner;
            RwReal          dot;

            /* Check against plane */
            vCorner.x = ((RwV3d *) &worldSector->boundingBox)[1-frustPlane->closestX].x;
            vCorner.y = ((RwV3d *) &worldSector->boundingBox)[1-frustPlane->closestY].y;
            vCorner.z = ((RwV3d *) &worldSector->boundingBox)[1-frustPlane->closestZ].z;

            dot = RwV3dDotProduct(&vCorner, &frustPlane->plane.normal);
            dot -= frustPlane->plane.distance;

            if (dot > 0)
            {
                /* Its outside the plane */
                *SKYINSTANCEINFRUSTUM(worldSector->repEntry) = rwSPHEREBOUNDARY;
                break;
            }

            frustPlane++;
        }
    }

    /* Nothing done in the pipe yet */
    polyHeader->pipelineOpFlags = 0;
#if 0
{
/* Dump pkt we just built */
    int i;
    RwUInt32* inst = polyHeader->vertices;

printf("World: pkt\n");
    for (i=0; i<debugVertSize; i+=16)
    {
        printf("%x %.8x_%.8x_%.8x_%.8x\n", inst, *(inst+3), *(inst+2), *(inst+1), *(inst));
        inst+=4;
    }
printf("\n");
}
#endif

    PFEXIT(PF_rwSkyTSWorldSectorInstance);
    RWRETURN(worldSector->repEntry);
}

/****************************************************************************
 _rwPS2WorldSectorInstanceNodeBody()
 */

static RwBool
_rwPS2WorldSectorInstanceNodeBody(RxPipelineNodeInstance *self,
                                  RxMemoryArena *arena __RWUNUSED__,
                                  void *data)
{
    RpWorldSector   *worldSector = (RpWorldSector *) data;
    RwInstDataPolyHeader *polyHeader;
    RwResEntry      *repEntry = NULL;
    RpMeshHeader    *meshHeader;

    RWFUNCTION(RWSTRING("_rwPS2WorldSectorInstanceNodeBody"));
    PFENTRY(PF_rwSkyWorldSectorSelectInstance);
    RWASSERT(data);

    polyHeader = SKYPOLYHEADER(worldSector->repEntry);
    meshHeader = worldSector->mesh;


#ifdef RWMETRICS
    RWSRCGLOBAL(metrics)->numVertices += worldSector->numVertices;
    RWSRCGLOBAL(metrics)->numTriangles += worldSector->numPolygons;
    if (meshHeader->flags & rpMESHHEADERTRISTRIP)
    {
        RWSRCGLOBAL(metrics)->numProcTriangles +=
            meshHeader->totalIndicesInMesh - (2 * meshHeader->numMeshes);
    }
    else
    {
        RWSRCGLOBAL(metrics)->numProcTriangles +=
            meshHeader->totalIndicesInMesh / 3;
    }
#endif /* RWMETRICS */

    if (meshHeader->flags & rpMESHHEADERTRISTRIP)
    {
        PFCALL(PF_rwSkyWorldSectorSelectInstance);
        repEntry = _rwSkyTSWorldSectorInstance(data);
        PFRET(PF_rwSkyWorldSectorSelectInstance);
    }
    else
    {
        PFCALL(PF_rwSkyWorldSectorSelectInstance);
        repEntry = _rwSkyWorldSectorInstance(data);
        PFRET(PF_rwSkyWorldSectorSelectInstance);
    }

    if ( repEntry &&
           _rwQueuePS2DMAChainPacket(self, repEntry) )
    {
        PFEXIT(PF_rwSkyWorldSectorSelectInstance);
        RWRETURN(TRUE);
    }
    else
    {
        PFEXIT(PF_rwSkyWorldSectorSelectInstance);
        RWRETURN(FALSE);
    }
}

/**********************************************************************
 * _func <f RxNodeDefinitionGetPS2WorldSectorInstance> returns a pointer to a node 
 * to <f todo>
 *
 * _rdesc pointer to a node to <f todo>
 *
 * _xref <f RxPipelineNodePS2MatInstanceGenerateCluster>
 * _xref <f RxNodeDefinitionGetPS2AtomicInstance>
 * _xref <f RxNodeDefinitionGetPS2Dispatch>
 * _xref <f RxNodeDefinitionGetPS2Light>
 * _xref <f RxNodeDefinitionGetPS2MatBridge>
 * _xref <f RxNodeDefinitionGetPS2MatInstance>
 * _xref <f RxNodeDefinitionGetPS2ObjClose>
 * _xref <f RxNodeDefinitionGetPS2ObjEnumMeshes>
 * _xref <f RxNodeDefinitionGetPS2ObjOpen>
 */


RxNodeDefinition *
RxNodeDefinitionGetPS2WorldSectorInstance(void)
{
    static RxClusterRef gNodeClusters[] =
    {
        /* 0 */
        { &RxClPS2DMAChain, rxCLALLOWABSENT, rxCLRESERVED  }
    };

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

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

    /* output state (this array parallel to ClusterRefs) */
    static RxClusterValid 
        gNodeOut1[NUMCLUSTERSOFINTEREST] =
    {
        rxCLVALID_VALID
    };

    static RwChar _DefaultOutput[] = RWSTRING("DefaultOutput");

    static RxOutputSpec gNodeOuts[] =
    {
        {
            _DefaultOutput,             // Name
            gNodeOut1,                   // OutputClusters
            rxCLVALID_INVALID             // AllOtherClusters
        }
    };

    #define NUMOUTPUTS \
        ((sizeof(gNodeOuts))/(sizeof(gNodeOuts[0])))

    static RwChar _PS2WorldSectorInstance_csl[] = RWSTRING("PS2WorldSectorInstance.csl");

    static RxNodeDefinition nodePS2WorldSectorInstanceCSL =
    {
        _PS2WorldSectorInstance_csl,          // Name
        {                                      // nodemethods
            _rwPS2WorldSectorInstanceNodeBody, // +-- nodebody
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL
        },
        {                                      // Io
            NUMCLUSTERSOFINTEREST,             // +-- NumClustersOfInterest
            gNodeClusters,                     // +-- ClustersOfInterest
            gNodeReqs,                         // +-- InputRequirements
            NUMOUTPUTS,                        // +-- NumOutputs
            gNodeOuts                          // +-- Outputs
        },
        0,
        FALSE,
        0
    };

    RxNodeDefinition *result = &nodePS2WorldSectorInstanceCSL;
    
    RWAPIFUNCTION(RWSTRING("RxNodeDefinitionGetPS2WorldSectorInstance"));

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

    RWRETURN(result);
}
