/*
 *  ctbsp.h
 */

#include <rwcore.h>

#if (!defined(_CTBSP_H))
#define _CTBSP_H

 
/******************************************************************************
 *  Defines
 */
#define rpCOLLBSP_LEAF_NODE    (1)
#define rpCOLLBSP_BRANCH_NODE  (2)
#define rpCOLLBSP_MIN_POLYGONS_FOR_SPLIT  (4) /* Won't split sector if less */
#define rpCOLLBSP_MAX_DEPTH          (32)


/******************************************************************************
 *  Types
 */

/*** Collision BSP Tree Data Structure ***/

typedef struct RpCollBSPLeafNode RpCollBSPLeafNode;
struct RpCollBSPLeafNode
{
    RwUInt16    numPolygons;    /* Number of polygons */
    RwUInt16    firstPolygon;   /* Index of first polygon */
};

typedef struct RpCollBSPBranchNode RpCollBSPBranchNode;
struct RpCollBSPBranchNode
{
    RwUInt16    type;         /* clipping plane type, defined for use */
                              /*    with GETCOORD(v3d, type) */

    RwUInt8     leftType;     /* rpCOLLBSP_LEAF_NODE | rpCOLLBSP_BRANCH_NODE */
    RwUInt8     rightType;    /* (not enum type due to space requirements) */

    RwUInt16    leftNode;     /* Array index of left child node */
    RwUInt16    rightNode;    /* Array index of right child node */

    RwReal      leftValue;    /* Left sector clip value */
    RwReal      rightValue;   /* Right sector clip value */
};

typedef struct RpCollBSPTree RpCollBSPTree;
struct RpCollBSPTree
{
    RwUInt32            numLeafNodes;

    RpCollBSPBranchNode *branchNodes; /* Self-indexing array of branch nodes */

    RpCollBSPLeafNode   *leafNodes;   /* Array of leaf nodes */

    RwUInt8             pad[4];       /* Pad to quadword */
};

typedef RwBool (*RpCollBSPLeafCB)(RwInt32 numPolygons, RwInt32 polyOffset, 
                                  void *data);

/* Vector gradient for fast line-plane clipping */
typedef struct RpV3dGradient RpV3dGradient;
struct RpV3dGradient
{
    RwReal  dydx;  /* y/x etc. */
    RwReal  dzdx;
    RwReal  dxdy;
    RwReal  dzdy;
    RwReal  dxdz;
    RwReal  dydz;
};

/******************************************************************************
 *  Macros
 */

#define _rpV3dGradientMacro(grad, vec)                              \
MACRO_START                                                         \
{                                                                   \
    RwReal  recip;                                                  \
                                                                    \
    recip = ((vec)->x != 0.0f) ? (1.0f / (vec)->x) : 0.0f;          \
    (grad)->dydx = (vec)->y * recip;                                \
    (grad)->dzdx = (vec)->z * recip;                                \
                                                                    \
    recip = ((vec)->y != 0.0f) ? (1.0f / (vec)->y) : 0.0f;          \
    (grad)->dxdy = (vec)->x * recip;                                \
    (grad)->dzdy = (vec)->z * recip;                                \
                                                                    \
    recip = ((vec)->z != 0.0f) ? (1.0f / (vec)->z) : 0.0f;          \
    (grad)->dxdz = (vec)->x * recip;                                \
    (grad)->dydz = (vec)->y * recip;                                \
}                                                                   \
MACRO_STOP


#define _rpLinePlaneIntersectMacro(vOut, _line, grad, plane, val)   \
MACRO_START                                                         \
{                                                                   \
    RwReal  delta;                                                  \
    switch (plane)                                                  \
    {                                                               \
        case 0:                                                     \
            delta = (val) - (_line)->start.x;                       \
            (vOut)->x = (val);                                      \
            (vOut)->y = (_line)->start.y + (grad)->dydx * delta;    \
            (vOut)->z = (_line)->start.z + (grad)->dzdx * delta;    \
            break;                                                  \
        case 4:                                                     \
            delta = (val) - (_line)->start.y;                       \
            (vOut)->x = (_line)->start.x + (grad)->dxdy * delta;    \
            (vOut)->y = (val);                                      \
            (vOut)->z = (_line)->start.z + (grad)->dzdy * delta;    \
            break;                                                  \
        case 8:                                                     \
            delta = (val) - (_line)->start.z;                       \
            (vOut)->x = (_line)->start.x + (grad)->dxdz * delta;    \
            (vOut)->y = (_line)->start.y + (grad)->dydz * delta;    \
            (vOut)->z = (val);                                      \
            break;                                                  \
    }                                                               \
}                                                                   \
MACRO_STOP


/******************************************************************************
 *  Functions
 */

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


/* Creation and destruction */
extern void
_rpCollBSPTreeDestroy(RpCollBSPTree *tree);

extern RpCollBSPTree *
_rpCollBSPTreeCreate(RwInt32 numLeafNodes);

extern RpCollBSPTree *
_rpCollBSPTreeInit(RpCollBSPTree *tree, RwInt32 numLeafNodes);

extern RwInt32
_rpCollBSPTreeMemGetSize(RwInt32 numLeafNodes);


/* Stream read and write functions */
extern const RpCollBSPTree *
_rpCollBSPTreeStreamWrite(const RpCollBSPTree *tree, RwStream *stream);

extern RpCollBSPTree *
_rpCollBSPTreeStreamRead(RpCollBSPTree *tree, RwStream *stream);

extern RwInt32
_rpCollBSPTreeStreamGetSize(const RpCollBSPTree *tree);

extern RpCollBSPTree *
_rpCollBSPTreeTranslate(RpCollBSPTree *tree, const RwV3d *offset);

/* Collision tests */
extern RpCollBSPTree *
_rpCollBSPTreeForAllLineLeafNodeIntersections(
    RpCollBSPTree       *tree,
    RwLine              *line,
    RpV3dGradient       *grad,
    RpCollBSPLeafCB     callback,
    void                *data);

extern RpCollBSPTree *
_rpCollBSPTreeForAllBoxLeafNodeIntersections(
    RpCollBSPTree       *tree,
    RwBBox              *box,
    RpCollBSPLeafCB     callBack,
    void                *data);


#ifdef    __cplusplus
}
#endif                          /* __cplusplus */

#endif /* (!defined(_CTBSP_H)) */
