/*
 * Potentially Visible Set plug-in
 */

/****************************************************************************
 *                                                                          *
 * module : nodePVSWorldSector.c                                            *
 *                                                                          *
 * purpose: yawn...                                                         *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 includes
 */

#include <math.h>

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

#include <rpdbgerr.h>
#include "rppvs.h"
#include "rppvsdef.h"
#include "nodePVSWorldSector.h"

#if (!defined(DOXYGEN_SHOULD_SKIP_THIS))
static const char   rcsid[] __RWUNUSED__ =
    "@@(#)$Id: nodePVSWorldSector.c,v 1.38 2001/02/14 11:48:23 Colinh Exp $";
#endif /* (!defined(DOXYGEN_SHOULD_SKIP_THIS)) */

/****************************************************************************
 local defines
 */

#define NUMCLUSTERSOFINTEREST                   1
#define NUMOUTPUTS                              1

#define MESSAGE(_string)                                            \
    RwDebugSendMessage(rwDEBUGMESSAGE, "PVSWorldSectorCSL", _string)

/****************************************************************************
 local (static) prototypes
 */
static              RwInt32
PVSWorldSectorNodeFn(RxPipelineNodeInstance * __RWUNUSED__ self,
                     const RxPipelineNodeParam * params)
{
    RwBool              visible;
    RpPVSCache         *pvsCache;
    RpPVS              *pvs;
    RpWorldSector      *worldSector;
    RpWorld            *world;

    RWFUNCTION(RWSTRING("PVSWorldSectorNodeFn"));

    RWASSERT(NULL != self);

    worldSector = (RpWorldSector *) RxPipelineNodeParamGetData(params);
    RWASSERT(NULL != worldSector);

    world = rpPVSGlobals.world;

    pvs = PVSFROMWORLDSECTOR(worldSector);
    RWASSERT(NULL != pvs);
    pvsCache = PVSCACHEFROMWORLD(world);
    RWASSERT(NULL != pvsCache);

    /* Don't reject empty sectors since they may contain visible atomics. */

    visible = TRUE;

    if ((worldSector->numPolygons > 0) &&
        (NULL != pvsCache->sectorVismap))
    {
        RwInt32             sectorID = pvs->sectorID;

        pvsCache->ptotal += worldSector->numPolygons;

        /* Vismap is different for older version of the pvs data. */
        if (pvsCache->sectorCache.version == PVSVERSION)
        {
            if (PVSVISMAPGETSECTOR(pvsCache->sectorVismap, sectorID))
            {
                visible = TRUE;
            }
            else
            {
                visible = FALSE;
            }
        }
        else
        {
            RwChar             *sectorVismap;

            sectorVismap = (RwChar *) pvsCache->sectorVismap;

            if (PVSVISMAPGETSECTORVER2(sectorVismap, sectorID))
            {
                visible = TRUE;
            }
            else
            {
                visible = FALSE;
            }

        }
    }

    if (visible)
    {
        pvsCache->paccept += worldSector->numPolygons;

        /* Make the rest of the pipe execute */
        RxPacketDispatch(NULL, 0, self);
    }

    /* If we're not visible, we didn't dispatch, so the pipe will unwind
     * (without error) without going beyond this point */
    RWRETURN(TRUE);
}

/**
 * \ingroup rppvs
 * \ref RxNodeDefinitionGetPVSWorldSectorCSL
 * returns a pointer to a node implementing PVS culling in custom pipelines.
 *
 * This nodes removes world sectors from rendering if they are culled by the
 * PVS visilibility map.
 *
 * This node is to be used as early as possible and before the world sector
 * instance nodes. If a world sector is visible, execution continues to
 * the next node otherwise the pipeline is stop at this point.
 *
 * \verbatim
   The node has one outputs.
   The input requirements of this node:

   N/A.

   The characteristics of this node's outputs:

   N/A.
   \endverbatim
 *
 * \return pointer to node for PVS  culling in custom pipelines on success,
 * NULL otherwise
 *
 */
RxNodeDefinition   *
RxNodeDefinitionGetPVSWorldSectorCSL(void)
{
    static RwChar       _PVSData_csl[] = RWSTRING("PVSData.csl");

    static RxClusterDefinition clusterPVSData =
        { _PVSData_csl,
          0,
          0,
          (const RwChar *)NULL };

    /* TODO: can you not have nodes without any clusters of interest?
     *       This cluster is entirely useless... */
    static RxClusterRef gPVSNodeClusters[NUMCLUSTERSOFINTEREST] = {
        {&clusterPVSData, rxCLALLOWABSENT, rxCLRESERVED}
    };

    static RxClusterValidityReq gPVSNodeReqs[NUMCLUSTERSOFINTEREST] = {
        rxCLREQ_DONTWANT,
    };

    static RxClusterValid gPVSNodeValid[NUMCLUSTERSOFINTEREST] = {
        rxCLVALID_VALID,
    };

    static RwChar       _Output[] = RWSTRING("Output");

    static RxOutputSpec gPVSNodeOutputs[NUMOUTPUTS] = {
        {_Output, gPVSNodeValid, rxCLVALID_INVALID}
    };

    static RwChar       _PVSWorldSector_csl[] =
        RWSTRING("PVSWorldSector.csl");

    static RxNodeDefinition nodePVSWorldSectorCSL = { /* */
        _PVSWorldSector_csl,    /* Name */
        {                      /* nodemethods */
         PVSWorldSectorNodeFn, /* +-- nodebody */
         (RxNodeInitFn)NULL,
         (RxNodeTermFn)NULL,
         (RxPipelineNodeInitFn)NULL,
         (RxPipelineNodeTermFn)NULL,
         (RxPipelineNodeConfigFn)NULL,
         (RxConfigMsgHandlerFn)NULL },
        {                      /* Io */
         NUMCLUSTERSOFINTEREST, /* +-- NumClustersOfInterest */
         gPVSNodeClusters,     /* +-- ClustersOfInterest */
         gPVSNodeReqs,         /* +-- InputRequirements */
         NUMOUTPUTS,           /* +-- NumOutputs */
         gPVSNodeOutputs       /* +-- Outputs */
         },
        0,
        (RxNodeDefEditable)FALSE,
        0
    };

    RxNodeDefinition   *result = &nodePVSWorldSectorCSL;

    RWAPIFUNCTION(RWSTRING("RxNodeDefinitionGetPVSWorldSectorCSL"));

    /*RWMESSAGE((RWSTRING("Pipeline II node"))); */

    RWRETURN(result);
}

/****************************************************************************
 * UNUSED CODE
 */

#if (0)

/****************************************************************************/
RxPipeline         *
_rpPVSRxHook(RxPipeline * pipe)
{
    RWFUNCTION(RWSTRING("_rpPVSRxHook"));

    if (pipe != NULL)
    {
        RwUInt32            index;
        RxPipeline         *lpipe;
        RxPipelineNode     *entryNode, *pvsNode;

        /* Lock the pipe. */
        if ((lpipe = RxPipelineLock(pipe)) != NULL)
        {
            /* Check if the PVS node exist in the pipe, if so then do
             * nothing.
             */
            if ((pvsNode = RxPipelineFindNodeByName(lpipe,
                                                    nodePVSWorldSectorCSL.
                                                    name, NULL,
                                                    NULL)) == NULL)
            {
                /* Add the node to the pipeline. */
                if (RxLockedPipeAddFragment(lpipe, &index,
                                            &nodePVSWorldSectorCSL,
                                            NULL))
                {
                    RxNodeOutput        hOutput;

                    /* Reset the entry point.
                     * The PVS node becomes the head and the output goes
                     * into the previous head.
                     */
                    pvsNode = RxPipelineFindNodeByIndex(lpipe, index);
                    hOutput = RxPipelineNodeFindOutputByName(pvsNode,
                                                             "Output");

                    entryNode = RxLockedPipeGetEntryPoint(lpipe);

                    RxLockedPipeAddPath(lpipe, hOutput, entryNode);

                    RxLockedPipeSetEntryPoint(lpipe, pvsNode);
                }
            }

            /* unlock the pipe. */
            RxLockedPipeUnlock(lpipe);
        }
    }

    RWRETURN(pipe);
}

/****************************************************************************/
RpWorldSector      *
_rpPVSSectorRxHook(RpWorldSector * spSect, void *pData)
{
    RWFUNCTION(RWSTRING("_rpPVSSectorRxHook"));

    if (spSect)
    {
        RxPipeline         *pipe;

        /* Get the world sector pipe. */
        RpWorldSectorGetInstancePipeline(spSect, &pipe);

        /* Get the world sector pipe. */
        if (pipe != NULL)
            _rpPVSRxHook(pipe);
    }

    RWRETURN(spSect);
}

/****************************************************************************/
RxPipeline         *
_rpPVSRxUnhook(RxPipeline * pipe)
{
    RWFUNCTION(RWSTRING("_rpPVSRxUnhook"));

    if (pipe != NULL)
    {
        RxPipeline         *lpipe;
        RxPipelineNode     *pvsNode;

        /* Lock the pipe. */
        if ((lpipe = RxPipelineLock(pipe)) != NULL)
        {
            /* Check if the PVS node exist in the pipe, if not then do
             * nothing.
             */
            if ((pvsNode = RxPipelineFindNodeByName(lpipe,
                                                    nodePVSWorldSectorCSL.
                                                    name, NULL,
                                                    NULL)) != NULL)
            {
                /* Remove the node from the pipe. We are assuming the
                 * pipe will clean up any connections to or from this
                 * node.
                 */

                RxLockedPipeDeleteNode(lpipe, pvsNode);
            }

            /* unlock the pipe. */
            RxLockedPipeUnlock(lpipe);
        }
    }

    RWRETURN(pipe);
}

/****************************************************************************/
RpWorldSector      *
_rpPVSSectorRxUnhook(RpWorldSector * spSect, void *pData)
{
    RWFUNCTION(RWSTRING("_rpPVSSectorRxUnhook"));

    if (spSect)
    {
        RxPipeline         *pipe;

        /* Get the world sector pipe. */
        RpWorldSectorGetInstancePipeline(spSect, &pipe);

        if (pipe != NULL)
            _rpPVSRxHook(pipe);
    }

    RWRETURN(spSect);
}

#endif /* UNUSED CODES */
