
/****************************************************************************
 *                                                                          *
 * module : matinstance.h                                                   *
 *                                                                          *
 * purpose: MACROS to aid in instancing into DMA data                       *
 *                                                                          *
 ****************************************************************************/

#ifndef MATINSTANCE_H
#define MATINSTANCE_H

#include "nodeps2all.h"


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

/* RWPUBLIC */

/* Instancing support structs */

struct _rwPS2AllNormalLUTs
{
    /* mask[] masks the index into the indices array
     * in UNindexed mode and masks the addition of
     * vert in INdexed mode - vice versa for mask2 */
    RwUInt8 counterGen;
    RwUInt8 startIndOffset;
    RwUInt8 shiftMask[1];
    RwUInt8 shiftMask2[1];
    RwUInt8 vertInc[1];
};

struct _rwPS2AllTriFanLUTs
{
    /* mask[] masks the index into the indices array
     * in UNindexed mode and masks the addition of
     * vert in INdexed mode - vice versa for mask2 */
    RwUInt8 counterGen;
    RwUInt8 startIndOffset;
    RwUInt8 shiftMask[3];
    RwUInt8 shiftMask2[3];
    RwUInt8 vertInc[3];
};

struct _rwPS2AllPolyLineLUTs
{
    /* mask[] masks the index into the indices array
     * in UNindexed mode and masks the addition of
     * vert in INdexed mode - vice versa for mask2 */
    RwUInt8 counterGen;
    RwUInt8 startIndOffset;
    RwUInt8 shiftMask[2];
    RwUInt8 shiftMask2[2];
    RwUInt8 vertInc[2];
};

typedef struct rwPS2AllIndexLUTs rwPS2AllIndexLUTs;
struct rwPS2AllIndexLUTs
{
    RxVertexIndex         fakeIndices[1];
    struct _rwPS2AllNormalLUTs    normal;
    struct _rwPS2AllTriFanLUTs    triFan;
    struct _rwPS2AllPolyLineLUTs  polyLine;
};

/* 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[4]: THIS EXPANDED TO PROVIDE A GENERAL REINSTANCE DATA BLOCK PEOPLE CAN USE (WITH/WITHOUT THESE)
//        ONE BLOCK PER VERTEX FORMAT DESCRIPTOR(?PER PRIMTYPE?), POTENTIALLY PER MESH, ALLOW SWAPPING
//        OF BROKEN-OUT CLUSTER POINTERS

/* RWPUBLIC */

/* Instancing support defines:
 *
 * Allows one piece of code to instance from any of the
 * six primitive types, indexed or unindexed. Uses a struct
 * of small values (e.g bytes as 4x2-bit LUTs) and redundant
 * cheap calcs to remove predication or code duplication */

#define INDEXDECLARE()                                                          \
        RwUInt32       cntGen, indOffset = 0, startIndOffset, modCnt = 0;       \
        RwUInt32       vertRatio, vertLeadup;                                   \
        const RwUInt8  *shiftMask, *shiftMask2, *vertInc, *swap;                \
        const RxVertexIndex *indexArray

/* N.B Trailing semicolon deliberately omitted from INDEXDECLARE */

#define INDEXSETUP(_flags, _indices)                                          \
MACRO_START                                                                   \
{                                                                             \
    static const rwPS2AllIndexLUTs IndexLUT =                                 \
    {                                                                         \
        {0},                                         /* Fake index 'array' */ \
        {0, 0, {~0},         { 0},       {1}},       /* Normal (default) */   \
        {9, 1, {~0, ~0, ~0}, {~0, 0, 0}, {0, 1, 0}}, /* TriFan */             \
        {1, 0, {~0, ~0},     { 0, 0},    {1, 0}}     /* PolyLine */           \
    };                                                                        \
                                                                              \
    if (_flags & rpMESHHEADERTRIFAN)                                          \
    {                                                                         \
        cntGen         = IndexLUT.triFan.counterGen;                          \
        startIndOffset = IndexLUT.triFan.startIndOffset;                      \
        shiftMask      = &(IndexLUT.triFan.shiftMask[0]);                     \
        shiftMask2     = &(IndexLUT.triFan.shiftMask2[0]);                    \
        vertInc        = &(IndexLUT.triFan.vertInc[0]);                       \
        vertRatio      = 3;                                                   \
        vertLeadup     = 2;                                                   \
    }                                                                         \
    else if (_flags & rpMESHHEADERPOLYLINE)                                   \
    {                                                                         \
        cntGen         = IndexLUT.polyLine.counterGen;                        \
        startIndOffset = IndexLUT.polyLine.startIndOffset;                    \
        shiftMask      = &(IndexLUT.polyLine.shiftMask[0]);                   \
        shiftMask2     = &(IndexLUT.polyLine.shiftMask2[0]);                  \
        vertInc        = &(IndexLUT.polyLine.vertInc[0]);                     \
        vertRatio      = 2;                                                   \
        vertLeadup     = 1;                                                   \
    }                                                                         \
    else                                                                      \
    {                                                                         \
        cntGen         = IndexLUT.normal.counterGen;                          \
        startIndOffset = IndexLUT.normal.startIndOffset;                      \
        shiftMask      = &(IndexLUT.normal.shiftMask[0]);                     \
        shiftMask2     = &(IndexLUT.normal.shiftMask2[0]);                    \
        vertInc        = &(IndexLUT.normal.vertInc[0]);                       \
        vertRatio      = 1;                                                   \
        vertLeadup     = 0;                                                   \
    }                                                                         \
    PS2ALLMACROASSERT(sizeof(RwImVertexIndex) == sizeof(RxVertexIndex));      \
    if (! ((_flags) & rpMESHHEADERUNINDEXED) )                                \
    {                                                                         \
        /* The roles of mask and mask2 swap */                                \
        /* between INdexed and UNindexed */                                   \
        swap       = shiftMask;                                               \
        shiftMask  = shiftMask2;                                              \
        shiftMask2 = swap;                                                    \
        PS2ALLMACROASSERT(_indices != NULL);                                  \
        indexArray = (_indices);                                              \
    }                                                                         \
    else                                                                      \
    {                                                                         \
        indexArray = &(IndexLUT.fakeIndices[0]);                              \
    }                                                                         \
}                                                                             \
MACRO_STOP                                                                    \

/* To be called before each instancing loop */
#define INDEXRESET() (modCnt = 0, indOffset = startIndOffset)

/* Gets the current index */
#define INDEXGET()                                                          \
    ( (indOffset << shiftMask2[modCnt]) + indexArray[indOffset << shiftMask[modCnt]] )

/* Increment after each vertex */
#define INDEXINC()                          \
    ( indOffset += vertInc[modCnt],         \
      modCnt = 3&(cntGen >> (modCnt << 1))) \

/* Reversal only affects tristrips and their counter is
 * constant at zero, so it's simple */
#define STRIPREVERSE(_reverse)   ( indOffset -= (_reverse) )

/* Used as RpMeshPS2AllTestNumVerts (function in debug) */
#define RpMeshPS2AllTestNumVertsMacro(_ps2AllPipeData)                                                  \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd  = (_ps2AllPipeData);                                                      \
    rwPS2AllResEntryHeader *_rshd;                                                                      \
    RwUInt32 _nvrt;                                                                                     \
                                                                                                        \
    PS2ALLMACROASSERT(NULL != _p2apd->cacheEntryRef);                                                   \
    PS2ALLMACROASSERT(NULL != *(_p2apd->cacheEntryRef));                                                \
    _rshd = RWPS2ALLRESENTRYHEADERFROMRESENTRY(*(_p2apd->cacheEntryRef));                               \
                                                                                                        \
    RPMESHPS2ALLCALCNUMVERTS(_p2apd, &_nvrt);                                                           \
    if (_nvrt != _rshd->numVerts)                                                                       \
    {                                                                                                   \
        /* NumVerts changing changes the size of the resEntry */                                        \
        _p2apd->meshInstance |= rxINSTANCEFULLINSTANCE;                                                 \
        REDEBUGPrintf(("numVerts change caused full reinstance: %d\n", _nvrt));                         \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

#define RPMESHPS2ALLMAKEMESHID(_meshHeader)             ((RwUInt32)(_meshHeader)->flags)
#define RPMESHPS2ALLMESHIDGETFLAGS(_meshID)             ((RwUInt32)(_meshID))

/* Used as RpMeshPS2AllTestMeshID (function in debug) */
#define RpMeshPS2AllTestMeshIDMacro(_ps2AllPipeData)                                                    \
MACRO_START                                                                                             \
{                                                                                                       \
    RxPS2AllPipeData *_p2apd  = (_ps2AllPipeData);                                                      \
    rwPS2AllResEntryHeader *_rshd;                                                                      \
                                                                                                        \
    PS2ALLMACROASSERT(NULL != _p2apd->cacheEntryRef);                                                   \
    PS2ALLMACROASSERT(NULL != *(_p2apd->cacheEntryRef));                                                \
    _rshd = RWPS2ALLRESENTRYHEADERFROMRESENTRY(*(_p2apd->cacheEntryRef));                               \
                                                                                                        \
    _p2apd->meshIdentifier = RPMESHPS2ALLMAKEMESHID(_p2apd->meshHeader);                                \
    if (_p2apd->meshIdentifier != _rshd->meshIdentifier)                                                \
    {                                                                                                   \
        /* This (meshHeader flags changing) causes a full reinstance of this mesh */                    \
        _p2apd->meshInstance |= rxINSTANCEFULLINSTANCE;                                                 \
        REDEBUGPrintf(("meshIdentifier change caused full reinstance: (%x)\n",                          \
                       _p2apd->meshIdentifier));                                                        \
    }                                                                                                   \
}                                                                                                       \
MACRO_STOP

/* RWPUBLICEND */

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

/* RWPUBLIC */

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

/* Callback components, for use in the MeshInstanceTestCB */
extern void RpMeshPS2AllTestNumVertsFunc(
    RxPS2AllPipeData *ps2AllPipeData);
extern void RpMeshPS2AllTestMeshIDFunc(
    RxPS2AllPipeData *ps2AllPipeData);
#if (defined(RWDEBUG))
#define RpMeshPS2AllTestNumVerts                RpMeshPS2AllTestNumVertsFunc
#define RpMeshPS2AllTestMeshID                  RpMeshPS2AllTestMeshIDFunc
#else /* (defined(RWDEBUG)) */
#define RpMeshPS2AllTestNumVerts                RpMeshPS2AllTestNumVertsMacro
#define RpMeshPS2AllTestMeshID                  RpMeshPS2AllTestMeshIDMacro
#endif /* (defined(RWDEBUG)) */

/* RWPUBLICEND */

/* These exposed so nodePS2All.c/ps2allmat.c can get at them */
extern void reDestroyCallBack(RwResEntry *resEntry);
extern RwUInt32 DMADataSizeRecalc(RxPS2AllPipeData *ps2AllPipeData,
                                  RwUInt32 numVerts,
                                  rwPS2AllFieldRec *fieldRec,
                                  RwUInt32 *batchSizePtr,
                                  RwUInt32 *batchesPerTagPtr,
                                  RwUInt32 *numBatchesPtr);
extern RwBool   DMADataFillTags(RxPS2AllPipeData *ps2AllPipeData);
extern RwBool   _rwPS2AllRabinsConstructionTimeCode(rxNodePS2AllMatPvtData *pvtData);

/* RWPUBLIC */

#ifdef    __cplusplus
}
#endif /* __cplusplus */


/* RWPUBLICEND */


#endif /* MATINSTANCE_H */

