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

#ifndef NODEPS2MATINSTANCE_H
#define NODEPS2MATINSTANCE_H

#define FASTMORPH
#define DMAALIGN
#define VUCONTINUEx

#include <rwcore.h>
#include "wrldpipe.h"
#include "ps2clusterattribs.h"

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

/* Maybe this should be public ? */

/* WARNING: dataoffset doesn't mean quite what you expect! If we have less */
/* than batchSize verts, the offset is actually smaller by reverse qws */
typedef struct rwPS2FieldRec rwPS2FieldRec;
struct rwPS2FieldRec
{
    /* When the cluster is opaque, this is the number of vertices in a
     * batch (excepting the last batch), but when the cluster is broken-out,
     * it's equal to the total number of vertices in the mesh. This is so
     * that broken-out clusters can be instanced in one go without doing
     * any tristrip vertex duplication per-batch (that is done with DMA ref
     * tag fiddling for these clusters). */
    int                 numVerts;                       /**< Internal Use */
#ifdef FASTMORPH
    int                 morphNumVerts;                  /**< Internal Use */
#endif                          /* FASTMORPH */
    /* offset from start of data area in ResEntry (in quadwords) */
    int                 dataoffset;                     /**< Internal Use */
#ifdef FASTMORPH
    int                 morphDataoffset;                /**< Internal Use */
#endif                          /* FASTMORPH */
    /* skip jumps from the beginning of one batch of this cluster's data to
     * the next, including space for other (opaque) clusters inbetween and
     * tags (inc. ref tags pointing into broken-out arrays). Should be zero
     * for non-opaque clusters */
    short               skip;                           /**< Internal Use */
#ifdef FASTMORPH
    short               morphSkip;                      /**< Internal Use */
#endif                          /* FASTMORPH */
    /* The reverse field is used cos the last set of batches is smaller, so
     * to pack the batch blocks together each one needs to be shuffled back
     * till it hits the (receded) end of the previous one. Should be zero
     * for non-opaque clusters */
    short               reverse;                        /**< Internal Use */
    unsigned char       vuoffset;                       /**< Internal Use */
#ifndef FASTMORPH
    unsigned char       pad[3];                         /**< Internal Use */
#else                           /* FASTMORPH */
    unsigned char       pad[1];                         /**< Internal Use */
#endif                          /* FASTMORPH */
};

typedef struct rwPS2ClusterQuickInfo rwPS2ClusterQuickInfo;
struct rwPS2ClusterQuickInfo
{
    u_long128          *data;   /**< the instance data */
    RwUInt32            stride; /**< in bytes for broken-out clusters */
};

typedef struct rwPS2ResEntryHeader rwPS2ResEntryHeader;
struct rwPS2ResEntryHeader
{
    /* These fields must be first. They are used by the dma reference count
     * management system */
    RwInt32             refCnt;                         /**< Internal Use */
    RwInt32             clrCnt;                         /**< Internal Use */

    u_long128          *data;                           /**< Internal Use */

    /* Serial Number of data that was instanced */
    RwUInt32            serialNum;                      /**< Internal Use */

    /* Hmm, aren't these dead yet? */
    RwUInt16            overloadFlags;                  /**< Internal Use */ 
    RwUInt16            pad0;                           /**< Internal Use */

    /* For opaque clusters, numVerts = batchSize.
     * For non-opaque clusters, it equals meshheader->numIndices
     * Should we have to reinstance data, we can work
     * in place if refCnt = 0 & numVerts == newNumVerts */
    RwUInt32            numVerts;                       /**< Internal Use */
    /* We might need to shuffle data about if mesh type changes
     * but numverts stays the same, due to the tristrip vertex
     * duplication crap (yeah I know... it's paranoid) */
    RwUInt32            meshType;                       /**< Internal Use */

    /* batchSize is number of verts in a batch */
    int                 batchSize;                      /**< Internal Use */
    /* batchesPerTag is number of batches that a tag can deal with
     * in the totally opaque case (max data transferrable by one tag is
     * 2^16 quadwords) */
    int                 batchesPerTag;                  /**< Internal Use */
    /* Useful to cache, not costless to recalc */
    int                 numBatches;                     /**< Internal Use */ 

#ifdef FASTMORPH
    int                 morphStart;                     /**< Internal Use */
    int                 morphFinish;                    /**< Internal Use */
    int                 morphNum;                       /**< Internal Use */
#endif                          /* FASTMORPH */

#ifndef FASTMORPH
    /* used in envisionClusters */
    rwPS2ClusterQuickInfo clquickinfo[CL_MAXCL];        /**< Internal Use */

    /* we want the data localised */
    rwPS2FieldRec       fieldRec[CL_MAXCL];             /**< Internal Use */ 
#else                           /* !FASTMORPH */
    /* used in envisionClusters */
    rwPS2ClusterQuickInfo clquickinfo[CL_MAXCL + 2];    /**< Internal Use */ 

     /* we want the data localised */
    rwPS2FieldRec       fieldRec[CL_MAXCL + 2];         /**< Internal Use */
#endif                          /* !FASTMORPH */
};

/* Instancing support structs */

typedef struct _rwPS2normalLUTs _rwPS2normalLUTs;
struct _rwPS2normalLUTs
{
    /* 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;                     /**< Internal Use */
    RwUInt8             startIndOffset;                 /**< Internal Use */
    RwUInt8             shiftMask[1];                   /**< Internal Use */
    RwUInt8             shiftMask2[1];                  /**< Internal Use */
    RwUInt8             vertInc[1];                     /**< Internal Use */
};

typedef struct _rwPS2triFanLUTs _rwPS2triFanLUTs;
struct _rwPS2triFanLUTs
{
    /* 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;                     /**< Internal Use */
    RwUInt8             startIndOffset;                 /**< Internal Use */
    RwUInt8             shiftMask[3];                   /**< Internal Use */
    RwUInt8             shiftMask2[3];                  /**< Internal Use */
    RwUInt8             vertInc[3];                     /**< Internal Use */
};

typedef struct _rwPS2polyLineLUTs _rwPS2polyLineLUTs;
struct _rwPS2polyLineLUTs
{
    /* 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;                     /**< Internal Use */
    RwUInt8             startIndOffset;                 /**< Internal Use */
    RwUInt8             shiftMask[2];                   /**< Internal Use */
    RwUInt8             shiftMask2[2];                  /**< Internal Use */
    RwUInt8             vertInc[2];                     /**< Internal Use */
};

typedef struct rwPS2indexLUTs rwPS2indexLUTs;
struct rwPS2indexLUTs
{
    RxVertexIndex       fakeIndices[1];                 /**< Internal Use */
    _rwPS2normalLUTs    normal;                         /**< Internal Use */
    _rwPS2triFanLUTs    triFan;                         /**< Internal Use */
    _rwPS2polyLineLUTs  polyLine;                       /**< Internal Use */
};

/* These exposed so nodePS2Manager can get at them */

typedef struct rwPS2MatInstanceInitData rwPS2MatInstanceInitData;
struct rwPS2MatInstanceInitData
{
    /* We use this as the pipelinenode's initialisation data, used during the
     * pipeline node init function. It is set up by RxPS2MatInstanceGenerateCluster() */
#ifndef FASTMORPH
     /* All the required clusters */
    RxClusterDefinition *clusters[CL_MAXCL];            /**< Internal Use */
#else                           /* !FASTMORPH */
 /* All the required clusters */
    RxClusterDefinition *clusters[CL_MAXCL + 2];        /**< Internal Use */
#endif                          /* !FASTMORPH */
    RwInt32             vu1MaxTSInputSize;              /**< Internal Use */
    RwInt32             vu1MaxTLInputSize;              /**< Internal Use */
    RwInt32             vu1MaxPLInputSize;              /**< Internal Use */
    RwInt32             checkSizeOnVU;                  /**< Internal Use */
    RpMeshHeaderFlags   pipeType;                       /**< Internal Use */
};

typedef struct rwPS2ClusterInstanceInfo rwPS2ClusterInstanceInfo;
struct rwPS2ClusterInstanceInfo
{
    /* a copy of default data */
    RwUInt32            attrib;                         /**< Internal Use */
    /* number of quadwords */
    RwUInt32            stride;                         /**< Internal Use */ 
};

typedef struct rwPS2ResEntryFormat rwPS2ResEntryFormat;
struct rwPS2ResEntryFormat
{
    int                 batchSize;                      /**< Internal Use */
    int                 batchesPerTag;                  /**< Internal Use */
#ifdef FASTMORPH
    int                 morphBatchSize;                 /**< Internal Use */
    int                 morphBatchesPerTag;             /**< Internal Use */
#endif                          /* FASTMORPH */
#ifndef FASTMORPH
    rwPS2FieldRec       fieldRec[CL_MAXCL];             /**< Internal Use */
#else                           /* !FASTMORPH */
    rwPS2FieldRec       fieldRec[CL_MAXCL + 2];         /**< Internal Use */
#endif                          /* !FASTMORPH */
};

typedef struct rwPS2MatInstancePvtData rwPS2MatInstancePvtData;
struct rwPS2MatInstancePvtData
{
    RwUInt32            numBreakOuts;                   /**< Internal Use */

    /* arrays are indexed by CL_xxx defines above */
#ifndef FASTMORPH
    /* clinstanceinfo */
    rwPS2ClusterInstanceInfo clinfo[CL_MAXCL];          /**< Internal Use */ 
    /* cluster indices of only the broken-out clusters */
    RwUInt32            cliIndex[CL_MAXCL];             /**< Internal Use */ 
   /* pointers to broken-out clusters at run-time */
    RxCluster          *clusters[CL_MAXCL];             /**< Internal Use */ 
#else                           /* !FASTMORPH */
    /* clinstanceinfo */
    rwPS2ClusterInstanceInfo clinfo[CL_MAXCL + 2];      /**< Internal Use */ 
    /* cluster indices of only the broken-out clusters */
    RwUInt32            cliIndex[CL_MAXCL + 2];         /**< Internal Use */ 
    /* pointers to broken-out clusters at run-time */
    RxCluster          *clusters[CL_MAXCL + 2];         /**< Internal Use */ 
#endif                          /* !FASTMORPH */

    RpMeshHeaderFlags   pipeType;                       /**< Internal Use */

    RwInt32             vu1MaxTSInputSize;              /**< Internal Use */
    RwInt32             vu1MaxTLInputSize;              /**< Internal Use */
    RwInt32             vu1MaxPLInputSize;              /**< Internal Use */
    RwInt32             checkSizeOnVU;                  /**< Internal Use */
    RwInt32             globalsInUse;                   /**< Internal Use */

    RwUInt8             totallyOpaque;                  /**< Internal Use */
    RwUInt8             numStripes;                     /**< Internal Use */
    RwUInt8             sizeOnVU;                       /**< Internal Use */
    RwUInt8             pad0;                           /**< Internal Use */

    rwPS2ResEntryFormat triStrip;                       /**< Internal Use */
    /* triList data reused for point-list pipes */
    rwPS2ResEntryFormat triList;                        /**< Internal Use */
};

/************************************************************************
 */
#define envisionclustersMacro(_pvtData, _ps2ResHeader)                      \
MACRO_START                                                                 \
{                                                                           \
    RwUInt32 i;                                                             \
    RWASSERT(_pvtData != NULL);                                             \
                                                                            \
    for (i = 0; i < _pvtData->numBreakOuts/*CL_MAXCL*/;i++)                 \
    {                                                                       \
        rwPS2ClusterQuickInfo *quickinfo = &(_ps2ResHeader->clquickinfo[i]);\
        RxCluster *cluster = _pvtData->clusters[i];                         \
        RWASSERT(cluster != NULL);                                          \
        RxClusterSetExternalData(cluster,                                   \
                                 quickinfo->data,                           \
                                 quickinfo->stride,                         \
                                 _ps2ResHeader->numVerts);                  \
        /* Temporary fixup until we get external modifiable */              \
        cluster->flags &= ~rxCLFLAGS_EXTERNAL;                              \
        cluster->flags |=  rxCLFLAGS_EXTERNALMODIFIABLE;                    \
    }                                                                       \
}                                                                           \
MACRO_STOP

/* RWPUBLIC */

/* LEGACY-SUPPORT macro (old name was non-standard) */
#define RxPipelineNodePS2MatInstanceNodeSetVUBufferSizes(_self,      \
                                      _stride, _TSCount, _TLCount)   \
    (RxPipelineNodePS2MatInstanceSetVUBufferSizes(_self, _stride,    \
                                                 _TSCount, _TLCount))

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

extern RxNodeDefinition *
RxNodeDefinitionGetPS2MatInstance(void);

/* instance node's "construction time customisation" api;
   following call is available between time node
   is add()ed to pipeline and time pipeline is unlock()ed */

extern RxPipelineNode *
RxPipelineNodePS2MatInstanceGenerateCluster(RxPipelineNode * self,
                                            RxClusterDefinition * cluster2generate,
                                            RwUInt32 type);


#define RxPipelineNodePS2MatInstanceSetVUBufferSizes(_self,                     \
                                                     _strideOfInputCluster,     \
                                                     _vuTSVertexMaxCount,       \
                                                     _vuTLTriMaxCount)          \
        RxPS2MatInstanceSetVUBufferSizes(_self,                                 \
                                         _strideOfInputCluster,                 \
                                         _vuTSVertexMaxCount,                   \
                                         _vuTLTriMaxCount)  

extern RxPipelineNode *
RxPipelineNodePS2MatInstanceSetVUBufferSizes(RxPipelineNode * self,
                                             RwInt32 strideOfInputCluster,
                                             RwInt32 vuTSVertexMaxCount,
                                             RwInt32 vuTLTriMaxCount);

#define RxPipelineNodePS2MatInstanceSetPointListVUBufferSize(_self,                     \
                                                             _strideOfInputCluster,     \
                                                             _vuPLVertexMaxCount)       \
        RxPS2MatInstanceSetPointListVUBufferSize(_self,                                 \
                                                 _strideOfInputCluster,                 \
                                                 _vuPLVertexMaxCount)

extern RxPipelineNode *
RxPipelineNodePS2MatInstanceSetPointListVUBufferSize(RxPipelineNode * self, 
                                                     RwInt32 strideOfInputCluster,
                                                     RwInt32 vuPLVertexMaxCount);

/* RWPUBLICEND */

/************************************************************************
 */
extern RwBool
_rwPS2MatInstancePipelineNodeInit(RxPipelineNode * self);

extern RwBool
_rwRabinsMatInstanceCode(RxPS2DMASessionRecord *
                         const DMASessionRecord,
                         const RxPS2Mesh * const Mesh,
                         rwPS2MatInstancePvtData * pvtdata);

extern void         reDestroyCallback(RwResEntry * resEntry);

/* RWPUBLIC */

#ifdef    __cplusplus
}
#endif                          /* __cplusplus */

/* RWPUBLICEND */

#endif                          /* NODEPS2MATINSTANCE_H */
