/*
	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/>.
*/

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Renderer.cpp
//
// Purpose:  Holds and modifies transformation matrices and renders polygons..
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        //¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬
        // Includes
        //¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬

        #include "Renderer.h"      

        //¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬
        // Constants
        //¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬

                // Identity matrix

                static const float m_identity[] =
                        {
                        1 , 0 , 0 , 0,
                        0 , 1 , 0 , 0,
                        0 , 0 , 1 , 0,
                        0 , 0 , 0 , 1
                        };

        //¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬
        // Variables
        //¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬

                // Transformation matrix

                static float m_transform[16];

                // Center of screen , width and height of screen

                static int screen_center_x;
                static int screen_center_y;
                static int screen_width;
                static int screen_height;

                // Focal lenght of projection

                static float focalLenght;
                
                // Handle to the window and device context
                
                HWND windowHandle   = NULL;
                HDC  deviceContext  = NULL;
                
                // Screen buffer and depth buffer pointers

                float * depthBuffer    = NULL;
                int   * screenBuffer   = NULL;

				// DirectX 9 stuff

				IDirect3D9 *			d3d					= NULL;
				IDirect3DDevice9 *		device				= NULL;
				IDirect3DTexture9 *		backBufferTexture	= NULL;

                // Viewing frustrum planes

                static struct Plane pl_near;
                static struct Plane pl_far;
                static struct Plane pl_left;
                static struct Plane pl_right;
                static struct Plane pl_top;
                static struct Plane pl_bottom;

                // Performance timing

                static unsigned int lastClockCount;

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_Init()
//
// Purpose: Initialises the renderer
// Params:  Window handle
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_Init( HWND hwnd )
{
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Screen width, height and center of screen buffer
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

     screen_width    = RES_X;
     screen_height   = RES_Y;
     screen_center_x = RES_X / 2;
     screen_center_y = RES_Y / 2;

     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Initialise the screen buffer and save window handle and device context.
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     
     windowHandle   = hwnd;                 // Save window handle
     deviceContext  = GetDC(windowHandle);  // Get rendering device context
     
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Initialise Direct 3D
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
     
     R_InitDirect3D();

     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Initialise the depth buffer
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

     depthBuffer = new float[RES_X*RES_Y];
     
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Set projection mode
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
     
     R_ProjectionMode( 1.0 , 500.0 , 90.0 );

     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Initialise the transformation matrix
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

     R_LoadIdentity();
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_InitDirect3D()
//
// Purpose: Initialises direct 3D
// Params:  None
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_InitDirect3D()
{
	// Firstly create teh direct3d object

	d3d = Direct3DCreate9(D3D_SDK_VERSION);

	if (d3d == NULL)
	{
	    Win_Message
        (
			"Direct 3D failed to initialise. Aborting!",
			"Graphics failure"
        );
	}

	// Fill in the presentation parameters

	D3DPRESENT_PARAMETERS d3dpp;
	memset(&d3dpp, 0, sizeof(D3DPRESENT_PARAMETERS));

	d3dpp.Windowed				= true;
	d3dpp.SwapEffect			= D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat		= D3DFMT_UNKNOWN;
	d3dpp.BackBufferWidth		= RES_X;
	d3dpp.BackBufferHeight		= RES_Y;
	d3dpp.PresentationInterval	= D3DPRESENT_INTERVAL_ONE;

	// Create the device itself

	if (d3d->CreateDevice(D3DADAPTER_DEFAULT, 
							D3DDEVTYPE_HAL, 
							windowHandle, 
							D3DCREATE_HARDWARE_VERTEXPROCESSING, 
							&d3dpp, 
							&device) != D3D_OK)
	{
	    Win_Message
        (
			"Direct 3D failed to initialise. Aborting!",
			"Graphics failure"
        );
	}

	// Create a texture to hold to result of rendering

	if (device->CreateTexture(RES_X, RES_Y, 1, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED , &backBufferTexture, NULL) != D3D_OK)
	{
	    Win_Message
        (
			"Failed to initialize! Couldn't create back buffer texture. Aborting!",
			"Graphics failure"
        );
	}
}

/**
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_InitDirectDraw()
//
// Purpose: Initialises direct draw and all the drawing surfaces
// Params:  None
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_InitDirectDraw()
{
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Create direct draw object: exit on failure
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      
    if ( DirectDrawCreate( NULL,& directDraw, NULL ) != DD_OK )
    {
        // If failed then abort program immediately
        
        Win_Message
            (
            "Direct Draw failed to initialise. Aborting!",
            "Graphics failure"
            );
            
        exit(1);
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    
    // Set the 'cooperate level' for direct draw. This determines how much control
    // the program has over the graphics hardware
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    

    if ( directDraw->SetCooperativeLevel(windowHandle,DDSCL_NORMAL ) != DD_OK)
    {
        // Failed to get the control wanted. Quit..
        
        Win_Message
            (
            "Direct Draw failed to set cooperative level. Aborting!",
            "Graphics failure"
            );
            
        directDraw->Release();                
        exit(1);
    }    

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      
    // Create a surface description which will be used when getting the front buffer..
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  

    DDSURFACEDESC surfDesc;
         
    memset(&surfDesc,0,sizeof(DDSURFACEDESC));         // Initialise struct as 0  
    surfDesc.dwSize         = sizeof(DDSURFACEDESC);   // Fill in size info
    surfDesc.dwFlags        = DDSD_CAPS;               // Which info will be used ?
    surfDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;  // Primary surface     
  
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      
    // Create the front buffer
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      
    
    if ( directDraw->CreateSurface(&surfDesc,&frontSurface,NULL) != DD_OK )
    {
        // Failed to create surface. Quit..
        
        Win_Message
            (
            "Failed to get primary direct draw surface!",
            "Graphics failure"
            );
            
        directDraw->Release();                
        exit(1);   
    } 
    
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      
    // Create surface description for back buffer...
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
        
    memset(&surfDesc,0,sizeof(DDSURFACEDESC));                      // Initialise struct as 0  
    
    surfDesc.dwSize         = sizeof(DDSURFACEDESC);                // Fill in size info
    surfDesc.dwFlags        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; // Which info to use
    surfDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;               // Make this an offscreen surface
    surfDesc.dwWidth        = RES_X;                                // Set surface width
    surfDesc.dwHeight       = RES_Y;                                // Set surface height

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      
    // Create the back buffer...
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
    
    if ( directDraw->CreateSurface(&surfDesc,&backSurface,NULL) != DD_OK )
    {
        // Failed to create surface. Quit..
        
        Win_Message
            (
            "Failed to create back buffer draw surface!",
            "Graphics failure"
            );
            
        directDraw->Release();                
        exit(1);   
    }     

}
**/

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_ShutDown()
//
// Purpose: Shuts down the renderer and frees up resources
// Params:  None
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_ShutDown()
{
	backBufferTexture->Release();
	backBufferTexture = NULL;

	device->Release();
	device = NULL;

	d3d->Release();
	d3d = NULL;
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_DrawText()
//
// Purpose: Draws the specified text to the screen
// Params:  X position, Y position, string
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_DrawText ( int x , int y , char * text )
{
    // Set the text color and make background opaque: GDI functions
    
    SetBkMode       (deviceContext, OPAQUE		);
    SetTextColor    (deviceContext, 0x000000ff  );
    
    // Draw the text
    
	TextOut( deviceContext , x , y , text , strlen(text) );
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_UpdatePerformanceCounter()
//
// Purpose: Updates the performance counter
// Params:  None
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_UpdatePerformanceCounter ( void )
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Hold last clock time here
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        static unsigned int lastClockTime;

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Get the current clock count from windows
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        static LARGE_INTEGER clock; QueryPerformanceCounter(&clock);

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Set the amount of clocks between the last invocation of the timer
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        lastClockCount = clock.LowPart - lastClockTime;
        lastClockTime  = clock.LowPart;
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_GetClockCount()
//
// Purpose: Returns the clock count between the last two invocations of the
//          performance timer being updated
// Params:  None
// Returns: clock count
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

unsigned int R_GetClockCount ( void ){ return lastClockCount; }

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_GetMatrix()
//
// Purpose: Returns transformation matrix used
// Params:  None
// Returns: Pointer to transformation matrix
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

float * R_GetMatrix (){ return m_transform ; }

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_MMult4x4_4x4()
//
// Purpose: Multiplies two 4x4 matrices together and saves result in second matrix
// Params:  Two pointers to matrices
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_MMult4x4_4x4 ( float * m1 , float * m2 )
{
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Copy m2 here so that it is held for the duration of multiplication
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

     static float m2cpy[16]; memcpy( m2cpy , m2 , sizeof m2cpy );

     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Do the multiplication
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

     m2[0 ]= m1[0 ] * m2cpy[0 ] + m1[1 ] * m2cpy[4 ] + m1[2 ] * m2cpy[8 ] + m1[3 ] * m2cpy[12];
     m2[1 ]= m1[0 ] * m2cpy[1 ] + m1[1 ] * m2cpy[5 ] + m1[2 ] * m2cpy[9 ] + m1[3 ] * m2cpy[13];
     m2[2 ]= m1[0 ] * m2cpy[2 ] + m1[1 ] * m2cpy[6 ] + m1[2 ] * m2cpy[10] + m1[3 ] * m2cpy[14];
     m2[3 ]= m1[0 ] * m2cpy[3 ] + m1[1 ] * m2cpy[7 ] + m1[2 ] * m2cpy[11] + m1[3 ] * m2cpy[15];

     m2[4 ]= m1[4 ] * m2cpy[0 ] + m1[5 ] * m2cpy[4 ] + m1[6 ] * m2cpy[8 ] + m1[7 ] * m2cpy[12];
     m2[5 ]= m1[4 ] * m2cpy[1 ] + m1[5 ] * m2cpy[5 ] + m1[6 ] * m2cpy[9 ] + m1[7 ] * m2cpy[13];
     m2[6 ]= m1[4 ] * m2cpy[2 ] + m1[5 ] * m2cpy[6 ] + m1[6 ] * m2cpy[10] + m1[7 ] * m2cpy[14];
     m2[7 ]= m1[4 ] * m2cpy[3 ] + m1[5 ] * m2cpy[7 ] + m1[6 ] * m2cpy[11] + m1[7 ] * m2cpy[15];

     m2[8 ]= m1[8 ] * m2cpy[0 ] + m1[9 ] * m2cpy[4 ] + m1[10] * m2cpy[8 ] + m1[11] * m2cpy[12];
     m2[9 ]= m1[8 ] * m2cpy[1 ] + m1[9 ] * m2cpy[5 ] + m1[10] * m2cpy[9 ] + m1[11] * m2cpy[13];
     m2[10]= m1[8 ] * m2cpy[2 ] + m1[9 ] * m2cpy[6 ] + m1[10] * m2cpy[10] + m1[11] * m2cpy[14];
     m2[11]= m1[8 ] * m2cpy[3 ] + m1[9 ] * m2cpy[7 ] + m1[10] * m2cpy[11] + m1[11] * m2cpy[15];

     m2[12]= m1[12] * m2cpy[0 ] + m1[13] * m2cpy[4 ] + m1[14] * m2cpy[8 ] + m1[15] * m2cpy[12];
     m2[13]= m1[12] * m2cpy[1 ] + m1[13] * m2cpy[5 ] + m1[14] * m2cpy[9 ] + m1[15] * m2cpy[13];
     m2[14]= m1[12] * m2cpy[2 ] + m1[13] * m2cpy[6 ] + m1[14] * m2cpy[10] + m1[15] * m2cpy[14];
     m2[15]= m1[12] * m2cpy[3 ] + m1[13] * m2cpy[7 ] + m1[14] * m2cpy[11] + m1[15] * m2cpy[15];

}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_MMult4x4_4x1()
//
// Purpose: Multiplies a 4x1 matrix by a 4x4 matrix and saves result
// Params:  Two pointers to matrices
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_MMult4x4_4x1 ( float * m1 , float * m2 )
{
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Copy m2 here so that it is held for the duration of multiplication
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

     static float m2cpy[4]; memcpy( m2cpy , m2 , sizeof m2cpy );

     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     // Do the multiplication
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

     m2[0 ]= m1[0 ] * m2cpy[0] + m1[1 ] * m2cpy[1] + m1[2 ] * m2cpy[2 ] + m1[3 ] * m2cpy[3];
     m2[1 ]= m1[4 ] * m2cpy[0] + m1[5 ] * m2cpy[1] + m1[6 ] * m2cpy[2 ] + m1[7 ] * m2cpy[3];
     m2[2 ]= m1[8 ] * m2cpy[0] + m1[9 ] * m2cpy[1] + m1[10] * m2cpy[2 ] + m1[11] * m2cpy[3];
     m2[3 ]= m1[12] * m2cpy[0] + m1[13] * m2cpy[1] + m1[14] * m2cpy[2 ] + m1[15] * m2cpy[3];
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_MInverseTransform()
//
// Purpose: Makes a 4x4 matrix to the inverse transform
// Params:  Pointer to matrix
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_MInverseTransform ( float * matrix )
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Save matrix temporarily
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        static float temp[16]; memcpy(temp,matrix,sizeof temp);

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Set rotation part of inverse transform matrix:
        // Optimisation: swaps along the diagonal are missing because not needed!
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        matrix[4 ] = temp[1 ];
        matrix[8 ] = temp[2 ];

        matrix[1 ] = temp[4 ];
        matrix[9 ] = temp[6 ];

        matrix[2 ] = temp[8 ];
        matrix[6 ] = temp[9 ];

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Set translation part of inverse transform matrix:
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        matrix[4 ]  = -( matrix[0 ] * temp[4 ] + matrix[1 ] * temp[8 ] + matrix[2 ] * temp[12] );
        matrix[8 ]  = -( matrix[4 ] * temp[4 ] + matrix[5 ] * temp[8 ] + matrix[6 ] * temp[12] );
        matrix[12]  = -( matrix[8 ] * temp[4 ] + matrix[9 ] * temp[8 ] + matrix[10] * temp[12] );
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_LoadIdentity()
//
// Purpose: Loads the identity transform matrix
// Params:  None
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_LoadIdentity()
{
        memcpy(m_transform,m_identity,sizeof m_identity);
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_ProjectionMode()
//
// Purpose: Sets up the projection settings and clip planes for the scene
// Params:  near plane distance , far plane distance,
//          horizontal feeling of view angle (degrees)
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_ProjectionMode ( float near_dist , float far_dist , float hfov )
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Calculate focal lenght for projection: ajust focal lenght distance so
        // that projection plane no longer ranges from x=-1 to x=1 and is instead
        // the width of the screen. Needed for correct projection!
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        float e      = 1.0f / tanf( hfov * 0.5f * TO_RADIANS );
        float a      = (float)(screen_height)/(float)(screen_width);
        focalLenght  = screen_center_x / e; 
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Calculate clip planes
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Near clip plane ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                // Normal

                pl_near.a =  0;
                pl_near.b =  0;
                pl_near.c = -1;

                // Distance from origin along plane normal

                pl_near.d = near_dist;

                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Far clip plane ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                // Normal

                pl_far.a =  0;
                pl_far.b =  0;
                pl_far.c =  1;

                // Distance from origin along plane normal

                pl_far.d = -far_dist;

                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Left clip plane ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                // Make up normal

                float pl_left_normal[3] =
                        {
                        e,
                        0,
                        -1.0,
                        };

                // Normalise vector

                R_NormaliseVector(pl_left_normal);

                // Save

                pl_left.a = pl_left_normal[0];
                pl_left.b = pl_left_normal[1];
                pl_left.c = pl_left_normal[2];

                // Distance from origin along plane normal

                pl_left.d  = 1e-2f;

                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Right clip plane ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                // Save

                pl_right.a = -pl_left_normal[0];
                pl_right.b =  pl_left_normal[1];
                pl_right.c =  pl_left_normal[2];

                // Distance from origin along plane normal

                pl_right.d = 1e-2f;

                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Bottom clip plane ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                // Make up normal vector

                float pl_bottom_normal[3] =
                        {
                        0,
                        e,
                        -a
                        };

                // Normalise vector

                R_NormaliseVector(pl_bottom_normal);

                // Save

                pl_bottom.a = pl_bottom_normal[0];
                pl_bottom.b = pl_bottom_normal[1];
                pl_bottom.c = pl_bottom_normal[2];

                // Distance from origin along plane normal

                pl_bottom.d = 1e-2f;

                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Top clip plane ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                // Save

                pl_top.a =  pl_bottom_normal[0];
                pl_top.b = -pl_bottom_normal[1];
                pl_top.c =  pl_bottom_normal[2];

                // Distance from origin along plane normal

                pl_top.d = 1e-2f;
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_ClearScreen()
//
// Purpose: Clears the screen and depth buffer
// Params:  None
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_ClearScreen ( void )
{
        // Call the assembly implementation of the clear screen function

        R_ClearScreenASM
            ( 
            screen_width * screen_height , 
            screenBuffer                 , 
            depthBuffer 
            );         
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_BeginDraw()
//
// Purpose: Begins drawing by locking the back buffer and getting a pointer to
//          the pixel data...
// Params:  None
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_BeginDraw( void )
{
	// Begin rendering:

	device->BeginScene();

	// Clear the screen:

	device->Clear(0, NULL, D3DCLEAR_TARGET, 0, 0, 0);

	// Lock the texture that we will draw everything into:

	D3DLOCKED_RECT lockedRect;
	memset(&lockedRect, 0, sizeof(D3DLOCKED_RECT));

	if (backBufferTexture->LockRect(0, &lockedRect, NULL, D3DLOCK_DISCARD) != D3D_OK)
	{
	    Win_Message
        (
			"Failed to render! Couldn't lock the back buffer texture. Aborting!",
			"Graphics failure"
        );

		exit(1);
	}

	screenBuffer = (int*) lockedRect.pBits;
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_EndDraw()
//
// Purpose: Finishes up drawing and swaps the back buffer onto the screen
// Params:  None
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_EndDraw( void )
{
	// Finished with the texture - unlock

	backBufferTexture->UnlockRect(0);

	// Clear the screen buffer pointer - now no longer valid

	screenBuffer = NULL;

	// Set the fixed function vertex type:

	device->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);

	// Setup the vertices for rendering the texture:

	struct VERTEX
	{
		float x;
		float y;
		float z;
		float w;
		UINT32 color;
		float u;
		float v;
	};

	VERTEX vertices[] =
	{
		{0, 0,			1.0f, 1.0f, 0xffffffff, 0, 0},
		{RES_X, 0,		1.0f, 1.0f, 0xffffffff, 1, 0},
		{RES_X, RES_Y,	1.0f, 1.0f, 0xffffffff, 1, 1},
		{RES_X, RES_Y,	1.0f, 1.0f, 0xffffffff, 1, 1},
		{0, RES_Y,		1.0f, 1.0f, 0xffffffff, 0, 1},
		{0, 0,			1.0f, 1.0f, 0xffffffff, 0, 0},
	};

	// Bind the texture

	device->SetTexture(0, backBufferTexture);

	// Draw the texture to the screen:
	
	device->DrawPrimitiveUP(D3DPT_TRIANGLELIST, 2, vertices, sizeof(VERTEX));

	// Done with the texture:
	
	device->SetTexture(0, NULL);

	// Done rendering:
	
	device->EndScene();

	// Present to screen
	
	device->Present(NULL, NULL, 0, NULL);
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_RotateY()
//
// Purpose: Combines rotation about y axis to transformation matrix
// Params:  Angle of rotation in degrees
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_RotateY ( float angle )
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Convert angle to radians
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        angle *= TO_RADIANS;

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Construct the rotation matrix for rotation about y axis at given angle
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        static float m_rot[16]; memcpy(m_rot,m_identity,sizeof m_rot);

        m_rot[0 ] =  cos(angle);
        m_rot[2 ] =  sin(angle);
        m_rot[8 ] = -sin(angle);
        m_rot[10] =  cos(angle);

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Multiply current transform matrix with the newly constructed matrix
        // in order to combine those transforms.
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        R_MMult4x4_4x4(m_rot,m_transform);
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_Translate()
//
// Purpose: Translates the camera
// Params:  x,y,z translation
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_Translate ( float x  , float y , float z )
{
    m_transform[3 ] += x;
    m_transform[7 ] += y;
    m_transform[11] += z;
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_NormaliseVec()
//
// Purpose: Normalises the given vector (ignores homogenous vector part)
// Params:  Vector
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_NormaliseVector ( float * vector )
{
    // Get vector lenght

    float lenght;

    lenght  = vector[0] * vector[0];
    lenght += vector[1] * vector[1];
    lenght += vector[2] * vector[2];

    lenght  = sqrt(lenght);

    // Check for zero division: abort if zero lenght

    if ( lenght == 0 ) return;

    // Divide by lenght

    vector[0] /= lenght;
    vector[1] /= lenght;
    vector[2] /= lenght;
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_RejectPoly()
//
// Purpose: Tests if the polygon lies completely on the outside of a plane.
//          Used to reject polygons completely outside the view frustrum.
// Params:  Polygon , a plane
// Returns: True if completely outside, false if not
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

bool R_RejectPoly( struct Polygon * p , struct Plane * pl )
{
        // Get distance of three points to the plane

        float d1 = (p->p1[0]*pl->a) + (p->p1[1]*pl->b) + (p->p1[2]*pl->c) - pl->d;
        float d2 = (p->p2[0]*pl->a) + (p->p2[1]*pl->b) + (p->p2[2]*pl->c) - pl->d;
        float d3 = (p->p3[0]*pl->a) + (p->p3[1]*pl->b) + (p->p3[2]*pl->c) - pl->d;
        
        #define REJECT_DIST 0e-2
        
        // Set the reject vertice flags on the polygon for points that fall outside the plane
        
        if ( d1 <= REJECT_DIST && d2 <= REJECT_DIST && d3 <= REJECT_DIST )
        {
             return true;      // all points outside plane: reject
        }
        else
        {
             return false;     // Not all points outside the plane
        }
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_ClipPoly()
//
// Purpose: Clips a polygon against a plane. Assumes the reject poly test has been
//          taken first, where a polygon is rejected if it is completely outside
//          the viewing frustrum.
//
//          Bugfix: Floating point inaccuracy caused occasional infinite subdivision
//          of the polygons and hence a stack overflow. Solution- only consider
//          point to be on wrong side of plane when perpendicular distance < -0.0015625
//          Works reasonably well.
//
// Params:  a polygon , a plane
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_ClipPoly( struct Polygon * p , struct Plane * pl )
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Local variables
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        float points[4][3]; // Need room for four points
        float colors[4][3]; // And four color sets...
        float dist1;        // Distance from first point to plane
        float dist2;        // Distance from second point to plane
        float ratio;        // Ratio along the line that point intersects plane
        int numPoints = 0 ; // Holds number of points created,always 3 or 4
        
        #define CLIP_DISTANCE   1e-16f  // distance within plane point has to be in order to be clipped.
        #define CLIP_SAFETY     1e-32f  // 'Safety' value to add to clip distance to prevent divide by zero

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Clip first line to the plane
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        // Get distance from point at start and end of line to the plane

        dist1  =  p->p1[0] * pl->a + p->p1[1] * pl->b + p->p1[2] * pl->c - pl->d;
        dist2  =  p->p2[0] * pl->a + p->p2[1] * pl->b + p->p2[2] * pl->c - pl->d;

        // Decide what to do based on perpendicular distances

        if ( dist1 <= CLIP_DISTANCE )
        {
            // First point on wrong side of plane: see where second point is

            if ( dist2 <= CLIP_DISTANCE )
            {
                // Second point on wrong side of plane ... discard line
            }
            else
            {
                // Second point on right side of the plane. Find ratio that line intersects the plane

                ratio = fabs(dist1) / ( fabs(dist1) + fabs(dist2) + CLIP_SAFETY );

                // Save intersection point

                points[numPoints][0] = p->p1[0] + ( p->p2[0] - p->p1[0] ) * ratio;
                points[numPoints][1] = p->p1[1] + ( p->p2[1] - p->p1[1] ) * ratio;
                points[numPoints][2] = p->p1[2] + ( p->p2[2] - p->p1[2] ) * ratio;
                colors[numPoints][0] = p->c1[0] + ( p->c2[0] - p->c1[0] ) * ratio;
                colors[numPoints][1] = p->c1[1] + ( p->c2[1] - p->c1[1] ) * ratio;
                colors[numPoints][2] = p->c1[2] + ( p->c2[2] - p->c1[2] ) * ratio;

                numPoints++;
            }
        }
        else
        {
            // First point on right side of plane: see where second point is

            if ( dist2 <= CLIP_DISTANCE )
            {
                // Save first point first, its ok- on right side of plane.

                points[numPoints][0] = p->p1[0];
                points[numPoints][1] = p->p1[1];
                points[numPoints][2] = p->p1[2];
                colors[numPoints][0] = p->c1[0];
                colors[numPoints][1] = p->c1[1];
                colors[numPoints][2] = p->c1[2];

                numPoints++;

                // Find ratio that line intersects the plane

                ratio = fabs(dist1) / ( fabs(dist1) + fabs(dist2) + CLIP_SAFETY );

                // Save intersection point

                points[numPoints][0] = p->p1[0] + ( p->p2[0] - p->p1[0] ) * ratio;
                points[numPoints][1] = p->p1[1] + ( p->p2[1] - p->p1[1] ) * ratio;
                points[numPoints][2] = p->p1[2] + ( p->p2[2] - p->p1[2] ) * ratio;
                colors[numPoints][0] = p->c1[0] + ( p->c2[0] - p->c1[0] ) * ratio;
                colors[numPoints][1] = p->c1[1] + ( p->c2[1] - p->c1[1] ) * ratio;
                colors[numPoints][2] = p->c1[2] + ( p->c2[2] - p->c1[2] ) * ratio;

                numPoints++;
            }
            else
            {
                // Second point on right side of plane: keep first point

                points[numPoints][0] = p->p1[0];
                points[numPoints][1] = p->p1[1];
                points[numPoints][2] = p->p1[2];
                colors[numPoints][0] = p->c1[0];
                colors[numPoints][1] = p->c1[1];
                colors[numPoints][2] = p->c1[2];

                numPoints++;
            }

        }  // end else part of if ( dist1 < 0 )

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Clip second line to the plane
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        // Get distance from point at start and end of line to the plane

        dist1  =  p->p2[0] * pl->a + p->p2[1] * pl->b + p->p2[2] * pl->c - pl->d;
        dist2  =  p->p3[0] * pl->a + p->p3[1] * pl->b + p->p3[2] * pl->c - pl->d;

        // Decide what to do based on perpendicular distances

        if ( dist1 <= CLIP_DISTANCE )
        {
            // First point on wrong side of plane: see where second point is

            if ( dist2 <= CLIP_DISTANCE )
            {
                // Second point on wrong side of plane ... discard line
            }
            else
            {
                // Second point on right side of the plane. Find ratio that line intersects the plane

                ratio = fabs(dist1) / ( fabs(dist1) + fabs(dist2) + CLIP_SAFETY );                

                // Save intersection point

                points[numPoints][0] = p->p2[0] + ( p->p3[0] - p->p2[0] ) * ratio;
                points[numPoints][1] = p->p2[1] + ( p->p3[1] - p->p2[1] ) * ratio;
                points[numPoints][2] = p->p2[2] + ( p->p3[2] - p->p2[2] ) * ratio;
                colors[numPoints][0] = p->c2[0] + ( p->c3[0] - p->c2[0] ) * ratio;
                colors[numPoints][1] = p->c2[1] + ( p->c3[1] - p->c2[1] ) * ratio;
                colors[numPoints][2] = p->c2[2] + ( p->c3[2] - p->c2[2] ) * ratio;

                numPoints++;
            }
        }
        else
        {
            // First point on right side of plane: see where second point is

            if ( dist2 <= CLIP_DISTANCE )
            {
                // Save first point first, its ok- on right side of plane.

                points[numPoints][0] = p->p2[0];
                points[numPoints][1] = p->p2[1];
                points[numPoints][2] = p->p2[2];
                colors[numPoints][0] = p->c2[0];
                colors[numPoints][1] = p->c2[1];
                colors[numPoints][2] = p->c2[2];

                numPoints++;

                // Find ratio that line intersects the plane

                ratio = fabs(dist1) / ( fabs(dist1) + fabs(dist2) + CLIP_SAFETY );                               

                // Save intersection point

                points[numPoints][0] = p->p2[0] + ( p->p3[0] - p->p2[0] ) * ratio;
                points[numPoints][1] = p->p2[1] + ( p->p3[1] - p->p2[1] ) * ratio;
                points[numPoints][2] = p->p2[2] + ( p->p3[2] - p->p2[2] ) * ratio;
                colors[numPoints][0] = p->c2[0] + ( p->c3[0] - p->c2[0] ) * ratio;
                colors[numPoints][1] = p->c2[1] + ( p->c3[1] - p->c2[1] ) * ratio;
                colors[numPoints][2] = p->c2[2] + ( p->c3[2] - p->c2[2] ) * ratio;

                numPoints++;
            }
            else
            {
                // Second point on right side of plane: keep first point

                points[numPoints][0] = p->p2[0];
                points[numPoints][1] = p->p2[1];
                points[numPoints][2] = p->p2[2];
                colors[numPoints][0] = p->c2[0];
                colors[numPoints][1] = p->c2[1];
                colors[numPoints][2] = p->c2[2];

                numPoints++;
            }

        }  // end else part of if ( dist1 < 0 )

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Clip third line to the plane
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        // Get distance from point at start and end of line to the plane

        dist1  =  p->p3[0] * pl->a + p->p3[1] * pl->b + p->p3[2] * pl->c - pl->d;
        dist2  =  p->p1[0] * pl->a + p->p1[1] * pl->b + p->p1[2] * pl->c - pl->d;

        // Decide what to do based on perpendicular distances

        if ( dist1 < CLIP_DISTANCE )
        {
            // First point on wrong side of plane: see where second point is

            if ( dist2 <= CLIP_DISTANCE )
            {
                // Second point on wrong side of plane ... discard line
            }
            else
            {
                // Second point on right side of the plane. Find ratio that line intersects the plane

                ratio = fabs(dist1) / ( fabs(dist1) + fabs(dist2) + CLIP_SAFETY );                              

                // Save intersection point

                points[numPoints][0] = p->p3[0] + ( p->p1[0] - p->p3[0] ) * ratio;
                points[numPoints][1] = p->p3[1] + ( p->p1[1] - p->p3[1] ) * ratio;
                points[numPoints][2] = p->p3[2] + ( p->p1[2] - p->p3[2] ) * ratio;
                colors[numPoints][0] = p->c3[0] + ( p->c1[0] - p->c3[0] ) * ratio;
                colors[numPoints][1] = p->c3[1] + ( p->c1[1] - p->c3[1] ) * ratio;
                colors[numPoints][2] = p->c3[2] + ( p->c1[2] - p->c3[2] ) * ratio;

                numPoints++;
            }
        }
        else
        {
            // First point on right side of plane: see where second point is

            if ( dist2 <= CLIP_DISTANCE )
            {
                // Save first point first, its ok- on right side of plane.

                points[numPoints][0] = p->p3[0];
                points[numPoints][1] = p->p3[1];
                points[numPoints][2] = p->p3[2];
                colors[numPoints][0] = p->c3[0];
                colors[numPoints][1] = p->c3[1];
                colors[numPoints][2] = p->c3[2];

                numPoints++;

                // Find ratio that line intersects the plane

                ratio = fabs(dist1) / ( fabs(dist1) + fabs(dist2) + CLIP_SAFETY );                          

                // Save intersection point

                points[numPoints][0] = p->p3[0] + ( p->p1[0] - p->p3[0] ) * ratio;
                points[numPoints][1] = p->p3[1] + ( p->p1[1] - p->p3[1] ) * ratio;
                points[numPoints][2] = p->p3[2] + ( p->p1[2] - p->p3[2] ) * ratio;
                colors[numPoints][0] = p->c3[0] + ( p->c1[0] - p->c3[0] ) * ratio;
                colors[numPoints][1] = p->c3[1] + ( p->c1[1] - p->c3[1] ) * ratio;
                colors[numPoints][2] = p->c3[2] + ( p->c1[2] - p->c3[2] ) * ratio;

                numPoints++;
            }
            else
            {
                // Second point on right side of plane: keep first point

                points[numPoints][0] = p->p3[0];
                points[numPoints][1] = p->p3[1];
                points[numPoints][2] = p->p3[2];
                colors[numPoints][0] = p->c3[0];
                colors[numPoints][1] = p->c3[1];
                colors[numPoints][2] = p->c3[2];

                numPoints++;
            }

        }  // end else part of if ( dist1 < 0 )

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Save coordinates for first three points in the polygon given.
        // If there was less than three points then make all of them in top corner
        // of the screen to discard polygon...
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        if ( numPoints < 3 )
        {
             // Not a valid polygon: discard points
             
             p->p1[0] = - RES_X / 2; p->p1[1] = - RES_Y / 2; p->p1[2] = 1;  
             p->p2[0] = - RES_X / 2; p->p2[1] = - RES_Y / 2; p->p2[2] = 1;                  
             p->p3[0] = - RES_X / 2; p->p3[1] = - RES_Y / 2; p->p3[2] = 1;             
        }
        else
        {
            // Valid polygon: Keep points
            
            p->p1[0] = points[0][0];
            p->p1[1] = points[0][1];
            p->p1[2] = points[0][2];
            p->c1[0] = colors[0][0];
            p->c1[1] = colors[0][1];
            p->c1[2] = colors[0][2];
        
            p->p2[0] = points[1][0];
            p->p2[1] = points[1][1];
            p->p2[2] = points[1][2];
            p->c2[0] = colors[1][0];
            p->c2[1] = colors[1][1];
            p->c2[2] = colors[1][2];
        
            p->p3[0] = points[2][0];
            p->p3[1] = points[2][1];
            p->p3[2] = points[2][2];
            p->c3[0] = colors[2][0];
            p->c3[1] = colors[2][1];
            p->c3[2] = colors[2][2]; 
        }         

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // If there is a fourth point then create an entirely new trinagle
        // from fourth and first two points, and call the draw function:
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        if ( numPoints == 4 )
        {
            // Create new polygon

            struct Polygon newPoly;

            newPoly.p1[0] = points[0][0];
            newPoly.p1[1] = points[0][1];
            newPoly.p1[2] = points[0][2];
            newPoly.p1[3] = 1.0;
            newPoly.c1[0] = colors[0][0];
            newPoly.c1[1] = colors[0][1];
            newPoly.c1[2] = colors[0][2];
  
            newPoly.p2[0] = points[2][0];
            newPoly.p2[1] = points[2][1];
            newPoly.p2[2] = points[2][2];
            newPoly.p2[3] = 1.0;            
            newPoly.c2[0] = colors[2][0];
            newPoly.c2[1] = colors[2][1];
            newPoly.c2[2] = colors[2][2];

            newPoly.p3[0] = points[3][0];
            newPoly.p3[1] = points[3][1];
            newPoly.p3[2] = points[3][2];
            newPoly.p3[3] = 1.0;
            newPoly.c3[0] = colors[3][0];
            newPoly.c3[1] = colors[3][1];
            newPoly.c3[2] = colors[3][2];
            
            newPoly.polyFlags = p->polyFlags;

            // Call draw function: do not transform polygon because already transformed!

            R_DrawPoly(&newPoly,false);
        }

}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_DrawPoly()
//
// Purpose: Draws a wireframe polygon
// Params:  Pointer to a polygon , whether or not transforms are to be applied
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_DrawPoly ( struct Polygon * poly , bool doTransforms )
{
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Make local copy of polygon. Don't modify original
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        struct Polygon tpoly; memcpy(&tpoly,poly,sizeof(struct Polygon));

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Transform polygon points by transform matrix: only if transforms enabled
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        if ( doTransforms )
        {
            R_MMult4x4_4x1( m_transform , tpoly.p1 );
            R_MMult4x4_4x1( m_transform , tpoly.p2 );
            R_MMult4x4_4x1( m_transform , tpoly.p3 );
        }
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Test which points are outside the viewing frustrums and abort drawing
        // if they are all outside
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        if ( R_RejectPoly( &tpoly,&pl_near    ) ) return;
        if ( R_RejectPoly( &tpoly,&pl_far     ) ) return;
        if ( R_RejectPoly( &tpoly,&pl_left    ) ) return;
        if ( R_RejectPoly( &tpoly,&pl_right   ) ) return; 
        if ( R_RejectPoly( &tpoly,&pl_top     ) ) return;
        if ( R_RejectPoly( &tpoly,&pl_bottom  ) ) return;            

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Do clipping of the polygon: but only if hasn't been clipped before
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        if ( ! ( tpoly.polyFlags & PF_CLIPPED_NEAR ) )
        {
            tpoly.polyFlags |= PF_CLIPPED_NEAR; R_ClipPoly(&tpoly,&pl_near);
        }
        
        if ( ! ( tpoly.polyFlags & PF_CLIPPED_FAR ) )
        {
            tpoly.polyFlags |= PF_CLIPPED_FAR; R_ClipPoly(&tpoly,&pl_far);
        }
        
        if ( ! ( tpoly.polyFlags & PF_CLIPPED_LEFT ) )
        {
            tpoly.polyFlags |= PF_CLIPPED_LEFT; R_ClipPoly(&tpoly,&pl_left);
        }  
        
        if ( ! ( tpoly.polyFlags & PF_CLIPPED_RIGHT ) )
        {
            tpoly.polyFlags |= PF_CLIPPED_RIGHT; R_ClipPoly(&tpoly,&pl_right);
        }             
        
        if ( ! ( tpoly.polyFlags & PF_CLIPPED_BOTTOM ) )
        {
            tpoly.polyFlags |= PF_CLIPPED_BOTTOM; R_ClipPoly(&tpoly,&pl_bottom);
        }                    
        
        if ( ! ( tpoly.polyFlags & PF_CLIPPED_TOP ) )
        {
            tpoly.polyFlags |= PF_CLIPPED_TOP; R_ClipPoly(&tpoly,&pl_top);
        } 
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Do perspective transformations: invert y component since y is inverted
        // on screen (0,0) is at top and not at bottom!
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        tpoly.p1[0] /= ( tpoly.p1[2] / -focalLenght );
        tpoly.p1[1] /= ( tpoly.p1[2] /  focalLenght );
        tpoly.p2[0] /= ( tpoly.p2[2] / -focalLenght );
        tpoly.p2[1] /= ( tpoly.p2[2] /  focalLenght );
        tpoly.p3[0] /= ( tpoly.p3[2] / -focalLenght );
        tpoly.p3[1] /= ( tpoly.p3[2] /  focalLenght );
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Depth testing fix: interpolate 1/z instead of z
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        
        
        tpoly.p1[2] = 1.0f / tpoly.p1[2];
        tpoly.p2[2] = 1.0f / tpoly.p2[2];
        tpoly.p3[2] = 1.0f / tpoly.p3[2]; 
        
        tpoly.p1[0] = ROUND(tpoly.p1[0]);               
        tpoly.p2[0] = ROUND(tpoly.p2[0]);
        tpoly.p3[0] = ROUND(tpoly.p3[0]); 
        tpoly.p1[1] = ROUND(tpoly.p1[1]);               
        tpoly.p2[1] = ROUND(tpoly.p2[1]);
        tpoly.p3[1] = ROUND(tpoly.p3[1]);                         
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Move points so that coordinate system is at screen center:
        // Clamp points to screen edges also for safety.
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        tpoly.p1[0] += screen_center_x;
        tpoly.p2[0] += screen_center_x;
        tpoly.p3[0] += screen_center_x;
        tpoly.p1[1] += screen_center_y;
        tpoly.p2[1] += screen_center_y;
        tpoly.p3[1] += screen_center_y;

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Scan convert the polygon to its pixels
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        R_ScanPoly(&tpoly);
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// R_ScanPoly()
//
// Purpose: Draws a fully filled polygon. I've searched far and wide across the
//	    internet for a good polygon filling algorithm and the simplest and
//	    most elegant idea i've found was a: 
//	    
//	    http://www.geocities.com/wronski12/3d_tutor/tri_fillers.html
//
//	    This algorithm is mostly based around the idea of linear interpolation,
//	    and because of the way it works, is easily adaptable to do gourad shading
//	    texture mapping and other niceties. It probably isn't the fastest,
//	    but is really easy to implement so that is the main reason i've chosen it.
//
//	    The code below is my own implementaion of the algorithm described,
//      i've had to make some ajustements also to the original algorithm to
//      handle special cases and improve accuracy.
//
// Params:  Pointer to a polygon
// Returns: None
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void R_ScanPoly ( struct Polygon * p )
{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// First task is to sort the triangles in acesending order in terms of
	// their y components. The algorithm assumes p1.y < p2.y < p3.y so
	// the polygons must be sorted. The reason for this is that when the edges
	// are sorted in this way, the second edge always directly is opposite to
	// the other two edges. This allows the algorithm to split the filling into
	// two really easy cases: fill between edge3 and edge1, fill between edge3
	// and edge2- which makes things a whole lot easier (and possibly faster too)..
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	if ( p->p1[1] > p->p3[1] )
	{
		// Out of order: swap

        float t;

        t = p->p1[0] ; p->p1[0] = p->p3[0] ; p->p3[0] = t;
        t = p->p1[1] ; p->p1[1] = p->p3[1] ; p->p3[1] = t;
        t = p->p1[2] ; p->p1[2] = p->p3[2] ; p->p3[2] = t;
        t = p->c1[0] ; p->c1[0] = p->c3[0] ; p->c3[0] = t;
        t = p->c1[1] ; p->c1[1] = p->c3[1] ; p->c3[1] = t;
        t = p->c1[2] ; p->c1[2] = p->c3[2] ; p->c3[2] = t;
	}
	
	if ( p->p1[1] > p->p2[1] )
	{
		// Out of order: swap

        float t;

        t = p->p1[0] ; p->p1[0] = p->p2[0] ; p->p2[0] = t;
        t = p->p1[1] ; p->p1[1] = p->p2[1] ; p->p2[1] = t;
        t = p->p1[2] ; p->p1[2] = p->p2[2] ; p->p2[2] = t;
        t = p->c1[0] ; p->c1[0] = p->c2[0] ; p->c2[0] = t;
        t = p->c1[1] ; p->c1[1] = p->c2[1] ; p->c2[1] = t;
        t = p->c1[2] ; p->c1[2] = p->c2[2] ; p->c2[2] = t;
	}	

	if ( p->p2[1] > p->p3[1] )
	{
		// Out of order: swap

        float t;

        t = p->p2[0] ; p->p2[0] = p->p3[0] ; p->p3[0] = t;
        t = p->p2[1] ; p->p2[1] = p->p3[1] ; p->p3[1] = t;
        t = p->p2[2] ; p->p2[2] = p->p3[2] ; p->p3[2] = t;
        t = p->c2[0] ; p->c2[0] = p->c3[0] ; p->c3[0] = t;
        t = p->c2[1] ; p->c2[1] = p->c3[1] ; p->c3[1] = t;
        t = p->c2[2] ; p->c2[2] = p->c3[2] ; p->c3[2] = t;
	}



	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// The next task is to figure out the change in x with respect to y
	// for all three edges of the triangle. This will give the amount
	// by which the start and endpoints of the scanline change when moving
	// onto the next scan line. If dy <= SAFETY then make dxdy = dx
	// Note how the 3rd edge, or the edge which is always scanned against has its 
    // dxdy component defined in reverse order. This makes sense if you draw 
    // the thing on paper as it causes the the scanlines to expand outwards as 
    // they move down through the triangle.
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	#define SAFETY 0.0078125

    float deltax1 = p->p2[1] - p->p1[1] <= SAFETY ? p->p2[0] - p->p1[0] : (p->p2[0] - p->p1[0] ) / ( p->p2[1] - p->p1[1] );
	float deltax2 = p->p3[1] - p->p1[1] <= SAFETY ? p->p3[0] - p->p1[0] : (p->p3[0] - p->p1[0] ) / ( p->p3[1] - p->p1[1] );
	float deltax3 = p->p3[1] - p->p2[1] <= SAFETY ? p->p3[0] - p->p2[0] : (p->p3[0] - p->p2[0] ) / ( p->p3[1] - p->p2[1] );

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Change in depth along each of the edges:
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
    float deltaz1 = p->p2[1] - p->p1[1] <= SAFETY ? 0 : (p->p2[2] - p->p1[2]) / (p->p2[1] - p->p1[1]);
    float deltaz2 = p->p3[1] - p->p1[1] <= SAFETY ? 0 : (p->p3[2] - p->p1[2]) / (p->p3[1] - p->p1[1]);
    float deltaz3 = p->p3[1] - p->p2[1] <= SAFETY ? 0 : (p->p3[2] - p->p2[2]) / (p->p3[1] - p->p2[1]); 

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Change in color along each of the edges: 
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
    float deltacr1 = p->p2[1] - p->p1[1] <= SAFETY ? 0 : (p->c2[0] - p->c1[0]) / (p->p2[1] - p->p1[1]);
    float deltacg1 = p->p2[1] - p->p1[1] <= SAFETY ? 0 : (p->c2[1] - p->c1[1]) / (p->p2[1] - p->p1[1]);
    float deltacb1 = p->p2[1] - p->p1[1] <= SAFETY ? 0 : (p->c2[2] - p->c1[2]) / (p->p2[1] - p->p1[1]);

    float deltacr2 = p->p3[1] - p->p1[1] <= SAFETY ? 0 : (p->c3[0] - p->c1[0]) / (p->p3[1] - p->p1[1]);
    float deltacg2 = p->p3[1] - p->p1[1] <= SAFETY ? 0 : (p->c3[1] - p->c1[1]) / (p->p3[1] - p->p1[1]);
    float deltacb2 = p->p3[1] - p->p1[1] <= SAFETY ? 0 : (p->c3[2] - p->c1[2]) / (p->p3[1] - p->p1[1]);

    float deltacr3 = p->p3[1] - p->p2[1] <= SAFETY ? 0 : (p->c3[0] - p->c2[0]) / (p->p3[1] - p->p2[1]);
    float deltacg3 = p->p3[1] - p->p2[1] <= SAFETY ? 0 : (p->c3[1] - p->c2[1]) / (p->p3[1] - p->p2[1]);
    float deltacb3 = p->p3[1] - p->p2[1] <= SAFETY ? 0 : (p->c3[2] - p->c2[2]) / (p->p3[1] - p->p2[1]);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Set start and end of scanlines to top point of the triangle
	// Set start y position and end y position
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	float scan_start = p->p1[0];       // X value at scanline start
	float scan_end   = p->p1[0];       // X value at scanline end

    float z_start    = p->p1[2];       // z value at scanline start
    float z_end      = p->p1[2];       // z value at scanline end

    float r_start    = p->c1[0];       // Red color at scanline start
    float g_start    = p->c1[1];       // Green color at scanline start
    float b_start    = p->c1[2];       // Blue color at scanline start
    float r_end      = p->c1[0];       // Red color at scanline end
    float g_end      = p->c1[1];       // Green color at scanline end
    float b_end      = p->c1[2];       // Blue color at scanline end

	int y_current    = (int)p->p1[1];  // Current scanline
	int y_end1	     = (int)p->p2[1];  // Y value of end of edge1
	int y_end2	     = (int)p->p3[1];  // Y value of end of edge3

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// There are two cases which the filler must deal with when filling lines,
	// the start point of the third edge can either be on the left side of the triangle
	// or on the right side of the triangle. In the case of the right side, the start
	// and end of the scan line are reversed because of deltax values, so we need a
	// special loop to handle both situations. 
	//
	// First thing to do is check which side of the triangle the 3rd point is:
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~	

	if ( deltax1 > deltax2 )
	{
		//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
		// Major scan edge on left side of triangle. Scan line x coords
		// in normal order
		//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
               
		//######################################################################
		// Scan between edge3 (major edge) and edge1
		//######################################################################

	    for ( ; y_current < y_end1 ; y_current++ )
	    {            
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Begin plotting pixels
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            
            // Quick divider to figure out change in z,r,g,b values along each scanline pixel
            
            float divdx = scan_end - scan_start != 0 ? 1.0f / ( scan_end - scan_start ) : 0;
            
            // Call assembly scanline function
            
			/*
			R_ScanLineASM
			     (
			     screenBuffer + (int)(scan_start) + (y_current * screen_width),
			     depthBuffer  + (int)(scan_start) + (y_current * screen_width),			     
			     (int)(ROUND(scan_end)) - (int)(scan_start),			 
			     z_start,
                 r_start,
                 g_start,
                 b_start,
			     ( z_end - z_start ) * divdx,
			     ( r_end - r_start ) * divdx,
			     ( g_end - g_start ) * divdx,
			     ( b_end - b_start ) * divdx
			     );			
            */
            
			R_ScanLineASM
			     (
			     screenBuffer + (int)(scan_start) + (y_current * screen_width),
			     depthBuffer  + (int)(scan_start) + (y_current * screen_width),			     
			     (int)( scan_end - scan_start + 0.99 ),					 
			     z_start,
                 r_start,
                 g_start,
                 b_start,
			     ( z_end - z_start ) * divdx,
			     ( r_end - r_start ) * divdx,
			     ( g_end - g_start ) * divdx,
			     ( b_end - b_start ) * divdx
			     );	            
            
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			// Ajust scan positions, z values, colors and texcoords
            // before moving onto the next scanline
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

			scan_start += deltax2;      scan_end   += deltax1;
            z_start    += deltaz2;      z_end      += deltaz1;
            r_start    += deltacr2;     r_end      += deltacr1;
            g_start    += deltacg2;     g_end      += deltacg1;
            b_start    += deltacb2;     b_end      += deltacb1;

        } // End of scan between edge 2 and edge 1 loop

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Accuracy fix: floating point inaccuracy caused when dy has a small
        // value caused some values to ween off course. Solution: use the values
        // at the second point before starting again. 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        scan_end  = p->p2[0];
        z_end     = p->p2[2];
        r_end     = p->c2[0];
        g_end     = p->c2[1];
        b_end     = p->c2[2];

		//######################################################################
		// Scan between edge3 (major edge) and edge2
		//######################################################################

		for ( ; y_current < y_end2 ; y_current++ )
		{
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Begin plotting pixels
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            
            // Quick divider to figure out change in z,r,g,b values along each scanline pixel
            
            float divdx = scan_end - scan_start != 0 ? 1.0f / ( scan_end - scan_start ) : 0;
            
            // Call assembly scanline function
			
			/*
			R_ScanLineASM
			     (
			     screenBuffer + (int)(scan_start) + (y_current * screen_width),
			     depthBuffer  + (int)(scan_start) + (y_current * screen_width),			     
			     (int)(ROUND(scan_end)) - (int)(scan_start),			 
			     z_start,
                 r_start,
                 g_start,
                 b_start,
			     ( z_end - z_start ) * divdx,
			     ( r_end - r_start ) * divdx,
			     ( g_end - g_start ) * divdx,
			     ( b_end - b_start ) * divdx
			     );			
            */
            
			R_ScanLineASM
			     (
			     screenBuffer + (int)(scan_start) + (y_current * screen_width),
			     depthBuffer  + (int)(scan_start) + (y_current * screen_width),			     
			     (int)( scan_end - scan_start + 0.99 ),					 			 
			     z_start,
                 r_start,
                 g_start,
                 b_start,
			     ( z_end - z_start ) * divdx,
			     ( r_end - r_start ) * divdx,
			     ( g_end - g_start ) * divdx,
			     ( b_end - b_start ) * divdx
			     );	 		
			     
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			// Ajust scan positions, z values, colors and texcoords
            // before moving onto the next scanline
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

			scan_start += deltax2;      scan_end   += deltax3;
            z_start    += deltaz2;      z_end      += deltaz3;
            r_start    += deltacr2;     r_end      += deltacr3;
            g_start    += deltacg2;     g_end      += deltacg3;
            b_start    += deltacb2;     b_end      += deltacb3;
            
		} // End of scan between edge 2 and edge 3 loop 
		
	}
	else
	{
		//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
		// Major scan edge on right side of triangle. Scan line x coords
		// in reverse order:
		//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

		//######################################################################
		// Scan between edge3 (major edge) and edge1
		//######################################################################

		for ( ; y_current < y_end1 ; y_current++ )
		{
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Begin plotting pixels
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            
            // Quick divider to figure out change in z,r,g,b values along each scanline pixel
            
            float divdx = scan_end - scan_start != 0 ? 1.0f / ( scan_end - scan_start ) : 0;
            
            // Call assembly scanline function
			
			/*
			R_ScanLineASM
			     (
			     screenBuffer + (int)(scan_start) + (y_current * screen_width),
			     depthBuffer  + (int)(scan_start) + (y_current * screen_width),			     
			     (int)(ROUND(scan_end)) - (int)(scan_start),			 
			     z_start,
                 r_start,
                 g_start,
                 b_start,
			     ( z_end - z_start ) * divdx,
			     ( r_end - r_start ) * divdx,
			     ( g_end - g_start ) * divdx,
			     ( b_end - b_start ) * divdx
			     );			
            */
            
			R_ScanLineASM
			     (
			     screenBuffer + (int)(scan_start) + (y_current * screen_width),
			     depthBuffer  + (int)(scan_start) + (y_current * screen_width),			     
			     (int)( scan_end - scan_start + 0.99 ),				 			 
			     z_start,
                 r_start,
                 g_start,
                 b_start,
			     ( z_end - z_start ) * divdx,
			     ( r_end - r_start ) * divdx,
			     ( g_end - g_start ) * divdx,
			     ( b_end - b_start ) * divdx
			     );	 		
            
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			// Ajust scan positions, z values, colors and texcoords
            // before moving onto the next scanline
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

			scan_start += deltax1;      scan_end   += deltax2;
            z_start    += deltaz1;      z_end      += deltaz2;
            r_start    += deltacr1;     r_end      += deltacr2;
            g_start    += deltacg1;     g_end      += deltacg2;
            b_start    += deltacb1;     b_end      += deltacb2;
            
		} // End of scan between edge 2 and edge 1 loop

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Accuracy fix: floating point inaccuracy caused when dy has a small
        // value caused some values to ween off course. Solution: use the values
        // at the second point before starting again. 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        scan_start  = p->p2[0];
        z_start     = p->p2[2];
        r_start     = p->c2[0];
        g_start     = p->c2[1];
        b_start     = p->c2[2];    

		//######################################################################
		// Scan between edge3 (major edge) and edge2
		//######################################################################

		for ( ; y_current < y_end2 ; y_current++ )
		{
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Begin plotting pixels
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            
            // Quick divider to figure out change in z,r,g,b values along each scanline pixel
            
            float divdx = scan_end - scan_start != 0 ? 1.0f / ( scan_end - scan_start ) : 0;
            
            // Call assembly scanline function
			
			/*
			R_ScanLineASM
			     (
			     screenBuffer + (int)(scan_start) + (y_current * screen_width),
			     depthBuffer  + (int)(scan_start) + (y_current * screen_width),			     
			     (int)(ROUND(scan_end)) - (int)(scan_start),			 
			     z_start,
                 r_start,
                 g_start,
                 b_start,
			     ( z_end - z_start ) * divdx,
			     ( r_end - r_start ) * divdx,
			     ( g_end - g_start ) * divdx,
			     ( b_end - b_start ) * divdx
			     );			
            */
            
			R_ScanLineASM
			     (
			     screenBuffer + (int)(scan_start) + (y_current * screen_width),
			     depthBuffer  + (int)(scan_start) + (y_current * screen_width),			     
			     (int)( scan_end - scan_start + 0.99 ),			 			 
			     z_start,
                 r_start,
                 g_start,
                 b_start,
			     ( z_end - z_start ) * divdx,
			     ( r_end - r_start ) * divdx,
			     ( g_end - g_start ) * divdx,
			     ( b_end - b_start ) * divdx
			     );	 		
            
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			// Ajust scan positions, z values, colors and texcoords
            // before moving onto the next scanline
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

			scan_start += deltax3;      scan_end   += deltax2;
            z_start    += deltaz3;      z_end      += deltaz2;
            r_start    += deltacr3;     r_end      += deltacr2;
            g_start    += deltacg3;     g_end      += deltacg2;
            b_start    += deltacb3;     b_end      += deltacb2;
            
		} // End of scan between edge 2 and edge 3 loop

	} // End else of if ( major edge on left side of triangle )
	
} // End of scan triangle function


