
/* *INDENT-OFF* */

/*
 * ps2allsector
 * default sector-specific callback functions for PS2-specific object pipelines
 * 
 * Copyright (c) Criterion Software Limited
 */

/****************************************************************************
 *                                                                          *
 * module : ps2allsector.c                                                  *
 *                                                                          *
 * purpose: Sector-specific PS2All callbacks                                *
 *                                                                          *
 ****************************************************************************/

/*
#### SYNCHRONISATION
####
#### UP TO DATE WITH VERSION 1.72 OF nodePS2Manager.c
#### UP TO DATE WITH VERSION 1.85 OF nodePS2ObjAllInOne.c
#### UP TO DATE WITH VERSION 1.174 OF nodePS2MatInstance.c
#### UP TO DATE WITH VERSION 1.113 OF nodePS2MatBridge.c
####
#### SYNCHRONISATION
*/

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

#include "matputil.h"

#include "nodePS2All.h"

#include "ps2alldbg.h"
#include "matinstance.h"

#include "ps2allatomic.h"
#include "ps2allsector.h"


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

   Functions

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

/********************* Wrapper funcs for macros in debug *********************/

/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllGetMeshHeaderMeshCache is a macro
 * to extract the RpMeshHeader and RwMeshCache from an RpWorldSector.
 *
 * This is a helper macro which is called from the default
 * \ref RxPipelineNodePS2AllObjectSetupCallBack for RpWorldSectors,
 * \ref RpWorldSectorPS2AllObjectSetupCallBack. It fills in the
 * meshHeader and meshCache fields of the \ref RxPS2AllPipeData
 * struct. It may be used in constructing user callbacks.
 *
 * This macro will be a function in a debug build, but it may be
 * used explicitly in macro form through the name
 * RpWorldSectorPS2AllGetMeshHeaderMeshCacheMacro and in function
 * form through RpWorldSectorPS2AllGetMeshHeaderMeshCacheFunc,
 * depending on how you wish to balance code size and function
 * call overheads. Note that the macro contains RWASSERT, so it
 * must be used within functions using RWFUNCTION and RWRETURN.
 *
 *
 * \param  sector         A pointer to the current RpWorldSector
 * \param  ps2AllPipeData A pointer to a \ref RxPS2AllPipeData struct
 *                        containing information relevant to the
 *                        current pipeline execution
 *
 * \see RpWorldSectorPS2AllObjectSetupCallBack
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 */
void
#if (defined(DOXYGEN))
RpWorldSectorPS2AllGetMeshHeaderMeshCache(RpWorldSector *sector,
                                          RxPS2AllPipeData *ps2AllPipeData)
#else /* (defined(DOXYGEN)) */
RpWorldSectorPS2AllGetMeshHeaderMeshCacheFunc(RpWorldSector *sector,
                                              RxPS2AllPipeData *ps2AllPipeData)
#endif /* (defined(DOXYGEN)) */
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllGetMeshHeaderMeshCache"));
    RpWorldSectorPS2AllGetMeshHeaderMeshCacheMacro(sector, ps2AllPipeData);
    RWRETURNVOID();
}

/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllGatherObjMetrics is a macro
 * to gather metrics information from an RpWorldSector.
 *
 * This is a helper macro which is called from the default
 * \ref RxPipelineNodePS2AllObjectSetupCallBack for RpWorldSectors,
 * \ref RpWorldSectorPS2AllObjectSetupCallBack. It gathers metrics
 * information from the current sector (though in non-RWMETRICS
 * builds, the macro will boil away to nothing during compilation).
 * It may be used in constructing user callbacks.
 *
 * This macro will be a function in a debug build, but it
 * may be used explicitly in macro form through the name
 * RpWorldSectorPS2AllGatherObjMetricsMacro and in function
 * form through RpWorldSectorPS2AllGatherObjMetricsFunc,
 * depending on how you wish to balance code size and function
 * call overheads.
 *
 *
 * \param  sector         A pointer to the current RpWorldSector
 *
 * \see RpWorldSectorPS2AllObjectSetupCallBack
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 */
void
#if (defined(DOXYGEN))
RpWorldSectorPS2AllGatherObjMetrics(RpWorldSector *sector)
#else /* (defined(DOXYGEN)) */
RpWorldSectorPS2AllGatherObjMetricsFunc(RpWorldSector *sector)
#endif /* (defined(DOXYGEN)) */
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllGatherObjMetrics"));
    RpWorldSectorPS2AllGatherObjMetricsMacro();
    RWRETURNVOID();
}

/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllObjInstanceTest is a macro
 * to test an RpWorldSector to determine if it needs
 * reinstancing.
 *
 * This is a helper macro which is called from the default
 * \ref RxPipelineNodePS2AllObjectSetupCallBack for RpWorldSectors,
 * \ref RpWorldSectorPS2AllObjectSetupCallBack. It generates a new
 * object identifier for the current RpWorldSector and stores this
 * in the objIdentifier field of the \ref RxPS2AllPipeData struct.
 * It also tests this identifier against the existing identifier
 * stored in the sector's instance data (it uses
 * rwMeshCacheGetEntryRef, RWPS2ALLRESENTRYHEADERFROMRESENTRY and
 * RWPS2ALLRESENTRYHEADERGETOBJIDENTIFIER to extract this from the
 * header of the sector's first mesh's instance data - all meshes
 * within an object will have the same objIdentifier in their
 * instance data header) and updates the objInstance member if
 * it finds any differences.
 *
 * The macro RPWORLDSECTORMAKEOBJID can be used to construct the
 * identifier for an RpWorldSector from its RpMeshHeader.
 * RPWORLDSECTOROBJIDGETSERIALNUM can be used to extract the
 * serial number from this indentifier and
 * RPWORLDSECTOROBJIDGETFLAGS can be used to extract the flags
 * (which are obtained from the CURRENT world - so be careful
 * where you use this macro if you have multiple worlds). All
 * these macros may be used in constructing user callbacks.
 *
 * If the flags of the sector have changed, it will be fully
 * reinstanced (see \ref RxInstanceFlags). All other changes
 * will result in a congruent reinstance (it is not anticipated
 * that sectors will be edited at run-time in most applications,
 * so an optimisation which is likely to be feasible in most
 * cases is simply to omit this callback from the pipeline).
 *
 * RpWorldSectorPS2AllObjInstanceTest will be a function in a
 * debug build, but it may be used explicitly in macro form
 * through the name RpWorldSectorPS2AllObjInstanceTestMacro and
 * in function form through RpWorldSectorPS2AllObjInstanceTestFunc,
 * depending on how you wish to balance code size and function
 * call overheads. Note that the macro contains RWASSERT, so it
 * must be used within functions using RWFUNCTION and RWRETURN.
 *
 *
 * \param  sector         A pointer to the current RpWorldSector
 *
 * \see RpWorldSectorPS2AllObjectSetupCallBack
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 */
void
#if (defined(DOXYGEN))
RpWorldSectorPS2AllObjInstanceTest(RxPS2AllPipeData *ps2AllPipeData)
#else /* (defined(DOXYGEN)) */
RpWorldSectorPS2AllObjInstanceTestFunc(RxPS2AllPipeData *ps2AllPipeData)
#endif /* (defined(DOXYGEN)) */
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllObjInstanceTest"));
    RpWorldSectorPS2AllObjInstanceTestMacro(ps2AllPipeData);
    RWRETURNVOID();
}

/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllTransformSetup is a macro
 * to set up the transformation matrix for an RpWorldSector.
 *
 * This is a helper macro which is called from the default
 * \ref RxPipelineNodePS2AllObjectSetupCallBack for RpWorldSectors,
 * \ref RpWorldSectorPS2AllObjectSetupCallBack. It simply sets the
 * transform pointer to point to the current camera's view matrix
 * (see \ref RwCameraGetViewMatrix), since the coordinates of
 * RpWorldSectors are specified with respect to the global origin.
 * It may be used in constructing user callbacks.
 *
 * This macro will be a function in a debug build, but it
 * may be used explicitly in macro form through the name
 * RpWorldSectorPS2AllTransformSetupMacro and in function
 * form through RpWorldSectorPS2AllTransformSetupFunc,
 * depending on how you wish to balance code size and function
 * call overheads. Note that the macro contains RWASSERT, so it
 * must be used within functions using RWFUNCTION and RWRETURN.
 *
 *
 * \param  transform   A pointer to a pointer to the \ref RwMatrix
 *                     holding the object-space to camera-space
 *                     transform for the current RpWorldSector
 *
 * \see RpWorldSectorPS2AllObjectSetupCallBack
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 */
void
#if (defined(DOXYGEN))
RpWorldSectorPS2AllTransformSetup(RwMatrix **transform)
#else /* (defined(DOXYGEN)) */
RpWorldSectorPS2AllTransformSetupFunc(RwMatrix **transform)
#endif /* (defined(DOXYGEN)) */
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllTransformSetupMacro"));
    RpWorldSectorPS2AllTransformSetupMacro(transform);
    RWRETURNVOID();
}

/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllFrustumTest is a macro to test
 * an RpWorldSector against the current camera's view frustum
 * to determine if clipping is necessary.
 *
 * This is a helper macro which is called from the default
 * \ref RxPipelineNodePS2AllObjectSetupCallBack for RpWorldSectors,
 * \ref RpWorldSectorPS2AllObjectSetupCallBack. It tests the
 * bounding box of the current RpWorldSector
 * (see \ref RpWorldSectorGetBBox) against the frustum of the
 * current RwCamera. It may be used in constructing user callbacks.
 *
 * This macro will be a function in a debug build, but it
 * may be used explicitly in macro form through the name
 * RpWorldSectorPS2AllFrustumTestMacro and in function
 * form through RpWorldSectorPS2AllFrustumTestFunc,
 * depending on how you wish to balance code size and
 * function call overheads. Note that the macro contains
 * RWASSERT, so it must be used within functions using
 * RWFUNCTION and RWRETURN.
 *
 *
 * \param  sector         A pointer to the current RpWorldSector
 * \param  inFrustum      A pointer to a \ref RwFrustumTestResult
 *                        to receive the result of a frustum test
 *                        on the atomic
 *
 * \see RpWorldSectorPS2AllObjectSetupCallBack
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 */
void
#if (defined(DOXYGEN))
RpWorldSectorPS2AllFrustumTest(RpWorldSector *sector,
                               RwFrustumTestResult *inFrustum)
#else /* (defined(DOXYGEN)) */
RpWorldSectorPS2AllFrustumTestFunc(RpWorldSector *sector,
                                   RwFrustumTestResult *inFrustum)
#endif /* (defined(DOXYGEN)) */
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllFrustumTest"));
    RpWorldSectorPS2AllFrustumTestMacro(sector, inFrustum);
    RWRETURNVOID();
}

/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllMatModulateSetup is a macro
 * to determine whether material color is to be modulated for
 * an RpWorldSector.
 *
 * This is a helper macro which is called from the default
 * \ref RxPipelineNodePS2AllObjectSetupCallBack for RpWorldSectors,
 * \ref RpWorldSectorPS2AllObjectSetupCallBack. It sets up the
 * matModulate member of the \ref RxPS2AllPipeData struct
 * depending on the flags of the current world (so be careful
 * where you call this from if you have multiple worlds). It may
 * be used in constructing user callbacks.
 *
 * This macro will be a function in a debug build, but it may be
 * used explicitly in macro form through the name
 * RpWorldSectorPS2AllMatModulateSetupMacro and in function
 * form through RpWorldSectorPS2AllMatModulateSetupFunc,
 * depending on how you wish to balance code size and function
 * call overheads. Note that the macro contains RWASSERT, so it
 * must be used within functions using RWFUNCTION and RWRETURN.
 *
 *
 * \param  ps2AllPipeData A pointer to a \ref RxPS2AllPipeData struct
 *                        containing information relevant to the
 *                        current pipeline execution
 *
 * \see RpWorldSectorPS2AllObjectSetupCallBack
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 */
void
#if (defined(DOXYGEN))
RpWorldSectorPS2AllMatModulateSetup(RxPS2AllPipeData *ps2AllPipeData)
#else /* (defined(DOXYGEN)) */
RpWorldSectorPS2AllMatModulateSetupFunc(RxPS2AllPipeData *ps2AllPipeData)
#endif /* (defined(DOXYGEN)) */
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllMatModulateSetup"));
    RpWorldSectorPS2AllMatModulateSetupMacro(ps2AllPipeData);
    RWRETURNVOID();
}

/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllLightingSetup is a macro to set up
 * lights to be used during an RpWorldSector's transform on VU1.
 *
 * This is a helper macro which is called from the default
 * \ref RxPipelineNodePS2AllObjectSetupCallBack for RpWorldSectors,
 * \ref RpWorldSectorPS2AllObjectSetupCallBack. It iterates over
 * all lights which affect the given sector and applies each
 * using the supplied \ref RxWorldApplyLightFunc (this sets
 * up an array of data which will be uploaded to VU1 prior
 * to the rendering of the sector's meshes). It may be used in
 * constructing user callbacks.
 *
 * This macro will be a function in a debug build, but it
 * may be used explicitly in macro form through the name
 * RpWorldSectorPS2AllLightingSetupMacro and in function
 * form through RpWorldSectorPS2AllLightingSetupFunc,
 * depending on how you wish to balance code size and
 * function call overheads. Note that the macro contains
 * RWASSERT, so it must be used within functions using
 * RWFUNCTION and RWRETURN.
 *
 *
 * \param  ps2AllPipeData A pointer to a \ref RxPS2AllPipeData struct
 *                        containing information relevant to the
 *                        current pipeline execution
 * \param  lightingFunc   A \ref RxWorldApplyLightFunc to upload
 *                        information on a light to VU1
 *
 * \see RpWorldSectorPS2AllObjectSetupCallBack
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 */
void
#if (defined(DOXYGEN))
RpWorldSectorPS2AllLightingSetup(RxPS2AllPipeData *ps2AllPipeData,
                                 RxWorldApplyLightFunc lightingFunc)
#else /* (defined(DOXYGEN)) */
RpWorldSectorPS2AllLightingSetupFunc(RxPS2AllPipeData *ps2AllPipeData,
                                     RxWorldApplyLightFunc lightingFunc)
#endif /* (defined(DOXYGEN)) */
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllLightingSetup"));
    RpWorldSectorPS2AllLightingSetupMacro(ps2AllPipeData, lightingFunc);
    RWRETURNVOID();
}

/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllResEntryAlloc is a macro to
 * allocate space for the instance data of an RpMesh in
 * an RpWorldSector.
 *
 * This is a helper macro which is called from the default
 * \ref RxPipelineNodePS2AllMatResEntryAllocCallBack for
 * RpWorldSectors, \ref RpMeshPS2AllResEntryAllocCallBack.
 * It allocates space in the Resource Arena (see
 * \ref rwresourcesoverview for details) for the instance
 * data of the current mesh and stores a pointer to this
 * space in the appropriate location. It may be used in
 * constructing user callbacks.
 *
 * This macro will be a function in a debug build, but it
 * may be used explicitly in macro form through the name
 * RpWorldSectorPS2AllResEntryAllocMacro and in function
 * form through RpWorldSectorPS2AllResEntryAllocFunc,
 * depending on how you wish to balance code size and
 * function call overheads. Note that the macro contains
 * RWASSERT, so it must be used within functions using
 * RWFUNCTION and RWRETURN.
 *
 *
 * \param  ps2AllPipeData A pointer to a \ref RxPS2AllPipeData struct
 *                        containing information relevant to the
 *                        current pipeline execution
 * \param  repEntry       A pointer to a pointer to a \ref RwResEntry
 * \param  size           A \ref RwUInt32 value specifying the size
 *                        in bytes of the memory block to allocate
 *                        (excluding the \ref RwResEntry header).
 * \param  destroyNotify  A \ref RwResEntryDestroyNotify callback
 *                        (see \ref RwResourcesFreeResEntry)
 *
 * \see RpMeshPS2AllResEntryAllocCallBack
 * \see RxPipelineNodePS2AllMatResEntryAllocCallBack
 */
void
#if (defined(DOXYGEN))
RpWorldSectorPS2AllResEntryAlloc(RxPS2AllPipeData *ps2AllPipeData,
                                 RwResEntry **repEntry,
                                 RwUInt32 size,
                                 RwResEntryDestroyNotify destroyCallBack)
#else /* (defined(DOXYGEN)) */
RpWorldSectorPS2AllResEntryAllocFunc(RxPS2AllPipeData *ps2AllPipeData,
                                     RwResEntry **repEntry,
                                     RwUInt32 size,
                                     RwResEntryDestroyNotify destroyCallBack)
#endif /* (defined(DOXYGEN)) */
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllResEntryAlloc"));
    RpWorldSectorPS2AllResEntryAllocMacro(
        ps2AllPipeData, repEntry, size, destroyCallBack);
    RWRETURNVOID();
}

/***************** End of wrapper funcs for macros in debug ******************/


/**
 * \ingroup rpworldsectorps2all
 * \ref RpWorldSectorPS2AllObjectSetupCallBack is the
 * \ref RxPipelineNodePS2AllObjectSetupCallBack used in the default
 * RenderWare RpWorldSector pipeline.
 *
 * See \ref RxPipelineNodePS2AllObjectSetupCallBack for an overview
 * of this callback type. For RpWorldSectors, this default callback
 * is composed of the following helper macros (of which you may use
 * some or all to construct your own replacement callback), in the
 * order shown here (try and keep this order, some macros rely on
 * the results of prior ones):
 *
 * \li \ref RpWorldSectorPS2AllGetMeshHeaderMeshCache, 
 * \li \ref RpWorldSectorPS2AllGatherObjMetrics,
 * \li \ref RpWorldSectorPS2AllObjInstanceTest,
 * \li \ref RpWorldSectorPS2AllTransformSetup,
 * \li \ref RpWorldSectorPS2AllFrustumTest,
 * \li \ref RpAtomicPS2AllPrimTypeTransTypeSetup,
 * \li \ref RpWorldSectorPS2AllMatModulateSetup,
 * \li \ref RpWorldSectorPS2AllLightingSetup
 *
 *
 * \param  ps2AllPipeData A pointer to a \ref RxPS2AllPipeData struct
 *                        containing information relevant to the
 *                        current pipeline execution
 * \param  transform      A pointer to a pointer to a transform matrix
 * \param  lightingFunc   A pointer to a \ref RxWorldApplyLightFunc
 *                        function to upload information about lights
 *                        to VU1
 *
 * \return TRUE on success, FALSE to prematurely terminate the pipeline
 *
 * \see RxPipelineNodePS2AllObjectSetupCallBack
 * \see RpWorldSectorPS2AllGetMeshHeaderMeshCache
 * \see RpWorldSectorPS2AllGatherObjMetrics
 * \see RpWorldSectorPS2AllObjInstanceTest
 * \see RpWorldSectorPS2AllTransformSetup
 * \see RpWorldSectorPS2AllFrustumTest
 * \see RpAtomicPS2AllPrimTypeTransTypeSetup
 * \see RpWorldSectorPS2AllMatModulateSetup
 * \see RpWorldSectorPS2AllLightingSetup
 */
RwBool
RpWorldSectorPS2AllObjectSetupCallBack(
    RxPS2AllPipeData *ps2AllPipeData,
    RwMatrix **transform,
    RxWorldApplyLightFunc lightingFunc)
{
    RpWorldSector      *sector;
    RwInt32             numVerts;
    RwFrustumTestResult inFrustum;

    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllObjectSetupCallBack"));

    sector = (RpWorldSector *)(ps2AllPipeData->sourceObject);
    RWASSERT(NULL != sector);

    /* Check ASAP for an empty sector */
    numVerts = RpWorldSectorGetNumVertices(sector);
    if (numVerts <= 0)
    {
        /* Don't execute the rest of the pipeline */
        RWRETURN(TRUE);
    }

    /* Get the RwMeshCache from the sector */
    RpWorldSectorPS2AllGetMeshHeaderMeshCache(sector, ps2AllPipeData);

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

    /* Gather metrics */
    RpWorldSectorPS2AllGatherObjMetrics(sector);

    /* Decide whether this sector needs instancing or not */
    RpWorldSectorPS2AllObjInstanceTest(ps2AllPipeData);

    /* We need to cache the transform */
    RpWorldSectorPS2AllTransformSetup(transform);

    /* We need to to a frustum test here */
    RpWorldSectorPS2AllFrustumTest(sector, &inFrustum);

    /* Set up primType and transtype (share the atomic version) */
    RpAtomicPS2AllPrimTypeTransTypeSetupMacro(ps2AllPipeData, inFrustum);

    /* Do we modulate with material colours for this world? */
    RpWorldSectorPS2AllMatModulateSetup(ps2AllPipeData);

    /* Let there b... no, sorry, bad idea. */
    RpWorldSectorPS2AllLightingSetupMacro(ps2AllPipeData, lightingFunc);

    RWRETURN(TRUE);
}

/* Shares RpMeshPS2AllMeshInstanceTestCallBack */

/* Shares RpMeshPS2AllResEntryAllocCallBack */

/* Shares RpMeshPS2AllInstanceCallBack */

/* Shares RpMeshPS2AllBridgeCallBack */

/* Shares RpMeshPS2AllPostMeshCallBack */

/****************************************************************************
 RpWorldSectorPS2AllDoApplyLight

 Applies a light to an atomic sector

 On entry   : light, lighting data (package of data required)
 On exit    : light pointer on success
 */
RpLight*
RpWorldSectorPS2AllDoApplyLight(RpLight *light, void *pData)
{
    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllDoApplyLight"));
    RWASSERT(light);
    RWASSERTISTYPE(light, rpLIGHT);

    if (rwObjectTestFlags(light, rpLIGHTLIGHTWORLD))
    {
        rpWorldSectorPS2AllLightData *lightingData =
            (rpWorldSectorPS2AllLightData *) pData;

        RWASSERT(lightingData);

        lightingData->lightFunc(light, NULL, 1.0f, lightingData->surface);
    }

    RWRETURN(light);
}

/****************************************************************************
 RpWorldSectorPS2AllInstance
 */
RwBool
RpWorldSectorPS2AllInstance(RxPS2AllPipeData *ps2AllPipeData
/*TODO[4]: NEW REINSTANCING STRUCTURE AND DATA BLOCK IN HERE...*/)
{
    RpWorld *world;
    RpWorldSector *sector;
    rwPS2AllResEntryHeader *ps2AllResHeader;
    RwUInt32 numVerts;

    /* These next two MUST be signed or they'll wrap around causing infinite loops!! */
    RwInt32 vertCounter, j;
    RwUInt32 stripTmp = 0;
    INDEXDECLARE();

    RWFUNCTION(RWSTRING("RpWorldSectorPS2AllInstance"));


    /* Need world for renderflags (normals, rgbas, uvs/uv2s, etc) */
    world = (RpWorld *)RWSRCGLOBAL(curWorld);
    sector = (RpWorldSector *)(ps2AllPipeData->sourceObject);
    ps2AllResHeader = RWPS2ALLRESENTRYHEADERFROMRESENTRY(
                       *(ps2AllPipeData->cacheEntryRef));
    numVerts = ps2AllResHeader->numVerts;


    REDEBUGPrintf(("RpWorldSectorPS2AllInstance"));
    REDEBUGPrintf(("numVerts: %d\n", numVerts));
    REDEBUGPrintf(("batchSize: %d\n", ps2AllResHeader->batchSize));


/*TODO[3]: WOULD BE IDEAL TO SHARE THIS FUNC TWEEN ATOMICS/SECTORS (SAVE LOTS OF CODE DUPE)
        ONLY NEED TO PREDICATE NORMALS AND MORPHING ON ATOMIC/SECTORS
         - CAN USE objIdentifier TO TELL THE DIFFERENCE
         - DON'T WANT TO USE THE objIdentifier MECHANISM THOUGH */


    if (ps2AllPipeData->meshHeader->flags & rpMESHHEADERTRISTRIP)
    {
        stripTmp = 2;
    }
//TODO[3]: ERM... SHOUDLN'T STRIPTMP BE A PART OF THE INSTANCING MACROS? HUH? WELL?!?!?!?!
    INDEXSETUP(ps2AllPipeData->meshHeader->flags, ps2AllPipeData->mesh->indices);


    /* We attempt to instance the required data */
    if ((  ps2AllPipeData->meshInstance & rxINSTANCEXYZ) &&
        (  ps2AllPipeData->matPvtData->clinfo[CL_XYZ].attrib & CL_ATTRIB_REQUIRED  ) &&
        (!(ps2AllPipeData->matPvtData->clinfo[CL_XYZ].attrib & CL_ATTRIB_DONT_FILL))   )
    {
        u_long128 *data = (ps2AllResHeader->data
                           + ps2AllResHeader->fieldRec[CL_XYZ].dataoffset);
        RwReal *dataTmp = (RwReal *)data;
        RwV3d *vertices = sector->vertices;

        INDEXRESET();

        vertCounter = numVerts;
        while (vertCounter > 0)
        {
            if (vertCounter <= (RwInt32)ps2AllResHeader->batchSize)
            {
                data -= ps2AllResHeader->fieldRec[CL_XYZ].reverse;
                dataTmp = (RwReal *)data;
                j = vertCounter;
                vertCounter = 0;
            }
            else
            {
                j = ps2AllResHeader->fieldRec[CL_XYZ].numVerts;
                vertCounter -= (j - stripTmp);
            }

            while (j--)
            {
                RxVertexIndex idx = INDEXGET();
                RwV3d        *pos = &(vertices[idx]);

               *dataTmp++ = pos->x;
               *dataTmp++ = pos->y;
               *dataTmp++ = pos->z;

                INDEXINC();
            }

            STRIPREVERSE(stripTmp);

            data += ps2AllResHeader->fieldRec[CL_XYZ].skip;
            dataTmp = (RwReal *)data;
        }
    }

    /* Really I'd like to move this test up into the attributes so that uv's */
    /* aren't instanced or uploaded if they are not required */
    if ((  ps2AllPipeData->meshInstance & (rxINSTANCEUV | rxINSTANCEUV2)) &&
        (  ps2AllPipeData->matPvtData->clinfo[CL_UV].attrib & CL_ATTRIB_REQUIRED  ) &&
        (!(ps2AllPipeData->matPvtData->clinfo[CL_UV].attrib & CL_ATTRIB_DONT_FILL)) &&
        (rwObjectTestFlags(world, rpWORLDTEXTURED ) ||
         rwObjectTestFlags(world, rpWORLDTEXTURED2)   ) )
    {
        u_long128 *data =
            (ps2AllResHeader->data + ps2AllResHeader->fieldRec[CL_UV].dataoffset);
        RwReal *dataTmp = (RwReal *)data;
        RwTexCoords *tc = sector->texCoords[0];

        INDEXRESET();

        vertCounter = numVerts;
        while (vertCounter > 0)
        {
            if (vertCounter <= (RwInt32)ps2AllResHeader->batchSize)
            {
                data -= ps2AllResHeader->fieldRec[CL_UV].reverse;
                dataTmp = (RwReal *)data;
                j = vertCounter;
                vertCounter = 0;
            }
            else
            {
                j = ps2AllResHeader->fieldRec[CL_UV].numVerts;
                vertCounter -= (j - stripTmp);
            }

            while (j--)
            {
                RxVertexIndex idx   = INDEXGET();
                RwTexCoords  *coord = &(tc[idx]);

               *dataTmp++ = coord->u;
               *dataTmp++ = coord->v;

                INDEXINC();
            }

            STRIPREVERSE(stripTmp);

            data += ps2AllResHeader->fieldRec[CL_UV].skip;
            dataTmp = (RwReal *)data;
        }
    }

    if ((  ps2AllPipeData->meshInstance & rxINSTANCEUV2) &&
        (  ps2AllPipeData->matPvtData->clinfo[CL_UV2].attrib & CL_ATTRIB_REQUIRED  ) &&
        (!(ps2AllPipeData->matPvtData->clinfo[CL_UV2].attrib & CL_ATTRIB_DONT_FILL)) &&
        (rwObjectTestFlags(world, rpWORLDTEXTURED2))                 )
    {
        u_long128 *data =
            (ps2AllResHeader->data + ps2AllResHeader->fieldRec[CL_UV2].dataoffset);
        RwReal *dataTmp = (RwReal *)data;
        RwTexCoords *tc1 = sector->texCoords[0];
        RwTexCoords *tc2 = sector->texCoords[1];

        INDEXRESET();

        vertCounter = numVerts;
        while (vertCounter > 0)
        {
            if (vertCounter <= (RwInt32)ps2AllResHeader->batchSize)
            {
                data -= ps2AllResHeader->fieldRec[CL_UV2].reverse;
                dataTmp = (RwReal *)data;
                j = vertCounter;
                vertCounter = 0;
            }
            else
            {
                j = ps2AllResHeader->fieldRec[CL_UV2].numVerts;
                vertCounter -= (j - stripTmp);
            }

            while (j--)
            {
                RxVertexIndex idx   = INDEXGET();
                RwTexCoords  *coord;

                coord = &(tc1[idx]);
                *dataTmp++ = coord->u;
                *dataTmp++ = coord->v;

                coord = &(tc2[idx]);
                *dataTmp++ = coord->u;
                *dataTmp++ = coord->v;

                INDEXINC();
            }

            STRIPREVERSE(stripTmp);

            data += ps2AllResHeader->fieldRec[CL_UV2].skip;
            dataTmp = (RwReal *)data;
        }
    }

    if ((  ps2AllPipeData->meshInstance & rxINSTANCERGBA) &&
        (  ps2AllPipeData->matPvtData->clinfo[CL_RGBA].attrib & CL_ATTRIB_REQUIRED  ) &&
        (!(ps2AllPipeData->matPvtData->clinfo[CL_RGBA].attrib & CL_ATTRIB_DONT_FILL))   )
//TODOCS[3]:WHY NOT TESTING WORLD FLAGS?? (ETC, OTHER PLACES... AFFECTED BY IN-PLACE INSTANCING OR LACK THEREOF?
//         SHOULD WE EVEN BOTHER TESTING? I MEAN, PEOPLE ARE ASKIGN US TO ISNTANCE SOMETHING WE CAN'T... SHOULD WE
//         LEAVE UNINITIALISED OR FILL BLANK?
    {
        u_long128 *data =
            (ps2AllResHeader->data + ps2AllResHeader->fieldRec[CL_RGBA].dataoffset);
        RwUInt32 *dataTmp = (RwUInt32*)data;
        RwRGBA *preLitLum = sector->preLitLum;

        INDEXRESET();

        vertCounter = numVerts;
        while (vertCounter > 0)
        {
            if (vertCounter <= (RwInt32)ps2AllResHeader->batchSize)
            {
                data -= ps2AllResHeader->fieldRec[CL_RGBA].reverse;
                dataTmp = (RwUInt32 *)data;
                j = vertCounter;
                vertCounter = 0;
            }
            else
            {
                j = ps2AllResHeader->fieldRec[CL_RGBA].numVerts;
                vertCounter -= (j - stripTmp);
            }

            /* Really I want this in the attributes */
            if (rwObjectTestFlags(world, rpWORLDPRELIT))
            {
                while (j--)
                {
#if (!defined(OVERRIDELIGHT))
                    RxVertexIndex idx = INDEXGET();
                    const RwRGBA *col = &(preLitLum[idx]);

                   *dataTmp++ = ((RwUInt32)((RwUInt8)col->red  ) <<  0) |
                                ((RwUInt32)((RwUInt8)col->green) <<  8) |
                                ((RwUInt32)((RwUInt8)col->blue ) << 16) |
                                ((RwUInt32)((RwUInt8)col->alpha) << 24);
                    INDEXINC();
#else /* !OVERRIDELIGHT */
                   *dataTmp++ = 0xffffffff;
#endif /* !OVERRIDELIGHT */
                }
            }
            else
            {
                while (j--)
                {
#if (!defined(SUBSISTLIGHT))
                    *dataTmp++ = 0xff000000;
#else /* !SUBSISTLIGHT */
                    *dataTmp++ = 0xffffffff;
#endif /* !SUBSISTLIGHT */
                }
            }

            STRIPREVERSE(stripTmp);

            data += ps2AllResHeader->fieldRec[CL_RGBA].skip;
            dataTmp = (RwUInt32 *)data;
        }
    }

    if ((  ps2AllPipeData->meshInstance & rxINSTANCENORMAL) &&
        (  ps2AllPipeData->matPvtData->clinfo[CL_NORMAL].attrib & CL_ATTRIB_REQUIRED  ) &&
        (!(ps2AllPipeData->matPvtData->clinfo[CL_NORMAL].attrib & CL_ATTRIB_DONT_FILL))   )
//TODO[3]:WHY NOT TESTING WORLD FLAGS??
    {
        u_long128 *data = (ps2AllResHeader->data
                           + ps2AllResHeader->fieldRec[CL_NORMAL].dataoffset);
        RwUInt8 *dataTmp = (RwUInt8 *)data;
        RpVertexNormal *normals = sector->normals;

        INDEXRESET();

        vertCounter = numVerts;
        while (vertCounter > 0)
        {
            if (vertCounter <= (RwInt32)ps2AllResHeader->batchSize)
            {
                data -= ps2AllResHeader->fieldRec[CL_NORMAL].reverse;
                dataTmp = (RwUInt8 *)data;
                j = vertCounter;
                vertCounter = 0;
            }
            else
            {
                j = ps2AllResHeader->fieldRec[CL_NORMAL].numVerts;
                vertCounter -= (j - stripTmp);
            }

            /* Really I want this in the attributes */
            if (rwObjectTestFlags(world, rpWORLDNORMALS))
            {
                if (ps2AllPipeData->matPvtData->clinfo[CL_NORMAL].attrib & CL_ATTRIB_OPAQUE)
                {
                    while (j--)
                    {
                        RwUInt32        idx    = INDEXGET();
                        RpVertexNormal *normal = &(normals[idx]);

                       *dataTmp++ = normal->x;
                       *dataTmp++ = normal->y;
                       *dataTmp++ = normal->z;

                        INDEXINC();
                    }
                }
                else
                {
                    while (j--)
                    {
                        RwUInt32        idx    = INDEXGET();
                        RpVertexNormal *normal = &(normals[idx]);

                       *dataTmp++ = normal->x;
                       *dataTmp++ = normal->y;
                       *dataTmp++ = normal->z;
                       *dataTmp++ = 0;

                        INDEXINC();
                    }
                }
            }
#if (defined(RWDEBUG))
            else
            {
                if (ps2AllPipeData->matPvtData->clinfo[CL_NORMAL].attrib & CL_ATTRIB_OPAQUE)
                {
                    while (j--)
                    {
                       *dataTmp++ = 0;
                       *dataTmp++ = 0;
                       *dataTmp++ = 0;
                    }
                }
                else
                {
                    while (j--)
                    {
                       *dataTmp++ = 0;
                       *dataTmp++ = 0;
                       *dataTmp++ = 0;
                       *dataTmp++ = 0;
                    }
                }
            }
#endif /* (defined(RWDEBUG)) */

            STRIPREVERSE(stripTmp);

            data += ps2AllResHeader->fieldRec[CL_NORMAL].skip;
            dataTmp = (RwUInt8 *)data;
        }
    }

    REDEBUGObjectDMADumpMacro();

    RWRETURN(TRUE);
}
