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

#ifndef NODEPS2ALLMAT_H
#define NODEPS2ALLMAT_H

#include <rwcore.h>
#include "skyisms.h"
#include "ps2clusterattribs.h"
#include "nodeps2objallinone.h"
#include "bamesh.h"


/*
#### SYNCHRONISATION
####
#### UP TO DATE WITH VERSION 1.18 OF nodePS2Manager.h
#### UP TO DATE WITH VERSION 1.18 OF nodePS2ObjAllInOne.h
#### UP TO DATE WITH VERSION 1.38 OF nodePS2MatInstance.h
#### UP TO DATE WITH VERSION 1.30 OF nodePS2MatBridge.h
####
#### SYNCHRONISATION
*/


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

/* RWPUBLIC */

#define REDEBUGx
#define DMAALIGN
#define FASTMORPH

#if (!defined(FASTMORPH))
#define FMADD (0)
#else /* (!defined(FASTMORPH)) */
#define FMADD (2)
#endif /* (!defined(FASTMORPH)) */

/* RWPUBLICEND */

#define PS2ALLMATMAGICVALUE (0x34F9)
#define PS2ALLMATPRIVATEDATASIZE (sizeof(rxNodePS2AllMatPvtData))

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

/* rwPS2AllClusterInstanceInfo is stealth public, for the benefit of rxNodePS2AllMatPvtData */
/* RWPUBLIC */

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

/* RWPUBLICEND */
/* rwPS2AllFieldRec is stealth public, for the benefit of rxNodePS2AllMatPvtData and
 * rwPS2AllResEntryHeader and so that people can have non-void pointers to it and can
 * get at objIdenitifer and meshIdentifier. To access these, they should use the macros
 * RWPS2ALLRESENTRYHEADERGETOBJIDENTIFIER and RWPS2ALLRESENTRYHEADERGETMESHIDENTIFIER,
 * they should never need to access members of the struct themselves. */
/* RWPUBLIC */

typedef struct rwPS2AllFieldRec rwPS2AllFieldRec;
struct rwPS2AllFieldRec
{
    int numVerts;                                       /**< Internal Use */
#if (defined(FASTMORPH))
    int morphNumVerts;                                  /**< Internal Use */
#endif /* (defined(FASTMORPH)) */
    int dataoffset;                                     /**< Internal Use */
#if (defined(FASTMORPH))
    int morphDataoffset;                                /**< Internal Use */
#endif /* (defined(FASTMORPH)) */
    short skip;                                         /**< Internal Use */
#if (defined(FASTMORPH))
    short morphSkip;                                    /**< Internal Use */
#endif /* (defined(FASTMORPH)) */
    short reverse;                                      /**< Internal Use */
    unsigned char vuoffset;                             /**< Internal Use */
#if (!defined(FASTMORPH))
    unsigned char pad[3];                               /**< Internal Use */
#else /* (!defined(FASTMORPH)) */
    unsigned char pad[1];                               /**< Internal Use */
#endif /* (!defined(FASTMORPH)) */
/* RWPUBLICEND */
    /* numVerts   - 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).
     * dataOffset - offset from start of data area in ResEntry (in quadwords)
     *              WARNING: If we have less than batchSize verts, the offset
     *              is actually smaller by reverse QWs.
     *              [morphDataoffset is affected by batch size for both opaque
     *              and non-opaque clusters (not the first opaque actually)]
     * skip       - QW-offset from the beginning of one batch of this cluster's
     *              data to the next, including space for other (opaque)
     *              clusters in-between and tags (inc. ref tags pointing into
     *              broken-out arrays). Should be zero for non-opaque clusters.
     *              [fastmorphing will affect batchSize, hence morphSkip]
     * reverse    - this (QW-offset) is used because the last batch is smaller, so
     *              to pack the batch blocks (for all the clusters) 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.
     *              [NOTE: unlike skip, reverse is set up at instance-time, when
     *              it is known whether the object is morphing or not - hence
     *              there's no need for a morphing and a non-morphing version of
     *              the variable. reverse represents whichever is the case]
     * vuoffset   - The QW offset from the start of the vertex in VU memory. So
     *              XYZ is zero, UV is 1, etc... doesn't apply to striped clusters
     *              like the FASTMORPH ones (how *do* their locations get determined?).
     */
/* RWPUBLIC */
};

/* RWPUBLICEND */
/* rwPS2AllResEntryFormat is stealth public, for the benefit of rxNodePS2AllPvtData */
/* RWPUBLIC */
typedef struct rwPS2AllResEntryFormat rwPS2AllResEntryFormat;
struct rwPS2AllResEntryFormat
{
    int batchSize;                                      /**< Internal Use */
    int batchesPerTag;                                  /**< Internal Use */
#if (defined(FASTMORPH))
    int morphBatchSize;                                 /**< Internal Use */
    int morphBatchesPerTag;                             /**< Internal Use */
#endif /* (defined(FASTMORPH)) */
    rwPS2AllFieldRec fieldRec[CL_MAXCL + FMADD];        /**< Internal Use */
};

/**
 * \ingroup rpworldp2sky2
 * \ref RxPipelineNodePS2AllMatCallBackType
 * PS2AllMat.csl callback types (see \ref RxPipelineNodePS2AllMatSetCallBack
 * and \ref RxPipelineNodePS2AllSetCallBack), in order of execution within
 * the node.
 */
enum RxPipelineNodePS2AllMatCallBackType
{
    rxPS2ALLMATCALLBACKNACALLBACK          = 0,
    rxPS2ALLMATCALLBACKIM3DPREMESH         = 1, /**< Temporary, for internal use */
    rxPS2ALLMATCALLBACKMESHINSTANCETEST    = 2, /**< Performs per-mesh reinstance tests
                                                 *   (see \ref RxPipelineNodePS2AllMatMeshInstanceTestCallBack) */
    rxPS2ALLMATCALLBACKRESENTRYALLOC       = 3, /**< Allocates memory for instance data
                                                 *   (see \ref RxPipelineNodePS2AllMatResEntryAllocCallBack) */
    rxPS2ALLMATCALLBACKINSTANCE            = 4, /**< Instances a mesh and performs static data uploads
                                                 *   (see \ref RxPipelineNodePS2AllMatInstanceCallBack) */
    rxPS2ALLMATCALLBACKBRIDGE              = 5, /**< Selects VU code and sets up material properties
                                                 *   (see \ref RxPipelineNodePS2AllMatBridgeCallBack) */
    rxPS2ALLMATCALLBACKIM3DPOSTMESH        = 6, /**< Temporary, for internal use */
    rxPS2ALLMATCALLBACKIM3DMESHSPLIT       = 7, /**< Temporary, for internal use */
/* RWPUBLICEND */
/* TODO[3][5]: POSTMESHCB IS JUST USED BY IM3D AND METRICS - TOO HEAVYWEIGHT?
 *   HMM, THE IM3D 'FREE INSTANCE DATA' PACKET MUST GO INTO THE CHAIN AFTER THE
 *   BRIDGE CODE HAS INSERTED THE UPLOAD PACKET */
/* RWPUBLIC */
    rxPS2ALLMATCALLBACKPOSTMESH            = 8, /**< Performs per-mesh post-render tasks
                                                 *   (see \ref RxPipelineNodePS2AllMatPostMeshCallBack) */

    rxPS2ALLMATCALLBACKFORCEENUMSIZEINT = RWFORCEENUMSIZEINT
};
typedef enum RxPipelineNodePS2AllMatCallBackType RxPipelineNodePS2AllMatCallBackType;

/* RWPUBLICEND */
/* Resolves a dependency on nodeps2all.h */
/* RWPUBLIC */
typedef struct RxPS2AllPipeData RxPS2AllPipeData;

/* RWPUBLICEND */
/* [see _rwIm3DPS2AllIm3DPreMeshCallBack in ps2allim3d.c]
 * This takes a look at the incoming primitive and works out (conservatively)
 * how big the DMA data will end up being. If it's too big (the circ allocator
 * currently for Im3D can only alloc blocks 64k in size), it works out how many
 * indices it can do in one go, so that it can do N batches of that size. It
 * stores info needed for this in some static globals so that later callbacks can
 * get at them. It also fills in the meshCache and mesh with plausible values. */
/* RWPUBLIC */
typedef RwBool (*RxPipelineNodePS2AllMatIm3DPreMeshCallBack)
    (RxPS2AllPipeData *ps2AllPipeData);

/* RWPUBLICEND */
/* Docs for this is in nodeps2all.c - the definition's duplicated
 * there, so don't forget to update that if this changes! */
/* RWPUBLIC */
typedef RwBool (*RxPipelineNodePS2AllMatMeshInstanceTestCallBack)
    (RxPS2AllPipeData *ps2AllPipeData);

/* RWPUBLICEND */
/* Docs for this is in nodeps2all.c - the definition's duplicated
 * there, so don't forget to update that if this changes! */
/* RWPUBLIC */
typedef RwResEntry *(*RxPipelineNodePS2AllMatResEntryAllocCallBack)
    (RxPS2AllPipeData *ps2AllPipeData,
     RwResEntry **repEntry,
     RwUInt32 size,
     RwResEntryDestroyNotify destroyNotify);

/* RWPUBLICEND */
/* Docs for this is in nodeps2all.c - the definition's duplicated
 * there, so don't forget to update that if this changes! */
/* TODO[4]: INSTANCE CALLBACK WILL TAKE NEW INSTANCING DATA BLOCK */
/* RWPUBLIC */
typedef RwBool (*RxPipelineNodePS2AllMatInstanceCallBack)
    (RxPS2AllPipeData *ps2AllPipeData, void **clusterData, RwUInt32 numClusters);

/* RWPUBLICEND */
/* Docs for this is in nodeps2all.c - the definition's duplicated
 * there, so don't forget to update that if this changes! */
/* RWPUBLIC */
typedef RwBool (*RxPipelineNodePS2AllMatBridgeCallBack)
    (RxPS2AllPipeData *ps2AllPipeData);

/* RWPUBLICEND */
/* Performs the fiddly process of updating the mesh, meshHeader
 * and stash at the end of each block of indices for large (split)
 * Im3D primitives. Also makes sure the instance data gets built
 * for the next block and puts the "free" command in the DMA
 * chain for the just-finished block's instance data. */
/* RWPUBLIC */
typedef RwBool (*RxPipelineNodePS2AllMatIm3DPostMeshCallBack)
    (RxPS2AllPipeData *ps2AllPipeData);

/* RWPUBLICEND */
/* Returns TRUE if there are more sub-blocks of indices yet to
 * do in large (split) Im3D primitives. */
/* RWPUBLIC */
typedef RwBool (*RxPipelineNodePS2AllMatIm3DMeshSplitCallBack)
    (RxPS2AllPipeData *ps2AllPipeData);

/* RWPUBLICEND */
/* Returns TRUE if there are more sub-blocks of indices yet to
 * do in large (split) Im3D primitives. */
/* RWPUBLIC */
typedef RwBool (*RxPipelineNodePS2AllMatPostMeshCallBack)
    (RxPS2AllPipeData *ps2AllPipeData);

/* RWPUBLICEND */
/* rxNodePS2AllMatPvtData is used in a matBridge helper macro, but it's undocumented/stealth */
/* RWPUBLIC */
typedef struct rxNodePS2AllMatPvtData rxNodePS2AllMatPvtData;
struct rxNodePS2AllMatPvtData
{
    /* Callbacks */
    RxPipelineNodePS2AllMatIm3DPreMeshCallBack im3DPreMeshCB;  /**< Internal Use */
    RxPipelineNodePS2AllMatMeshInstanceTestCallBack meshInstanceTestCB;  /**< Internal Use */
    RxPipelineNodePS2AllMatResEntryAllocCallBack resEntryAllocCB;  /**< Internal Use */
    RxPipelineNodePS2AllMatInstanceCallBack instanceCB;  /**< Internal Use */
    RxPipelineNodePS2AllMatBridgeCallBack bridgeCB;     /**< Internal Use */
    RxPipelineNodePS2AllMatIm3DPostMeshCallBack im3DPostMeshCB;  /**< Internal Use */
    RxPipelineNodePS2AllMatIm3DMeshSplitCallBack im3DMeshSplitCB;  /**< Internal Use */
    RxPipelineNodePS2AllMatPostMeshCallBack postMeshCB;  /**< Internal Use */

    /* MatBridge sub-section [first for memory coherence,
     * instance stuff is not fast-path] */
    int      vifOffset;                                 /**< Internal Use */
    void   **vu1CodeArray;                              /**< Internal Use */
    RwUInt32 codeArrayLength;                           /**< Internal Use */

    /* MatInstance sub-section
     * [Arrays are indexed by CL_xxx defines in PS2ClusterAttribs.h] */

    rwPS2AllClusterInstanceInfo clinfo[CL_MAXCL + FMADD];  /**< Internal Use */
    RwUInt32 cliIndex[CL_MAXCL + FMADD];                /**< Internal Use */

    RpMeshHeaderFlags pipeType;                         /**< Internal Use */

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

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

    rwPS2AllResEntryFormat triStrip;                    /**< Internal Use */
    rwPS2AllResEntryFormat triList;                     /**< Internal Use */

    RwUInt32 magicValue;                                /**< Internal Use */

/* RWPUBLICEND */
    /* Just about everything can be different per-mesh (you should
     * be able to vary all matinstance/matbridge data and it should
     * 'just work').
     *
     * MatInstance sub-section
     * [Arrays are indexed by CL_xxx defines in PS2ClusterAttribs.h]
     *
     * clinfo          - attribs (in REQUIRED) and stride for ALL clusters
     * cliIndex        - Cluster indices of only the broken-out clusters
     * pipeType        - pointlist/not basically
 * TODO[3]: CAN PIPETYPE REALLY VARY PER-MESH? RpMeshHeaders ARE PER OBJECT,
 *   YES, BUT THIS RELATES TO THE VU CODE AND BUFFER SIZE API FUNCS SO IT
 *   SHOULD BE PER-MESH. THE ACTUAL MESHHEADER FLAGS WILL HAVE TO BE
 *   CIRCUMVENTED BY THE USER TO HAVE TRULY DIFFERENT MESH TYPES. BUT I
 *   THINK IT'S BETTER TO BE FLEXIBLE, NOT A POTENTIAL PROBLEM IN THIS CASE.
     *
     * vu1MaxTSInputSize - Max number of vertices in a triStrip batch times the QW
     *                    stride of the vertex in VU memory (implicitly including
     *                    FASTMORPH extras, not that the user knows it when they
     *                    set it). Used in calc'ing batchSize (where it may be
     *                    rounded down).
     * vu1MaxTLInputSize - Max num of verts in a triList batch times stride as above.
     * vu1MaxPLInputSize - Max num of verts in a pointList batch times stride as above.
 * TODO[9]: OBSOLETE? USEFUL AS A REMINDER FOR LATER? ->     * globalsInUse
     *
     * totallyOpaque     - If (numStripes == 0), this is TRUE
     * numStripes        - Number of non-opaque clusters (in 'stripes' parallel to the opaque dma chain)
     * sizeOnVU          - Stride in QW of vertices in VU memory - DOES NOT include FASTMORPH extras.
     *
     * triStrip          - batchSize, batchesPerTag (and FASTMORPH versions) for tristrips
     * triList           - batchSize, batchesPerTag (and FASTMORPH versions) for trilists,
     *                    also reused for point-list pipes.
 * TODO[4]: triStrip/triList WILL BE REPLACED WITH PRIMTYPE-SPECIFIC AND
 *   PRIM-TYPE-INDEPENDENT SECTIONS OF THE INSTANCE DATA BLOCK AND VERTEX
 *   FORMAT DESCRIPTOR. ANY GIVEN PIPE SHOULD BE ENABLEABLE FOR ANY OR
 *   ALL OF THE POSSIBLE PRIMTYPES.
     *
     *
     * MatBridge sub-section
     *
     * vifOffset         - The address of the second data input buffer. The first is at
     *                    address zero, with static data starting at the top of VU memory.
     * vu1CodeArray      - duh (where's length specified now? Just assume indices never exceed?)
     *
     * magicValue        - Make sure the pipe/node are the right types.
 * TODO[5]: OMIT magicValue IN DEBUG?
     */

/* TODO[4]: SHOULD PUT THE CL_CODE VALUE INTO ATTRIBS ALSO. THESE ARE DEFAULT,
 *   RW-UNDERSTOOD CLUSTER TYPES (SOURCED/INSTANCEABLE FROM RW-UNDERSTOOD
 *   OBJECTS LIKE ATOMICS). THIS WILL BE USEFUL (FOR CLUSTER IDENTIFICATION)
 *   IN THE NEW FLEXIBLE SCHEME WHERE YOU WILL BE ABLE TO HAVE ANY NUMBER OF
 *   ANY CLUSTERS IN ANY ORDER (DATA-DRIVEN, NO HARD-CODED MOLLY-CODDLING) */

/* RWPUBLIC */
};
/* RWPUBLICEND */

typedef struct rxNodePS2AllMatInitData rxNodePS2AllMatInitData;
struct rxNodePS2AllMatInitData
{
    /* We use this as the pipelinenode's initialisation data, used during the
     * pipeline node init function. It is set up by RxPS2MatInstanceGenerateCluster() */
    RxClusterDefinition *clusters[CL_MAXCL + FMADD]; /**< All the required clusters */
    RwInt32 vu1MaxTSInputSize; /**< Max number of vertices in a triStrip batch times the QW
                                * stride of the vertex in VU memory (implicitly including
                                * FASTMORPH extras, not that the user knows it when they
                                * set it). Used in calc'ing batchSize (where it may be
                                * rounded down) */
    RwInt32 vu1MaxTLInputSize; /**< Max num of verts in a triList batch times stride as above */
    RwInt32 vu1MaxPLInputSize; /**< Max num of verts in a pointList batch times stride as above */
    RwInt32 sizeOnVU; /**< Stride in QW of vertices in VU memory - NOT including FASTMORPH extras */
    /**< Ok, so we set pipeType implicitly through RxPipelineNodePS2AllMatSetVUBufferSizes,
     * but it should probably not change between materials... not that we can
     * test it and it might work but... well, I'm not guaranteeing it :) */
/* TODO[3]: SHOULD HAVE DEFINITES IN THE ABOVE COMMENT */
    RpMeshHeaderFlags    pipeType;
};


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

/* RWPUBLIC */

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

extern RxNodeDefinition *RxNodeDefinitionGetPS2AllMat(void);


/* Pre-unlock API functions (set up permanent properties
 * of the pipeline at construction time) */

/* RWPUBLICEND */
/* TODO[4]: THE VERTEX-FORMAT-DESCRIPTOR-CREATION
 *  API WILL REPLACE THESE FUNCTIONS:
 *   RxPipelineNodePS2AllMatGenerateCluster,
 *   RxPipelineNodePS2AllMatSetVUBufferSizes,
 *   RxPipelineNodePS2AllMatSetPointListVUBufferSize */
/* RWPUBLIC */

extern RxPipelineNode *
RxPipelineNodePS2AllMatGenerateCluster(RxPipelineNode      *self,
                                       RxClusterDefinition *cluster2generate,
                                       RwUInt32             type);
extern RxPipelineNode *
RxPipelineNodePS2AllMatSetVUBufferSizes(RxPipelineNode *self,
                                        RwInt32 strideOfInputVertex,
                                        RwInt32 vuTSVertexMaxCount,
                                        RwInt32 vuTLTriMaxCount);
extern RxPipelineNode *
RxPipelineNodePS2AllMatSetPointListVUBufferSize(RxPipelineNode *self,
                                                RwInt32 strideOfInputVertex,
                                                RwInt32 vuPLVertexMaxCount);


/* Post-unlock API functions (these may be called more than
 * once, to change pipe behaviour between pipe executions) */

/* Find out the batch size that's actually used (not what you asked for) */
/* RWPUBLICEND */
/* TODO[6]: I THINK YOU NEED TO BE ABLE TO SPECIFY MAX SIZE *AND* GRANULARITY,
 *    E.G (64, 16) FOR QUAD BEZ PATCHES, (60, 10) FOR TRI BEZ PATCHES, ETC */
/* RWPUBLIC */
extern RwInt32
RxPipelineNodePS2AllMatGetVUBatchSize( RxPipelineNode *self,
                                       RpMeshHeaderFlags flags);
extern RxPipelineNode *
RxPipelineNodePS2AllMatSetVIFOffset(   RxPipelineNode *self,
                                       int vifOffset);
extern RxPipelineNode *
RxPipelineNodePS2AllMatSetVU1CodeArray(RxPipelineNode *self,
                                       void **vu1CodeArray,
                                       RwUInt32 length);
extern const void **
RxPipelineNodePS2AllMatGetVU1CodeArray(RxPipelineNode * self,
                                       RwUInt32 *length);
extern RxPipelineNode *
RxPipelineNodePS2AllMatSetCallBack(    RxPipelineNode *self,
                                       RxPipelineNodePS2AllMatCallBackType type,
                                       void *func);

#ifdef    __cplusplus
}
#endif /* __cplusplus */


/* RWPUBLICEND */


#endif /* NODEPS2ALLMAT_H */

