/****************************************************************************
 *
 * 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.
 *
 */

/****************************************************************************
 *
 * ppsprite.c
 *
 * Copyright (C) 2001 Criterion Technologies.
 *
 * Original author: Alexandre Hadjdaj.
 *
 * Purpose: Illustrate the possibility of creating a custom VU pipe
 *
 ****************************************************************************/

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

#include "skeleton.h"

#include "ppsprite.h"

#include "stddata.h"

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

#define PPSPRITESTATICDATASTRYDE (1)
static RxPipeline *PPSpritePipeline = NULL;

static PPSprite   *_CurrentSpriteList = NULL;

extern long         ppspriteNT      __attribute__ ((section(".vudata")));
/* The developement pipeline */
extern long         ppspritePRSDev     __attribute__ ((section(".vudata")));
/* The optimized pipeline */
extern long         ppspritePRSOpt     __attribute__ ((section(".vudata")));

#define ppspritePRS ppspritePRSDev

static RxClusterDefinition PPSClPS2xyz;
static RxClusterDefinition PPSClPS2rgba;
static RxClusterDefinition PPSClPS2hNw;
static RxClusterDefinition PPSClPS2uv;

#if (0)
static RxClusterDefinition PPSClPS2uv2;
#endif /* (0) */

#if ( defined(ALTCALLBACK) )

/*
 *****************************************************************************
 */
static RwBool
altPPSpriteInstanceCallBack(void **clusterData, RwUInt32 numClusters)
{
    RxPS2Mesh          *ps2mesh = clusterData[0];
    const RpMesh       *mesh = (const RpMesh *) ps2mesh->mesh;
    RwReal             *pos = clusterData[2];
    RwUInt8            *RGBA = clusterData[3];
    RwReal             *HW = clusterData[4];
    RwReal             *uv1 = clusterData[5];
    PPSprite           *sprList = _CurrentSpriteList;
    RwBool              aBool = FALSE;
    RwUInt32            i = numClusters;
#if (0)
    RwReal             *uv2 = clusterData[6];
    RxPS2DMASessionRecord *RxPS2DMASessionRecord = clusterData[1];
#endif /* (0) */


    for (i = 0; i < mesh->numIndices; i++)
    {
        *pos++ = sprList->pos.x;
        *pos++ = sprList->pos.y;
        *pos++ = sprList->pos.z;

        *RGBA++ = sprList->color.red;
        *RGBA++ = sprList->color.green;
        *RGBA++ = sprList->color.blue;
        *RGBA++ = sprList->color.alpha;

        *HW++ = sprList->size.x * 0.5f;
        *HW++ = sprList->size.y * 0.5f;

        *uv1++ = sprList->uv1.x;
        *uv1++ = sprList->uv1.y;
        *uv1++ = sprList->uv2.x;
        *uv1++ = sprList->uv2.x;

        sprList++;
    }

    _CurrentSpriteList = sprList;
    aBool = 
        sweFinaliseOpenLocalPkt(SWE_PKT_DMA_MODE_CHAIN_TTE |
  
                                SWE_PKT_LOCAL |
                                SWE_PKT_VU1 |
                                SWE_PKT_CIRCALLOC, -2 - PPSPRITESTATICDATASTRYDE); /* 1 QW */
    if (sweLocalPacket)
    {
        RwUInt32            prim = 0x0l;
        RwSkySplitBits128   packed;

        /* unpack transfer 1 quadwords */

        packed.field_64[1] = ((VIFCMD_CYCLE |
                               (4l << 8) |
                               ((4)))) |
            ((VIFCMD_UNPACK |
              (PPSPRITESTATICDATASTRYDE << 16) |
              ((long) (pipeASymbStaticDataStart))) << 32);
        packed.field_64[0] = (1l << 28) |
            (PPSPRITESTATICDATASTRYDE); /* 2 QW after the DMAtag */
        SWEADDCONTFAST(packed.field128);

        /* giftag says use context 1 */
        prim = ( /* fix  */ 0x0l << 10 |
                 /* ctxt */ 0x0l << 9 |
                 /* fst  */ 0x0l << 8 |
                 /* aa1  */ 0x0l << 7 |
                 /* abe  */ 0x1l << 6 |
                 /* fge  */ 0x0l << 5 |
                 /* tme  */ 0x1l << 4 |
                 /* iip  */ 0x1l << 3 |
                 /* prim */ 0x6l << 0);
        /* 6 = sprite */

        packed.field_64[1] = 0x42412l; /* 5 registers, not 3, since we're writing a sprite */
        packed.field_64[0] = ( /* regs */ 0x5l << (60 - 32) |
                               /* flg  */ 0x0l << (58 - 32) |
                               /* prim */ prim << (47 - 32) |
                               /* pre  */ 0x1l << (46 - 32)) << 32;
        SWEADDCONTFAST(packed.field128);

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

        sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
    }

    return TRUE;
}

#define PPSpriteInstanceCallBack  altPPSpriteInstanceCallBack

#else /*  ( defined(ALTCALLBACK) ) */

/*
 *****************************************************************************
 */
static              RwBool
skyPPSpriteInstanceCallBack(void **clusterData, RwUInt32 numClusters)
{
    RxPS2Mesh          *ps2mesh = clusterData[0];
    const RpMesh       *mesh = (const RpMesh *) ps2mesh->mesh;
    RwReal             *pos = clusterData[2];
    RwUInt8            *RGBA = clusterData[3];
    RwReal             *HW = clusterData[4];
    RwReal             *uv1 = clusterData[5];
    PPSprite           *sprList = _CurrentSpriteList;
    RwBool              aBool = FALSE;
    RwUInt32            i = numClusters;

#if (0)
    RwReal             *uv2 = clusterData[6];
    RxPS2DMASessionRecord *RxPS2DMASessionRecord = clusterData[1];
#endif /* (0) */


    for (i = 0; i < mesh->numIndices; i++)
    {
        *pos++ = sprList->pos.x;
        *pos++ = sprList->pos.y;
        *pos++ = sprList->pos.z;

        *RGBA++ = sprList->color.red;
        *RGBA++ = sprList->color.green;
        *RGBA++ = sprList->color.blue;
        *RGBA++ = sprList->color.alpha;

        *HW++ = sprList->size.x * 0.5f;
        *HW++ = sprList->size.y * 0.5f;

        *uv1++ = sprList->uv1.x;
        *uv1++ = sprList->uv1.y;
        *uv1++ = sprList->uv2.x;
        *uv1++ = sprList->uv2.x;

        sprList++;
    }

    _CurrentSpriteList = sprList;
    aBool = sweFinaliseOpenLocalPkt(SWE_PKT_DMA_MODE_CHAIN_TTE |
                                    SWE_PKT_LOCAL |
                                    SWE_PKT_VU1 |
                                    SWE_PKT_CIRCALLOC, -2 - PPSPRITESTATICDATASTRYDE); /* 1 QW */
    if (sweLocalPacket)
    {
        RwUInt32            prim = 0x0l;
        RwUInt64            tmp, tmp1;
        u_long128           ltmp = 0;

        /* unpack transfer 1 quadwords */
        tmp = (1l << 28) |
            (PPSPRITESTATICDATASTRYDE); /* 2 QW after the DMAtag */
        tmp1 = ((VIFCMD_CYCLE |
                 (4l << 8) |
                 ((4)))) |
            ((VIFCMD_UNPACK |
              (PPSPRITESTATICDATASTRYDE << 16) |
              ((long) (pipeASymbStaticDataStart))) << 32);
        MAKE128(ltmp, tmp1, tmp); /* ^^^^^^^^ 1QW */
        SWEADDCONTFAST(ltmp);

        /* giftag says use context 1 */
        prim = ( /* fix  */ 0x0l << 10 |
                 /* ctxt */ 0x0l << 9 |
                 /* fst  */ 0x0l << 8 |
                 /* aa1  */ 0x0l << 7 |
                 /* abe  */ 0x1l << 6 |
                 /* fge  */ 0x0l << 5 |
                 /* tme  */ 0x1l << 4 |
                 /* iip  */ 0x1l << 3 |
                 /* prim */ 0x6l << 0);
        /* 6 = sprite */

        tmp = ( /* regs */ 0x5l << (60 - 32) |
                /* flg  */ 0x0l << (58 - 32) |
                /* prim */ prim << (47 - 32) |
                /* pre  */ 0x1l << (46 - 32)) << 32;
        tmp1 = 0x42412l;       /* 5 registers, not 3, since we're writing a sprite */
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

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

        sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
    }

    return TRUE;
}

#define PPSpriteInstanceCallBack  skyPPSpriteInstanceCallBack


#endif /*  ( defined(ALTCALLBACK) ) */


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

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

    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);

    memcpy(&PPSClPS2xyz,&RxClPS2xyz,sizeof(RxClusterDefinition));
    PPSClPS2xyz.defaultAttributes &= ~CL_ATTRIB_OPAQUE;
    PPSClPS2xyz.defaultAttributes |= CL_ATTRIB_WRITE;
    PPSClPS2xyz.defaultAttributes |= CL_ATTRIB_REQUIRED;
    PPSClPS2xyz.defaultAttributes |= CL_ATTRIB_DONT_FILL;

    memcpy(&PPSClPS2rgba,&RxClPS2rgba,sizeof(RxClusterDefinition));
    PPSClPS2rgba.defaultAttributes &= ~CL_ATTRIB_OPAQUE;
    PPSClPS2rgba.defaultAttributes |= CL_ATTRIB_WRITE;
    PPSClPS2rgba.defaultAttributes |= CL_ATTRIB_REQUIRED;
    PPSClPS2rgba.defaultAttributes |= CL_ATTRIB_DONT_FILL;

    memcpy(&PPSClPS2hNw,&RxClPS2user1,sizeof(RxClusterDefinition));
    PPSClPS2hNw.defaultAttributes = CL_V2_32;
    PPSClPS2hNw.defaultAttributes |= CL_ATTRIB_WRITE;
    PPSClPS2hNw.defaultAttributes |= CL_ATTRIB_REQUIRED;
    PPSClPS2hNw.defaultAttributes |= CL_ATTRIB_DONT_FILL;

    memcpy(&PPSClPS2uv,&RxClPS2user2,sizeof(RxClusterDefinition));
    PPSClPS2uv.defaultAttributes = CL_V4_32;
    PPSClPS2uv.defaultAttributes |= CL_ATTRIB_WRITE;
    PPSClPS2uv.defaultAttributes |= CL_ATTRIB_REQUIRED;
    PPSClPS2uv.defaultAttributes |= CL_ATTRIB_DONT_FILL;

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

    RxPipelineNodePS2ManagerGenerateCluster(managerNode,
                                            &PPSClPS2rgba, CL_RGBA);

    RxPipelineNodePS2ManagerGenerateCluster(managerNode,
                                            &PPSClPS2hNw, CL_USER1);

    RxPipelineNodePS2ManagerGenerateCluster(managerNode,
                                            &PPSClPS2uv, CL_USER2);


    /* Set the VU vertex buffer size, and stride */
    RxPipelineNodePS2ManagerSetVUBufferSizes(managerNode,
                                             pipeASymbStrideOfInputCluster, /* Stride of input cluster */
                                             pipeASymbPointCount,           /* Tristrip vertex count */
                                             pipeASymbPointCount/3);        /* Trilist vertex count */

    /* 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,
                                                PPSpriteInstanceCallBack);

    /* Set the VU code array to point to our Custom VU pipeline */
    /**************   FOG   *   CLIP  *  TL/TS   * PEE/ISO*******/
    /**************    N    *    N    *    TL    *   PER  *************/
    cfxVUCodeArray[TRANSNFOG|TRANSNCL |TRANSLIST |TRANSPER]=&ppspritePRS;

    /**************    Y    *    N    *    TL    *   PER  *************/
    cfxVUCodeArray[TRANSFOG |TRANSNCL |TRANSLIST |TRANSPER]=&ppspritePRS;

    /**************    N    *    N    *    TS    *   PER  *************/
    cfxVUCodeArray[TRANSNFOG|TRANSNCL |TRANSSTRIP|TRANSPER]=&ppspritePRS;

    /**************    Y    *    N    *    TS    *   PER  *************/
    cfxVUCodeArray[TRANSFOG |TRANSNCL |TRANSSTRIP|TRANSPER]=&ppspritePRS;

    /**************    N    *    Y    *    TL    *   PER  *************/
    cfxVUCodeArray[TRANSNFOG|TRANSCLIP|TRANSLIST |TRANSPER]=&ppspritePRS;

    /**************    Y    *    Y    *    TL    *   PER  *************/
    cfxVUCodeArray[TRANSFOG |TRANSCLIP|TRANSLIST |TRANSPER]=&ppspritePRS;

    /**************    N    *    Y    *    TS    *   PER  *************/
    cfxVUCodeArray[TRANSNFOG|TRANSCLIP|TRANSSTRIP|TRANSPER]=&ppspritePRS;

    /**************    Y    *    Y    *    TS    *   PER  *************/
    cfxVUCodeArray[TRANSFOG |TRANSCLIP|TRANSSTRIP|TRANSPER]=&ppspritePRS;

    /**************    N    *    N    *    TL    *   ISO  *************/
    cfxVUCodeArray[TRANSNFOG|TRANSNCL |TRANSLIST |TRANSISO]=&ppspritePRS;

    /**************    Y    *    N    *    TL    *   ISO  *************/
    cfxVUCodeArray[TRANSFOG |TRANSNCL |TRANSLIST |TRANSISO]=&ppspritePRS;

    /**************    N    *    N    *    TS    *   ISO  *************/
    cfxVUCodeArray[TRANSNFOG|TRANSNCL |TRANSSTRIP|TRANSISO]=&ppspritePRS;

    /**************    Y    *    N    *    TS    *   ISO  *************/
    cfxVUCodeArray[TRANSFOG |TRANSNCL |TRANSSTRIP|TRANSISO]=&ppspritePRS;

    /**************    N    *    Y    *    TL    *   ISO  *************/
    cfxVUCodeArray[TRANSNFOG|TRANSCLIP|TRANSLIST |TRANSISO]=&ppspritePRS;

    /**************    Y    *    Y    *    TL    *   ISO  *************/
    cfxVUCodeArray[TRANSFOG |TRANSCLIP|TRANSLIST |TRANSISO]=&ppspritePRS;

    /**************    N    *    Y    *    TS    *   ISO  *************/
    cfxVUCodeArray[TRANSNFOG|TRANSCLIP|TRANSSTRIP|TRANSISO]=&ppspritePRS;

    /**************    Y    *    Y    *    TS    *   ISO  *******/
    cfxVUCodeArray[TRANSFOG |TRANSCLIP|TRANSSTRIP|TRANSISO]=&ppspritePRS;

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

    return lockedPipe;

}


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

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

        if (lockedPipe != NULL)
        {
            lockedPipe = skyPPSpriteManagerNodeSetup(lockedPipe);
        }
    }

    return lockedPipe;
}


/*
 *****************************************************************************
 */
RwBool
PPSpriteCreatePipe(void)
{

    PPSpritePipeline = skyPPSpriteCreatePipe();

    if( PPSpritePipeline == NULL)
    {
        return FALSE;
    }

    return TRUE;
}


/*
 *****************************************************************************
 */
RwBool
PPSPriteDraw(PPSprite *sprites, RwInt32 numSprites )
{
    static RwMatrix *nullTMat = NULL;

    if( NULL == nullTMat )
    {
        nullTMat = RwMatrixCreate();
        RwMatrixSetIdentity(nullTMat);

    }

    if(sprites)
    {
        rwIm3DPool         *pool;
        RwBool                          trueClipperState = RpSkyGetTrueTLClipper();
        /* establish geometry space */

        _CurrentSpriteList = sprites;

        pool = _rwIm3DGetPool();

        pool->elements = _CurrentSpriteList;

        pool->stride = sizeof(PPSprite);

        pool->stash.ltm = nullTMat;
        pool->stash.flags =  0;

        pool->stash.pipeline = (RxPipeline *) PPSpritePipeline;
        pool->stash.primType = (RwPrimitiveType) rwPRIMTYPETRILIST;
        pool->stash.indices = (RxVertexIndex *) NULL;


        pool->numElements = ((RwUInt16) numSprites);
        pool->stash.numIndices = ((RwUInt16) numSprites);

                /*
                 * Disable true TriList Clipper : 
                 * use Large Clipping/Culling frustrum
                 * The user may have to switch it back on after
                 * Sprites has been rendered.
                 */
                RpSkySelectTrueTLClipper(FALSE);

        RxPipelineExecute(PPSpritePipeline,  (void *) &pool->stash, TRUE);

                RpSkySelectTrueTLClipper(trueClipperState);
                
        return TRUE;
    }


    return FALSE;
}
