/****************************************************************************
 *
 * This file is a product of Criterion Software Ltd.
 *
 * This file is provided as is with no warranties of any kind and is
 * provided without any obligation on Criterion Software Ltd.
 * or Canon Inc. to assist in its use or modification.
 *
 * Criterion Software Ltd. and Canon Inc. will not, under any
 * circumstances, be liable for any lost revenue or other damages
 * arising from the use of this file.
 *
 * Copyright (c) 2001 Criterion Software Ltd.
 * All Rights Reserved.
 *
 */
/****************************************************************************
 *
 * skycfx.c
 *
 * Copyright (C) 2001 Criterion Technologies.
 *
 * Original author: Alexandre Hadjadj
 * Reviewed by:
 *
 * Purpose: To illustrate the possibility of creating a custom VU pipe
 *
 ****************************************************************************/

#include "rwcore.h"
#include "rpworld.h"
#include "skeleton.h"

#include "cfx.h"
#include "skycfx.h"
#include "stddata.h"

#define VIFCMD_DIRECT (0x50l << 24)
#define VIFCMD_NOP (0x00l << 24)

/*
 *****************************************************************************
 */
static RwBool
SkyCustomFXInstanceCallBack(void **clusterData,
                            RwUInt32 numClusters)
{
    RxPS2DMASessionRecord *dmaSesRec = NULL;
    RwUInt32            primSwitch = 0x4l;
    RwUInt32            fogSwitch = 0x0l;
    RwRaster           *customRaster = NULL;
    RwUInt64            tmp, tmp1;
    u_long128           ltmp = 0;
    RwUInt32            prim = 0x0l;

    dmaSesRec = (RxPS2DMASessionRecord *) clusterData[1];

    /* Is our atomic tri-listed ? */
    if (dmaSesRec->transType & 4)
    {
        /* Tri-list */
        primSwitch = 0x3l;
    }

    /* Is fog active */
    if (dmaSesRec->transType & 1)
    {
        /* Fog active */
        fogSwitch = 0x1l;
    }

    customRaster = RwTextureGetRaster(CustomTexture);

    /* Upload Custom Texture */
    skyTexCacheAccessRaster(customRaster, TRUE);

    /* Extend DMA Packet */
    sweFinaliseOpenLocalPkt(SWE_PKT_DMA_MODE_CHAIN_TTE |
                            SWE_PKT_LOCAL |
                            SWE_PKT_VU1 |
                            SWE_PKT_CIRCALLOC,
                            -8 - 2); /* We send 8 QW in DMA transfer for RW
                                      * standard stuff + 2 for CustomFX */

    /* Append data to the packet */

    /* 6 QW are effective + 2 for CustomFX */
    tmp = ((1l << 28) | (6 + 2));

    tmp1 = ((((0x6cl << 24) | ((1l + 2l) << 16) | /* 1+2 are in Upper memory */
              ((long) (pipeASymbStaticDataStart))) << 32) |
            ((1l << 24) | (4 << 8) | (4)));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /*
     * Setting the second graphic context for CustomFX
     * activate fog for second context and primitive type
     * according to the previous read of the DMA Session Record
     */
    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 */ primSwitch << 0);
    tmp = ( /* regs */ 0x3l << (60 - 32) |
            /* flg  */ 0x0l << (58 - 32) |
            /* prim */ prim << (47 - 32) |
            /* pre  */ 0x1l << (46 - 32)) << 32;
    tmp1 = 0x412l;             /* registers */
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* CustomFX Datas */

    /* Rotation Matrix Row 0 */
    tmp = *(RwUInt64 *) (CFXRotMat0);
    tmp1 = *(RwUInt64 *) (CFXRotMat1);
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* UV Translation */
    tmp = *(RwUInt64 *) (CFXTrans);
    /* UV Scale */
    tmp1 = *(RwUInt64 *) (CFXScale);
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* Padding */
    tmp = VIFCMD_NOP | (VIFCMD_NOP << 32);
    tmp1 = (VIFCMD_NOP) | ((VIFCMD_DIRECT | 4) << 32);
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* Setting the second context registers */
    tmp = /* NLOOP */ 3l       /* Second Context settings */
        | /* EOP */ (1l << 15)
        | /* PRE */ (0l << 46)
        | /* FLG */ (0l << 58)
        | /* NREG */ (1l << 60);
    tmp1 = /* A+D */ (0xel << (64 - 64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);
    tmp = 0x8000000044l;       /* Blend mode is Cf = Cd x (1-As) + As x Cs */
    tmp1 = GS_ALPHA_2;         /* Additive with Alpha FB combiner */
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);
    tmp = 0x0;
    tmp1 = GS_CLAMP_2;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);
    tmp = 0x50002l;            /* No alpha test */
    tmp1 = GS_TEST_2;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* Terminate the DMA with an interrupt */
    tmp = (0xfl << 28);
    MAKE128(ltmp, 0l, tmp);
    SWEADDCONTFAST(ltmp);

    /* finalize packet */
    sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);

    return TRUE;
}


/*
 *****************************************************************************
 */
static RxLockedPipe *
SkyCustomFXManagerNodeSetup(RxLockedPipe * lockedPipe)
{
    static void        *cfxVUCodeArray[VU1CODEARRAYSIZE] =
    {   &CustomFXNT, &CustomFXNT, &CustomFXNT, &CustomFXNT,
        &CustomFXNT, &CustomFXNT, &CustomFXNT, &CustomFXNT,
        &CustomFXNT, &CustomFXNT, &CustomFXNT, &CustomFXNT,
        &CustomFXNT, &CustomFXNT, &CustomFXNT, &CustomFXNT
    };
    RxNodeDefinition   *manager = NULL;
    RxPipelineNode     *managerNode = NULL;

    /* Get the Atomic PS2 Manager Node */
    manager = RxNodeDefinitionGetPS2Manager(rxOBJTYPE_ATOMIC);

    if (manager == NULL)
    {
        RsErrorMessage(RWSTRING("Error : Could not get PS2 Manager."));
        return NULL;
    }

    /* Add PS2 Manager fragment to the new pipeline */
    RxLockedPipeAddFragment(lockedPipe,
                            (RwUInt32 *) NULL,
                            manager, (RxNodeDefinition *) NULL);

    /* Get a pointer to the new pipe's manager node */
    managerNode = RxPipelineFindNodeByName(lockedPipe,
                                           manager->name,
                                           (RxPipelineNode *) NULL,
                                           (RwInt32 *) NULL);

    /* Force the manager node to create those clusters */
    RxPipelineNodePS2ManagerGenerateCluster(managerNode,
                                            &RxClPS2xyz, CL_XYZ);

    RxPipelineNodePS2ManagerGenerateCluster(managerNode,
                                            &RxClPS2uv, CL_UV);

    RxPipelineNodePS2ManagerGenerateCluster(managerNode,
                                            &RxClPS2rgba, CL_RGBA);

    RxPipelineNodePS2ManagerGenerateCluster(managerNode,
                                            &RxClPS2normal, CL_NORMAL);

    /* Set the VU vertex buffer size, and stride */
    RxPipelineNodePS2ManagerSetVUBufferSizes(managerNode,
                                             pipeASymbStrideOfInputCluster,
                                             pipeASymbTSVertexCount,
                                             pipeASymbTLTriCount);

    /* Unlock the pipe! */
    RxLockedPipeUnlock(lockedPipe);

    /* Allways re-find nodes after an unlock */
    managerNode = RxPipelineFindNodeByName(lockedPipe,
                                           manager->name,
                                           (RxPipelineNode *) NULL,
                                           (RwInt32 *) NULL);

    /* Set VIF offset */
    RxPipelineNodePS2ManagerSetVIFOffset(managerNode,
                                         pipeASymbVIFOffset);

    /* Set Instance Callback */
    RxPipelineNodePS2ManagerSetInstanceCallBack(managerNode,
                                                SkyCustomFXInstanceCallBack);

    /* Set the VU code array to point to our Custom VU pipeline */
    cfxVUCodeArray[TRANSNFOG|TRANSNCL |TRANSLIST |TRANSPER] = &CustomFXPRS;
    cfxVUCodeArray[TRANSFOG |TRANSNCL |TRANSLIST |TRANSPER] = &CustomFXPRS;
    cfxVUCodeArray[TRANSNFOG|TRANSNCL |TRANSSTRIP|TRANSPER] = &CustomFXPRS;
    cfxVUCodeArray[TRANSFOG |TRANSNCL |TRANSSTRIP|TRANSPER] = &CustomFXPRS;
    cfxVUCodeArray[TRANSNFOG|TRANSCLIP|TRANSLIST |TRANSPER] = &CustomFXPRS;
    cfxVUCodeArray[TRANSFOG |TRANSCLIP|TRANSLIST |TRANSPER] = &CustomFXPRS;
    cfxVUCodeArray[TRANSNFOG|TRANSCLIP|TRANSSTRIP|TRANSPER] = &CustomFXPRS;
    cfxVUCodeArray[TRANSFOG |TRANSCLIP|TRANSSTRIP|TRANSPER] = &CustomFXPRS;
    
    cfxVUCodeArray[TRANSNFOG|TRANSNCL |TRANSLIST |TRANSISO] = &CustomFXPRL;
    cfxVUCodeArray[TRANSFOG |TRANSNCL |TRANSLIST |TRANSISO] = &CustomFXPRL;
    cfxVUCodeArray[TRANSNFOG|TRANSNCL |TRANSSTRIP|TRANSISO] = &CustomFXPRL;
    cfxVUCodeArray[TRANSFOG |TRANSNCL |TRANSSTRIP|TRANSISO] = &CustomFXPRL;
    cfxVUCodeArray[TRANSNFOG|TRANSCLIP|TRANSLIST |TRANSISO] = &CustomFXPRL;
    cfxVUCodeArray[TRANSFOG |TRANSCLIP|TRANSLIST |TRANSISO] = &CustomFXPRL;
    cfxVUCodeArray[TRANSNFOG|TRANSCLIP|TRANSSTRIP|TRANSISO] = &CustomFXPRL;
    cfxVUCodeArray[TRANSFOG |TRANSCLIP|TRANSSTRIP|TRANSISO] = &CustomFXPRL;

    /* Adding the VU code to the pipeline */
    RxPipelineNodePS2ManagerSetVU1CodeArray(managerNode, cfxVUCodeArray);

    return lockedPipe;
}


/*
 *****************************************************************************
 */
RxPipeline  *
SkyCustomFXCreatePipe(void)
{
    RxPipeline         *newPipe = NULL;
    RxLockedPipe       *lockedPipe = NULL;

    newPipe = RxPipelineCreate();
    if (newPipe)
    {
        lockedPipe = RxPipelineLock(newPipe);

        if (lockedPipe != NULL)
            lockedPipe = SkyCustomFXManagerNodeSetup(lockedPipe);
    }

    return lockedPipe;
}


/*
 *****************************************************************************
 */
