
/****************************************************************************
 *                                                                          *
 * module : ps2allatomic.h                                                  *
 *                                                                          *
 * purpose: see ps2allatomic.c                                              *
 *                                                                          *
 ****************************************************************************/

#ifndef ps2allatomic_H
#define ps2allatomic_H

#include "nodeps2all.h"


/****************************************************************************
 global types
 */

/* Used in RpAtomicPS2AllLightingSetupMacro, undocumented/stealth */

/* RWPUBLIC */

typedef struct rpAtomicPS2AllLightData rpAtomicPS2AllLightData;
struct rpAtomicPS2AllLightData
{
    RwSurfaceProperties  *surface;
    RwMatrix              invMat;
    RwReal                invScale;
    RxWorldApplyLightFunc lightFunc;
};

/* RWPUBLICEND */

/****************************************************************************
 global defines
 */

/* TODO[3][5]: CONFIRM CORRECTNESS/NECESSITY OF ALL REINSTANCE TEST FLAG COMPONENTS:
objectflags seem redundant now??? (they are RpGeometryFlags...
  these CAN'T change unless the geometry is destroyed... ???
  can't you just replace this with an assert or summat?)
what is serialNum and when is it changed??
  not when the mesh is rebuilt surely? that requires lockpolygons and... that should
  cause a FULL reinstance but we only test serialnum for a full reinstance!!
Basically:
  Go through all reinstance checks (per-object and per-mesh) and remove
    as many as you possibly can... make an early-out test if you can which
    says "yes this object has changed" and if that fails go and see exactly
    what has changed... we spend too much time/code checking as it is.
    rpGEOMETRYLOCKPOLYGON should *not* cause a full reinstance unless it
    changes numverts which is already tested... so we can save a test (see
    revision 1.127 of nodePS2MatInstance.c where Rabin put in this test in
    two places - only the first is necessary). Test this by getting a geometry
    tristripped exhaustively by the exporter then lock and unlock it in-app
    and see if it crashes with/without the FULLREINSTANCE on rpGEOMETRYLOCKPOLYGONS
    the meshType is strictly necessary (given RW seems to allow people to edit
     anything and keeps no conglomerated early-out flag for "I have changed"...
     then again, if a customer changes this, it could be impossible to track
     down... I mean, we don't do extra work to work around stupidity at the
     cost of performance, but this is PS2 and bugs are nasty...)
    remember, you only get problems when you reinstance wrongly (dma size
      should have been recalced but wasn't), not when you skip the reinstance
      code altogether... check this, but if rabinsmatinstancecode is skipped
      then nothing need be checked... so earlying-out wrongly ain't gonna
      make it crash... HENCE we can leave detailed tests until after we've
      decided to reinstance (at which point we're slow anyway)
       - so speed up the "do we reinstance at all?" side of things */
//TODO[5]: MAY BE MORE EFFICIENT TO OR ALL CONGRUENT(PARTIAL)(/IN-PLACE?) PROPERTIES
//       TOGETHER AND ALL FULL PROPERTIES AND TEST FULL THEN IF NOT CHANGED TEST
//       CONGRUENT(/IN-PLACE?)


/* RWPUBLIC */
#define RPATOMICPS2ALLMAKEOBJID(meshHeader, geom)                           \
    (((RwUInt16)((meshHeader)->serialNum)) |                                \
     (((RwUInt8)(rwObjectGetFlags(geom) & ATOMICRENDERTYPEMASK)) << 16) |   \
     (((RwUInt8)((geom)->numMorphTargets)) << 24))
/* RWPUBLICEND */
//TODO[3]: WE'VE LOST "meshType" (I.E meshHeader->flags) - ENSURE THAT'S COVERED BY SOMETHING ELSE (serialNum?)
/* RWPUBLIC */
#define RPATOMICPS2ALLOBJIDGETSERIALNUM(_objID)         ((RwUInt16)(_objID))
#define RPATOMICPS2ALLOBJIDGETFLAGS(_objID)             ((RwUInt8)((_objID) >> 16))
#define RPATOMICPS2ALLOBJIDGETNUMMORPHTARGETS(_objID)   ((RwUInt8)((_objID) >> 24))

/* RWPUBLICEND */
/* REDEBUGPrintf and RWASSERT are NOPs in rpworld.h, for app code */
/* RWPUBLIC */
#if (!defined(REDEBUGPrintf))
#define REDEBUGPrintf(args) /* No op */
#endif /* (!defined(REDEBUGPrintf)) */
#if (defined(RWASSERT))
#define PS2ALLMACROASSERT(args) RWASSERT(args)
#else /* (!defined(RWASSERT)) */
#define PS2ALLMACROASSERT(args) /* No op */
#endif /* (!defined(RWASSERT)) */

/* Used as RpAtomicGetMeshHeaderMeshCache (function in debug) */
/* RWPUBLICEND */
//TODO[3][5]: SHOULD WE GET THE MESHCACHE IF THE GEOM IS PERSISTENT? THIS STUFF'S ONLY ACCESSED IN INSTANCING CODE...
/* RWPUBLIC */
#define RpAtomicPS2AllGetMeshHeaderMeshCacheMacro(_atomic, _ps2AllPipeData)                             \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd = (_ps2AllPipeData);                                                       \
    RpAtomic   *_atmc = (_atomic);                                                                      \
    RpGeometry *_gmty = RpAtomicGetGeometry(_atmc);                                                     \
                                                                                                        \
    _p2apd->meshHeader = _gmty->mesh;                                                                   \
    PS2ALLMACROASSERT(NULL != _p2apd->meshHeader);                                                      \
                                                                                                        \
    if (RpGeometryGetNumMorphTargets(_gmty) != 1)                                                       \
    {                                                                                                   \
        _p2apd->meshCache =                                                                             \
            rpAtomicGetMeshCache(_atmc, _gmty->mesh->numMeshes);                                        \
    }                                                                                                   \
    else                                                                                                \
    {                                                                                                   \
        _p2apd->meshCache =                                                                             \
            rpGeometryGetMeshCache(_gmty, _gmty->mesh->numMeshes);                                      \
    }                                                                                                   \
    PS2ALLMACROASSERT(NULL != _p2apd->meshCache);                                                       \
}                                                                                                       \
MACRO_STOP

/* Used as RpPS2AllAtomicGatherObjMetrics (function in debug) */
#if (defined(RWMETRICS))
#define RpAtomicPS2AllGatherObjMetricsMacro(_atomic)                                                    \
MACRO_START                                                                                             \
{                                                                                                       \
    RpAtomic *_gmty = RpAtomicGetGeometry(_atomic);                                                     \
    /* Update our metrics statistics */                                                                 \
    {                                                                                                   \
        RWSRCGLOBAL(metrics)->numVertices  +=                                                           \
            RpGeometryGetNumVertices(_gmty);                                                            \
        RWSRCGLOBAL(metrics)->numTriangles +=                                                           \
            RpGeometryGetNumTriangles(_gmty);                                                           \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP
#else  /* (defined(RWMETRICS)) */
#define RpAtomicPS2AllGatherObjMetricsMacro(_atomic) /* No op */
#endif /* (defined(RWMETRICS)) */

/* Used as RpAtomicPS2AllMorphSetup (function in debug) */
/* RWPUBLICEND */
// TODO[7]: TEMPORARY - TILL THE FINAL FASTMORPH IS
//         IMPLEMENTED AS A NON-SPECIALLY-HANDLED PLUGIN
/* RWPUBLIC */
#if (defined(FASTMORPH))
#define RpAtomicPS2AllMorphSetupMacro(_atomic, _ps2AllPipeData)                                         \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd = (_ps2AllPipeData);                                                       \
    RpAtomic   *_atmc = (_atomic);                                                                      \
    RpGeometry *_gmty = RpAtomicGetGeometry(_atmc);                                                     \
                                                                                                        \
    _p2apd->numMorphTargets = _gmty->numMorphTargets;                                                   \
    /* Set up morph scale where appropriate */                                                          \
    if (_p2apd->numMorphTargets > 1)                                                                    \
    {                                                                                                   \
        RpInterpolator *_itpltr;                                                                        \
                                                                                                        \
        _itpltr = &(_atmc->interpolator);                                                               \
        PS2ALLMACROASSERT(NULL != _itpltr);                                                             \
                                                                                                        \
        /* Upload morph 'scale' value in the extra float */                                             \
        _p2apd->spExtra = _itpltr->recipTime*                                                           \
                          _itpltr->position;                                                            \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP
#else  /* (defined(FASTMORPH)) */
#define RpAtomicPS2AllMorphSetupMacro(_atomic, _ps2AllPipeData) /* No op */
#endif /* (defined(FASTMORPH)) */

/* Used as RpAtomicObjInstanceTest (function in debug) */
/* RWPUBLICEND */
//TODO[3]: SEE TOP O' FILE, WE NEED TO RATIONALISE REINSTANCE TESTS MON... ATOMICS AND SECTORS
//TODO[6]: THIS SHOULD CHECK SEE IF THE NUMBER CHANGED FROM >1 TO =1 OR VICE VERSA, IF IT WENT
//        FROM 2-3 THEN NO NEED TO DO A FULL REINSTANCE COS YOU DO FASTMORPH STUFF IN BOTH CASES
//TODO[3]: [on resetting the instance flag once per object] IS THIS NECESSARY ANY MORE? GIVEN WE HAVE
//        OBJINSTANCE AND MESHINSTANCE FLAGS SEPARATELY? THE OBJECTENDCB FOR ATOMICS MIGHT BE REDUNDANT NOW...
/* RWPUBLIC */

/* Can't nest # directives in macros, so have to have predicate some
 * separated sub-sections for RpAtomicPS2AllObjInstanceTestMacro */
#if (defined(FASTMORPH))
#define _AtomicObjInstTestMorph()                                                                       \
     ||                                                                                                 \
     (RPATOMICPS2ALLOBJIDGETNUMMORPHTARGETS(_p2apd->objIdentifier) !=                                   \
      RPATOMICPS2ALLOBJIDGETNUMMORPHTARGETS(ps2AllResHeader->objIdentifier))
#define _AtomicObjInstTestInterp()                                                                      \
MACRO_START                                                                                             \
{                                                                                                       \
     /* Rules are a bit different here. We check to see if the morph */                                 \
     /* start/end have changed, and only then reinstance */                                             \
     if ((ps2AllResHeader->morphFinish != interpolator->endMorphTarget) ||                              \
         (ps2AllResHeader->morphStart  != interpolator->startMorphTarget) )                             \
     {                                                                                                  \
         _p2apd->objInstance |= rxINSTANCECONGRUENTINSTANCE;                                            \
         REDEBUGPrintf(("interpolator forced disconnect of resEntry\n"));                               \
         REDEBUGPrintf(("start: %d, end: %d\n",                                                         \
                       interpolator->startMorphTarget,                                                  \
                       interpolator->endMorphTarget));                                                  \
     }                                                                                                  \
}                                                                                                       \
MACRO_STOP
#else /* (defined(FASTMORPH)) */
#define _AtomicObjInstTestMorph() /* No op */
#define _AtomicObjInstTestInterp()                                                                      \
MACRO_START                                                                                             \
{                                                                                                       \
    /* NB: we only OR the value here so if polygons changed */                                          \
    /* above the FULL reinstance isn't cancelled! */                                                    \
    _p2apd->objInstance |= rxINSTANCECONGRUENTINSTANCE;                                                 \
    /* Flag reset once per atomic, not per mesh. */                                                     \
    /* (otherwise only the first mesh is animated!) */                                                  \
    REDEBUGPrintf(("interpolator forced rebuild\n"));                                                   \
}                                                                                                       \
MACRO_STOP
#endif /* (defined(FASTMORPH)) */

/* RWPUBLICEND */
/* All the resentries within an object should have the same morphing-related
 * values - ugh, this may change (per-material 'pipes') but FASTMORPH is a
 * big ugly hack that will DIE... so let's live with it for now, eh? */
/* RWPUBLIC */
#define RpAtomicPS2AllObjInstanceTestMacro(_atomic, _ps2AllPipeData)                                    \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd = (_ps2AllPipeData);                                                       \
    RpAtomic   *_atmc = (_atomic);                                                                      \
    RpGeometry *_gmty = RpAtomicGetGeometry(_atmc);                                                     \
                                                                                                        \
    if ( !(_gmty->instanceFlags & rpGEOMETRYPERSISTENT) )                                               \
    {                                                                                                   \
        RwResEntry *resEntry;                                                                           \
                                                                                                        \
        /* The per-mesh identifier will be identical for all meshes in an object. Regardless, we    */  \
        /* allow per-material reinstance tests to override/replace the per-object ones done here    */  \
                                                                                                        \
        /* Make a new object identifier */                                                              \
        _p2apd->objIdentifier = RPATOMICPS2ALLMAKEOBJID(_p2apd->meshHeader, _gmty);                     \
                                                                                                        \
        /* Remove DONTINSTANCE so (given we're not using persistent */                                  \
        /* instance data) meshes are free to do their tests */                                          \
        /* (and we don't skip instancing if (resEntry == NULL)) */                                      \
        _p2apd->objInstance &= ~rxINSTANCEDONTINSTANCE;                                                 \
                                                                                                        \
        /* The same object identifier goes into every */                                                \
        /* mesh's resEntry, so just grab the first one */                                               \
        resEntry = *rwMeshCacheGetEntryRef(_p2apd->meshCache, 0);                                       \
        if (NULL != resEntry)                                                                           \
        {                                                                                               \
            /* Test our new identifier against the old one */                                           \
            /* to see how much reinstancing needs doing. */                                             \
            rwPS2AllResEntryHeader *ps2AllResHeader =                                                   \
                RWPS2ALLRESENTRYHEADERFROMRESENTRY(resEntry);                                           \
            RpInterpolator *interpolator = &(_atmc->interpolator);                                      \
                                                                                                        \
            /* This is important, else the instance CB can't tell atomics from sectors */               \
            PS2ALLMACROASSERT(0 != RPATOMICPS2ALLOBJIDGETNUMMORPHTARGETS(_p2apd->objIdentifier));       \
            if (_p2apd->objIdentifier != ps2AllResHeader->objIdentifier)                                \
            {                                                                                           \
                if ( (RPATOMICPS2ALLOBJIDGETFLAGS(_p2apd->objIdentifier) !=                             \
                      RPATOMICPS2ALLOBJIDGETFLAGS(ps2AllResHeader->objIdentifier))                      \
                     /* This is FASTMORPH-predicated, see above */                                      \
                     _AtomicObjInstTestMorph() )                                                        \
                {                                                                                       \
                    /* Changing object flags or morphtarget number causes a full reinstance */          \
                    _p2apd->objInstance |= rxINSTANCEFULLINSTANCE;                                      \
                }                                                                                       \
                else                                                                                    \
                {                                                                                       \
                    /* Only a congruent reinstance for other changes (serialNum atm) */                 \
                    _p2apd->objInstance |= rxINSTANCECONGRUENTINSTANCE;                                 \
                    REDEBUGPrintf(("objIdentifier change caused congruent reinstancing: (%x)\n",        \
                                   _p2apd->objIdentifier));                                             \
                }                                                                                       \
            }                                                                                           \
                                                                                                        \
            if (0 != _gmty->lockedSinceLastInst)                                                        \
            {                                                                                           \
                /* Something's changed.... what needs to be reinstanced? */                             \
                if (_gmty->lockedSinceLastInst & rpGEOMETRYLOCKPOLYGONS)                                \
                {                                                                                       \
                    /* Polygons changed... implies numverts changed */                                  \
                    _p2apd->objInstance |= rxINSTANCEFULLINSTANCE;                                      \
                }                                                                                       \
                else                                                                                    \
                {                                                                                       \
                    /* We allow in-place instancing again. Quick conversion from */                     \
                    /* RpGeometryLockMode to RxInstanceFlags, with ASSERT safety-net */                 \
                    _p2apd->objInstance |= rxINSTANCEINPLACEINSTANCE;                                   \
                    _p2apd->objInstance |= _gmty->lockedSinceLastInst << 3;                             \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKVERTICES   << 3) == rxINSTANCEXYZ);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKNORMALS    << 3) == rxINSTANCENORMAL);             \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS  << 3) == rxINSTANCEUV);                 \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS2 << 3) == rxINSTANCEUV2);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKPRELIGHT   << 3) == rxINSTANCERGBA);               \
                }                                                                                       \
            }                                                                                           \
                                                                                                        \
            REDEBUGPrintf(("atomic _p2apd->objInstance %x\n", (RwUInt32)_p2apd->objInstance));          \
                                                                                                        \
            if (interpolator->flags & (RwInt32)rpINTERPOLATORDIRTYINSTANCE)                             \
            {                                                                                           \
                /* This is FASTMORPH-predicated, see above */                                           \
                _AtomicObjInstTestInterp();                                                             \
            }                                                                                           \
        }                                                                                               \
        else                                                                                            \
        {                                                                                               \
            /* Reinstancing will always occur if (resEntry == NULL) */                                  \
        }                                                                                               \
    }                                                                                                   \
    else                                                                                                \
    {                                                                                                   \
        /* Make sure this "persistent instance data" malarky's not just a bluff. */                     \
        PS2ALLMACROASSERT((RwResEntry *)NULL !=                                                         \
                          *rwMeshCacheGetEntryRef(_p2apd->meshCache, 0));                               \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* Used as RpAtomicPS2AllTransformSetup (function in debug) */
#define RpAtomicPS2AllTransformSetupMacro(_atomic, _transform)                                          \
MACRO_START                                                                                             \
{                                                                                                       \
    RpAtomic  *_atmc = (_atomic);                                                                       \
    RwMatrix **_tnfm = (_transform);                                                                    \
                                                                                                        \
    RwMatrix * const _mpLocalToWorld =                                                                  \
        RwFrameGetLTM((RwFrame *)rwObjectGetParent(_atmc));                                             \
    RwMatrix * const _viewMatrix =                                                                      \
        &(((RwCamera *)RWSRCGLOBAL(curCamera))->viewMatrix);                                            \
                                                                                                        \
    PS2ALLMACROASSERT(RWMATRIXALIGNMENT(_mpLocalToWorld));                                              \
    PS2ALLMACROASSERT(RWMATRIXALIGNMENT(_viewMatrix));                                                  \
                                                                                                        \
    RwMatrixMultiply(*_tnfm, _mpLocalToWorld, _viewMatrix);                                             \
}                                                                                                       \
MACRO_STOP

/* Used as RpAtomicPS2AllFrustumTest (function in debug) */
#define RpAtomicPS2AllFrustumTestMacro(_atomic, _inFrustum)                                             \
MACRO_START                                                                                             \
{                                                                                                       \
    RpAtomic  *_atmc = (_atomic);                                                                       \
    RwFrustumTestResult *_infm = (_inFrustum);                                                          \
                                                                                                        \
    RwFrustumPlane     *_frustPlane;                                                                    \
    const RwSphere     *_sphere;                                                                        \
    RwUInt32           _numPlanes;                                                                      \
                                                                                                        \
    /* Assume innocent until proven guilty */                                                           \
   *_inFrustum = rwSPHEREINSIDE;                                                                        \
                                                                                                        \
    _sphere = RpAtomicGetWorldBoundingSphere(_atmc);                                                    \
    PS2ALLMACROASSERT(_sphere);                                                                         \
                                                                                                        \
    _frustPlane = CAMERAEXTFROMCAMERA(RWSRCGLOBAL(curCamera))                                           \
                      ->largeFrustumPlanes;                                                             \
    _numPlanes = 6;                                                                                     \
    while (_numPlanes--)                                                                                \
    {                                                                                                   \
        RwReal dot;                                                                                     \
        dot = RwV3dDotProduct(&_sphere->center,                                                         \
                              &_frustPlane->plane.normal);                                              \
        dot -= _frustPlane->plane.distance;                                                             \
                                                                                                        \
        /* We only need to detect boundary case, we */                                                  \
        /* should never get a totally outside. */                                                       \
        if (dot > -_sphere->radius)                                                                     \
        {                                                                                               \
           *_inFrustum = rwSPHEREBOUNDARY;                                                              \
            break;                                                                                      \
        }                                                                                               \
        _frustPlane++;                                                                                  \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* Used as RpAtomicPS2AllPrimTypeTransTypeSetup (function in debug) */
/* RWPUBLICEND */
//TODO[5]: WE SEEM TO DO NEAR-IDENTICAL THINGS FOR CHOOSING TRANSTYPE/PRIMTYPE BETWEEN
//        ATOMICS/SECTORS/IM3D SO MAYBE WE SHOULD HAVE THIS AS COMMON CODE AND THE
//        OBJECTSETUPCBS MERELY SUPPLY MESHHEADER->FLAGS (IM3D'LL NEED TO CONVERT FROM
//        RWPRIMTYPE) AND INFRUSTUM? IF THEY'RE SET UP ALREADY, IT SKIPS THE CODE?
//         ALSO, USE A LUT FFS! THIS IS TOO MUCH CODE AND PREDICATION TO WADE THROUGH...
/* RWPUBLIC */
#define RpAtomicPS2AllPrimTypeTransTypeSetupMacro(_ps2AllPipeData, _inFrustum)                          \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd  = (_ps2AllPipeData);                                                      \
    RwFrustumTestResult _infm = (_inFrustum);                                                           \
                                                                                                        \
    /* Select ps2AllPipeData->primType based on mesh type (meshHeader->flags) and clipping */           \
    /* (inFrustum).[in-VU fans/polylines not supported] and choose transType, as in */                  \
    /* RxSkyTransTypeFlags: [FOG], [PERSP/ISO], CLIP, STRIP/LIST, TRI/LINE, [CULL] */                   \
    /* The [] bracketed ones are from the global state variable skyTransType */                         \
                                                                                                        \
    if ((_p2apd->meshHeader->flags & rpMESHHEADERTRISTRIP) == rpMESHHEADERTRISTRIP)                     \
    {                                                                                                   \
        /* GS manual p113/114 - triStrip */                                                             \
        _p2apd->primType = 4;                                                                           \
        if (_infm == rwSPHEREINSIDE)                                                                    \
        {                                                                                               \
            /* We don't need to clip  :-) */                                                            \
            _p2apd->transType =                                                                         \
                skyTransType /*| rxSKYTRANSTYPECLIP | rxSKYTRANSTYPELIST | rxSKYTRANSTYPELINE*/;        \
        }                                                                                               \
        else                                                                                            \
        {                                                                                               \
            /* We need to clip  :-( */                                                                  \
            _p2apd->transType =                                                                         \
                skyTransType | rxSKYTRANSTYPECLIP /*| rxSKYTRANSTYPELIST | rxSKYTRANSTYPELINE*/;        \
            /* The true TS clipper fixes up the primCode itself on VU1 */                               \
            /* The culling clipper still submits strips */                                              \
        }                                                                                               \
    }                                                                                                   \
    else if (((_p2apd->meshHeader->flags & rpMESHHEADERTRIFAN  ) == rpMESHHEADERTRIFAN) ||              \
             ((_p2apd->meshHeader->flags & rpMESHHEADERPRIMMASK) == 0) /* trilist */      )             \
    {                                                                                                   \
        /* TriFans instance into triLists. GS manual p113/114 - triList */                              \
        _p2apd->primType = 3;                                                                           \
        if (_infm == rwSPHEREINSIDE)                                                                    \
        {                                                                                               \
            _p2apd->transType =                                                                         \
                skyTransType /*| rxSKYTRANSTYPECLIP */| rxSKYTRANSTYPELIST /*| rxSKYTRANSTYPELINE*/;    \
        }                                                                                               \
        else                                                                                            \
        {                                                                                               \
            _p2apd->transType =                                                                         \
                skyTransType | rxSKYTRANSTYPECLIP | rxSKYTRANSTYPELIST /*| rxSKYTRANSTYPELINE*/;        \
        }                                                                                               \
    }                                                                                                   \
    else if ((_p2apd->meshHeader->flags & rpMESHHEADERPOINTLIST) == rpMESHHEADERPOINTLIST)              \
    {                                                                                                   \
        /* We can't guess what primitive type you'll submit, so we choose something arbitrarily */      \
        if (_infm == rwSPHEREINSIDE)                                                                    \
        {                                                                                               \
            /* Assume tri strip */                                                                      \
            _p2apd->transType =                                                                         \
                skyTransType /*| rxSKYTRANSTYPECLIP | rxSKYTRANSTYPELIST | rxSKYTRANSTYPELINE*/;        \
        }                                                                                               \
        else                                                                                            \
        {                                                                                               \
            /* Assume ADC-culled tri strips */                                                          \
            _p2apd->transType =                                                                         \
                skyTransType | rxSKYTRANSTYPECLIP /*| rxSKYTRANSTYPELIST | rxSKYTRANSTYPELINE*/;        \
        }                                                                                               \
        /* Tri strip (possibly ADC-culled) */                                                           \
        _p2apd->primType = 4;                                                                           \
    }                                                                                                   \
    else /* (((_p2apd->meshHeader->flags & rpMESHHEADERLINELIST) == rpMESHHEADERLINELIST) ||  */        \
         /*  ((_p2apd->meshHeader->flags & rpMESHHEADERPOLYLINE) == rpMESHHEADERPOLYLINE)   ) */        \
    {                                                                                                   \
        /* PolyLines instance into LineLists. GS manual p113/114 - lineList (lineStrip is 2) */         \
        _p2apd->primType = 1;                                                                           \
                                                                                                        \
        /* For now the line transform always culls and never fogs */                                    \
        if (_infm == rwSPHEREINSIDE)                                                                    \
        {                                                                                               \
            _p2apd->transType =                                                                         \
                skyTransType /*| rxSKYTRANSTYPECLIP */| rxSKYTRANSTYPELIST | rxSKYTRANSTYPELINE;        \
        }                                                                                               \
        else                                                                                            \
        {                                                                                               \
            _p2apd->transType =                                                                         \
                skyTransType | rxSKYTRANSTYPECLIP | rxSKYTRANSTYPELIST | rxSKYTRANSTYPELINE;            \
        }                                                                                               \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* Used as RpAtomicPS2AllMatModulateSetup (function in debug) */
#define RpAtomicPS2AllMatModulateSetupMacro(_atomic, _ps2AllPipeData)                                   \
    ((_ps2AllPipeData)->matModulate =                                                                   \
        (RpGeometryGetFlags(RpAtomicGetGeometry(_atomic)) &                                             \
         rpGEOMETRYMODULATEMATERIALCOLOR) ? TRUE : FALSE )

/* Used as RpAtomicPS2AllLightingSetup (function in debug) */
#define RpAtomicPS2AllLightingSetupMacro(_ps2AllPipeData, _lightingFunc)                                \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd = (_ps2AllPipeData);                                                       \
    RxWorldApplyLightFunc _ltfn = (_lightingFunc);                                                      \
    RpAtomic *_atmc;                                                                                    \
                                                                                                        \
    PS2ALLMACROASSERT(NULL != _p2apd);                                                                  \
    PS2ALLMACROASSERT(NULL != _ltfn);                                                                   \
                                                                                                        \
    _atmc = (RpAtomic *)_p2apd->sourceObject;                                                           \
    PS2ALLMACROASSERT(NULL != _atmc);                                                                   \
    PS2ALLMACROASSERT(NULL != _atmc->geometry);                                                         \
                                                                                                        \
    if (rwObjectTestFlags((_atmc->geometry), rpGEOMETRYLIGHT))                                          \
    {                                                                                                   \
        RwLLLink *cur, *end;                                                                            \
        RwSurfaceProperties *surface;                                                                   \
        RpMeshHeader *meshHeader;                                                                       \
        rpAtomicPS2AllLightData lightingData;                                                           \
        RwMatrix *frameMat;                                                                             \
        RpWorld  *world;                                                                                \
                                                                                                        \
        PS2ALLMACROASSERT(RWMATRIXALIGNMENT(&lightingData.invMat));                                     \
                                                                                                        \
        /* Increase lightFrame - used to ensure that each light is applied only */                      \
        /* once (if an atomic and light both overlap two sectors, you can see how */                    \
        /* the light might be applied twice if we didn't have a check). */                              \
        RWSRCGLOBAL(lightFrame)++;                                                                      \
                                                                                                        \
        world = (RpWorld *) RWSRCGLOBAL(curWorld);                                                      \
        PS2ALLMACROASSERT(NULL != world);                                                               \
                                                                                                        \
        frameMat = RwFrameGetLTM((RwFrame *)rwObjectGetParent(_atmc));                                  \
        PS2ALLMACROASSERT(RWMATRIXALIGNMENT(frameMat));                                                 \
                                                                                                        \
        /* Setup the lighting data block */                                                             \
        lightingData.lightFunc = _ltfn;                                                                 \
        RwMatrixInvert(&lightingData.invMat, frameMat);                                                 \
                                                                                                        \
        if ((rwMatrixGetFlags(frameMat) & rwMATRIXTYPEMASK) == rwMATRIXTYPEORTHONORMAL)                 \
        {                                                                                               \
            lightingData.invScale = (RwReal) 1.0;                                                       \
        }                                                                                               \
        else                                                                                            \
        {                                                                                               \
            lightingData.invScale = RwV3dDotProduct(&lightingData.invMat.at, &lightingData.invMat.at);  \
                                                                                                        \
            rwSqrtMacro(lightingData.invScale, lightingData.invScale);                                  \
        }                                                                                               \
                                                                                                        \
        /*lightingData.surface = &atomic->geometry->surfaceProps;*/                                     \
        /* temporary hack to get the surface properties from the first mesh */                          \
        meshHeader = _atmc->geometry->mesh;                                                             \
        PS2ALLMACROASSERT(NULL != meshHeader);                                                          \
        surface = &((RpMesh*)(((RwUInt8*)(meshHeader + 1)) +                                            \
                              meshHeader->firstMeshOffset))->material->surfaceProps;                    \
        lightingData.surface = surface;                                                                 \
                                                                                                        \
        /* Directional light it */                                                                      \
        rpWorldForAllGlobalLights(RpAtomicPS2AllDoApplyLight, &lightingData);                           \
                                                                                                        \
        /* For all sectors that this atomic lies in, apply all lights within */                         \
        cur = rwLinkListGetFirstLLLink(&_atmc->llWorldSectorsInAtomic);                                 \
        end = rwLinkListGetTerminator(&_atmc->llWorldSectorsInAtomic);                                  \
        while (cur != end)                                                                              \
        {                                                                                               \
            RpTie *tpTie = rwLLLinkGetData(cur, RpTie, lWorldSectorInAtomic);                           \
                                                                                                        \
            /* Now apply all the lights (but this time we do the frame thing) */                        \
            rpWorldSectorForAllLocalLights(                                                             \
                tpTie->worldSector, RpAtomicPS2AllDoApplyLightFrame, &lightingData);                    \
                                                                                                        \
            /* Next one */                                                                              \
            cur = rwLLLinkGetNext(cur);                                                                 \
        }                                                                                               \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* Used as RpAtomicPS2AllFinalize (function in debug) */
/* RWPUBLICEND */
//TODO[3]: (the once-per-atomic comment below, as per one of the RpAtomicPS2AllObjInstanceTestMacro)
//        TODO comments) IS THIS TRUE ANY MORE? GIVEN WE HAVE OBJINSTANCE AND
//        MESHINSTANCE FLAGS SEPARATELY? THIS OBJECTENDCB MIGHT BE REDUNDANT....
/* RWPUBLIC */
#define RpAtomicPS2AllFinalizeMacro(_atomic)                                                            \
MACRO_START                                                                                             \
{                                                                                                       \
    /* Some flags are cleared once per atomic not per mesh, */                                          \
    /* given that meshes are instanced/reinstanced separately */                                        \
    RpAtomic *_atmc = (_atomic);                                                                        \
    RpGeometry *_gmty = RpAtomicGetGeometry(_atmc);                                                     \
    RpInterpolator *_iplr = RpAtomicGetInterpolator(_atmc);                                             \
                                                                                                        \
    PS2ALLMACROASSERT(NULL != _gmty);                                                                   \
    PS2ALLMACROASSERT(NULL != _iplr);                                                                   \
                                                                                                        \
    _gmty->lockedSinceLastInst = 0;                                                                     \
    _iplr->flags &= ~rpINTERPOLATORDIRTYINSTANCE;                                                       \
}                                                                                                       \
MACRO_STOP

/* Used as RpMeshPS2AllGatherMeshMetrics (function in debug) */
#if (defined(RWMETRICS))
/* RWPUBLICEND */
//TODO[3]: THIS'LL COUNT POINTLISTS AS TRILISTS Y'KNOW...
/* RWPUBLIC */
#define RpMeshPS2AllGatherMeshMetricsMacro(_ps2AllPipeData)                                             \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd  = (_ps2AllPipeData);                                                      \
                                                                                                        \
    /* We don't count lines */                                                                          \
    if (!(_p2apd->transType & rxSKYTRANSTYPELINE))                                                      \
    {                                                                                                   \
        if (_p2apd->transType & rxSKYTRANSTYPELIST)                                                     \
        {                                                                                               \
            RWSRCGLOBAL(metrics)->numProcTriangles +=                                                   \
                _p2apd->mesh->numIndices / 3;                                                           \
        }                                                                                               \
        else                                                                                            \
        {                                                                                               \
            RWSRCGLOBAL(metrics)->numProcTriangles +=                                                   \
                _p2apd->mesh->numIndices - 2;                                                           \
        }                                                                                               \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP
#else  /* (defined(RWMETRICS)) */
#define RpMeshPS2AllGatherMeshMetricsMacro(_ps2AllPipeData) /* No op */
#endif /* (defined(RWMETRICS)) */


/* Can't nest # directives in macros, so have to have predicate some
 * separated sub-sections for RpAtomicPS2AllResEntryAllocMacro */
#if (defined(FASTMORPH))
#define _AtomicResAllocMorph()                                                                          \
    if (_p2apd->fastMorphing)                                                                           \
    {                                                                                                   \
        /* FastMorphing atomic */                                                                       \
       *_rsny = RwResourcesAllocateResEntry(_p2apd->sourceObject, _rsny, _cyze, _dycb);                 \
    }                                                                                                   \
    else
#else /* (defined(FASTMORPH)) */
#define _AtomicResAllocMorph() /* No op */
#endif /* (defined(FASTMORPH)) */

/* Used as RpAtomicPS2AllResEntryAlloc (function in debug) */
#define RpAtomicPS2AllResEntryAllocMacro(_ps2AllPipeData, _repEntry, _size, _destroyCallBack)           \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd  = (_ps2AllPipeData);                                                      \
    RwResEntry **_rsny = (_repEntry);                                                                   \
    RwResEntryDestroyNotify _dycb = (_destroyCallBack);                                                 \
    RwUInt32 _cyze = (_size);                                                                           \
                                                                                                        \
    _AtomicResAllocMorph()                                                                              \
    {                                                                                                   \
        RpGeometry *_gmty;                                                                              \
                                                                                                        \
        _gmty = RpAtomicGetGeometry((RpAtomic *)(_p2apd->sourceObject));                                \
        PS2ALLMACROASSERT(NULL != _gmty);                                                               \
                                                                                                        \
        if (_gmty->instanceFlags & rpGEOMETRYINSTANCE)                                                  \
        {                                                                                               \
            /* Persistent geometry */                                                                   \
           *_rsny = _rwResourcesAllocateResEntry(_gmty, _rsny, _cyze, rwMEMORYPOOLDEFAULT, _dycb);      \
        }                                                                                               \
        else                                                                                            \
        {                                                                                               \
            /* Non-fastmorphing, non-persistent atomic */                                               \
           *_rsny = RwResourcesAllocateResEntry(_gmty, _rsny, _cyze, _dycb);                            \
        }                                                                                               \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP


/* RWPUBLICEND */

/****************************************************************************
 global prototypes
 */

/* RWPUBLIC */

#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

/* Default RW callbacks */
extern RwBool RpAtomicPS2AllObjectSetupCallBack(
    RxPS2AllPipeData *ps2AllPipeData,
    RwMatrix **transform,
    RxWorldApplyLightFunc lightingFunc);
/* Shared between atomics and sectors (so materials may be shared) */
extern RwBool RpMeshPS2AllMeshInstanceTestCallBack(
    RxPS2AllPipeData *ps2AllPipeData);
/* Shared between atomics and sectors (so materials may be shared) */
extern RwResEntry *RpMeshPS2AllResEntryAllocCallBack(
    RxPS2AllPipeData *ps2AllPipeData,
    RwResEntry **repEntry,
    RwUInt32 size,
    RwResEntryDestroyNotify destroyCallBack);
/* Shared between atomics and sectors (so materials may be shared) */
extern RwBool RpMeshPS2AllInstanceCallBack(
    RxPS2AllPipeData *ps2AllPipeData,
    void **clusters,
    RwUInt32 numClusters);
/* Shared between atomics and sectors (so materials may be shared) */
extern RwBool RpMeshPS2AllBridgeCallBack(
    RxPS2AllPipeData *ps2AllPipeData);
/* Shared between atomics and sectors (so materials may be shared) */
extern RwBool RpMeshPS2AllPostMeshCallBack(
    RxPS2AllPipeData *ps2AllPipeData);
extern RwBool RpAtomicPS2AllObjectFinalizeCallBack(
    RxPS2AllPipeData *ps2AllPipeData);

/* Standard instance func for atomics */
RwBool RpAtomicPS2AllInstance(RxPS2AllPipeData *ps2AllPipeData);

/* Callbacks used by the standard lighting func */
extern RpLight *RpAtomicPS2AllDoApplyLight(RpLight *light, void *pData);
extern RpLight *RpAtomicPS2AllDoApplyLightFrame(RpLight *light, void *pData);

/* Callback components, for use in user callbacks */
/* ObjectSetupCB */
extern void RpAtomicPS2AllGetMeshHeaderMeshCacheFunc(
    RpAtomic *atomic,
    RxPS2AllPipeData *ps2AllPipeData);
extern void RpAtomicPS2AllGatherObjMetricsFunc(
    RpAtomic *atomic);
extern void RpAtomicPS2AllMorphSetupFunc(
    RpAtomic *atomic,
    RxPS2AllPipeData *ps2AllPipeData);
extern void RpAtomicPS2AllObjInstanceTestFunc(
    RpAtomic *atomic,
    RxPS2AllPipeData *ps2AllPipeData);
extern void RpAtomicPS2AllTransformSetupFunc(
    RpAtomic *atomic,
    RwMatrix **transform);
extern void RpAtomicPS2AllFrustumTestFunc(
    RpAtomic *atomic,
    RwFrustumTestResult *inFrustum);
extern void RpAtomicPS2AllPrimTypeTransTypeSetupFunc(
    RxPS2AllPipeData *ps2AllPipeData,
    RwFrustumTestResult inFrustum);
extern void RpAtomicPS2AllMatModulateSetupFunc(
    RpAtomic *atomic,
    RxPS2AllPipeData *ps2AllPipeData);
extern void RpAtomicPS2AllLightingSetupFunc(
    RxPS2AllPipeData *ps2AllPipeData,
    RxWorldApplyLightFunc lightingFunc);
/* ObjectFinalizeCB */
extern void RpAtomicPS2AllFinalizeFunc(
    RpAtomic *atomic);
/* ResEntryAllocCB */
extern void RpAtomicPS2AllResEntryAllocFunc(
    RxPS2AllPipeData *ps2AllPipeData,
    RwResEntry **repEntry,
    RwUInt32 size,
    RwResEntryDestroyNotify destroyCallBack);
/* PostMeshCB */
extern void RpMeshPS2AllGatherMeshMetricsFunc(
    RxPS2AllPipeData *ps2AllPipeData);

#if (defined(RWDEBUG))
#define RpAtomicPS2AllGetMeshHeaderMeshCache    RpAtomicPS2AllGetMeshHeaderMeshCacheFunc
#define RpAtomicPS2AllGatherObjMetrics          RpAtomicPS2AllGatherObjMetricsFunc
#define RpAtomicPS2AllMorphSetup                RpAtomicPS2AllMorphSetupFunc
#define RpAtomicPS2AllObjInstanceTest           RpAtomicPS2AllObjInstanceTestFunc
#define RpAtomicPS2AllTransformSetup            RpAtomicPS2AllTransformSetupFunc
#define RpAtomicPS2AllFrustumTest               RpAtomicPS2AllFrustumTestFunc
#define RpAtomicPS2AllPrimTypeTransTypeSetup    RpAtomicPS2AllPrimTypeTransTypeSetupFunc
#define RpAtomicPS2AllMatModulateSetup          RpAtomicPS2AllMatModulateSetupFunc
#define RpAtomicPS2AllLightingSetup             RpAtomicPS2AllLightingSetupFunc
#define RpAtomicPS2AllFinalize                  RpAtomicPS2AllFinalizeFunc
#define RpAtomicPS2AllResEntryAlloc             RpAtomicPS2AllResEntryAllocFunc
#define RpMeshPS2AllGatherMeshMetrics           RpMeshPS2AllGatherMeshMetricsFunc
#else /* (defined(RWDEBUG)) */
#define RpAtomicPS2AllGetMeshHeaderMeshCache    RpAtomicPS2AllGetMeshHeaderMeshCacheMacro
#define RpAtomicPS2AllGatherObjMetrics          RpAtomicPS2AllGatherObjMetricsMacro
#define RpAtomicPS2AllMorphSetup                RpAtomicPS2AllMorphSetupMacro
#define RpAtomicPS2AllObjInstanceTest           RpAtomicPS2AllObjInstanceTestMacro
#define RpAtomicPS2AllTransformSetup            RpAtomicPS2AllTransformSetupMacro
#define RpAtomicPS2AllFrustumTest               RpAtomicPS2AllFrustumTestMacro
#define RpAtomicPS2AllPrimTypeTransTypeSetup    RpAtomicPS2AllPrimTypeTransTypeSetupMacro
#define RpAtomicPS2AllMatModulateSetup          RpAtomicPS2AllMatModulateSetupMacro
#define RpAtomicPS2AllLightingSetup             RpAtomicPS2AllLightingSetupMacro
#define RpAtomicPS2AllFinalize                  RpAtomicPS2AllFinalizeMacro
#define RpAtomicPS2AllResEntryAlloc             RpAtomicPS2AllResEntryAllocMacro
#define RpMeshPS2AllGatherMeshMetrics           RpMeshPS2AllGatherMeshMetricsMacro
#endif /* (defined(RWDEBUG)) */

#ifdef    __cplusplus
}
#endif /* __cplusplus */

/* RWPUBLICEND */


#endif /* ps2allatomic_H */

