//=================================================
//HighTide - Helper Functions
//Gos2 Test App
//
//Written by Matt Bronder
//Ported to GOS2 by Bob Crocco 8/2000
//=================================================
#include <Gos2\Gos.h>
#include "d3dx8.h"
#include "HighTide.h"
#include "Helper.h"

//******************************************************************************
//
// Function:
//
//     GenerateBeachNormals
//
// Description:
//
//     Generate vertex normals for the given beach mesh.
//
// Arguments:
//
//     LPD3DVERTEX pr           - Array of vertices describing the beach mesh.
//
// Return Value:
//
//     None.
//
//******************************************************************************
void GenerateBeachNormals(PHHVERTEX pr) {

    D3DXVECTOR3* pvFaceNormal;
    D3DXVECTOR3  v1, v2, r;
    UINT         x, y;

    pvFaceNormal = (D3DXVECTOR3*)GosMalloc(((BEACH_GRID_DIM_Y - 1) 
                                    * (BEACH_GRID_DIM_X - 1) * 2) * sizeof(D3DXVECTOR3), DBGNAME("Beach Normals"));
    if (!pvFaceNormal) {
        return;
    }

    // Generate face normals for the triangles
    for (y = 0; y < BEACH_GRID_DIM_Y - 1; y++) {

        for (x = 0; x < BEACH_GRID_DIM_X - 1; x++) {

            v1 = SubtractVertex(&pr[y * BEACH_GRID_DIM_X + x + 1], 
                                &pr[y * BEACH_GRID_DIM_X + x]);
            v2 = SubtractVertex(&pr[(y + 1) * BEACH_GRID_DIM_X + x], 
                                &pr[y * BEACH_GRID_DIM_X + x]);
            D3DXVec3Normalize(&pvFaceNormal[y * (BEACH_GRID_DIM_X - 1) * 2 + x * 2],
                                D3DXVec3Cross(&r, &v1, &v2));

            v1 = SubtractVertex(&pr[y * BEACH_GRID_DIM_X + x + 1], 
                                &pr[(y + 1) * BEACH_GRID_DIM_X + x + 1]);
            v2 = SubtractVertex(&pr[(y + 1) * BEACH_GRID_DIM_X + x], 
                                &pr[(y + 1) * BEACH_GRID_DIM_X + x + 1]);
            D3DXVec3Normalize(&pvFaceNormal[y * (BEACH_GRID_DIM_X - 1) * 2 + x * 2 + 1],
                                D3DXVec3Cross(&r, &v2, &v1));
        }
    }

    // Generate vertex normals by averaging the adjoining face normals
    for (y = 1; y < BEACH_GRID_DIM_Y - 1; y++) {

        for (x = 1; x < BEACH_GRID_DIM_X - 1; x++) {

            v1 = pvFaceNormal[(y - 1) * (BEACH_GRID_DIM_X - 1) * 2 
                                + (x - 1) * 2 + 1];
            v1 += pvFaceNormal[(y - 1) * (BEACH_GRID_DIM_X - 1) * 2 
                                + x * 2];
            v1 += pvFaceNormal[(y - 1) * (BEACH_GRID_DIM_X - 1) * 2 
                                + x * 2 + 1];
            v1 += pvFaceNormal[y * (BEACH_GRID_DIM_X - 1) * 2 + (x - 1) * 2];
            v1 += pvFaceNormal[y * (BEACH_GRID_DIM_X - 1) * 2 
                                + (x - 1) * 2 + 1];
            v1 += pvFaceNormal[y * (BEACH_GRID_DIM_X - 1) * 2 + x * 2];
            v1 = v1 / 6.0f;
            pr[y * BEACH_GRID_DIM_X + x].nx = v1.x;
            pr[y * BEACH_GRID_DIM_X + x].ny = v1.y;
            pr[y * BEACH_GRID_DIM_X + x].nz = v1.z;
        }
    }

    GosFree(pvFaceNormal);
}


//******************************************************************************
//
// Function:
//
//     GenerateCliffNormals
//
// Description:
//
//     Generate vertex normals for the given cliff mesh.
//
// Arguments:
//
//     LPD3DVERTEX pr           - Array of vertices describing the cliff mesh.
//
// Return Value:
//
//     None.
//
//******************************************************************************
void GenerateCliffNormals(PHHVERTEX pr) {

    D3DXVECTOR3* pvFaceNormal;
    D3DXVECTOR3  v1, v2, r;
    UINT         x, y;

    pvFaceNormal = (D3DXVECTOR3*)GosMalloc(((CLIFF_GRID_DIM_Y - 1) 
                                    * (CLIFF_GRID_DIM_X - 1) * 2) * sizeof(D3DXVECTOR3),
									DBGNAME("Cliff Normals"));
    if (!pvFaceNormal) {
        return;
    }

    // Generate face normals for the triangles
    for (y = 0; y < CLIFF_GRID_DIM_Y - 1; y++) {

        for (x = 0; x < CLIFF_GRID_DIM_X - 1; x++) {

            v1 = SubtractVertex(&pr[y * CLIFF_GRID_DIM_X + x + 1], 
                                &pr[y * CLIFF_GRID_DIM_X + x]);
            v2 = SubtractVertex(&pr[(y + 1) * CLIFF_GRID_DIM_X + x], 
                                &pr[y * CLIFF_GRID_DIM_X + x]);
            D3DXVec3Normalize(&pvFaceNormal[y * (CLIFF_GRID_DIM_X - 1) * 2 + x * 2], D3DXVec3Cross(&r, &v1, &v2));

            v1 = SubtractVertex(&pr[y * CLIFF_GRID_DIM_X + x + 1], 
                                &pr[(y + 1) * CLIFF_GRID_DIM_X + x + 1]);
            v2 = SubtractVertex(&pr[(y + 1) * CLIFF_GRID_DIM_X + x], 
                                &pr[(y + 1) * CLIFF_GRID_DIM_X + x + 1]);
            D3DXVec3Normalize(&pvFaceNormal[y * (CLIFF_GRID_DIM_X - 1) * 2 + x * 2 + 1], D3DXVec3Cross(&r, &v2, &v1));
        }
    }

    // Generate vertex normals by averaging the adjoining face normals
    for (y = 1; y < CLIFF_GRID_DIM_Y - 1; y++) {

        for (x = 1; x < CLIFF_GRID_DIM_X - 1; x++) {

            v1 = pvFaceNormal[(y - 1) * (CLIFF_GRID_DIM_X - 1) * 2 
                                + (x - 1) * 2 + 1];
            v1 += pvFaceNormal[(y - 1) * (CLIFF_GRID_DIM_X - 1) * 2 + x * 2];
            v1 += pvFaceNormal[(y - 1) * (CLIFF_GRID_DIM_X - 1) * 2 
                                + x * 2 + 1];
            v1 += pvFaceNormal[y * (CLIFF_GRID_DIM_X - 1) * 2 + (x - 1) * 2];
            v1 += pvFaceNormal[y * (CLIFF_GRID_DIM_X - 1) * 2 
                                + (x - 1) * 2 + 1];
            v1 += pvFaceNormal[y * (CLIFF_GRID_DIM_X - 1) * 2 + x * 2];
            v1 = v1 / 6.0f;
            pr[y * CLIFF_GRID_DIM_X + x].nx = v1.x;
            pr[y * CLIFF_GRID_DIM_X + x].ny = v1.y;
            pr[y * CLIFF_GRID_DIM_X + x].nz = v1.z;
        }
    }

    GosFree(pvFaceNormal);
}


//******************************************************************************
//
// Function:
//
//     SubtractVertex
//
// Description:
//
//     Subtract the positional vector components of the two vertices and
//     return the resulting vector.
//
// Arguments:
//
//     D3DVERTEX r1             - First vector.
//
//     D3DVERTEX r2             - Second vector.
//
// Return Value:
//
//     The resulting difference vector.
//
//******************************************************************************
static D3DXVECTOR3 SubtractVertex(PHHVERTEX pr1, PHHVERTEX pr2)
{

    D3DXVECTOR3 v;

    v.x = pr1->x - pr2->x;
    v.y = pr1->y - pr2->y;
    v.z = pr1->z - pr2->z;

    return v;
}


//******************************************************************************
//
// Function:
//
//     SetLight
//
// Description:
//
//     Set a light using the given light data.
//
// Arguments:
//
//     D3DLIGHT8* plight            - Pointer to the light to set
//
//     D3DLIGHTTYPE d3dlt           - Type of light to Set
//
//     D3DXVECTOR3 vPos             - Position of the light
//
//     D3DXVECTOR3 vDir             - Direction of the light
//
//     D3DCOLOR cDiffuse (Optional) - Diffuse color of the light
//
//     D3DCOLOR cSpecular(Optional) - Specular color of the light
//
//     D3DCOLOR cAmbient (Optional) - Ambient color of the light
//
//     float fRange (Optional)      - Range of the light
//
//     float fFalloff (Optional)    - Falloff of a spotlight's penumbra
//
//     float fAttenuation0          - Change in the light's intensity
//              (Optional)
//
//     float fAttenuation1          - Change in the light's intensity
//              (Optional)
//
//     float fAttenuation2          - Change in the light's intensity
//              (Optional)
//
//     float fTheta (Optional)      - Angle of a spotlight's umbra
//
//     float fPhi (Optional)        - Angle of a spotlight's penumbra
//
// Return Value:
//
//     TRUE on success, FALSE on failure.
//
//******************************************************************************
void SetLight(D3DLIGHT8* plight, D3DLIGHTTYPE d3dlt,
                             D3DXVECTOR3 vPos, D3DXVECTOR3 vDir, 
                             D3DCOLOR cDiffuse, D3DCOLOR cSpecular,
                             D3DCOLOR cAmbient,
                             float fRange, float fFalloff, 
                             float fAttenuation0, float fAttenuation1, 
                             float fAttenuation2, float fTheta, 
                             float fPhi)
{
    if (plight) {

        // Initialize the light data
        memset(plight, 0, sizeof(D3DLIGHT8));
        plight->Type = d3dlt;
        plight->Diffuse.r = (float)RGBA_GETRED(cDiffuse) / 255.0f;
        plight->Diffuse.g = (float)RGBA_GETGREEN(cDiffuse) / 255.0f;
        plight->Diffuse.b = (float)RGBA_GETBLUE(cDiffuse) / 255.0f;
        plight->Diffuse.a = (float)RGBA_GETALPHA(cDiffuse) / 255.0f;
        plight->Specular.r = (float)RGBA_GETRED(cSpecular) / 255.0f;
        plight->Specular.g = (float)RGBA_GETGREEN(cSpecular) / 255.0f;
        plight->Specular.b = (float)RGBA_GETBLUE(cSpecular) / 255.0f;
        plight->Specular.a = (float)RGBA_GETALPHA(cSpecular) / 255.0f;
        plight->Ambient.r = (float)RGBA_GETRED(cAmbient) / 255.0f;
        plight->Ambient.g = (float)RGBA_GETGREEN(cAmbient) / 255.0f;
        plight->Ambient.b = (float)RGBA_GETBLUE(cAmbient) / 255.0f;
        plight->Ambient.a = (float)RGBA_GETALPHA(cAmbient) / 255.0f;
        plight->Position = vPos;
        plight->Direction = vDir;
        plight->Range = fRange;
        plight->Falloff = fFalloff;
        plight->Attenuation0 = fAttenuation0;
        plight->Attenuation1 = fAttenuation1;
        plight->Attenuation2 = fAttenuation2;
        plight->Theta = fTheta;
        plight->Phi = fPhi;
    }
}


//******************************************************************************
// Material functions
//******************************************************************************

//******************************************************************************
//
// Function:
//
//     SetMaterial
//
// Description:
//
//     Initialize a material with the given color information.
//
// Arguments:
//
//     D3DMATERIAL8* pmaterial          - Pointer to the material
//
//     D3DCOLOR cDiffuse (Optional)     - Diffuse component of the material
//
//     D3DCOLOR cAmbient (Optional)     - Ambient component of the material
//
//     D3DCOLOR cSpecular (Optional)    - Specular component of the material
//
//     D3DCOLOR cEmissive (Optional)    - Emissive component of the material
//
//     float fPower (Optional)          - Sharpness of the specular highlights
//
// Return Value:
//
//     None.
//
//******************************************************************************
void SetMaterial(D3DMATERIAL8* pmaterial,
                       D3DCOLOR cDiffuse, D3DCOLOR cAmbient,
                       D3DCOLOR cSpecular, D3DCOLOR cEmissive,
                       float fPower) 
{
    if (pmaterial) {

        // Intialize the material
        memset(pmaterial, 0, sizeof(D3DMATERIAL8));
        pmaterial->Ambient.r = (float)RGBA_GETRED(cAmbient) / 255.0f;
        pmaterial->Ambient.g = (float)RGBA_GETGREEN(cAmbient) / 255.0f;
        pmaterial->Ambient.b = (float)RGBA_GETBLUE(cAmbient) / 255.0f;
        pmaterial->Ambient.a = (float)RGBA_GETALPHA(cAmbient) / 255.0f;
        pmaterial->Diffuse.r = (float)RGBA_GETRED(cDiffuse) / 255.0f;
        pmaterial->Diffuse.g = (float)RGBA_GETGREEN(cDiffuse) / 255.0f;
        pmaterial->Diffuse.b = (float)RGBA_GETBLUE(cDiffuse) / 255.0f;
        pmaterial->Diffuse.a = (float)RGBA_GETALPHA(cDiffuse) / 255.0f;
        pmaterial->Specular.r = (float)RGBA_GETRED(cSpecular) / 255.0f;
        pmaterial->Specular.g = (float)RGBA_GETGREEN(cSpecular) / 255.0f;
        pmaterial->Specular.b = (float)RGBA_GETBLUE(cSpecular) / 255.0f;
        pmaterial->Specular.a = (float)RGBA_GETALPHA(cSpecular) / 255.0f;
        pmaterial->Emissive.r = (float)RGBA_GETRED(cEmissive) / 255.0f;
        pmaterial->Emissive.g = (float)RGBA_GETGREEN(cEmissive) / 255.0f;
        pmaterial->Emissive.b = (float)RGBA_GETBLUE(cEmissive) / 255.0f;
        pmaterial->Emissive.a = (float)RGBA_GETALPHA(cEmissive) / 255.0f;
        pmaterial->Power = fPower;
    }
}

//******************************************************************************
// Matrix utility functions
//******************************************************************************

//******************************************************************************
//
// Function:
//
//     InitMatrix
//
// Description:
//
//     Initialize the given matrix with the given values.
//
// Arguments:
//
//     LPD3DMATRIX pd3dm        - Matrix to initialize
//
//     float _11 to _44         - Initialization values
//
// Return Value:
//
//     None.
//
//******************************************************************************
void InitMatrix(D3DMATRIX* pd3dm,
                float _11, float _12, float _13, float _14,
                float _21, float _22, float _23, float _24,
                float _31, float _32, float _33, float _34,
                float _41, float _42, float _43, float _44) 
{
    if (pd3dm) {

        pd3dm->_11 = _11;
        pd3dm->_12 = _12;
        pd3dm->_13 = _13;
        pd3dm->_14 = _14;
        pd3dm->_21 = _21;
        pd3dm->_22 = _22;
        pd3dm->_23 = _23;
        pd3dm->_24 = _24;
        pd3dm->_31 = _31;
        pd3dm->_32 = _32;
        pd3dm->_33 = _33;
        pd3dm->_34 = _34;
        pd3dm->_41 = _41;
        pd3dm->_42 = _42;
        pd3dm->_43 = _43;
        pd3dm->_44 = _44;
    }
}

//******************************************************************************
// Revolution functions
//******************************************************************************

//******************************************************************************
//
// Function:
//
//     CreateRevolution
//
// Description:
//
//     Generate a set of vertices defining a three dimensional object 
//     according to a given radius function "revolved" around the y axis
//     (to determine the discrete points of the vertices).
//
// Arguments:
//
//     PREVOLUTION                  - Pointer to a structure that will
//                                    contain the generated vertices
//
//     float fLength                - Length of the object along the y axis
//
//     UINT uSteps                  - Maximum number of steps along the y axis 
//                                    at which the radius function will
//                                    be evaluated to generate vertices.  The
//                                    actual number of steps used may be 
//                                    less than uSteps depending on the value
//                                    of fThreshold.
//
//     UINT uRevolutions            - Number of rotational steps taken about
//                                    the y axis for each step (uSteps) taken
//                                    along the length of the object
//
//     RADPROC pfnRadius            - Radius function used to shape the object
//
//     LPVOID pvContext             - User variable passed into the radius function
//
//     float fThreshold             - Threshold value used to cull steps along
//              (Optional)            the y axis.  If the rate of change from
//                                    one step to the next is not significant
//                                    enough according to the threshold, the
//                                    vertices at that step will not be included
//                                    in the final mesh.
//
//     BOOL bConvex (Optional)      - TRUE to generate a convex object, FALSE
//                                    to generate a concave object
//
//     float fScaleU (Optional)     - Horizontal texture scale value
//
//     float fScaleV (Optional)     - Vertical texture scale value
//
// Return Value:
//
//     TRUE on success, FALSE on failure.
//
//******************************************************************************
BOOL CreateRevolution(PREVOLUTION prv, float fLength, UINT uSteps, 
                        UINT uRevolutions, RADPROC pfnRadius, LPVOID pvContext,
                        float fThreshold, BOOL bConvex, 
                        float fScaleU, float fScaleV)
{
    UINT                    uNumVertices;
    PVERTEX                 pdvVertices;
    VERTEX                  rTemp;
    D3DXVECTOR3             v1, v2, v3, v4, n1, n2, n3, n4;
    UINT                    i, j, k;
    float                   a, da = M_2PI / (float)(uRevolutions);
    float                   y;
    float                   fRadius;
    BOOL                    bPointedTop, bPointedBottom;
    float*                  pfSteps;

    // Parameter validation
    if (!prv || !pfnRadius) {
        return FALSE;
    }

    // Initialize the revolution structure
    prv->prTop = NULL;
    prv->uTopVertices = 0;
    prv->prBottom = NULL;
    prv->uBottomVertices = 0;

    // Use the threshold to cull the steps
    pfSteps = new float[uSteps + 1];
    if (!pfSteps) {
        return FALSE;
    }
    GetSteps(fLength, &uSteps, pfSteps, pfnRadius, pvContext, fThreshold);
    pfSteps[uSteps] = 0.0f;

    uNumVertices = (uSteps + 1) * uRevolutions;

    // Create the vertices
    pdvVertices = new VERTEX[uNumVertices];
    if (!pdvVertices) {
        delete [] pfSteps;
        return FALSE;
    }
    memset(pdvVertices, 0, uNumVertices*sizeof(VERTEX));

    for (i = 0; i <= uSteps; i++) {
        a = (float)(0.0);
        y = pfSteps[i];
        fRadius = pfnRadius(y, fLength, pvContext);
        for (j = 0; j < uRevolutions; j++) {
            (pdvVertices+(i*uRevolutions)+j)->vPosition.x = (fRadius*(float)(cos((double)a)));
            (pdvVertices+(i*uRevolutions)+j)->vPosition.z = (fRadius*(float)(sin((double)a)));
            (pdvVertices+(i*uRevolutions)+j)->vPosition.y = y;
            (pdvVertices+(i*uRevolutions)+j)->u0 = (bConvex?a / M_2PI:(M_2PI - a) / M_2PI) * fScaleU;
            (pdvVertices+(i*uRevolutions)+j)->v0 = ((fLength - y) / fLength) * fScaleV;
            if ((pdvVertices+(i*uRevolutions)+j)->vPosition.x < (float)(0.00001) 
                            && (pdvVertices+(i*uRevolutions)+j)->vPosition.x > (float)(-0.00001)) {
                (pdvVertices+(i*uRevolutions)+j)->vPosition.x = (float)(0.0);
            }
            if ((pdvVertices+(i*uRevolutions)+j)->vPosition.y < (float)(0.00001) 
                            && (pdvVertices+(i*uRevolutions)+j)->vPosition.y > (float)(-0.00001)) {
                (pdvVertices+(i*uRevolutions)+j)->vPosition.y = (float)(0.0);
            }
            if ((pdvVertices+(i*uRevolutions)+j)->vPosition.z < (float)(0.00001) 
                            && (pdvVertices+(i*uRevolutions)+j)->vPosition.z > (float)(-0.00001)) {
                (pdvVertices+(i*uRevolutions)+j)->vPosition.z = (float)(0.0);
            }
            if ((pdvVertices+(i*uRevolutions)+j)->u0 < (float)(0.00001) 
                            && (pdvVertices+(i*uRevolutions)+j)->u0 > (float)(-0.00001)) {
                (pdvVertices+(i*uRevolutions)+j)->u0 = (float)(0.0);
            }
            if ((pdvVertices+(i*uRevolutions)+j)->v0 < (float)(0.00001) 
                            && (pdvVertices+(i*uRevolutions)+j)->v0 > (float)(-0.00001)) {
                (pdvVertices+(i*uRevolutions)+j)->v0 = (float)(0.0);
            }
            a += da;
        }
    }

    delete [] pfSteps;

    bPointedTop = (pdvVertices[0].vPosition.x == pdvVertices[1].vPosition.x && 
                    pdvVertices[0].vPosition.y == pdvVertices[1].vPosition.y &&
                    pdvVertices[0].vPosition.z == pdvVertices[1].vPosition.z);
    bPointedBottom = ((pdvVertices + uNumVertices - uRevolutions)->vPosition.x 
                        == (pdvVertices + uNumVertices - uRevolutions + 1)->vPosition.x &&
                       (pdvVertices + uNumVertices - uRevolutions)->vPosition.y 
                        == (pdvVertices + uNumVertices - uRevolutions + 1)->vPosition.y &&
                       (pdvVertices + uNumVertices - uRevolutions)->vPosition.z 
                        == (pdvVertices + uNumVertices - uRevolutions + 1)->vPosition.z);

    // Generate the normals
    for (i = 0; i < uRevolutions; i++) {
        if (bPointedTop && uSteps > 1) {
            pdvVertices[i].vNormal.x = (float)(0.0);
            pdvVertices[i].vNormal.y = (float)(1.0);
            pdvVertices[i].vNormal.z = (float)(0.0);
        }
        else {
            if (bPointedTop) {
                v1 = SubtractVertexVector(pdvVertices[!(i%uRevolutions)?i+uRevolutions-1+uRevolutions:i-1+uRevolutions], pdvVertices[i]);
                v3 = SubtractVertexVector(pdvVertices[!((i+1)%uRevolutions)?i+1:i+1+uRevolutions], pdvVertices[i]);
            }
            else {
                v1 = SubtractVertexVector(pdvVertices[!(i%uRevolutions)?i+uRevolutions-1:i-1], pdvVertices[i]);
                v3 = SubtractVertexVector(pdvVertices[!((i+1)%uRevolutions)?i-uRevolutions+1:i+1], pdvVertices[i]);
            }
            v4 = SubtractVertexVector(pdvVertices[i+uRevolutions], pdvVertices[i]);
            if (bConvex) {
                D3DXVec3Cross(&n3, &v3, &v4);
                D3DXVec3Cross(&n4, &v4, &v1);
            }
            else {
                D3DXVec3Cross(&n3, &v4, &v3);
                D3DXVec3Cross(&n4, &v1, &v4);
            }
            pdvVertices[i].vNormal.x = (n3.x + n4.x) / (float)(2.0);
            pdvVertices[i].vNormal.y = (n3.y + n4.y) / (float)(2.0);
            pdvVertices[i].vNormal.z = (n3.z + n4.z) / (float)(2.0);
            NormalizeVertexNormal(&pdvVertices[i]);

            if (pdvVertices[i].vNormal.x < (float)(0.00001) && pdvVertices[i].vNormal.x > (float)(-0.00001)) {
                pdvVertices[i].vNormal.x = (float)(0.0);
            }
            if (pdvVertices[i].vNormal.y < (float)(0.00001) && pdvVertices[i].vNormal.y > (float)(-0.00001)) {
                pdvVertices[i].vNormal.y = (float)(0.0);
            }
            if (pdvVertices[i].vNormal.z < (float)(0.00001) && pdvVertices[i].vNormal.z > (float)(-0.00001)) {
                pdvVertices[i].vNormal.z = (float)(0.0);
            }
        }
    }

    for (i = uRevolutions; i < uNumVertices - uRevolutions; i++) {
        v1 = SubtractVertexVector(pdvVertices[!(i%uRevolutions)?i+uRevolutions-1:i-1], pdvVertices[i]);
        v2 = SubtractVertexVector(pdvVertices[i-uRevolutions], pdvVertices[i]);
        v3 = SubtractVertexVector(pdvVertices[!((i+1)%uRevolutions)?i-uRevolutions+1:i+1], pdvVertices[i]);
        v4 = SubtractVertexVector(pdvVertices[i+uRevolutions], pdvVertices[i]);
        if (bConvex) {
            D3DXVec3Cross(&n1, &v1, &v2);
            D3DXVec3Cross(&n2, &v2, &v3);
            D3DXVec3Cross(&n3, &v3, &v4);
            D3DXVec3Cross(&n4, &v4, &v1);
        }
        else {
            D3DXVec3Cross(&n1, &v2, &v1);
            D3DXVec3Cross(&n2, &v3, &v2);
            D3DXVec3Cross(&n3, &v4, &v3);
            D3DXVec3Cross(&n4, &v1, &v4);
        }
        pdvVertices[i].vNormal.x = (n1.x + n2.x + n3.x + n4.x) / (float)(4.0);
        pdvVertices[i].vNormal.y = (n1.y + n2.y + n3.y + n4.y) / (float)(4.0);
        pdvVertices[i].vNormal.z = (n1.z + n2.z + n3.z + n4.z) / (float)(4.0);
        NormalizeVertexNormal(&pdvVertices[i]);
        if (pdvVertices[i].vNormal.x < (float)(0.00001) && pdvVertices[i].vNormal.x > (float)(-0.00001)) {
            pdvVertices[i].vNormal.x = (float)(0.0);
        }
        if (pdvVertices[i].vNormal.y < (float)(0.00001) && pdvVertices[i].vNormal.y > (float)(-0.00001)) {
            pdvVertices[i].vNormal.y = (float)(0.0);
        }
        if (pdvVertices[i].vNormal.z < (float)(0.00001) && pdvVertices[i].vNormal.z > (float)(-0.00001)) {
            pdvVertices[i].vNormal.z = (float)(0.0);
        }
    }

    for (i = uNumVertices - uRevolutions; i < uNumVertices; i++) {
        if (bPointedBottom && uSteps > 1) {
            pdvVertices[i].vNormal.x = (float)(0.0);
            pdvVertices[i].vNormal.y = (float)(-1.0);
            pdvVertices[i].vNormal.z = (float)(0.0);
        }
        else {
            if (bPointedBottom) {
                v1 = SubtractVertexVector(pdvVertices[!(i%uRevolutions)?i-1:i-1-uRevolutions], pdvVertices[i]);
                v3 = SubtractVertexVector(pdvVertices[!((i+1)%uRevolutions)?i-uRevolutions+1-uRevolutions:i+1-uRevolutions], pdvVertices[i]);
            }
            else {
                v1 = SubtractVertexVector(pdvVertices[!(i%uRevolutions)?i+uRevolutions-1:i-1], pdvVertices[i]);
                v3 = SubtractVertexVector(pdvVertices[!((i+1)%uRevolutions)?i-uRevolutions+1:i+1], pdvVertices[i]);
            }
            v2 = SubtractVertexVector(pdvVertices[i-uRevolutions], pdvVertices[i]);
            if (bConvex) {
                D3DXVec3Cross(&n1, &v1, &v2);
                D3DXVec3Cross(&n2, &v2, &v3);
            }
            else {
                D3DXVec3Cross(&n1, &v2, &v1);
                D3DXVec3Cross(&n2, &v3, &v2);
            }
            pdvVertices[i].vNormal.x = (n1.x + n2.x) / (float)(2.0);
            pdvVertices[i].vNormal.y = (n1.y + n2.y) / (float)(2.0);
            pdvVertices[i].vNormal.z = (n1.z + n2.z) / (float)(2.0);
            NormalizeVertexNormal(&pdvVertices[i]);
            if (pdvVertices[i].vNormal.x < (float)(0.00001) && pdvVertices[i].vNormal.x > (float)(-0.00001)) {
                pdvVertices[i].vNormal.x = (float)(0.0);
            }
            if (pdvVertices[i].vNormal.y < (float)(0.00001) && pdvVertices[i].vNormal.y > (float)(-0.00001)) {
                pdvVertices[i].vNormal.y = (float)(0.0);
            }
            if (pdvVertices[i].vNormal.z < (float)(0.00001) && pdvVertices[i].vNormal.z > (float)(-0.00001)) {
                pdvVertices[i].vNormal.z = (float)(0.0);
            }
        }
    }

    // Allocate the center vertices
    prv->uCenterVertices = (uSteps + 1) * (uRevolutions + 1);
    prv->prCenter = (PVERTEX)GosMalloc(prv->uCenterVertices * sizeof(VERTEX), DBGNAME("Center Vertices") );
    if (!prv->prCenter) {
        delete [] pdvVertices;
        return FALSE;
    }

    for (i = 0; i <= uSteps; i++) {
        memcpy(&prv->prCenter[i*(uRevolutions+1)], &pdvVertices[i*uRevolutions], uRevolutions * sizeof(VERTEX));
        prv->prCenter[(i+1)*(uRevolutions+1)-1] = pdvVertices[i*uRevolutions];
        prv->prCenter[(i+1)*(uRevolutions+1)-1].u0 = bConvex?fScaleU:(float)(0.0);
    }

    // Add the list indices
    prv->uCenterIndices = uRevolutions * uSteps * 6;
    prv->pwCenter = new WORD[prv->uCenterIndices];
    if (!prv->pwCenter) {
        delete [] prv->prCenter;
        delete [] pdvVertices;
        return FALSE;
    }

    k = 0;
    for (i = 1; i <= uSteps; i++) {
        for (j = 0; j < uRevolutions; j++) {
            prv->pwCenter[k++] = i*(uRevolutions+1)+j;
            prv->pwCenter[k++] = (i-1)*(uRevolutions+1)+j;
            prv->pwCenter[k++] = i*(uRevolutions+1)+j+1;
            prv->pwCenter[k++] = (i-1)*(uRevolutions+1)+j;
            prv->pwCenter[k++] = (i-1)*(uRevolutions+1)+j+1;
            prv->pwCenter[k++] = i*(uRevolutions+1)+j+1;
        }
    }

    // Close off the ends
    if (bConvex) {
        for (i = 0; i < uRevolutions / 2; i++) {
            rTemp = pdvVertices[i];
            pdvVertices[i] = pdvVertices[uRevolutions - i - 1];
            pdvVertices[uRevolutions - i - 1] = rTemp;
        }
    }
    else {
        for (i = 0; i < uRevolutions / 2; i++) {
            rTemp = pdvVertices[uNumVertices - uRevolutions + i];
            pdvVertices[uNumVertices - uRevolutions + i] = pdvVertices[uNumVertices - i - 1];
            pdvVertices[uNumVertices - i - 1] = rTemp;
        }
    }
    v1 = SubtractVertexVector(pdvVertices[1], pdvVertices[0]);
    v2 = SubtractVertexVector(pdvVertices[2], pdvVertices[0]);
    D3DXVec3Cross(&v1, &v1, &v2);
    D3DXVec3Normalize(&v1, &v1);
    if (v1.x < (float)(0.00001) && v1.x > (float)(-0.00001)) {
        v1.x = (float)(0.0);
    }
    if (v1.y < (float)(0.00001) && v1.y > (float)(-0.00001)) {
        v1.y = (float)(0.0);
    }
    if (v1.z < (float)(0.00001) && v1.z > (float)(-0.00001)) {
        v1.z = (float)(0.0);
    }
    for (i = 0; i < uRevolutions; i++) {
        pdvVertices[i].vNormal.x = v1.x;
        pdvVertices[i].vNormal.y = v1.y;
        pdvVertices[i].vNormal.z = v1.z;
    }
    v1 = SubtractVertexVector(pdvVertices[uNumVertices - uRevolutions + 1], pdvVertices[uNumVertices - uRevolutions]);
    v2 = SubtractVertexVector(pdvVertices[uNumVertices - uRevolutions + 2], pdvVertices[uNumVertices - uRevolutions]);
    D3DXVec3Cross(&v1, &v1, &v2);
    D3DXVec3Normalize(&v1, &v1);
    if (v1.x < (float)(0.00001) && v1.x > (float)(-0.00001)) {
        v1.x = (float)(0.0);
    }
    if (v1.y < (float)(0.00001) && v1.y > (float)(-0.00001)) {
        v1.y = (float)(0.0);
    }
    if (v1.z < (float)(0.00001) && v1.z > (float)(-0.00001)) {
        v1.z = (float)(0.0);
    }
    for (i = uNumVertices - uRevolutions; i < uNumVertices; i++) {
        pdvVertices[i].vNormal.x = v1.x;
        pdvVertices[i].vNormal.y = v1.y;
        pdvVertices[i].vNormal.z = v1.z;
    }
    if (pdvVertices[0].vPosition.x != pdvVertices[1].vPosition.x 
                                || pdvVertices[0].vPosition.y != pdvVertices[1].vPosition.y
                                || pdvVertices[0].vPosition.z != pdvVertices[1].vPosition.z) {
        prv->prTop = (PVERTEX)GosMalloc(uRevolutions * sizeof(VERTEX), DBGNAME("Revolution Vertices") );
        if (prv->prTop) {
            memcpy(prv->prTop, pdvVertices, uRevolutions * sizeof(VERTEX));
            prv->uTopVertices = uRevolutions;
        }
    }
    if ((pdvVertices + uNumVertices - uRevolutions)->vPosition.x != (pdvVertices + uNumVertices - uRevolutions + 1)->vPosition.x
        || (pdvVertices + uNumVertices - uRevolutions)->vPosition.y != (pdvVertices + uNumVertices - uRevolutions + 1)->vPosition.y
        || (pdvVertices + uNumVertices - uRevolutions)->vPosition.z != (pdvVertices + uNumVertices - uRevolutions + 1)->vPosition.z) {
        prv->prBottom = (PVERTEX)GosMalloc(uRevolutions * sizeof(VERTEX), DBGNAME("Revolution Vertices2"));
        if (prv->prBottom) {
            memcpy(prv->prBottom, pdvVertices + uNumVertices - uRevolutions, uRevolutions * sizeof(VERTEX));
            prv->uBottomVertices = uRevolutions;
        }
    }
        
    delete [] pdvVertices;

    return TRUE;
}


//******************************************************************************
//
// Function:
//
//     GetSteps
//
// Description:
//
//     Generate a set of vertices defining a three dimensional object 
//     according to a given radius function "revolved" around the y axis
//     (to determine the discrete points of the vertices).
//
// Arguments:
//
//     float fLength             - Length of the object along the y axis
//
//     UINT* puSteps (In)           - Pointer to the maximum number of steps 
//                                    along the y axis at which the radius 
//                                    function will be evaluated to generate
//                                    vertices.
//
//     UINT* puSteps (Out)          - Actual number of culled steps to use
//
//     float* pfSteps           - Array to be filled with the step values
//                                    along the y axis at which the radius
//                                    function will be evaluated
//
//     RADPROC pfnRadius            - Radius function used to shape the object
//
//     LPVOID pvContext             - User variable passed into the radius function
//
//     float fThreshold          - Threshold value used to cull steps along
//              (Optional)            the y axis.  If the rate of change from
//                                    one step to the next is not significant
//                                    enough according to the threshold, the
//                                    vertices at that step will not be included
//                                    in the final mesh.
//
// Return Value:
//
//     None.
//
//******************************************************************************
static void GetSteps(float fLength, UINT* puSteps, float* pfSteps,
                     RADPROC pfnRadius, LPVOID pvContext, float fThreshold) 
{
    float fStep = fLength / ((float)(*puSteps));
    float fChange;
    float fRadius1, fRadius2;
    float fRadiusDelta1, fRadiusDelta2;
    float f;

    pfSteps[0] = fLength;
    *puSteps = 1;

    fRadius1 = pfnRadius(fLength - fStep, fLength, pvContext);
    fRadiusDelta1 = pfnRadius(fLength, fLength, pvContext) - fRadius1;

    for (f = fLength - fStep; f > 0.00001f; f -= fStep) {

        fRadius2 = pfnRadius(f - fStep, fLength, pvContext);
        fRadiusDelta2 = fRadius1 - fRadius2;
        fChange = (float)(fabs(fRadiusDelta1 - fRadiusDelta2));
        fRadius1 = fRadius2;
        fRadiusDelta1 = fRadiusDelta2;
        if (fChange > fThreshold) {
            // The rate of change of the function at this step is significant
            // enough to include the vertices in the revolution
            pfSteps[(*puSteps)++] = f;
        }
    }
}

//SubtractVertexVector
static D3DVECTOR SubtractVertexVector(VERTEX r1, VERTEX r2) 
{

    D3DXVECTOR3 v;

    v.x = r1.vPosition.x - r2.vPosition.x;
    v.y = r1.vPosition.y - r2.vPosition.y;
    v.z = r1.vPosition.z - r2.vPosition.z;

    return v;
}

//NormalizeVertexNormal
static void NormalizeVertexNormal(PVERTEX pr) {

/*
    D3DXVECTOR3 v;
    v.x = pr->vNormal.x;
    v.y = pr->vNormal.y;
    v.z = pr->vNormal.z;
    D3DXVec3Normalize(&v, &v);
    pr->vNormal.x = v.x;
    pr->vNormal.y = v.y;
    pr->vNormal.z = v.z;
*/
    D3DXVec3Normalize(&pr->vNormal, &pr->vNormal);
}


//******************************************************************************
// Revolution profiles
//******************************************************************************

//******************************************************************************
//
// Function:
//
//     BaseRadius
//
// Description:
//
//     Describe the function profile of the lighthouse base.  This function will
//     return the radius of the base at a given height.
//
// Arguments:
//
//     float y               - Height at which to evaluate the radius
//
//     float fLength         - Maximum height
//
//     LPVOID pvContext         - Variable supplied by the calling function.
//
// Return Value:
//
//     The radius at the given height.
//
//******************************************************************************
float BaseRadius(float y, float fHeight, LPVOID pvContext) {

    return 0.45f;
}


//******************************************************************************
//
// Function:
//
//     BalconyRadius
//
// Description:
//
//     Describe the function profile of the lighthouse balcony.  This function
//     will return the radius of the balcony at a given height.
//
// Arguments:
//
//     float y               - Height at which to evaluate the radius
//
//     float fLength         - Maximum height
//
//     LPVOID pvContext         - Variable supplied by the calling function.
//
// Return Value:
//
//     The radius at the given height.
//
//******************************************************************************
float BalconyRadius(float y, float fHeight, LPVOID pvContext) {

    return 0.2f;
}


//******************************************************************************
//
// Function:
//
//     PortalRadius
//
// Description:
//
//     Describe the function profile of the lighthouse portal.  This function 
//     will return the radius of the portal at a given height.
//
// Arguments:
//
//     float y               - Height at which to evaluate the radius
//
//     float fLength         - Maximum height
//
//     LPVOID pvContext         - Variable supplied by the calling function.
//
// Return Value:
//
//     The radius at the given height.
//
//******************************************************************************
float PortalRadius(float y, float fHeight, LPVOID pvContext) {

    return 0.125f;
}

//******************************************************************************
//
// Function:
//
//     RoofRadius
//
// Description:
//
//     Describe the function profile of the lighthouse roof.  This function will
//     return the radius of the roof at a given height.
//
// Arguments:
//
//     float y               - Height at which to evaluate the radius
//
//     float fLength         - Maximum height
//
//     LPVOID pvContext         - Variable supplied by the calling function.
//
// Return Value:
//
//     The radius at the given height.
//
//******************************************************************************
float RoofRadius(float y, float fHeight, LPVOID pvContext) {

    return 0.2f * ((0.25f - y) / 0.25f);
}


//******************************************************************************
//
// Function:
//
//     TrunkRadius
//
// Description:
//
//     Describe the function profile of the lighthouse trunk.  This function
//     will return the radius of the trunk at a given height.
//
// Arguments:
//
//     float y               - Height at which to evaluate the radius
//
//     float fLength         - Maximum height
//
//     LPVOID pvContext         - Variable supplied by the calling function.
//
// Return Value:
//
//     The radius at the given height.
//
//******************************************************************************
float TrunkRadius(float y, float fHeight, LPVOID pvContext) {

    return 0.15f + 0.125f * ((2.4f - y) / 2.4f);
}


//******************************************************************************
//
// Function:
//
//     ReleaseRevolution
//
// Description:
//
//     Release the memory allocated in the creation of the revolved object.
//
// Arguments:
//
//     None.
//
// Return Value:
//
//     None.
//
//******************************************************************************
void ReleaseRevolution(PREVOLUTION prv) {

    if (prv->prCenter) {
        GosFree(prv->prCenter);
        prv->prCenter = NULL;
    }
    if (prv->pwCenter) {
		//BUGBUG: calling right kind of operator here?
        delete [] prv->pwCenter;
        prv->pwCenter = NULL;
    }
    if (prv->prTop) {
        GosFree(prv->prTop);
        prv->prTop = NULL;
    }
    if (prv->prBottom) {
        GosFree(prv->prBottom);
        prv->prBottom = NULL;
    }
}


//******************************************************************************
//
// Function:
//
//     InitWaveSources
//
// Description:
//
//     Initialize the wave sources for the water.
//
// Arguments:
//
//     PWAVESOURCE pws          - Array of wave sources to initialize
//
// Return Value:
//
//     None.
//
//******************************************************************************
void InitWaveSources(PWAVESOURCE pws) {
    
    UINT i;

    pws[0].vPos = D3DXVECTOR3(0.0f, 0.0f, 20.0f);
        pws[0].fWavelength = 1.0f;

    for (i = 0; i < NUM_WAVE_SOURCES; i++) {
        pws[i].fFrequency = M_PI / 8.0f;
        pws[i].fAmplitude = 0.05f;
        pws[i].fPhase = 0.0f;
    }
}


//******************************************************************************
//
// Function:
//
//     GenerateWaveNormals
//
// Description:
//
//     Generate vertex normals for the given wave mesh.
//
// Arguments:
//
//     LPD3DVERTEX pr           - Array of vertices describing the wave mesh.
//
// Return Value:
//
//     None.
//
//******************************************************************************
void GenerateWaveNormals(PHHVERTEX pr, float* m_pfSqrtLookup) {

    D3DXVECTOR3 *pvFaceNormal, *pv;
    D3DXVECTOR3 *pv1, *pv2, *pv3, *pv4, *pv5, *pv6;
    PHHVERTEX   prn;
    D3DXVECTOR3 v1, v2, v3, v4, v5, v6;
    float       f1, f2, fMag;
    UINT        x, y, uBase, uBase2, uBase3, x2;

    pvFaceNormal = (D3DXVECTOR3*)GosMalloc(((WAVE_GRID_DIM_Y - 1) 
                                    * (WAVE_GRID_DIM_X - 1) * 2) * sizeof(D3DXVECTOR3), DBGNAME("Wave Normals"));
    if (!pvFaceNormal) {
        return;
    }

    v1.x = WAVE_UNIT_LENGTH_X;
    v1.z = 0.0f;
    v2.x = 0.0f;
    v2.z = -WAVE_UNIT_LENGTH_Y;
    v3.x = 0.0f;
    v3.z = WAVE_UNIT_LENGTH_Y;
    v4.x = -WAVE_UNIT_LENGTH_X;
    v4.z = 0.0f;
    v5.y = v1.z * v2.x - v1.x * v2.z;
    v6.y = v4.z * v3.x - v4.x * v3.z;
    f1 = v5.y * v5.y;
    f2 = v6.y * v6.y;

    // Generate face normals for the triangles
    for (y = 0; y < WAVE_GRID_DIM_Y - 1; y++) {

        uBase = y * WAVE_GRID_DIM_X;
        uBase2 = y * (WAVE_GRID_DIM_X - 1) * 2;

        for (x = 0, x2 = 0; x < WAVE_GRID_DIM_X - 1; x++, x2 += 2) {

            v1.y = pr[uBase + x + 1].y - pr[uBase + x].y;
            v2.y = pr[uBase + WAVE_GRID_DIM_X + x].y - pr[uBase + x].y;
            v5.x = v1.y * v2.z - v1.z * v2.y; 
            v5.z = v1.x * v2.y - v2.y * v1.x;
            pv = &pvFaceNormal[uBase2 + x2];
            fMag = 6.0f * m_pfSqrtLookup[(UINT)(((v5.x * v5.x + f1 + v5.z * v5.z) 
                        - 0.000368) / 0.000132 * SQRT_TABLE_SIZE)];
            pv->x = v5.x / fMag;
            pv->y = v5.y / fMag;
            pv->z = v5.z / fMag;

            v3.y = pr[uBase + x + 1].y - pr[uBase + WAVE_GRID_DIM_X + x + 1].y;
            v4.y = pr[uBase + WAVE_GRID_DIM_X + x].y 
                        - pr[uBase + WAVE_GRID_DIM_X + x + 1].y;
            v6.x = v4.y * v3.z - v4.z * v3.y;
            v6.z = v4.x * v3.y - v4.y * v3.x;
            pv = &pvFaceNormal[uBase2 + x2 + 1];
            fMag = 6.0f * m_pfSqrtLookup[(UINT)(((v6.x * v6.x + f2 + v6.z * v6.z) 
                        - 0.000368) / 0.000132 * SQRT_TABLE_SIZE)];
            pv->x = v6.x / fMag;
            pv->y = v6.y / fMag;
            pv->z = v6.z / fMag;
        }
    }

    // Generate vertex normals by averaging the adjoining face normals
    for (y = 1; y < WAVE_GRID_DIM_Y - 1; y++) {

        uBase = y * (WAVE_GRID_DIM_X - 1) * 2;
        uBase2 = (y - 1) * (WAVE_GRID_DIM_X - 1) * 2;
        uBase3 = y * WAVE_GRID_DIM_X;

        for (x = 1, x2 = 2; x < WAVE_GRID_DIM_X - 1; x++, x2 += 2) {

            pv1 = &pvFaceNormal[uBase2 + x2 - 1];
            pv2 = &pvFaceNormal[uBase2 + x2];
            pv3 = &pvFaceNormal[uBase2 + x2 + 1];
            pv4 = &pvFaceNormal[uBase + x2 - 2];
            pv5 = &pvFaceNormal[uBase + x2 - 1];
            pv6 = &pvFaceNormal[uBase + x2];
            prn = &pr[uBase3 + x];
            prn->nx = pv1->x + pv2->x + pv3->x + pv4->x + pv5->x + pv6->x;
            prn->ny = pv1->y + pv2->y + pv3->y + pv4->y + pv5->y + pv6->y;
            prn->nz = pv1->z + pv2->z + pv3->z + pv4->z + pv5->z + pv6->z;
        }
    }

    GosFree(pvFaceNormal);
}


//******************************************************************************
//
// Function:
//
//     SetMaterialTranslucency
//
// Description:
//
//     Set the alpha channel information on a material.
//
// Arguments:
//
//     D3DMATERIAL8* pmaterial              - Material to make translucent
//
//     float fAlpha                         - Translucency value ranging from
//                                            0.0 to 1.0, where 0.0 is 
//                                            transparent and 1.0 is opaque
//
// Return Value:
//
//     None.
//
//******************************************************************************
void SetMaterialTranslucency(D3DMATERIAL8* pmaterial, float fAlpha) {

    if (pmaterial) {

        // Set the alpha information
        pmaterial->Ambient.a = fAlpha;
        pmaterial->Diffuse.a = fAlpha;
        pmaterial->Specular.a = fAlpha;
        pmaterial->Emissive.a = fAlpha;
    }
}


//******************************************************************************
//
// Function:
//
//     LightConeRadius
//
// Description:
//
//     Describe the function profile of the lighthouse spotlight cone.  This
//     function will return the radius of the light cone at a given height.
//
// Arguments:
//
//     float y               - Height at which to evaluate the radius
//
//     float fLength         - Maximum height
//
//     LPVOID pvContext         - Variable supplied by the calling function.
//
// Return Value:
//
//     The radius at the given height.
//
//******************************************************************************
float LightConeRadius(float y, float fHeight, LPVOID pvContext) {

    return (fHeight - y) / fHeight * 1.6f;
}


//******************************************************************************
//
// Function:
//
//     SetSpotLight
//
// Description:
//
//     Set a spot light using the given light data.
//
// Arguments:
//
//     D3DLIGHT8* plight            - Pointer to the light to set
//
//     D3DXVECTOR3 vPos             - Position of the light
//
//     D3DXVECTOR3 vDir             - Direction of the light
//
//     D3DCOLOR c (Optional)        - Color of the light
//
//     float fRange (Optional)      - Range of the light
//
//     float fFalloff (Optional)    - Falloff of a spotlight's penumbra
//
//     float fAttenuation0          - Change in the light's intensity
//              (Optional)
//
//     float fAttenuation1          - Change in the light's intensity
//              (Optional)
//
//     float fAttenuation2          - Change in the light's intensity
//              (Optional)
//
//     float fTheta (Optional)      - Angle of a spotlight's umbra
//
//     float fPhi (Optional)        - Angle of a spotlight's penumbra
//
// Return Value:
//
//     None.
//
//******************************************************************************
void SetSpotLight(D3DLIGHT8* plight, D3DXVECTOR3 vPos, 
                                D3DXVECTOR3 vDir, D3DCOLOR c, float fRange, 
                                float fFalloff, float fAttenuation0, 
                                float fAttenuation1, float fAttenuation2, 
                                float fTheta, float fPhi)
{
    SetLight(plight, D3DLIGHT_SPOT, vPos, vDir, c, c, c,
                       fRange, fFalloff, fAttenuation0, fAttenuation1, 
                       fAttenuation2, fTheta, fPhi);
}
