コード例 #1
0
void CGameGraphBuilder::save_cross_table	(const float &start, const float &amount)
{
	Progress							(start);

	Msg									("Saving cross table");

//	CTimer								timer;
//	timer.Start							();

	CMemoryWriter						tMemoryStream;
	CGameLevelCrossTable::CHeader		tCrossTableHeader;
	
	tCrossTableHeader.dwVersion			= XRAI_CURRENT_VERSION;
	tCrossTableHeader.dwNodeCount		= level_graph().header().vertex_count();
	tCrossTableHeader.dwGraphPointCount = graph().header().vertex_count();
	tCrossTableHeader.m_level_guid		= level_graph().header().guid();
	tCrossTableHeader.m_game_guid		= m_graph_guid;
	
	tMemoryStream.open_chunk			(CROSS_TABLE_CHUNK_VERSION);
	tMemoryStream.w						(&tCrossTableHeader,sizeof(tCrossTableHeader));
	tMemoryStream.close_chunk			();
	
	tMemoryStream.open_chunk			(CROSS_TABLE_CHUNK_DATA);

	for (int i=0, n=level_graph().header().vertex_count(); i<n; i++) {
		CGameLevelCrossTable::CCell	tCrossTableCell;
		tCrossTableCell.tGraphIndex = (GameGraph::_GRAPH_ID)m_results[i];
		VERIFY						(graph().header().vertex_count() > tCrossTableCell.tGraphIndex);
		tCrossTableCell.fDistance	= float(m_distances[tCrossTableCell.tGraphIndex][i])*level_graph().header().cell_size();
		tMemoryStream.w				(&tCrossTableCell,sizeof(tCrossTableCell));
	}

	tMemoryStream.close_chunk();
	
//	Msg						("CT:SAVE : %f",timer.GetElapsed_sec());
//	Msg						("Flushing cross table");

#ifdef PRIQUEL
	tMemoryStream.save_to	(m_cross_table_name);
#else // PRIQUEL
	string_path				file_name;
	strconcat				(sizeof(file_name), file_name,*m_level_name,CROSS_TABLE_NAME_RAW);
	tMemoryStream.save_to	(file_name);
#endif // PRIQUEL
//	Msg						("CT:SAVE : %f",timer.GetElapsed_sec());

//	Msg						("Freiing cross table resources");

	m_marks.clear			();
	m_mark_stack.clear		();
	m_distances.clear		();
	m_current_fringe.clear	();
	m_next_fringe.clear		();

//	Msg						("CT:SAVE : %f",timer.GetElapsed_sec());
	Progress				(start + amount);
}
コード例 #2
0
ファイル: xr_graph_merge.cpp プロジェクト: 2asoft/xray
	void						vfSaveVertices(CMemoryWriter &tMemoryStream, u32 &dwOffset, u32 &dwPointOffset, LEVEL_POINT_STORAGE *tpLevelPoints)
	{
		GRAPH_VERTEX_IT			I = m_tpVertices.begin();
		GRAPH_VERTEX_IT			E = m_tpVertices.end();
		GameGraph::CVertex		tVertex;
		for ( ; I != E; I++) {
			tVertex.tLocalPoint		= (*I).tLocalPoint;
			tVertex.tGlobalPoint	= (*I).tGlobalPoint;
			tVertex.tNodeID			= (*I).tNodeID;
			Memory.mem_copy			(tVertex.tVertexTypes,(*I).tVertexTypes,GameGraph::LOCATION_TYPE_COUNT*sizeof(GameGraph::_LOCATION_ID));
			tVertex.tLevelID		= (*I).tLevelID;
			tVertex.dwEdgeOffset	= dwOffset;
			tVertex.dwPointOffset	= dwPointOffset;
		
			VERIFY					((*I).tNeighbourCount < (u32(1) << (8*sizeof(u8))));
			tVertex.tNeighbourCount = (u8)(*I).tNeighbourCount;

			VERIFY					((*I).tDeathPointCount < (u32(1) << (8*sizeof(u8))));
			tVertex.tDeathPointCount= (u8)(*I).tDeathPointCount;

			tMemoryStream.w			(&tVertex,sizeof(tVertex));
			dwOffset				+= (*I).tNeighbourCount*sizeof(CGameGraph::CEdge);
			dwPointOffset			+= (*I).tDeathPointCount*sizeof(CGameGraph::CLevelPoint);
		}
	};
コード例 #3
0
ファイル: xr_graph_merge.cpp プロジェクト: 2asoft/xray
	void						vfSaveEdges(CMemoryWriter &tMemoryStream)
	{
		GRAPH_VERTEX_IT			I = m_tpVertices.begin();
		GRAPH_VERTEX_IT			E = m_tpVertices.end();
		for ( ; I != E; I++)
			for (int i=0; i<(int)(*I).tNeighbourCount; i++)
				tMemoryStream.w	((*I).tpaEdges + i,sizeof(CGameGraph::CEdge));
	};
コード例 #4
0
ファイル: xrCompress.cpp プロジェクト: OLR-xray/OLR-3.0
IC	void write_file_header	(LPCSTR file_name, const u32 &crc, const u32 &ptr, const u32 &size_real, const u32 &size_compressed)
{
#ifndef PROTECTED_BUILD
	fs_desc.w_stringZ	(file_name);
	fs_desc.w_u32		(crc);	// crc
	fs_desc.w_u32		(ptr);
	fs_desc.w_u32		(size_real);
	fs_desc.w_u32		(size_compressed);
#else // PROTECTED_BUILD
	u32					file_name_size = (xr_strlen(file_name) + 0)*sizeof(char);
	u32					buffer_size = file_name_size + 4*sizeof(u32);
	VERIFY				(buffer_size <= 65535);
	u32					full_buffer_size = buffer_size + sizeof(u16);
	u8					*buffer = (u8*)_alloca(full_buffer_size);
	u8					*buffer_start = buffer;
	*(u16*)buffer		= (u16)buffer_size;
	buffer				+= sizeof(u16);

	*(u32*)buffer		= size_real;
	buffer				+= sizeof(u32);

	*(u32*)buffer		= size_compressed;
	buffer				+= sizeof(u32);

	*(u32*)buffer		= crc;
	buffer				+= sizeof(u32);

	Memory.mem_copy		(buffer,file_name,file_name_size);
	buffer				+= file_name_size;

	*(u32*)buffer		= ptr;

//	trivial_encryptor::encode	(buffer_start,full_buffer_size,buffer_start);

	fs_desc.w			(buffer_start,full_buffer_size);
#endif // PROTECTED_BUILD
}
コード例 #5
0
ファイル: xr_graph_merge.cpp プロジェクト: 2asoft/xray
								CLevelGameGraph	(
									LPCSTR graph_file_name,
									LPCSTR raw_cross_table_file_name,
									CGameGraph::SLevel *tLevel,
									LPCSTR S,
									u32 dwOffset,
									u32 dwLevelID,
									CInifile *Ini
								)
	{
		m_tLevel				= *tLevel;
		m_dwOffset				= dwOffset;
		m_tpLevelPoints.clear	();
		
		FILE_NAME				caFileName;
		
		// loading graph
		strcpy_s				(caFileName,graph_file_name);
		m_tpGraph				= new CGameGraph(caFileName);

		strcpy_s				(caFileName,raw_cross_table_file_name);
		CGameLevelCrossTable	*l_tpCrossTable = new CGameLevelCrossTable(caFileName);

		CLevelGraph				*l_tpAI_Map = new CLevelGraph(S);

		VERIFY2					(l_tpCrossTable->header().level_guid() == l_tpAI_Map->header().guid(), "cross table doesn't correspond to the AI-map, rebuild graph!");
		VERIFY2					(l_tpCrossTable->header().game_guid() == m_tpGraph->header().guid(), "cross table doesn't correspond to the graph, rebuild graph!");
		VERIFY2					(m_tpGraph->header().level(GameGraph::_LEVEL_ID(0)).guid() == l_tpAI_Map->header().guid(), "cross table doesn't correspond to the AI-map, rebuild graph!");

		VERIFY					(l_tpAI_Map->header().vertex_count() == l_tpCrossTable->header().level_vertex_count());
		VERIFY					(m_tpGraph->header().vertex_count() == l_tpCrossTable->header().game_vertex_count());

		tLevel->m_guid			= l_tpAI_Map->header().guid();

		{
			for (GameGraph::_GRAPH_ID i=0, n = m_tpGraph->header().vertex_count(); i<n; ++i)
				if ((!l_tpAI_Map->valid_vertex_id(m_tpGraph->vertex(i)->level_vertex_id()) ||
					(l_tpCrossTable->vertex(m_tpGraph->vertex(i)->level_vertex_id()).game_vertex_id() != i) ||
					!l_tpAI_Map->inside(m_tpGraph->vertex(i)->level_vertex_id(),m_tpGraph->vertex(i)->level_point()))) {
						Msg				("! Graph doesn't correspond to the cross table");
						R_ASSERT2		(false,"Graph doesn't correspond to the cross table");
				}
		}

		m_tpVertices.resize		(m_tpGraph->header().vertex_count());
		GRAPH_VERTEX_IT			B = m_tpVertices.begin();
		GRAPH_VERTEX_IT			I = B;
		GRAPH_VERTEX_IT			E = m_tpVertices.end();
		for ( ; I != E; I++) {
			(*I).tLocalPoint		= m_tpGraph->vertex(int(I - B))->level_point();
			(*I).tGlobalPoint.add	(m_tpGraph->vertex(int(I - B))->game_point(),m_tLevel.offset());
			(*I).tLevelID			= dwLevelID;
			(*I).tNodeID			= m_tpGraph->vertex(int(I - B))->level_vertex_id();
			Memory.mem_copy			((*I).tVertexTypes,m_tpGraph->vertex(int(I - B))->vertex_type(),GameGraph::LOCATION_TYPE_COUNT*sizeof(GameGraph::_LOCATION_ID));
			(*I).tNeighbourCount	= m_tpGraph->vertex(int(I - B))->edge_count();
			CGameGraph::const_iterator	b,i,e;
			m_tpGraph->begin		(int(I - B),i,e);
			(*I).tpaEdges			= (CGameGraph::CEdge*)xr_malloc((*I).tNeighbourCount*sizeof(CGameGraph::CEdge));
			b						= i;
			for ( ; i != e; ++i) {
				GameGraph::CEdge	&edge = (*I).tpaEdges[i - b];
				edge				= *i;
				VERIFY				((edge.vertex_id() + dwOffset) < (u32(1) << (8*sizeof(GameGraph::_GRAPH_ID))));
				edge.m_vertex_id	= (GameGraph::_GRAPH_ID)(edge.m_vertex_id + dwOffset);
			}
			(*I).dwPointOffset		= 0;
			vfGenerateDeathPoints	(int(I - B),l_tpCrossTable,l_tpAI_Map,(*I).tDeathPointCount);
		}

		xr_delete					(l_tpCrossTable);
		xr_delete					(l_tpAI_Map);
		
		// updating cross-table
		{
			strcpy_s				(caFileName,raw_cross_table_file_name);
			CGameLevelCrossTable	*tpCrossTable = new CGameLevelCrossTable(caFileName);
			xr_vector<CGameLevelCrossTable::CCell> tCrossTableUpdate;
			tCrossTableUpdate.resize(tpCrossTable->header().level_vertex_count());
			for (int i=0; i<(int)tpCrossTable->header().level_vertex_count(); i++) {
				tCrossTableUpdate[i] = tpCrossTable->vertex(i);
				VERIFY				(u32(tCrossTableUpdate[i].tGraphIndex) < tpCrossTable->header().game_vertex_count());
				tCrossTableUpdate[i].tGraphIndex = tCrossTableUpdate[i].tGraphIndex + (GameGraph::_GRAPH_ID)dwOffset;
			}

			CGameLevelCrossTable::CHeader	tCrossTableHeader;

			tCrossTableHeader.dwVersion			= XRAI_CURRENT_VERSION;
			tCrossTableHeader.dwNodeCount		= tpCrossTable->m_tCrossTableHeader.dwNodeCount;
			tCrossTableHeader.dwGraphPointCount = tpCrossTable->m_tCrossTableHeader.dwGraphPointCount;
			tCrossTableHeader.m_level_guid		= tpCrossTable->m_tCrossTableHeader.m_level_guid;
			tCrossTableHeader.m_game_guid		= tGraphHeader.m_guid;

			xr_delete			(tpCrossTable);

			m_cross_table.w(&tCrossTableHeader,sizeof(tCrossTableHeader));
			for (int i=0; i<(int)tCrossTableHeader.dwNodeCount; i++)
				m_cross_table.w(&(tCrossTableUpdate[i]),sizeof(tCrossTableUpdate[i]));
		}

		// fill vertex map
		{
			string_path								fName;
			strconcat								(sizeof(fName),fName,S,"level.spawn");
			IReader									*F = FS.r_open(fName);
			u32										id;
			IReader									*O = F->open_chunk_iterator(id);
			for (int i=0; O; O = F->open_chunk_iterator(id,O))	{
				NET_Packet							P;
				P.B.count							= O->length();
				O->r								(P.B.data,P.B.count);
				u16									ID;
				P.r_begin							(ID);
				R_ASSERT							(M_SPAWN==ID);
				P.r_stringZ							(fName);
				CSE_Abstract						*E = F_entity_Create(fName);
				R_ASSERT3							(E,"Can't create entity.",fName);
//				E->Spawn_Read						(P);
				CSE_ALifeGraphPoint					*tpGraphPoint = smart_cast<CSE_ALifeGraphPoint*>(E);
				if (tpGraphPoint) {
					E->Spawn_Read					(P);

					Fvector							tVector;
					tVector							= tpGraphPoint->o_Position;
					GameGraph::_GRAPH_ID			tGraphID = GameGraph::_GRAPH_ID(-1);
					float							fMinDistance = 1000000.f;
					{
						GRAPH_VERTEX_IT					B = m_tpVertices.begin();
						GRAPH_VERTEX_IT					I = B;
						GRAPH_VERTEX_IT					E = m_tpVertices.end();
						for ( ; I != E; I++) {
							float fDistance = (*I).tLocalPoint.distance_to(tVector);
							if (fDistance < fMinDistance) {
								fMinDistance	= fDistance;
								tGraphID		= GameGraph::_GRAPH_ID(I - B);
								if (fMinDistance < EPS_L)
									break;
							}
						}
					}
					if (fMinDistance < EPS_L) {
						SConnectionVertex				T;
						LPSTR							S;
						S								= xr_strdup(tpGraphPoint->name_replace());
						T.caConnectName					= xr_strdup(*tpGraphPoint->m_caConnectionPointName);
						T.dwLevelID						= dwfGetIDByLevelName(Ini,*tpGraphPoint->m_caConnectionLevelName);
//						T.tGraphID						= (GameGraph::_GRAPH_ID)i;
//						T.tOldGraphID					= tGraphID;
						T.tOldGraphID					= (GameGraph::_GRAPH_ID)i;
						T.tGraphID						= tGraphID;

						bool							ok = true;
						VERTEX_MAP::const_iterator		II = m_tVertexMap.begin();
						VERTEX_MAP::const_iterator		EE = m_tVertexMap.end();
						for ( ; II != EE; ++II)
							if (T.tOldGraphID == (*II).second.tOldGraphID) {
								ok						= false;
								Msg						("Graph point %s is removed,because it has the same position as some another graph point",E->name_replace());
								break;
							}

						if (ok) {
							m_tVertexMap.insert			(mk_pair(S,T));
							i++;
						}
					}
				}
				F_entity_Destroy					(E);
			}
			if (i != m_tpGraph->header().vertex_count())
				Msg									("Graph for the level %s doesn't correspond to the graph points from Level Editor! (%d : %d)",*m_tLevel.name(),i,m_tpGraph->header().vertex_count());
			
			VERTEX_MAP::const_iterator				I = m_tVertexMap.begin();
			VERTEX_MAP::const_iterator				E = m_tVertexMap.end();
			for ( ; I != E; ++I) {
				R_ASSERT3	(!xr_strlen((*I).second.caConnectName) || ((*I).second.tGraphID < m_tpVertices.size()),"Rebuild graph for the level",*m_tLevel.name());
			}

//			VERIFY3									(i == m_tpGraph->header().vertex_count(), "Rebuild graph for the level ",m_tLevel.name());
			O->close								();
			FS.r_close								(F);
		}
	};
コード例 #6
0
ファイル: ESceneDOTools.cpp プロジェクト: 2asoft/xray
bool EDetailManager::Export(LPCSTR path) 
{
    AnsiString fn		= AnsiString(path)+"build.details";
    bool bRes=true;

    SPBItem* pb = UI->ProgressStart(5,"Making details...");
	CMemoryWriter F;

    pb->Inc				("merge textures");
    Fvector2Vec			offsets;
    Fvector2Vec			scales;
    boolVec				rotated;
    RStringSet 			textures_set;
    RStringVec 			textures;
    U32Vec				remap;
    U8Vec remap_object	(objects.size(),u8(-1));

    int slot_cnt		= dtH.size_x*dtH.size_z;
	for (int slot_idx=0; slot_idx<slot_cnt; slot_idx++){
    	DetailSlot* it 	= &dtSlots[slot_idx];
        for (int part=0; part<4; part++){
        	u8 id		= it->r_id(part);
        	if (id!=DetailSlot::ID_Empty) {
            	textures_set.insert(((EDetail*)(objects[id]))->GetTextureName());
                remap_object[id] = 1;
            }
        }
    }
    textures.assign		(textures_set.begin(),textures_set.end());

    U8It remap_object_it= remap_object.begin();

    u32 new_idx			= 0;
    for (DetailIt d_it=objects.begin(); d_it!=objects.end(); d_it++,remap_object_it++)
    	if ((*remap_object_it==1)&&(textures_set.find(((EDetail*)(*d_it))->GetTextureName())!=textures_set.end()))
	    	*remap_object_it	= (u8)new_idx++;

    AnsiString 			do_tex_name = ChangeFileExt(fn,"_details");
    int res				= ImageLib.CreateMergedTexture(textures,do_tex_name.c_str(),STextureParams::tfADXT1,256,1024,256,1024,offsets,scales,rotated,remap);
    if (1!=res)			bRes=FALSE;

    pb->Inc				("export geometry");
    // objects
    int object_idx		= 0;
    if (bRes){
	    do_tex_name 	= ExtractFileName(do_tex_name);
        F.open_chunk	(DETMGR_CHUNK_OBJECTS);
        for (DetailIt it=objects.begin(); it!=objects.end(); it++){
        	if (remap_object[it-objects.begin()]!=u8(-1)){
                F.open_chunk	(object_idx++);
                if (!((EDetail*)(*it))->m_pRefs){
                    ELog.DlgMsg(mtError, "Bad object or object not found '%s'.", ((EDetail*)(*it))->m_sRefs.c_str());
                    bRes=false;
                }else{
                    LPCSTR tex_name = ((EDetail*)(*it))->GetTextureName();
                    for (u32 t_idx=0; t_idx<textures.size(); t_idx++) 
                        if (textures[t_idx]==tex_name) break;
                    VERIFY(t_idx<textures.size());
                    t_idx = remap[t_idx];
                    ((EDetail*)(*it))->Export	(F,do_tex_name.c_str(),offsets[t_idx],scales[t_idx],rotated[t_idx]);
                }
                F.close_chunk	();
                if (!bRes) break;
            }
        }
        F.close_chunk		();
    }
    
    pb->Inc	("export slots");
    // slots
    if (bRes){
    	xr_vector<DetailSlot> dt_slots(slot_cnt); dt_slots.assign(dtSlots,dtSlots+slot_cnt);
        for (slot_idx=0; slot_idx<slot_cnt; slot_idx++){
            DetailSlot& it 	= dt_slots[slot_idx];
            // zero colors need lighting
	        it.c_dir		= 0;
	        it.c_hemi		= 0;
	        it.c_r			= 0;
	        it.c_g			= 0;
	        it.c_b			= 0;
            for (int part=0; part<4; part++){
                u8 id		= it.r_id(part);
                if (id!=DetailSlot::ID_Empty) it.w_id(part,remap_object[id]);
            }
        }
		F.open_chunk	(DETMGR_CHUNK_SLOTS);
		F.w				(dt_slots.begin(),dtH.size_x*dtH.size_z*sizeof(DetailSlot));
	    F.close_chunk	();
        pb->Inc();

        // write header
        dtH.version		= DETAIL_VERSION;
        dtH.object_count= object_idx;

        F.w_chunk		(DETMGR_CHUNK_HEADER,&dtH,sizeof(DetailHeader));

    	bRes 			= F.save_to(fn.c_str());
    }

    pb->Inc();
    UI->ProgressEnd(pb);
    return bRes;
}
コード例 #7
0
void CGameGraphBuilder::save_graph			(const float &start, const float &amount)
{
	Progress				(start);

	Msg						("Saving graph");

	// header
	CMemoryWriter				writer;
	CGameGraph::CHeader			header;
	header.m_version			= XRAI_CURRENT_VERSION;
	VERIFY						(graph().vertices().size() < (u32(1) << (8*sizeof(GameGraph::_GRAPH_ID))));
	header.m_vertex_count		= (GameGraph::_GRAPH_ID)graph().vertices().size();
	VERIFY						(graph().edge_count() < (u32(1) << (8*sizeof(GameGraph::_GRAPH_ID))));
	header.m_edge_count			= (GameGraph::_GRAPH_ID)graph().edge_count();
	header.m_death_point_count	= 0;
	header.m_guid				= m_graph_guid;

	// levels
	CGameGraph::SLevel			level;
	level.m_offset.set			(0,0,0);
	level.m_id					= 0;
	level.m_name				= m_level_name;
	level.m_section				= "";
	level.m_guid				= level_graph().header().guid();

	header.m_levels.insert		(std::make_pair(level.m_id,level));

	header.save					(&writer);

	{
		u32								edge_offset = graph().vertices().size()*sizeof(CGameGraph::CVertex);

		graph_type::const_vertex_iterator	I = graph().vertices().begin();
		graph_type::const_vertex_iterator	E = graph().vertices().end();
		for ( ; I != E; ++I) {
			CGameGraph::CVertex		&vertex = (*I).second->data();

			VERIFY					((*I).second->edges().size() < 256);
			vertex.tNeighbourCount	= (u8)(*I).second->edges().size();
			vertex.dwEdgeOffset		= edge_offset;
			edge_offset				+= vertex.tNeighbourCount*sizeof(CGameGraph::CEdge);

			writer.w				(&vertex,sizeof(CGameGraph::CVertex));
		}
	}
	
	{
		graph_type::const_vertex_iterator	I = graph().vertices().begin();
		graph_type::const_vertex_iterator	E = graph().vertices().end();
		for ( ; I != E; ++I) {
			graph_type::const_iterator	i = (*I).second->edges().begin();
			graph_type::const_iterator	e = (*I).second->edges().end();
			for ( ; i != e; ++i) {
				GameGraph::CEdge			edge;
				VERIFY						((*i).vertex_id() < (u32(1) << (8*sizeof(GameGraph::_GRAPH_ID))));
				edge.m_vertex_id			= (GameGraph::_GRAPH_ID)(*i).vertex_id();
				edge.m_path_distance		= (*i).weight();

				writer.w					(&edge.m_vertex_id,sizeof(edge.m_vertex_id));
				writer.w_float				(edge.m_path_distance);
			}
		}
	}

#ifdef PRIQUEL
	writer.save_to				(m_graph_name);
#else // PRIQUEL
	string_path					file_name;
	strconcat					(sizeof(file_name),file_name,*m_level_name,GAME_LEVEL_GRAPH);
	writer.save_to				(file_name);
#endif // PRIQUEL
	Msg							("%d bytes saved",int(writer.size()));

	Progress					(start + amount);
}
コード例 #8
0
ファイル: xrCrossTable.cpp プロジェクト: NeoAnomaly/xray
CCrossTableBuilder::CCrossTableBuilder(LPCSTR caProjectName)
{
	FILE_NAME			caFileName;
	strconcat			(sizeof(caFileName),caFileName,caProjectName,GAME_LEVEL_GRAPH);
	
	Phase				("Loading level graph");
	CGameGraph			tGraph(caFileName);
	
	Phase				("Loading AI map");
	CLevelGraph			tMap(caProjectName);
	
	Phase				("Building dynamic objects");
	FLOAT_VECTOR_VECTOR	tDistances;
	int					iVertexCount	= tGraph.header().vertex_count();
	R_ASSERT2			(iVertexCount > 0,"There are no graph points in the graph!");
	int					iNodeCount		= tMap.header().vertex_count();
	xr_vector<bool>		tMarks;
	tMarks.assign		(tMap.header().vertex_count(),false);
	{
		for (int i=0; i<iVertexCount; i++)
			vfRecurseMark(tMap,tMarks,tGraph.vertex(i)->level_vertex_id());
		tMarks.flip		();
	}

	tDistances.resize	(iVertexCount);
	{
		FLOAT_VECTOR_IT		I = tDistances.begin();
		FLOAT_VECTOR_IT		E = tDistances.end();
		for ( ; I != E; I++) {
			(*I).resize		(iNodeCount);
			FLOAT_IT		i = (*I).begin();
			FLOAT_IT		e = (*I).end();
			for ( ; i != e; i++)
				*i			= u32(-1);
		}
	}
	
	Phase				("Building cross table");
	Progress(0.f);
	for (int i=0; i<iVertexCount; ++i) {
		if (i)
			for (int k=0; k<(int)tMap.header().vertex_count(); k++)
				tDistances[i][k] = tDistances[i - 1][k];
		g_tDistances	= &tDistances[i];
		g_tMap			= &tMap;
		g_tMarks		= &tMarks;
		vfRecurseUpdate(tGraph.vertex(i)->level_vertex_id(),i,iVertexCount);
		Progress(float(i + 1)/float(iVertexCount));
	}
	Progress			(1.f);
	
	Phase				("Saving cross table");
	CMemoryWriter					tMemoryStream;
	CGameLevelCrossTable::CHeader	tCrossTableHeader;
	
	tCrossTableHeader.dwVersion			= XRAI_CURRENT_VERSION;
	tCrossTableHeader.dwNodeCount		= iNodeCount;
	tCrossTableHeader.dwGraphPointCount = iVertexCount;
	tCrossTableHeader.m_level_guid		= tMap.header().guid();
	tCrossTableHeader.m_game_guid		= tGraph.header().guid();
	
	tMemoryStream.open_chunk			(CROSS_TABLE_CHUNK_VERSION);
	tMemoryStream.w						(&tCrossTableHeader,sizeof(tCrossTableHeader));
	tMemoryStream.close_chunk			();
	
	tMemoryStream.open_chunk			(CROSS_TABLE_CHUNK_DATA);
	{
		for (int i=0; i<iNodeCount; i++) {
			FLOAT_VECTOR_IT		I = tDistances.begin(), B = I;
			FLOAT_VECTOR_IT		E = tDistances.end();
			CGameLevelCrossTable::CCell tCrossTableCell;
			tCrossTableCell.fDistance = flt_max;
			tCrossTableCell.tGraphIndex = u16(-1);
			for ( ; I != E; I++)
				if (float((*I)[i])*tMap.header().cell_size() < tCrossTableCell.fDistance) {
					tCrossTableCell.fDistance	= float((*I)[i])*tMap.header().cell_size();
					tCrossTableCell.tGraphIndex = GameGraph::_GRAPH_ID(I - B);
				}
			
			for (int j=0; j<iVertexCount; j++)
				if ((tGraph.vertex(j)->level_vertex_id() == (u32)i) && (tCrossTableCell.tGraphIndex != j)) {
					Msg("! Warning : graph points are too close, therefore cross table is automatically validated");
					Msg("%d : [%f][%f][%f] %d[%f] -> %d[%f]",i,VPUSH(tGraph.vertex(j)->level_point()),tCrossTableCell.tGraphIndex,tCrossTableCell.fDistance,j,tDistances[j][i]);
					tCrossTableCell.fDistance	= float(tDistances[j][i])*tMap.header().cell_size();
					tCrossTableCell.tGraphIndex = (GameGraph::_GRAPH_ID)j;
				}
			tMemoryStream.w(&tCrossTableCell,sizeof(tCrossTableCell));
		}
	}
	tMemoryStream.close_chunk();
	
	strconcat			(sizeof(caFileName),caFileName,caProjectName,CROSS_TABLE_NAME_RAW);
	tMemoryStream.save_to(caFileName);
}