/***************************************************************************
 *                                                                         *
 * Module  : basector.h                                                    *
 *                                                                         *
 * Purpose : handling of sectors                                           *
 *                                                                         *
 **************************************************************************/

#ifndef RWSECTOR_H
#define RWSECTOR_H

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

#include <rwcore.h>
#include "bamateri.h"
#include "bamatlst.h"
#include "bamesh.h"

/* RWPUBLIC */

/*
 * Handling atomic sectors
 * Atomic sectors are use to divide up the world into manageable portions
 *
 * Copyright (c) 1998 Criterion Software Ltd.
*/

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

/* Type ID */
#define rpWorldSector 0xff     /* Not a true 'type'! */

#define rpMINDISTANCEBETWEENVERTICES (RwReal)(0.0001)

#define RPV3DFROMVERTEXNORMAL(v, n) \
    (v).x = (((RwReal)((n).x)) * ( (RwReal)(1.0/128))); \
    (v).y = (((RwReal)((n).y)) * ( (RwReal)(1.0/128))); \
    (v).z = (((RwReal)((n).z)) * ( (RwReal)(1.0/128)))

#define RPVERTEXNORMALFROMRWV3D(n, v)           \
    {                                           \
        RwFixed naTmp[3];                       \
                                                \
        naTmp[0] = RwRealToFixed((v).x);        \
        naTmp[1] = RwRealToFixed((v).y);        \
        naTmp[2] = RwRealToFixed((v).z);        \
                                                \
        if (naTmp[0] >= RwFixedCast(1))         \
        {                                       \
            naTmp[0] = RwFixedCast(1)-1;        \
        }                                       \
        if (naTmp[0] <= RwFixedCast(-1))        \
        {                                       \
            naTmp[0] = RwFixedCast(-1)+1;       \
        }                                       \
        if (naTmp[1] >= RwFixedCast(1))         \
        {                                       \
            naTmp[1] = RwFixedCast(1)-1;        \
        }                                       \
        if (naTmp[1] <= RwFixedCast(-1))        \
        {                                       \
            naTmp[1] = RwFixedCast(-1)+1;       \
        }                                       \
        if (naTmp[2] >= RwFixedCast(1))         \
        {                                       \
            naTmp[2] = RwFixedCast(1)-1;        \
        }                                       \
        if (naTmp[2] <= RwFixedCast(-1))        \
        {                                       \
            naTmp[2] = RwFixedCast(-1)+1;       \
        }                                       \
                                                \
        (n).x = (RwInt8)(naTmp[0]>>9);          \
        (n).y = (RwInt8)(naTmp[1]>>9);          \
        (n).z = (RwInt8)(naTmp[2]>>9);          \
    }

/* RpCollSector access macros - for pre v304 data */
#define RWCOLLSECTORGETTYPE(sect) \
    ((sect).cType&0x80)

#define RWCOLLSECTORGETPLANE(sect) \
    ((((sect).cType)>>3)&0xc)

#define RWCOLLSECTORGETON(sect) \
    (((sect).cType)&0x1f)

#define RWCOLLSECTORGETVERTEX(sect) \
    (sect).vertex

#define RWCOLLSECTORGETSTART(sect) \
    (sect).start

#define RWCOLLSECTORGETNOPOLYS(sect) \
    (sect).cType

#define RWCOLLSECTORSETPOLYGONS(sect,no,st) \
    (sect).cType = (RwUInt8)(no); \
    (sect).start = (RwUInt8)(st)

#define rwMAXCOLLISIONCUTS 7

/****************************************************************************
 Global types
 */

typedef struct RpVertexNormal RpVertexNormal;

struct RpVertexNormal
{
    RwInt8          x;
    RwInt8          y;
    RwInt8          z;
    RwUInt8         pad; /* pad character to force alignment */
};

typedef struct RpPolygon RpPolygon;

struct RpPolygon
{
    RwUInt16        matIndex;
    RwUInt16        vertIndex[3];
};

/* RpCollSector - for pre v304 data */
#define RWCOLLSECTORSETPLANE(sect,plane,vert,no,st) \
    (sect).cType = (RwUInt8)(0x80|((plane)<<3)|(no)); \
    (sect).vertex = (RwUInt8)(vert); \
    (sect).start = (RwUInt8)(st)

typedef struct RpCollSector RpCollSector;

struct RpCollSector
{
    RwUInt8 cType;    /* Bit 7   - 1 plane */
                      /*           0 polygons */
                      /* Bit 6-5 - plane */
                      /* Bit 4-0 - amount ON plane */
    RwUInt8 vertex;   /* Vertex index used for the split */
    RwUInt8 start;    /* Starting polygon */
};

/**
 * \ingroup rpworlddatatypes
 * \typedef RpWorldSector
 * World Sector object. This should be
 * considered an opaque type. Use the RpWorldSector API functions to access.
 */
typedef struct RpWorldSector RpWorldSector;

#if (!defined(DOXYGEN))
struct RpWorldSector
{
    RwInt32                 type;

    RpPolygon               *polygons;              /* Polygons themselves */

    RwV3d                   *vertices;              /* Vertex positions */
    RpVertexNormal          *normals;               /* Vertex normals */

    RwTexCoords             *texCoords[rwMAXTEXTURECOORDS]; /* Texture coordinates */

    RwRGBA                  *preLitLum;             /* Pre lit luminances */

    /* Pointer to memory allocated for vertex and poly pointers */
    RwResEntry              *repEntry;

    /* Atomics in this sectors */
    /* The pointers are frigged so they look like they are pointing to
       Atomics when they are pointing to here */
    RwLinkList              collAtomicsInWorldSector;       /* Coll priority */
    RwLinkList              noCollAtomicsInWorldSector;     /* No Coll priority */

    /* Lights in an atomic sector */
    RwLinkList              lightsInWorldSector;

    /* World space size of this sector */
    RwBBox                  boundingBox;

    /* The root of the bsp collision tree for pre v304 data */
    RpCollSector            *colSectorRoot;

    /* The mesh which groups same material polygons together */
    RpMeshHeader            *mesh;

    /* The WorldSector object pipeline for this WorldSector */
    RxPipeline    *pipeline;

    /* Material list window base
     * (triangles in a given sector can "see"
     * the 256 materials from
     * MatList[matListWindowBase] -> MatList[matListWindowBase + 255])
     */
    RwUInt16                matListWindowBase;

    RwUInt16                numVertices;            /* Num vertices */
    RwUInt16                numPolygons;            /* Num polygons */
    RwUInt16                pad;
};
#endif /* (!defined(DOXYGEN)) */

/**
 * \ingroup rpworlddatatypes
 * \typedef RpWorldSectorCallBack
 \ref RpWorldSectorCallBack
 * represents the function called from \ref RpWorldForAllWorldSectors,
 * \ref RpAtomicForAllWorldSectors and \ref RpLightForAllWorldSectors for all
 * world sectors in a given world or world sectors a given atomic or light lies
 * in. This function should return a pointer to the current world sector to
 * indicate success. The callback may return NULL to terminate further
 * callbacks on the atomic or light.
 *
 * \return Pointer to the current world sector.
 *
 * \param  sector   Pointer to the current world sector
 * \param  data  Pointer to developer-defined data structure.
 */
typedef RpWorldSector *(*RpWorldSectorCallBack)(RpWorldSector *worldSector, void *data);

typedef struct RpSector RpSector;

struct RpSector
{
    RwInt32                 type;
};

/* NOTE: The left and right pointers can point to an RpPlaneSector or
 *       an RpWorldSector
 * This is determined what the type is via the type value
 */

typedef struct RpPlaneSector RpPlaneSector;

struct RpPlaneSector
{
    RwInt32                 type;

    RwReal                  value;
    RpSector                *leftSubTree;   /* Sector 'left' (less) of the plane */
    RpSector                *rightSubTree;  /* Sector 'right' (more) of the plane */
    RwReal                  leftValue;
    RwReal                  rightValue;
};


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

extern RwPluginRegistry sectorTKList;

#ifdef    __cplusplus
}
#endif                          /* __cplusplus */

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

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

/* RWPUBLICEND */
/* Opening and closing */
extern void *_rpSectorOpen(void *instance, RwInt32 offset, RwInt32 size);
extern void *_rpSectorClose(void *instance, RwInt32 offset, RwInt32 size);

/* RWPUBLIC */

/* Get info from atomic sectors */
extern RwInt32 RpWorldSectorGetNumPolygons(const RpWorldSector *Sector);
extern RwInt32 RpWorldSectorGetNumVertices(const RpWorldSector *Sector);

/* Instancing and deinstancing sectors */
extern RpWorldSector * RpWorldSectorRender(RpWorldSector *worldSector);

extern const RpWorldSector *RpWorldSectorForAllMeshes(const RpWorldSector *sector,
                                                      RpMeshCallBack fpCallBack,
                                                      void *pData);

extern const RwBBox *RpWorldSectorGetBBox(const RpWorldSector *sector);

/* Plugins */
extern RwInt32 RpWorldSectorRegisterPlugin(RwInt32 size, RwUInt32 pluginID,
                                           RwPluginObjectConstructor constructCB,
                                           RwPluginObjectDestructor destructCB,
                                           RwPluginObjectCopy copyCB);
extern RwInt32 RpWorldSectorRegisterPluginStream(RwUInt32 pluginID,
                                                 RwPluginDataChunkReadCallBack readCB,
                                                 RwPluginDataChunkWriteCallBack writeCB,
                                                 RwPluginDataChunkGetSizeCallBack getSizeCB);
extern RwInt32 RpWorldSectorSetStreamAlwaysCallBack(
                    RwUInt32 pluginID,
                    RwPluginDataChunkAlwaysCallBack alwaysCB);
extern RwInt32 RpWorldSectorSetStreamRightsCallBack(RwUInt32 pluginID,
                                      RwPluginDataChunkRightsCallBack rightsCB);
extern RwInt32 RpWorldSectorGetPluginOffset(RwUInt32 pluginID);
extern RwBool RpWorldSectorValidatePlugins(const RpWorldSector *sector);

#ifdef    __cplusplus
}
#endif                          /* __cplusplus */
/* RWPUBLICEND */

#endif /* RWSECTOR_H */

