/*
	Lan Wars.
	Copyright (c) 2009, 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.
*/

//=====================================================================================================================
/// @brief		Represents a doom level, loaded from a WAD file. This class only loads the first level in a wad
///				file, it does not load further levels. This loader is also only able to process WADS that have been
///				processed with the glBSP tool or similar tools that produce BSP information in a triangle friendly
///				GL-NODEs format. Such tools are used with all modern Doom source ports. The version of GL-NODES
///				supported is VERSION 2 only. Other versions are not supported. 
//=====================================================================================================================

#include "Precompiled.h"
#include "Doom/DoomLevel.h"
#include "Files/DiskFile.h"
#include "Game/GameCore.h"
#include "Resources/PixelShaderManager.h"
#include "Resources/VertexShaderManager.h"
#include "Resources/TextureManager.h"
#include "Graphics/Camera3D.h"
#include "Graphics/Display.h"
#include "Doom/DoomCollision.h"
#include "Game/GameSession.h"

using namespace Game;

//=====================================================================================================================
/// @brief		Creates the doom level from the given WAD file
/// @param		wad_file - name of the wad file to load
/// @pre		The game must exist
/// @pre		The game must have a texture manager
/// @pre		The game must have a vertex shader manager
/// @pre		The game must have a pixel shader manager
/// @pre		The game must have a valid display
//=====================================================================================================================

DoomLevel::DoomLevel( const char * wad_file )
{
	// Make sure ok:
	
	ENSURE( GameCore::GetSingleton()							, "The game must exist"							)
	ENSURE( GameCore::GetSingleton()->GetTextureManager()		, "The game must have a texture manager"		)
	ENSURE( GameCore::GetSingleton()->GetVertexShaderManager()	, "The game must have a vertex shader manager"	)
	ENSURE( GameCore::GetSingleton()->GetPixelShaderManager()	, "The game must have a pixel shader manager"	)
	ENSURE( GameCore::GetSingleton()->GetDisplay()				, "The game must have a valid display"			)

	// Initialise all these:
	
	m_things				= 0;
	m_thing_count			= 0;
	m_sectors				= 0;
	m_sector_count			= 0;
	m_vertices				= 0;
	m_vertex_count			= 0;
	m_linedefs				= 0;
	m_linedef_count			= 0;
	m_sidedefs				= 0;
	m_sidedef_count			= 0;
	m_gl_vertices			= 0;
	m_gl_vertex_count		= 0;
	m_gl_segs				= 0;
	m_gl_seg_count			= 0;
	m_gl_nodes				= 0;
	m_gl_node_count			= 0;
	m_gl_subsectors			= 0;
	m_gl_subsector_count	= 0;
	m_reject_map			= 0;
	m_reject_map_size		= 0;
	m_seg_subsectors		= 0;
	m_seg_sectors			= 0;
	m_subsector_sectors		= 0;
	m_pp_nodes				= 0;
	m_pp_segs				= 0;
	m_collision				= 0;
		
	m_valid_resources		= false;

	// Make a disk file:
	
	DiskFile d; d.Open(wad_file);
	
	// See if opened:
	
	if ( d.IsGood() )
	{
		// Read the wad header
		
		d.Read( &m_header , sizeof(WadHeader) );
		
		// Make sure the header is ok:
		
		if 
		(
			(
				m_header.m_id[0] == 'I' &&
				m_header.m_id[1] == 'W' &&
				m_header.m_id[2] == 'A' &&
				m_header.m_id[3] == 'D'
			)
			||
			(
				m_header.m_id[0] == 'P' &&
				m_header.m_id[1] == 'W' &&
				m_header.m_id[2] == 'A' &&
				m_header.m_id[3] == 'D'
			)			
		)
		{	
			ReadLumpHeaders(d);			// Read the headers for all lumps
			ReadAllLumps(d);			// Read all the lumps
			
			// Build all render geometry:
			
			std::map< std::string , std::list<DoomTriangle> > level_tris;
			
				// Build the triangles:
			
				BuildRenderGeometry(level_tris);
				
				// Prepare for rendering and generate the vertex arrays
				
				PrepareRenderGeometry(level_tris);

			// Load all of our graphics resources such as pixel shaders etc
			
			LoadGraphicsResources();	
			
			// Create the collision object
			
			m_collision = new DoomCollision(this);
		}
		else
		{
			Errors::IssueError("Unable to open WAD file for level");
		}
	}
}

//=====================================================================================================================
/// @brief		Reads the headers for each lump in the WAD
/// @param		file - file we are reading from. 
//=====================================================================================================================

void DoomLevel::ReadLumpHeaders( DiskFile & file )
{
	// Goto the location of the lump table in the file:
	
	file.Seek( m_header.m_directory_offset , DiskFile::FROM_BEG );
	
	// If there are no lumps then issue an error
	
	if ( m_header.m_lump_count == 0 ) Errors::IssueError("No lumps in WAD file");
	
	// Read all of the directory lumps:
	
	for ( unsigned i = 0 ; i < m_header.m_lump_count ; ++i )
	{
		// Store this lump here:
		
		WadLump lump; memset(&lump,0,sizeof(WadLump)); file.Read(&lump,sizeof(WadLump));
		
		// Get the c-string for it's name:
		
		char name[16] = {0}; memcpy(name,lump.m_name,8);
		
		// Save this lump:
		
		m_lumps[std::string(name)] = lump;
	}
}

//=====================================================================================================================
/// @brief		Destructor. Free's all resources.
//=====================================================================================================================

DoomLevel::~DoomLevel()
{
	// Free all resources:
	
	if ( m_things				) delete[] m_things;
	if ( m_sectors				) delete[] m_sectors;
	if ( m_vertices				) delete[] m_vertices;
	if ( m_linedefs				) delete[] m_linedefs;
	if ( m_sidedefs				) delete[] m_sidedefs;
	if ( m_gl_vertices			) delete[] m_gl_vertices;
	if ( m_gl_segs				) delete[] m_gl_segs;
	if ( m_gl_nodes				) delete[] m_gl_nodes;
	if ( m_gl_subsectors		) delete[] m_gl_subsectors;
	if ( m_reject_map			) delete[] m_reject_map;
	if ( m_seg_subsectors		) delete[] m_seg_subsectors;
	if ( m_seg_sectors			) delete[] m_seg_sectors;
	if ( m_subsector_sectors	) delete[] m_subsector_sectors;
	if ( m_pp_nodes				) delete[] m_pp_nodes;
	if ( m_pp_segs				) delete[] m_pp_segs;
	if ( m_collision			) delete[] m_collision;

	// Need to free all the render lists:
	
	for ( unsigned i = 0 ; i < m_triangle_lists.size() ; ++i )
	{
		// Get this triangle list
		
		TriangleList & t = m_triangle_lists[i];
		
		// Clear it's resources
		
		delete t.m_texture; delete[] t.m_vertices;
	}
}

//=====================================================================================================================
/// @brief		Returns the offset of the given lump name in the file
/// @param		name - name of the lump to go and get
/// @returns	The offset of the lump in the file, or 0xffffffff on failure
/// @pre		Name must be given
/// @pre		The given name must not be > 8 chars in length
//=====================================================================================================================

unsigned DoomLevel::GetLumpOffset( const char * name )
{
	// Make sure ok:
	
	ENSURE( name				, "Name must be given"								)
	ENSURE( strlen(name) <= 8	, "The given name must not be > 8 chars in length"	)
	
	// See if the lump is in the map:
	
	std::map<std::string,WadLump>::iterator i = m_lumps.find(name);
	
	// See if found:
	
	if ( i != m_lumps.end() )
	{
		// Found the lump: return it's offset
		
		return (*i).second.m_offset;
	}
	else
	{
		// Can't find the lump
	
		return 0xffffffff;
	}
}


//=====================================================================================================================
/// @brief		Returns the size of the given lump name in the file
/// @param		name - name of the lump
/// @returns	Size of the given lump or 0xffffffff on failure
//=====================================================================================================================

unsigned DoomLevel::GetLumpSize( const char * name )
{
	// Make sure ok:
	
	ENSURE( name				, "Name must be given"								)
	ENSURE( strlen(name) <= 8	, "The given name must not be > 8 chars in length"	)
	
	// See if the lump is in the map:
	
	std::map<std::string,WadLump>::iterator i = m_lumps.find(name);
	
	// See if found:
	
	if ( i != m_lumps.end() )
	{
		// Found the lump: return it's size
		
		return (*i).second.m_size;
	}
	else
	{
		// Can't find the lump
	
		return 0xffffffff;
	}
}

//=====================================================================================================================
/// @brief		Reads all of the different lumps in the WAD
/// @param		file - file we are reading the lumps from
//=====================================================================================================================

void DoomLevel::ReadAllLumps( DiskFile & file )
{
	// Current lump offset and size
	
	unsigned loff = 0;
	unsigned lsiz = 0;
	
	#pragma region(LUMP_LOADING)

	// Read things:
	
		loff = GetLumpOffset("THINGS"); lsiz = GetLumpSize("THINGS");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Save how many things there are:
			
			m_thing_count = lsiz / sizeof(DoomThing);
			
			// Only if there are things:
			
			if ( m_thing_count > 0 )
			{					
				// Make the things array:
				
				m_things = new DoomThing[m_thing_count];			
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all things:
				
				file.Read( m_things, m_thing_count * sizeof(DoomThing) );
			}
			else
			{
				// No things:
				
				m_things = 0;
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing THINGS definition.");
		}
		
	// Read vertices:
	
		loff = GetLumpOffset("VERTEXES"); lsiz = GetLumpSize("VERTEXES");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Save how many vertices there are:
			
			m_vertex_count = lsiz / sizeof(DoomVertex);
			
			// Only if there are things:
			
			if ( m_vertex_count > 0 )
			{					
				// Make the vertex array:
				
				m_vertices = new DoomVertex[m_vertex_count];			
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all things:
				
				file.Read( m_vertices, m_vertex_count * sizeof(DoomVertex) );
			}
			else
			{
				// No vertices:
				
				Errors::IssueError("Wad file is missing vertices under the VERTEXES definition.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing VERTEXES definition.");
		}
	
	// Read sectors:
	
		loff = GetLumpOffset("SECTORS"); lsiz = GetLumpSize("SECTORS");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Save how many sectors there are:
			
			m_sector_count = lsiz / sizeof(DoomSector);
			
			// Only if there are things:
			
			if ( m_sector_count > 0 )
			{					
				// Make the sectors array:
				
				m_sectors = new DoomSector[m_sector_count];			
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all sectors:
				
				file.Read( m_sectors, m_sector_count * sizeof(DoomSector) );
			}
			else
			{
				// No sectors:
				
				Errors::IssueError("Wad file is missing entries under SECTORS definition.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing SECTORS definition.");
		}
		
	// Read linedefs:
	
		loff = GetLumpOffset("LINEDEFS"); lsiz = GetLumpSize("LINEDEFS");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Save how many linedefs there are:
			
			m_linedef_count = lsiz / sizeof(DoomLinedef);
			
			// Only if there are linedefs:
			
			if ( m_linedef_count > 0 )
			{					
				// Make the linedefs array:
				
				m_linedefs = new DoomLinedef[m_linedef_count];			
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all linedefs:
				
				file.Read( m_linedefs, m_linedef_count * sizeof(DoomLinedef) );
			}
			else
			{
				// No linedefs:
				
				Errors::IssueError("Wad file is missing entries under LINEDEFS definition.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing LINEDEFS definition.");
		}		
	
	// Read sidedefs:
	
		loff = GetLumpOffset("SIDEDEFS"); lsiz = GetLumpSize("SIDEDEFS");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Save how many sidedefs there are:
			
			m_sidedef_count = lsiz / sizeof(DoomSidedef);
			
			// Only if there are sidedefs:
			
			if ( m_sidedef_count > 0 )
			{					
				// Make the sidedefs array:
				
				m_sidedefs = new DoomSidedef[m_sidedef_count];			
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all sidedefs:
				
				file.Read( m_sidedefs, m_sidedef_count * sizeof(DoomSidedef) );
			}
			else
			{
				// No sidedefs:
				
				Errors::IssueError("Wad file is missing entries under SIDEDEFS definition.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing SIDEDEFS definition.");
		}				
		
	// Read gl vertices:
	
		loff = GetLumpOffset("GL_VERT"); lsiz = GetLumpSize("GL_VERT");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Goto the lump position:
		
			file.Seek( loff , DiskFile::FROM_BEG );
		
			// Read the magic id for this GL_VERT definition:
			
			char magic[4] = {0}; file.Read( magic, 4 );
			
			// See if it is ok:
			
			if ( magic[0] == 'g' && magic[1] == 'N' && magic[2] == 'd' && magic[3] == '2' )
			{
				// Good: calculate the size of the vertices in this lump:
				
				unsigned vertices_size = lsiz - 4;
				
				// Calculate the number of vertices:
				
				m_gl_vertex_count = vertices_size / sizeof(DoomGLVertexV2);
				
				// See if there are any vertices:
				
				if ( m_gl_vertex_count > 0 )
				{
					// Make the gl vertices array:
					
					m_gl_vertices = new DoomGLVertexV2[m_gl_vertex_count];			
					
					// Read all vertices:
					
					file.Read( m_gl_vertices, m_gl_vertex_count * sizeof(DoomGLVertexV2) );					
				}
			}
			else
			{
				// Invalid magic vertex format: not supported
				
				Errors::IssueError("Wad file has an unsupported GL_VERT definition version.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing GL_VERT definition.");
		}
		
	// Read gl segs:
	
		loff = GetLumpOffset("GL_SEGS"); lsiz = GetLumpSize("GL_SEGS");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Calculate the number of segs:
			
			m_gl_seg_count = lsiz / sizeof(DoomGLSegV2);
			
			// See if there are any vertices:
			
			if ( m_gl_seg_count > 0 )
			{
				// Make the gl segs array:
				
				m_gl_segs = new DoomGLSegV2[m_gl_seg_count];			
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all segs:
				
				file.Read( m_gl_segs , m_gl_seg_count * sizeof(DoomGLSegV2) );					
			}
			else
			{
				// Missing segs
				
				Errors::IssueError("Wad file has no segs in GL_SEGS lump.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing GL_SEGS definition.");
		}
		
	// Read gl nodes:
	
		loff = GetLumpOffset("GL_NODES"); lsiz = GetLumpSize("GL_NODES");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Calculate the number of segs:
			
			m_gl_node_count = lsiz / sizeof(DoomGLNodeV2);
			
			// See if there are any vertices:
			
			if ( m_gl_node_count > 0 )
			{
				// Make the gl nodes array:
				
				m_gl_nodes = new DoomGLNodeV2[m_gl_node_count];			
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all nodes:
				
				file.Read( m_gl_nodes , m_gl_node_count * sizeof(DoomGLNodeV2) );					
			}
			else
			{
				// Missing nodes
				
				Errors::IssueError("Wad file has no nodes in GL_NODES lump.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing GL_NODES definition.");
		}
		
	// Read gl subsectors:
	
		loff = GetLumpOffset("GL_SSECT"); lsiz = GetLumpSize("GL_SSECT");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// Calculate the number of sub sectors :
			
			m_gl_subsector_count = lsiz / sizeof(DoomGLSubsectorV2);
			
			// See if there are any sub sectors:
			
			if ( m_gl_subsector_count > 0 )
			{
				// Make the gl nodes array:
				
				m_gl_subsectors = new DoomGLSubsectorV2[m_gl_subsector_count];			
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all nodes:
				
				file.Read( m_gl_subsectors , m_gl_subsector_count * sizeof(DoomGLSubsectorV2) );					
			}
			else
			{
				// Missing nodes
				
				Errors::IssueError("Wad file has no nodes in GL_NODES lump.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing GL_NODES definition.");
		}
		
	// Read reject map:
	
		loff = GetLumpOffset("REJECT"); lsiz = GetLumpSize("REJECT");
		
		// Make sure this lump exists:

		if ( loff != 0xffffffff && lsiz != 0xffffffff )
		{
			// See if the reject map is there
			
			if ( lsiz > 0 )
			{			
				// Save the size of the reject map
				
				m_reject_map_size = lsiz;			
			
				// Make the reject map array
				
				m_reject_map = new unsigned char[m_reject_map_size];
			
				// Goto the lump position:
			
				file.Seek( loff , DiskFile::FROM_BEG );
				
				// Read all nodes:
				
				file.Read( m_reject_map ,m_reject_map_size );					
			}
			else
			{
				// Missing nodes
				
				Errors::IssueError("Wad file has no reject entries in the REJECTMAP lump.");
			}
		}
		else
		{
			Errors::IssueError("Wad file is missing REJECTMAP definition.");
		}
		
	#pragma endregion
	
	#pragma region(POST_PROCESSING)	

	/// Generate the mapping from segs to subsectors

	m_seg_subsectors = new DoomGLSubsectorV2 * [m_gl_seg_count];

		// Build the map:

		for ( unsigned i = 0 ; i < m_gl_subsector_count ; ++i )
		{
			// Run through all the segs in this subsector

			for ( unsigned j = 0 ; j < m_gl_subsectors[i].m_count ; ++j )
			{
				// Get the seg index:

				unsigned seg_index = m_gl_subsectors[i].m_first_seg + j;

				// Fill this segs parent:

				m_seg_subsectors[seg_index] = &m_gl_subsectors[i];
			}
		}	

	// Generate mapping from subsectors to sectors

	m_subsector_sectors = new DoomSector * [m_gl_subsector_count];
	
		// Build the map:
	
		for ( unsigned i = 0 ; i < m_gl_subsector_count ; ++i )
		{
			// Get this subsector:
			
			DoomGLSubsectorV2 & subsector = m_gl_subsectors[i];
			
			// Save it's parent sector here:
			
			DoomSector * parent = 0;
		
			// Run through all the segs in this subsector:
			
			for ( unsigned j = 0 ; j < subsector.m_count ; ++j )
			{
				// Get this seg:
				
				DoomGLSegV2 & seg = m_gl_segs[subsector.m_first_seg+j];
				
				// See if it is a miniseg: minisegs have no parent linedef and therefore we cannot find a sector from them
				
				if ( seg.m_linedef < m_gl_seg_count )
				{
					// Get the parent linedef for the seg:
					
					DoomLinedef & linedef = m_linedefs[seg.m_linedef];
					
					// See what side of the linedef we are on:
					
					if ( ( seg.m_side & 1 ) == 0 )
					{
						// On the right side of the parent linedef: get the sidedef for this side of the linedef
						
						DoomSidedef & sidedef = m_sidedefs[linedef.m_sidedef_r];
						
						// Save the sector: found it
						
						parent = &m_sectors[sidedef.m_sector]; break;
					}
					else
					{
						// On the left side of the parent linedef: get the sidedef for this side of the linedef
						
						DoomSidedef & sidedef = m_sidedefs[linedef.m_sidedef_l];
						
						// Save the sector: found it
						
						parent = &m_sectors[sidedef.m_sector]; break;
					}		
				}
			}
			
			// Save the sector for this subsector
			
			m_subsector_sectors[i] = parent;
		}
		
	// Generate the mapping from segs to sectors
	
	m_seg_sectors = new DoomSector * [ m_gl_seg_count ];
	
		// Build the map:
	
		for ( unsigned i = 0 ; i < m_gl_seg_count ; ++i )
		{
			// Get the segs subsector
			
			DoomGLSubsectorV2 & subsector = * m_seg_subsectors[i];
			
			// Calculate the sector number of this sector
			
			unsigned subsec_num = ( unsigned(&subsector) - unsigned(m_gl_subsectors) ) / sizeof(DoomGLSubsectorV2);
			
			// Get it's sector and save:
			
			m_seg_sectors[i] = m_subsector_sectors[subsec_num];
		}
		
	// Build the array of pre-processed tree nodes
	
	m_pp_nodes = new DoomPPNode[ m_gl_node_count ];
	
		// Do all the pre-processing:
		
		for ( unsigned i = 0 ; i < m_gl_node_count ; ++i )
		{
			// Save all the basic info:
		
			m_pp_nodes[i].m_start.x	= (float) m_gl_nodes[i].m_x;
			m_pp_nodes[i].m_start.y	= (float) m_gl_nodes[i].m_y;
			m_pp_nodes[i].m_end.x	= m_gl_nodes[i].m_x + (float) m_gl_nodes[i].m_dx;
			m_pp_nodes[i].m_end.y	= m_gl_nodes[i].m_y + (float) m_gl_nodes[i].m_dy;
			
			// Calculate the line direction:
			
			m_pp_nodes[i].m_direction = ( m_pp_nodes[i].m_end - m_pp_nodes[i].m_start );
			
			// Normalise:
			
			m_pp_nodes[i].m_direction.Normalize();
			
			// Save normal:
			
			m_pp_nodes[i].m_normal = m_pp_nodes[i].m_direction.Perpendicular();

			// Save the 3d versions of direction and normal

			m_pp_nodes[i].m_direction_3d = Vec3f
			( 
				m_pp_nodes[i].m_direction.x	, 
				0 ,
				m_pp_nodes[i].m_direction.y
			);

			m_pp_nodes[i].m_normal_3d = Vec3f
			( 
				m_pp_nodes[i].m_normal.x , 
				0 ,
				m_pp_nodes[i].m_normal.y 
			);
			
			// See if there is a left or right child:
			
			if ( ( m_gl_nodes[i].m_right_child & 0x8000 ) != 0 )
			{
				m_pp_nodes[i].m_right_subsector	= &m_gl_subsectors[ m_gl_nodes[i].m_right_child & 0x7fff ];
				m_pp_nodes[i].m_right_child		= 0;
			}
			else
			{
				m_pp_nodes[i].m_right_child		= &m_pp_nodes[ m_gl_nodes[i].m_right_child ];
				m_pp_nodes[i].m_right_subsector	= 0;
			}
			
			if ( ( m_gl_nodes[i].m_left_child & 0x8000 ) != 0 )
			{
				m_pp_nodes[i].m_left_subsector	= &m_gl_subsectors[ m_gl_nodes[i].m_left_child & 0x7fff ];
				m_pp_nodes[i].m_left_child		= 0;
			}
			else
			{
				m_pp_nodes[i].m_left_child		= &m_pp_nodes[ m_gl_nodes[i].m_left_child ];
				m_pp_nodes[i].m_left_subsector	= 0;
			}		
			
			// Save these fields
			
			m_pp_nodes[i].m_minus_n_dot_p = - m_pp_nodes[i].m_normal.Dot( m_pp_nodes[i].m_start );
		}
		
	// Build the array of preprocessed segs
	
	m_pp_segs = new DoomPPSeg[ m_gl_seg_count ];
	
		// Do all the preprocessing:
		
		for ( unsigned i = 0 ; i < m_gl_seg_count ; ++i )
		{
			//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
			// Need to calculate the index of the start and end vertexes. gl vertices will have bit 15 set so
			// we need to unset this bit and add the gl vertex index to the index to get the true index in this case.
			// This class treats vanilla doom vertices and gl vertices as if they were in the same array
			//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
			
			unsigned si = m_gl_segs[i].m_start; 
			unsigned ei = m_gl_segs[i].m_end;
			
			if ( si & 0x8000 ) 
			{
				// Is a GL vertex: fix the index
			
				si &= 0x7fff; si += m_vertex_count;
			}

			if ( ei & 0x8000 ) 
			{
				// Is a GL vertex: fix the index
			
				ei &= 0x7fff; ei += m_vertex_count;
			}
		
			// Save all the basic info:
		
			m_pp_segs[i].m_start			= GetVertex(si);
			m_pp_segs[i].m_end				= GetVertex(ei);
			m_pp_segs[i].m_direction		= m_pp_segs[i].m_end - m_pp_segs[i].m_start;
			
			m_pp_segs[i].m_length = m_pp_segs[i].m_direction.Normalize();

			m_pp_segs[i].m_direction_3d		= Vec3f(m_pp_segs[i].m_direction.x,0,m_pp_segs[i].m_direction.y);
			m_pp_segs[i].m_normal			= m_pp_segs[i].m_direction.Perpendicular();
			m_pp_segs[i].m_normal_3d		= Vec3f(m_pp_segs[i].m_normal.x,0,m_pp_segs[i].m_normal.y);
			m_pp_segs[i].m_minus_n_dot_p	= -m_pp_segs[i].m_normal.Dot( m_pp_segs[i].m_start );
			m_pp_segs[i].m_minus_d_dot_p	= -m_pp_segs[i].m_direction.Dot( m_pp_segs[i].m_start );
			m_pp_segs[i].m_sector			= m_seg_sectors[i];
			m_pp_segs[i].m_subsector		= m_seg_subsectors[i];
			m_pp_segs[i].m_floor_height		= float( m_pp_segs[i].m_sector->m_floor_y );
			m_pp_segs[i].m_ceiling_height	= float( m_pp_segs[i].m_sector->m_ceiling_y );
			
			// Store the linedef if the seg has one:
			
			if ( m_gl_segs[i].m_linedef < m_linedef_count )
			{
				// Got a linedef: get the sidedef:
				
				m_pp_segs[i].m_linedef = &m_linedefs[m_gl_segs[i].m_linedef];
				
				// See if on the left or right side of the parent:
				
				if ( ( m_gl_segs[i].m_side & 1 ) ==  0 )
				{
					// On the right side:
					
					m_pp_segs[i].m_sidedef = &m_sidedefs[m_pp_segs[i].m_linedef->m_sidedef_r];
				}
				else
				{
					// On the left side:
					
					m_pp_segs[i].m_sidedef = &m_sidedefs[m_pp_segs[i].m_linedef->m_sidedef_l];				
				}
			}
			else
			{
				// No linedef and sidedef: miniseg
			
				m_pp_segs[i].m_linedef = 0;
				m_pp_segs[i].m_sidedef = 0;
			}			
						
			// Figure out what seg type it is:
			
			if ( m_gl_segs[i].m_opposite_seg < m_gl_seg_count )
			{
				// Save it's neighbour
			
				m_pp_segs[i].m_opposite_seg = &m_pp_segs[m_gl_segs[i].m_opposite_seg];
			
				// Potentially passible: get the sector of it's neighbour
				
				DoomSector & sector = * m_seg_sectors[m_gl_segs[i].m_opposite_seg];
				
				// See if floors match height, or if we are higher:
				
				if ( m_pp_segs[i].m_sector->m_floor_y >= sector.m_floor_y )
				{
					// Can't collide with a lower wall: see if our ceiling is lower than or equal to the other seg's:
					
					if ( m_pp_segs[i].m_sector->m_ceiling_y <= sector.m_ceiling_y )
					{
						// There can be no collision: 
						
						m_pp_segs[i].m_upper_height = m_pp_segs[i].m_ceiling_height;
						m_pp_segs[i].m_lower_height = m_pp_segs[i].m_floor_height;
						m_pp_segs[i].m_type			= DoomPPSeg::SEG_TYPE_NO_WALL;
					}
					else
					{
						// Can collide with an upper wall: see if the other sector is fully closed on the y axis
						
						if ( sector.m_ceiling_y <= sector.m_floor_y )
						{
							// This seg is impassible:
							
							m_pp_segs[i].m_upper_height = m_pp_segs[i].m_ceiling_height;
							m_pp_segs[i].m_lower_height = m_pp_segs[i].m_floor_height;	
							m_pp_segs[i].m_type			= DoomPPSeg::SEG_TYPE_FULL_WALL;					
						}
						else
						{
							// We can collide with the upper wall:
							
							m_pp_segs[i].m_upper_height = sector.m_ceiling_y;
							m_pp_segs[i].m_lower_height = m_pp_segs[i].m_floor_height;	
							m_pp_segs[i].m_type			= DoomPPSeg::SEG_TYPE_UPPER_WALL;
						}
					}
				}
				else
				{
					// Our floor is lower: therefore we can collide on the bottom potentially. See if the ceiling is higher than the others:				
					
					if ( m_pp_segs[i].m_sector->m_ceiling_y > sector.m_ceiling_y )
					{
						// Our ceiling is higher: we can collide with both the floor and the ceiling: see if the other sector is fully closed first:
						
						if ( sector.m_ceiling_y <= sector.m_floor_y )
						{
							// This seg is impassible:
							
							m_pp_segs[i].m_upper_height = m_pp_segs[i].m_ceiling_height;
							m_pp_segs[i].m_lower_height = m_pp_segs[i].m_floor_height;	
							m_pp_segs[i].m_type			= DoomPPSeg::SEG_TYPE_FULL_WALL;
						}
						else
						{
							// Can collide with both the lower and upper walls
							
							m_pp_segs[i].m_upper_height = sector.m_ceiling_y;
							m_pp_segs[i].m_lower_height = sector.m_floor_y;	
							m_pp_segs[i].m_type			= DoomPPSeg::SEG_TYPE_LOWER_UPPER_WALL;
						}
					}
					else
					{
						// Our ceiling is not higher: we can only collide with the lower wall. See if the other sector is fully closed first:
						
						if ( sector.m_ceiling_y <= sector.m_floor_y )
						{
							// This seg is impassible:
							
							m_pp_segs[i].m_upper_height = m_pp_segs[i].m_ceiling_height;
							m_pp_segs[i].m_lower_height = m_pp_segs[i].m_floor_height;	
							m_pp_segs[i].m_type			= DoomPPSeg::SEG_TYPE_FULL_WALL;
						}
						else
						{						
							m_pp_segs[i].m_lower_height = sector.m_floor_y;
							m_pp_segs[i].m_upper_height = m_pp_segs[i].m_ceiling_height;
							m_pp_segs[i].m_type			= DoomPPSeg::SEG_TYPE_LOWER_WALL;
						}
					}
				}
			}
			else
			{
				// No neighbour:
				
				m_pp_segs[i].m_opposite_seg = 0;
			
				// This seg is impassible: no neighbour
				
				m_pp_segs[i].m_upper_height = m_pp_segs[i].m_ceiling_height;
				m_pp_segs[i].m_lower_height = m_pp_segs[i].m_floor_height;
				m_pp_segs[i].m_type			= DoomPPSeg::SEG_TYPE_FULL_WALL;
			}
		}
	
	#pragma endregion
}

//=====================================================================================================================
/// @brief		Loads vertex and pixel shaders for the level renderer
//=====================================================================================================================

void DoomLevel::LoadGraphicsResources()
{
	// Get the game:
	
	GameCore * game = GameCore::GetSingleton();
		
	// Now: we need to load all of our resources
	
	m_vs = game->GetVertexShaderManager()->GetVertexShader("Shaders\\LitAndColored.vsh.bin");
	m_ps = game->GetPixelShaderManager()->GetPixelShader("Shaders\\LitAndColored.psh.bin");
	
	// Create our vertex format:
	
	{
		VertexFormat::ElementUsage usages[] = 
		{
			VertexFormat::VF_USAGE_POSITION	,
			VertexFormat::VF_USAGE_TEXCOORD	,
			VertexFormat::VF_USAGE_COLOR	,
			VertexFormat::VF_USAGE_NORMAL
		};
		
		VertexFormat::ElementType types[] = 
		{
			VertexFormat::VF_TYPE_FLOAT3	,
			VertexFormat::VF_TYPE_FLOAT2	,
			VertexFormat::VF_TYPE_FLOAT4	,
			VertexFormat::VF_TYPE_FLOAT3
		};
		
		// Make it:
		
		m_vf = VertexFormat( GameCore::GetSingleton()->GetDisplay() , 4 , usages , types );
	}
	
	// See if all is valid so far:
	
	if ( m_vs.IsValid() && m_ps.IsValid() && m_vf.IsValid() )
	{
		// Try and get the transform shader uniform
		
		m_u_transform = m_vs.GetUniform("Transform");
		
		// Try and get the camera position uniform
		
		m_u_camera_position = m_ps.GetUniform("CameraPosition");
		
		// Get the fog distance uniform
		
		m_u_fog_distance = m_ps.GetUniform("FogDistance");
		
		// Get the fog color uniform
		
		m_u_fog_color = m_ps.GetUniform("FogColor");
				
		// See if ok:
		
		if ( m_u_transform.IsValid() && m_u_camera_position.IsValid() && m_u_fog_distance.IsValid() && m_u_fog_color.IsValid() ) 
		{
			m_valid_resources = true;
		}
	}
	else
	{
		// Have invalid resources
	
		m_valid_resources = false;
	}
}

//=====================================================================================================================
/// @brief		Triangulates the floors and ceilings for a given subsector 
/// @param		subsector - sub sector to triangulate
/// @param		output - where to save the output triangles
/// @pre		There must be at least 3 segs in the given subsector 
//=====================================================================================================================

void DoomLevel::TriangulateFlats( const DoomGLSubsectorV2 & subsector , std::map< std::string , std::list<DoomTriangle> > & output )
{
	// Make sure ok:
	
	ENSURE( subsector.m_count >= 3 , "There must be at least 3 segs in the given subsector" ) 

	// Build a list of segs for this sub sector: we will insert fake segs into this every now and then for the triangulation
	
	std::list<DoomPPSeg> segs;
	
	// Build the list:
	
	for ( unsigned i = 0 ; i < subsector.m_count ; i++ ) 
	{
		segs.push_back( m_pp_segs[subsector.m_first_seg+i] );
	}
	
	// Need to get the parent sector for this subsector

	const DoomSector & sector = GetSector(subsector);
	
	// Get the texture name for the floor and ceiling of the sector:
	
	std::string tex_f = GetFloorTexture(sector); 
	std::string tex_c = GetCeilingTexture(sector);
	
	// If there are no entries for these two textures in the output list of triangles then make them:
	
	if ( output.find(tex_f) == output.end() ) 
	{
		output[tex_f] = std::list<DoomTriangle>();
	}
	
	if ( output.find(tex_c) == output.end() ) 
	{
		output[tex_c] = std::list<DoomTriangle>();
	}
	
	// Calculate the light level for this sector:
	
	Color sector_color = Color
	( 
		sector.m_light_level / 255.0f , 
		sector.m_light_level / 255.0f , 
		sector.m_light_level / 255.0f , 
		1.0f
	);
	
	// Use these two triangles for ceilings and floors:
	
	DoomTriangle f_tri;
	DoomTriangle c_tri;
	
	// Set the triangle colors
	
	f_tri.m_color = sector_color;
	c_tri.m_color = sector_color;
	
	// Set the heights of the vertices:
	
	f_tri.m_p1.y = sector.m_floor_y; 
	f_tri.m_p2.y = sector.m_floor_y;
	f_tri.m_p3.y = sector.m_floor_y;
	
	c_tri.m_p1.y = sector.m_ceiling_y; 
	c_tri.m_p2.y = sector.m_ceiling_y;
	c_tri.m_p3.y = sector.m_ceiling_y;
		
	// Continue until the list of segs is empty:
	
	while ( segs.size() > 0 )
	{
		// See if there 3 segs left: this is an easy case and we can just stick the triangle into the list
		
		if ( segs.size() == 3 )
		{
			// Makeup the triangle points:
			
			DoomPPSeg s1 = segs.front(); segs.pop_front(); Vec2f s1p = s1.m_start;
			DoomPPSeg s2 = segs.front(); segs.pop_front(); Vec2f s2p = s2.m_start;
			DoomPPSeg s3 = segs.front(); segs.pop_front(); Vec2f s3p = s3.m_start;
			
			// Set vertex positions
			
			f_tri.m_p1.x = s1p.x; f_tri.m_p1.z = s1p.y;
			f_tri.m_p2.x = s2p.x; f_tri.m_p2.z = s2p.y;
			f_tri.m_p3.x = s3p.x; f_tri.m_p3.z = s3p.y;

			c_tri.m_p3.x = s1p.x; c_tri.m_p3.z = s1p.y;
			c_tri.m_p2.x = s2p.x; c_tri.m_p2.z = s2p.y;
			c_tri.m_p1.x = s3p.x; c_tri.m_p1.z = s3p.y;
			
			// Set the texture coordiantes:
			
			f_tri.m_t1.x = f_tri.m_p1.x; f_tri.m_t1.y = f_tri.m_p1.z;
			f_tri.m_t2.x = f_tri.m_p2.x; f_tri.m_t2.y = f_tri.m_p2.z;
			f_tri.m_t3.x = f_tri.m_p3.x; f_tri.m_t3.y = f_tri.m_p3.z;
			
			c_tri.m_t1.x = c_tri.m_p1.x; c_tri.m_t1.y = c_tri.m_p1.z;
			c_tri.m_t2.x = c_tri.m_p2.x; c_tri.m_t2.y = c_tri.m_p2.z;
			c_tri.m_t3.x = c_tri.m_p3.x; c_tri.m_t3.y = c_tri.m_p3.z;
			
			// Pop these two triangles into the list of triangles:
			
			output[tex_f].push_back(f_tri);
			output[tex_c].push_back(c_tri);
		}
		else
		{
			// Not so easy case: take the first two segs from the list 
			
			DoomPPSeg s1 = segs.front(); segs.pop_front(); Vec2f s1p = s1.m_start;
			DoomPPSeg s2 = segs.front(); segs.pop_front(); Vec2f s2p = s2.m_start;
			
			//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
			// The third seg comes runs from the start point of seg 1 to the end point of seg 2.
			// In essence we are cutting a triangular cross section out of the convex area here. We will add this
			// Seg back into the triangle after we are done. Note: we are not interested in other
			// Seg attributes here either, just start and end points... 
			//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
			
			DoomPPSeg s3; 
			
			s3.m_start	= s1.m_start; 
			s3.m_end	= s2.m_end; 
			Vec2f s3p	= s2.m_end;
			
			// Set vertex positions
			
			f_tri.m_p1.x = s1p.x; f_tri.m_p1.z = s1p.y;
			f_tri.m_p2.x = s2p.x; f_tri.m_p2.z = s2p.y;
			f_tri.m_p3.x = s3p.x; f_tri.m_p3.z = s3p.y;

			c_tri.m_p3.x = s1p.x; c_tri.m_p3.z = s1p.y;
			c_tri.m_p2.x = s2p.x; c_tri.m_p2.z = s2p.y;
			c_tri.m_p1.x = s3p.x; c_tri.m_p1.z = s3p.y;
			
			// Set the texture coordiantes:
			
			f_tri.m_t1.x = f_tri.m_p1.x; f_tri.m_t1.y = f_tri.m_p1.z;
			f_tri.m_t2.x = f_tri.m_p2.x; f_tri.m_t2.y = f_tri.m_p2.z;
			f_tri.m_t3.x = f_tri.m_p3.x; f_tri.m_t3.y = f_tri.m_p3.z;
			
			c_tri.m_t1.x = c_tri.m_p1.x; c_tri.m_t1.y = c_tri.m_p1.z;
			c_tri.m_t2.x = c_tri.m_p2.x; c_tri.m_t2.y = c_tri.m_p2.z;
			c_tri.m_t3.x = c_tri.m_p3.x; c_tri.m_t3.y = c_tri.m_p3.z;
			
			// Pop these two triangles into the list of triangles:
			
			output[tex_f].push_back(f_tri);
			output[tex_c].push_back(c_tri);
			
			// Put the last seg back into the list of remaining triangles: must go at the front to correct link with the rest of the segs.
			
			segs.push_front(s3);
		}
	}
}

//=====================================================================================================================
/// @brief		Triangulates either upper or lower walls as necessary for the given two sided-seg
/// @param		seg - seg to triangulate the upper and lower walls for
/// @param		sector - sector the seg belongs to
/// @param		output - where to save the output triangles
//=====================================================================================================================

void DoomLevel::TriangulateTwoSidedWalls
( 
	const DoomPPSeg & seg , const DoomSector & sector, std::map< std::string , std::list<DoomTriangle> > & output 
)
{
	// Get the segment on the other side of this seg: only do if it is valid
	
	if ( seg.m_opposite_seg )
	{
		// Right: get the opposite seg:
		
		DoomPPSeg & opposite_seg = * seg.m_opposite_seg;
		
		// Make sure it is not a mini seg:
		
		if ( IsMiniseg(opposite_seg) ) return;
		
		// Get the sector this seg belongs to:
		
		const DoomSector & opposite_sector = * opposite_seg.m_sector;
		
		// Get the sidedef for this seg
		
		const DoomSidedef & sidedef = * seg.m_sidedef;

		// Get the linedef for this seg:

		const DoomLinedef & linedef = * seg.m_linedef;
		
		// See if our floor is lower than it's floor: if so then we need to make a lower wall
		
		if ( sector.m_floor_y < opposite_sector.m_floor_y )
		{
			// Use this triangle:
			
			DoomTriangle tri;
			
			// Set the color of the triangle:
			
			tri.m_color.r = float( sector.m_light_level ) / 255.0f;
			tri.m_color.g = float( sector.m_light_level ) / 255.0f;
			tri.m_color.b = float( sector.m_light_level ) / 255.0f;
			tri.m_color.a = 1;
			
			// Get the start and end points for this seg:
			
			Vec2f ss = seg.m_start;
			Vec2f se = seg.m_end;
			
			// Get the length of this line for texture length adjustment.
			
			float len = ( ss - se ).Length();
			
			// Calculate the points for the first triangle:
			
			tri.m_p1 = Vec3f( ss.x , opposite_sector.m_floor_y	, ss.y );
			tri.m_p2 = Vec3f( se.x , opposite_sector.m_floor_y	, se.y );
			tri.m_p3 = Vec3f( se.x , sector.m_floor_y			, se.y );
			
			tri.m_t1 = Vec2f( 0  		, 0 );
			tri.m_t2 = Vec2f( 0	+ len	, 0	);
			tri.m_t3 = Vec2f( 0	+ len	, 0 );
			
			tri.m_t1.x += sidedef.m_tex_x_shift;
			tri.m_t2.x += sidedef.m_tex_x_shift;
			tri.m_t3.x += sidedef.m_tex_x_shift;
			
			tri.m_t1.y += sidedef.m_tex_y_shift;
			tri.m_t2.y += sidedef.m_tex_y_shift;
			tri.m_t3.y += sidedef.m_tex_y_shift;

			if ( ( linedef.m_flags & DoomLinedef::FLAG_LOWER_UNPEGGED ) == 0 )
			{
				tri.m_t1.y -= opposite_sector.m_floor_y - sector.m_floor_y;
				tri.m_t2.y -= opposite_sector.m_floor_y - sector.m_floor_y;
			}
			else
			{
				tri.m_t1.y += ( sector.m_ceiling_y - opposite_sector.m_floor_y );
				tri.m_t2.y += ( sector.m_ceiling_y - opposite_sector.m_floor_y );
				tri.m_t3.y += ( sector.m_ceiling_y - opposite_sector.m_floor_y ) + ( opposite_sector.m_floor_y - sector.m_floor_y );
			}
			
			// Push this triangle onto the list of triangles
			
				// Get the name of the lower texture for the sidedef:
				
				std::string tex_name = GetLowerTexture(sidedef);
			
				// Need to make a new list of triangles if there is not one already
				
				if ( output.find(tex_name) == output.end() ) output[tex_name] = std::list<DoomTriangle>();
				
				// Pop into the list:
				
				output[tex_name].push_back(tri);
				
			// Calculate the points for the second triangle:
			
			tri.m_p1 = Vec3f( ss.x , opposite_sector.m_floor_y	, ss.y );
			tri.m_p2 = Vec3f( se.x , sector.m_floor_y			, se.y );
			tri.m_p3 = Vec3f( ss.x , sector.m_floor_y			, ss.y );
			
			tri.m_t1 = Vec2f( 0  		, 0	);
			tri.m_t2 = Vec2f( 0	+ len	, 0 );
			tri.m_t3 = Vec2f( 0			, 0 );
			
			tri.m_t1.x += sidedef.m_tex_x_shift;
			tri.m_t2.x += sidedef.m_tex_x_shift;
			tri.m_t3.x += sidedef.m_tex_x_shift;
			
			tri.m_t1.y += sidedef.m_tex_y_shift;
			tri.m_t2.y += sidedef.m_tex_y_shift;
			tri.m_t3.y += sidedef.m_tex_y_shift;

			if ( ( linedef.m_flags & DoomLinedef::FLAG_LOWER_UNPEGGED ) == 0 )
			{
				tri.m_t1.y -= opposite_sector.m_floor_y - sector.m_floor_y;
			}
			else
			{
				tri.m_t1.y += ( sector.m_ceiling_y - opposite_sector.m_floor_y );
				tri.m_t2.y += ( sector.m_ceiling_y - opposite_sector.m_floor_y ) + ( opposite_sector.m_floor_y - sector.m_floor_y );
				tri.m_t3.y += ( sector.m_ceiling_y - opposite_sector.m_floor_y ) + ( opposite_sector.m_floor_y - sector.m_floor_y );
			}
			
			// Push this triangle onto the list of triangles

			output[tex_name].push_back(tri);				
		}
		
		// See if our ceiling is higher than it's ceiling: if so then we need to make an upper wall
		
		if ( sector.m_ceiling_y > opposite_sector.m_ceiling_y )
		{
			// Use this triangle:
			
			DoomTriangle tri;
			
			// Set the color of the triangle:
			
			tri.m_color.r = float( sector.m_light_level ) / 255.0f;
			tri.m_color.g = float( sector.m_light_level ) / 255.0f;
			tri.m_color.b = float( sector.m_light_level ) / 255.0f;
			tri.m_color.a = 1;
			
			// Get the start and end points for this seg:
			
			Vec2f ss = seg.m_start;
			Vec2f se = seg.m_end;
			
			// Get the length of this line for texture length adjustment.
			
			float len = ( ss - se ).Length();
			
			// Calculate the points for the first triangle:
			
			tri.m_p1 = Vec3f( ss.x , sector.m_ceiling_y				, ss.y );
			tri.m_p2 = Vec3f( se.x , sector.m_ceiling_y				, se.y );
			tri.m_p3 = Vec3f( se.x , opposite_sector.m_ceiling_y	, se.y );
			
			tri.m_t1 = Vec2f( 0  		, 0 );
			tri.m_t2 = Vec2f( 0	+ len	, 0 );
			tri.m_t3 = Vec2f( 0	+ len	, 0 );
			
			tri.m_t1.x += sidedef.m_tex_x_shift;
			tri.m_t2.x += sidedef.m_tex_x_shift;
			tri.m_t3.x += sidedef.m_tex_x_shift;
			
			tri.m_t1.y += sidedef.m_tex_y_shift;
			tri.m_t2.y += sidedef.m_tex_y_shift;
			tri.m_t3.y += sidedef.m_tex_y_shift;

			if ( ( linedef.m_flags & DoomLinedef::FLAG_UPPER_UNPEGGED ) != 0 )
			{
				tri.m_t3.y += sector.m_ceiling_y - opposite_sector.m_ceiling_y;
			}
			else
			{
				tri.m_t1.y -= ( sector.m_ceiling_y - opposite_sector.m_ceiling_y );
				tri.m_t2.y -= ( sector.m_ceiling_y - opposite_sector.m_ceiling_y );
			}
			
			// Push this triangle onto the list of triangles
			
				// Get the name of the upper texture for the sidedef:
				
				std::string tex_name = GetUpperTexture(sidedef);
			
				// Need to make a new list of triangles if there is not one already
				
				if ( output.find(tex_name) == output.end() ) output[tex_name] = std::list<DoomTriangle>();
				
				// Pop into the list:
				
				output[tex_name].push_back(tri);
				
			// Calculate the points for the second triangle:
			
			tri.m_p1 = Vec3f( ss.x , sector.m_ceiling_y				, ss.y );
			tri.m_p2 = Vec3f( se.x , opposite_sector.m_ceiling_y	, se.y );
			tri.m_p3 = Vec3f( ss.x , opposite_sector.m_ceiling_y	, ss.y );
			
			tri.m_t1 = Vec2f( 0  		, 0 );
			tri.m_t2 = Vec2f( 0	+ len	, 0 );
			tri.m_t3 = Vec2f( 0			, 0 );
			
			tri.m_t1.x += sidedef.m_tex_x_shift;
			tri.m_t2.x += sidedef.m_tex_x_shift;
			tri.m_t3.x += sidedef.m_tex_x_shift;
			
			tri.m_t1.y += sidedef.m_tex_y_shift;
			tri.m_t2.y += sidedef.m_tex_y_shift;
			tri.m_t3.y += sidedef.m_tex_y_shift;

			if ( ( linedef.m_flags & DoomLinedef::FLAG_UPPER_UNPEGGED ) != 0 )
			{
				tri.m_t2.y += sector.m_ceiling_y - opposite_sector.m_ceiling_y;
				tri.m_t3.y += sector.m_ceiling_y - opposite_sector.m_ceiling_y;
			}
			else
			{
				tri.m_t1.y -= ( sector.m_ceiling_y - opposite_sector.m_ceiling_y );
			}
			
			// Push this triangle onto the list of triangles

			output[tex_name].push_back(tri);				
		}		
	}
}

//=====================================================================================================================
/// @brief		Builds the level geometry for level rendering and sorts all triangles by texture number
/// @param		output - This is a map from texture names to a list of triangles of the same texture. It is here the 
///				output geometry is saved.
//=====================================================================================================================
 
void DoomLevel::BuildRenderGeometry( std::map< std::string , std::list<DoomTriangle> > & output )
{
	// Run through all the subsectors:
	
	for ( unsigned i = 0 ; i < m_gl_subsector_count ; ++i )
	{
		// Get the current subsector
		
		DoomGLSubsectorV2 & subsector = m_gl_subsectors[i];
		
		// Need to get the parent sector for this subsector
		
		const DoomSector & sector = GetSector(subsector);	
		
		// Make sure there are at least 3 lines in the subsector:
		
		if ( subsector.m_count < 3 ) continue;
		
		// Make sure the subsector is closed
		
		if ( m_gl_segs[subsector.m_first_seg].m_start != m_gl_segs[subsector.m_first_seg+subsector.m_count-1].m_end ) continue;
		
		// Calculate the light level for this sector:
		
		Color sector_color = Color
		( 
			sector.m_light_level / 255.0f , 
			sector.m_light_level / 255.0f , 
			sector.m_light_level / 255.0f , 
			1.0f
		);
		
		// Use this triangle:
		
		DoomTriangle tri;
		
		// Set the triangle color
		
		tri.m_color = sector_color;
		
		// Triangulate the ceilings and floors:
		
		TriangulateFlats(subsector,output);						
		
		// Run through all segs for the subsector:
		
		for ( unsigned j = 0 ; j < subsector.m_count ; ++j )
		{
			// Get this seg:
			
			DoomPPSeg & seg = m_pp_segs[ subsector.m_first_seg + j ];
			
			// Need to make a triangle for this edge: start by finding out the start and end points of this subsector
			
			Vec2f s = seg.m_start; Vec2f e = seg.m_end;
			
			// See if this segment is a Miniseg. If so then it has no upper, lower or middle walls and was only created from a linesplit
			
			if ( IsMiniseg(seg) == false )
			{
				// Get the sidedef for this seg:
				
				const DoomSidedef & sidedef = * seg.m_sidedef;

				// Get the linedef for this seg:

				const DoomLinedef & linedef = * seg.m_linedef;
			
				// Right: see if the linedef the seg belongs to is a one-sided side-def
				
				if ( seg.m_opposite_seg == 0 )
				{
					// Awesome. We can just tesselate this sidedef normally.
					
						// Get the sidedef texture
						
						std::string tex_name = GetMiddleTexture(sidedef);
					
						// Get the length of this line for texture length adjustment. Don't ask why, but we have to mul by 1.412 or root 2.
						
						float len = ( s - e ).Length();
					
						// Do the first triangle:
						
						tri.m_p1 = Vec3f( s.x	, sector.m_ceiling_y	, s.y	);
						tri.m_p2 = Vec3f( e.x	, sector.m_ceiling_y	, e.y	);
						tri.m_p3 = Vec3f( e.x	, sector.m_floor_y		, e.y	);
						
						tri.m_t1 = Vec2f( 0  		, 0 );
						tri.m_t2 = Vec2f( 0	+ len	, 0 );
						tri.m_t3 = Vec2f( 0	+ len	, 0 );
						
						tri.m_t1.x += sidedef.m_tex_x_shift;
						tri.m_t2.x += sidedef.m_tex_x_shift;
						tri.m_t3.x += sidedef.m_tex_x_shift;
						
						tri.m_t1.y += sidedef.m_tex_y_shift;
						tri.m_t2.y += sidedef.m_tex_y_shift;
						tri.m_t3.y += sidedef.m_tex_y_shift;

						if ( ( linedef.m_flags & DoomLinedef::FLAG_LOWER_UNPEGGED ) != 0  )
						{
							tri.m_t1.y -= sector.m_ceiling_y - sector.m_floor_y;
							tri.m_t2.y -= sector.m_ceiling_y - sector.m_floor_y;
						}
						else
						{
							tri.m_t3.y += sector.m_ceiling_y - sector.m_floor_y;							
						}
						
						// Push this triangle onto the list of triangles
						
							// Need to make a new list of triangles if there is not one already
							
							if ( output.find(tex_name) == output.end() ) output[tex_name] = std::list<DoomTriangle>();
							
							// Pop into the list:
							
							output[tex_name].push_back(tri);
						
						// Do the second triangle:
						
						tri.m_p1 = Vec3f( s.x	, sector.m_ceiling_y	, s.y );
						tri.m_p2 = Vec3f( e.x	, sector.m_floor_y		, e.y );
						tri.m_p3 = Vec3f( s.x	, sector.m_floor_y		, s.y );
						
						tri.m_t1 = Vec2f( 0  		, 0 );
						tri.m_t2 = Vec2f( 0	+ len	, 0 );
						tri.m_t3 = Vec2f( 0			, 0 );
						
						tri.m_t1.x += sidedef.m_tex_x_shift;
						tri.m_t2.x += sidedef.m_tex_x_shift;
						tri.m_t3.x += sidedef.m_tex_x_shift;
						
						tri.m_t1.y += sidedef.m_tex_y_shift;
						tri.m_t2.y += sidedef.m_tex_y_shift;
						tri.m_t3.y += sidedef.m_tex_y_shift;

						if ( ( linedef.m_flags & DoomLinedef::FLAG_LOWER_UNPEGGED ) != 0 )
						{
							tri.m_t1.y -= sector.m_ceiling_y - sector.m_floor_y;
						}
						else
						{
							tri.m_t2.y += sector.m_ceiling_y - sector.m_floor_y;
							tri.m_t3.y += sector.m_ceiling_y - sector.m_floor_y;
						}
						
						// Push this triangle onto the list of triangles				
							
						output[tex_name].push_back(tri);												
				}
				else
				{
					// Two sided line segment: triangulate it's lower and upper walls
					
					TriangulateTwoSidedWalls(seg,sector,output);
				}
			}
		}
	}
}

//=====================================================================================================================
/// @brief		Preps the level geometry for level rendering, putting all the triangles into m_triangle_lists field
/// @param		input - This is a map from texture names to a list of triangles of the same texture. This is used to
///				build the final vertex arrays for the level.
//=====================================================================================================================

void DoomLevel::PrepareRenderGeometry( std::map< std::string , std::list<DoomTriangle> > & input )
{
	// Clear the triangle lists:
	
	m_triangle_lists.clear();

	// Reserve the required amount on the triangle lists array to accomdate all the lists we'll need:
	
	m_triangle_lists.reserve( input.size() + 2 );
	
	// Get the texture manager off the game:
	
	TextureManager * tex_mgr = GameCore::GetSingleton()->GetTextureManager();
	
	// Now do each triangle list:
	
	std::map< std::string , std::list<DoomTriangle> >::iterator i = input.begin();
	std::map< std::string , std::list<DoomTriangle> >::iterator e = input.end();
	
	while ( i != e )
	{		
		// Get the list of triangles for this texture
		
		std::list<DoomTriangle> & tris = (*i).second; 
	
		// Makeup the definition for this triangle list:
		
		TriangleList t; 
		
		// Size the triangle list
		
		t.m_num_triangles = tris.size();
		
		// Ignore this triangle if zero sized:
		
		if ( t.m_num_triangles == 0 ) 
		{
			// Move on and continue:
			
			i++; continue;
		}
		
		// Makeup the render vertex array:
		
		t.m_vertices = new RenderVertex[t.m_num_triangles*3];
		
		// Get the fully formed name of the texture
		
		std::string name = std::string("Textures\\Level\\") + (*i).first + std::string(".dds");
		
		// Set the texture
		
		t.m_texture	= new Texture( tex_mgr->GetTexture(name) );
		
		// See if it loaded ok:
		
		if ( t.m_texture->IsValid() == false )
		{
			// Load the default texture
			
			( * t.m_texture ) = tex_mgr->GetTexture("Textures\\Default.dds");
			
			// If using the debug console then print an error:
			
			#ifdef USE_DEBUG_CONSOLE 
			
				// Print to this buffer
				
				char buffer[1024]; sprintf_s(buffer,1024,"\nWARNING: MISSING LEVEL TEXTURE '%s'",(*i).first.c_str() );
				
				// Print the warning
			
				DebugConsole::Print(buffer);
			
			#endif
		}
		
		// Get the floating point width and height of the texture
		
		float tex_w = float(t.m_texture->GetWidth());
		float tex_h = float(t.m_texture->GetHeight());
		
		// This is the current position in the render vertex array to fill
		
		unsigned render_vertex_pos = 0;
		
		// Keep going until the list of vertices is empty:
		
		while ( tris.size() > 0 )
		{
			// Get this triangle
			
			DoomTriangle & triangle = tris.front();						
			
			// Fill in the render vertex array
			
			t.m_vertices[render_vertex_pos].m_pos	=	triangle.m_p1;
			t.m_vertices[render_vertex_pos].m_tex	=	triangle.m_t1;
			t.m_vertices[render_vertex_pos].m_tex.x /=	tex_w;
			t.m_vertices[render_vertex_pos].m_tex.y /=	tex_h;			
			t.m_vertices[render_vertex_pos].m_col	=	triangle.m_color;
			
			render_vertex_pos++;
			
			t.m_vertices[render_vertex_pos].m_pos	=	triangle.m_p2;
			t.m_vertices[render_vertex_pos].m_tex	=	triangle.m_t2;
			t.m_vertices[render_vertex_pos].m_tex.x /=	tex_w;
			t.m_vertices[render_vertex_pos].m_tex.y /=	tex_h;			
			t.m_vertices[render_vertex_pos].m_col	=	triangle.m_color;
			
			render_vertex_pos++;
			
			t.m_vertices[render_vertex_pos].m_pos	=	triangle.m_p3;
			t.m_vertices[render_vertex_pos].m_tex	=	triangle.m_t3;
			t.m_vertices[render_vertex_pos].m_tex.x /=	tex_w;
			t.m_vertices[render_vertex_pos].m_tex.y /=	tex_h;
			t.m_vertices[render_vertex_pos].m_col	=	triangle.m_color;
			
			render_vertex_pos++;
			
			// Calculate the normal for the previous 3 vertices:
			
			{
				/// Get the direction of the 1st line in the triangle
				
				Vec3f line1dir = t.m_vertices[render_vertex_pos-1].m_pos - t.m_vertices[render_vertex_pos-2].m_pos;
				
				/// Get the direction of the 2nd line in the triangle
				
				Vec3f line2dir = t.m_vertices[render_vertex_pos-2].m_pos - t.m_vertices[render_vertex_pos-3].m_pos;
				
				// Get the cross product of the two: this is the normal
				
				Vec3f cross = line2dir.Cross(line1dir); cross.Normalize();
				
				// Save the normal:
				
				t.m_vertices[render_vertex_pos-1].m_nor = cross;
				t.m_vertices[render_vertex_pos-2].m_nor = cross;
				t.m_vertices[render_vertex_pos-3].m_nor = cross;
			}		
			
			// Done with this triangle
			
			tris.pop_front();
		}
		
		// Push this back into the render list array
		
		m_triangle_lists.push_back(t);
		
		// Move on:
		
		i++;		
	}
}

//=====================================================================================================================
/// @brief		Draws the level:
/// @param		camera - camera viewpoint to draw from
//=====================================================================================================================

void DoomLevel::Draw( Camera3D * camera )
{
	// Get the graphics device off the game:
	
	IDirect3DDevice9 * device = GameCore::GetSingleton()->GetDisplay()->GetDevice();
	
	// Disable this:
	
	device->SetRenderState( D3DRS_CULLMODE			, D3DCULL_CW	);
	device->SetRenderState( D3DRS_ALPHATESTENABLE	, FALSE			);
	device->SetRenderState( D3DRS_ALPHABLENDENABLE	, FALSE			);

	// Only if we have valid resources:
	
	if ( m_valid_resources )
	{	
		// Set the vertex format:
		
		m_vf.Apply();
		
		// Set the vertex and pixel shaders used:
		
		m_vs.Apply();
		m_ps.Apply();
		
		// Get the transform for the camera:

		Matrix4f t = camera->GetViewProjectionMatrix();		
		
		// Set:
		
		m_u_transform.SetMatrix(t);
		
		// Set the position of the camera:
		
		m_u_camera_position.SetVec3f( camera->GetPosition() );
		
		// Set fog distance and color:
		
		if ( GameSession::GetSingleton().IsConnected() && GameSession::GetSingleton().IsServer() )
		{
			m_u_fog_distance.SetFloat(20000.0f); m_u_fog_color.SetVec4f(Vec4f(0.025f,0.025f,0.05f));
		}
		else
		{
			m_u_fog_distance.SetFloat(2000.0f); m_u_fog_color.SetVec4f(Vec4f(0.025f,0.025f,0.05f));
		}
		
		// Draw all the triangle lists:
		
		for ( unsigned i = 0 ; i < m_triangle_lists.size() ; ++i )
		{
			// Get this render tris:
			
			TriangleList & l = m_triangle_lists[i];
			
			// Apply it's texture if it is valid:
			
			{
				// Bind the texture if valid:
			
				if ( l.m_texture->IsValid() ) l.m_texture->Apply(0);
				
				// Draw the trianlges:			
				
				device->DrawPrimitiveUP( D3DPT_TRIANGLELIST , l.m_num_triangles , l.m_vertices , m_vf.GetVertexSize() );
			}
		}
	}
	
	// Renable this:
	
	device->SetRenderState( D3DRS_CULLMODE			, D3DCULL_NONE	);
	device->SetRenderState( D3DRS_ALPHATESTENABLE	, TRUE			);
	device->SetRenderState( D3DRS_ALPHABLENDENABLE	, TRUE			);
}


//=====================================================================================================================
/// @brief		Returns the current subsector a given 2D point (representing X and Z coords) is in.
/// @param		p - point to check
/// @returns	The subsector the point is in or null if it is outside the map bounds
/// @pre		We must have nodes in the tree
//=====================================================================================================================

const DoomGLSubsectorV2 * DoomLevel::GetSubsector( const Vec2f & p ) const
{
	// Make sure ok:

	ENSURE( m_gl_nodes , "We must have nodes in the tree" )

	// Get the root tree node
	
	const DoomPPNode * n = (const DoomPPNode*)(&(GetRootNode()));

	// Store the output subsector here:

	const DoomGLSubsectorV2 * subsector = 0;

	// Keep going until we have reached the end:

	while ( n )
	{
		// See what side of the plane the current point is on:

		if ( n->m_minus_n_dot_p + n->m_normal.Dot(p) >= 0 )
		{ 
			// On the right side of the plane: see if the right child is a seg or another node

			if ( n->m_right_child == 0 )
			{
				// Set the subsector:
				
				subsector = n->m_right_subsector;
				
				// We are done:

				n = 0;
			}
			else
			{
				// Continue traversing down the tree

				n = n->m_right_child;
			}
		}
		else
		{
			// On the left side of the plane: see if the left child is a seg or another node

			if ( n->m_left_child == 0 )
			{
				// Set the subsector:
				
				subsector = n->m_left_subsector;

				// We are done:

				n = 0;
			}
			else
			{
				// Continue traversing down the tree

				n = n->m_left_child;
			}
		}	
	}

	// Return the found subsector

	return subsector;
}

