//===============================================================================
// @ Player.cpp
// ------------------------------------------------------------------------------
// Player
//
// Copyright (C) 2004 by Elsevier, Inc. All rights reserved.
//
//
//
// This example shows how sharp lighting edges can be created through the
// duplication of vertices.  Two cubes are drawn side by side, lit by the same
// single point light (represented by a 3D plus sign icon in the scene).  The
// cube on the left uses only 8 vertices, sharing them across each coincident
// face.  As a result, while the geometry clearly approximates a cube, the smooth
// lighting edges and shared normals cause the lighting to more closely represent
// a poorly-tesselated sphere.  The cube on the right uses sharp geometric edges
// and per-facet, per-vertex normals.  While this requires significantly more
// vertices to create the cube, the effect of a sharp-edged cube is more 
// convincing.
//
// The key commands are:
//
// i, k - translate light in x
// j, l - translate light in y
// Space - reset transforms
//
//===============================================================================

//-------------------------------------------------------------------------------
//-- Dependencies ---------------------------------------------------------------
//-------------------------------------------------------------------------------

#include <IvEventHandler.h>
#include <IvGLHelp.h>
#include <IvMath.h>
#include <IvMatrix44.h>

#include "Player.h"
#include "Game.h"

//-------------------------------------------------------------------------------
//-- Static Members -------------------------------------------------------------
//-------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
//-- Methods --------------------------------------------------------------------
//-------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
// @ Player::Player()
//-------------------------------------------------------------------------------
// Constructor
//-------------------------------------------------------------------------------
Player::Player()
{
    mLightPos.Set(5.0f, 0.0f, 2.0f, 1.0f);

    mSmoothCubeVerts = 0;
    mSmoothCubeNormals = 0;
    mSmoothCubeIndices = 0;

    mSharpCubeVerts = 0;
    mSharpCubeNormals = 0;
    mSharpCubeIndices = 0;

    CreateSmoothCubeVertexArrays();
    CreateSharpCubeVertexArrays();
}   // End of Player::Player()


//-------------------------------------------------------------------------------
// @ Player::~Player()
//-------------------------------------------------------------------------------
// Destructor
//-------------------------------------------------------------------------------
Player::~Player()
{
    delete[] mSmoothCubeVerts;
    delete[] mSmoothCubeNormals;
    delete[] mSmoothCubeIndices;

    delete[] mSharpCubeVerts;
    delete[] mSharpCubeNormals;
    delete[] mSharpCubeIndices;
}   // End of Player::~Player()


//-------------------------------------------------------------------------------
// @ Player::Update()
//-------------------------------------------------------------------------------
// Main update loop
//-------------------------------------------------------------------------------
void
Player::Update( float dt )
{       
    // translate light source
    IvVector4 xlate;
    xlate.Zero();
    if (IvGame::mGame->mEventHandler->IsKeyDown('k'))
    {
        xlate.x += 3.0f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('i'))
    {
        xlate.x -= 3.0f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('l'))
    {
        xlate.y += 3.0f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('j'))
    {
        xlate.y -= 3.0f*dt;
    }
    mLightPos += xlate;
    
    // clear transform
    if (IvGame::mGame->mEventHandler->IsKeyDown(' '))
    {
        mLightPos.Set(5.0f, 0.0f, 2.0f, 1.0f);
    }

}   // End of Player::Update()


//-------------------------------------------------------------------------------
// @ Player::Render()
//-------------------------------------------------------------------------------
// Render stuff
//-------------------------------------------------------------------------------
void 
Player::Render()                                    
{   
    // store current matrix
    glPushMatrix();

    // multiply by transform
    IvMatrix44 transform;
    transform(0, 3) = mLightPos.x;
    transform(1, 3) = mLightPos.y;
    transform(2, 3) = mLightPos.z;
    glMultMatrixf(transform);

    // turn on the lights
    glDisable( GL_LIGHTING );

    // Draw a visual representation of the light
    DrawLightIcon();

    // restore original matrix          
    glPopMatrix();

    // turn on the lights
    glEnable( GL_LIGHTING );
    glEnable( GL_NORMALIZE );
    glDisable(GL_COLOR_MATERIAL);

    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_POSITION, (float *)&mLightPos);

    // store current matrix
    glPushMatrix();

    // set up translate for smooth cube
    transform(0, 3) = 0.0f;
    transform(1, 3) = -2.0f;
    transform(2, 3) = 0.0f;
    glMultMatrixf(transform);

    // draw smooth-edged cube geometry
    glColor3f(1.0f, 1.0f, 1.0f);
    DrawSmoothCubeVertexArrays();

    // restore original matrix          
    glPopMatrix();

    // store current matrix
    glPushMatrix();

    // set up translate for sharp cube
    transform(0, 3) = 0.0f;
    transform(1, 3) = 2.0f;
    transform(2, 3) = 0.0f;
    glMultMatrixf(transform);

    // draw sharp-edged cube geometry
    glColor3f(1.0f, 1.0f, 1.0f);
    DrawSharpCubeVertexArrays();

    // restore original matrix          
    glPopMatrix();

    glDisable( GL_LIGHTING );

}   // End of Player::Render()


//-------------------------------------------------------------------------------
// @ Player::DrawSmoothCubeVertexArrays()
//-------------------------------------------------------------------------------
// Draw vertex arrays for a shared-vertex cube
//-------------------------------------------------------------------------------
void
Player::DrawSmoothCubeVertexArrays()
{
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, mSmoothCubeVerts); 
    glNormalPointer(GL_FLOAT, 0, mSmoothCubeNormals); 

    glDrawElements(GL_TRIANGLES, mSmoothCubeIndexCount, GL_UNSIGNED_SHORT, 
        mSmoothCubeIndices);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
} // End of Player::DrawSmoothCubeVertexArrays()
    
//-------------------------------------------------------------------------------
// @ Player::DrawSharpCubeVertexArrays()
//-------------------------------------------------------------------------------
// Draw vertex arrays for a sharp-edged (non-shared vertex) cube
//-------------------------------------------------------------------------------
void
Player::DrawSharpCubeVertexArrays()
{
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, mSharpCubeVerts); 
    glNormalPointer(GL_FLOAT, 0, mSharpCubeNormals); 

    glDrawElements(GL_TRIANGLES, mSharpCubeIndexCount, GL_UNSIGNED_SHORT, 
        mSharpCubeIndices);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
} // End of Player::DrawSharpCubeVertexArrays()
    
//-------------------------------------------------------------------------------
// @ Player::CreateSmoothCubeVertexArrays()
//-------------------------------------------------------------------------------
// Create vertex arrays for a shared-vertex cube
//-------------------------------------------------------------------------------
void 
Player::CreateSmoothCubeVertexArrays()                                  
{
    // only need 8 vertices, as we will share vertices across faces
    mSmoothCubeVerts = new IvVector3[8];
    mSmoothCubeNormals = new IvVector3[8];

    // top 4 vertices
    mSmoothCubeVerts[0].Set(-1.0, -1.0, 1.0f);
    mSmoothCubeVerts[1].Set(1.0, -1.0, 1.0f);
    mSmoothCubeVerts[2].Set(1.0, 1.0, 1.0f);
    mSmoothCubeVerts[3].Set(-1.0, 1.0, 1.0f);
    
    // bottom four vertices
    mSmoothCubeVerts[4].Set(-1.0, -1.0, -1.0f);
    mSmoothCubeVerts[5].Set(1.0, -1.0, -1.0f);
    mSmoothCubeVerts[6].Set(1.0, 1.0, -1.0f);
    mSmoothCubeVerts[7].Set(-1.0, 1.0, -1.0f);

    // three per triangle, two triangles per face, six faces
    mSmoothCubeIndexCount = 3 * 2 * 6;
    mSmoothCubeIndices = new unsigned short[mSmoothCubeIndexCount];

    unsigned short* temp = mSmoothCubeIndices;
    // +Z
    *(temp++) = 0;
    *(temp++) = 1;
    *(temp++) = 2;
    *(temp++) = 0;
    *(temp++) = 2;
    *(temp++) = 3;

    // -Z
    *(temp++) = 4;
    *(temp++) = 6;
    *(temp++) = 5;
    *(temp++) = 4;
    *(temp++) = 7;
    *(temp++) = 6;

    // -Y
    *(temp++) = 0;
    *(temp++) = 4;
    *(temp++) = 5;
    *(temp++) = 0;
    *(temp++) = 5;
    *(temp++) = 1;

    // +Y
    *(temp++) = 2;
    *(temp++) = 6;
    *(temp++) = 7;
    *(temp++) = 2;
    *(temp++) = 7;
    *(temp++) = 3;

    // -X
    *(temp++) = 3;
    *(temp++) = 7;
    *(temp++) = 4;
    *(temp++) = 3;
    *(temp++) = 4;
    *(temp++) = 0;

    // +X
    *(temp++) = 1;
    *(temp++) = 5;
    *(temp++) = 6;
    *(temp++) = 1;
    *(temp++) = 6;
    *(temp++) = 2;

    // simply assign sphere-like smooth normals
    unsigned int i;
    for (i = 0; i < 8; i++)
    {
        mSmoothCubeNormals[i] = mSmoothCubeVerts[i];
        mSmoothCubeNormals[i].Normalize();
    }

}   // End of Player::CreateSmoothCubeVertexArrays()

//-------------------------------------------------------------------------------
// @ Player::CreateSmoothCubeVertexArrays()
//-------------------------------------------------------------------------------
// Create vertex arrays for a sharp-edged (non-shared vertex) cube
//-------------------------------------------------------------------------------
void 
Player::CreateSharpCubeVertexArrays()                                   
{
    // need 24 vertices, as we will cannot share vertices across faces
    mSharpCubeVerts = new IvVector3[24];
    mSharpCubeNormals = new IvVector3[24];

    // +Z
    mSharpCubeVerts[0].Set(-1.0, -1.0, 1.0f);
    mSharpCubeVerts[1].Set(1.0, -1.0, 1.0f);
    mSharpCubeVerts[2].Set(1.0, 1.0, 1.0f);
    mSharpCubeVerts[3].Set(-1.0, 1.0, 1.0f);
    mSharpCubeNormals[0] = mSharpCubeNormals[1] = mSharpCubeNormals[2]
        = mSharpCubeNormals[3] = IvVector3::zAxis;
    
    // -Z
    mSharpCubeVerts[4].Set(-1.0, -1.0, -1.0f);
    mSharpCubeVerts[5].Set(-1.0, 1.0, -1.0f);
    mSharpCubeVerts[6].Set(1.0, 1.0, -1.0f);
    mSharpCubeVerts[7].Set(1.0, -1.0, -1.0f);
    mSharpCubeNormals[4] = mSharpCubeNormals[5] = mSharpCubeNormals[6]
        = mSharpCubeNormals[7] = -IvVector3::zAxis;

    // +Y
    mSharpCubeVerts[8].Set(-1.0, 1.0, -1.0f);
    mSharpCubeVerts[9].Set(-1.0, 1.0, 1.0f);
    mSharpCubeVerts[10].Set(1.0, 1.0, 1.0f);
    mSharpCubeVerts[11].Set(1.0, 1.0, -1.0f);
    mSharpCubeNormals[8] = mSharpCubeNormals[9] = mSharpCubeNormals[10]
        = mSharpCubeNormals[11] = IvVector3::yAxis;

    // -Y
    mSharpCubeVerts[12].Set(-1.0, -1.0, -1.0f);
    mSharpCubeVerts[13].Set(1.0, -1.0, -1.0f);
    mSharpCubeVerts[14].Set(1.0, -1.0, 1.0f);
    mSharpCubeVerts[15].Set(-1.0, -1.0, 1.0f);
    mSharpCubeNormals[12] = mSharpCubeNormals[13] = mSharpCubeNormals[14]
        = mSharpCubeNormals[15] = -IvVector3::yAxis;

    // +X
    mSharpCubeVerts[16].Set(1.0f, -1.0, -1.0f);
    mSharpCubeVerts[17].Set(1.0f, 1.0, -1.0f);
    mSharpCubeVerts[18].Set(1.0f, 1.0, 1.0f);
    mSharpCubeVerts[19].Set(1.0f, -1.0, 1.0f);
    mSharpCubeNormals[16] = mSharpCubeNormals[17] = mSharpCubeNormals[18]
        = mSharpCubeNormals[19] = IvVector3::xAxis;

    // -X
    mSharpCubeVerts[20].Set(-1.0f, -1.0, -1.0f);
    mSharpCubeVerts[21].Set(-1.0f, -1.0, 1.0f);
    mSharpCubeVerts[22].Set(-1.0f, 1.0, 1.0f);
    mSharpCubeVerts[23].Set(-1.0f, 1.0, -1.0f);
    mSharpCubeNormals[20] = mSharpCubeNormals[21] = mSharpCubeNormals[22]
        = mSharpCubeNormals[23] = -IvVector3::xAxis;

    // three per triangle, two triangles per face, six faces
    mSharpCubeIndexCount = 3 * 2 * 6;
    mSharpCubeIndices = new unsigned short[mSharpCubeIndexCount];

    unsigned short* temp = mSharpCubeIndices;
    // +Z
    *(temp++) = 0;
    *(temp++) = 1;
    *(temp++) = 2;
    *(temp++) = 0;
    *(temp++) = 2;
    *(temp++) = 3;

    // -Z
    *(temp++) = 4;
    *(temp++) = 5;
    *(temp++) = 6;
    *(temp++) = 4;
    *(temp++) = 6;
    *(temp++) = 7;

    // -Y
    *(temp++) = 8;
    *(temp++) = 9;
    *(temp++) = 10;
    *(temp++) = 8;
    *(temp++) = 10;
    *(temp++) = 11;

    // +Y
    *(temp++) = 12;
    *(temp++) = 13;
    *(temp++) = 14;
    *(temp++) = 12;
    *(temp++) = 14;
    *(temp++) = 15;

    // -X
    *(temp++) = 16;
    *(temp++) = 17;
    *(temp++) = 18;
    *(temp++) = 16;
    *(temp++) = 18;
    *(temp++) = 19;

    // +X
    *(temp++) = 20;
    *(temp++) = 21;
    *(temp++) = 22;
    *(temp++) = 20;
    *(temp++) = 22;
    *(temp++) = 23;

}   // End of Player::CreateSharpCubeVertexArrays()

//-------------------------------------------------------------------------------
// @ DrawLightIcon()
//-------------------------------------------------------------------------------
// Draw icon for light source
//-------------------------------------------------------------------------------
void 
Player::DrawLightIcon()
{
    glBegin(GL_LINES);

    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(-0.25f, 0.0f, 0.0f);
    glVertex3f(0.25f, 0.0f, 0.0f);

    glVertex3f(0.0f, -0.25f, 0.0f);
    glVertex3f(0.0f, 0.25f, 0.0f);

    glVertex3f(0.0f, 0.0f, -0.25f);
    glVertex3f(0.0f, 0.0f, 0.25f);

    glEnd();
    
}   // End of DrawLightIcon()

