
/****************************************************************************
 *                                                                          *
 * 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;                      /**< Internal Use */
    RwMatrix              invMat;                       /**< Internal Use */
    RwReal                invScale;                     /**< Internal Use */
    RxWorldApplyLightFunc lightFunc;                    /**< Internal Use */
};

/* RWPUBLICEND */

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

/* TODO[3][5]: CONFIRM CORRECTNESS/NECESSITY OF REINSTANCE TEST COMPONENTS:
 *   ARE objectflags redundant? (THEY ARE RpGeometryFlags AND THESE CAN'T
 *  CHANGE UNLESS THE GEOMETRY IS DESTROYED - REPLACE THIS WITH AN ASSERT?)
 *   WHAT *EXACTLY* IS SERIALNUM AND WHEN IS IT CHANGED? IF ITS WHEN THE
 *  MESH IS REBUILT, THAT REQUIRES LOCKPOLYGONS - WHICH SHOULD CAUSE A FULL
 *  REINSTANCE ANYWAY!
 *   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).
 *   THE meshType TEST IS NECESSARY GIVEN RW SEEMS TO ALLOW PEOPLE TO EDIT
 *  ANYTHING BEHIND ITS BACK. IF A CUSTOMER CHANGES IT AND WE DON'T REINSTANCE
 *  IT COULD BE IMPOSSIBLE TO TRACK DOWN THE BUG. WE DON'T WANT TO DO EXTRA
 *  WORK TO WORK AROUND STUPIDITY AT THE COST OF PERFORMANCE, BUT THIS IS PS2
 *  AND BUGS ARE NASTY.
 *   REMEMBER, 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)
 *   SHOULD MIGRATE TESTS->ASSERTS AND REQUIRE PEOPLE DO "RpGeometryDirty()"
 *  SO WE HAVE A 1-BIT TEST "HAS THIS OBJECT CHANGED?" AND IF THAT IS TRUE,
 *  GO AND SEE EXACTLY WHAT HAS CHANGED */

/* TODO[5]: [MADE OBSOLETE BY THE ABOVE] 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 FETCH THE MESHCACHE IF THE GEOM IS PERSISTENT?
 *   IT'S ONLY ACCESSED IN INSTANCING CODE, RIGHT? */
/* 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                                                                                             \
{                                                                                                       \
    RpGeometry *_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 (FASTER) 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[6]: THIS SHOULD CHECK SEE IF NUMMORPHTARGETS CHANGED FROM >1 TO =1
 *  OR VICE VERSA. IF IT WENT FROM 2-3 THEN NO NEED TO DO A FULL REINSTANCE,
 *  SINCE 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 = (RxInstanceFlags)                                                        \
             (_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 = (RxInstanceFlags)                                                             \
        ( _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 = (RxInstanceFlags)                                                         \
             (_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 = (RxInstanceFlags)                                             \
                        (_p2apd->objInstance | rxINSTANCEFULLINSTANCE);                                 \
                }                                                                                       \
                else                                                                                    \
                {                                                                                       \
                    /* Only a congruent reinstance for other changes (serialNum atm) */                 \
                    _p2apd->objInstance = (RxInstanceFlags)                                             \
                        (_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 = (RxInstanceFlags)                                             \
                        (_p2apd->objInstance | rxINSTANCEFULLINSTANCE);                                 \
                }                                                                                       \
                else                                                                                    \
                {                                                                                       \
                    /* We allow in-place instancing again. Quick conversion from */                     \
                    /* RpGeometryLockMode to RxInstanceFlags, with ASSERT safety-net */                 \
                    _p2apd->objInstance = (RxInstanceFlags)                                             \
                        (_p2apd->objInstance | rxINSTANCEINPLACEINSTANCE);                              \
                    _p2apd->objInstance = (RxInstanceFlags)                                             \
                        (_p2apd->objInstance | (_gmty->lockedSinceLastInst << 3));                      \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKVERTICES   << 3) == rxINSTANCEXYZ);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKNORMALS    << 3) == rxINSTANCENORMAL);             \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKPRELIGHT   << 3) == rxINSTANCERGBA);               \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS  << 3) == rxINSTANCEUV);                 \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS1 << 3) == rxINSTANCEUV1);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS2 << 3) == rxINSTANCEUV2);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS3 << 3) == rxINSTANCEUV3);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS4 << 3) == rxINSTANCEUV4);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS5 << 3) == rxINSTANCEUV5);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS6 << 3) == rxINSTANCEUV6);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS7 << 3) == rxINSTANCEUV7);                \
                    PS2ALLMACROASSERT((rpGEOMETRYLOCKTEXCOORDS8 << 3) == rxINSTANCEUV8);                \
                }                                                                                       \
            }                                                                                           \
                                                                                                        \
            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 RpAtomicPS2AllClear (function in debug) */
#define RpAtomicPS2AllClearMacro(_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 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 */                                                           \
   *_infm = 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)                                                                     \
        {                                                                                               \
           *_infm = rwSPHEREBOUNDARY;                                                                   \
            break;                                                                                      \
        }                                                                                               \
        _frustPlane++;                                                                                  \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* Used as RpAtomicPS2AllPrimTypeTransTypeSetup (function in debug) */
/* RWPUBLICEND */
/* TODO[5]: USE A LUT FOR THIS! THIS IS TOO MUCH CODE/PREDICATION */
/* 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 RpAtomicPS2AllLightingPersist (function in debug) */
#define RpAtomicPS2AllLightingPersistMacro(_inverse, _invScale)                                         \
MACRO_START                                                                                             \
{                                                                                                       \
    RwMatrix *_invs = (_inverse);                                                                       \
    RwReal _insl = _invScale;                                                                           \
                                                                                                        \
    /* Make sure they're not doing lighting *and* calling this! */                                      \
    RWASSERT(0 == _skyLightQWordsWritten);                                                              \
                                                                                                        \
    /* This being a macro, this'll boil away to an unconditional single clause */                       \
    if (NULL != _invs)                                                                                  \
    {                                                                                                   \
        /* We're letting lights persist and updating the inverse lighting matrix */                     \
       *_skyLightFillPos++ = _invs->right.x;                                                            \
       *_skyLightFillPos++ = _invs->right.y;                                                            \
       *_skyLightFillPos++ = _invs->right.z;                                                            \
       *_skyLightFillPos++ = _insl;                                                                     \
       *_skyLightFillPos++ = _invs->up.x;                                                               \
       *_skyLightFillPos++ = _invs->up.y;                                                               \
       *_skyLightFillPos++ = _invs->up.z;                                                               \
       *_skyLightFillPos++ = _insl;                                                                     \
       *_skyLightFillPos++ = _invs->at.x;                                                               \
       *_skyLightFillPos++ = _invs->at.y;                                                               \
       *_skyLightFillPos++ = _invs->at.z;                                                               \
       *_skyLightFillPos++ = _insl;                                                                     \
       *_skyLightFillPos++ = _invs->pos.x;                                                              \
       *_skyLightFillPos++ = _invs->pos.y;                                                              \
       *_skyLightFillPos++ = _invs->pos.z;                                                              \
       *_skyLightFillPos++ = _insl;                                                                     \
        _skyLightQWordsWritten = (RwUInt32)(-5);                                                        \
    }                                                                                                   \
    else                                                                                                \
    {                                                                                                   \
        /* We're letting both lights and the inverse lighting matrix persist */                         \
        _skyLightQWordsWritten = (RwUInt32)(-1);                                                        \
    }                                                                                                   \
}                                                                                                       \
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);

/* 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 RpAtomicPS2AllClearFunc(
    RpAtomic *atomic);
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);
extern void RpAtomicPS2AllLightingPersistFunc(
    RwMatrix *inverse,
    RwReal invScale);
/* 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 RpAtomicPS2AllClear                     RpAtomicPS2AllClearFunc
#define RpAtomicPS2AllTransformSetup            RpAtomicPS2AllTransformSetupFunc
#define RpAtomicPS2AllFrustumTest               RpAtomicPS2AllFrustumTestFunc
#define RpAtomicPS2AllPrimTypeTransTypeSetup    RpAtomicPS2AllPrimTypeTransTypeSetupFunc
#define RpAtomicPS2AllMatModulateSetup          RpAtomicPS2AllMatModulateSetupFunc
#define RpAtomicPS2AllLightingSetup             RpAtomicPS2AllLightingSetupFunc
#define RpAtomicPS2AllLightingPersist           RpAtomicPS2AllLightingPersistFunc
#define RpAtomicPS2AllResEntryAlloc             RpAtomicPS2AllResEntryAllocFunc
#define RpMeshPS2AllGatherMeshMetrics           RpMeshPS2AllGatherMeshMetricsFunc
#else /* (defined(RWDEBUG)) */
#define RpAtomicPS2AllGetMeshHeaderMeshCache    RpAtomicPS2AllGetMeshHeaderMeshCacheMacro
#define RpAtomicPS2AllGatherObjMetrics          RpAtomicPS2AllGatherObjMetricsMacro
#define RpAtomicPS2AllMorphSetup                RpAtomicPS2AllMorphSetupMacro
#define RpAtomicPS2AllObjInstanceTest           RpAtomicPS2AllObjInstanceTestMacro
#define RpAtomicPS2AllClear                     RpAtomicPS2AllClearMacro
#define RpAtomicPS2AllTransformSetup            RpAtomicPS2AllTransformSetupMacro
#define RpAtomicPS2AllFrustumTest               RpAtomicPS2AllFrustumTestMacro
#define RpAtomicPS2AllPrimTypeTransTypeSetup    RpAtomicPS2AllPrimTypeTransTypeSetupMacro
#define RpAtomicPS2AllMatModulateSetup          RpAtomicPS2AllMatModulateSetupMacro
#define RpAtomicPS2AllLightingSetup             RpAtomicPS2AllLightingSetupMacro
#define RpAtomicPS2AllLightingPersist           RpAtomicPS2AllLightingPersistMacro
#define RpAtomicPS2AllResEntryAlloc             RpAtomicPS2AllResEntryAllocMacro
#define RpMeshPS2AllGatherMeshMetrics           RpMeshPS2AllGatherMeshMetricsMacro
#endif /* (defined(RWDEBUG)) */

/* Exposed for RpAtomicPS2AllLightingPersistMacro */
extern RwReal * _skyLightFillPos;
extern RwUInt32 _skyLightQWordsWritten;

#ifdef    __cplusplus
}
#endif /* __cplusplus */

/* RWPUBLICEND */


#endif /* ps2allatomic_H */

