/*
	Wheels Of Steel
	Copyright (c) 2006, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:   TrackSection
// Purpose:      Class that represents each horizontal strip of the track
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "TrackSection.h"

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

// Shading

const float TrackSection::SECTION_GROUND_COL_R   = 0.87f;      // section ground color: r
const float TrackSection::SECTION_GROUND_COL_G   = 0.87f;      // section ground color: g
const float TrackSection::SECTION_GROUND_COL_B   = 0.9f;       // section ground color: b

const float TrackSection::SECTION_TARMAC_COL_R   = 0.20f;      // section taramc color: r
const float TrackSection::SECTION_TARMAC_COL_G   = 0.20f;      // section taramc color: g
const float TrackSection::SECTION_TARMAC_COL_B   = 0.20f;      // section taramc color: b

const float TrackSection::SECTION_LINE_LR_COL_R  = 0.8f;       // section line left/right color: r
const float TrackSection::SECTION_LINE_LR_COL_G  = 0.8f;       // section line left/right color: g
const float TrackSection::SECTION_LINE_LR_COL_B  = 0.25f;      // section line left/right color: b

const float TrackSection::SECTION_LINE_C_COL_R   = 0.8f;       // section line center color: r
const float TrackSection::SECTION_LINE_C_COL_G   = 0.8f;       // section line center color: g
const float TrackSection::SECTION_LINE_C_COL_B   = 0.8f;       // section line center color: b

const float TrackSection::SECTION_DIST_FADE_AMT  = 0.000005f;  // amount each section fades with distance

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Static variable declarations

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

HBRUSH TrackSection::brush_table_tarmac[256]={0};     // lookup table of colors for the tarmac
HBRUSH TrackSection::brush_table_ground[256]={0};     // lookup table of colors for the ground
HBRUSH TrackSection::brush_table_lineLR[256]={0};     // lookup table of colors for the left and right lines
HBRUSH TrackSection::brush_table_lineC [256]={0};     // lookup table of colors for the center line

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     TrackSection::TrackSection
Purpose:  Constructor
Params:   As commented.
Returns:  n/a

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

TrackSection::TrackSection
(
    unsigned int section_number,    // which section of the track is this?
    float fl_x,                     // section front left   x
    float fr_x,                     // section front right  x
    float bl_x,                     // section back  left   x
    float br_x,                     // section back  right  x
    float f_y,                      // section front        y
    float b_y                       // section back         y
)
{
    // Set the y and z coordinates for the front and back of the whole road section

    this->section_f_y   = f_y;
    this->section_b_y   = b_y;

    this->section_f_z   = ( section_number + 1 ) * Track::SECTION_SIZE_Z;
    this->section_b_z   = ( section_number + 0 ) * Track::SECTION_SIZE_Z;

    // Set the coordinates for the tarmac part of the section

    this->tarmac_fl_x   = fl_x;
    this->tarmac_fr_x   = fr_x;
    this->tarmac_bl_x   = bl_x;
    this->tarmac_br_x   = br_x;

    // Set the coordinates for the left road line

    this->line_l_fl_x   = fl_x + Track::SECTION_LINE_SIZE_X;
    this->line_l_fr_x   = fl_x + Track::SECTION_LINE_SIZE_X * 2.0f;
    this->line_l_bl_x   = bl_x + Track::SECTION_LINE_SIZE_X;
    this->line_l_br_x   = bl_x + Track::SECTION_LINE_SIZE_X * 2.0f;

    // Set the coordinates for the right road line

    this->line_r_fl_x   = fr_x - Track::SECTION_LINE_SIZE_X * 2.0f;
    this->line_r_fr_x   = fr_x - Track::SECTION_LINE_SIZE_X;
    this->line_r_bl_x   = br_x - Track::SECTION_LINE_SIZE_X * 2.0f;
    this->line_r_br_x   = br_x - Track::SECTION_LINE_SIZE_X;

    //--------------------------------------------------------------------------
    // Set the coordinates for the center road line
    //--------------------------------------------------------------------------

    // Get the x coordinate of the left and right side of the tarmac at 1/3 and 2/3 of track section depth

    float section_l_x1 = tarmac_bl_x + ( tarmac_fl_x - tarmac_bl_x ) * 0.33333f;
    float section_l_x2 = tarmac_bl_x + ( tarmac_fl_x - tarmac_bl_x ) * 0.66666f;
    float section_r_x1 = tarmac_br_x + ( tarmac_fr_x - tarmac_br_x ) * 0.33333f;
    float section_r_x2 = tarmac_br_x + ( tarmac_fr_x - tarmac_br_x ) * 0.66666f;

    // Use interpolation between the left and right values calulated to figure out the x coords at the
    // front and back of the line respectively.

    this->line_c_fl_x = section_l_x2 + ( section_r_x2 - section_l_x2 ) * 0.5f - Track::SECTION_LINE_SIZE_X;
    this->line_c_fr_x = section_l_x2 + ( section_r_x2 - section_l_x2 ) * 0.5f + Track::SECTION_LINE_SIZE_X;
    this->line_c_bl_x = section_l_x1 + ( section_r_x1 - section_l_x1 ) * 0.5f - Track::SECTION_LINE_SIZE_X;
    this->line_c_br_x = section_l_x1 + ( section_r_x1 - section_l_x1 ) * 0.5f + Track::SECTION_LINE_SIZE_X;

    // Set the y and z values for the front and back of the center line

    this->line_c_f_y = section_f_y - ( section_f_y - section_b_y ) * 0.33333f;
    this->line_c_b_y = section_b_y + ( section_f_y - section_b_y ) * 0.33333f;

    this->line_c_f_z = section_f_z - ( section_f_z - section_b_z ) * 0.33333f;
    this->line_c_b_z = section_b_z + ( section_f_z - section_b_z ) * 0.33333f;

    //--------------------------------------------------------------------------
    // Calculate surface normal for track section
    //--------------------------------------------------------------------------

    float tangent_y = this->section_f_y - this->section_b_y;    // get change in y over section (un-normalised tangent vector)
    float tangent_z = this->section_f_z - this->section_b_z;    // get change in z over section (un-normalised tangent vector)

        //______________________________________________________________________
        //
        // For a normal the dot product with the tangent will be:
        //
        // y1.y2 + z1.z2 = 0
        //
        // To solve for y2 (the normals y coordinate):
        //
        // let y1 = tangent_y
        // let z1 = tangent_z
        // let z2 = tangent_z
        //
        // So thus by this:
        //
        // normal_y = - ( tangent_z * tangent_z ) / ( tangent_y )
        //
        //______________________________________________________________________

    // If tangent y = 0 then normal is easy to find:

    if ( tangent_y == 0 )
    {
        //----------------------------------------------------------------------
        // Simple vertical normal
        //----------------------------------------------------------------------

        this->normal_y = 1;
        this->normal_z = 0;

    }
    else
    {
        //----------------------------------------------------------------------
        // More general normal
        //----------------------------------------------------------------------

        // Calculate normal y coordinate and z coordinate

        this->normal_y  =  tangent_z * tangent_z;
        this->normal_y /= -tangent_y;
        this->normal_z  =  tangent_z;

        // If normal is pointing downwards reverse to point in right direction

        if ( normal_y < 0 )
        {
            normal_y *= -1;
            normal_z *= -1;
        }

        // Get normal lenght and divide by lenght to normalise the normal

        float normal_l;

        normal_l  = powf(normal_y,2.0f);
        normal_l += powf(normal_z,2.0f);
        normal_l  = powf(normal_l,0.5f);

        normal_y /= float(normal_l);
        normal_z /= float(normal_l);
    }

    //--------------------------------------------------------------------------
    // Initialise the null terminated array of game objects and object count
    //--------------------------------------------------------------------------

    // Set all objects to null

    for ( unsigned int i = 0; i < SECTION_MAX_OBJECTS + 1 ; i ++ )
    {
        object_array[i] = 0;
    }

    // Set object count to zero

    object_count = 0;
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     TrackSection::addObject
Purpose:  Adds the given game object to the sections internal array, ready for
          drawing when the time comes.
Params:   object to add
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void TrackSection::addObject( GameObject * object )
{
    // If object count is at the max then abort

    if ( object_count >= SECTION_MAX_OBJECTS ) return;

    // Otherwise add the object into the last slot of the array

    object_array[object_count] = object;

    // Increment object count

    object_count++;
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     TrackSection::delObject
Purpose:  Trys to delete the given object from the internal array of the track section
Params:   object to delete
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void TrackSection::delObject( GameObject * object )
{
    // Abort if null pointer

    if ( object == 0 ) return;

    // Try to find the object specified

    for ( unsigned int i = 0 ; i < object_count ; i++ )
    {
        // Compare pointer in the array with pointer given

        if ( object_array[i] == object )
        {
            // Found the object given. See how many objects are in the array

            if ( object_count == 1 )
            {
                // Only one object in the array: this one, just set pointer to null

                object_array[i] = 0;
            }
            else
            {
                // More than one object in the array. Swap the last object into this objects position.

                object_array[i] = object_array[object_count-1];

                // Set the last object to null

                object_array[object_count-1] = 0;
            }

            // Decrease the number of objects in the array

            object_count--;

            // Break from the search loop

            break;

        } // end if ( search object == current object in the array )

    }   // end of search for object loop
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     TrackSection::createColorLookupTables
Purpose:  Creates a lookup table of windows brush colors for each of the main
          track elements.
Params:   none
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void TrackSection::createColorLookupTables()
{
    //--------------------------------------------------------------------------
    // A windows logical brush struct used when creating brushes with windows: set members
    //--------------------------------------------------------------------------

    LOGBRUSH logBrush;

    logBrush.lbStyle = BS_SOLID;    // solid brush type
    logBrush.lbColor = 0;           // color
    logBrush.lbHatch = 0;           // dont care about the hatch type

    //--------------------------------------------------------------------------
    // Create the tarmac color lookup tables for intensities ranging from 255 to 0
    //--------------------------------------------------------------------------

    for ( int intensity = 0 ; intensity < 256 ; intensity++ )
    {
        // Figure out rgb values for this intesity: section colors are defined as normalised floats in the header for this .cpp file

        unsigned char r = (unsigned char)(SECTION_TARMAC_COL_R * (float)(intensity));
        unsigned char g = (unsigned char)(SECTION_TARMAC_COL_G * (float)(intensity));
        unsigned char b = (unsigned char)(SECTION_TARMAC_COL_B * (float)(intensity));

        // Set the color member of the logical brush: use windows rgb macro to make a color

        logBrush.lbColor = RGB(r,g,b);

        // Create the brush with windows and save its handle

        brush_table_tarmac[intensity] = CreateBrushIndirect(&logBrush);
    }

    //--------------------------------------------------------------------------
    // Create the ground color lookup tables for intensities ranging from 255 to 0
    //--------------------------------------------------------------------------

    for ( int intensity = 0 ; intensity < 256 ; intensity++ )
    {
        // Figure out rgb values for this intesity: section colors are defined as normalised floats in the header for this .cpp file

        unsigned char r = (unsigned char)(SECTION_GROUND_COL_R * (float)(intensity));
        unsigned char g = (unsigned char)(SECTION_GROUND_COL_G * (float)(intensity));
        unsigned char b = (unsigned char)(SECTION_GROUND_COL_B * (float)(intensity));

        // Set the color member of the logical brush: use windows rgb macro to make a color

        logBrush.lbColor = RGB(r,g,b);

        // Create the brush with windows and save its handle

        brush_table_ground[intensity] = CreateBrushIndirect(&logBrush);
    }

    //--------------------------------------------------------------------------
    // Create the left/right line color lookup tables for intensities ranging from 255 to 0
    //--------------------------------------------------------------------------

    for ( int intensity = 0 ; intensity < 256 ; intensity++ )
    {
        // Figure out rgb values for this intesity: section colors are defined as normalised floats in the header for this .cpp file

        unsigned char r = (unsigned char)(SECTION_LINE_LR_COL_R * (float)(intensity));
        unsigned char g = (unsigned char)(SECTION_LINE_LR_COL_G * (float)(intensity));
        unsigned char b = (unsigned char)(SECTION_LINE_LR_COL_B * (float)(intensity));

        // Set the color member of the logical brush: use windows rgb macro to make a color

        logBrush.lbColor = RGB(r,g,b);

        // Create the brush with windows and save its handle

        brush_table_lineLR[intensity] = CreateBrushIndirect(&logBrush);
    }

    //--------------------------------------------------------------------------
    // Create the center line color lookup tables for intensities ranging from 255 to 0
    //--------------------------------------------------------------------------

    for ( int intensity = 0 ; intensity < 256 ; intensity++ )
    {
        // Figure out rgb values for this intesity: section colors are defined as normalised floats in the header for this .cpp file

        unsigned char r = (unsigned char)(SECTION_LINE_C_COL_R * (float)(intensity));
        unsigned char g = (unsigned char)(SECTION_LINE_C_COL_G * (float)(intensity));
        unsigned char b = (unsigned char)(SECTION_LINE_C_COL_B * (float)(intensity));

        // Set the color member of the logical brush: use windows rgb macro to make a color

        logBrush.lbColor = RGB(r,g,b);

        // Create the brush with windows and save its handle

        brush_table_lineC[intensity] = CreateBrushIndirect(&logBrush);
    }

}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     TrackSection::deleteColorLookupTables
Purpose:  Deletes the windows brush color lookup tables
Params:   none
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void TrackSection::deleteColorLookupTables()
{
    // Delete all the brush objects with windows

    for ( int intensity = 0 ; intensity < 256 ; intensity++ )
    {
        DeleteObject(brush_table_tarmac [intensity]);
        DeleteObject(brush_table_ground [intensity]);
        DeleteObject(brush_table_lineLR [intensity]);
        DeleteObject(brush_table_lineC  [intensity]);
    }
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     TrackSection::draw
Purpose:  Draws this section of the track
Params:   Window and camera position
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void TrackSection::draw(Window & window, Camera * camera)
{
    //--------------------------------------------------------------------------
    // Do back face culling first
    //--------------------------------------------------------------------------

    // Get cameras coordinates local to the end of the section

    float cam_local_y = camera->getY() - this->section_f_y;
    float cam_local_z = camera->getZ() - this->section_f_z;

    // Do the dot product with the cameras local position to surface with the normal

    float dot = cam_local_y * normal_y + cam_local_z * normal_z;

    // If dot product is negative then cull face: camera is on the back side of it
    // But first draw all contained sprites. sprites are not culled at all.

    if ( dot < 0 )
    {
        // Draw all contained sprites within the section

        drawContainedObjects(window,camera);

        // Abort any further drawing

        return;
    }

    //--------------------------------------------------------------------------
    // xy coordinates for a polygon containing four points
    //--------------------------------------------------------------------------

    int points[8];

    //--------------------------------------------------------------------------
    // Whether to cull the center line or not
    //--------------------------------------------------------------------------

    bool cullCenterLine = false;

    //--------------------------------------------------------------------------
    // Get camera position
    //--------------------------------------------------------------------------

    register float camera_x = camera->getX();
    register float camera_y = camera->getY();
    register float camera_z = camera->getZ();

    //--------------------------------------------------------------------------
    // Translate x,y and z values by camera position
    //--------------------------------------------------------------------------

    float section_f_z = this->section_f_z - camera_z;
    float section_b_z = this->section_b_z - camera_z;

    float section_f_y = this->section_f_y - camera_y;
    float section_b_y = this->section_b_y - camera_y;

    float tarmac_fl_x = this->tarmac_fl_x - camera_x;
    float tarmac_fr_x = this->tarmac_fr_x - camera_x;
    float tarmac_bl_x = this->tarmac_bl_x - camera_x;
    float tarmac_br_x = this->tarmac_br_x - camera_x;

    float line_l_fl_x = this->line_l_fl_x - camera_x;
    float line_l_fr_x = this->line_l_fr_x - camera_x;
    float line_l_bl_x = this->line_l_bl_x - camera_x;
    float line_l_br_x = this->line_l_br_x - camera_x;

    float line_r_fl_x = this->line_r_fl_x - camera_x;
    float line_r_fr_x = this->line_r_fr_x - camera_x;
    float line_r_bl_x = this->line_r_bl_x - camera_x;
    float line_r_br_x = this->line_r_br_x - camera_x;

    float line_c_fl_x = this->line_c_fl_x - camera_x;
    float line_c_fr_x = this->line_c_fr_x - camera_x;
    float line_c_bl_x = this->line_c_bl_x - camera_x;
    float line_c_br_x = this->line_c_br_x - camera_x;

    float line_c_f_y  = this->line_c_f_y  - camera_y;
    float line_c_b_y  = this->line_c_b_y  - camera_y;

    float line_c_f_z  = this->line_c_f_z  - camera_z;
    float line_c_b_z  = this->line_c_b_z  - camera_z;


    //--------------------------------------------------------------------------
    // Clip the tarmac and left/right lines against the near plane
    //--------------------------------------------------------------------------

    // Check to see if back of section is past the near plane

    if ( section_b_z < Track::VIEW_PLANE_NEAR )
    {
        // Section back is past near plane: figure out how much it extrudes past near plane

        float clipPercent = ( Track::VIEW_PLANE_NEAR - section_b_z ) / ( section_f_z - section_b_z );

        // Clip the x coordinates of the tarmac against the near plane

        tarmac_bl_x += ( tarmac_fl_x - tarmac_bl_x ) * clipPercent;
        tarmac_br_x += ( tarmac_fr_x - tarmac_br_x ) * clipPercent;

        // Clip the x coordinates of the left and right lines against the near plane

        line_l_bl_x += ( line_l_fl_x - line_l_bl_x ) * clipPercent;
        line_l_br_x += ( line_l_fr_x - line_l_br_x ) * clipPercent;
        line_r_bl_x += ( line_r_fl_x - line_r_bl_x ) * clipPercent;
        line_r_br_x += ( line_r_fr_x - line_r_br_x ) * clipPercent;

        // Clip the y coordinate for the back of the section against the near plane

        section_b_y += ( section_f_y - section_b_y ) * clipPercent;

        // Set the back z value of the section to the near plane z value

        section_b_z = Track::VIEW_PLANE_NEAR;
    }

    //--------------------------------------------------------------------------
    // Clip the center line against the near plane
    //--------------------------------------------------------------------------

    if ( line_c_f_z < Track::VIEW_PLANE_NEAR )
    {
        // Front of center line is past near plane: cull

        cullCenterLine = true;

    }
    else if ( line_c_b_z < Track::VIEW_PLANE_NEAR )
    {
        // Back of center line is past near plane: figure out how much it extrudes past near plane

        float clipPercent = ( Track::VIEW_PLANE_NEAR - line_c_b_z ) / ( line_c_f_z - line_c_b_z );

        // Clip the x coordinates of the back of the center line against the near plane

        line_c_bl_x += ( line_c_fl_x - line_c_bl_x ) * clipPercent;
        line_c_br_x += ( line_c_fr_x - line_c_br_x ) * clipPercent;

        // Clip the back y coordinate against the z plane

        line_c_b_y += ( line_c_f_y - line_c_b_y ) * clipPercent;

        // Clamp the back z value to the near plane

        line_c_b_z = Track::VIEW_PLANE_NEAR;
    }

    //--------------------------------------------------------------------------
    // Do perspective projection
    //--------------------------------------------------------------------------

    {
        //----------------------------------------------------------------------
        // Amount to upscale coordinates to fit onto a screen plane
        //----------------------------------------------------------------------

        const float UPSCALE_X = float(Game::RESOLUTION_X) / 2.0f;
        const float UPSCALE_Y = float(Game::RESOLUTION_Y) / 2.0f;

        //----------------------------------------------------------------------
        // Project track and left/right lines
        //----------------------------------------------------------------------

        // Quick multipliers to replace division

        register float div_fz   = 1.0f / section_f_z;
        register float div_bz   = 1.0f / section_b_z;

        // Project section, tarmac and left/right line coords: fov is 90 degrees or simple x/z, y/z projection

        section_f_y *= -div_fz;
        section_b_y *= -div_bz;

        tarmac_fl_x *= div_fz;
        tarmac_fr_x *= div_fz;
        tarmac_bl_x *= div_bz;
        tarmac_br_x *= div_bz;

        line_l_fl_x *= div_fz;
        line_l_fr_x *= div_fz;
        line_l_bl_x *= div_bz;
        line_l_br_x *= div_bz;

        line_r_fl_x *= div_fz;
        line_r_fr_x *= div_fz;
        line_r_bl_x *= div_bz;
        line_r_br_x *= div_bz;

        // Upscale all the coordinates so that they fit on a screen sized plane
        // and not a -1 to 1 sized plane on the x,y dimensions

        tarmac_fl_x *= UPSCALE_X;
        tarmac_fr_x *= UPSCALE_X;
        tarmac_bl_x *= UPSCALE_X;
        tarmac_br_x *= UPSCALE_X;

        line_l_fl_x *= UPSCALE_X;
        line_l_fr_x *= UPSCALE_X;
        line_l_bl_x *= UPSCALE_X;
        line_l_br_x *= UPSCALE_X;

        line_r_fl_x *= UPSCALE_X;
        line_r_fr_x *= UPSCALE_X;
        line_r_bl_x *= UPSCALE_X;
        line_r_br_x *= UPSCALE_X;

        section_f_y *= UPSCALE_Y;
        section_b_y *= UPSCALE_Y;

        //----------------------------------------------------------------------
        // Project the center line if it is not culled
        //----------------------------------------------------------------------

        if ( ! cullCenterLine )
        {

            // Quick multipliers to replace division

            register float div_line_c_fz  = 1.0f / line_c_f_z;
            register float div_line_c_bz  = 1.0f / line_c_b_z;

            // Project center line coords: fov is 90 degrees or simple x/z, y/z projection

            line_c_fl_x *=  div_line_c_fz;
            line_c_fr_x *=  div_line_c_fz;
            line_c_bl_x *=  div_line_c_bz;
            line_c_br_x *=  div_line_c_bz;

            line_c_f_y  *= -div_line_c_fz;
            line_c_b_y  *= -div_line_c_bz;

            // Upscale all the coordinates so that they fit on a screen sized plane
            // and not a -1 to 1 sized plane on the x,y dimensions

            line_c_fl_x *= UPSCALE_X;
            line_c_fr_x *= UPSCALE_X;
            line_c_bl_x *= UPSCALE_X;
            line_c_br_x *= UPSCALE_X;

            line_c_f_y  *= UPSCALE_Y;
            line_c_b_y  *= UPSCALE_Y;
        }

    }

    //--------------------------------------------------------------------------
    // Trasnlate all points so that projection tends towards the screen center
    //--------------------------------------------------------------------------

    {
        // Amount to translate by

        register float tx = float( Game::RESOLUTION_X >> 1 );
        register float ty = float( Game::RESOLUTION_Y >> 1 );

        // Translate the points

        section_f_y += ty;
        section_b_y += ty;

        line_c_f_y  += ty;
        line_c_b_y  += ty;

        tarmac_fl_x += tx;
        tarmac_fr_x += tx;
        tarmac_bl_x += tx;
        tarmac_br_x += tx;

        line_l_fl_x += tx;
        line_l_fr_x += tx;
        line_l_bl_x += tx;
        line_l_br_x += tx;

        line_r_fl_x += tx;
        line_r_fr_x += tx;
        line_r_bl_x += tx;
        line_r_br_x += tx;

        if ( ! cullCenterLine )
        {
            line_c_fl_x += tx;
            line_c_fr_x += tx;
            line_c_bl_x += tx;
            line_c_br_x += tx;
        }
    }

    //--------------------------------------------------------------------------
    // Figure out the intensity of the section from 0-255. Intensity decreases with distance
    // and the rate of fade is controlled by the 'SECTION_DIST_FADE_AMT' constant
    //--------------------------------------------------------------------------

    unsigned int section_intensity = (unsigned int)( 255.0f / ( section_b_z * SECTION_DIST_FADE_AMT + 1.0f ));

    //--------------------------------------------------------------------------
    // Draw the ground part
    //--------------------------------------------------------------------------

    {
        // Select the appropriate brush color for section intensity with the window

        window.selectBrush(brush_table_ground[section_intensity]);

        // Draw the ground

        window.drawRectangle
        (
            0,                      // top left x
            (int)section_f_y-1,     // top left y:     minus 1 unit to account for fact that pen outline is switched off for speed reasons
            Game::RESOLUTION_X+2,   // bottom right x: plus  2 unit to account for fact that 2 units to right are not drawn in windowed mode
            (int)section_b_y        // bottom right y
        );
    }

    //--------------------------------------------------------------------------
    // Draw the tarmac
    //--------------------------------------------------------------------------

    {
        // Select the appropriate brush color for section intensity with the window

        window.selectBrush(brush_table_tarmac[section_intensity]);

        // Set the points needed: subtract 1 from front y positions to account for gdi pen being switched off

        points[0] = (int)tarmac_bl_x;   points[1] = (int)section_b_y;
        points[2] = (int)tarmac_br_x;   points[3] = (int)section_b_y;
        points[4] = (int)tarmac_fr_x;   points[5] = (int)section_f_y - 1;
        points[6] = (int)tarmac_fl_x;   points[7] = (int)section_f_y - 1;

        // Draw the tarmac

        window.drawPolygon(points,4);
    }

    //--------------------------------------------------------------------------
    // Draw all 3 lines if section not past the maximum depth for drawing lines
    //--------------------------------------------------------------------------

    if ( section_b_z < Track::LINE_CULL_DISTANCE )
    {
        //----------------------------------------------------------------------
        // Draw the left line
        //----------------------------------------------------------------------

        // Select the appropriate brush color for section intensity with the window

        window.selectBrush(brush_table_lineLR[section_intensity]);

        // Set the points needed

        points[0] = (int)line_l_bl_x;
        points[2] = (int)line_l_br_x;
        points[4] = (int)line_l_fr_x;
        points[6] = (int)line_l_fl_x;

        // Draw the left line

        window.drawPolygon(points,4);

        //----------------------------------------------------------------------
        // Draw the right line
        //----------------------------------------------------------------------

        // Set the points needed

        points[0] = (int)line_r_bl_x;
        points[2] = (int)line_r_br_x;
        points[4] = (int)line_r_fr_x;
        points[6] = (int)line_r_fl_x;

        // Draw the right line

        window.drawPolygon(points,4);

        //----------------------------------------------------------------------
        // Draw the center line- if it hasn't been culled
        //----------------------------------------------------------------------

        if ( ! cullCenterLine )
        {
            // Select the appropriate brush color for section intensity with the window

            window.selectBrush(brush_table_lineC[section_intensity]);

            // Set the points needed: subtract 1 from back y positions to account for gdi pen being off

            points[0] = (int)line_c_bl_x;    points[1] = (int)line_c_b_y;
            points[2] = (int)line_c_br_x;    points[3] = (int)line_c_b_y;
            points[4] = (int)line_c_fr_x;    points[5] = (int)line_c_f_y - 1;
            points[6] = (int)line_c_fl_x;    points[7] = (int)line_c_f_y - 1;

            // Draw the center line

            window.drawPolygon(points,4);
        }

    }

    //--------------------------------------------------------------------------
    // Draw all objects contained in the track section
    //--------------------------------------------------------------------------

    drawContainedObjects(window,camera);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     TrackSection::drawContainedObjects
Purpose:  Draws all objects contained within the section.
Params:   Window and camera position
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void TrackSection::drawContainedObjects(Window & window, Camera * camera)
{
    //--------------------------------------------------------------------------
    // Z sort all the objects first
    //--------------------------------------------------------------------------

    for ( unsigned int i = 0 ; i < object_count ; i++ )
    for ( unsigned int j = 0 ; j < object_count ; j++ )
    {
        // If the z depth is greater on the compare object then swap.
        // Objects with higher depth will be drawn first according to the painters algo.

        if ( object_array[i]->getPos_z() > object_array[j]->getPos_z() )
        {
            // Swap: compare object is greater in z than the other object

            GameObject * temp = object_array[j];

            object_array[j] = object_array[i];
            object_array[i] = temp;
        }
    }

    //--------------------------------------------------------------------------
    // Draw the objects
    //--------------------------------------------------------------------------

    for ( unsigned int i = 0 ; i < object_count ; i++ )
    {
        // Draw this object

        object_array[i]->event_draw(window,camera);
    }

}
