/****************************************************************************
 *                                                                          *
 *  Module  :   patgemsky.c                                                 *
 *                                                                          *
 *  Purpose :   PSX2 multipass patch pipe.                                  *
 *                                                                          *
 ****************************************************************************/

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

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

/* RW includes */
#include "rpplugin.h"
#include <rpdbgerr.h>
#include <rwcore.h>
#include <rpworld.h>
#include <rpmatfx.h>

#include "rppatch310.h"
#include "patchvar.h"
#include "patchgen.h"

#include "patchsky.h"
#include "patgemsky.h"

#include "GEMstddata.h"

#if (!defined(DOXYGEN))
static const char   rcsid[] __RWUNUSED__ =
    "@@@@(#)$Id: patgemsky.c,v 1.15 2001/09/24 12:39:26 johns Exp $";
#endif /* (!defined(DOXYGEN)) */

/****************************************************************************
 Local Defines
 */

#define VIFCMD_UNPACK (0x6cl << 24)
#define VIFCMD_CYCLE (0x01l << 24)
#define VIFCMD_DIRECT (0x50l << 24)
#define VIFCMD_NOP (0x00l << 24)

/****************************************************************************
 Globals (across program)
 */

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

/*----------------------------------------------------------------------*/

/*
 *
 *
 *
 */
static              RwBool
PatchNodeGEMPS2ManagerInstanceCallBack(void **clusterData,
                                       RwUInt32 __RWUNUSED__
                                       numClusters)
{
    RwBool              result = TRUE;
    RxPS2Mesh          *ps2Mesh = (RxPS2Mesh *) (clusterData[0]);
    RxPS2DMASessionRecord *dmaSessionRec = (RxPS2DMASessionRecord *)
        (clusterData[1]);
    RpAtomic           *atomic;
    RpMaterial         *mat;
    RwTexture          *emapTexture, *texture;
    RwRaster           *emapRaster, *texRaster;
    RwMatrix            emapMatrix;
    RwBool              blendmode;
    RwUInt32            fogCol;
    RwUInt64            fogSwitch = 0x0l;
    RwReal              emapCooef, emapparams[4];
    RwInt32             lod;
    RwReal              dx1, dx2, dx3;
    u_long128          *u;
    RwUInt64            prim = 0x0l;
    RwUInt32            rasmsb = 0;
    RwUInt32            raslsb = 0;
    RwSkySplitBits128   packed;

    /* RwUInt64            count64, convert64, subdiv64, tessel64; */

    RWFUNCTION(RWSTRING("PatchNodeGEMPS2ManagerInstanceCallBack"));

    atomic = dmaSessionRec->sourceObject.atomic;

    lod = rpPatchGlobals.atomicLODCallBack(atomic);

    dx1 = ((RwReal) 1) / (RwReal) (lod - 1);
    dx2 = dx1 * dx1;
    dx3 = dx2 * dx1;

    RwMatrixInvert(&rpPatchGlobals.invCamMatrix,
                   RwFrameGetLTM(rpPatchGlobals.camFrame));

    RwMatrixMultiply(&emapMatrix,
                     &rpPatchGlobals.invCamMatrix,
                     RwFrameGetLTM(RpAtomicGetFrame(atomic)));

    /*--------------- get the textures ------------*/
    mat = ps2Mesh->mesh->material;

    emapTexture = RpMatFXMaterialGetEnvMapTexture(mat);
    emapRaster = RwTextureGetRaster(emapTexture);
    emapCooef = RpMatFXMaterialGetEnvMapCoefficient(mat);

    texture = RpMaterialGetTexture(mat);
    texRaster = RwTextureGetRaster(texture);

    blendmode = RpMatFXMaterialGetEnvMapFrameBufferAlpha(mat);

    emapparams[0] = rpPatchGlobals.emapParams[0];
    emapparams[1] = rpPatchGlobals.emapParams[1];
    emapparams[2] = rpPatchGlobals.emapParams[2];
    emapparams[3] = rpPatchGlobals.emapParams[3];

    /*--------------- set the fog ----------------*/
    if (dmaSessionRec->transType & 1)
    {
        /*--- 2nd context fog activated ------*/
        fogSwitch = 0x1l;
    }

    RwRenderStateGet(rwRENDERSTATEFOGCOLOR, &fogCol);

        /*--- Now upload the envMap texture -----*/
    skyTexCacheAccessRaster(emapRaster, TRUE);

    sweFinaliseOpenLocalPkt(SWE_PKT_DMA_MODE_CHAIN_TTE |
                            SWE_PKT_LOCAL |
                            SWE_PKT_VU1 |
                            SWE_PKT_CIRCALLOC,
                            -(RPPATCHGEMNUMQWORD + 2));
    /* 19 QW */

    if (!sweLocalPacket)
    {
        return (FALSE);
    }
        /*------- Transfer Information ----------------------*/
    packed.field_64[1] = ((((0x6cl << 24) | (12 << 16) | /* 12 QW */
                            ((long) (pipeASymbStaticDataStart))) << 32)
                          | ((1l << 24) | (4 << 8) | (4)));
    packed.field_64[0] = ((1l << 28) | (RPPATCHGEMNUMQWORD)); /* 17 QW */
    SWEADDCONTFAST(packed.field128);

    /*------- Patch tesselation -------------------------*/

    packed.field_32[3].nUInt = lod;
    packed.field_32[2].nReal = dx1;
    packed.field_32[1].nReal = dx2;
    packed.field_32[0].nReal = dx3;
    SWEADDCONTFAST(packed.field128);

        /*------- Dark Fog Tag / Tex0-2 Info ----------------*/
    packed.field_64[1] = /* A+D */ (0xel << (64 - 64));
    packed.field_64[0] = ( /* NLOOP */ 2l |
                          /* EOP   */ (1l << 15) |
                          /* PRE   */ (0l << 46) |
                          /* FLG   */ (0l << 58) |
                          /* NREG  */ (1l << 60));
    SWEADDCONTFAST(packed.field128);

    packed.field_64[1] = GS_FOGCOL;
    packed.field_64[0] = 0x000000l;
    SWEADDCONTFAST(packed.field128);

    skyTexGetTex0(emapRaster, &rasmsb, &raslsb);

    packed.field_64[1] = GS_TEX0_2;
    packed.field_64[0] = (((RwUInt64) rasmsb << 32) | raslsb);
    SWEADDCONTFAST(packed.field128);

    /*------- Real Fog Tag / Tex0-1 Info ----------------*/
    packed.field_64[1] = /* A+D */ (0xel << (64 - 64));
    packed.field_64[0] = ( /* NLOOP */ 2l |
                          /* EOP   */ (1l << 15) |
                          /* PRE   */ (0l << 46) |
                          /* FLG   */ (0l << 58) |
                          /* NREG  */ (1l << 60));
    SWEADDCONTFAST(packed.field128);

    packed.field_64[1] = GS_FOGCOL;
    packed.field_64[0] = fogCol;
    SWEADDCONTFAST(packed.field128);

    skyTexGetTex0(texRaster, &rasmsb, &raslsb);
    packed.field_64[1] = GS_TEX0_1;
    packed.field_64[0] = (((RwUInt64) rasmsb << 32) | raslsb);
    SWEADDCONTFAST(packed.field128);

    /*------- Context 2 Env-Map GifTag ------------------*/
    prim = ( /* fix  */ 0x0l << 10 |
            /* ctxt */ 0x1l << 9 |
            /* fst  */ 0x0l << 8 |
            /* aa1  */ 0x0l << 7 |
            /* abe  */ 0x1l << 6 |
            /* fge  */ fogSwitch << 5 |
            /* tme  */ 0x1l << 4 |
            /* iip  */ 0x1l << 3 |
            /* prim */ 0x4l << 0);

    packed.field_64[1] = 0x412l; /* registers */
    packed.field_64[0] = ( /* regs */ 0x3l << (60 - 32) |
                          /* flg  */ 0x0l << (58 - 32) |
                          /* prim */ prim << (47 - 32) |
                          /* pre  */ 0x1l << (46 - 32)) << 32;
    SWEADDCONTFAST(packed.field128);

        /*------------ Env Map matrix -----------------------*/
    u = (u_long128 *) & (emapMatrix.right);
    packed.field128 = *u;
    SWEADDCONTFAST(packed.field128);
    u = (u_long128 *) & (emapMatrix.up);
    packed.field128 = *u;
    SWEADDCONTFAST(packed.field128);

    packed.field_32[3].nReal = emapCooef;
    packed.field_32[2].nReal = emapMatrix.at.z;
    packed.field_32[1].nReal = emapMatrix.at.y;
    packed.field_32[0].nReal = emapMatrix.at.x;

    SWEADDCONTFAST(packed.field128);

        /*------------ Environment Map Datas ----------------*/
    packed.field_64[1] = *(const RwUInt64 *) (emapparams + 2);
    packed.field_64[0] = *(const RwUInt64 *) (emapparams);
    SWEADDCONTFAST(packed.field128);

        /*------------ Padding ------------------------------*/
    packed.field_64[1] = ((VIFCMD_NOP) | ((VIFCMD_DIRECT | 4) << 32));
    packed.field_64[0] = (VIFCMD_NOP | (VIFCMD_NOP << 32));
    SWEADDCONTFAST(packed.field128);

        /*------------- Second Context Registers ------------*/
    packed.field_64[1] = /* A+D */ (0xel << (64 - 64));
    packed.field_64[0] = ( /* NLOOP */ 3l |
                          /* EOP   */ (1l << 15) |
                          /* PRE   */ (0l << 46) |
                          /* FLG   */ (0l << 58) |
                          /* NREG  */ (1l << 60));
    SWEADDCONTFAST(packed.field128);

    packed.field_64[1] = GS_ALPHA_2;
    packed.field_64[0] = (blendmode) ? 0x8000000058l : 0x8000000068l;
    SWEADDCONTFAST(packed.field128);

    packed.field_64[1] = GS_CLAMP_2;
    packed.field_64[0] = 0x0l;
    SWEADDCONTFAST(packed.field128);

    packed.field_64[1] = GS_TEST_2;
    packed.field_64[0] = 0x50002l;
    SWEADDCONTFAST(packed.field128);

        /*------- Terminate the DMA with an interrupt -------*/
    packed.field_64[1] = 0x0l;
    packed.field_64[0] = (0xfl << 28);
    SWEADDCONTFAST(packed.field128);

        /*---------------------------------------------------*/
    sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);

    RWRETURN(result);
}

static RxPipelineNode *
PatchGEMPipelineInitialize(RxLockedPipe * lpipe,
                           RxNodeDefinition * manager)
{
    RxPipelineNode     *status;
    RxPipelineNode     *plnode;

    RWFUNCTION(RWSTRING("PatchGEMPipelineInitialize"));

    plnode = RxPipelineFindNodeByName(lpipe,
                                      manager->name,
                                      (RxPipelineNode *) NULL,
                                      (RwInt32 *) NULL);
    RWASSERT(plnode != NULL);

    RxPipelineNodePS2ManagerSetVIFOffset(plnode, pipeASymbVIFOffset);
    status = RxPipelineNodePS2ManagerSetVU1CodeArray(plnode,
                                                     rpPatchGlobals.sky.
                                                     GEMvucode);
    RxPipelineNodePS2ManagerSetInstanceCallBack(plnode,
                                                PatchNodeGEMPS2ManagerInstanceCallBack);

    RWRETURN(status);
}

/*
 *
 *
 *
 */
RxPipeline         *
_rpPatchGEMPipelineCreate(void)
{
    RwInt32             i;
    RxPipeline         *pipe;
    void              **GEMvucode;

    RWFUNCTION(RWSTRING("_rpPatchGEMPipelineCreate"));

    /*--------------------- Clean The Vu-code Array --------------------*/
    for (i = 0; i < 16; i++)
    {
        rpPatchGlobals.sky.GEMvucode[i] = &FP_nulltransform;
    }

    /*--------------------- Set Patch Vu codes -------------------------*/

    GEMvucode = rpPatchGlobals.sky.GEMvucode;

    GEMvucode[TRANSNFOG | TRANSNCL | TRANSSTRIP | TRANSPER] =
        &FP_GEMBpatch;
    GEMvucode[TRANSFOG | TRANSNCL | TRANSSTRIP | TRANSPER] =
        &FP_GEMBpatch;
    GEMvucode[TRANSNFOG | TRANSCLIP | TRANSSTRIP | TRANSPER] =
        &FP_GEMBpatch;
    GEMvucode[TRANSFOG | TRANSCLIP | TRANSSTRIP | TRANSPER] =
        &FP_GEMBpatch;

    pipe = RxPipelineCreate();

    if (pipe)
    {
        RxLockedPipe       *lpipe = (RxLockedPipe *) NULL;

        pipe->pluginId = rwID_PATCHPLUGIN;

        lpipe = RxPipelineLock(pipe);

        if (NULL != lpipe)
        {
            RxPipelineNode     *plnode, *status;
            RxNodeDefinition   *manager;

            manager = RxNodeDefinitionGetPS2Manager(rxOBJTYPE_ATOMIC);
            RWASSERT(NULL != manager);

            lpipe = RxLockedPipeAddFragment(lpipe,
                                            (RwUInt32 *) NULL,
                                            manager, NULL);
            RWASSERT(NULL != lpipe);

            plnode = RxPipelineFindNodeByName(lpipe,
                                              manager->name,
                                              (RxPipelineNode *) NULL,
                                              (RwInt32 *) NULL);
            RWASSERT(plnode != NULL);

            status =
                RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                        &RxClPS2xyz,
                                                        CL_XYZ);
            RWASSERT(NULL != status);

            status =
                RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                        &RxClPS2uv,
                                                        CL_UV);
            RWASSERT(NULL != status);

            status =
                RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                        &RxClPS2rgba,
                                                        CL_RGBA);
            RWASSERT(NULL != status);

            status =
                RxPipelineNodePS2ManagerGenerateCluster(plnode,
                                                        &RxClPS2normal,
                                                        CL_NORMAL);
            RWASSERT(NULL != status);

            RxPipelineNodePS2ManagerSetPointListVUBufferSize(plnode,
                                                             pipeASymbStrideOfInputCluster,
                                                             pipeASymbTSVertexCount);

            lpipe = RxLockedPipeUnlock(lpipe);

            if (lpipe != NULL)
                status = PatchGEMPipelineInitialize(lpipe, manager);

        }
    }

    RWRETURN(pipe);
}
