//===============================================================================
// @ Player.cpp
// ------------------------------------------------------------------------------
// Player
//
// Copyright (C) 2004 by Elsevier, Inc. All rights reserved.
//
//
//
// This example shows the effects of non-mipmapped rendering, mipmapping without
// interpolation across mipmap levels, and mipmapping with trilinear interpolation.
// Initially, the demo uses bilinear filtering.  The left quad shows a special
// texture image in which each mipmap level is a different solid color.  This
// makes it easy to see the transitions between selected mipmap levels.  The
// right quad is the same size, has the same UVs, and uses a texture that is the
// same size as the one on the left.  However, the right quad is textured with a
// high-contrast checkerboard, to make aliasing and blurring more obvious.
//
// The key commands are:
//
// u, o - rotate around x axis
// p, ; - uniformly scale
// Space - reset transforms
// f - cycle filtering modes
//
//===============================================================================

//-------------------------------------------------------------------------------
//-- 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()
{
    // Rotate the quads so that they are initially oblique, showing multiple
    // mipmap levels across their surfaces (or extreme aliasing if mipmapping is
    // not enabled)
    mRotate.RotationY(-kPI * 0.35f);

    mScale = 1.0f;

    glEnable( GL_TEXTURE_2D );

    // Create a texture with a solid color per mipmap level.  This will make it
    // very clear what mipmap level is being rendered, as each mipmap level has
    // its own color.

    // create a texture "name" to reference later
    glGenTextures( 1, &mMipmapLevelBandsTex );
    glBindTexture( GL_TEXTURE_2D, mMipmapLevelBandsTex );

    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

    unsigned char texels[256 * 256 * 3];

    // Cannot use GLU to generate mipmap levels, as each mipmap level has nothing
    // to do with the previous mipmap level.  Each mipmap level is simply a solid
    // color
    unsigned int i;
    for (i = 0; i < 9; i++)
    {
        unsigned int size = 256 >> i;

        unsigned char red = (i & 0x1) ? 255 : 127;
        unsigned char green = (i & 0x2) ? 255 : 127;
        unsigned char blue = (i & 0x4) ? 255 : 127;
        
        unsigned int j;
        for (j  = 0; j < size * size; j++)
        {
            texels[3 * j + 0] = red;
            texels[3 * j + 1] = green;
            texels[3 * j + 2] = blue;
        }

        glTexImage2D( GL_TEXTURE_2D, i, GL_RGB, size, size, 0, GL_RGB, 
            GL_UNSIGNED_BYTE, texels );
    }

    // Create a classic checkerboard pattern to show off aliasing

    // create a texture "name" to reference later
    glGenTextures( 1, &mCheckerBoardTex );
    glBindTexture( GL_TEXTURE_2D, mCheckerBoardTex );

    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

    unsigned int j;
    for (j  = 0; j < 256; j++)
    {
        for (i  = 0; i < 256; i++)
        {
            unsigned int pixelIndex = 3 * (j * 256 + i);

            // 8x8 texel checkerboard patterns
            if (((j >> 3) + (i >> 3)) & 0x1)
            {
                texels[pixelIndex + 0] = texels[pixelIndex + 1] 
                    = texels[pixelIndex + 2] = 255;
            }
            else
            {
                texels[pixelIndex + 0] = texels[pixelIndex + 1] 
                    = texels[pixelIndex + 2] = 0;
            }
        }
    }

    // Use GLU to automatically generate mipmap levels for this texture
    gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGB, 256, 256, GL_RGB, 
        GL_UNSIGNED_BYTE, texels );

    mFilter = FILTER_BILERP;

}   // End of Player::Player()


//-------------------------------------------------------------------------------
// @ Player::~Player()
//-------------------------------------------------------------------------------
// Destructor
//-------------------------------------------------------------------------------
Player::~Player()
{
    // Free the texture resources we have created
    glDeleteTextures( 1, &mMipmapLevelBandsTex ); 
    glDeleteTextures( 1, &mCheckerBoardTex ); 

}   // End of Player::~Player()


//-------------------------------------------------------------------------------
// @ Player::Update()
//-------------------------------------------------------------------------------
// Main update loop
//-------------------------------------------------------------------------------
void
Player::Update( float dt )
{
    // get scale, rotate, translate for this frame
    float s = 1.0f;
    float r = 0.0f;
    
    // set up scaling
    if (IvGame::mGame->mEventHandler->IsKeyDown(';'))
    {
        s -= 0.25f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('p'))
    {
        s += 0.25f*dt;
    }
    mScale *= s;
    
    // set up rotate
    if (IvGame::mGame->mEventHandler->IsKeyDown('o'))
    {
        r -= kPI*0.25f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('u'))
    {
        r += kPI*0.25f*dt;
    }
    IvMatrix33 rotate;
    rotate.RotationY(r);
    mRotate = rotate*mRotate;
    
    // clear transform
    if (IvGame::mGame->mEventHandler->IsKeyDown(' '))
    {
        mRotate.RotationY(-kPI * 0.35f);
        mScale = 1.0f;
    }

    // cycle through the filtering modes - note that we need to set the filtering modes for
    // both textures, as these are stored as a part of BindTexture
    if (IvGame::mGame->mEventHandler->IsKeyDown('f'))
    {
        switch (mFilter)
        {
        case FILTER_BILERP:
            // Switch to mipmap with bilerp across the selected level
            glBindTexture(GL_TEXTURE_2D, mMipmapLevelBandsTex);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
            glBindTexture(GL_TEXTURE_2D, mCheckerBoardTex);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
            mFilter = FILTER_MIPMAP;
            break;
        case FILTER_MIPMAP:
            // Switch to trilinear filtering
            glBindTexture(GL_TEXTURE_2D, mMipmapLevelBandsTex);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
            glBindTexture(GL_TEXTURE_2D, mCheckerBoardTex);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
            mFilter = FILTER_TRILERP;
            break;
        case FILTER_TRILERP:
            // Switch to non-mipmapped bilinear filtering
            glBindTexture(GL_TEXTURE_2D, mMipmapLevelBandsTex);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glBindTexture(GL_TEXTURE_2D, mCheckerBoardTex);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            mFilter = FILTER_BILERP;
            break;
        };

        IvGame::mGame->mEventHandler->KeyUp('f');
    }

}   // End of Player::Update()


//-------------------------------------------------------------------------------
// @ Player::Render()
//-------------------------------------------------------------------------------
// Render stuff
//-------------------------------------------------------------------------------
void 
Player::Render()                                    
{   
    // build 4x4 matrix
    IvMatrix44 transform(mRotate);

    transform(0,0) *= mScale;
    transform(1,0) *= mScale;
    transform(2,0) *= mScale;
    transform(0,1) *= mScale;
    transform(1,1) *= mScale;
    transform(2,1) *= mScale;
    transform(0,2) *= mScale;
    transform(1,2) *= mScale;
    transform(2,2) *= mScale;
    transform(0,3) = 0.0f;
    transform(1,3) = 0.0f;
    transform(2,3) = 0.0f;
    
    // store current matrix
    glPushMatrix();

    // multiply by transform
    glMultMatrixf(transform);

    // turn off the lights
    glDisable( GL_LIGHTING );

    // draw geometry
    glColor3f(1.0f,1.0f,1.0f);
    DrawQuads();
    
    // restore original matrix          
    glPopMatrix();
} // End of Player::Render()


//-------------------------------------------------------------------------------
// @ Player::DrawQuads()
//-------------------------------------------------------------------------------
// Draw two quads centered around the origin
//-------------------------------------------------------------------------------
void 
Player::DrawQuads()                                 
{   
    // Render a quad with the mipmap band texture
    glBindTexture(GL_TEXTURE_2D, mMipmapLevelBandsTex);

    glBegin(GL_QUADS);

    // left
    glNormal3f(1.0f, 0.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(0.0f, -2.5f, -2.0f);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(0.0f, -0.5f, -2.0f);
    glTexCoord2f(1.0f, 2.0f);
    glVertex3f(0.0f, -0.5f, 2.0f);
    glTexCoord2f(0.0f, 2.0f);
    glVertex3f(0.0f, -2.5f, 2.0f);

    glEnd();

    // Render a quad with the checkerboard
    glBindTexture(GL_TEXTURE_2D, mCheckerBoardTex);

    glBegin(GL_QUADS);

    // right
    glNormal3f(1.0f, 0.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(0.0f, 0.5f, -2.0f);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(0.0f, 2.5f, -2.0f);
    glTexCoord2f(1.0f, 2.0f);
    glVertex3f(0.0f, 2.5f, 2.0f);
    glTexCoord2f(0.0f, 2.0f);
    glVertex3f(0.0f, 0.5f, 2.0f);

    glEnd();
}  // End of Player::DrawQuads()

