/** 
 * Data structures for a particle system plugin 
 *
 * Copyright (c) Criterion Software Limited
 */

/***************************************************************************
 *                                                                         *
 * Module  : rpprtcls.h                                                    *
 *                                                                         *
 * Purpose : See rpprtcls.c                                                *
 *                                                                         *
 **************************************************************************/

#ifndef RPPRTCLS_H
#define RPPRTCLS_H

/**
 * \defgroup rpprtcls RpPrtcls
 * \ingroup retired
 *
 * Alternative Particle System Plug-In for RenderWare.
 */

/****************************************************************************
 Includes
 */

#include <rwcore.h>
#include <rpworld.h>

/****************************************************************************
 Global Defines
 */

/* Access macro gives you the geom->extData pointer */
#define RPEXTFROMGEOMETRY(_geom) \
    ( *(RpParticleSystem **)( ((RwUInt8 *)(_geom)) + rpDemoExtensionOffset ))

#define RPEXTFROMCONSTGEOMETRY(_geom) \
    ( *(const RpParticleSystem **)( ((const RwUInt8 *)(_geom)) + rpDemoExtensionOffset ))

/* TODO: is there a way to force this enum to be byte-sized? */
/**
 * \ingroup rpprtcls
 *  typedef to an enum enumerating particle states 
 */
enum RpParticleState
{
    rpPARTICLEALIVE = 0,  /**<Frame zero (if there is any animation) by
                           * default. All values from 0 to 254 are valid */
    rpPARTICLEDEAD  = 255 /**<Particles flagged with this are dead */
};
typedef enum RpParticleState RpParticleState;

/****************************************************************************
 Global Types
 */

typedef struct RpParticle RpParticle;
typedef struct RpParticleSystem RpParticleSystem;
typedef struct RpParticleSystemType RpParticleSystemType;

/**
 * \ingroup rpprtcls
 * \typedef RpParticleTimeCB 
 * \ref RpParticleTimeCB is the callback to retrieve a time value for a particular particle system
 *
 * \return \ref RwUInt32 time value.
 */
typedef RwUInt32 (*RpParticleTimeCB)(void);

/**
 * \ingroup rpprtcls
 * \typedef RpParticleSystemDataAllocCB 
 * \ref RpParticleSystemDataAllocCB is the callback to allocate particle-system-type-specific data when a particle
 * system of a given type is created.
 *
 * \param  data   a pointer to a pointer to receive
 * the allocation result.
 *
 * \return TRUE on success, FALSE on failure.
 */
typedef RwBool (*RpParticleSystemDataAllocCB)(void **data);

/**
 * \ingroup rpprtcls
 * \typedef RpParticleSystemDataFreeCB 
 * \ref RpParticleSystemDataFreeCB is the callback to free particle-system-type-specific data when a particle
 * system of a given type is destroyed.
 *
 * \param  data   a pointer to the memory to free.
 */
typedef void (*RpParticleSystemDataFreeCB)(void *data);

/**
 * \ingroup rpprtcls
 * \typedef RpParticleSystemInitCB 
 * \ref RpParticleSystemInitCB is the callback to initialize particle-system-type-specific data
 * when a particle system of a given type is initialized.
 *
 * \param  system   A pointer to a \ref RpParticleSystem to be initialized.
 * \param  geom   A pointer to the \ref RpGeometry which owns the \ref RpParticleSystem.
 * \param  mat   A pointer to the \ref RpMaterial of the \ref RpGeometry (this should
 * contain the relevant particle system render \ref RxPipeline).
 *
 * \return TRUE on success, FALSE on failure.
 */
typedef RwBool (*RpParticleSystemInitCB)(RpParticleSystem *system,
                                         RpGeometry *geom,
                                         RpMaterial *mat);

/**
 * \ingroup rpprtcls
 * \typedef RpParticleSystemUpdateCB
 * \ref RpParticleSystemUpdateCB is the callback to update particle-system-type-specific data
 * when a particle system of a given type is updated (typically
 * once a frame).
 *
 * \param  system   A pointer to a \ref RpParticleSystem to be updated.
 *
 * \return TRUE on success, FALSE on failure.
 */
typedef RwBool (*RpParticleSystemUpdateCB)(RpParticleSystem *system);

/**
 * \ingroup rpprtcls
 * \struct RpParticle
 * typedef to a structure describing a particle */
struct RpParticle
{
    RwV3d   vel;      /**< \ref RwV3d Current/initial velocity */
    RwUInt8 state;    /**< \ref RpParticleState current state of the particle,
                       * treated as animation frame unless dead */
    RwUInt8 stuff[3]; /**< \ref RwUInt8[3] Mass, random seed, whatever */
};

/**
 * \ingroup rpprtcls
 * \struct RpParticleSystem 
 * typedef to a structure describing a particle system */
struct RpParticleSystem
{
    RpParticle           *particles;    /**< A pointer to an array of \ref RpParticle's */
    RwUInt32              numParticles; /**< \ref RwUInt32 number of particles in the array */
    RpParticleSystemType *type;         /**< \ref RpParticleSystemType pointer to
                                         * a particle system type specifier structure */
    void                 *typeData;     /**< A void pointer to particle-system-type-specific data */
};

#if (!defined(RpParticleSystemAssign))
#define RpParticleSystemAssign(_target, _source)        \
    ( *(_target) = *(_source) )
#endif /* (!defined(RpParticleSystemAssign)) */

/**
 * \ingroup rpprtcls
 * \struct RpParticleSystemType
 * typedef to a structure describing a particle system type */
struct RpParticleSystemType
{
    RwChar *name;                         /**< A pointer to a string identifying
                                           * this particle system type */
    RwInt32 dataSize;                     /**< Each type of particle system has some
                                           * associated data, of this size in bytes */
    /* Callbacks to alloc/dealloc said data */
    RpParticleSystemDataAllocCB allocCB;  /**< \ref RpParticleSystemDataAllocCB callback to allocate
                                           * the \ref RpParticleSystem type-specific data when the
                                           * \ref RpParticleSystem is created */
    RpParticleSystemDataFreeCB  freeCB;   /**< \ref RpParticleSystemDataFreeCB callback to free
                                           * the \ref RpParticleSystem type-specific data when the
                                           * \ref RpParticleSystem is destroyed */
    RpParticleSystemInitCB      initCB;   /**< \ref RpParticleSystemInitCB callback to initialize
                                           * the \ref RpParticleSystem type-specific data when the
                                           * \ref RpParticleSystem is initialized */
    RpParticleSystemUpdateCB    updateCB; /**< \ref RpParticleSystemUpdateCB callback to update
                                           * the \ref RpParticleSystem type-specific data each time
                                           * the \ref RpParticleSystem is updated (typically once a
                                           * frame) */
};

typedef struct RpParticleSystemInstanceData RpParticleSystemInstanceData;
/**
 * \ingroup rpprtcls
 * \struct RpParticleSystemInstanceData 
 *  typedef to an enum enumerating particle states */
struct RpParticleSystemInstanceData
{
    RwBool parametric; /**< \ref RwBool TRUE if the particle system animation is parametric,
                        * in which case the RepEntry will be constant. FALSE if the particle
                        * system animation is incremental, in which case the RepEntry will be
                        * modified from frame to frame (by way of clusters flagged as
                        * rxCLFLAGS_EXTERNALMODIFIABLE). */
};

/**
 * \ingroup rpprtcls
 *  typedef to flags defining the
 * animation options of ParticleExpand.csl */
enum RxPipelineNodePtExAnimFlag
{
    rxNODEPTEXANIMFLAGZERO     = 0, /**<(default) Set all particles to frame zero of their animation */
    rxNODEPTEXANIMFLAGRANDOM   = 1, /**<Set each particle to a random frame */
    rxNODEPTEXANIMFLAGINC      = 2, /**<Increment frames at 'animRate' (see \ref RxPipelineNodeParticleExpandData) */
    rxNODEPTEXANIMFLAGOVERRIDE = 4, /**<We use per particle \ref RpParticle data (if present) to choose frames */
    rxNODEPTEXANIMFLAGFORCEENUMSIZEINT = RWFORCEENUMSIZEINT
};
typedef enum RxPipelineNodePtExAnimFlag RxPipelineNodePtExAnimFlag;

/* LEGACY-SUPPORT define for old enum name */
#define RxNodePtExAnimFlag RxPipelineNodePtExAnimFlag

typedef struct RxPipelineNodeParticleExpandData RxPipelineNodeParticleExpandData;
/**
 * \ingroup rpprtcls
 * \struct RxPipelineNodeParticleExpandData
 * typedef to a structure describing */
struct RxPipelineNodeParticleExpandData
{
    RwBool             particlesOn;        /**< \ref RwBool to toggle operation of the node on/off */
    RwBool             useQuads;           /**< \ref RwBool to specify whether to draw particles
                                            * as quads or tris */
    RwReal             xSize;              /**< World-space \ref RwReal size of particles, aligned
                                            * to the 'right' vector of camera-space */
    RwReal             ySize;              /**< World-space \ref RwReal size of particles, aligned
                                            * to the 'up' vector of camera-space */
    RwUInt32           numFrames;          /**< \ref RwUInt32 specifying how many frames of animation
                                            * within the texture there are */
    RxPipelineNodePtExAnimFlag animFlags;  /**< \ref RxPipelineNodePtExAnimFlag specifying mode of animation */
    RwBlendFunction    srcBlend;           /**< \ref RwBlendFunction, often additive I guess, but
                                            * not always... */
    RwBlendFunction    destBlend;          /**< \ref RwBlendFunction, often additive I guess, but
                                            * not always... */
    RwBool             zWrite;             /**< \ref RwBool specifying that particles should write to
                                            * the Z-Buffer (they will often mess up if they do) */
    RwTexture         *texture;            /**< A \ref RwTexture pointer to the texture to use for
                                            * the particles */
    RwRGBA             color;              /**< A \ref RwRGBA with which to override incoming vertex
                                            * colour (e.g to fade them out globally) */
    RwReal            *U;                  /**< A pointer to the 3/4 \ref RwReal U coordinates at the
                                            * corners of the particle for each frame */
    RwReal            *V;                  /**< A pointer to the 3/4 \ref RwReal V coordinates at the
                                            * corners of the particle for each frame */
    RwUInt32           animRate;           /**< A \ref RwUInt32 time delay in milliseconds between
                                            * particle animation frames */
};

/* LEGACY-SUPPORT define for old enum name */
#define RxNodeParticleExpandData RxPipelineNodeParticleExpandData

/****************************************************************************
 Function prototypes
 */

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

/*
 * Globals across the program
 */

extern RwInt32 rpDemoExtensionOffset;

/* Uses the RpParticle type (see below) */
extern RxClusterDefinition clusterRpParticles;
extern RxClusterDefinition clusterRpParticleSystemData;

extern RwBool                
RpParticleGeometryPluginAttach(RpParticleTimeCB timer);

extern RwUInt32              
RpParticleSystemTimer(void);

extern RpParticleSystem *
RpParticleGeometryGetParticleSystem(RpGeometry *geom);

extern RpGeometry *
RpParticleGeometryCreate(RpGeometry *geom, RpParticleSystemType *type);

extern void                  
RpParticleGeometryDestroy(RpGeometry *geom);

extern RwBool                
RpParticleGeometryValidate(RpGeometry *geom);

extern RpParticleSystemType *
RpParticleGeometryGetType(RpGeometry *geom);

extern RpGeometry *
RpParticleGeometrySetType(RpGeometry *geom, RpParticleSystemType *type);

extern RwBool
RpParticleGeometryInitialize(RpGeometry *geom, RpMaterial *mat);

extern RwBool                
RpParticleSystemUpdate(RpParticleSystem *system);

extern RxNodeDefinition *
RxNodeDefinitionGetParticleSystemInstanceCSL(void);

extern RxNodeDefinition *
RxNodeDefinitionGetParticleExpandCSL(void);

extern RwBool                
RxPipelineNodeParticleExpandUVMalloc(RxPipelineNode *node,
                                     RwInt32 numFrames,
                                     RwBool useQuads,
                                     RwReal **Us,
                                     RwReal **Vs);

#ifdef    __cplusplus
}
#endif         /* __cplusplus */

#endif /* RPPRTCLS_H */

