/*
 * Potentially Visible Set plug-in
 */

/**********************************************************************
 *
 * file :     rppvs.h
 *
 * abstract : handle culling of worldsectors in RenderWare
 *
 **********************************************************************
 *
 * This file is a product of Criterion Software Ltd.
 *
 * This file is provided as is with no warranties of any kind and is
 * provided without any obligation on Criterion Software Ltd. or
 * Canon Inc. to assist in its use or modification.
 *
 * Criterion Software Ltd. will not, under any
 * circumstances, be liable for any lost revenue or other damages arising
 * from the use of this file.
 *
 * Copyright (c) 2001 Criterion Software Ltd.
 * All Rights Reserved.
 *
 * RenderWare is a trademark of Canon Inc.
 *
 ************************************************************************/

/**
 * \defgroup rppvs rppvs
 * \ingroup rpplugin
 *
 * Geometric Potentially Visible Set Plugin for RenderWare.
 */


/****************************************************************************
 Defines
 */

#define SECTORCULL TRUE        /* Tests whole sectors, and avoids testing many individual polygons */
#define TRANSLUCENCY TRUE      /* Treats translucent polygons as a special case: can be occluded, but not occluder */
#define DETESS TRUE            /* Detesselates triangles to improve the preprocessing speed */
#define DECOLINEAR TRUE        /* Removes slivers and colinear vertices to improve speed and accuracy */
#define JUMP TRUE              /* Skips sectors already determined as visible or in a trivial sector: see SECTORCULL */

#define ABS(x) ((x)<0.0 ? -(x) : (x)) /* Return absolute value */
#define SMALL 0.005f           /* A small value to represent geometric zero */
#define TINY 0.000001f         /* A very small value to represent computational zero */
#define PVSPNZ(x) ((x)<-SMALL ? -1 : ((x)>SMALL ? 1 : 0)) /* Checks the sign of the value, returns Pos (1), Neg (-1), Zero (0) */
#define SECTORCULLTOL 8

#define PVSFROMWORLDSECTOR(sector) \
    ((RpPVS *)(((char *)(sector))+rpPVSGlobals.sectorOffset))
#define PVSFROMCONSTWORLDSECTOR(sector) \
    ((const RpPVS *)(((const char *)(sector))+rpPVSGlobals.sectorOffset))

#define PVSCACHEFROMWORLD(world) \
    ((RpPVSCache *)(((char *)(world))+rpPVSGlobals.worldOffset))
#define PVSCACHEFROMCONSTWORLD(world) \
    ((const RpPVSCache *)(((const char *)(world))+rpPVSGlobals.worldOffset))

/* Progress callback message types */
#define rpPVSPROGRESSSTART              20
#define rpPVSPROGRESSUPDATE             12
#define rpPVSPROGRESSEND                22

typedef             RwBool(*RpPVSProgressCallBack) (RwInt32 msg,
                                                     RwReal value);

typedef RpWorldSector *(*RpPVSCallBack) (RpWorldSector * worldSector,
                                          const RwBBox * box,
                                          void *pData);

#define RpPVSCallback                   RpPVSCallBack

typedef struct _RpPVSCallBack _RpPVSCallBack;
struct _RpPVSCallBack
{
    RpPVSCallBack      callback;
    void               *data;
};

typedef RwUInt8     RpPVSVisMap;

#define PVSVISMAPSETSECTOR(_vismap, _id)        \
    (_vismap)[(_id) >> 3] |= (1 << ((_id) & 7))

#define PVSVISMAPUNSETSECTOR(_vismap, _id)        \
    (_vismap)[(_id) >> 3] ^= (1 << ((_id) & 7))

#define PVSVISMAPGETSECTOR(_vismap, _id)        \
    ((_vismap)[(_id) >> 3] & (1 << ((_id) & 7)))

#define PVSVISMAPLENGTH(_vismaplength, _nosectors) \
    (_vismaplength) = ((_nosectors + 7) >> 3)

enum PartitionId
{
    FRONT,
    BACK,
    SPLIT,
    COPLANAR
};
typedef enum PartitionId PartitionId;

typedef struct polyList PolyList;
typedef struct polyList *PolyListPtr;

typedef struct poly Poly;
typedef struct poly *PolyPtr;

typedef struct planeEq PlaneEq;
struct planeEq
{
    RwReal              x;
    RwReal              y;
    RwReal              z;
    RwReal              w;

    RwReal              l;      /* recip of length of the normal */

    PartitionId         lastresult; /* temp: stores result of last polygon wrt this plane */
};

typedef struct polyRecord PolyRecord;
struct polyRecord
{
    RwBool              original; /* True if not a fragment */
    RwReal              priority; /* Used for sorting, lower values higher priority */
    PolyListPtr         parent; /* Unique pointer to original parent */
    PolyPtr             geom;   /* corners of the poly */
    PlaneEq             plane;  /* plane equation of the poly */
    RwInt32             home;   /* world sector id in range 0..numsectors */
    RpWorldSector      *homeaddr; /* world sector pointer */
    RwBool              translucent;

    RwBool              hasbeenclipper; /* Used during WA creation */

    /* used by proximity culling, calculated once */
    RwV3d               centroid;
    RwReal              radius;
    RwV3d               extreme; /* the vertex furthest away from the centroid */

};

struct poly
{
    RwV3d               v;
    Poly               *next;

    RwInt32             pscalar; /* Used during clipping only */
    RwReal              scalar; /* Used during clipping only */
    PlaneEq             shadowPlane; /* Used during clipping only */

};

struct polyList
{
    PolyRecord          data;
    PolyList           *next;
};

typedef struct RpPVS RpPVS;
struct RpPVS
{
    RwInt32             sectorID; /* Id of the sector */
    RwInt32             vismaplength; /* Length of vismap */
    RwInt32             sampleKey; /* Currently unused, for future use */
    
    RpPVSVisMap       *vismap;

    PolyListPtr         sectailpoly; /* Pointer to last polygon in polygons list that is in this sector */

    PartitionId         potential; /* temp: is sector in out or split from current shadow volume  - for heirarchical clip */
    RwUInt32            numpols;
    const RwBBox       *envelope; /* Bounding box of the sector */
    RwV3d               centre; /* Centre of the sector */
    RwReal              diagonal;
};

typedef struct RpPVSCache RpPVSCache;
struct RpPVSCache
{
    RwBool              processed; /* flag to indicate exisiting PVS data for the world */
    RwBool              formatted; /* flag to indicate exisiting intermediate polygonal data for PVS generation */

    /* stats collection */
    RwInt32             ptotal;
    RwInt32             paccept;

    /* pipeline hooking */
    RwBool              hooked;

    /* used during vismap allocation */
    RwUInt32            nextID;

    RwInt32             viscount;

    /* Used during construction */
    RpPVSProgressCallBack progressCallBack;

    PolyListPtr         polygons; /* A copy of the input data set of all world polygons */

};

typedef struct rpPVSGlobalVars RpPVSGlobalVars;
struct rpPVSGlobalVars
{
    RpWorld            *World;

    RwInt32             worldOffset; /* Offset into global data */
    RwInt32             sectorOffset; /* Offset into global data */

    RwInt32             NumWorldSectors;

    RwInt32             progress_count;

    RwReal              gran;

    RwInt32             InSector; /* Current sector id */
    RwV3d               ViewPos; /* Current view pos */
    RpPVS             *CurrPVS; /* Current PVS sector */

    /* Used during construction */
    PolyListPtr         VisibilityStack; /* Result of PVS from one view point - the list of visible polygons from there */

};

extern RpPVSGlobalVars    rpPVSGlobals;

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

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



extern RpWorld     *RpPVSSetProgressCallBack(RpWorld * world,
                                              RpPVSProgressCallBack
                                              callback);
extern RpWorld     *RpPVSSetViewPosition(RpWorld * wpWorld,
                                          RwV3d * pos);
extern RpWorld     *RpPVSDestroy(RpWorld * wpWorld);
extern RwBool       RpPVSWorldSectorVisible(RpWorldSector * sector);
extern RwBool       RpPVSPluginAttach(void);
extern RpWorldSector *RpPVSAddWorldSector(RpWorldSector * spSect);
extern void         RpPVSAddPOV(RwV3d * pos);
extern RpWorld     *RpPVSAddExtraPOV(RpWorld * wpWorld,
                                      RwRaster __RWUNUSED__ * raster,
                                      RwRaster __RWUNUSED__ * zraster,
                                      RwReal __RWUNUSED__ mindist,
                                      RwReal __RWUNUSED__ maxdist,
                                      RwMatrix * mat);
extern RwBool       RpPVSQuery(RpWorld * wpWorld);
extern RwBool       RpPVSAtomicVisible(RpAtomic * atom);
extern RpWorld     *RpPVSStatisticsGet(RpWorld * wpWorld,
                                        RwInt32 * ptotal,
                                        RwInt32 * paccept);
extern RpPVSProgressCallBack RpPVSGetProgressCallBack(RpWorld *
                                                        world);
extern RpWorld     *RpPVSCreate(RpWorld * wpWorld, RwRaster * raster,
                                 RwRaster * zraster, RwReal mindist,
                                 RwReal maxdist, RwInt32 maxdepth,
                                 RpPVSCallBack callback, void *pData);
extern RpWorldSector *RpPVSGeneric(RpWorldSector * sector,
                                    const RwBBox __RWUNUSED__ * box,
                                    void *data);

extern RpWorld     *RpPVSUnhook(RpWorld * wpWorld);
extern RpWorld     *RpPVSHook(RpWorld * wpWorld);

extern RpWorldSector *RpPVSSetWorldSectorVisibility(RpWorldSector * sector,
                                                     RwBool visible);

extern void         RpPVSSamplePOV(RwV3d * pos, RwBool colltest);

RxNodeDefinition   *RxNodeDefinitionGetPVSWorldSectorCSL(void);

/* These three functions are added for backwards compatibility... */
#define RpPVSAddPOV(_pos) \
    RpPVSSamplePOV(_pos, FALSE)

#define RpPVSAddWorldSector(_sector) \
    RpPVSSetWorldSectorVisibility(_sector, TRUE)


#define RpPVSAddExtraPOV(_world, _raster, _zraster, _mindist, _mazdist, _matrix)    \
    rpPVSGlobals.World = (_world);                                                  \
    RpPVSSamplePOV(&((_matrix)->pos), TRUE);

















#ifdef    __cplusplus
}
#endif                          /* __cplusplus */

#undef RWRETURN
#define RWRETURN(x) return (x)

#undef RWRETURNVOID
#define RWRETURNVOID() return

#undef RWAPIFUNCTION
#define RWAPIFUNCTION(x)       /* No op */

#undef RWFUNCTION
#define RWFUNCTION(x)          /* No op */

#undef RWASSERT
#define RWASSERT(x)            /* No op */

#undef RWERROR
#define RWERROR(x)             /* No op */
