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

#ifndef PS2ALLSECTOR_H
#define PS2ALLSECTOR_H

#include "nodeps2all.h"

/****************************************************************************
 global types
 */
/* Used in RpWorldSectorPS2AllLightingSetupMacro, undocumented/stealth */

/* RWPUBLIC */

typedef struct rpWorldSectorPS2AllLightData rpWorldSectorPS2AllLightData;
struct rpWorldSectorPS2AllLightData
{
    RwSurfaceProperties  *surface;                      /**< Internal Use */
    RxWorldApplyLightFunc lightFunc;                    /**< Internal Use */
};

/* RWPUBLICEND */

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

/* 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)) */
/* RWPUBLICEND */

/* TODO[3][5]: WHY TEST FOR WORLDSECTOR REINSTANCING DUE TO DATA CHANGES?
 *   SEE TOP OF ps2allatomic.h FOR MORE DISCUSSION OF REINSTANCE TESTS */
/* RWPUBLIC */
#define RPWORLDSECTORMAKEOBJID(meshHeader)           \
    (((RwUInt16)((meshHeader)->serialNum)) |    \
     (((RwUInt8)(rwObjectGetFlags((RpWorld *)RWSRCGLOBAL(curWorld)) & WORLDRENDERTYPEMASK)) << 16))

#define RPWORLDSECTOROBJIDGETSERIALNUM(objID) ((RwUInt16)(objID))
#define RPWORLDSECTOROBJIDGETFLAGS(objID)     ((RwUInt8)((objID) >> 16))

/* TRUE if the pointer points to an RpWorldSector, FALSE if an RpAtomic */
#define RPWORLDSECTORVERIFY(_object) (*(RwUInt8 *)(_object) == (RwUInt8)-1)


/* Used as RpWorldSectorPS2AllGetMeshHeaderMeshCache (function in debug) */
#define RpWorldSectorPS2AllGetMeshHeaderMeshCacheMacro(_sector, _ps2AllPipeData)                        \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd = (_ps2AllPipeData);                                                       \
    RpWorldSector *_sctr = (_sector);                                                                   \
                                                                                                        \
    PS2ALLMACROASSERT(NULL != _p2apd);                                                                  \
    PS2ALLMACROASSERT(NULL != _sctr);                                                                   \
                                                                                                        \
    _p2apd->meshHeader = _sctr->mesh;                                                                   \
    PS2ALLMACROASSERT(NULL != _p2apd->meshHeader);                                                      \
                                                                                                        \
    /* Get the RwMeshCache from the sector */                                                           \
    _p2apd->meshCache = rpWorldSectorGetMeshCache(_sctr, _p2apd->meshHeader->numMeshes);                \
    PS2ALLMACROASSERT(NULL != _p2apd->meshCache);                                                       \
}                                                                                                       \
MACRO_STOP

/* Used as RpPS2AllWorldSectorGatherObjMetrics (function in debug) */
#if (defined(RWMETRICS))
#define RpWorldSectorPS2AllGatherObjMetricsMacro(_sector)                                               \
MACRO_START                                                                                             \
{                                                                                                       \
    RpWorldSector *_sctr = (_sector);                                                                   \
                                                                                                        \
    /* Update our metrics statistics */                                                                 \
    RWSRCGLOBAL(metrics)->numVertices  += RpWorldSectorGetNumVertices(_sctr);                           \
    RWSRCGLOBAL(metrics)->numTriangles += RpWorldSectorGetNumPolygons(_sctr);                           \
}                                                                                                       \
MACRO_STOP
#else /* (defined(RWMETRICS)) */
#define RpWorldSectorPS2AllGatherObjMetricsMacro(_sector) /* No op */
#endif /* (defined(RWMETRICS)) */

/* Used as RpWorldSectorPS2AllObjInstanceTest (function in debug) */
#define RpWorldSectorPS2AllObjInstanceTestMacro(_ps2AllPipeData)                                        \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd = (_ps2AllPipeData);                                                       \
    RwResEntry *_rsnt;                                                                                  \
                                                                                                        \
    /* 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 = RPWORLDSECTORMAKEOBJID(_p2apd->meshHeader);                                 \
                                                                                                        \
    /* Remove DONTINSTANCE so 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 */        \
    _rsnt = *rwMeshCacheGetEntryRef(_p2apd->meshCache, 0);                                              \
    if (NULL != _rsnt)                                                                                  \
    {                                                                                                   \
        /* Test our new identifier against the old one to see how much reinstancing needs doing */      \
        rwPS2AllResEntryHeader *ps2AllResHeader =                                                       \
            RWPS2ALLRESENTRYHEADERFROMRESENTRY(_rsnt);                                                  \
                                                                                                        \
        if (_p2apd->objIdentifier != ps2AllResHeader->objIdentifier)                                    \
        {                                                                                               \
            if (RPWORLDSECTOROBJIDGETFLAGS(_p2apd->objIdentifier) !=                                    \
                RPWORLDSECTOROBJIDGETFLAGS(ps2AllResHeader->objIdentifier))                             \
            {                                                                                           \
                /* Changing object flags 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));                                                 \
            }                                                                                           \
        }                                                                                               \
        /* Currently in-place instancing won't occur because we don't expect editing of sector data */  \
    }                                                                                                   \
    else                                                                                                \
    {                                                                                                   \
        /* Reinstancing will always occur if (resEntry == NULL) */                                      \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* Used as RpWorldSectorPS2AllTransformSetup (function in debug) */
#define RpWorldSectorPS2AllTransformSetupMacro(_transform)                                              \
MACRO_START                                                                                             \
{                                                                                                       \
    RwMatrix **_tnfm = (_transform);                                                                    \
                                                                                                        \
    /* We just cache the camera matrix */                                                               \
   *_tnfm = &(((RwCamera *)RWSRCGLOBAL(curCamera))->viewMatrix);                                        \
    PS2ALLMACROASSERT(RWMATRIXALIGNMENT(*_tnfm));                                                       \
}                                                                                                       \
MACRO_STOP

/* Used as RpWorldSectorPS2AllFrustumTest (function in debug) */
#define RpWorldSectorPS2AllFrustumTestMacro(_sector, _inFrustum)                                        \
MACRO_START                                                                                             \
{                                                                                                       \
    RpWorldSector       *_sctr = (_sector);                                                             \
    RwFrustumTestResult *_infm = (_inFrustum);                                                          \
                                                                                                        \
    RwFrustumPlane      *_frustPlane;                                                                   \
    RwUInt32             _numPlanes;                                                                    \
                                                                                                        \
    /* Assume innocent until proven guilty */                                                           \
   *_infm = rwSPHEREINSIDE;                                                                             \
                                                                                                        \
    _frustPlane = CAMERAEXTFROMCAMERA(RWSRCGLOBAL(curCamera))->largeFrustumPlanes;                      \
                                                                                                        \
    _numPlanes = 6;                                                                                     \
    while (_numPlanes--)                                                                                \
    {                                                                                                   \
        RwV3d  vCorner;                                                                                 \
        RwReal dot;                                                                                     \
                                                                                                        \
        /* Check against plane */                                                                       \
        vCorner.x = ((RwV3d *)&_sctr->boundingBox)[1 - _frustPlane->closestX].x;                        \
        vCorner.y = ((RwV3d *)&_sctr->boundingBox)[1 - _frustPlane->closestY].y;                        \
        vCorner.z = ((RwV3d *)&_sctr->boundingBox)[1 - _frustPlane->closestZ].z;                        \
        dot = RwV3dDotProduct(&vCorner, &_frustPlane->plane.normal);                                    \
        dot -= _frustPlane->plane.distance;                                                             \
                                                                                                        \
        /* We only need to detect a boundary case, we should never be wholly outside here. */           \
        if (dot > 0)                                                                                    \
        {                                                                                               \
            /* Its outside the plane */                                                                 \
           *_infm = rwSPHEREBOUNDARY;                                                                   \
            break;                                                                                      \
        }                                                                                               \
        _frustPlane++;                                                                                  \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* Used as RpWorldSectorPS2AllMatModulateSetup (function in debug) */
#define RpWorldSectorPS2AllMatModulateSetupMacro(_ps2AllPipeData)                                       \
MACRO_START                                                                                             \
{                                                                                                       \
    PS2ALLMACROASSERT(NULL != RWSRCGLOBAL(curWorld));                                                   \
    (_ps2AllPipeData)->matModulate =                                                                    \
        (RpWorldGetFlags((RpWorld *) RWSRCGLOBAL(curWorld)) &                                           \
         rpWORLDMODULATEMATERIALCOLOR) ? TRUE : FALSE;                                                  \
}                                                                                                       \
MACRO_STOP

/* Used as RpWorldSectorPS2AllLightingSetup (function in debug) */
#define RpWorldSectorPS2AllLightingSetupMacro(_ps2AllPipeData, _lightingFunc)                           \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd = (_ps2AllPipeData);                                                       \
    RxWorldApplyLightFunc _ltfn = (_lightingFunc);                                                      \
    RpWorldSector *_sctr = (RpWorldSector *)_p2apd->sourceObject;                                       \
    RpWorld *_wrld = (RpWorld *)RWSRCGLOBAL(curWorld);                                                  \
                                                                                                        \
    PS2ALLMACROASSERT(NULL != _ltfn);                                                                   \
    PS2ALLMACROASSERT(NULL != _sctr);                                                                   \
    /* If we are rendering atomic sectors we must have a world! */                                      \
    PS2ALLMACROASSERT(NULL != _wrld);                                                                   \
                                                                                                        \
    if(rwObjectTestFlags(_wrld, rpWORLDLIGHT))                                                          \
    {                                                                                                   \
        rpWorldSectorPS2AllLightData lightingData;                                                      \
                                                                                                        \
        /* Ideally I'd like to use the code in baworld.c but */                                         \
        /* the function _rpWorldSectorLight isn't exposed */                                            \
        lightingData.surface = &(_wrld->surfaceProps);                                                  \
        lightingData.lightFunc = _ltfn;                                                                 \
                                                                                                        \
        rpWorldForAllGlobalLights(_rpWorldSectorPS2AllDoApplyLight, &lightingData);                     \
        rpWorldSectorForAllLocalLights(_sctr, _rpWorldSectorPS2AllDoApplyLight, &lightingData);         \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* Used as RpWorldSectorPS2AllResEntryAlloc (function in debug) */
#define RpWorldSectorPS2AllResEntryAllocMacro(_ps2AllPipeData, _repEntry, _size, _destroyCallBack)      \
MACRO_START                                                                                             \
{                                                                                                       \
    RwResEntry **_rsny = (_repEntry);                                                                   \
   *_rsny = RwResourcesAllocateResEntry(                                                                \
        (_ps2AllPipeData)->sourceObject, _rsny, _size, _destroyCallBack);                               \
}                                                                                                       \
MACRO_STOP


/* RWPUBLICEND */

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

/* RWPUBLIC */

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

/* Default RW callbacks */
extern RwBool RpWorldSectorPS2AllObjectSetupCallBack(
    RxPS2AllPipeData *ps2AllPipeData,
    RwMatrix **transform,
    RxWorldApplyLightFunc lightingFunc);

/* Standard instance func for sectors */
extern RwBool RpWorldSectorPS2AllInstance(RxPS2AllPipeData *ps2AllPipeData);

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

/* Callback components, for use in user callbacks */
/* ObjectSetupCB */
extern void RpWorldSectorPS2AllGetMeshHeaderMeshCacheFunc(
    RpWorldSector *sector,
    RxPS2AllPipeData *ps2AllPipeData);
extern void RpWorldSectorPS2AllGatherObjMetricsFunc(
    RpWorldSector *sector);
extern void RpWorldSectorPS2AllObjInstanceTestFunc(
    RxPS2AllPipeData *ps2AllPipeData);
extern void RpWorldSectorPS2AllTransformSetupFunc(
    RwMatrix **transform);
extern void RpWorldSectorPS2AllFrustumTestFunc(
    RpWorldSector *sector,
    RwFrustumTestResult *inFrustum);
extern void RpWorldSectorPS2AllMatModulateSetupFunc(
    RxPS2AllPipeData *ps2AllPipeData);
extern void RpWorldSectorPS2AllLightingSetupFunc(
    RxPS2AllPipeData *ps2AllPipeData,
    RxWorldApplyLightFunc lightingFunc);
extern void RpWorldSectorPS2AllResEntryAllocFunc(
    RxPS2AllPipeData *ps2AllPipeData,
    RwResEntry **repEntry,
    RwUInt32 size,
    RwResEntryDestroyNotify destroyCallBack);

#if (defined(RWDEBUG))
#define RpWorldSectorPS2AllGetMeshHeaderMeshCache   RpWorldSectorPS2AllGetMeshHeaderMeshCacheFunc
#define RpWorldSectorPS2AllGatherObjMetrics         RpWorldSectorPS2AllGatherObjMetricsFunc
#define RpWorldSectorPS2AllObjInstanceTest          RpWorldSectorPS2AllObjInstanceTestFunc
#define RpWorldSectorPS2AllTransformSetup           RpWorldSectorPS2AllTransformSetupFunc
#define RpWorldSectorPS2AllFrustumTest              RpWorldSectorPS2AllFrustumTestFunc
#define RpWorldSectorPS2AllMatModulateSetup         RpWorldSectorPS2AllMatModulateSetupFunc
#define RpWorldSectorPS2AllLightingSetup            RpWorldSectorPS2AllLightingSetupFunc
#define RpWorldSectorPS2AllResEntryAlloc            RpWorldSectorPS2AllResEntryAllocFunc
#else /* (defined(RWDEBUG)) */
#define RpWorldSectorPS2AllGetMeshHeaderMeshCache   RpWorldSectorPS2AllGetMeshHeaderMeshCacheMacro
#define RpWorldSectorPS2AllGatherObjMetrics         RpWorldSectorPS2AllGatherObjMetricsMacro
#define RpWorldSectorPS2AllObjInstanceTest          RpWorldSectorPS2AllObjInstanceTestMacro
#define RpWorldSectorPS2AllTransformSetup           RpWorldSectorPS2AllTransformSetupMacro
#define RpWorldSectorPS2AllFrustumTest              RpWorldSectorPS2AllFrustumTestMacro
#define RpWorldSectorPS2AllMatModulateSetup         RpWorldSectorPS2AllMatModulateSetupMacro
#define RpWorldSectorPS2AllLightingSetup            RpWorldSectorPS2AllLightingSetupMacro
#define RpWorldSectorPS2AllResEntryAlloc            RpWorldSectorPS2AllResEntryAllocMacro
#endif /* (defined(RWDEBUG)) */

#ifdef    __cplusplus
}
#endif /* __cplusplus */

/* RWPUBLICEND */


#endif /* PS2ALLSECTOR_H */

