Files
FC1/Editor/GameExporter.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1397 lines
44 KiB
C++

#include "StdAfx.h"
#include "gameexporter.h"
#include "gameengine.h"
#include "PluginManager.h"
#include "texturecompression.h"
#include "DimensionsDialog.h"
#include "TerrainTexture.h"
#include "TerrainLighting.h"
#include "SurfaceType.h"
#include "EquipPackLib.h"
#include "TopRendererWnd.h"
#include "ViewManager.h"
#include "VegetationObject.h"
#include "CryEditDoc.h"
#include "Mission.h"
#include "VegetationMap.h"
#include "Util\FileUtil.h"
#include "Objects\ObjectManager.h"
#include "Objects\BaseObject.h"
#include "Brush\BrushExporter.h"
//#include "Anim\IMovieSystem.h"
#include "AnimationSerializer.h"
#include "Material\MaterialManager.h"
#include "Material\MaterialLibrary.h"
#include "Particles\ParticleManager.h"
#include "Music\MusicManager.h"
#include "BaseLibrary.h"
#include "ParticleExporter.h"
#include "Heightmap.h"
#include "TerrainTexGen.h"
#include "TerrainBeachGen.h"
#include <IAgent.h>
#include <IAISystem.h>
#include <I3dengine.h>
#include <IGame.h>
#include <ICryPak.h>
#include <I3DEngine.h>
//////////////////////////////////////////////////////////////////////////
#define VEGETATION_FILE "objects.lst"
#define MUSIC_LEVEL_LIBRARY_FILE "Music.xml"
#define MATERIAL_LEVEL_LIBRARY_FILE "Materials.xml"
#define TERRAIN_BEACHES_FILENAME "beach7.tmp"
//////////////////////////////////////////////////////////////////////////
CGameExporter::CGameExporter( ISystem *system )
{
m_IEditor = GetIEditor();
m_ISystem = system;
m_I3DEngine = m_ISystem->GetI3DEngine();
m_IAISystem = m_ISystem->GetAISystem();
m_IEntitySystem = m_ISystem->GetIEntitySystem();
m_iExportTexWidth = 0;
m_bHiQualityCompression = true;
}
CGameExporter::~CGameExporter(void)
{
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::Export( bool bSurfaceTexture,bool bReloadTerrain )
{
CString szLevelPath = Path::GetPath( m_IEditor->GetDocument()->GetPathName() );
szLevelPath = GetIEditor()->GetRelativePath(szLevelPath);
if (szLevelPath.IsEmpty())
{
// Bad path.
Warning( "Export Failed!\r\nLevel folder must be relative to root folder of the game.",MB_OK|MB_ICONERROR );
return;
}
m_levelName = Path::RemoveBackslash(szLevelPath);
m_levelPath = Path::RemoveBackslash(szLevelPath);
if (bSurfaceTexture)
{
CDimensionsDialog cDialog;
// 4096x4096 is default
cDialog.SetDimensions(4096);
// Query the size of the preview
if (cDialog.DoModal() == IDCANCEL)
return;
m_bHiQualityCompression = cDialog.GetCompressionQuality();
m_iExportTexWidth = cDialog.GetDimensions();
}
m_missionName = m_IEditor->GetGameEngine()->GetMissionName();
// Make sure the path is ready for adding a filename
char pszGamePath[_MAX_PATH];
CString strTempCopyStr = m_IEditor->GetDocument()->GetPathName();
strcpy(pszGamePath, strTempCopyStr.GetBuffer(1));
PathRemoveFileSpec(pszGamePath);
PathAddBackslash(pszGamePath);
CString pakFilename = CString(pszGamePath) + "Level.pak";
//////////////////////////////////////////////////////////////////////////
// Make sure 3D engine closes texture handle.
GetIEditor()->Get3DEngine()->CloseTerrainTextureFile();
// Close this pak file.
if (!m_ISystem->GetIPak()->ClosePack( pakFilename ))
{
Warning( "Export Failed!\r\nCannot Close Pak File %s",(const char*)pakFilename );
}
//////////////////////////////////////////////////////////////////////////
if (!CFileUtil::OverwriteFile(pakFilename))
return;
// Delete old pak file.
//DeleteFile( pakFilename );
if (!m_levelPakFile.Open( pakFilename ))
{
Warning( "Export Failed!\r\nCannot Open pak file %s for writing.",(const char*)pakFilename );
return;
}
//////////////////////////////////////////////////////////////////////////
// Start Export.
//////////////////////////////////////////////////////////////////////////
// Reset all animations before exporting.
GetIEditor()->GetAnimation()->ResetAnimations(false);
////////////////////////////////////////////////////////////////////////
// Export all data to the game
////////////////////////////////////////////////////////////////////////
ExportMap(pszGamePath, bSurfaceTexture);
////////////////////////////////////////////////////////////////////////
// Export the cloud layer
////////////////////////////////////////////////////////////////////////
//m_IEditor->SetStatusText("Exporting cloud layer...");
//CLogFile::WriteLine("Exportin Cloud layer...");
////////////////////////////////////////////////////////////////////////
// Export the heightmap, store shadow informations in it
////////////////////////////////////////////////////////////////////////
ExportHeightMap(pszGamePath);
////////////////////////////////////////////////////////////////////////
// Export static objects
////////////////////////////////////////////////////////////////////////
ExportStaticObjects( pszGamePath );
HEAP_CHECK
////////////////////////////////////////////////////////////////////////
// Exporting map setttings
////////////////////////////////////////////////////////////////////////
//ExportMapIni( pszGamePath );
//////////////////////////////////////////////////////////////////////////
// Export Particles.
{
CParticlesExporter partExporter;
partExporter.ExportParticles( pszGamePath,m_levelPath,m_levelPakFile );
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//! Export Level data.
CLogFile::WriteLine("Exporting LevelData.xml");
ExportLevelData( pszGamePath );
CLogFile::WriteLine("Exporting LevelData.xml done.");
HEAP_CHECK
ExportLevelInfo( pszGamePath );
////////////////////////////////////////////////////////////////////////
// Export the data of all plugin
////////////////////////////////////////////////////////////////////////
CLogFile::WriteLine("Exporting plugin data...");
if (!m_IEditor->GetPluginManager()->CallExport(pszGamePath))
{
CLogFile::WriteLine("Error while exporting plugin data !");
ASSERT(false);
AfxMessageBox("Error while exporting plugin data !");
}
ExportAIGraph(pszGamePath);
//////////////////////////////////////////////////////////////////////////
// Start Movie System animations.
//////////////////////////////////////////////////////////////////////////
ExportAnimations(pszGamePath);
//////////////////////////////////////////////////////////////////////////
// Export Brushes.
//////////////////////////////////////////////////////////////////////////
ExportBrushes( pszGamePath );
//////////////////////////////////////////////////////////////////////////
// End Exporting Game data.
//////////////////////////////////////////////////////////////////////////
// Close all packs.
m_levelPakFile.Close();
// m_texturePakFile.Close();
////////////////////////////////////////////////////////////////////////
// Reload the level in the engine
////////////////////////////////////////////////////////////////////////
if (bReloadTerrain)
{
GetIEditor()->SetStatusText( _T("Reloading Level...") );
m_IEditor->GetGameEngine()->LoadLevel( szLevelPath, m_missionName,false,false );
}
// Force hide local player.
#ifndef _ISNOTFARCRY
GetIXGame( GetIEditor()->GetGame() )->HideLocalPlayer( true, true );
#endif
GetIEditor()->SetStatusText( _T("Ready") );
// Reopen this pak file.
if (!m_ISystem->GetIPak()->OpenPack( pakFilename ))
{
Warning( "Export Failed!\r\nCannot Open Pak File %s",(const char*)pakFilename );
return;
}
CLogFile::WriteLine("Exporting was successful.");
}
void CGameExporter::ExportMap(char *pszGamePath, bool bSurfaceTexture)
{
m_IEditor->ShowConsole( true );
char szBuffer[512 + _MAX_PATH];
// Settings
int iExportTexWidth = m_iExportTexWidth; // Size of the exported surface texture
const int iMapPreviewWidth = 512; // Size of the map preview
bool bHiQualityCompression = m_bHiQualityCompression;
// No need to generate texture if there are no layers or the caller does
// not demand the texture to be generated
if (m_IEditor->GetDocument()->GetLayerCount() == 0 || !bSurfaceTexture)
return;
/*
if (!GetPathName().IsEmpty())
{
OnSaveDocument( GetPathName() );
}
*/
// Check if the .cry file is in the correct folder
strcpy(szBuffer, pszGamePath);
szBuffer[strlen(GetIEditor()->GetMasterCDFolder())] = '\0';
if (stricmp(szBuffer,GetIEditor()->GetMasterCDFolder()) != 0)
{
AfxMessageBox("Can't export because the .cry file is not in the Levels folder inside" \
" the Master CD folder, or editor executable is not in the Master CD folder. Always create" \
" your projects with the 'Create / open level...' command. Do not manually move" \
" the .cry file or the editor executable.");
return;
}
CWaitCursor wait;
CLogFile::FormatLine("Exporting data to game (%s)...", pszGamePath);
////////////////////////////////////////////////////////////////////////
// Create the necessary subdirectories
////////////////////////////////////////////////////////////////////////
//sprintf(szFileOutputPath, "%sterrain", pszGamePath);
//CreateDirectory( szFileOutputPath,0 );
////////////////////////////////////////////////////////////////////////
// Export the map preview
////////////////////////////////////////////////////////////////////////
//Timur[2/6/2003] Commented out, Not used now.
/*
{
m_IEditor->SetStatusText("Exporting map preview...");
CLogFile::FormatLine("Map preview (%ix%i)...", iMapPreviewWidth, iMapPreviewWidth);
CImage preview;
preview.Allocate( iMapPreviewWidth,iMapPreviewWidth );
CTerrainTexGen texGen( iMapPreviewWidth );
if (!texGen.GenerateSurfaceTexture( ETTG_QUIET|ETTG_KEEP_LAYERMASKS|ETTG_LIGHTING ))
{
Error( "Failed to generate preview texture.");
return;
}
// Construct the filename of the map preview file
sprintf(szFileOutputPath, "%s\\terrain\\map_preview.jpg", pszGamePath);
// Save the surface texture preview as JPEG file
if (!CImageUtil::SaveImage( szFileOutputPath, preview ))
{
Error("Can't export / save surface texture for map preview");
return;
}
}
*/
////////////////////////////////////////////////////////////////////////
// Export the surface texture
////////////////////////////////////////////////////////////////////////
CLogFile::WriteLine("Exporting Surface texture.");
CHeightmap &HeightMap=m_IEditor->GetDocument()->GetHeightmap();
m_IEditor->SetStatusText("Exporting surface texture (Generating)...");
// Check dimensions
if (HeightMap.GetWidth() != HeightMap.GetHeight() || HeightMap.GetWidth() % 128)
{
ASSERT(HeightMap.GetWidth() % 128 == 0);
AfxMessageBox("Can't export a heightmap with dimensions that can't be" \
" evenly divided by 128 or that are not square !");
CLogFile::WriteLine("Can't export a heightmap");
return;
}
/*
// Make Texture Pack.
CString pakFilename = CString(pszGamePath) + "LevelTexture.pak";
// Close this pak file.
if (!m_ISystem->GetIPak()->ClosePack( pakFilename ))
{
Error( "Cannot Close Pak File %s",(const char*)pakFilename );
}
*/
//////////////////////////////////////////////////////////////////////////
//if (!CFileUtil::OverwriteFile(pakFilename))
//return;
uint t0 = GetTickCount();
// Time how long does it take to generate Surface texture.
{
// Allocate memory for the lighting bit array
CBitArray lightBits;
lightBits.reserve( iExportTexWidth*iExportTexWidth );
lightBits.clear_num( iExportTexWidth*iExportTexWidth );
CTerrainTexGen texGen( iExportTexWidth );
texGen.SetLightingBits( &lightBits );
// Allocate the memory for the texture
CImage surfaceTexture;
surfaceTexture.Allocate( iExportTexWidth,iExportTexWidth );
if (!surfaceTexture.GetData())
{
Error( "Surface Texture Memory Allocation Failed.\r\nChoose smaller texture size." );
return;
}
if (!texGen.GenerateSurfaceTexture(ETTG_QUIET|ETTG_KEEP_LAYERMASKS|ETTG_LIGHTING|ETTG_STATOBJ_SHADOWS|ETTG_STATOBJ_PAINTBRIGHTNESS|ETTG_ABGR,surfaceTexture ))
return;
uint t1 = GetTickCount();
CLogFile::FormatLine( "Surface Texture Generated in %u seconds",(t1-t0)/1000 );
// Delete old pak file.
//DeleteFile( pakFilename );
//m_texturePakFile.Open( pakFilename,false );
ExportLowResSurfaceTexture( pszGamePath,surfaceTexture );
HeightMap.SetLightingBit( lightBits,iExportTexWidth,iExportTexWidth );
HeightMap.CalcSurfaceTypes();
if (!ExportSurfaceTexture( pszGamePath,surfaceTexture.GetData(),surfaceTexture.GetWidth(),bHiQualityCompression ))
return;
}
/*
CLogFile::WriteLine("Compressing surface texture...");
// Call the compression function
sprintf(szFileOutputPath, "%sTerrain\\", pszGamePath);
cCompression.CompressCTU( szFileOutputPath,bHiQualityCompression );
*/
//int texSize = m_I3DEngine->GetTerrainTextureDim();
//if (texSize > 0)
HeightMap.SetSurfaceTextureSize( iExportTexWidth,iExportTexWidth );
int t2 = GetTickCount();
CLogFile::FormatLine( "Surface Texture Exported in %u seconds.",(t2-t0)/1000 );
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportHeightMap(char *pszGamePath)
{
char szFileOutputPath[_MAX_PATH];
t_hmap *pHeightmapBits = NULL;
t_hmap *pHeightmapBitsStart = NULL;
t_hmap *pHeightmapBitsEnd = NULL;
uint16 *pSaveHeightmapData = NULL;
uint16 *pSaveHeightmapDataStart = NULL;
uint16 *pSaveHeightmapDataEnd = NULL;
CHeightmap &HeightMap=m_IEditor->GetDocument()->GetHeightmap();
m_IEditor->SetStatusText("Exporting heightmap...");
CLogFile::WriteLine("Heightmap...");
m_IEditor->SetStatusText("Calculating Surface Types...");
CLogFile::WriteLine("Calculating Surface Types...");
//HeightMap.CalcSurfaceTypes();
m_IEditor->SetStatusText("Exporting heightmap...");
HEAP_CHECK
// Allocate memory for the 16 bit version that will be saved
pSaveHeightmapData = new uint16 [HeightMap.GetWidth() * HeightMap.GetHeight()];
ASSERT(pSaveHeightmapData);
pSaveHeightmapDataStart = pSaveHeightmapData;
pSaveHeightmapDataEnd = &pSaveHeightmapData[HeightMap.GetWidth() * HeightMap.GetHeight()];
// Set the loop pointers
pHeightmapBitsStart = HeightMap.GetData();
pHeightmapBits = pHeightmapBitsStart;
pHeightmapBitsEnd = &pHeightmapBitsStart[HeightMap.GetWidth() * HeightMap.GetHeight()];
// Convert the heightmap data to 16 bit and store it in the 16 bit array
while (pHeightmapBits != pHeightmapBitsEnd)
{
// Must multiply to 256.
*pSaveHeightmapData++ = ftoi((*pHeightmapBits)*256.0f);
pHeightmapBits++;
}
// Restore the pointers
pHeightmapBits = pHeightmapBitsStart;
pSaveHeightmapData = pSaveHeightmapDataStart;
/* gameExporter.*/FlattenHeightmap( pSaveHeightmapData,HeightMap.GetWidth(),HeightMap.GetHeight() );
HEAP_CHECK
// Construct the filename of the heightmap file
sprintf(szFileOutputPath, "%sTerrain\\LAND_MAP.H16", pszGamePath);
// Open the heightmap file
//if (!CFileUtil::OverwriteFile( szFileOutputPath ))
//return;
// Save heightmap file.
CMemFile hmapFile;
hmapFile.Write( pSaveHeightmapDataStart,2*HeightMap.GetWidth()*HeightMap.GetHeight() );
m_levelPakFile.UpdateFile( szFileOutputPath,hmapFile );
// Free the memory of the 16 bit version of the heightmap data
if (pSaveHeightmapDataStart)
{
delete [] pSaveHeightmapDataStart;
pSaveHeightmapDataStart = NULL;
}
ExportTerrainBeaches( pszGamePath );
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportAnimations( const CString &path )
{
GetIEditor()->SetStatusText( _T("Exporting Animation Sequences...") );
CLogFile::WriteLine("Export animation sequences...");
CAnimationSerializer animSaver;
animSaver.SaveAllSequences( path,m_levelPakFile );
CLogFile::WriteString("Done.");
/*
char szPath[_MAX_PATH];
strcpy( szPath,m_levelPath );
PathAddBackslash(szPath);
strcat( szPath,"Animation\\" );
ISequenceIt *It=m_IEditor->GetMovieSystem()->GetSequences();
IAnimSequence *seq=It->first();
while (seq)
{
char szFile[_MAX_PATH];
_makepath( szFile,0,szPath,seq->GetName(),"seq" );
CAnimationSerializer animSerializer;
animSerializer.SaveSequence( seq,szFile,false );
seq=It->next();
}
It->Release();
*/
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportStaticObjects( const CString &exportPath )
{
int iCount,i;
Vec3 p;
CHeightmap *pHeightMap = m_IEditor->GetHeightmap();
int worldSize = pHeightMap->GetUnitSize() * pHeightMap->GetWidth();
GetIEditor()->SetStatusText( _T("Exporting Vegetation...") );
CLogFile::WriteLine("Exporting Vegetation...");
// Assign indices to vegetation objects.
CVegetationMap *pVegetationMap = m_IEditor->GetVegetationMap();
for (i = 0; i < pVegetationMap->GetObjectCount(); i++)
{
pVegetationMap->GetObject(i)->SetIndex(i);
}
std::vector<CVegetationInstance*> vegInstances;
pVegetationMap->GetAllInstances( vegInstances );
// Generate a list of static objects
std::vector<StatObjInstance> statObjects;
statObjects.resize( vegInstances.size() );
if (statObjects.size() > 0)
{
memset( &statObjects[0],0,sizeof(StatObjInstance)*statObjects.size() );
}
for (i = 0; i < statObjects.size(); i++)
{
CVegetationInstance *vegobj = vegInstances[i];
CVegetationObject *so = vegobj->object;
StatObjInstance &inst = statObjects[i];
// Swap x/y.
inst.SetXYZ( vegobj->pos.x,vegobj->pos.y,vegobj->pos.z,worldSize );
inst.SetID( so->GetIndex() );
inst.SetScale( vegobj->scale );
inst.SetBrightness( vegobj->brightness );
}
/*
// Construct the filename of the static object index file
sprintf(szFileOutputPath, "%sobjects.idx", (const char*)exportPath );
// Open the index file
//if (!CFileUtil::OverwriteFile( szFileOutputPath ))
//return;
//FILE *hFile = fopen(szFileOutputPath, "w");
CMemFile objectsidxFile;
typedef StdMap<CString,int> StatObjFileMap;
StatObjFileMap statObjFileMap;
const char *strFormat = "%s Angles=(%g,%g,%g) Bending=%g Hide=%d\n";
float fZero = 0;
int iZero = 0;
// Write the objects into the index file
for (i=0; i < pVegetationMap->GetObjectCount(); i++)
{
CVegetationObject *obj = pVegetationMap->GetObject(i);
CString objName = obj->GetFileName();
float fBending = obj->GetBending();
const char *objFileName = obj->GetFileName();
int hidable = obj->IsHideable();
char str[1024];
sprintf( str,strFormat,objFileName,fZero,fZero,fZero,fBending,hidable );
objectsidxFile.Write( str,strlen(str) );
statObjFileMap.Insert( str,i );
}
// Update file in pak.
m_levelPakFile.UpdateFile( szFileOutputPath,objectsidxFile );
*/
// Write the objects into the index file
int iStaticId = pVegetationMap->GetObjectCount();
CString filename = Path::Make( exportPath,VEGETATION_FILE );
if (statObjects.size() != 0)
{
CLogFile::FormatLine("Exporting %i static object instances...", statObjects.size() );
CMemFile objectlstFile;
// Write the size of one static object
iCount = sizeof(statObjects[0]);
objectlstFile.Write( &iCount,sizeof(iCount) );
// Save every static object
for (int i = 0; i < statObjects.size(); i++)
{
// Write the structure to the file
objectlstFile.Write( &statObjects[i], sizeof(statObjects[i]) );
}
// Update file in pak.
m_levelPakFile.UpdateFile( filename,objectlstFile );
}
else
{
m_levelPakFile.RemoveFile( filename );
}
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportSurfaceTypes( const CString &iniFile )
{
/*
std::vector<CString> detailObjects;
int i;
// Fill detail objects.
for (i = 0; i < m_IEditor->GetDocument()->GetSurfaceTypeCount(); i++)
{
CSurfaceType *sf = m_IEditor->GetDocument()->GetSurfaceType(i);
for (int j = 0; j < sf->GetDetailObjectCount(); j++)
{
if (std::find(detailObjects.begin(),detailObjects.end(),sf->GetDetailObject(j)) == detailObjects.end())
{
detailObjects.push_back( sf->GetDetailObject(j) );
}
}
}
// Write detail objects.
char str[256];
char str1[256];
for (i = 0; i < detailObjects.size(); i++)
{
sprintf( str,"DetailObject%d_FileName",i );
WritePrivateProfileString("DetailObjects", str, detailObjects[i], iniFile );
sprintf( str,"DetailObject%d_Scale",i );
WritePrivateProfileString("DetailObjects", str, "0.5",iniFile );
}
// Write surface types defenitions.
for (i = 0; i < m_IEditor->GetDocument()->GetSurfaceTypeCount(); i++)
{
CSurfaceType *sf = m_IEditor->GetDocument()->GetSurfaceType(i);
sprintf( str,"Surface%d_Texture",i );
WritePrivateProfileString("SurfaceDefinition", str, sf->GetDetailTexture(), iniFile );
sprintf( str,"Surface%d_DetailObjects",i );
strcpy( str1,"" );
for (int j = 0; j < sf->GetDetailObjectCount(); j++)
{
int id = std::find(detailObjects.begin(),detailObjects.end(),sf->GetDetailObject(j)) - detailObjects.begin();
char str2[64];
strcat( str1,itoa( id,str2,10 ) );
strcat( str1,"," );
}
// remove last comma.
int len1 = strlen(str1);
if (len1 > 0)
{
str1[len1-1] = 0;
}
WritePrivateProfileString("SurfaceDefinition", str, str1, iniFile );
}
*/
}
void CGameExporter::ExportLevelData( const CString &path,bool bExportMission )
{
GetIEditor()->SetStatusText( _T("Exporting LevelData.xml...") );
int i;
XmlNodeRef root = new CXmlNode( "LevelData" );
root->setAttr( "SandboxVersion",(const char*)GetIEditor()->GetFileVersion().ToFullString() );
ExportMapInfo( root );
//////////////////////////////////////////////////////////////////////////
/// Export vegetation objects.
XmlNodeRef vegetationNode = root->newChild( "Vegetation" );
CVegetationMap *pVegetationMap = m_IEditor->GetVegetationMap();
for (i = 0; i < pVegetationMap->GetObjectCount(); i++)
{
XmlNodeRef node = vegetationNode->newChild( "Object" );
pVegetationMap->GetObject(i)->Serialize( node,false );
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Export materials.
ExportMaterials( root,path );
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Export particle manager.
GetIEditor()->GetParticleManager()->Export( root );
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Export Dymaic Music info.
ExportMusic( root,path );
//////////////////////////////////////////////////////////////////////////
//XmlNodeRef objectsRoot = root->newChild( "Objects" );
//m_IEditor->GetObjectManager()->Export( path,objectsRoot,true );
// Save contents of current mission.
//m_IEditor->GetDocument()->GetCurrentMission()->SyncContent( false );
CCryEditDoc *pDocument = m_IEditor->GetDocument();
CMission *pCurrentMission = 0;
if (bExportMission)
{
pCurrentMission = pDocument->GetCurrentMission();
// Save contents of current mission.
}
//////////////////////////////////////////////////////////////////////////
// Export missions tag.
//////////////////////////////////////////////////////////////////////////
XmlNodeRef missionsNode = root->newChild("Missions");
CString missionFileName;
CString currentMissionFileName;
for (i = 0; i < pDocument->GetMissionCount(); i++)
{
CMission *pMission = pDocument->GetMission(i);
CString name = pMission->GetName();
name.Replace( ' ','_' );
missionFileName.Format( "Mission_%s.xml",(const char*)name );
XmlNodeRef missionDescNode = missionsNode->newChild("Mission");
missionDescNode->setAttr( "Name",pMission->GetName() );
missionDescNode->setAttr( "File",missionFileName );
missionDescNode->setAttr( "CGFCount",m_I3DEngine->GetLoadedObjectCount() );
int nProgressBarRange = m_numExportedMaterials/10 + m_I3DEngine->GetLoadedObjectCount();
missionDescNode->setAttr( "ProgressBarRange",nProgressBarRange );
if (pMission == pCurrentMission)
{
currentMissionFileName = missionFileName;
}
}
//////////////////////////////////////////////////////////////////////////
// Save Level Data XML
//////////////////////////////////////////////////////////////////////////
CString levelDataFile = path + "LevelData.xml";
//if (!CFileUtil::OverwriteFile( levelDataFile ))
//return;
XmlString xmlData = root->getXML();
CMemFile file;
file.Write( xmlData.c_str(),xmlData.length() );
m_levelPakFile.UpdateFile( levelDataFile,file );
if (bExportMission)
{
//////////////////////////////////////////////////////////////////////////
// Export current mission file.
//////////////////////////////////////////////////////////////////////////
XmlNodeRef missionNode = root->createNode("Mission");
m_IEditor->GetEquipPackLib()->Serialize(missionNode, false);
pCurrentMission->Export( missionNode );
missionNode->setAttr( "CGFCount",m_I3DEngine->GetLoadedObjectCount() );
//if (!CFileUtil::OverwriteFile( path+currentMissionFileName ))
// return;
xmlData = missionNode->getXML();
CMemFile file;
file.Write( xmlData.c_str(),xmlData.length() );
m_levelPakFile.UpdateFile( path+currentMissionFileName,file );
}
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportLevelInfo( const CString &path )
{
//////////////////////////////////////////////////////////////////////////
// Export short level info xml.
//////////////////////////////////////////////////////////////////////////
XmlNodeRef root = new CXmlNode( "LevelInfo" );
root->setAttr( "SandboxVersion",(const char*)GetIEditor()->GetFileVersion().ToFullString() );
CString levelName = GetIEditor()->GetGameEngine()->GetLevelName();
root->setAttr( "Name",levelName );
root->setAttr( "HeightmapSize",GetIEditor()->GetHeightmap()->GetWidth() );
// Save all missions in this level.
XmlNodeRef missionsNode = root->newChild( "Missions" );
int numMissions = GetIEditor()->GetDocument()->GetMissionCount();
for (int i = 0; i < numMissions; i++)
{
CMission *pMission = GetIEditor()->GetDocument()->GetMission(i);
XmlNodeRef missionNode = missionsNode->newChild( "Mission" );
missionNode->setAttr( "Name",pMission->GetName() );
missionNode->setAttr( "Description",pMission->GetDescription() );
}
//////////////////////////////////////////////////////////////////////////
// Save LevelInfo file.
//////////////////////////////////////////////////////////////////////////
CString filename = path + "LevelInfo.xml";
XmlString xmlData = root->getXML();
CMemFile file;
file.Write( xmlData.c_str(),xmlData.length() );
m_levelPakFile.UpdateFile( filename,file );
}
void CGameExporter::ExportMapIni( const CString &path )
{
/*
m_IEditor->SetStatusText("Exporting map settings...");
CLogFile::WriteLine("Map settings...");
// Construct the filename of the map INI file
char szFileOutputPath[1024];
char szBuffer[1024];
sprintf(szFileOutputPath, "%smap.ini", path);
// Delete any old INI file
if (PathFileExists(szFileOutputPath))
{
if (!m_IEditor->GetDocument()->FileDelete(szFileOutputPath))
{
AfxMessageBox("Can't rebuilt map.ini file !");
CLogFile::WriteLine("Can't rebuilt map.ini file !");
}
}
// Write the version of the file format
VERIFY(WritePrivateProfileString("File", "MapFormatVersion", "1.0", szFileOutputPath));
// Write the creation time of the file
_strdate(szBuffer);
VERIFY(WritePrivateProfileString("File", "MapCreationDate", szBuffer, szFileOutputPath));
// Write the name of the map
strcpy(szBuffer, LPCTSTR(m_IEditor->GetDocument()->GetTitle()));
PathRemoveExtension(szBuffer);
VERIFY(WritePrivateProfileString("Map", "MapName", szBuffer, szFileOutputPath));
CHeightmap &HeightMap=m_IEditor->GetDocument()->GetHeightmap();
// Write the size of the heightmap
ASSERT(HeightMap.GetHeight() == HeightMap.GetWidth());
sprintf(szBuffer, "%i", HeightMap.GetWidth());
VERIFY(WritePrivateProfileString("Map", "HeightmapSize", szBuffer, szFileOutputPath));
// Write the water level
sprintf(szBuffer, "%i", (int)HeightMap.GetWaterLevel() );
VERIFY(WritePrivateProfileString("Map", "MapWaterLevel", szBuffer, szFileOutputPath));
// Write the water color.
sprintf(szBuffer, "%x", m_IEditor->GetDocument()->GetWaterColor());
VERIFY(WritePrivateProfileString("Map", "MapWaterColor", szBuffer, szFileOutputPath));
//////////////////////////////////////////////////////////////////////////
CLogFile::WriteLine("...Success");
ExportSurfaceTypes( szFileOutputPath );
*/
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportMapInfo( XmlNodeRef &node )
{
XmlNodeRef info = node->newChild( "LevelInfo" );
// Write the creation time of the file
char szBuffer[1024];
_strdate(szBuffer);
info->setAttr( "CreationDate",szBuffer );
strcpy(szBuffer, LPCTSTR(m_IEditor->GetDocument()->GetTitle()));
PathRemoveExtension(szBuffer);
info->setAttr( "Name",szBuffer );
CHeightmap *heightmap = m_IEditor->GetHeightmap();
if (heightmap)
{
info->setAttr( "HeightmapSize",heightmap->GetWidth() );
info->setAttr( "HeightmapUnitSize",heightmap->GetUnitSize() );
info->setAttr( "WaterLevel",heightmap->GetWaterLevel() );
}
// Serialize surface types.
CXmlArchive xmlAr;
xmlAr.bLoading = false;
xmlAr.root = node;
m_IEditor->GetDocument()->SerializeSurfaceTypes( xmlAr );
}
//////////////////////////////////////////////////////////////////////////
bool CGameExporter::ExportSurfaceTexture( const CString &path,uint *pSurface,int iExportTexWidth,bool bHiQuality )
{
unsigned int iTexSX, iTexSY;
unsigned int iCurTexelX, iCurTexelY;
uint dwBlendColor,dwClr1, dwClr2;
uint iStartPosX, iStartPosY;
CHeightmap *heightmap = m_IEditor->GetHeightmap();
heightmap->SetSurfaceTextureSize(iExportTexWidth,iExportTexWidth);
SSectorInfo sectorInfo;
heightmap->GetSectorsInfo( sectorInfo );
int lSectorDimensions = sectorInfo.sectorTexSize;
m_IEditor->SetStatusText("Exporting surface texture (Saving)...");
CLogFile::WriteLine("Saving surface texture...");
// Fix seams on texture
for (iCurTexelY=1; iCurTexelY<iExportTexWidth; iCurTexelY++)
{
for (iCurTexelX=1; iCurTexelX<iExportTexWidth; iCurTexelX++)
{
// Horizontal seam ?
if (iCurTexelX % lSectorDimensions == 0)
{
// Get the two neighbor colors from the texture
dwClr1 = pSurface[iCurTexelX + iCurTexelY * iExportTexWidth];
dwClr2 = pSurface[iCurTexelX - 1 + iCurTexelY * iExportTexWidth];
// Blend the two colors
dwBlendColor = ((127 * (dwClr1 & 0x000000FF) + (dwClr2 & 0x000000FF) * 127) >> 8) |
(((127 * (dwClr1 & 0x0000FF00) >> 8) + ((dwClr2 & 0x0000FF00) >> 8) * 127) >> 8) << 8 |
(((127 * (dwClr1 & 0x00FF0000) >> 16) + ((dwClr2 & 0x00FF0000) >> 16) * 127) >> 8) << 16;
// Write the back
pSurface[iCurTexelX + iCurTexelY * iExportTexWidth] =
pSurface[iCurTexelX - 1 + iCurTexelY * iExportTexWidth] = dwBlendColor;
}
// Vertical seam ?
if (iCurTexelY % lSectorDimensions == 0)
{
// Get the two neighbor colors from the texture
dwClr1 = pSurface[iCurTexelX + iCurTexelY * iExportTexWidth];
dwClr2 = pSurface[iCurTexelX + iCurTexelY * iExportTexWidth - iExportTexWidth];
// Blend the two colors
dwBlendColor = ((127 * (dwClr1 & 0x000000FF) + (dwClr2 & 0x000000FF) * 127) >> 8) |
(((127 * (dwClr1 & 0x0000FF00) >> 8) + ((dwClr2 & 0x0000FF00) >> 8) * 127) >> 8) << 8 |
(((127 * (dwClr1 & 0x00FF0000) >> 16) + ((dwClr2 & 0x00FF0000) >> 16) * 127) >> 8) << 16;
// Write the back
pSurface[iCurTexelX + iCurTexelY * iExportTexWidth] =
pSurface[iCurTexelX + iCurTexelY * iExportTexWidth - iExportTexWidth] = dwBlendColor;
}
}
}
CTextureCompression texcomp;
CImage sector;
sector.Allocate( lSectorDimensions,lSectorDimensions );
CMemFile ctcFile;
ctcFile.Write( &lSectorDimensions,4 );
CWaitProgress progress( "Compressing Surface Texture" );
int counter = 0;
int numSectors = iExportTexWidth/lSectorDimensions;
CString ddsFilename;
// Write the texture to the file
for (iTexSY=0; iTexSY<iExportTexWidth / lSectorDimensions; iTexSY++)
{
// Calculate the start position in the array
iStartPosY = iTexSY * lSectorDimensions;
for (iTexSX=0; iTexSX<iExportTexWidth / lSectorDimensions; iTexSX++)
{
// Calculate the start position in the array
iStartPosX = iTexSX * lSectorDimensions;
// Write the sector dimensions
//file.Write( &lSectorDimensions,4 );
// Write sector.
for (iCurTexelY=0; iCurTexelY<lSectorDimensions; iCurTexelY++)
{
for (iCurTexelX=0; iCurTexelX<lSectorDimensions; iCurTexelX++)
{
// Write the texel to the file
sector.ValueAt(iCurTexelX,iCurTexelY) = pSurface[(iStartPosX + iCurTexelX)+(iStartPosY + iCurTexelY)*iExportTexWidth];
}
}
/*
// Save sector.
CMemFile ddsFile;
ddsFilename.Format( "%sTerrain\\Texture_%d.dds",(const char*)path,counter );
texcomp.CompressDDS( ddsFile,sector,bHiQuality );
m_texturePakFile.UpdateFile( ddsFilename,ddsFile );
*/
texcomp.CompressDXT1( ctcFile,sector,bHiQuality );
counter++;
if (!progress.Step( (counter*100)/(numSectors*numSectors) ))
return false;
/*
// Write the texture data to the file
for (iCurTexelY=0; iCurTexelY<lSectorDimensions; iCurTexelY++)
{
for (iCurTexelX=0; iCurTexelX<lSectorDimensions; iCurTexelX++)
{
// Write the texel to the file
file.Write( &pSurface[(iStartPosX + iCurTexelX) + (iStartPosY + iCurTexelY)*iExportTexWidth],3 );
}
}
*/
}
}
CString ctcFilename;
ctcFilename.Format( "%sTerrain\\cover.ctc",(const char*)path );
//m_texturePakFile.UpdateFile( ctcFilename,ctcFile );
m_levelPakFile.UpdateFile( ctcFilename,ctcFile,false ); // Do not compress this file.
return true;
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportAIGraph( const CString &path )
{
GetIEditor()->SetStatusText( _T("Exporting AI Graph...") );
char szLevel[1024];
char szMission[1024];
char fileName[1024];
char fileHideName[1024];
strcpy( szLevel,path );
strcpy( szMission, m_IEditor->GetDocument()->GetCurrentMission()->GetName() );
PathRemoveBackslash(szLevel);
sprintf( fileName,"%s\\net%s.bai",szLevel,szMission );
sprintf( fileHideName,"%s\\hide%s.bai",szLevel,szMission );
if (m_IEditor->GetSystem()->GetAISystem()->GetNodeGraph())
{
CLogFile::FormatLine( "Exporting AI Graph to %s",fileName );
m_IEditor->GetSystem()->GetAISystem()->GetNodeGraph()->WriteToFile( fileName );
CLogFile::FormatLine( "Exporting Hide Graph to %s",fileHideName );
m_IEditor->GetSystem()->GetAISystem()->GetHideGraph()->WriteToFile( fileHideName);
// Read theose files back and put them to Pak file.
CFile file;
if (file.Open( fileName,CFile::modeRead ))
{
CMemoryBlock mem;
mem.Allocate( file.GetLength() );
file.Read( mem.GetBuffer(),file.GetLength() );
file.Close();
m_levelPakFile.UpdateFile( fileName,mem );
DeleteFile( fileName );
}
if (file.Open( fileHideName,CFile::modeRead ))
{
CMemoryBlock mem;
mem.Allocate( file.GetLength() );
file.Read( mem.GetBuffer(),file.GetLength() );
file.Close();
m_levelPakFile.UpdateFile( fileHideName,mem );
DeleteFile( fileHideName );
}
}
CLogFile::WriteLine( "Exporting AI Graph done." );
//m_IEditor->GetSystem()->GetAISystem()->GetNodeGraph()->ReadFromFile( fileName );
//m_IEditor->GetSystem()->GetAISystem()->GetNodeGraph()->RemoveIndoorNodes();
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::FlattenHeightmap( uint16 *pSaveHeightmapData,int width,int height )
{
float fDist,fMaxDist;
int iX, iY;
float fAttenuation;
int i,j;
int iStartPosX, iStartPosY;
Vec3 op;
std::vector<CBaseObject*> objects;
m_IEditor->GetObjectManager()->GetObjects( objects );
// Mark holes and used flag.
for (j=0; j<height; j++)
{
for (i=0; i<width; i++)
{
// Clear inuse and hole bit.
pSaveHeightmapData[i + j * width] &= ~TERRAIN_BITMASK;
}
}
CHeightmap *pHeightmap = m_IEditor->GetHeightmap();
int unitSize = pHeightmap->GetUnitSize();
// Encoding reserved bit and flatten the ground in the marked area
// of map objects
for (i=0; i < objects.size(); i++)
{
CBaseObject *obj = objects[i];
int iEmptyRadius = ftoi(obj->GetArea()/2.0f);
// Skip if the object has no empty radius
if (iEmptyRadius == 0)
continue;
// Swap X/Y
op = obj->GetWorldPos();
float fZ = m_IEditor->GetTerrainElevation(op.x,op.y);
int objX = ftoi( op.y/unitSize );
int objY = ftoi( op.x/unitSize );
//m_vegetationMap->ClearObjects( CPoint(objX*UNIT_SIZE,objY*UNIT_SIZE),2*iEmptyRadius );
if (!obj->CheckFlags(OBJFLAG_FLATTEN))
continue;
// Calculate the maximum distance to be able to calculate attenuation
// inside the loop
fMaxDist = (float) sqrtf(iEmptyRadius*iEmptyRadius + iEmptyRadius*iEmptyRadius);
// Mark the area for this object
for (iStartPosX= -(iEmptyRadius + 15); iStartPosX<iEmptyRadius+15; iStartPosX++)
{
for (iStartPosY= -(iEmptyRadius + 15); iStartPosY<iEmptyRadius + 15; iStartPosY++)
{
// Calculate current position
iX = objX + iStartPosX;
iY = objY + iStartPosY;
// Skip invalid positions
if (iX < 0 || iY < 0 || iX >= width || iY >= height)
{
continue;
}
// Calclate the attenuation for the current distance
fDist = sqrtf((float) (abs(iStartPosX) * abs(iStartPosX) + abs(iStartPosY) * abs(iStartPosY)));
fDist = __max(0.0f, fDist - 15);
fAttenuation = 1.0f - __min(1.0f, fDist / fMaxDist);
// Make the area around the building flat
uint16 *src = &pSaveHeightmapData[iX+iY*width];
unsigned int h = ftoi(fAttenuation * (fZ * 256.0f) + (1.0f - fAttenuation) * (*src));
*src &= TERRAIN_BITMASK; // Leave only bitsflags.
*src |= h & ~(TERRAIN_BITMASK); // Combine height width flags.
if (fAttenuation < 0.3)
continue;
// Set the third bit
pHeightmap->InfoAt(iX,iY) &= ~HEIGHTMAP_INFO_SFTYPE_MASK;
}
}
}
// Mark holes and used flag.
for (j=0; j<height; j++)
{
for (i=0; i<width; i++)
{
unsigned char hinfo = pHeightmap->InfoAt(i,j);
uint sfType = ((hinfo&HEIGHTMAP_INFO_SFTYPE_MASK) >> HEIGHTMAP_INFO_SFTYPE_SHIFT);
pSaveHeightmapData[i + j*width] |= sfType;
if (hinfo&HEIGHTMAP_INFO_HOLE)
{
pSaveHeightmapData[i + j*width] |= SURFACE_TYPE_MASK;
}
if (hinfo&HEIGHTMAP_INFO_LIGHT)
{
pSaveHeightmapData[i + j*width] |= TERRAIN_LIGHT_BIT;
}
//pSaveHeightmapData[i + j*width] |= LIGHT_BIT;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportLowResSurfaceTexture( const char* pszGamePath,CImage &surfaceTexture )
{
char szLowLodFilePath[256];
sprintf(szLowLodFilePath, "%sTerrain\\cover_low.dds", pszGamePath);
if (!CFileUtil::OverwriteFile( szLowLodFilePath ))
return;
// Saving low-res version for terrain rendering optimizations
uint *pSurface = surfaceTexture.GetData();
uint iExportTexWidth = surfaceTexture.GetWidth();
SSectorInfo info;
GetIEditor()->GetHeightmap()->GetSectorsInfo(info);
int nTerrainSize = info.numSectors*info.sectorSize;
int k = 2*(iExportTexWidth/nTerrainSize);
if(k<4)
k=4;
int nTexSize = surfaceTexture.GetWidth()/k;
CLogFile::FormatLine("Saving lowres version of surface texture %dx%d...",nTexSize,nTexSize );
CImage lowres;
lowres.Allocate(nTexSize,nTexSize);
uchar *pData = (uchar*)lowres.GetData();
if (!pData)
return;
for(int x=0; x<nTexSize; x++)
{
for(int y=0; y<nTexSize; y++)
{
pData[(y*nTexSize+x)*4+0] = (pSurface[(x*k) + (y*k) * iExportTexWidth] & 0x000000FF) ;
pData[(y*nTexSize+x)*4+1] = (pSurface[(x*k) + (y*k) * iExportTexWidth] & 0x0000FF00) >> 8;
pData[(y*nTexSize+x)*4+2] = (pSurface[(x*k) + (y*k) * iExportTexWidth] & 0x00FF0000) >> 16;
pData[(y*nTexSize+x)*4+3] = 255;
}
}
CMemFile ddsFile;
CTextureCompression texcomp;
texcomp.CompressDDS( ddsFile,lowres,true );
//m_texturePakFile.UpdateFile( szLowLodFilePath,ddsFile );
m_levelPakFile.UpdateFile( szLowLodFilePath,ddsFile );
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportBrushes( const CString &path )
{
GetIEditor()->SetStatusText( _T("Exporting Brushes...") );
CBrushExporter brushExport;
brushExport.ExportBrushes( path,m_levelPath,m_levelPakFile );
}
void CGameExporter::ForceDeleteFile( const char *filename )
{
SetFileAttributes( filename,FILE_ATTRIBUTE_NORMAL );
DeleteFile( filename );
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::DeleteOldFiles( const CString &levelDir,bool bSurfaceTexture )
{
ForceDeleteFile( levelDir + "brush.lst" );
ForceDeleteFile( levelDir + "particles.lst" );
ForceDeleteFile( levelDir + "LevelData.xml" );
ForceDeleteFile( levelDir + "MovieData.xml" );
ForceDeleteFile( levelDir + "objects.lst" );
ForceDeleteFile( levelDir + "objects.idx" );
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportTerrainBeaches( const CString &path )
{
/*
CMemFile file;
CTerrainBeachGenerator terrGen( GetIEditor()->GetHeightmap() );
terrGen.Generate( file );
m_levelPakFile.UpdateFile( path + "Terrain\\beach7.tmp",file );
*/
////////////////////////////////////////////////////////////////////////
// Recalculate shore geometry and save it to disk
////////////////////////////////////////////////////////////////////////
m_I3DEngine->RecompileBeaches();
// Read file back and put to pack file and delete from disk.
CString filename = Path::AddBackslash(path) + TERRAIN_BEACHES_FILENAME;
CFile file;
if (file.Open( filename,CFile::modeRead ))
{
CMemoryBlock mem;
mem.Allocate( file.GetLength() );
file.Read( mem.GetBuffer(),file.GetLength() );
m_levelPakFile.UpdateFile( filename,mem );
file.Close();
DeleteFile( filename );
}
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportMusic( XmlNodeRef &levelDataNode,const CString &path )
{
// Export music manager.
CMusicManager *pMusicManager = GetIEditor()->GetMusicManager();
pMusicManager->Export( levelDataNode );
CString musicPath = Path::AddBackslash(path) + "Music";
CString filename = Path::Make( musicPath,MUSIC_LEVEL_LIBRARY_FILE );
bool bEmptyLevelLib = true;
XmlNodeRef nodeLib = new CXmlNode( "MusicThemeLibrary" );
// Export Music local level library.
for (int i = 0; i < pMusicManager->GetLibraryCount(); i++)
{
IDataBaseLibrary *pLib = pMusicManager->GetLibrary(i);
if (pLib->IsLevelLibrary())
{
if (pLib->GetItemCount() > 0)
{
bEmptyLevelLib = false;
// Export this library.
pLib->Serialize( nodeLib,false );
}
}
else
{
if (pLib->GetItemCount() > 0)
{
// Export this library to pak file.
XmlNodeRef nodeLib = new CXmlNode( "MusicThemeLibrary" );
pLib->Serialize( nodeLib,false );
CMemFile file;
XmlString xmlData = nodeLib->getXML();
file.Write( xmlData.c_str(),xmlData.length() );
CString filename = Path::Make( musicPath,Path::GetFile(pLib->GetFilename()) );
m_levelPakFile.UpdateFile( filename,file );
}
}
}
if (!bEmptyLevelLib)
{
XmlString xmlData = nodeLib->getXML();
CMemFile file;
file.Write( xmlData.c_str(),xmlData.length() );
m_levelPakFile.UpdateFile( filename,file );
}
else
{
m_levelPakFile.RemoveFile( filename );
}
}
//////////////////////////////////////////////////////////////////////////
void CGameExporter::ExportMaterials( XmlNodeRef &levelDataNode,const CString &path )
{
//////////////////////////////////////////////////////////////////////////
// Export materials manager.
CMaterialManager *pManager = GetIEditor()->GetMaterialManager();
pManager->Export( levelDataNode );
CString filename = Path::Make( path,MATERIAL_LEVEL_LIBRARY_FILE );
bool bHaveItems = true;
int numMtls = 0;
XmlNodeRef nodeMaterials = new CXmlNode( "MaterialsLibrary" );
// Export Materials local level library.
for (int i = 0; i < pManager->GetLibraryCount(); i++)
{
XmlNodeRef nodeLib = nodeMaterials->newChild( "Library" );
CMaterialLibrary *pLib = (CMaterialLibrary*)pManager->GetLibrary(i);
if (pLib->GetItemCount() > 0)
{
bHaveItems = false;
// Export this library.
numMtls += pManager->ExportLib( pLib,nodeLib );
}
}
if (!bHaveItems)
{
XmlString xmlData = nodeMaterials->getXML();
CMemFile file;
file.Write( xmlData.c_str(),xmlData.length() );
m_levelPakFile.UpdateFile( filename,file );
}
else
{
m_levelPakFile.RemoveFile( filename );
}
m_numExportedMaterials = numMtls;
}