R E T I R E D






/*
 * _doc nodePS2Light
 * _topic TODO: nodePS2Light |
 * _index | nodePS2Light
 * _normal Copyright (c) Criterion Software Limited
 */
/****************************************************************************
 *                                                                          *
 * module : nodePS2Light.c                                                  *
 *                                                                          *
 * purpose: yawn...                                                         *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 includes
 */
#include <math.h>
#include <eekernel.h> /* SyncDCache(), etc. */
#include "rwcore.h"
#include "baworld.h"
#include "p2stdclsw.h"
#include "skyisms.h"
#include "nodePS2Light.h"

static const char rcsid[] __RWUNUSED__ = "@@(#)$Id: nodePS2Light.c,v 1.29 2000/09/29 16:08:31 iestynb Exp $";


/****************************************************************************
 local types
 */

/****************************************************************************
 local (static) prototypes
 */

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

#define LIGHTBLOCKQWORDS 32

/****************************************************************************
 local (static) globals
 */

/****************************************************************************
 functions
 */

/****************************************************************************
 _rwBuildPS2LightsBlock()
 */

static RwBool
_rwBuildPS2LightsBlock(RxCluster *clPS2Lights,
                       RpLight **rwLights,
                       RwUInt32 numRwLights,
                       RxMeshStateVector *meshstate)
{
    RxCluster   *cluster;
    RwReal      *lightFillPos;
    RwUInt32 qWordsWritten;

    RWFUNCTION(RWSTRING("_rwBuildPS2LightsBlock"));

    cluster =  RxClusterInitialiseData(clPS2Lights,
                                       LIGHTBLOCKQWORDS, 
                                       16 /* stride = sizeof(PS2_QWORD) */);
    
    if (cluster)
    {
        RwUInt32 n;
        /* 
         * Reset the buffer for capturing the lights 
         *   (leave space for the DMA tag)... 
         */
        lightFillPos = RxClusterGetCursorData(clPS2Lights, RwReal);

        /* Make space for the DMA tag (RET) */
        lightFillPos += 4;
        qWordsWritten = 0;

        for (n = 0; n < numRwLights; n++)
        {
            const RpLight *light = rwLights[n];
            RpLightType type = RpLightGetType(light);
            const RwRGBAReal *color = RpLightGetColor(light);
            RwFrame *frame = RpLightGetFrame(light);
            const RwV3d *pos, *at;

            /* Only if we have a lightblock, 
             * and have sufficient space left for the
             * light and a termination word (4 qwords)
             */

            /* We quietly ignore the fact the the matrix is 4 qwords, 
             * since this  happens on the first light only, 
             * and we know the buffer is big
             * enough for this and a light and a termination word
             */
            if (qWordsWritten < (LIGHTBLOCKQWORDS-4))
            {
                /* Capture the matrix and lighting coefficients on 
                 * the first light */
                if (!qWordsWritten)
                {
                    if ( !(meshstate->Obj2World.flags & 
                           rwMATRIXINTERNALIDENTITY) )
                    {
                        RwMatrix *inverse;
                        RwReal invscale;

                        inverse = RwMatrixCreate();
                        RwMatrixInvert(inverse, &meshstate->Obj2World);
                        invscale = RwV3dDotProduct(&inverse->at, &inverse->at);
                        invscale = _rwRSqrt(&invscale);

                        *lightFillPos++ = inverse->right.x;
                        *lightFillPos++ = inverse->right.y;
                        *lightFillPos++ = inverse->right.z;
                        *lightFillPos++ = invscale;

                        *lightFillPos++ = inverse->up.x;
                        *lightFillPos++ = inverse->up.y;
                        *lightFillPos++ = inverse->up.z;
                        *lightFillPos++ = invscale;

                        *lightFillPos++ = inverse->at.x;
                        *lightFillPos++ = inverse->at.y;
                        *lightFillPos++ = inverse->at.z;
                        *lightFillPos++ = invscale;

                        *lightFillPos++ = inverse->pos.x;
                        *lightFillPos++ = inverse->pos.y;
                        *lightFillPos++ = inverse->pos.z;
                        *lightFillPos++ = invscale;

                        RwMatrixDestroy(inverse);
                    }
                    else
                    {
                        /* Else use identity matrix */
                        *lightFillPos++ = (RwReal)(1.0f);
                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(1.0f) /* invScale */;

                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(1.0f);
                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(1.0f) /* invScale */;

                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(1.0f);
                        *lightFillPos++ = (RwReal)(1.0f) /* invScale */;

                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(0.0f);
                        *lightFillPos++ = (RwReal)(1.0f) /* invScale */;
                    }
                    qWordsWritten += 4;

                    /* Capture lighting coefficients too */

                    /* SDM - 
                     * this is a write into a global variable in skyinst.c,
                     * that is folded into the packet in openVU1SetupPkt() 
                     */
                    ((RwReal *)&surfLightCoeffs)[0] = 
                        meshstate->SurfaceProperties.ambient  * 
                        (RwReal)(255.0f);
                    ((RwReal *)&surfLightCoeffs)[1] = 
                        meshstate->SurfaceProperties.specular * 
                        (RwReal)(255.0f);
                    ((RwReal *)&surfLightCoeffs)[2] = 
                        meshstate->SurfaceProperties.diffuse  * 
                        (RwReal)(255.0f);
                }

                if (frame)
                {
                    RwMatrix *matrix = RwFrameGetLTM(frame);

                    at = RwMatrixGetAt(matrix);
                    pos = RwMatrixGetPos(matrix);
                }
                else
                {
                    at = pos = NULL;
                }

                /* Add the light to the light buffer (first the generic bit) */
                lightFillPos[0] = color->red;
                lightFillPos[1] = color->green;
                lightFillPos[2] = color->blue;
                ((RwUInt32 *)(&lightFillPos[3]))[0] = (RwUInt32)type;

                /* Then the light specific bit */
                switch (type)
                {
                    case (rpLIGHTDIRECTIONAL):
                        {
                            lightFillPos += 4;
                            *lightFillPos++ = at->x;
                            *lightFillPos++ = at->y;
                            *lightFillPos++ = at->z;
                            *lightFillPos++ = (RwReal)(0.0f);
                            qWordsWritten += 2;
                            break;
                        }
                    case (rpLIGHTAMBIENT):
                        {
                            lightFillPos += 4;
                            qWordsWritten++;
                            break;
                        }
                    case (rpLIGHTPOINT):
                        {
                            lightFillPos += 4;
                            *lightFillPos++ = pos->x;
                            *lightFillPos++ = pos->y;
                            *lightFillPos++ = pos->z;
                            *lightFillPos++ = light->radius;
                            qWordsWritten += 2;
                            break;
                        }
                    case (rpLIGHTSPOT):
                    case (rpLIGHTSPOTSOFT):
                        {
                            lightFillPos += 4;
                            *lightFillPos++ = pos->x;
                            *lightFillPos++ = pos->y;
                            *lightFillPos++ = pos->z;
                            *lightFillPos++ = light->radius;
                            *lightFillPos++ = at->x;
                            *lightFillPos++ = at->y;
                            *lightFillPos++ = at->z;
                            *lightFillPos++ = light->open;
                            qWordsWritten += 3;
                            break;
                        }
                    case (rpNALIGHTTYPE):
                    default:
                        {
                            break;
                        }
                }
            }
        }

        {
            RwUInt64 tmp, tmp1;
            u_long128 retHeader128;

            /* Test to see if we have a light buffer */
            if (lightFillPos)
            {
                RwUInt32 destAddr;

                destAddr = VU1LIGHTOFFSET;
                if (qWordsWritten)
                {
                    /* Terminate and finalise the light buffer */
                    ((u_long128 *)lightFillPos)[0] = nullLightBlock[1];
                    qWordsWritten++;

                    lightFillPos = RxClusterGetCursorData(clPS2Lights, RwReal);

                    tmp = (1l<<28) | (qWordsWritten);
                    tmp1 = (((0x6cl<<24)|((RwUInt64)qWordsWritten<<16) | 
                             (destAddr)) << 32) |
                        ((1l<<24)|(4<<8)|(4));
                    MAKE128(retHeader128, tmp1, tmp);
                    qWordsWritten++;

                    /* Header for return 
                     * (with VIF TAG to upload to output buffer) 
                     */
                    ((u_long128 *)lightFillPos)[0] = retHeader128;

                    /* TEMPORARY - Flush it out of the cache */
                    PFCALL(PF_rwSkyWorldPostLight);
                    SyncDCache(lightFillPos, 
                               SCESYNCDCACHEROUNDUP(lightFillPos + 
                                                    (qWordsWritten*4)));
                    PFRET(PF_rwSkyWorldPostLight);
                }
                else
                {
                    /* This is what we will upload */
                    memcpy(RxClusterGetCursorData(clPS2Lights, RwReal), 
                           nullLightBlock, 
                           sizeof(nullLightBlock));
                    /* lightFillPos = (RwReal *)nullLightBlock; */
                    qWordsWritten = sizeof(nullLightBlock)/sizeof(u_long128);
                }
            }
        }

        clPS2Lights->numUsed = qWordsWritten;
    }

    RWRETURN(NULL !=cluster);
}

/****************************************************************************
 _rwPS2LightNodeBody()
 */

static RwBool
_rwPS2LightNodeBody(RxPipelineNodeInstance *self,
                    RxMemoryArena *arena __RWUNUSED__,
                    void *data __RWUNUSED__)
{
    static RxMeshStateVector *defaultMeshState = NULL;
    RxPacket *pk;

    RWFUNCTION(RWSTRING("_rwPS2LightNodeBody"));

    /* RWASSERT( data != NULL ); */

    /* used as a fallback in the event RxClMeshState is not provided */
    if ( defaultMeshState == NULL )
    {
        static RxMeshStateVector _meshState;

        _meshState.SurfaceProperties.ambient  = ((RwReal) 2) / ((RwReal) 7);
        _meshState.SurfaceProperties.specular = ((RwReal) 1) / ((RwReal) 7);
        _meshState.SurfaceProperties.diffuse  = ((RwReal) 4) / ((RwReal) 7);
        {
            RwMatrix *mat = RwMatrixCreate();
            _meshState.Obj2World = *mat;
            RwMatrixDestroy(mat);
        }

        defaultMeshState = &_meshState;
    }

    while ( (pk = RxPacketFetch(self)) != NULL )
    {
        RxCluster *clRwLights, *clMeshState, *clPS2Lights;

        if ( (clRwLights = 
              RxClusterLockRead(pk, 0 /* RxClLights */)) != NULL )
        {
            RxMeshStateVector *currentMeshState = defaultMeshState;

            if ( (clMeshState = 
                  RxClusterLockRead(pk, 1 /* RxClMeshState */)) != NULL )
            {
                if ( (clMeshState->flags & rxCLFLAGS_CLUSTERVALID) )
                {
                    currentMeshState = 
                        RxClusterGetCursorData(clMeshState, RxMeshStateVector);
                }
                else
                {
                    RxClusterUnlock(clMeshState);
                }
            }

            if ( (clPS2Lights =
                  RxClusterLockWrite(pk,
                                       2 /* RxClPS2LightsBlock */, 
                                       self)) != NULL )
            {
                _rwBuildPS2LightsBlock(clPS2Lights,
                                       RxClusterGetCursorData(clRwLights, RpLight *),
                                       clRwLights->numUsed,
                                       currentMeshState);

                RxClusterUnlock(clPS2Lights);
            }

            if ( currentMeshState != defaultMeshState )
            {
                RxClusterUnlock(clMeshState);
            }

            RxClusterUnlock(clRwLights);
        }

        RxPacketDispatch(pk, 0 /* DefaultOutput */, self);
    }

    RWRETURN(TRUE);
}

/************************************************************************
 * _func <f RxNodeDefinitionGetPS2Light> <f todo>
 *
 * _rdesc a pointer to a node to <f todo>
 *
 * _xref <f RxPipelineNodePS2MatInstanceGenerateCluster>
 * _xref <f RxNodeDefinitionGetPS2AtomicInstance>
 * _xref <f RxNodeDefinitionGetPS2Dispatch>
 * _xref <f RxNodeDefinitionGetPS2MatBridge>
 * _xref <f RxNodeDefinitionGetPS2MatInstance>
 * _xref <f RxNodeDefinitionGetPS2ObjClose>
 * _xref <f RxNodeDefinitionGetPS2ObjEnumMeshes>
 * _xref <f RxNodeDefinitionGetPS2ObjOpen>
 * _xref <f RxNodeDefinitionGetPS2WorldSectorInstance>
 */

RxNodeDefinition *
RxNodeDefinitionGetPS2Light(void)
{
    /***************************************/
    /**                                   **/
    /**  PS2LIGHT.CSL NODE SPECIFICATION  **/
    /**                                   **/
    /***************************************/

    static RxClusterRef nodeClusters[] =
    {
        /* 0 */              /* 1 */                /* 2 */
        { &RxClLights , rxCLALLOWABSENT, rxCLRESERVED },
        { &RxClMeshState , rxCLALLOWABSENT, rxCLRESERVED },
        { &RxClPS2LightsBlock , rxCLALLOWABSENT, rxCLRESERVED }
    };

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


    /* input requirements (this array parallel to ClusterRefs) */
    static RxClusterValidityReq nodeReqs[NUMCLUSTERSOFINTEREST] =
    {
        rxCLREQ_REQUIRED ,     rxCLREQ_OPTIONAL   ,   rxCLREQ_DONTWANT        
    };

    /* output state (this array parallel to ClusterRefs) */
    static RxClusterValid nodeOut1[NUMCLUSTERSOFINTEREST] =
    {
        rxCLVALID_VALID   ,   rxCLVALID_NOCHANGE  ,   rxCLVALID_VALID         
    };

    static RwChar _DefaultOutput[] = RWSTRING("DefaultOutput");

    static RxOutputSpec nodeOuts[] =
    {
        {
            _DefaultOutput,             // Name
            nodeOut1,                    // OutputClusters
            rxCLVALID_NOCHANGE            // AllOtherClusters
        }
    };

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

    static RwChar _PS2Light_csl[] = RWSTRING("PS2Light.csl");

    static RxNodeDefinition nodePS2LightCSL =
    {
        _PS2Light_csl,             // Name
        {                           // nodemethods
            _rwPS2LightNodeBody,    // +-- nodebody
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL
        },
        {                           // Io
            NUMCLUSTERSOFINTEREST,  // +-- NumClustersOfInterest
            nodeClusters,           // +-- ClustersOfInterest
            nodeReqs,               // +-- InputRequirements
            NUMOUTPUTS,             // +-- NumOutputs
            nodeOuts                // +-- Outputs
        },
        0,
        FALSE,
        0
    };

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

    RxNodeDefinition *result = &nodePS2LightCSL;

    RWAPIFUNCTION(RWSTRING("RxNodeDefinitionGetPS2Light"));

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

    RWRETURN(result);
}
