/* 
 * Interpolating UVs with interpolants generated by clipping 
 * 
 * Copyright (c) Criterion Software Limited
 */
/****************************************************************************
 *                                                                          *
 *  Module  : nodeUVInterp.c                                                *
 *                                                                          *
 *  Purpose : Uses RxInterps generated by the clipper to generate           *
 *            a set of correctly interpolated rxUVs which are copied into   *
 *            the devVerts for resubmission.                                *
 *                                                                          *
 ****************************************************************************/

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

#include "batypes.h"
#include "balibtyp.h"
#include "badebug.h"
#include "bamemory.h"

#include "p2renderstate.h"
#include "p2define.h"
#include "p2resort.h"
#include "p2core.h"
#include "p2heap.h"
#include "p2macros.h"
#include "pip2model.h"
#include "p2stdcls.h"

#include "nodeUVInterp.h"

#if (!defined(DOXYGEN))
static const char rcsid[] __RWUNUSED__ = "@@(#)$Id: nodeUVInterp.c,v 1.60 2001/09/18 12:42:57 iestynb Exp $";
#endif /* (!defined(DOXYGEN)) */


/****************************************************************************
 Local defines
 */


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


/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/*****************************************************************************
 _UVInterpNodePipelineNodeInitFn

 Initialises the private data (sets UV interpolation/overwrite ON)
*/
static RwBool
_UVInterpNodePipelineNodeInitFn(RxPipelineNode *self)
{
    RWFUNCTION(RWSTRING("_UVInterpNodePipelineNodeInitFn"));

    if (self)
    {
        RxNodeUVInterpSettings data;
        
        if (RxRenderStateVectorSetDefaultRenderStateVector(&(data.state)) != NULL)
        {
            /* Multiplicative blending (default for lightmaps :o) ) */
            data.state.SrcBlend  = rwBLENDZERO;
            data.state.DestBlend = rwBLENDSRCCOLOR;
            /* NOTE: even though we don't *use* vertex alpha, we need to enable "VertexAlpha"
               on most cards in order to use these non-standard blending modes.
               It really signifies "we need to read from the framebuffer", not
               "vertices have alpha != 255"  */
            data.state.Flags        |= rxRENDERSTATEFLAG_VERTEXALPHAENABLE;
            data.state.TextureRaster = (RwRaster *) NULL;
            
            *(RxNodeUVInterpSettings *)self->privateData = data;
            
            RWRETURN(TRUE);
        }
        MESSAGE("Failed to initialise default UV pass render-state");
    }
    RWRETURN(FALSE);
}

/*****************************************************************************
 UVInterpNode

 Uses interpolation data generated by the clipper to quickly generate another
 pass of correctly interpolated rxUVs which are then copied into the devVerts
 for resubmission.
*/
static RwBool
UVInterpNode(RxPipelineNodeInstance * self,
             const RxPipelineNodeParam * __RWUNUSED__ params)
{
    RxNodeUVInterpSettings *uvInterpData;
    RxPacket             *packet;
    RxCluster            *devVerts;
    RxCluster            *renderState;
    RxCluster            *rxInterps;
    RxCluster            *rxUVs;
    RwUInt32              numInterpolants;
    RwInt32               interpolantsStride;
    RwInt32               devVertsStride, uvsStride;
    RxInterp             *interpolant;
    RxScrSpace2DVertex   *currentDevVert;
    RxRenderStateVector  *rsvp;
    RxUV                 *currentUV,  *parentUV1,         *parentUV2;
    RxVertexIndex         currentVert, currentInterpolant, nextInterpedVert;
    RwReal                recipZ, u1, u2, U, v1, v2, V;

    RWFUNCTION(RWSTRING("UVInterpNode"));

    uvInterpData = (RxNodeUVInterpSettings *) self->privateData;
    RWASSERT(NULL != uvInterpData);

    packet = (RxPacket *)RxPacketFetch(self);
    RWASSERT(NULL != packet);

    if (uvInterpData->uvInterpOn == FALSE)
    {
        /* Output the packet to the second output of this Node */
        RxPacketDispatch(packet, 1, self);

        RWRETURN(TRUE);
    }

    /* TODO: should use MeshState here (not devverts->numUsed!!)
     * also use it to test ClipFlagsAnd to avoid interpolation
     * and just do copying into devverts (worth it as opposed to
     * first loop disappears with "interps->numUsed == 0" ?
     * Yeah, you swap meshstate (more standard) for interpolants
     * in early-out cases) */

    devVerts = RxClusterLockWrite(packet, 0, self);
    RWASSERT((devVerts != NULL) && (devVerts->numUsed > 0));
    renderState = RxClusterLockWrite(packet, 1, self);
    RWASSERT(renderState != NULL);
    /* If interpolants are absent or contain no data, no triangles were clipped */
    rxInterps = RxClusterLockRead( packet, 2);
    rxUVs = RxClusterLockRead( packet, 3);
    RWASSERT((rxUVs != NULL) && (rxUVs->numUsed > 0));

    if (renderState->numAlloced <= 0 )
    {
        renderState = RxClusterInitializeData(
            renderState, 1, sizeof(RxRenderStateVector));
        RWASSERT(NULL != renderState);
    }

    rsvp = RxClusterGetCursorData(renderState, RxRenderStateVector);
    RWASSERT(NULL != rsvp);

    /* Set appropriate rendering modes */
    *rsvp = uvInterpData->state;
    renderState->numUsed = 1;

    numInterpolants = 0;
    interpolantsStride = 0;
    if ((rxInterps != NULL) && (rxInterps->numUsed > 0))
    {
        numInterpolants = rxInterps->numUsed;
        interpolantsStride = rxInterps->stride;
        /* We'll need to expand the array */
        rxUVs = RxClusterLockWrite(packet, 3, self);
        RWASSERT(NULL != rxUVs);
        /* Create an extra RxUV for every clipping-generated vertex.
         * This is necessary since some final rxUVs are interpolated 
         * from the rxUVs of clipping-generated vertices. */
        rxUVs = RxClusterResizeData(rxUVs, devVerts->numUsed);
        RWASSERT(NULL != rxUVs);
    }

    devVertsStride = devVerts->stride;
    uvsStride = rxUVs->stride;

    /* We assume the UVs have been set up for the original number
       of vertices. It would be a pain to check it... you'll just
       get junk if it's not the case */

    /* Copy rxUVs in to the devVerts, 
     * interpolating those that need to be interpolated */
    currentVert = 0;
    for (currentInterpolant = 0;
         currentInterpolant < numInterpolants;
         currentInterpolant++)
    {
        interpolant = RxClusterGetCursorData(rxInterps, RxInterp);
        nextInterpedVert = interpolant->originalVert;
        while (currentVert < nextInterpedVert)
        {
            currentUV = RxClusterGetCursorData(rxUVs, RxUV);
            currentDevVert =
                RxClusterGetCursorData(devVerts, RxScrSpace2DVertex);
            recipZ =
                RwIm2DVertexGetRecipCameraZ(currentDevVert);
            RwIm2DVertexSetU(currentDevVert,
                             currentUV->u, recipZ);
            RwIm2DVertexSetV(currentDevVert,
                             currentUV->v, recipZ);
            currentVert++;
            RxClusterIncCursorByStride(rxUVs,    uvsStride);
            RxClusterIncCursorByStride(devVerts, devVertsStride);
        }
        currentUV = RxClusterGetCursorData(rxUVs, RxUV);
        currentDevVert = RxClusterGetCursorData(devVerts, RxScrSpace2DVertex);
        parentUV1 =
            RxClusterGetIndexedData(rxUVs, RxUV,
                                    interpolant->parentVert1);
        parentUV2 =
            RxClusterGetIndexedData(rxUVs, RxUV,
                                    interpolant->parentVert2);
        recipZ =
            RwIm2DVertexGetRecipCameraZ(currentDevVert);
        u1 = parentUV1->u;
        u2 = parentUV2->u;
        U = u1 + interpolant->interp * (u2 - u1);
        RwIm2DVertexSetU(currentDevVert, U, recipZ);
        currentUV->u = U; /* Must be done in case any subsequent vertices interpolate from this one! */
        v1 = parentUV1->v;
        v2 = parentUV2->v;
        V = v1 + interpolant->interp * (v2 - v1);
        RwIm2DVertexSetV(currentDevVert, V, recipZ);
        currentUV->v = V;
        currentVert++;
        RxClusterIncCursorByStride(rxUVs,     uvsStride);
        RxClusterIncCursorByStride(devVerts,  devVertsStride);
        RxClusterIncCursorByStride(rxInterps, interpolantsStride);
    }
    while (currentVert < devVerts->numUsed)
    {
        currentUV = RxClusterGetCursorData(rxUVs, RxUV);
        currentDevVert = RxClusterGetCursorData(devVerts, RxScrSpace2DVertex);
        recipZ =
            RwIm2DVertexGetRecipCameraZ(currentDevVert);
        RwIm2DVertexSetU(currentDevVert, currentUV->u,
                         recipZ);
        RwIm2DVertexSetV(currentDevVert, currentUV->v,
                         recipZ);
        currentVert++;
        RxClusterIncCursorByStride(rxUVs,    uvsStride);
        RxClusterIncCursorByStride(devVerts, devVertsStride);
    }

    /* Output the packet to the first output of this Node */
    RxPacketDispatch(packet, 0, self);

    RWRETURN(TRUE);
}

/**
 * \ingroup rwcoregeneric
 * \ref RxNodeDefinitionGetUVInterp returns a pointer to a node to fill
 * screen-space vertices with a new set of correctly clipped texture
 * coordinates.
 *
 * This node takes an \ref RxCluster holding \ref RxUV's, which is assumed to
 * be parallel to the packet's screen-space vertices as they were PRIOR to
 * clipping, clips them as if they had been in the vertices during clipping
 * and then overwrites the UVs of the screen-space vertices with the
 * resulting values.
 *
 * This node can optionally make use of an \ref RxCluster holding \ref RxInterp
 * values in order to perform its task. This contains information generated
 * by the clipper which allows the node to interpolate subsequent sets of
 * texture coordinates. For every vertex generated by the clipper, there is
 * an associated \ref RxInterp value which holds the index of that vertex in
 * the vertex array (hence this process is sensitive to changes in the order
 * of the vertices inbetween clipping and interpolation), its two 'parent'
 * vertices (it was generated by clipping the edge between these two vertices)
 * and the interpolation value (where it is on the original edge between its
 * parent vertices - if it is zero, it is at the first parent vertex, if it
 * is one it is at the other end and values inbetween zero and one are
 * inbetween, linearly distributed). This process will cause the number of
 * elements in the \ref RxUV cluster to increase by the number of vertices
 * generated by clipping.
 *
 * If this node does not receive any \ref RxInterp values then it will simply
 * copy the texture coordinates straight into the screen-space vertices (this
 * case occurs when none of the triangles in the current mesh have been
 * clipped).
 *
 * When the packet is output from this node, after interpolation, the
 * \ref RxUV cluster's values will be valid (i.e in synch with the post-
 * clipping screen-space vertex cluster that they have been copied into)
 * and so are safe to inspect or reuse further down the pipeline. Note that
 * the texture coordinates of offscreen screen-space vertices were never
 * valid and still aren't.
 *
 * This node has a private data struct. This contains a boolean 'uvInterpOn'
 * which if set to FALSE will cause the node to effectively be skipped - it
 * will just pass packets on to its second output and do nothing else. This
 * second output will not invalidate the \ref RxUV or \ref RxInterp clusters,
 * because there may be subsequent nodes (e.g an RGBA interpolation node as
 * returned by \ref RxNodeDefinitionGetRGBAInterp) that need them. The private
 * data struct also contains an \ref RxRenderStateVector which is used to
 * overwrite the packet's renderstate, so that the renderstate of each
 * submitted pass of triangles can be set to whatever the user desires
 * (usually in terms of texture and blending modes).
 *
 * \ref RxPipelineNodeReplaceCluster may be used to make this node interpolate
 * any one of many \ref RxUV clusters, such that multiple instances of this
 * node within a single pipeline can enable you to render objects with more
 * than two textured passes.
 *
 * \verbatim
   The node has two outputs. Packets are sent unmodified to the second output
   if the 'uvInterpOn' RwBool in the node's private data is set to FALSE.
   The input requirements of this node:
  
   RxClScrSpace2DVertices - required
   RxClRenderState        - required
   RxClInterpolants       - optional
   RxClUVs                - required
  
   The characteristics of this node's first output:
  
   RxClScrSpace2DVertices - valid
   RxClRenderState        - valid
   RxClInterpolants       - no change
   RxClUVs                - valid
  
   The characteristics of this node's second output:
  
   RxClScrSpace2DVertices - no change
   RxClRenderState        - no change
   RxClInterpolants       - no change
   RxClUVs                - no change
  
   This node has a private data struct of the following form:
  
    struct RxNodeUVInterpSettings
   {
       RwBool              uvInterpOn;
       RxRenderStateVector state;
   };
  
   The RxInterp struct has the following form:
  
   struct RxInterp
   {
       RxVertexIndex originalVert;
       RxVertexIndex parentVert1, parentVert2;
       RwReal        interp;
   }
   \endverbatim
 *
 * \return pointer to a node to fill screen-space vertices with a new set
 * of correctly clipped rxUVs on success, NULL otherwise
 *
 * \see RxNodeDefinitionGetRGBAInterp
 * \see RxNodeDefinitionGetClipLine
 * \see RxNodeDefinitionGetClipTriangle
 * \see RxNodeDefinitionGetCullTriangle
 * \see RxNodeDefinitionGetImmInstance
 * \see RxNodeDefinitionGetImmMangleLineIndices
 * \see RxNodeDefinitionGetImmMangleTriangleIndices
 * \see RxNodeDefinitionGetImmRenderSetup
 * \see RxNodeDefinitionGetScatter
 * \see RxNodeDefinitionGetSubmitLine
 * \see RxNodeDefinitionGetSubmitTriangle
 * \see RxNodeDefinitionGetTransform
 *
 */

RxNodeDefinition *
RxNodeDefinitionGetUVInterp(void) 
{

    static RxClusterRef N1clofinterest[] = { /* */
        {&RxClScrSpace2DVertices, rxCLALLOWABSENT, rxCLRESERVED},
        {&RxClRenderState, rxCLALLOWABSENT, rxCLRESERVED},
        {&RxClInterpolants, rxCLALLOWABSENT, rxCLRESERVED},
        {&RxClUVs, rxCLALLOWABSENT, rxCLRESERVED}
    };

#define NUMCLUSTERSOFINTEREST \
        ((sizeof(N1clofinterest))/(sizeof(N1clofinterest[0])))


    static RxClusterValidityReq N1inputreqs[NUMCLUSTERSOFINTEREST] = { /* */
        rxCLREQ_REQUIRED,
        rxCLREQ_REQUIRED,
        rxCLREQ_OPTIONAL,
        rxCLREQ_REQUIRED
    };

    static RxClusterValid N1outclUVsOut[NUMCLUSTERSOFINTEREST] = { /* */
        rxCLVALID_VALID,
        rxCLVALID_VALID,
        rxCLVALID_NOCHANGE,
        rxCLVALID_VALID
    };

    static RxClusterValid N1outclPassThrough[NUMCLUSTERSOFINTEREST] = { /* */
        rxCLVALID_NOCHANGE,
        rxCLVALID_NOCHANGE,
        rxCLVALID_NOCHANGE,
        rxCLVALID_NOCHANGE
    };

    static RwChar _UVsOut[] = RWSTRING("UVsOut");
    static RwChar _PassThrough[] = RWSTRING("PassThrough");

    static RxOutputSpec N1outputs[] = { /* */
        {_UVsOut,      N1outclUVsOut,       rxCLVALID_NOCHANGE},
        {_PassThrough, N1outclPassThrough,  rxCLVALID_NOCHANGE}
    };

#define NUMOUTPUTS \
        ((sizeof(N1outputs))/(sizeof(N1outputs[0])))

    static RwChar _UVInterp_csl[] = RWSTRING("UVInterp.csl");

    static RxNodeDefinition nodeUVInterpCSL =
    {
        _UVInterp_csl,
        {UVInterpNode,
         (RxNodeInitFn)NULL,
         (RxNodeTermFn)NULL,
         _UVInterpNodePipelineNodeInitFn,
         (RxPipelineNodeTermFn) NULL, 
         (RxPipelineNodeConfigFn) NULL,
         (RxConfigMsgHandlerFn) NULL},
        {NUMCLUSTERSOFINTEREST,
         N1clofinterest,
         N1inputreqs,
         NUMOUTPUTS,
         N1outputs},
        /* This is probably a useful/cheap place to set renderstate for a pass */
        sizeof(RxNodeUVInterpSettings),
        (RxNodeDefEditable)FALSE,
        0
    };

    RxNodeDefinition *result = &nodeUVInterpCSL;

    RWAPIFUNCTION(RWSTRING("RxNodeDefinitionGetUVInterp"));

    /*RWMESSAGE((RWSTRING("result %p"), result));*/

    RWRETURN(result);
    
}

