khanat-opennel-code/code/nel/tools/3d/zone_lighter/zone_lighter.cpp

936 lines
28 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "nel/misc/stream.h"
#include "nel/misc/file.h"
#include "nel/misc/vector.h"
#include "nel/misc/time_nl.h"
#include "nel/misc/config_file.h"
#include "nel/misc/path.h"
#include "nel/georges/u_form.h"
#include "nel/georges/u_form_elm.h"
#include "nel/georges/u_form_loader.h"
#include "nel/3d/zone.h"
#include "nel/3d/zone_lighter.h"
#include "nel/3d/quad_grid.h"
#include "nel/3d/landscape.h"
#include "nel/3d/scene_group.h"
#include "nel/3d/shape.h"
#include "nel/3d/transform_shape.h"
#include "nel/3d/register_3d.h"
#include "nel/3d/water_shape.h"
#include "../zone_lib/zone_utility.h"
using namespace std;
using namespace NLMISC;
using namespace NL3D;
#define BAR_LENGTH 21
const char *progressbar[BAR_LENGTH]=
{
"[ ]",
"[. ]",
"[.. ]",
"[... ]",
"[.... ]",
"[..... ]",
"[...... ]",
"[....... ]",
"[........ ]",
"[......... ]",
"[.......... ]",
"[........... ]",
"[............ ]",
"[............. ]",
"[.............. ]",
"[............... ]",
"[................ ]",
"[................. ]",
"[.................. ]",
"[................... ]",
"[....................]"
};
// My zone lighter
class CMyZoneLighter : public CZoneLighter
{
// Progress bar
virtual void progress (const char *message, float progress)
{
// Progress bar
char msg[512];
uint pgId= (uint)(progress*(float)BAR_LENGTH);
pgId= min(pgId, (uint)(BAR_LENGTH-1));
sprintf (msg, "\r%s: %s", message, progressbar[pgId]);
uint i;
for (i=(uint)strlen(msg); i<79; i++)
msg[i]=' ';
msg[i]=0;
printf ("%s\r", msg);
}
};
struct CInstanceGroupRef
{
CInstanceGroup *IG;
bool AddLight;
};
//=======================================================================================
// load additionnal ig from a village (ryzom specific)
static void loadIGFromVillage(const NLGEORGES::UFormElm *villageItem, const std::string &continentName,
uint villageIndex, std::list<CInstanceGroupRef> &instanceGroups, CConfigFile::CVar &additionalIgNames)
{
const NLGEORGES::UFormElm *igNamesItem;
if (! (villageItem->getNodeByName (&igNamesItem, "IgList") && igNamesItem) )
{
nlwarning("No list of IGs was found in the continent form %s, village #%d", continentName.c_str(), villageIndex);
return;
}
// Get number of village
uint numIgs;
nlverify (igNamesItem->getArraySize (numIgs));
const NLGEORGES::UFormElm *currIg;
for(uint l = 0; l < numIgs; ++l)
{
if (!(igNamesItem->getArrayNode (&currIg, l) && currIg))
{
nlwarning("Couldn't get ig #%d in the continent form %s, in village #%d", l, continentName.c_str(), villageIndex);
continue;
}
const NLGEORGES::UFormElm *igNameItem;
currIg->getNodeByName (&igNameItem, "IgName");
std::string igName;
if (!igNameItem->getValue (igName))
{
nlwarning("Couldn't get ig name of ig #%d in the continent form %s, in village #%d", l, continentName.c_str(), villageIndex);
continue;
}
if (igName.empty())
{
nlwarning("Ig name of ig #%d in the continent form %s, in village #%d is an empty string", l, continentName.c_str(), villageIndex);
continue;
}
// ensure .ig
igName = CFile::getFilenameWithoutExtension(igName) + ".ig";
// verify that the ig is not already added (case of tr_water.ig in additional_igs)
for(uint igAdd= 0;igAdd<(uint)additionalIgNames.size();igAdd++)
{
if( toLower(additionalIgNames.asString()) == toLower(igName) )
{
nlwarning("Skipping Village Ig %s, cause already exist in additional ig", igName.c_str());
continue;
}
}
// add this ig
string nameLookup = CPath::lookup (igName, false, true);
if (!nameLookup.empty())
{
CIFile inputFile;
// Try to open the file
if (inputFile.open (nameLookup))
{
// New ig
std::auto_ptr<CInstanceGroup> group(new CInstanceGroup);
try
{
group->serial (inputFile);
}
catch(NLMISC::Exception &)
{
nlwarning ("Error while loading instance group %s\n", igName.c_str());
continue;
}
inputFile.close();
// Add to the list
CInstanceGroupRef iref;
iref.IG = group.release();
iref.AddLight = false;
instanceGroups.push_back (iref);
}
else
{
// Error
nlwarning ("Can't open instance group %s\n", igName.c_str());
}
}
}
}
//=======================================================================================
// load additionnal ig from a continent (ryzom specific)
static void loadIGFromContinent(NLMISC::CConfigFile &parameter, std::list<CInstanceGroupRef> &instanceGroups,
const std::vector<std::string> &zoneNameArray,
CConfigFile::CVar &additionalIgNames)
{
try
{
CConfigFile::CVar &continent_name_var = parameter.getVar ("continent_name");
CConfigFile::CVar &level_design_directory = parameter.getVar ("level_design_directory");
CConfigFile::CVar &level_design_world_directory = parameter.getVar ("level_design_world_directory");
CConfigFile::CVar &level_design_dfn_directory = parameter.getVar ("level_design_dfn_directory");
CPath::addSearchPath(level_design_dfn_directory.asString(), true, false);
CPath::addSearchPath(level_design_world_directory.asString(), true, false);
std::string continentName = continent_name_var.asString();
if (CFile::getExtension(continentName).empty())
continentName += ".continent";
// Load the form
NLGEORGES::UFormLoader *loader = NLGEORGES::UFormLoader::createLoader();
//
std::string pathName = level_design_world_directory.asString() + "/" + continentName;
if (pathName.empty())
{
nlwarning("Can't find continent form : %s", continentName.c_str());
return;
}
NLGEORGES::UForm *villageForm;
villageForm = loader->loadForm(pathName.c_str());
if(villageForm != NULL)
{
NLGEORGES::UFormElm &rootItem = villageForm->getRootNode();
// try to get the village list
// Load the village list
NLGEORGES::UFormElm *villagesItem;
if(!(rootItem.getNodeByName (&villagesItem, "Villages") && villagesItem))
{
nlwarning("No villages where found in %s", continentName.c_str());
return;
}
// Get number of village
uint numVillage;
nlverify (villagesItem->getArraySize (numVillage));
// For each village
for(uint k = 0; k < numVillage; ++k)
{
NLGEORGES::UFormElm *currVillage;
if (!(villagesItem->getArrayNode (&currVillage, k) && currVillage))
{
nlwarning("Couldn't get village %d in continent %s", continentName.c_str(), k);
continue;
}
// check that this village is in the dependency zones
NLGEORGES::UFormElm *zoneNameItem;
if (!currVillage->getNodeByName (&zoneNameItem, "Zone") && zoneNameItem)
{
nlwarning("Couldn't get zone item of village %d in continent %s", continentName.c_str(), k);
continue;
}
std::string zoneName;
if (!zoneNameItem->getValue(zoneName))
{
nlwarning("Couldn't get zone name of village %d in continent %s", continentName.c_str(), k);
continue;
}
zoneName = CFile::getFilenameWithoutExtension(zoneName);
for(uint l = 0; l < zoneNameArray.size(); ++l)
{
if (NLMISC::nlstricmp(CFile::getFilenameWithoutExtension(zoneNameArray[l]), zoneName) == 0)
{
// ok, it is in the dependant zones
loadIGFromVillage(currVillage, continentName, k, instanceGroups, additionalIgNames);
break;
}
}
}
}
else
{
nlwarning("Can't load continent form : %s", continentName.c_str());
}
}
catch (NLMISC::EUnknownVar &e)
{
nlinfo(e.what());
}
}
//=======================================================================================
int main(int argc, char* argv[])
{
// Start time
TTime time=CTime::getLocalTime ();
// Filter addSearchPath
NLMISC::createDebug();
InfoLog->addNegativeFilter ("adding the path");
WarningLog->addNegativeFilter ("continent.cfg");
// Register 3d
registerSerial3d ();
// Good number of args ?
if (argc<5)
{
// Help message
printf ("%s [zonein.zone] [zoneout.zone] [parameter_file] [dependancy_file] [-waterpatch bkupdir] \n", argv[0]);
}
else
{
// to patch only the tiles flags
bool tileWaterPatchOnly;
tileWaterPatchOnly= argc==7 && string(argv[5])=="-waterpatch";
string tileWaterPatchBkupDir;
if(tileWaterPatchOnly)
tileWaterPatchBkupDir = argv[6];
// Ok, read the zone
CIFile inputFile;
// Get extension
string ext=getExt (argv[1]);
string dir=getDir (argv[1]);
// Open it for reading
if (inputFile.open (argv[1]))
{
// Zone name
string zoneName=toLower (string ("zone_"+getName (argv[1])));
// Load the zone
try
{
// Read the config file
CConfigFile parameter;
CConfigFile dependency;
// Load and parse the dependency file
parameter.load (argv[3]);
// **********
// *** Build the lighter descriptor
// **********
CZoneLighter::CLightDesc lighterDesc;
// Get bank name
CConfigFile::CVar &bank_name = parameter.getVar ("bank_name");
// Load instance group ?
CConfigFile::CVar &load_ig= parameter.getVar ("load_ig");
bool loadInstanceGroup = load_ig.asInt ()!=0;
CConfigFile::CVar &additionnal_ig = parameter.getVar ("additionnal_ig");
// Grid size
CConfigFile::CVar &quad_grid_size = parameter.getVar ("quad_grid_size");
lighterDesc.GridSize=quad_grid_size.asInt();
// Grid size
CConfigFile::CVar &quad_grid_cell_size = parameter.getVar ("quad_grid_cell_size");
lighterDesc.GridCellSize=quad_grid_cell_size.asFloat();
// Heightfield cell size
CConfigFile::CVar &global_illumination_cell_size = parameter.getVar ("global_illumination_cell_size");
lighterDesc.HeightfieldCellSize=global_illumination_cell_size.asFloat();
// Heightfield size
CConfigFile::CVar &global_illumination_length = parameter.getVar ("global_illumination_length");
lighterDesc.HeightfieldSize=global_illumination_length.asFloat();
// Light direction
CConfigFile::CVar &sun_direction = parameter.getVar ("sun_direction");
lighterDesc.SunDirection.x=sun_direction.asFloat(0);
lighterDesc.SunDirection.y=sun_direction.asFloat(1);
lighterDesc.SunDirection.z=sun_direction.asFloat(2);
lighterDesc.SunDirection.normalize ();
// Light center position
CConfigFile::CVar &sun_center = parameter.getVar ("sun_center");
lighterDesc.SunCenter.x=sun_center.asFloat(0);
lighterDesc.SunCenter.y=sun_center.asFloat(1);
lighterDesc.SunCenter.z=sun_center.asFloat(2);
// Light distance
CConfigFile::CVar &sun_distance = parameter.getVar ("sun_distance");
lighterDesc.SunDistance=sun_distance.asFloat();
// Light FOV
CConfigFile::CVar &sun_fov = parameter.getVar ("sun_fov");
lighterDesc.SunFOV=sun_fov.asFloat();
// Light radius
CConfigFile::CVar &sun_radius = parameter.getVar ("sun_radius");
lighterDesc.SunRadius=sun_radius.asFloat();
// ZBuffer landscape size
CConfigFile::CVar &zbuffer_landscape_size = parameter.getVar ("zbuffer_landscape_size");
lighterDesc.ZBufferLandscapeSize=zbuffer_landscape_size.asInt();
// ZBuffer object size
CConfigFile::CVar &zbuffer_object_size = parameter.getVar ("zbuffer_object_size");
lighterDesc.ZBufferObjectSize=zbuffer_object_size.asInt();
// Soft shadow samples sqrt
CConfigFile::CVar &soft_shadow_samples_sqrt = parameter.getVar ("soft_shadow_samples_sqrt");
lighterDesc.SoftShadowSamplesSqrt=soft_shadow_samples_sqrt.asInt();
// Soft shadow jitter
CConfigFile::CVar &soft_shadow_jitter = parameter.getVar ("soft_shadow_jitter");
lighterDesc.SoftShadowJitter=soft_shadow_jitter.asFloat();
// Water rendering parameters
CConfigFile::CVar &water_zbias = parameter.getVar ("water_shadow_bias");
lighterDesc.WaterShadowBias = water_zbias.asFloat();
CConfigFile::CVar &water_ambient = parameter.getVar ("water_ambient");
lighterDesc.WaterAmbient = water_ambient.asFloat();
CConfigFile::CVar &water_diffuse = parameter.getVar ("water_diffuse");
lighterDesc.WaterDiffuse = water_diffuse.asFloat();
CConfigFile::CVar &modulate_water_color = parameter.getVar ("modulate_water_color");
lighterDesc.ModulateWaterColor = modulate_water_color.asInt() != 0;
CConfigFile::CVar &sky_contribution_for_water = parameter.getVar ("sky_contribution_for_water");
lighterDesc.SkyContributionForWater = sky_contribution_for_water.asInt() != 0;
// Number of CPU
CConfigFile::CVar &cpu_num = parameter.getVar ("cpu_num");
lighterDesc.NumCPU=cpu_num.asInt ();
// Sun contribution
CConfigFile::CVar &sun_contribution = parameter.getVar ("sun_contribution");
lighterDesc.SunContribution=sun_contribution.asInt ()!=0;
// Shadows enabled ?
CConfigFile::CVar &shadow = parameter.getVar ("shadow");
lighterDesc.Shadow=shadow.asInt ()!=0;
// Sky contribution
CConfigFile::CVar &sky_contribution = parameter.getVar ("sky_contribution");
lighterDesc.SkyContribution=sky_contribution.asInt ()!=0;
// Sky contribution
CConfigFile::CVar &sky_intensity = parameter.getVar ("sky_intensity");
lighterDesc.SkyIntensity=sky_intensity.asFloat ();
// Vegetable Height
CConfigFile::CVar &vegetable_height = parameter.getVar ("vegetable_height");
lighterDesc.VegetableHeight=vegetable_height.asFloat ();
// Shadow are enabled ?
if (lighterDesc.Shadow)
{
// Load and parse the dependency file
dependency.load (argv[4]);
}
// Get the search pathes
CConfigFile::CVar &search_pathes = parameter.getVar ("search_pathes");
uint path;
for (path = 0; path < (uint)search_pathes.size(); path++)
{
// Add to search path
CPath::addSearchPath (search_pathes.asString(path));
}
// A landscape allocated with new: it is not delete because destruction take 3 secondes more!
CLandscape *landscape=new CLandscape;
landscape->init();
// A zone lighter
CMyZoneLighter lighter;
lighter.init ();
// A vector of zone id
vector<uint> listZoneId;
// The zone
CZone zone;
// List of ig
std::list<CInstanceGroupRef> instanceGroup;
// Load
zone.serial (inputFile);
inputFile.close();
bool zoneIgLoaded = false;
// Load ig of the zone
string igName = getName (argv[1])+".ig";
string igNameLookup = CPath::lookup (igName, false, false);
if (!igNameLookup.empty())
igName = igNameLookup;
if (inputFile.open (igName))
{
// New ig
CInstanceGroup *group=new CInstanceGroup;
// Serial it
group->serial (inputFile);
inputFile.close();
// Add to the list
CInstanceGroupRef iref;
iref.IG = group;
iref.AddLight = true;
instanceGroup.push_back (iref);
zoneIgLoaded = true;
}
else
{
// Warning
fprintf (stderr, "Warning: can't load instance group %s\n", igName.c_str());
zoneIgLoaded = false;
}
// Load the bank
string bankName = bank_name.asString();
string bankNameLookup = CPath::lookup (bankName, false, false);
if (!bankNameLookup.empty())
bankName = bankNameLookup;
if (inputFile.open (bankName))
{
try
{
// Load
landscape->TileBank.serial (inputFile);
landscape->initTileBanks();
}
catch (Exception &e)
{
// Error
nlwarning ("ERROR error loading tile bank %s\n%s\n", bankName.c_str(), e.what());
}
}
else
{
// Error
nlwarning ("ERROR can't load tile bank %s\n", bankName.c_str());
}
// Add the zone
landscape->addZone (zone);
listZoneId.push_back (zone.getZoneId());
// Continue to build ?
bool continu=true;
// Try to load additionnal instance group.
// Additionnal instance group
if (loadInstanceGroup)
{
try
{
for (uint add=0; add<(uint)additionnal_ig.size(); add++)
{
// Input file
CIFile inputFile;
// Name of the instance group
string name = additionnal_ig.asString(add);
string nameLookup = CPath::lookup (name, false, false);
if (!nameLookup.empty())
name = nameLookup;
// Try to open the file
if (inputFile.open (name))
{
// New ig
CInstanceGroup *group=new CInstanceGroup;
// Serial it
group->serial (inputFile);
inputFile.close();
// Add to the list
CInstanceGroupRef iref;
iref.IG = group;
iref.AddLight = false;
instanceGroup.push_back (iref);
}
else
{
// Error
nlwarning ("ERROR can't load instance group %s\n", name.c_str());
// Stop before build
continu=false;
}
}
}
catch (NLMISC::EUnknownVar &)
{
nlinfo("No additionnal ig's to load");
}
}
// *** Scan dependency file
if (lighterDesc.Shadow)
{
CConfigFile::CVar &dependant_zones = dependency.getVar ("dependencies");
std::vector<std::string> zoneNameArray;
zoneNameArray.reserve(1 + (uint)dependant_zones.size());
zoneNameArray.push_back(argv[1]);
for (uint i=0; i<(uint)dependant_zones.size(); i++)
{
// Get zone name
string zoneName=dependant_zones.asString(i);
zoneNameArray.push_back(zoneName);
// Load the zone
CZone zoneBis;
// Open it for reading
if (inputFile.open (dir+zoneName+ext))
{
// Read it
zoneBis.serial (inputFile);
inputFile.close();
// Add the zone
landscape->addZone (zoneBis);
listZoneId.push_back (zoneBis.getZoneId());
}
else
{
// Error message and continue
nlwarning ("ERROR can't load zone %s\n", (dir+zoneName+ext).c_str());
}
// Try to load an instance group.
if (loadInstanceGroup)
{
string name = zoneName+".ig";
string nameLookup = CPath::lookup (name, false, false);
if (!nameLookup.empty())
name = nameLookup;
// Name of the instance group
if (inputFile.open (name))
{
// New ig
CInstanceGroup *group=new CInstanceGroup;
// Serial it
group->serial (inputFile);
inputFile.close();
// Add to the list
CInstanceGroupRef iref;
iref.IG = group;
iref.AddLight = true;
instanceGroup.push_back (iref);
}
else
{
// Error message and continue
nlwarning ("WARNING can't load instance group %s\n", name.c_str());
}
}
}
if (loadInstanceGroup)
{
// Ryzom specific : additionnal villages from a continent form
loadIGFromContinent(parameter, instanceGroup, zoneNameArray, additionnal_ig);
}
}
// A vector of CZoneLighter::CTriangle
vector<CZoneLighter::CTriangle> vectorTriangle;
// **********
// *** Build triangle array
// **********
landscape->checkBinds ();
// Add triangles from landscape
landscape->enableAutomaticLighting (false);
// no need to add obstacles in case of water patch
if(!tileWaterPatchOnly)
lighter.addTriangles (*landscape, listZoneId, 0, vectorTriangle);
// Map of shape
std::map<string, IShape*> shapeMap;
// For each instance group
std::list<CInstanceGroupRef>::iterator ite=instanceGroup.begin();
while (ite!=instanceGroup.end())
{
// Instance group
CInstanceGroup *group=ite->IG;
// Load and add shapes
if (lighterDesc.Shadow)
{
// For each instance
for (uint instance=0; instance<group->getNumInstance(); instance++)
{
// Get the instance shape name
string name=group->getShapeName (instance);
if (!name.empty())
{
// Skip it?? use the DontCastShadowForExterior flag. See doc of this flag
if(group->getInstance(instance).DontCastShadow || group->getInstance(instance).DontCastShadowForExterior)
continue;
// PS ?
if (toLower (CFile::getExtension (name)) == "ps")
continue;
// Add a .shape at the end ?
if (name.find('.') == std::string::npos)
name += ".shape";
// Add path
string nameLookup = CPath::lookup (name, false, false);
if (!nameLookup.empty())
name = nameLookup;
// Find the shape in the bank
std::map<string, IShape*>::iterator iteMap=shapeMap.find (name);
if (iteMap==shapeMap.end())
{
// Input file
CIFile inputFile;
if (inputFile.open (name))
{
// Load it
CShapeStream stream;
stream.serial (inputFile);
// Get the pointer
iteMap=shapeMap.insert (std::map<string, IShape*>::value_type (name, stream.getShapePointer ())).first;
}
else
{
// Error
nlwarning ("WARNING can't load shape %s\n", name.c_str());
}
}
// Loaded ?
if (iteMap!=shapeMap.end())
{
// Build the matrix
CMatrix scale;
scale.identity ();
scale.scale (group->getInstanceScale (instance));
CMatrix rot;
rot.identity ();
rot.setRot (group->getInstanceRot (instance));
CMatrix pos;
pos.identity ();
pos.setPos (group->getInstancePos (instance));
CMatrix mt=pos*rot*scale;
// Add triangles
// no need to add obstacles in case of water patch
if(!tileWaterPatchOnly)
lighter.addTriangles (*iteMap->second, mt, vectorTriangle);
/** If it is a lightable shape and we are dealing with the ig of the main zone,
* add it to the lightable shape list
*/
IShape *shape = iteMap->second;
if (ite == instanceGroup.begin() /* are we dealing with main zone */
&& zoneIgLoaded /* ig of the main zone loaded successfully (so its indeed the ig of the first zone) ? */
&& CZoneLighter::isLightableShape(*shape)
&& !tileWaterPatchOnly
)
{
lighter.addLightableShape(shape, mt);
}
/** If it is a water shape, add it to the lighter, so that it can check
* which tiles are above / below water for this zone. The result is saved in the flags of tiles.
* A tile that have their flags set to VegetableDisabled won't get setupped
*/
if (dynamic_cast<NL3D::CWaterShape *>(shape))
{
lighter.addWaterShape(static_cast<NL3D::CWaterShape *>(shape), mt);
}
}
}
}
}
// For each point light of the ig. No need wor tileWaterPatchOnly
if (ite->AddLight && !tileWaterPatchOnly)
{
const std::vector<CPointLightNamed> &pointLightList= group->getPointLightList();
for (uint plId=0; plId<pointLightList.size(); plId++)
{
// Add it to the Ig.
lighter.addStaticPointLight(pointLightList[plId]);
}
}
// Next instance group
ite++;
}
// If no waterpatch, and no WaterShape at all, no op
bool tileWaterSkip= false;
if(continu && tileWaterPatchOnly && lighter.getNumWaterShape()==0)
{
nlinfo("NO WATER INTERSECTION FOUND: don't patch at all");
continu= false;
tileWaterSkip= true;
}
// Continue ?
if (continu)
{
// **********
// *** Light!
// **********
// Output zone
CZone output;
// normal lighting
if(!tileWaterPatchOnly)
{
// Light the zone
lighter.light (*landscape, output, zone.getZoneId(), lighterDesc, vectorTriangle, listZoneId);
}
else
{
// Load the zonel.
CIFile zonelFile;
// load the zonel (keep lighting)
if (zonelFile.open (argv[2]))
{
// load the new zone
try
{
// load it
output.serial (zonelFile);
}
catch (Exception& except)
{
// Error message
nlwarning ("ERROR reading %s: %s\n", argv[2], except.what());
throw;
}
}
else
{
// Error can't open the file
nlwarning ("ERROR Can't open %s for reading\n", argv[1]);
throw Exception("ERROR Can't open the zonel for tile water patching. abort");
}
zonelFile.close();
// Bkup the zone
COFile outputFile;
string bkupFile= tileWaterPatchBkupDir + "/" + CFile::getFilename(argv[2]);
if (outputFile.open (bkupFile))
{
try
{
output.serial (outputFile);
}
catch (Exception& except)
{
nlwarning ("ERROR backuping %s: %s\n", bkupFile.c_str(), except.what());
}
outputFile.close();
}
else
nlwarning ("ERROR Can't open %s for writing\n", bkupFile.c_str());
// patch water flags
lighter.computeTileFlagsOnly(*landscape, output, zone.getZoneId(), lighterDesc, listZoneId);
}
// Save the zone
COFile outputFile;
// Open it
if (outputFile.open (argv[2]))
{
// Save the new zone
try
{
// Save it
output.serial (outputFile);
}
catch (Exception& except)
{
// Error message
nlwarning ("ERROR writing %s: %s\n", argv[2], except.what());
}
}
else
{
// Error can't open the file
nlwarning ("ERROR Can't open %s for writing\n", argv[2]);
}
// Compute time
printf ("\rCompute time: %d ms \r",
(uint)(CTime::getLocalTime ()-time));
}
else
{
if(!tileWaterSkip)
// Error
nlwarning ("ERROR Abort: files are missing.\n");
}
}
catch (Exception& except)
{
// Error message
nlwarning ("ERROR %s\n", except.what());
}
}
else
{
// Error can't open the file
nlwarning ("ERROR Can't open %s for reading\n", argv[1]);
}
}
// Landscape is not deleted, nor the instanceGroups, for faster quit.
// Must disalbe BlockMemory checks (for pointLights).
NL3D_BlockMemoryAssertOnPurge= false;
// Compute time
printf ("\rCompute time: %d ms \n",
(uint)(CTime::getLocalTime ()-time));
// exit.
return 0;
}