/*
	Software renderer
    Copyright (C) 2006  Darragh Coy.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Win.cpp
//
// Author:      Darragh Coy (GAM15039)
// Purpose:     Intialises the Win32 window. Handles shutdown and events also.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include "Win.h"
#include "PolygonList.h"

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Private module prototypes: Not for outside use!
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    void Win_CreateClass();     // Register a windows 'class' with windows
    void Win_CreateWindow();    // Actual window creation
    void Win_MessageLoop();     // Windows message loop
    
    // Windows event handling procedure
    
    LRESULT CALLBACK Win_WinProc(HWND,UINT,WPARAM,LPARAM);             
    
    // Actual event handling implementations
    
    void Win_Event_Paint();         // Paint event
    void Win_Event_KeyDown(short);  // Key press event
    void Win_Event_KeyUp(short);    // Key press event    
    
    // Camera movement
    
    void Win_MoveCamera();    

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Variables
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    // Program

    HINSTANCE   prgHandle   = NULL;     // Handle to program
    HWND        winHandle   = NULL;     // Handle to the created window
    WNDCLASSEX  winClass    = {0};      // Window class for this window
    
    // Camera

    float camera_angle      = 0;
    float camera_x          = 40;
    float camera_y          = 0 ;
    float camera_z          =-20;
    
    bool  cam_turn_left         = false;
    bool  cam_turn_right        = false;    
    bool  cam_move_up           = false;    
    bool  cam_move_down         = false; 
    bool  cam_move_forward      = false;       
    bool  cam_move_backward     = false;  
    
    // Time the last frame took: use for framerate independent movement 
    
    long double lastFrameTime = 0.0; 

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_Message()
//
// Purpose:    Shows a simple message box
// Params:     String pointer
// Returns:    None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_Message ( char * message , char * title )
{
     // Show the message box using the windows message box function  
     
     MessageBoxA(winHandle,message,title,MB_OK);           
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_Init()
//
// Purpose:    Handles the creation of the window and initialisation
// Params:     None
// Returns:    None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_Init ( void )
{
     // Get the Handle or pointer to the program from windows

     prgHandle = (HINSTANCE) GetModuleHandle( NULL );
     
     // Ensure that program handle is OK
     
     if ( prgHandle == 0 )
     { 
          // If got a NULL pointer then exit immediately
          
          Win_Message("Unable to create window","Error"); return;
     }

     Win_CreateClass();     // Create the window class description
     Win_CreateWindow();    // Create window itself
     Win_MessageLoop();     // Enter windows messaging loop
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_CreateClass()
//
// Purpose:     Creates and registers the windows class with windows. All windows 
//              must have a window class. 
// Parames:     None
// Returns:     None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_CreateClass ( void )
{
     // Reset window class memory to zero initially. Good idea in case something
     // is forgotten to be filled in.
     
     memset( & winClass , 0 , sizeof winClass );
     
     // Begin making settings to the window class
     
     winClass.cbSize        = sizeof winClass;                  // Class size
     winClass.cbClsExtra    = 0;                                // No extra memory in class struct
     winClass.cbWndExtra    = 0;                                // No extra memory for window     
     winClass.style         = CS_HREDRAW | CS_VREDRAW;          // Style: redraw on arbitrary window move
     winClass.lpfnWndProc   = Win_WinProc;                      // Pointer to window procedure
     winClass.hInstance     = prgHandle;                        // Handle to program
     winClass.hIcon         = LoadIcon( NULL , IDI_WINLOGO );   // Program Icon
     winClass.hCursor       = LoadCursor( NULL , IDC_ARROW );   // Program cursor
     winClass.hbrBackground = NULL;                             // Manually redraw background: No brush
     winClass.lpszMenuName  = NULL;                             // No menu
     winClass.lpszClassName = "MyWindowClass";                  // Window classname
     winClass.hIconSm       = LoadIcon( NULL , IDI_WINLOGO );   // Small window icon
     
     // Register the window class with windows: 
     
     RegisterClassEx(&winClass);
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_CreateWindow()
//
// Purpose:    Actually creates the Win32 window using the registered window class
// Parames:    None
// Returns:    None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_CreateWindow ( void )
{
     // Decide on window style first: No borders or menus or icons
               
     int winStyle = WS_OVERLAPPEDWINDOW;
     
     // Create the window and save its handle
     
     winHandle = CreateWindowEx
        (
        0,                      // Extended window style settings. Don't care about this
        winClass.lpszClassName, // Window class name
        WIN_TITLE,              // Window title as defined in this modules header
        winStyle,               // Regular window style
        0,                      // Window position ,x
        0,                      // Window position ,y        
        WIN_X,                  // Window width
        WIN_Y,                  // Window height
        NULL,                   // No parent window
        NULL,                   // No menu
        prgHandle,              // Handle to program
        NULL                    // No pointer to window creation data
        );
        
     // Show the window
     
     ShowWindow( winHandle , SW_SHOWNORMAL );          
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_MessageLoop() 
//
// Purpose:     This is the main 'Thread' for the program. It runs continuoulsy
//              until a WM_QUIT message is found in the programs message queue. 
//              This function simply runs constantly and dispatches any messages found
//              to the window procedure for processing. It is an essential piece
//              of code for any Win32 app.
// Params:      None
// Returns:     None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_MessageLoop ( void )
{
     // Hold the next message in the queue here
     
     MSG message;
     
     // Keep looping until there are no messages in the queue. I.e when the
     // program is finished this will happen...
     
     do
     {
          // Get the next message 
          
          GetMessage( &message , NULL , 0 , 0 );
           
          // Dispatch this message to the window procedure
           
          DispatchMessage(&message); 
              
     } while ( PeekMessage(&message,winHandle,0,0,PM_NOREMOVE) );
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_WinProc()
//
// Purpose:     This is where the beef is. All the events that happen to the window
//              get sent here in the form of messages. These messages can either
//              be processed by the application or handled by the default window 
//              procedure, which is part of the windows library.
// Params:      (1) Handle to the window
//              (2) Message identifier 
//              (3) First window paramter, 16 bit or one word
//              (4) Second window parameter, 32 bit or long
// Returns:     Error code, much like main. Mostly just return 0 for success
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

LRESULT CALLBACK Win_WinProc ( HWND winHandle, UINT message, WPARAM param1, LPARAM param2 )
{
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Decide what to do based on message
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    switch ( message )
    {

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Paint method: 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
        case WM_PAINT: { Win_Event_Paint(); } break;
       
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Key pressed down. Virual key code is in first param
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
        case WM_KEYDOWN:{ Win_Event_KeyDown( (short)(param1&0xffff) ); }break;
       
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Key released. Virual key code is in first param
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
        case WM_KEYUP:{ Win_Event_KeyUp( (short)(param1&0xffff) ); }break;

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Window closing: shutdown renderer and destroy the window
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
        case WM_CLOSE: { R_ShutDown(); DestroyWindow( winHandle ); }break;
       
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Window closing: destroy the window
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
        case WM_QUIT:        
        {    
            // Shutdown renderer and ask windows to destroy the window 
                
            R_ShutDown(); DestroyWindow( winHandle );

       }break;       
       
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Window created: 
        ////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
        case WM_CREATE:
        {
            // Show control info message
            
            Win_Message
                (
                "Welcome, controls are as follows: \n"
                "\n"
                "up - move forward\n"
                "down - move backward\n"
                "left - turn left\n"
                "right - turn right\n" 
                "a - move camera up\n"
                "z - move camera down\n"
                "esc - quit\n"  
                "\n",
                "Software Renderer V 1.02 - Controls"
                );
            
            // Initialise the renderer
            
            R_Init(winHandle);
            
        }break;
           
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Default case: all events we don't want to handle end up here. Call the
        // default window procedure and let it do whatever it wants with the message
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
        default:
        {
            // Call the default window procedure and return its result
            
            return DefWindowProc(winHandle,message,param1,param2);
            
        }break;

    } // End switch ( message )
    
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Return 0 for success 
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    return 0;
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_Event_Paint()
//
// Purpose:     Repaint method for the window
// Params:      None
// Returns:     None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_Event_Paint()
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Clear the screen, begin draw, and start performance counter
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        Win_UpdateTime();
        R_UpdatePerformanceCounter();        
        R_BeginDraw();
        R_ClearScreen();
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Move the camera
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        
        
        Win_MoveCamera();

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Setup transformation matrix for this draw
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        R_LoadIdentity();                               // Load identity transform
        R_Translate(-camera_x,-camera_y,-camera_z);     // Translate
        R_RotateY(camera_angle);                        // Rotate

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Draw all the polygons
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        struct Polygon * currentPoly = polygons;
        struct Polygon * polygonsEnd = polygons + sizeof(polygons) / sizeof(struct Polygon);
        
        while ( currentPoly < polygonsEnd )
        {
            // Reset the polygon flags
            
            currentPoly->polyFlags = 0;
            
            // Draw the polygon
            
            R_DrawPoly(currentPoly,true);
            
            // Move onto the next polygon
            
            currentPoly++;
        }

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Update performance timer and clock count
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        R_UpdatePerformanceCounter();
        unsigned int clocks = R_GetClockCount();
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Finish up and swap the back buffer onto the front
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        R_EndDraw();         

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Draw clock count to the screen
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        char clockStr[128] = {0};
		sprintf_s(clockStr,128,"%u",clocks);
        
        R_DrawText(0,0,"Clock Count: ");
        R_DrawText(90,0,clockStr);   
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_MoveCamera
//
// Purpose:     Moves the camera if keys are down
// Params:      None
// Returns:     None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_MoveCamera ( void )
{

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // ROTATION KEYS
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    if ( cam_turn_left )
    {
        camera_angle += CAM_TURN_AMT * float(lastFrameTime);  
    }
    
    if ( cam_turn_right )
    {
        camera_angle -= CAM_TURN_AMT * float(lastFrameTime);
    }
    
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // MOVEMENT KEYS
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    
    
    if ( cam_move_forward )
    {  
        // Move forward
    
        camera_x += sin( camera_angle * TO_RADIANS ) * CAM_MOVE_AMT * float(lastFrameTime);
        camera_z -= cos( camera_angle * TO_RADIANS ) * CAM_MOVE_AMT * float(lastFrameTime);  
    }
    
    if ( cam_move_backward )
    {
        // Move backward

        camera_x -= sin( camera_angle * TO_RADIANS ) * CAM_MOVE_AMT * float(lastFrameTime);
        camera_z += cos( camera_angle * TO_RADIANS ) * CAM_MOVE_AMT * float(lastFrameTime);    
    }
    
    if ( cam_move_up )
    {
        camera_y += CAM_MOVE_AMT * float(lastFrameTime);
    }
    
    if ( cam_move_down )
    {
        camera_y -= CAM_MOVE_AMT * float(lastFrameTime);
    }

}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_Event_KeyDown
//
// Purpose:     Handles key events
// Params:      Key pressed
// Returns:     N/A
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_Event_KeyDown ( short key )
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Set key states depending on which key was pressed
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        switch ( key )
        {
                case VK_RIGHT:      cam_turn_left       = true;    break;
                case VK_LEFT:       cam_turn_right      = true;    break;
                case VK_UP:         cam_move_forward    = true;    break;
                case VK_DOWN:       cam_move_backward   = true;    break;                
                case 'A':           cam_move_up         = true;    break;
                case 'Z':           cam_move_down       = true;    break;                
                
                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Quit key
                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                

                case VK_ESCAPE: { DestroyWindow( winHandle ); }break;
        }
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_Event_KeyUp
//
// Purpose:     Handles key events
// Params:      Key pressed
// Returns:     N/A
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_Event_KeyUp ( short key )
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Set key states depending on which key was pressed
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        switch ( key )
        {

                case VK_RIGHT:      cam_turn_left       = false;    break;
                case VK_LEFT:       cam_turn_right      = false;    break;
                case VK_UP:         cam_move_forward    = false;    break;
                case VK_DOWN:       cam_move_backward   = false;    break;                
                case 'A':           cam_move_up         = false;    break;
                case 'Z':           cam_move_down       = false;    break;                
        }
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Win_UpdateTime
//
// Purpose:     Updates the time the last frame took: use for framerate independent
//              movement...
// Params:      None
// Returns:     N/A
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void Win_UpdateTime()
{
    
    static LARGE_INTEGER lastClockTime      = {0};  // Clock time last time the clock was queried
    static LARGE_INTEGER processorClocks    = {0};  // Speed the processor runs at
    static LARGE_INTEGER currentClockTime   = {0};  // Current Clock Time
    
    QueryPerformanceFrequency(&processorClocks); // Get frequency of processor clock or number of clocks
    QueryPerformanceCounter(&currentClockTime);  // Get the current clock time
    
    // Figure out the number of clocks the last frame took
    
    lastFrameTime = (long double)( *((unsigned int*)&currentClockTime) - *((unsigned int*)&lastClockTime) );
    
    // Divide by frequency of processor to get time in seconds
    
    lastFrameTime /= (long double)( *((unsigned int*)&processorClocks) );
    
    // Save the current frame time
    
    memcpy(&lastClockTime,&currentClockTime,sizeof(LARGE_INTEGER));

}
