/* *INDENT-OFF* */

/*
 * objAllInOne
 * default per-object callback functions for PS2-specific object pipelines
 * 
 * Copyright (c) Criterion Software Limited
 */
/****************************************************************************
 *                                                                          *
 * module : objAllInOne.c                                                   *
 *                                                                          *
 * purpose: yawn...                                                         *
 *                                                                          *
 ****************************************************************************/

/*
#### SYNCHRONISATION
####
#### UP TO DATE WITH VERSION 1.85 OF nodePS2ObjAllInOne.c
####
#### SYNCHRONISATION
*/

/****************************************************************************
 includes
 */

#include "ObjAllInOne.h"


/****************************************************************************
 local (static) variables
 */

/* AlexF: 12/3/01: Changed the following 2 variables to be non static. This
   lets users write their own light functions should they want to, rather
   than being restricted to the default PS2AllSkyWorldApplyLight.

   Their names have been changed to reduce potential clashes with user code,
   from
       lightFillPos
       qWordsWritten
   to
       _skyLightFillPos
       _skyLightQWordsWritten
*/
extern RwReal * _skyLightFillPos;
extern RwUInt32 _skyLightQWordsWritten;

#define LIGHTBLOCKQWORDS 32
u_long128 lightBuffer[LIGHTBLOCKQWORDS];


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

   Functions

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


/****************************************************************************
 PS2AllSkyWorldApplyLight

 apply light function from skywrins.c
*/
void
PS2AllSkyWorldApplyLight(const void *voidLight, const RwMatrix *inverseMat,
                        RwReal invScale, const RwSurfaceProperties *surfaceProps)
{
    const RpLight       *light = (const RpLight *)voidLight;
    RpLightType         type = RpLightGetType(light);
    const RwRGBAReal    *color = RpLightGetColor(light);
    RwFrame             *frame = RpLightGetFrame(light);
    const RwV3d         *pos, *at;

    RWFUNCTION(RWSTRING("PS2AllSkyWorldApplyLight"));


//TODO[5][7]: THIS FUNCTION SHOULD BE ABLE TO TAKE AN ARRAY OF LIGHTS SO IT ONLY GETS CALLED ONCE
//           (IN A KNOWN ORDER TO AVOID THE SWITCH STATEMENT, JUST DO A LOAD OF WHILES)


    /* 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 (_skyLightQWordsWritten < (LIGHTBLOCKQWORDS-4))
    {
        /* Capture the matrix and lighting coefficients on the first light */
        if (!_skyLightQWordsWritten)
        {
            if (inverseMat)
            {
                RWASSERT(RWMATRIXALIGNMENT(inverseMat));

                /* Use the inverse matrix given */
                *_skyLightFillPos++ = inverseMat->right.x;
                *_skyLightFillPos++ = inverseMat->right.y;
                *_skyLightFillPos++ = inverseMat->right.z;
                *_skyLightFillPos++ = invScale;
                *_skyLightFillPos++ = inverseMat->up.x;
                *_skyLightFillPos++ = inverseMat->up.y;
                *_skyLightFillPos++ = inverseMat->up.z;
                *_skyLightFillPos++ = invScale;
                *_skyLightFillPos++ = inverseMat->at.x;
                *_skyLightFillPos++ = inverseMat->at.y;
                *_skyLightFillPos++ = inverseMat->at.z;
                *_skyLightFillPos++ = invScale;
                *_skyLightFillPos++ = inverseMat->pos.x;
                *_skyLightFillPos++ = inverseMat->pos.y;
                *_skyLightFillPos++ = inverseMat->pos.z;
                *_skyLightFillPos++ = invScale;
            }
            else
            {
                /* Else use identity matrix */
                *_skyLightFillPos++ = (RwReal)(1.0f);
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = invScale;
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = (RwReal)(1.0f);
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = invScale;
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = (RwReal)(1.0f);
                *_skyLightFillPos++ = invScale;
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = (RwReal)(0.0f);
                *_skyLightFillPos++ = invScale;
            }
            _skyLightQWordsWritten += 4;

            /* Capture lighting coefficients too */
            ((RwReal *)&surfLightCoeffs)[0] =
                surfaceProps->ambient * (RwReal)(255.0f);
            ((RwReal *)&surfLightCoeffs)[1] =
                surfaceProps->specular * (RwReal)(255.0f);
            ((RwReal *)&surfLightCoeffs)[2] =
                surfaceProps->diffuse * (RwReal)(255.0f);
        }

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

            RWASSERT(RWMATRIXALIGNMENT(matrix));

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

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

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

    RWRETURNVOID();
}

