/*********************************************************************//**
*	Modul k nacitani BSP levelu.
*      Zde je implementovano nacitani levelu formatu BSP. Cela procedura 
*	se sklada z mnoha procesu, kde na zacatku se veskera data nactou 
*	do pameti a pote je treba tyto data analyzovat a diky nim vytvorit
*	potrebne struktury a informace pro pozdejsi pouziti pri vykreslovani
*	nebo detekci kolizi.
*                                                                        
*
*	author: Michal Jirous
*	date: 4.2.2009
*	file: level_loader.cpp
**********************************************************************/  


#include "level_loader.h"
#include "error_handler.h"
#include "level_decals.h"
#include "textLibrary.h"
#include <fstream>

using namespace std;

CLevelLoader levelLoader;

CLevelLoader::CLevelLoader()
{
	clear();
}

void CLevelLoader::clear()
{
	m_LevelData.leaves_count = 0; 
	m_LevelData.faces_count = 0;
	m_bLevelLoaded = false;
	m_LevelData.current_session = 0;
	m_LevelData.elements_count = 0;
}

void CLevelLoader::destroy()
{
	for( int i = 0; i < m_LevelData.leaves_count; ++i )
		m_LevelData.leaves[i].destroy();

	for( size_t i = 0; i < m_LevelData.faces_count; ++i )
	{
		m_LevelData.faces[i].destroy();
	}

	m_Entities.decompileAll();
	m_Entities.unloadEntities();

	clear();

	decalSystem.destroy();
}


enum
{
	DATA_LOAD = 0,
	FACES,
	ENTITIES,

	TOTAL_PARTS


};

const float PERCENTAGE_START[TOTAL_PARTS] = { 0.0f, 0.2f, 0.9f }; 

#define FACE_LOAD_UPDATE_COUNT 200


#include "game_loadingscreen.h"
#include "sys_config.h"
using namespace loadingScreen;
/** @param bsp30file Cesta k souboru s levelem.
*	@return True, pokud se soubor nacetl, jinak false.
*/
bool CLevelLoader::loadLevel( std::string bsp30file )
{
	CONSOLE_PRINT_LN("\nLevel loading.");
	bsp30file = MAPS_DIRECTORY + bsp30file;
	destroy();

	/*==========================================
	*	1. Otevreni souboru
	*=========================================*/
	
	CONSOLE_PRINT_LN("\tOpening map file: '" << bsp30file << "'" );
	
	IFSTREAM_OPEN( fin, bsp30file, ios::in | ios::binary );
	
	if( !fin.is_open() )
	{
		bsp30file = bsp30file + EXTENSION;
		fin.clear();
		fin.open( bsp30file.c_str(), ios::in | ios::binary );
		if( !fin.is_open() )
		{
			ERR_FILENOTFOUND(bsp30file);
			return false;
		}
	}
	
	/*==========================================
	*	2. Nacteni hlavicky
	*=========================================*/
	CONSOLE_PRINT_LN("\tReading file header.");
	fin.read( (char*)&m_LevelData.header, sizeof(dheader_t) );
	
	if( m_LevelData.header.version != BSPVERSION )
	{
		if( m_LevelData.header.version < BSPVERSION )
			PRINT_ERROR("\tError: BSP version older than 30 " << m_LevelData.header.version << " provided");
		else
			PRINT_ERROR("\tError: BSP version newer than 30 " << m_LevelData.header.version << " provided");

		return false;
	}
	
	/*==========================================
	*	3. Nacteni Dat lumps krome vertexu
	*=========================================*/
	
	CONSOLE_PRINT_LN("\tReading file data.");
	//Nacteme text definujici entity
	fin.seekg( m_LevelData.header.lumps[LUMP_ENTITIES].fileofs, SEEK_SET );
	fin.read( m_LevelData.entitystring, m_LevelData.header.lumps[LUMP_ENTITIES].filelen );
	
	//Nacteme roviny
	fin.seekg( m_LevelData.header.lumps[LUMP_PLANES].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.planes, m_LevelData.header.lumps[LUMP_PLANES].filelen );
	m_LevelData.planes_count = m_LevelData.header.lumps[LUMP_PLANES].filelen / sizeof( dplane_t );

	//Nacteme data textur
	fin.seekg( m_LevelData.header.lumps[LUMP_TEXTURES].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.textures_data, m_LevelData.header.lumps[LUMP_TEXTURES].filelen );

	//Nacteme zakomprimovana data VIS
	fin.seekg( m_LevelData.header.lumps[LUMP_VISIBILITY].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.vis_data_compressed, m_LevelData.header.lumps[LUMP_VISIBILITY].filelen );

	//Nacteme uzly BSP stromu
	fin.seekg( m_LevelData.header.lumps[LUMP_NODES].fileofs, SEEK_SET );
	char*position = (char*)&m_LevelData.nodes[MAX_MAP_NODES];	//konec
	position -= m_LevelData.header.lumps[LUMP_NODES].filelen;	//konec - delka dat (data chceme ulozit na konec pole
	fin.read( (char*)position, m_LevelData.header.lumps[LUMP_NODES].filelen );
	m_LevelData.nodes_count = m_LevelData.header.lumps[LUMP_NODES].filelen / sizeof( dnode_t );

	//Nacteme texturovaci informace o plose
	fin.seekg( m_LevelData.header.lumps[LUMP_TEXINFO].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.textures_info, m_LevelData.header.lumps[LUMP_TEXINFO].filelen );
	m_LevelData.textures_info_count = m_LevelData.header.lumps[LUMP_TEXINFO].filelen / sizeof( texinfo_t );

	//Nacteme Faces
	fin.seekg( m_LevelData.header.lumps[LUMP_FACES].fileofs, SEEK_SET );
	position = (char*)&m_LevelData.faces[MAX_MAP_FACES];	//konec
	position -= m_LevelData.header.lumps[LUMP_FACES].filelen;	//konec - delka dat (data chceme ulozit na konec pole
	fin.read( (char*)position, m_LevelData.header.lumps[LUMP_FACES].filelen );
	m_LevelData.faces_count = m_LevelData.header.lumps[LUMP_FACES].filelen / sizeof(dface_t);
	
	//Nacteme svetelne informace (LIGHTMAPS)
	fin.seekg( m_LevelData.header.lumps[LUMP_LIGHTING].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.light_data,  m_LevelData.header.lumps[LUMP_LIGHTING].filelen );

	//Nacteme clip nodes
	fin.seekg(  m_LevelData.header.lumps[LUMP_CLIPNODES].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.clip_nodes,  m_LevelData.header.lumps[LUMP_CLIPNODES].filelen );
	m_LevelData.clip_nodes_count =  m_LevelData.header.lumps[LUMP_CLIPNODES].filelen / sizeof( dclipnode_t );

	//Nacteme Leafs (listy BSP stromu)
	fin.seekg( m_LevelData.header.lumps[LUMP_LEAFS].fileofs, SEEK_SET );
	position = (char*)&m_LevelData.leaves[MAX_MAP_LEAFS];	//konec
	position -= m_LevelData.header.lumps[LUMP_LEAFS].filelen;	//konec - delka dat (data chceme ulozit na konec pole
	fin.read( position, m_LevelData.header.lumps[LUMP_LEAFS].filelen );
	m_LevelData.leaves_count = m_LevelData.header.lumps[LUMP_LEAFS].filelen / sizeof( dleaf_t );
	
	//Nacteme Mark surfaces (cisla ploch v listu)
	fin.seekg( m_LevelData.header.lumps[LUMP_MARKSURFACES].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.marked_faces, m_LevelData.header.lumps[LUMP_MARKSURFACES].filelen );
	m_LevelData.marked_faces_count = m_LevelData.header.lumps[LUMP_MARKSURFACES].filelen / sizeof(unsigned short);

	//Nacteme edges
	fin.seekg( m_LevelData.header.lumps[LUMP_EDGES].fileofs, SEEK_SET );
	position = (char*)&m_LevelData.edges[MAX_MAP_EDGES];	//konec
	position -= m_LevelData.header.lumps[LUMP_EDGES].filelen;	//konec - delka dat (data chceme ulozit na konec pole
	fin.read( position, m_LevelData.header.lumps[LUMP_EDGES].filelen );
	m_LevelData.edges_count = m_LevelData.header.lumps[LUMP_EDGES].filelen / sizeof( dedge_t );

	//Nacteme surfedges (cisla hran v Facu)
	fin.seekg( m_LevelData.header.lumps[LUMP_SURFEDGES].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.surf_edges_pointer, m_LevelData.header.lumps[LUMP_SURFEDGES].filelen );
	m_LevelData.surf_edges_count = m_LevelData.header.lumps[LUMP_SURFEDGES].filelen / sizeof(int);
	m_LevelData.surf_edges = (int*)m_LevelData.surf_edges_pointer;

	//Nacteme modely
	fin.seekg( m_LevelData.header.lumps[LUMP_MODELS].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.models, m_LevelData.header.lumps[LUMP_MODELS].filelen );
	m_LevelData.models_count = m_LevelData.header.lumps[LUMP_MODELS].filelen / sizeof( dmodel_t );

	loading_screen.setProgress( PERCENTAGE_START[DATA_LOAD] );
	loading_screen.loading_func();
	
	/*==========================================
	*	4. Pripravime listy bsp stromu
	*		-prekopirovani dat
	*		-zjisteni viditelnosti ostanich leafs
	*=========================================*/

	CONSOLE_PRINT_LN("\tCreating bsp tree leafs.");
	if( !processLeafs() )
	{
		destroy();
		return false;
	}

	

	/*==========================================
	*	5. Nacteme vertexy do pole s visibility,
	*		ktere uz ted neni potreba
	*=========================================*/
	
	CONSOLE_PRINT_LN("\tReading vertices.");
	fin.seekg( m_LevelData.header.lumps[LUMP_VERTEXES].fileofs, SEEK_SET );
	fin.read( (char*)m_LevelData.vis_data_compressed, m_LevelData.header.lumps[LUMP_VERTEXES].filelen );
	m_TmpVerticesCount = m_LevelData.header.lumps[LUMP_VERTEXES].filelen / sizeof(dvertex_t);
	

	/*==========================================
	*	6. Prekopirovat a upravit edges
	*=========================================*/

	CONSOLE_PRINT_LN("\tProcessing edges.");
	processEdges();

	/*==========================================
	*	7. Pripravime faces
	*		-vytvoreni vertexu
	*		-vytvoreni texturovacich souradnic
	*		-nastaveni textury
	*		-nastaveni lightmap
	*=========================================*/

	CONSOLE_PRINT_LN("\tCreating faces.");
	m_LevelData.elements_count = 0;			//zatim zadne vertexy
	processFaces();	//zde hodne casu


	/*==========================================
	*	8. V surfedges nahradit indexy do edges
	*		ukazately na edges (nevim k cemu
	*		budu pozdeji potrebovat smer hrany)
	*=========================================*/

	CONSOLE_PRINT_LN("\tTransforming surfedges.");
	for( size_t i = 0; i < m_LevelData.surf_edges_count; ++i )
	{
		m_LevelData.surf_edges_pointer[i] = &m_LevelData.edges[ abs( m_LevelData.surf_edges[i] ) ];
	}

	/*==========================================
	*	9. Prekopirovat a upravit nodes
	*=========================================*/

	CONSOLE_PRINT_LN("\tProcessing bsp tree nodes.");
	processNodes();

	/*==========================================
	*	10. Opacne poradi vzdalenosti rovin
	*=========================================*/
	
	CONSOLE_PRINT_LN("\tConverting planes.");
	for( size_t i = 0; i < m_LevelData.planes_count; ++i )
		m_LevelData.planes[i].m_fDistance = -m_LevelData.planes[i].m_fDistance;


	/*============================================
	*	11. Nacteme texty
	*		MUSI BYT PRED ENTITAMI
	*===========================================*/
	CONSOLE_PRINT_LN("\tReading language texts.");
	if( m_LevelData.header.lumps[LUMP_TEXTS].filelen != 0 )
	{
		fin.seekg( m_LevelData.header.lumps[LUMP_TEXTS].fileofs, SEEK_SET );
		char *buffer = new char[m_LevelData.header.lumps[LUMP_TEXTS].filelen];
		fin.read( buffer, m_LevelData.header.lumps[LUMP_TEXTS].filelen );
		textLibrary::setLevelTexts( buffer );
		delete [] buffer;
	}



	/*============================================
	*	12. Analyza textu entit a jejich vytvoreni
	*===========================================*/
	CONSOLE_PRINT_LN("\tCreating entities and loading global game settings.");
	m_Entities.loadEntities( m_LevelData.entitystring );
	
	CONSOLE_PRINT("\tCompiling entities...");
	m_Entities.compileAll();
	CONSOLE_PRINT_LN("done...");

	loading_screen.setProgress( 1.0f );
	loading_screen.loading_func();

	m_bLevelLoaded = true;
	fin.close();
	CONSOLE_PRINT_LN("\tLevel successfully loaded.");
	return true;
}


unsigned short visibleLeafs[MAX_MAP_LEAFS];
unsigned short visibleLeafs_count = 0;

/* Funkce prochazi vsechny listy BSP stromu a pokud obsahuji nejake
*	informace o viditelnosti ostatnich uzlu, tak se provede dekomprese
*	techto informaci.
*/
bool CLevelLoader::processLeafs()
{
	dleaf_t *leaf_in =(dleaf_t*)((char*)&m_LevelData.leaves[MAX_MAP_LEAFS] - m_LevelData.header.lumps[LUMP_LEAFS].filelen);
	BSPLeaf *leaf_out = m_LevelData.leaves;

	if( m_LevelData.leaves_count == 0 )
		return false;


	//Leaf 0 je nevim k cemu, ale proste se nebere

	for( int i = 0; i < m_LevelData.leaves_count; ++i )
	{
		//zaloha jedineho potrebneho parametru, ktery v leaf_out neni
		int vis_pos = leaf_in[i].visofs;	//index do pole visibility
		leaf_out[i].create( leaf_in[i] );	//nektera data jen zkopirujeme (atributy s VIS se nenastavuji)
		
		/*==========================================
		*	Nyni je treba dekomprimovat Visibility
		*		data a ulozit je cilovymu listu
		*	=( zjisteni cisel listu, ktere muze list
		*	videt)
		*=========================================*/

		visibleLeafs_count = 0;	//reset

		if( vis_pos == -1 )	//-1 znamena, ze zadna VIS data nema
			leaf_out[i].visible_leafs = NULL;
		else
		{
			const byte* vis = m_LevelData.vis_data_compressed;	//data visibility
			for( int leaf = 1; leaf <= m_LevelData.models[0].visleafs; ++vis_pos )
			{
				if( vis[vis_pos] == 0 )	//znamena, ze tam jsou zakomprimovany nuly a tak muzeme preskocit nektere leafs
				{
					++vis_pos;	//na dalsi pozici je pocet leafs, ktere lze preskocit
					leaf += (vis[vis_pos] << 3);	//cislo rika pocet BYTU (1 bit = 1 leaf) = pocet preskocenych leafs = vis[vis_pos]*8
				}
				else	//pokud tam neni 0, tak to znamena, ze sou tam nejake jednicky
				{
					for( byte bit = 1; bit; bit <<= 1, ++leaf )	//a musime zjistit kde, proto prochazime vsech 8 bitu 
					{
						if( vis[vis_pos] & bit )	//kontrolujeme bit po bitu od 1 do 128
							visibleLeafs[visibleLeafs_count++] = leaf;	//pokud nalezneme jednicku, tak tento list je videt a tak jeho cislo pridame do seznamu viditelnych leafs
					}
				}
			}

			//nakonec seznam zkopirujeme listu
			leaf_out[i].visible_leafs = new unsigned short[visibleLeafs_count];
			memcpy( leaf_out[i].visible_leafs, visibleLeafs, visibleLeafs_count*sizeof(unsigned short) );
		}

		leaf_out[i].visible_leafs_count = visibleLeafs_count;

		
	}
	return true;
}

/* Funkce prevadi hrany (dedge_t) na format (Edge) ve stejnem poli,
*	jelikoz je struktura Edge vetsi nez dedge_t, tak nenastanou 
*	problemy.
*/
void CLevelLoader::processEdges()
{
	dedge_t *edge_in = (dedge_t*)((char*)&m_LevelData.edges[MAX_MAP_EDGES] - m_LevelData.header.lumps[LUMP_EDGES].filelen);
	Edge *edge_out = m_LevelData.edges;

	//novy edges maji misto
	for( size_t i = 0; i < m_LevelData.edges_count; ++i )
	{
		//misto indexu do pole priradime rovnou adresu bodu
		edge_out[i].A = &((Point*)m_LevelData.vis_data_compressed)[edge_in[i].v[0]];
		edge_out[i].B = &((Point*)m_LevelData.vis_data_compressed)[edge_in[i].v[1]];
		edge_out[i].session_key = m_LevelData.current_session;
	}
}

/* Funkce provadi prevod mezi dnote_t na BSPNode
*
*/
void CLevelLoader::processNodes()
{
	dnode_t *node_in = (dnode_t*)((char*)&m_LevelData.nodes[MAX_MAP_NODES] - m_LevelData.header.lumps[LUMP_NODES].filelen);
	BSPNode *node_out = m_LevelData.nodes;

	//novy edges maji misto
	for( size_t i = 0; i < m_LevelData.nodes_count; ++i )
		node_out[i].create( node_in[i] );
}



#include "textureslib.h"
#include "game.h"

/* Pri vytvareni Facu, se provadi nastaveni textury dle jmena
*
*/
void CLevelLoader::setTexture( Face &face, int miptex_idx )
{
	int *miptexofs = ((int*)m_LevelData.textures_data);
	int offset = miptexofs[miptex_idx + 1];
	
	miptex_t &miptex = *( (miptex_t*)(&m_LevelData.textures_data[offset]));	//miptex lezi na bytu urcenem promennou offset
	
	//!!!!!!!!
	//textury v BSP ignorujeme..pozdeji pridame support
	miptex.width = 0;	//hack :P zakonceni retezce name nulou :)

	if( strcmp(miptex.name, "sky" ) == 0)
		return;
	face.pTexture = game.m_GlobalTexturesPool.Pool_applyTexture( miptex.name );
	
	if( !face.pTexture )	//textura neexistuje a nemuze byt pouzita
	{
		PRINT_ERROR("Warning: Unknown texture: " << miptex.name );
	}
}




//hranicni AABB box texturovacich souradnic
enum texture_coords_bounds
{
	MIN_U = 0,
	MAX_U,
	MIN_V,
	MAX_V
};

/* Funkce provadi vytvareni Facu pro ucely teto aplikace,
*	coz obnasi vytvareni vertex arrays, texture coodinates arrays
*	nastaveni textury, lightmapy.
*/
bool CLevelLoader::processFaces()
{
	dface_t *face_in = (dface_t*)((char*)&m_LevelData.faces[MAX_MAP_FACES] - m_LevelData.header.lumps[LUMP_FACES].filelen);
	Face *face_out = m_LevelData.faces;

	float percentage = PERCENTAGE_START[FACES];
	float interval = PERCENTAGE_START[FACES+1] - PERCENTAGE_START[FACES];

	int faces_done = 0;

	for( size_t i = 0; i < m_LevelData.faces_count; ++i )
	{
		if( i - faces_done > FACE_LOAD_UPDATE_COUNT )
		{
			faces_done+=FACE_LOAD_UPDATE_COUNT;
			percentage = interval * (float)i / (float)m_LevelData.faces_count + PERCENTAGE_START[FACES];
			loading_screen.setProgress( percentage );
			loading_screen.loading_func();
		}


	

		//backup dat, ktere 'out' nema a jsou potreba k nastaveni facu
		const short texture_info_index = face_in[i].texinfo;
		const int lightmap_offset = face_in[i].lightofs;

		//zkopirovani spolecnych dat
		face_out[i].create( face_in[i] );
		face_out[i].session_key = m_LevelData.current_session;

		/*==========================================
		*	1. Ziskame texture info, diky cemuz
		*		lze spocitat texturovaci souradnice
		*=========================================*/

		texinfo_t &texture_info = m_LevelData.textures_info[texture_info_index];
		
		face_out[i].flags |= texture_info.flags;
		
		
		/*==========================================
		*	2. Ziskame miptex objekt, kde nalezneme
		*		jmeno textury a z knihovny textur
		*		ziskame objekt textury.
		*=========================================*/

		setTexture( face_out[i], texture_info.miptex );

		/*==========================================
		*	3. Ziskame vsechny vrcholy a ulozime je
		*		do pole vertexu.
		*	Ziskame i hranicni hodnoty texturovacich
		*	souradnic, ktere nanm daji velikost
		*	lightmapy
		*=========================================*/

		float tex_UV_bounds[4];
		createVerticesTexCoords( face_out[i], tex_UV_bounds, texture_info );
			
		/*==========================================
		*	4. Nacteme lightmapy jako textury
		*=========================================*/
		
		if( (face_out[i].flags & FACE_NO_LIGHTMAP) == 0 )
		{
			//musime zjistit velikost lightmapy
			const unsigned int lightmap_width = (int)(ceil( tex_UV_bounds[MAX_U]/16.0f ) - floor( tex_UV_bounds[MIN_U]/16.0f ) + 1.0f);
			const unsigned int lightmap_height = (int)(ceil( tex_UV_bounds[MAX_V]/16.0f ) - floor( tex_UV_bounds[MIN_V]/16.0f ) + 1.0f);

			const int size = lightmap_width * lightmap_height * 3;	//RGB lightmap
			unsigned char* luxels;
			TextureElement element;

			element.image.width = lightmap_width;
			element.image.height = lightmap_height;
			element.image.bpp = 24;	//RGB

			int styles_count = 0;
			for( ; styles_count < MAXLIGHTMAPS; ++styles_count )
			{
				if( face_out[i].styles[styles_count] == (unsigned char)-1 )
					break;
				
				luxels = &m_LevelData.light_data[ lightmap_offset + styles_count*size ];
				if( textureLibrary.createTexture( &element, luxels ) )
				{
					face_out[i].lightmaps[styles_count] = element.texture_id;
				}
				element.shared_index = 0;	//zadny sdileni
				element.texture_id = 0;
			}

			if( styles_count > 1 )	//vice nez jeden styl = pouziti blendingu pro multitexturing
				face_out[i].flags |= FACE_USES_STYLES;

			/*==========================================
			*	5. Spocitame texturovaci souradnice
			*		lightmapy
			*=========================================*/

			for( unsigned short j = 0; j <  face_out[i].elements_count; ++j )
			{
				Point *vertex = &m_LevelData.vertices_array[ face_out[i].firstvertex + j];

				float U = (vertex->Dot( (*(Point*)texture_info.vecs[0]) )) + texture_info.vecs[0][3];
				float V = (vertex->Dot( (*(Point*)texture_info.vecs[1]) )) + texture_info.vecs[1][3];


				float mid_poly_s = ( tex_UV_bounds[MIN_U] + tex_UV_bounds[MAX_U] ) / 2.0f;
                float mid_poly_t = ( tex_UV_bounds[MIN_V] + tex_UV_bounds[MAX_V] ) / 2.0f;
                float mid_tex_s = (float)lightmap_width / 2.0f;
                float mid_tex_t = (float)lightmap_height / 2.0f;
                U = mid_tex_s + (U - mid_poly_s) / 16.0f;
                V = mid_tex_t + (V - mid_poly_t) / 16.0f;
                U /= (float) lightmap_width;
                V /= (float) lightmap_height;
 
				m_LevelData.lightmap_coords_array[ face_out[i].firstvertex + j].u = U;
				m_LevelData.lightmap_coords_array[ face_out[i].firstvertex + j].v = V;
			}

			/*==========================================
			*	6. Provedeme vypocet prumerne hodnoty
			*	svetla facu (pro pozdejsi osvetleni
			*	objektu, ktere se nachazeji na facu
			*	facu
			*=========================================*/

			//pouzijeme prostredni bod lightmapy
			unsigned int halfArea = lightmap_width * lightmap_height;
			halfArea >>= 1;
			halfArea *= 3;

			//pro vsechny slozky barvy RGB
			for( int j = 0; j < 3; ++j )
				face_out[i].average_face_color[j] = (GLfloat)m_LevelData.light_data[ lightmap_offset + halfArea + j ] / 255.0f;
		}

		/*==========================================
		*	7. Nakonec otestujeme, jestli se neda
		*	pouzit bounding box misto klasicke
		*	kolize
		*=========================================*/

		determineFaceSimplicity( face_out[i] );
	}



	percentage = interval + PERCENTAGE_START[FACES];
	loading_screen.setProgress( percentage );
	loading_screen.loading_func();

	return true;
}

/* Funkce zjisti, zda ma face jednoduchy tvar (obdelnik osove natoceny),
*	protoze pokud ano, tak lze pouzit k detekci kolizi AABB vs. AABB
*/

void CLevelLoader::determineFaceSimplicity( Face &face )
{
	bool canBeSimple = true;

	Plane &plane = m_LevelData.planes[ face.planenum ];
	int notnull = 0;

	//aby to bylo mozne, tak vektor roviny musi byt rovnobezny s nekterou osou
	for( int j = 0; j < 3; ++j )
	{
		if( plane.m_vecNormal[j] != 0.0f )
			notnull++;
	}

	if( notnull > 1 )	//jakmile jsou 2 nenulove hodnoty souradnic, tak nemuze byt jednoduchy
	{
		canBeSimple = false;
	}
	else
	{
		//pote je treba otestovat u vsech hran polygonu, jestli jejich body maji rozdilnou vzdy pouze jednu hodnotu souradnice
		//cimz tvori hranaty objekt rovny bounding boxu
		for( unsigned short j = 0, n = face.elements_count-1; j <  face.elements_count; n=j,++j )	//musime projit vsechny body
		{
			Point &vertex = m_LevelData.vertices_array[ face.firstvertex + j];
			Point &vertexB = m_LevelData.vertices_array[ face.firstvertex + n];
			int diff = 0;
			for( int k = 0; k < 3; ++k )
			{
				if( compareFloats( vertex[k], vertexB[k], FLOAT_COMPARE_ACCURACY_HIGH )	 )
				{
					diff++;
				}
			
			}
			if( diff > 1 )
			{
				canBeSimple = false;
				break;
			}
		
		}
	}

	if( canBeSimple )
		face.flags |= FACE_SIMPLE_COLLISION;


}

/* Funkce provadi vypocet fog coordinates array
*
*/
void CLevelLoader::processFogCoords()
{
	dface_t *face_in = (dface_t*)((char*)&m_LevelData.faces[MAX_MAP_FACES] - m_LevelData.header.lumps[LUMP_FACES].filelen);
	dedge_t *edge_in = (dedge_t*)((char*)&m_LevelData.edges[MAX_MAP_EDGES] - m_LevelData.header.lumps[LUMP_EDGES].filelen);
	
	m_LevelData.elements_count = 0;
	
	/*==========================================
	*	1. Musime projit vsechny polygonu
	*	ve stejnem poradi, jako kdyz se
	*	polygonu vytvari
	*=========================================*/
	for( size_t i = 0; i < m_LevelData.faces_count; ++i )
	{
		/*==========================================
		*	2. U kazdeho polygonu musime projit vsechny
		*	hrany.
		*=========================================*/
		for( int index = 0; index < face_in[i].numedges; ++index )
		{
			//zaporne edge_idx znaci opacny smer hrany
			int edge_idx = m_LevelData.surf_edges[ face_in[i].firstedge + index ];
			
		
			/*==========================================
			*	3. Vezmeme pouze prvni vrchol
			*=========================================*/

			int edge_abs_id;
			unsigned short fog_coord_Idx = 0;
			if( edge_idx < 0 )
			{
				edge_abs_id = -edge_idx;
				fog_coord_Idx = edge_in[edge_abs_id].v[1];	//index vertexu
			}
			else
			{
				edge_abs_id = edge_idx;
				fog_coord_Idx = edge_in[edge_abs_id].v[0];	//index vertexu
			}

			m_LevelData.fog_coords_array[m_LevelData.elements_count] = ((FogCoord_t*)&m_LevelData.clip_nodes)[fog_coord_Idx];
			m_LevelData.elements_count++;
		}
	}
	m_LevelData.elements_count = 0;
}

/* Funkce je soucasti vytvareni Facu
*	provadi vypocet vertex array, texturovacich souradnic (texture coordinates array)
*	a dale vypocet informaci potrebnych k pouziti lightmapy.
*
*/
void CLevelLoader::createVerticesTexCoords( Face &face, float* tex_bounds, const texinfo_t &texture_info  )
{
	float U,V;

	face.firstvertex = m_LevelData.elements_count;
	
	bool use_fog = false;

	for( int index = 0; index < face.elements_count; ++index, ++m_LevelData.elements_count )
	{
		//zaporne edge_idx znaci opacny smer hrany
		int edge_idx = m_LevelData.surf_edges[ face.first_surfedge + index ];
		
	
		/*==========================================
		*	1. Ziskame vrchol a zkopirujeme ho
		*=========================================*/

		Point *vertex;
		int edge_abs_id;

		if( edge_idx < 0 )
		{
			edge_abs_id = -edge_idx;
			vertex = m_LevelData.edges[edge_abs_id].B;
		}
		else
		{
			edge_abs_id = edge_idx;
			vertex = m_LevelData.edges[edge_abs_id].A;
		}


		m_LevelData.vertices_array[m_LevelData.elements_count] = *vertex;
		if( index == 0 )
			face.m_Bounds.setBasicValues( *vertex );
		else
			face.m_Bounds.updateData( *vertex );
		
		/*==========================================
		*	2. Zde je treba upravit hranu tak, aby
		*		ukazovala do pole vertexu a ne
		*		do toho docasneho
		*=========================================*/
		if( m_LevelData.edges[ edge_abs_id ].session_key == 0 )
		{
			if( index == face.elements_count - 1 )	//posledni hrana
			{
				if( edge_idx < 0 )
				{
					m_LevelData.edges[edge_abs_id].B = &m_LevelData.vertices_array[m_LevelData.elements_count];
					m_LevelData.edges[edge_abs_id].A = &m_LevelData.vertices_array[face.firstvertex];
				}
				else
				{
					m_LevelData.edges[edge_abs_id].A = &m_LevelData.vertices_array[m_LevelData.elements_count];
					m_LevelData.edges[edge_abs_id].B = &m_LevelData.vertices_array[face.firstvertex];
				}
			}
			else
			{
				if( edge_idx < 0 )
				{
					m_LevelData.edges[edge_abs_id].B = &m_LevelData.vertices_array[m_LevelData.elements_count];
					m_LevelData.edges[edge_abs_id].A = &m_LevelData.vertices_array[m_LevelData.elements_count+1];
				}
				else
				{
					m_LevelData.edges[edge_abs_id].A = &m_LevelData.vertices_array[m_LevelData.elements_count];
					m_LevelData.edges[edge_abs_id].B = &m_LevelData.vertices_array[m_LevelData.elements_count+1];
				}
			
			}

			m_LevelData.edges[ edge_abs_id ].session_key = 1;
		}

		

		if( !face.pTexture || !face.pTexture->m_pTextureElement )
			continue;

		/*==========================================
		*	3. Spocitame texturovaci souradnice
		*	+spocitame max/min z U,V souradnic
		*=========================================*/

		U = (vertex->Dot( *(Point*)texture_info.vecs[0] )) + texture_info.vecs[0][3];
		V = (vertex->Dot( (*(Point*)texture_info.vecs[1]) )) + texture_info.vecs[1][3];	//toto je pro vypocet lightmapy jenom
		float final_V = (vertex->Dot( -(*(Point*)texture_info.vecs[1]) )) - texture_info.vecs[1][3];	//no...musime texturu horizontalne prevratit :)

		if( index == 0 )
		{
			tex_bounds[MIN_U] = U; 
			tex_bounds[MAX_U] = U;
			tex_bounds[MIN_V] = V;
			tex_bounds[MAX_V] = V;
		}
		else
		{
			tex_bounds[MIN_U] = min( tex_bounds[MIN_U], U );
			tex_bounds[MAX_U] = max( tex_bounds[MAX_U], U );
			tex_bounds[MIN_V] = min( tex_bounds[MIN_V], V );
			tex_bounds[MAX_V] = max( tex_bounds[MAX_V], V );
		}

		m_LevelData.texture_coords_array[m_LevelData.elements_count].u = U / face.pTexture->m_pTextureElement->image.width;
		m_LevelData.texture_coords_array[m_LevelData.elements_count].v = final_V / face.pTexture->m_pTextureElement->image.height;


		if( m_LevelData.fog_coords_array[m_LevelData.elements_count] > 0.0f )
			use_fog = true;
	}
	
	if( use_fog )
		face.flags |= FACE_FOG_COORDS;
}








/** @param model Cislo modelu, pro ktery se ma operace provest.
*	@param func Funkce, ktera se ma pro kazdy face zavolat.
*	@param initiator Uzivatelska data, ktera budou poslana jako parametr funkci func.
*/
Face *CLevelLoader::forEachFaceDo( size_t model, bool (*func)( Face &face, void* initiator ), void *initiator )
{
	int startNode = m_LevelData.models[model].headnode[0];
	m_LevelData.current_session++;
	return BSPTreeFacePassing( startNode, func, initiator );
}




/*	Funkce prochazi cely BSP strom a pro kazdy face provede operaci v parametru func.
*
*/

Face *CLevelLoader::BSPTreeFacePassing( int node,  bool (*func)( Face &face, void* initiator ), void *initiator )
{
	if( node < 0 )	//leaf
	{
		BSPLeaf &leaf =m_LevelData.leaves[ -(node+1) ];
		
		//vsechny faces
		for( unsigned short j = 0; j < leaf.mark_surfaces_count; ++j )
		{
			Face &face = m_LevelData.faces[ m_LevelData.marked_faces[ leaf.first_mark_surface + j] ];
		
			//nechceme face vicekrat
			if( face.session_key == m_LevelData.current_session )
				continue;

			face.session_key = m_LevelData.current_session;

			if( func( face, initiator ) )
				return &face;
		}
		return NULL;
	}

	BSPNode &nodeT = m_LevelData.nodes[node];
	Face *f = BSPTreeFacePassing( nodeT.children[0], func, initiator );	//prvni potomek
	if( f )
		return f;
	return BSPTreeFacePassing( nodeT.children[1], func, initiator );	//druhy potomek
}



