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

#ifndef NODEPS2ALL_H
#define NODEPS2ALL_H

#include <rwcore.h>
#include "bamesh.h"
#include "baclump.h"
#include "baworld.h"

#include "wrldpipe.h"
#include "skyisms.h"
#include "ps2clusterattribs.h"
#include "nodeps2objallinone.h"

#include "ps2allmat.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
 */


#if (defined(RWMETRICS))
#define __RWUNUSEDUNLESSMETRICS__ \
       /* No op */
#endif /* (defined(RWMETRICS)) */

#if (!(defined(__RWUNUSEDUNLESSMETRICS__)))
#define __RWUNUSEDUNLESSMETRICS__  \
        __RWUNUSED__
#endif /* (!(defined(__RWUNUSEDUNLESSMETRICS__))) */

/* RWPUBLIC */

#define REDEBUGx
#define DMAALIGN
#define VUCONTINUEx
#define FASTMORPH
#define OVERRIDELIGHTx
#define SUBSISTLIGHTx

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

/* RWPUBLICEND */

/* FASTMORPH happens transparently, there are no API additions - if an object
 * has morphtargets, an extra extra set of positions (cluster CL_MAXCL) and
 * (if the object has normals) normals (cluster CL_MAXCL+1) are instanced, the
 * second set of values coming from the end morphtarget. CPU-side the same
 * instancing structure is used - so batch size is shrunk to hold all the
 * clusters. VU-side, the second positions/normals are not interlaced, i.e you
 * end up with your normal set of vertices, then array of POS2s then an array
 * of NORMAL2s. Thus non-morphing code wouldn't break, it'd just think batch
 * sizes were unusually small and think something narrow-minded about it.
 *
 * A blend value is uploaded in the fourth float of the surfaceProperties QW.
 * If this is zero, morphing is skipped on VU1 - this applies to the case
 * where morphing isn't on for this object AND where the current keyframe is
 * an integer :o)
 *
 * When keyframe boundaries are crossed, reinstancing occurs, which costs
 * slightly more than normal reinstancing due to the extra clusters (but
 * probably no more than old morphing instancing given interpolation was done
 * CPU-side). Hence people to arrange their animations so keyframe
 * boundary-passing happens on different frames for different objects, so
 * you get minimal reinstances on any given frame - it's still work but it's
 * less and will hopefully either get eaten by the four-frame-lag 'averaging'
 * or will fit in your slack time per frame. */

/* RWPUBLIC */

/* For getting the ps2AllResHeader from a RwResEntry */
#define RWPS2ALLRESENTRYHEADERFROMRESENTRY(_resEntry) \
    ((rwPS2AllResEntryHeader *)(_resEntry + 1))

/* For getting the Object Identifier from a ps2AllResHeader */
#define RWPS2ALLRESENTRYHEADERGETOBJIDENTIFIER(_ps2AllResEntry) \
    ((_ps2AllResEntry)->objIdentifier)

/* For getting the Mesh Identifier from a ps2AllResHeader */
#define RWPS2ALLRESENTRYHEADERGETMESHIDENTIFIER(_ps2AllResEntry) \
    ((_ps2AllResEntry)->meshIdentifier)

/* We expand trifans and polylines during instancing into trilists and linelists
 * respectively and RPMESHPS2ALLCALCNUMVERTS calculates the 'numverts' in the
 * DMA chain and on VU1 (NOT counting tristrip vertex duplication). Note that
 * triFans will be instanced into triLists and polyLines into lineLists. */
/* RWPUBLICEND */
/* Note that numVerts affects the number of batches and fieldRec.reverse
 * but not batchsize nor fieldRec.skip. Our DMA formats are fixed and
 * so is this calculation. */ 
/* RWPUBLIC */
#define RPMESHPS2ALLCALCNUMVERTS(_ps2AllPipeData, _numVerts)                \
MACRO_START                                                                 \
{                                                                           \
    RxPS2AllPipeData *_psald = (_ps2AllPipeData);                           \
    RwUInt32 *_nmvt = (_numVerts);                                          \
                                                                            \
   *_nmvt = _psald->mesh->numIndices;                                       \
    if (_psald->meshHeader->flags & rpMESHHEADERTRIFAN)                     \
    {                                                                       \
       *_nmvt = ((*_nmvt) - 2)*3;                                           \
    }                                                                       \
    else if (_psald->meshHeader->flags & rpMESHHEADERPOLYLINE)              \
    {                                                                       \
       *_nmvt = ((*_nmvt) - 1)*2;                                           \
    }                                                                       \
}                                                                           \
MACRO_STOP

/* RWPUBLICEND */

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

/* TODO[6]: FOLD RxInstanceFlags WITH _rpGeometryInstanceFlags? I THINK (SEE
 *   rpGeometryInstance()) THAT WE CAN REPLACE rpGEOMETRYPERSISTENT WITH
 *  rxINSTANCEDONTINSTANCE AND rpGEOMETRYINSTANCE WITH rxINSTANCEFULLINSTANCE.
 *  (SHOULD WE BOTHER? _rpGeometryInstanceFlags ARE NON-API, THEY CAN BE
 *  MERGED WITH RxInstanceFlags LATER. BE CAREFUL _rpGeometryInstanceFlags WAS
 *  NEVER SERIALISED - IF IT WAS, THEN THE *VALUE* OF THE ENUMS DOES MATTER)

/* RWPUBLIC */

/**
 * \ingroup rpworldp2sky2
 * \ref RxInstanceFlags
 * These flags control the decision of whether to re-instance meshes
 * during the execution of PS2All.csl and PS2AllMat.csl. See
 * \ref RxPipelineNodePS2AllSetCallBack for an overview of this process.
 */
enum RxInstanceFlags
{
    rxINSTANCENAINSTANCEFLAG    = 0x0000,
    rxINSTANCEDONTINSTANCE      = 0x0001, /**< Signifies that no per-mesh reinstancing or testing
                                           *   should occur */
/* RWPUBLICEND */
    /* PERSISTENT geometry is flagged as rxINSTANCEDONTINSTANCE */
/* RWPUBLIC */
    rxINSTANCEINPLACEINSTANCE   = 0x0002, /**< Some or all clusters (depending on cluster reinstance
                                           *   flags below) are to be re-instanced 'in-place', in the
                                           *   existing resEntry (see \ref RxPipelineNodePS2AllSetCallBack
                                           *   for details of the consequences) (overrides DONTINSTANCE) */
    rxINSTANCECONGRUENTINSTANCE   = 0x0004, /**< The resEntry is thrown away but its size/layout is
                                           *   cached and reused for the new ResEntry (overrides
                                           *   DONTINSTANCE and INPLACEINSTANCE) */
    rxINSTANCEFULLINSTANCE      = 0x0008, /**< Size/layout have changed, so we can't cache them
                                           *   (overrides DONTINSTANCE, INPLACEINSTANCE and
                                           *   CONGRUENTINSTANCE) */
    rxINSTANCETYPEMASK          = (rxINSTANCEINPLACEINSTANCE   |
                                   rxINSTANCECONGRUENTINSTANCE |
                                   rxINSTANCEFULLINSTANCE),

/* RWPUBLICEND */
    /* rxINSTANCEINSTANCEPOLYGONS  = 0x00010, */
/* RWPUBLIC */
    rxINSTANCEXYZ               = 0x00010,/**< Vertex positions should be re-instanced in-place */
    rxINSTANCENORMAL            = 0x00020,/**< Vertex normals should be re-instanced in-place */
    rxINSTANCERGBA              = 0x00040,/**< Vertex (prelight) colors should be re-instanced in-place */
    rxINSTANCEUV                = 0x00080,/**< First vertex texture coordinates should be re-instanced in-place */
    rxINSTANCEUV1               = 0x00080,/**< Alternate version of rxINSTANCEUV, different naming convention */
    rxINSTANCEUV2               = 0x00100,/**< Second vertex texture coordinates should be re-instanced in-place */
    rxINSTANCEUV3               = 0x00200,/**< Third vertex texture coordinates should be re-instanced in-place */
    rxINSTANCEUV4               = 0x00400,/**< Fourth vertex texture coordinates should be re-instanced in-place */
    rxINSTANCEUV5               = 0x00800,/**< Fifth vertex texture coordinates should be re-instanced in-place */
    rxINSTANCEUV6               = 0x01000,/**< Sixth vertex texture coordinates should be re-instanced in-place */
    rxINSTANCEUV7               = 0x02000,/**< Seventh vertex texture coordinates should be re-instanced in-place */
    rxINSTANCEUV8               = 0x04000,/**< Eighth vertex texture coordinates should be re-instanced in-place */
    rxINSTANCEUSER1             = 0x08000,/**< User cluster 1 should be re-instanced in-place */
    rxINSTANCEUSER2             = 0x10000,/**< User cluster 2 should be re-instanced in-place */
    rxINSTANCEUSER3             = 0x20000,/**< User cluster 3 should be re-instanced in-place */
    rxINSTANCEUSER4             = 0x40000,/**< User cluster 4 should be re-instanced in-place */
    rxINSTANCEALL               = (rxINSTANCEXYZ    |
                                   rxINSTANCENORMAL |
                                   rxINSTANCERGBA   |
                                   rxINSTANCEUV1    |
                                   rxINSTANCEUV2    |
                                   rxINSTANCEUV3    |
                                   rxINSTANCEUV4    |
                                   rxINSTANCEUV5    |
                                   rxINSTANCEUV6    |
                                   rxINSTANCEUV7    |
                                   rxINSTANCEUV8    |
                                   rxINSTANCEUSER1  |
                                   rxINSTANCEUSER2  |
                                   rxINSTANCEUSER3  |
                                   rxINSTANCEUSER4),
    rxINSTANCEMASK              = (rxINSTANCEDONTINSTANCE |
                                   rxINSTANCETYPEMASK     |
                                   rxINSTANCEALL),

    rxINSTANCEFORCEENUMSIZEINT  = RWFORCEENUMSIZEINT
};
typedef enum RxInstanceFlags RxInstanceFlags;

/**
 * \ingroup rpworldp2sky2
 * \struct RxPS2AllPipeData
 * Describes information used within the RxPS2All.csl node
 * and passed to callbacks registered therewith.
 */
/* RWPUBLICEND */
/* TODO[5]: EMBED THIS IN THE NODE'S PRIVATE DATA TO GIVE AS COHERENT MEMORY
 *   ACCESS AS HUMANLY POSSIBLE? (NOT THREADSAFE, BUT WHAT IS?? (SIMILAR TACK
 *  FOR PIPELINE 'PLUGIN' DATA?) */
/* typedef struct RxPS2AllPipeData RxPS2AllPipeData; - declared in ps2allmat.h now, ugh */
/* RWPUBLIC */
struct RxPS2AllPipeData
{
    /* Per-object stuff */
    struct rxNodePS2AllPvtData
        *objPvtData;                /**< A pointer to the private data of the currently
                                     *   executing PS2All (object-pipeline) node */
    struct rxNodePS2AllMatPvtData
        *matPvtData;                /**< A pointer to the private data of the current
                                     *   material pipeline's PS2AllMat node */
    void *sourceObject;             /**< A pointer to the source object */
    RpMeshHeader *meshHeader;       /**< A pointer to the source object's \ref RpMeshHeader */
    RwMeshCache *meshCache;         /**< A pointer to the \ref RwMeshCache associated
                                     *   with meshHeader */
    RxInstanceFlags objInstance;    /**< Set by the ObjectSetup CB, says whether to instance
                                     *   the object and if so whether to do in-place, congruent
                                     *   or full reinstancing (and if in-place which clusters
                                     *   to reinstance) */
    RwUInt32 objIdentifier;         /**< Per-object, object-type-specific data that gets
                                     *   written into the PS2ResHeader such that state changes
                                     *   since creation of the instance data can be detected by
                                     *   object-specific code */
    RwReal spExtra;                 /**< An extra RwReal that can be uploaded in the same
                                     *   quadword as the surface properties. It is initialized
                                     *   by default to zero and persists between meshes. */
#if (defined(FASTMORPH))
    RwInt32 numMorphTargets;        /**< Internal Use */
    RwUInt32 fastMorphing;          /**< Internal Use */
/* RWPUBLICEND */
    /* TEMPORARY - till the final FASTMORPH is implemented as a non-specially-handled plugin
     * numMorphTargets: Handy temporary variable
     * fastMorphing: Used in zillions of tests in matInstance code,
     *               it's: 0 (if numMorphTargets == 1) or 2 (numMorphTargets > 1) */
/* RWPUBLIC */
#endif /* (defined(FASTMORPH)) */
/* RWPUBLICEND */
/* TODO[6]: FOR SAFETY OF transType BEING 8-BIT, PUT THIS IN THE NODE:
 *    "RWASSERT(rxSKYTRANSTYPEMASK < 255);"
/* RWPUBLIC */
    RwUInt8 transType;              /**< RwUInt8 flags, specifying the type of transform to use
                                     *   (ortho/persp, fog/not, etc - see \ref RxSkyTransTypeFlags) */
/* RWPUBLICEND */
/* TODO[6]: NEED AN ENUM FOR primType, PUT WITH RxSkyTransTypeFlags
 *   AND PUT AN "ASSERT(MASK < 255)" TOO */
/* RWPUBLIC */
    RwUInt8 primType;               /**< RwUInt8 code for the primitive type that will
                                     *   be submitted by VU1 to the GS (see GS manual ~p113) */
    RwUInt8 matModulate;            /**< RwUInt8 specifying whether or not to modulate
                                     *   the object material colours (0 means FALSE) */

    /* Following change per mesh */
    RwUInt8 vu1CodeIndex;           /**< RwUInt8 index into VU code array, */
                                    /*   specifying the transform to use */
    const RpMesh *mesh;             /**< A pointer to the source \ref RpMesh */
/* RWPUBLICEND */
    /* "ps2AllPipeData->meshNum = n;" from nodePS2Manager.c is omitted
     * because we have the meshHeader under PS2All, and can do:
     * "if (mesh == meshHeader->firstMesh)",
     * instead of: "if (meshNum == 0)" */
/* RWPUBLIC */
    RwResEntry **cacheEntryRef;     /**< A pointer to a pointer to a \ref RwResEntry
                                     *   holding the instance data for the mesh */
    RxInstanceFlags meshInstance;   /**< Optionally set by the MeshReInstanceTest CB, overrides
                                     *   objInstance in saying whether to instance the mesh and if
                                     *   so whether to do in-place, congruent or full reinstancing
                                     *   (and if in-place which clusters to reinstance) */
    RwUInt32 meshIdentifier;        /**< Per-mesh data that gets
                                     *   written into the PS2ResHeader such that state changes
                                     *   since creation of the instance data can be detected by
                                     *   object-specific code. */ 
    RwSurfaceProperties *surfProps; /**< A pointer to the current material surface properties. If
                                     *   left NULL, default {1, 1, 1} surface properties will be used. */
    RwTexture *texture;             /**< A pointer to a RwTexture for the current mesh */
    RwRGBA matCol;                  /**< RwRGBA material color value. Initialised by default to
                                     *   {255, 255, 255, 255} */
/* RWPUBLICEND */
/* TODO[5]: SEVERAL BOOLS ABOVE CAN BE PACKED INTO FLAGS, THE INTENTION BEING
 *   TO FIT RxPS2AllPipeData INTO 64 BYTES (ONE CACHE LINE) AND MAKE IT STATIC
 *  IF NECESSARY TO ALIGN IT THERE.
 *   objInstance/meshInstance SHOULD BE UINT8s (W/ PAD TO ALIGN SO IT'S CLEAR
 *  WE'VE ROOM LEFT) PUT THIS IN THE NODE: "RWASSERT(rxINSTANCEMASK <= 255);"
 *   numMorphTargets AND fastMorphing COULD BE SHRUNK! WHY INT32 AND UINT32?
 *   PUT PER-OBJECT MEMBERS AT THE END, COS THEY'RE ACCESSED LESS OFTEN */
/* RWPUBLIC */
};

/* RWPUBLICEND */



/* rwPS2AllPrimTypeLUT is stealth public, for the benefit of rxNodePS2AllPvtData */
/* RWPUBLIC */
typedef struct rwPS2AllPrimTypeLUT rwPS2AllPrimTypeLUT;
struct rwPS2AllPrimTypeLUT
{
    RwUInt8 vertToIndRatio[rwPRIMTYPEOR];               /**< Internal Use */
    RwUInt8 vertToIndOffset[rwPRIMTYPEOR];              /**< Internal Use */
};

/* RWPUBLICEND */
/* rwPS2AllClusterQuickInfo is stealth public, for the benefit of rwPS2AllResEntryHeader */
/* RWPUBLIC */

typedef struct rwPS2AllClusterQuickInfo rwPS2AllClusterQuickInfo;
struct rwPS2AllClusterQuickInfo
{
    u_long128           *data;                          /**< Internal Use */
    RwUInt32             stride;                        /**< Internal Use */
/* RWPUBLICEND */
    /* data   - a pointer to this (broken-out) cluster's instance data
     * stride - in bytes for this (broken-out) cluster */
/* RWPUBLIC */
};


/**
 * \ingroup rpworldp2sky2
 * \ref RxPipelineNodePS2AllCallBackType
 * PS2All.csl callback types (see \ref RxPipelineNodePS2AllSetCallBack
 * and \ref RxPipelineNodePS2AllMatSetCallBack), in order of execution within
 * the node.
 */
enum RxPipelineNodePS2AllCallBackType
{
    rxPS2ALLCALLBACKNACALLBACK          = 0,
    rxPS2ALLCALLBACKOBJECTSETUP         = 1, /**< Performs per-object setup
                                              *   (see \ref RxPipelineNodePS2AllObjectSetupCallBack) */
    rxPS2ALLCALLBACKOBJECTFINALIZE      = 2, /**< Performs per-object post-render tasks
                                              *   (see \ref RxPipelineNodePS2AllObjectFinalizeCallBack) */

    rxPS2ALLCALLBACKFORCEENUMSIZEINT = RWFORCEENUMSIZEINT
};
typedef enum RxPipelineNodePS2AllCallBackType RxPipelineNodePS2AllCallBackType;

/* 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 (*RxPipelineNodePS2AllObjectSetupCallBack)
    (RxPS2AllPipeData *ps2AllPipeData,
     RwMatrix **transform,
     RxWorldApplyLightFunc lightingFunc);

/* 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 (*RxPipelineNodePS2AllObjectFinalizeCallBack)
    (RxPS2AllPipeData *ps2AllPipeData);

/* RWPUBLICEND */
/* rxNodePS2AllPvtData is public only so we can get at the genericVU1Index
 * from default pipe setup code. Other people shouldn't access this struct */
/* RWPUBLIC */
typedef struct rxNodePS2AllPvtData rxNodePS2AllPvtData;
struct rxNodePS2AllPvtData
{
    rwPS2AllPrimTypeLUT              primLUT;           /**< Internal Use */

    /* Callbacks */
    RxPipelineNodePS2AllObjectSetupCallBack objectSetupCB; /**< Internal Use */
    RxPipelineNodePS2AllObjectFinalizeCallBack objectFinalizeCB; /**< Internal Use */

    /* ObjAllInOne sub-section */
    RxPipeline *groupPipe;                              /**< Internal Use */
    
/* RWPUBLICEND */

    /* primLUT         - Used in Im3D only currently I think... 
     *
     * objectSetupCB, objectFinalizeCB - see callback docs
     *
     * ObjAllInOne sub-section
     *
     * groupPipe       - If this is non-NULL then ALL meshes are sent to this
     *                   material pipeline. (replaces the 'groupMeshes' bool)
     */

/* TODO[5]: primLUT IS USED IN IM3D ONLY ATM -> MOVE TO THE END OF THE STRUCT
 *    [USED TO SKIP ALONG THE SOURCE DATA WHEN SPLITTING THE PRIMITIVE] */

/* RWPUBLIC */
};

/* RWPUBLICEND */
/* rwPS2AllResEntryHeader is public so RWPS2ALLRESENTRYHEADERGETOBJIDENTIFIER
 * and co can be used in user callbacks as macros not funcs. */
/* RWPUBLIC */
typedef struct rwPS2AllResEntryHeader rwPS2AllResEntryHeader;
struct rwPS2AllResEntryHeader
{
    RwInt32 refCnt;                                     /**< Internal Use */
    RwInt32 clrCnt;                                     /**< Internal Use */

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

    RwUInt32 numVerts;                                  /**< Internal Use */

    RwUInt32 objIdentifier;                             /**< Internal Use */
    RwUInt32 meshIdentifier;                            /**< Internal Use */

    int batchSize;                                      /**< Internal Use */
    int numBatches;                                     /**< Internal Use */
    int batchesPerTag;                                  /**< Internal Use */

#if (defined(FASTMORPH))
    int morphStart;                                     /**< Internal Use */
    int morphFinish;                                    /**< Internal Use */
    int morphNum;                                       /**< Internal Use */
#endif /* (defined(FASTMORPH)) */

    rwPS2AllClusterQuickInfo clquickinfo[CL_MAXCL + FMADD]; /**< Internal Use */
    rwPS2AllFieldRec fieldRec[CL_MAXCL + FMADD];        /**< Internal Use */


/* RWPUBLICEND */
    /* refCnt, clrCnt - These fields must be first. They are used by the dma
     *                  reference count management system
     * numVerts       - 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
     * objIdentifier  - So object-level changes can be detected
     * meshIdentifier - So mesh-level changes can be detected
     * batchSize      - Number of verts in a batch.
     * numBatches     - Useful to cache, not costless to recalc.
     * batchesPerTag  - The number of batches that a tag can deal with in the
     *                  totally opaque case (max data transferrable by one tag
     *                  is 2^16 quadwords).
     * clquickinfo    - Used to pass (void *) data pointers to instanceCBs,
     * fieldRec       - We want the data localised.
     */

/* TODO[5]: STRIDE MEMBER OF clquickinfo IS NOW UNUSED - REMOVE! */

/* TODO[4][5]: THIS STRUCTURE'S TOO BIG!! THINK OF THE DUNGEON; THIS IS LOTS
 *    OF BYTES PER MESH AND MESHES ARE TINY THERE... AS MUCH AS IS HUMANLY
 *   POSSIBLE SHOULD GO INTO PER-PIPE VERTEX FORMAT DESCRIPTOR STRUCTS. */

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

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

/* RWPUBLIC */

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

extern RxNodeDefinition *RxNodeDefinitionGetPS2All(void);


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

extern RxPipelineNode *
RxPipelineNodePS2AllSetCallBack(RxPipelineNode *self,
                                RxPipelineNodePS2AllCallBackType type,
                                void *func);
extern RxPipelineNode *
RxPipelineNodePS2AllGroupMeshes(RxPipelineNode *node, RxPipeline *pipe);


#ifdef    __cplusplus
}
#endif /* __cplusplus */


/* RWPUBLICEND */


#endif /* NODEPS2ALL_H */

