mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-23 17:38:44 +00:00
994 lines
28 KiB
C++
994 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 <string>
|
||
|
#include <deque>
|
||
|
|
||
|
#include <nel/misc/types_nl.h>
|
||
|
#include <nel/misc/config_file.h>
|
||
|
#include <nel/misc/debug.h>
|
||
|
#include <nel/misc/path.h>
|
||
|
#include <nel/misc/i18n.h>
|
||
|
|
||
|
#include <nel/3d/driver.h>
|
||
|
#include <nel/3d/camera.h>
|
||
|
#include <nel/3d/landscape_model.h>
|
||
|
#include <nel/3d/landscape.h>
|
||
|
#include <nel/3d/text_context.h>
|
||
|
#include <nel/3d/mini_col.h>
|
||
|
#include <nel/3d/nelu.h>
|
||
|
#include <nel/3d/scene_group.h>
|
||
|
#include <nel/3d/texture_file.h>
|
||
|
|
||
|
//#include "nel/net/local_entity.h"
|
||
|
|
||
|
#include "move_listener.h"
|
||
|
|
||
|
// Tempyoyo.
|
||
|
#include <nel/3d/height_map.h>
|
||
|
|
||
|
using namespace std;
|
||
|
using namespace NLMISC;
|
||
|
using namespace NL3D;
|
||
|
|
||
|
|
||
|
#define BANK_PAH_RELATIVE
|
||
|
|
||
|
#ifndef NL_ZVIEWER_CFG
|
||
|
#define NL_ZVIEWER_CFG "."
|
||
|
#endif // NL_ZVIEWER_CFG
|
||
|
|
||
|
/**
|
||
|
* CViewerConfig
|
||
|
*/
|
||
|
struct CViewerConfig
|
||
|
{
|
||
|
bool Windowed;
|
||
|
uint Width;
|
||
|
uint Height;
|
||
|
uint Depth;
|
||
|
CVector Position;
|
||
|
CVector Heading;
|
||
|
CVector EyesHeight;
|
||
|
CRGBA Background;
|
||
|
|
||
|
// Landscape
|
||
|
bool AutoLight;
|
||
|
CVector LightDir;
|
||
|
string ZonesPath;
|
||
|
string BanksPath;
|
||
|
string TilesPath;
|
||
|
bool UseDDS;
|
||
|
bool AllPathRelative;
|
||
|
|
||
|
string IgPath;
|
||
|
string ShapePath;
|
||
|
string MapsPath;
|
||
|
string Bank;
|
||
|
string FontPath;
|
||
|
CTextContext TextContext;
|
||
|
CFontManager FontManager;
|
||
|
float ZFar;
|
||
|
float LandscapeTileNear;
|
||
|
float LandscapeThreshold;
|
||
|
bool LandscapeNoise;
|
||
|
vector<string> Zones;
|
||
|
vector<string> Igs;
|
||
|
|
||
|
// HeightField.
|
||
|
string HeightFieldName;
|
||
|
float HeightFieldMaxZ;
|
||
|
float HeightFieldOriginX;
|
||
|
float HeightFieldOriginY;
|
||
|
float HeightFieldSizeX;
|
||
|
float HeightFieldSizeY;
|
||
|
|
||
|
// StaticLight
|
||
|
CRGBA LandAmbient;
|
||
|
CRGBA LandDiffuse;
|
||
|
|
||
|
CViewerConfig()
|
||
|
{
|
||
|
Windowed = true;
|
||
|
Width = 800;
|
||
|
Height = 600;
|
||
|
Depth = 32;
|
||
|
Position = CVector( 1088.987793f, -925.732178f, 0.0f );
|
||
|
Heading = CVector(0,1,0);
|
||
|
EyesHeight = CVector(0,0,1.8f);
|
||
|
Background = CRGBA(100,100,255);
|
||
|
AutoLight = false;
|
||
|
LightDir = CVector (1, 0, 0);
|
||
|
ZonesPath = "./";
|
||
|
BanksPath = "./";
|
||
|
TilesPath = "./";
|
||
|
UseDDS = false;
|
||
|
AllPathRelative = false;
|
||
|
IgPath = "./";
|
||
|
ShapePath = "./";
|
||
|
MapsPath = "./";
|
||
|
Bank = "bank.bank";
|
||
|
FontPath = "\\\\server\\code\\fonts\\arialuni.ttf";
|
||
|
ZFar = 1000;
|
||
|
LandscapeTileNear = 50.0f;
|
||
|
LandscapeThreshold = 0.001f;
|
||
|
LandscapeNoise = true;
|
||
|
|
||
|
HeightFieldName= "";
|
||
|
HeightFieldMaxZ= 100;
|
||
|
HeightFieldOriginX= 16000;
|
||
|
HeightFieldOriginY= -24000;
|
||
|
HeightFieldSizeX= 160;
|
||
|
HeightFieldSizeY= 160;
|
||
|
|
||
|
CRGBA diffuse (241, 226, 244);
|
||
|
CRGBA ambiant (17, 54, 100);
|
||
|
LandDiffuse= diffuse;
|
||
|
LandAmbient= ambiant;
|
||
|
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CViewerConfig ViewerCfg;
|
||
|
|
||
|
|
||
|
|
||
|
CLandscapeModel *Landscape = NULL;
|
||
|
CMoveListener MoveListener;
|
||
|
CMiniCol CollisionManager;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*******************************************************************\
|
||
|
getZoneNameByCoord()
|
||
|
\*******************************************************************/
|
||
|
string getZoneNameByCoord(float x, float y)
|
||
|
{
|
||
|
const float zoneDim = 160.0f;
|
||
|
|
||
|
float xcount = x/zoneDim;
|
||
|
float ycount = -y/zoneDim + 1;
|
||
|
|
||
|
string zoneName;
|
||
|
char ych[32];
|
||
|
sprintf(ych,"%d",(sint)ycount);
|
||
|
sint sz = (sint)strlen(ych);
|
||
|
for(sint i = 0; i<sz; i++)
|
||
|
{
|
||
|
zoneName += ych[i];
|
||
|
}
|
||
|
zoneName += '_';
|
||
|
zoneName += 'A' + (sint)xcount/26;
|
||
|
zoneName += 'A' + (sint)xcount%26;
|
||
|
|
||
|
return zoneName;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************\
|
||
|
displayOrientation()
|
||
|
\*********************************************************/
|
||
|
void displayOrientation()
|
||
|
{
|
||
|
float x = 0.9f*4.f/3.f;
|
||
|
float y = 0.1f;
|
||
|
float radius = 0.015f;
|
||
|
|
||
|
// Triangle
|
||
|
CMaterial mat;
|
||
|
mat.initUnlit();
|
||
|
mat.setSrcBlend(CMaterial::srcalpha);
|
||
|
mat.setDstBlend(CMaterial::invsrcalpha);
|
||
|
mat.setBlend(true);
|
||
|
|
||
|
CVertexBuffer vb;
|
||
|
vb.setVertexFormat (CVertexBuffer::PositionFlag);
|
||
|
vb.setNumVertices (7);
|
||
|
{
|
||
|
CVertexBufferReadWrite vba;
|
||
|
vb.lock(vba);
|
||
|
|
||
|
// tri
|
||
|
vba.setVertexCoord (0, CVector (-radius, 0, 0));
|
||
|
vba.setVertexCoord (1, CVector (radius, 0, 0));
|
||
|
vba.setVertexCoord (2, CVector (0, 0, 3*radius));
|
||
|
|
||
|
// quad
|
||
|
vba.setVertexCoord (3, CVector (-radius, 0, -radius));
|
||
|
vba.setVertexCoord (4, CVector (radius, 0, -radius));
|
||
|
vba.setVertexCoord (5, CVector (radius, 0, radius));
|
||
|
vba.setVertexCoord (6, CVector (-radius, 0, radius));
|
||
|
}
|
||
|
|
||
|
CNELU::Driver->activeVertexBuffer(vb);
|
||
|
|
||
|
CIndexBuffer pbTri;
|
||
|
pbTri.setNumIndexes (3);
|
||
|
{
|
||
|
CIndexBufferReadWrite iba;
|
||
|
pbTri.lock (iba);
|
||
|
iba.setTri (0, 0, 1, 2);
|
||
|
}
|
||
|
|
||
|
CIndexBuffer pbQuad;
|
||
|
pbQuad.setNumIndexes (6);
|
||
|
{
|
||
|
CIndexBufferReadWrite iba;
|
||
|
pbQuad.lock(iba);
|
||
|
iba.setTri (0, 3, 4, 5);
|
||
|
iba.setTri (3, 5, 6, 3);
|
||
|
}
|
||
|
|
||
|
CNELU::Driver->setFrustum (0.f, 4.f/3.f, 0.f, 1.f, -1.f, 1.f, false);
|
||
|
CMatrix mtx;
|
||
|
mtx.identity();
|
||
|
CNELU::Driver->setupViewMatrix (mtx);
|
||
|
|
||
|
mat.setColor(CRGBA(50,255,255,150));
|
||
|
|
||
|
// up
|
||
|
mtx.identity();
|
||
|
mtx.translate(CVector(x,0,y));
|
||
|
mtx.rotateY(MoveListener.getRotZ() );
|
||
|
mtx.translate(CVector(0,0,radius));
|
||
|
CNELU::Driver->setupModelMatrix (mtx);
|
||
|
CNELU::Driver->activeVertexBuffer(vb);
|
||
|
CNELU::Driver->activeIndexBuffer(pbTri);
|
||
|
CNELU::Driver->renderTriangles(mat, 0, pbTri.getNumIndexes()/3);
|
||
|
|
||
|
mat.setColor(CRGBA(50,50,255,150));
|
||
|
|
||
|
// down
|
||
|
mtx.identity();
|
||
|
mtx.translate(CVector(x,0,y));
|
||
|
mtx.rotateY(MoveListener.getRotZ() + (float)Pi);
|
||
|
mtx.translate(CVector(0,0,radius));
|
||
|
CNELU::Driver->setupModelMatrix (mtx);
|
||
|
CNELU::Driver->renderTriangles(mat, 0, pbTri.getNumIndexes()/3);
|
||
|
|
||
|
// left
|
||
|
mtx.identity();
|
||
|
mtx.translate(CVector(x,0,y));
|
||
|
mtx.rotateY(MoveListener.getRotZ() - (float)Pi/2);
|
||
|
mtx.translate(CVector(0,0,radius));
|
||
|
CNELU::Driver->setupModelMatrix (mtx);
|
||
|
CNELU::Driver->renderTriangles(mat, 0, pbTri.getNumIndexes()/3);
|
||
|
|
||
|
// right
|
||
|
mtx.identity();
|
||
|
mtx.translate(CVector(x,0,y));
|
||
|
mtx.rotateY(MoveListener.getRotZ() + (float)Pi/2);
|
||
|
mtx.translate(CVector(0,0,radius));
|
||
|
CNELU::Driver->setupModelMatrix (mtx);
|
||
|
CNELU::Driver->renderTriangles(mat, 0, pbTri.getNumIndexes()/3);
|
||
|
|
||
|
// center
|
||
|
mtx.identity();
|
||
|
mtx.translate(CVector(x,0,y));
|
||
|
mtx.rotateY(MoveListener.getRotZ());
|
||
|
CNELU::Driver->setupModelMatrix (mtx);
|
||
|
CNELU::Driver->activeIndexBuffer(pbQuad);
|
||
|
CNELU::Driver->renderTriangles(mat, 0, pbQuad.getNumIndexes()/3);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************\
|
||
|
displayZone()
|
||
|
\*********************************************************/
|
||
|
void displayZones()
|
||
|
{
|
||
|
const float zFarStep = 5.0f;
|
||
|
const float tileNearStep = 10.0f;
|
||
|
const float thresholdStep = 0.005f;
|
||
|
|
||
|
ViewerCfg.TextContext.setHotSpot(CComputedString::MiddleMiddle);
|
||
|
ViewerCfg.TextContext.setColor(CRGBA(255,255,255));
|
||
|
ViewerCfg.TextContext.setFontSize(20);
|
||
|
|
||
|
CNELU::clearBuffers(CRGBA(0,0,0));
|
||
|
CNELU::swapBuffers();
|
||
|
|
||
|
|
||
|
|
||
|
// Create landscape
|
||
|
CNELU::clearBuffers(CRGBA(0,0,0));
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.5f,"Creating landscape...");
|
||
|
CNELU::swapBuffers();
|
||
|
|
||
|
Landscape = (CLandscapeModel*)CNELU::Scene->createModel(LandscapeModelId);
|
||
|
Landscape->Landscape.setNoiseMode (ViewerCfg.LandscapeNoise);
|
||
|
Landscape->Landscape.setTileNear(ViewerCfg.LandscapeTileNear);
|
||
|
Landscape->Landscape.setThreshold(ViewerCfg.LandscapeThreshold);
|
||
|
|
||
|
Landscape->Landscape.enableAutomaticLighting (ViewerCfg.AutoLight);
|
||
|
Landscape->Landscape.setupAutomaticLightDir (ViewerCfg.LightDir);
|
||
|
|
||
|
// Enable Additive Tiles.
|
||
|
Landscape->enableAdditive(true);
|
||
|
|
||
|
// HeightField.
|
||
|
CBitmap heightBitmap;
|
||
|
CIFile file(ViewerCfg.HeightFieldName);
|
||
|
|
||
|
if( ViewerCfg.HeightFieldName!="" && heightBitmap.load(file) )
|
||
|
{
|
||
|
CHeightMap heightMap;
|
||
|
heightMap.buildFromBitmap(heightBitmap);
|
||
|
heightMap.MaxZ= ViewerCfg.HeightFieldMaxZ;
|
||
|
heightMap.OriginX= ViewerCfg.HeightFieldOriginX;
|
||
|
heightMap.OriginY= ViewerCfg.HeightFieldOriginY;
|
||
|
heightMap.SizeX = ViewerCfg.HeightFieldSizeX;
|
||
|
heightMap.SizeY = ViewerCfg.HeightFieldSizeY;
|
||
|
Landscape->Landscape.setHeightField(heightMap);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Init TileBank.
|
||
|
CNELU::clearBuffers(CRGBA(0,0,0));
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.5f,"Initializing TileBanks...");
|
||
|
CNELU::swapBuffers();
|
||
|
|
||
|
try
|
||
|
{
|
||
|
CIFile bankFile (ViewerCfg.BanksPath + "/" + ViewerCfg.Bank);
|
||
|
Landscape->Landscape.TileBank.serial(bankFile);
|
||
|
}
|
||
|
catch(Exception)
|
||
|
{
|
||
|
string tmp = string("Cant load bankfile ")+ViewerCfg.BanksPath + "/" + ViewerCfg.Bank;
|
||
|
nlerror (tmp.c_str());
|
||
|
}
|
||
|
|
||
|
if ((Landscape->Landscape.TileBank.getAbsPath ()=="")&&(ViewerCfg.TilesPath!=""))
|
||
|
Landscape->Landscape.TileBank.setAbsPath (ViewerCfg.TilesPath + "/");
|
||
|
|
||
|
if (ViewerCfg.UseDDS)
|
||
|
{
|
||
|
Landscape->Landscape.TileBank.makeAllExtensionDDS();
|
||
|
}
|
||
|
|
||
|
if (ViewerCfg.AllPathRelative)
|
||
|
Landscape->Landscape.TileBank.makeAllPathRelative();
|
||
|
|
||
|
string::size_type idx = ViewerCfg.Bank.find(".");
|
||
|
string farBank = ViewerCfg.Bank.substr(0,idx);
|
||
|
farBank += ".farbank";
|
||
|
|
||
|
try
|
||
|
{
|
||
|
CIFile farbankFile(ViewerCfg.BanksPath + "/" + farBank);
|
||
|
Landscape->Landscape.TileFarBank.serial(farbankFile);
|
||
|
}
|
||
|
catch(Exception)
|
||
|
{
|
||
|
string tmp = string("Cant load bankfile ")+ViewerCfg.BanksPath + "/" + farBank;
|
||
|
nlerror (tmp.c_str());
|
||
|
}
|
||
|
|
||
|
if ( ! Landscape->Landscape.initTileBanks() )
|
||
|
{
|
||
|
nlwarning( "You need to recompute bank.farbank for the far textures" );
|
||
|
}
|
||
|
|
||
|
// Init light color
|
||
|
CNELU::clearBuffers(CRGBA(0,0,0));
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.5f,"Initializing Light...");
|
||
|
CNELU::swapBuffers();
|
||
|
|
||
|
Landscape->Landscape.setupStaticLight (ViewerCfg.LandDiffuse, ViewerCfg.LandAmbient, 1.1f);
|
||
|
|
||
|
// Init collision manager
|
||
|
CollisionManager.init( &(Landscape->Landscape), 200);
|
||
|
|
||
|
|
||
|
// Preload of TileBank
|
||
|
CNELU::clearBuffers(CRGBA(0,0,0));
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.5f,"Loading TileBank...");
|
||
|
CNELU::swapBuffers();
|
||
|
|
||
|
for (int ts=0; ts<Landscape->Landscape.TileBank.getTileSetCount (); ts++)
|
||
|
{
|
||
|
CTileSet *tileSet=Landscape->Landscape.TileBank.getTileSet (ts);
|
||
|
sint tl;
|
||
|
for (tl=0; tl<tileSet->getNumTile128(); tl++)
|
||
|
Landscape->Landscape.flushTiles (CNELU::Scene->getDriver(), (uint16)tileSet->getTile128(tl), 1);
|
||
|
for (tl=0; tl<tileSet->getNumTile256(); tl++)
|
||
|
Landscape->Landscape.flushTiles (CNELU::Scene->getDriver(), (uint16)tileSet->getTile256(tl), 1);
|
||
|
for (tl=0; tl<CTileSet::count; tl++)
|
||
|
Landscape->Landscape.flushTiles (CNELU::Scene->getDriver(), (uint16)tileSet->getTransition(tl)->getTile (), 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Build zones.
|
||
|
CNELU::clearBuffers(CRGBA(0,0,0));
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.5f,"Loading zones...");
|
||
|
CNELU::swapBuffers();
|
||
|
uint32 i;
|
||
|
for(i =0; i<ViewerCfg.Zones.size(); i++)
|
||
|
{
|
||
|
CZone zone;
|
||
|
try
|
||
|
{
|
||
|
CIFile file(CPath::lookup(ViewerCfg.Zones[i]));
|
||
|
zone.serial(file);
|
||
|
file.close();
|
||
|
|
||
|
// Add it to landscape.
|
||
|
Landscape->Landscape.addZone(zone);
|
||
|
|
||
|
// Add it to collision manager.
|
||
|
CollisionManager.addZone(zone.getZoneId());
|
||
|
}
|
||
|
catch(Exception &e)
|
||
|
{
|
||
|
printf(e.what ());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// Load instance group.
|
||
|
CNELU::clearBuffers(CRGBA(0,0,0));
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.5f,"Loading objects...");
|
||
|
CNELU::swapBuffers();
|
||
|
for(i =0; i<ViewerCfg.Igs.size(); i++)
|
||
|
{
|
||
|
CInstanceGroup *group = new CInstanceGroup;
|
||
|
try
|
||
|
{
|
||
|
CIFile file(CPath::lookup(ViewerCfg.Igs[i]));
|
||
|
group->serial(file);
|
||
|
file.close();
|
||
|
|
||
|
// Add it to the scene.
|
||
|
group->addToScene (*CNELU::Scene);
|
||
|
}
|
||
|
catch(Exception &e)
|
||
|
{
|
||
|
printf(e.what ());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Init collision Manager.
|
||
|
CNELU::clearBuffers(CRGBA(0,0,0));
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.5f,"Initializing collision manager...");
|
||
|
CNELU::swapBuffers();
|
||
|
|
||
|
|
||
|
CollisionManager.setCenter(ViewerCfg.Position);
|
||
|
ViewerCfg.Position.z = 0.0f;
|
||
|
CollisionManager.snapToGround( ViewerCfg.Position, 1000.0f );
|
||
|
|
||
|
|
||
|
|
||
|
// hide mouse cursor
|
||
|
CNELU::Driver->showCursor(false);
|
||
|
#ifdef NL_RELEASE
|
||
|
CNELU::Driver->setCapture(true);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
// Events management
|
||
|
CNELU::EventServer.addEmitter(CNELU::Driver->getEventEmitter());
|
||
|
CNELU::AsyncListener.addToServer(CNELU::EventServer);
|
||
|
|
||
|
MoveListener.init(CNELU::Scene, ViewerCfg.Width, ViewerCfg.Height, *CNELU::Camera);
|
||
|
MoveListener.addToServer(CNELU::EventServer);
|
||
|
MoveListener.setPos( ViewerCfg.Position );
|
||
|
|
||
|
CNELU::Camera->setPerspective (float(80.0*Pi/180.0), 1.33f, 0.1f, 1000.0f);
|
||
|
|
||
|
bool showInfos = true;
|
||
|
|
||
|
|
||
|
// initializing Z-Clip Far
|
||
|
float left;
|
||
|
float right;
|
||
|
float bottom;
|
||
|
float top;
|
||
|
float znear;
|
||
|
float zfar;
|
||
|
CNELU::Camera->getFrustum(left, right, bottom, top, znear, zfar);
|
||
|
zfar = ViewerCfg.ZFar;
|
||
|
CNELU::Camera->setFrustum(left, right, bottom, top, znear, zfar);
|
||
|
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Time mgt.
|
||
|
//==========
|
||
|
static sint64 t0 = (sint64)CTime::getLocalTime();
|
||
|
static sint64 t1 = (sint64)CTime::getLocalTime();
|
||
|
static sint64 ts = 0;
|
||
|
|
||
|
t0 = t1;
|
||
|
t1 = (sint64)CTime::getLocalTime();
|
||
|
sint64 dt64 = t1-t0;
|
||
|
ts += dt64;
|
||
|
float dt= ((float)dt64)*0.001f;
|
||
|
|
||
|
|
||
|
CNELU::EventServer.pump();
|
||
|
|
||
|
|
||
|
// Manage movement and collision
|
||
|
MoveListener.setLocalTime(CTime::getLocalTime());
|
||
|
CVector oldpos = MoveListener.getPos();
|
||
|
MoveListener.changeViewMatrix();
|
||
|
if(MoveListener.getMode()==CMoveListener::WALK)
|
||
|
{
|
||
|
CVector pos = MoveListener.getPos();
|
||
|
CollisionManager.snapToGround( pos , 1000.0f );
|
||
|
MoveListener.setPos( pos );
|
||
|
}
|
||
|
CollisionManager.setCenter(MoveListener.getPos());
|
||
|
|
||
|
|
||
|
// Change move mode
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeySPACE))
|
||
|
{
|
||
|
MoveListener.swapMode();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Change displaying infos state
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeyF1))
|
||
|
{
|
||
|
showInfos = !showInfos;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Change eyes height
|
||
|
float eh = MoveListener.getEyesHeight();
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeyADD))
|
||
|
{
|
||
|
ViewerCfg.EyesHeight.z += 0.1f;
|
||
|
eh += 0.1f;
|
||
|
}
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeySUBTRACT))
|
||
|
{
|
||
|
ViewerCfg.EyesHeight.z -= 0.1f;
|
||
|
eh -= 0.1f;
|
||
|
}
|
||
|
if(ViewerCfg.EyesHeight.z<0.1f) ViewerCfg.EyesHeight.z = 0.1f;
|
||
|
if(eh<0.1f) eh = 0.1f;
|
||
|
MoveListener.setEyesHeight(eh);
|
||
|
|
||
|
|
||
|
// Change TileNear
|
||
|
float tileNear = Landscape->Landscape.getTileNear();
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeyHOME))
|
||
|
tileNear += tileNearStep;
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeyEND))
|
||
|
tileNear -= tileNearStep;
|
||
|
if(tileNear<0) tileNear = 0;
|
||
|
Landscape->Landscape.setTileNear(tileNear);
|
||
|
|
||
|
|
||
|
// Change Z-Far
|
||
|
CNELU::Camera->getFrustum(left, right, bottom, top, znear, zfar);
|
||
|
if(CNELU::AsyncListener.isKeyDown(KeyPRIOR))
|
||
|
zfar += zFarStep;
|
||
|
if(CNELU::AsyncListener.isKeyDown(KeyNEXT))
|
||
|
zfar -= zFarStep;
|
||
|
if(zfar<0) zfar = 0;
|
||
|
CNELU::Camera->setFrustum(left, right, bottom, top, znear, zfar);
|
||
|
|
||
|
|
||
|
// Change Threshold
|
||
|
float threshold = Landscape->Landscape.getThreshold();
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeyINSERT))
|
||
|
threshold += thresholdStep;
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeyDELETE))
|
||
|
threshold -= thresholdStep;
|
||
|
if(threshold<0.001f) threshold = 0.001f;
|
||
|
if(threshold>0.1f) threshold = 0.1f;
|
||
|
Landscape->Landscape.setThreshold(threshold);
|
||
|
|
||
|
|
||
|
// Switch between wired and filled scene display
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeyF3))
|
||
|
{
|
||
|
if (CNELU::Driver->getPolygonMode ()==IDriver::Filled)
|
||
|
CNELU::Driver->setPolygonMode (IDriver::Line);
|
||
|
else
|
||
|
CNELU::Driver->setPolygonMode (IDriver::Filled);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Switch between mouse move and keyboard-only move
|
||
|
if(CNELU::AsyncListener.isKeyPushed(KeyRETURN))
|
||
|
{
|
||
|
MoveListener.changeControlMode();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Render
|
||
|
//=======
|
||
|
CNELU::clearBuffers(ViewerCfg.Background);
|
||
|
CNELU::Driver->clearZBuffer();
|
||
|
CNELU::Scene->render();
|
||
|
|
||
|
|
||
|
if(showInfos)
|
||
|
{
|
||
|
|
||
|
// black top quad
|
||
|
CDRU::drawQuad(0,0.97f,1.0f,1.0f,*CNELU::Driver,CRGBA(0,0,0),CNELU::Scene->getViewport());
|
||
|
|
||
|
// black bottom quad
|
||
|
CDRU::drawQuad(0,0,1.0f,0.03f,*CNELU::Driver,CRGBA(0,0,0),CNELU::Scene->getViewport());
|
||
|
|
||
|
|
||
|
ViewerCfg.TextContext.setFontSize(12);
|
||
|
ViewerCfg.TextContext.setColor(CRGBA(255,255,255));
|
||
|
|
||
|
// Display fps.
|
||
|
ViewerCfg.TextContext.printfAt(0.05f,0.98f,"%.1f fps",1/dt);
|
||
|
|
||
|
// Display ms
|
||
|
ViewerCfg.TextContext.printfAt(0.12f,0.98f,"%d ms",dt64);
|
||
|
|
||
|
// Display Tile Near
|
||
|
ViewerCfg.TextContext.printfAt(0.75f,0.98f,"Tile Near : %.1f",tileNear);
|
||
|
|
||
|
//Display moving mode
|
||
|
ViewerCfg.TextContext.setColor(CRGBA(255,0,0));
|
||
|
switch(MoveListener.getMode())
|
||
|
{
|
||
|
case CMoveListener::WALK :
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.98f,"Walk Mode");
|
||
|
break;
|
||
|
case CMoveListener::FREE :
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.98f,"Free-Look Mode");
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
ViewerCfg.TextContext.setColor(CRGBA(255,255,255));
|
||
|
|
||
|
// Display Threshold
|
||
|
ViewerCfg.TextContext.printfAt(0.3f,0.98f,"Threshold : %.3f",threshold);
|
||
|
|
||
|
// Display Clip Far
|
||
|
ViewerCfg.TextContext.printfAt(0.92f,0.98f,"Clip Far : %.1f",zfar);
|
||
|
|
||
|
|
||
|
ViewerCfg.TextContext.setHotSpot(CComputedString::MiddleBottom);
|
||
|
|
||
|
// Display current zone name
|
||
|
CVector pos = MoveListener.getPos();
|
||
|
string zoneName = getZoneNameByCoord(pos.x, pos.y);
|
||
|
ViewerCfg.TextContext.printfAt(0.3f,0.01f,"Zone : %s",zoneName.c_str());
|
||
|
|
||
|
// Position
|
||
|
ViewerCfg.TextContext.printfAt(0.1f,0.01f,"Position : %d %d %d",(sint)pos.x,(sint)pos.y,(sint)pos.z);
|
||
|
|
||
|
// Eyes height
|
||
|
ViewerCfg.TextContext.printfAt(0.7f,0.01f,"Eyes : %.2f m",ViewerCfg.EyesHeight.z);
|
||
|
|
||
|
// Display speed in km/h
|
||
|
ViewerCfg.TextContext.setColor(CRGBA(255,0,0));
|
||
|
ViewerCfg.TextContext.printfAt(0.5f,0.01f,"Speed : %d km/h",(sint)(MoveListener.getSpeed()*3.6f));
|
||
|
ViewerCfg.TextContext.setColor(CRGBA(255,255,255));
|
||
|
|
||
|
// Heading
|
||
|
sint heading = -(sint)(MoveListener.getRotZ()*180/Pi)%360;
|
||
|
if(heading<0) heading += 360;
|
||
|
ViewerCfg.TextContext.printfAt(0.9f,0.01f,"Heading : %d degrees",heading);
|
||
|
|
||
|
// Display the cool compass.
|
||
|
displayOrientation();
|
||
|
}
|
||
|
|
||
|
|
||
|
CNELU::swapBuffers();
|
||
|
CNELU::screenshot();
|
||
|
}
|
||
|
while(!CNELU::AsyncListener.isKeyPushed(KeyESCAPE));
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
ViewerCfg.Position = MoveListener.getPos();
|
||
|
|
||
|
CNELU::AsyncListener.removeFromServer(CNELU::EventServer);
|
||
|
MoveListener.removeFromServer(CNELU::EventServer);
|
||
|
|
||
|
CNELU::Driver->showCursor(true);
|
||
|
#ifdef NL_RELEASE
|
||
|
CNELU::Driver->setCapture(false);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/****************************************************************\
|
||
|
writeConfigFile
|
||
|
\****************************************************************/
|
||
|
void writeConfigFile(const char * configFileName)
|
||
|
{
|
||
|
FILE * f = fopen(configFileName,"wt");
|
||
|
|
||
|
if(f==NULL)
|
||
|
{
|
||
|
nlerror("can't open file '%s'\n",configFileName);
|
||
|
}
|
||
|
|
||
|
fprintf(f,"FullScreen = %d;\n",ViewerCfg.Windowed?0:1);
|
||
|
fprintf(f,"Width = %d;\n",ViewerCfg.Width);
|
||
|
fprintf(f,"Height = %d;\n",ViewerCfg.Height);
|
||
|
fprintf(f,"Depth = %d;\n",ViewerCfg.Depth);
|
||
|
fprintf(f,"Position = { %f, %f, %f };\n", ViewerCfg.Position.x,ViewerCfg.Position.y,ViewerCfg.Position.z);
|
||
|
fprintf(f,"EyesHeight = %f;\n", ViewerCfg.EyesHeight.z);
|
||
|
fprintf(f,"Background = { %d, %d, %d };\n", ViewerCfg.Background.R,ViewerCfg.Background.G,ViewerCfg.Background.B);
|
||
|
fprintf(f,"ZFar = %f;\n", ViewerCfg.ZFar);
|
||
|
|
||
|
fprintf(f,"AutoLight = %d;\n", ViewerCfg.AutoLight?1:0);
|
||
|
fprintf(f,"LightDir = { %f, %f, %f };\n", ViewerCfg.LightDir.x, ViewerCfg.LightDir.y, ViewerCfg.LightDir.z);
|
||
|
fprintf(f,"LandscapeTileNear = %f;\n", ViewerCfg.LandscapeTileNear);
|
||
|
fprintf(f,"LandscapeThreshold = %f;\n", ViewerCfg.LandscapeThreshold);
|
||
|
fprintf(f,"LandscapeNoise = %d;\n", (int)ViewerCfg.LandscapeNoise);
|
||
|
fprintf(f,"BanksPath = \"%s\";\n",ViewerCfg.BanksPath.c_str());
|
||
|
fprintf(f,"TilesPath = \"%s\";\n",ViewerCfg.TilesPath.c_str());
|
||
|
fprintf(f,"UseDDS = \"%d\";\n",ViewerCfg.UseDDS?1:0);
|
||
|
fprintf(f,"AllPathRelative = \"%d\";\n",ViewerCfg.AllPathRelative?1:0);
|
||
|
fprintf(f,"Bank = \"%s\";\n",ViewerCfg.Bank.c_str());
|
||
|
fprintf(f,"ZonesPath = \"%s\";\n",ViewerCfg.ZonesPath.c_str());
|
||
|
fprintf(f,"IgPath = \"%s\";\n",ViewerCfg.IgPath.c_str());
|
||
|
fprintf(f,"ShapePath = \"%s\";\n",ViewerCfg.ShapePath.c_str());
|
||
|
fprintf(f,"MapsPath = \"%s\";\n",ViewerCfg.MapsPath.c_str());
|
||
|
fprintf(f,"FontPath = \"%s\";\n",ViewerCfg.FontPath.c_str());
|
||
|
|
||
|
fprintf(f,"HeightFieldName = \"%s\";\n", ViewerCfg.HeightFieldName.c_str());
|
||
|
fprintf(f,"HeightFieldMaxZ = %f;\n", ViewerCfg.HeightFieldMaxZ);
|
||
|
fprintf(f,"HeightFieldOriginX = %f;\n", ViewerCfg.HeightFieldOriginX);
|
||
|
fprintf(f,"HeightFieldOriginY = %f;\n", ViewerCfg.HeightFieldOriginY);
|
||
|
fprintf(f,"HeightFieldSizeX = %f;\n", ViewerCfg.HeightFieldSizeX);
|
||
|
fprintf(f,"HeightFieldSizeY = %f;\n", ViewerCfg.HeightFieldSizeY);
|
||
|
|
||
|
fprintf(f,"LandAmbient = { %d, %d, %d };\n", ViewerCfg.LandAmbient.R,ViewerCfg.LandAmbient.G,ViewerCfg.LandAmbient.B);
|
||
|
fprintf(f,"LandDiffuse = { %d, %d, %d };\n", ViewerCfg.LandDiffuse.R,ViewerCfg.LandDiffuse.G,ViewerCfg.LandDiffuse.B);
|
||
|
|
||
|
fprintf(f,"Zones = {\n");
|
||
|
fprintf(f,"};\n");
|
||
|
|
||
|
fprintf(f,"Ig = {\n");
|
||
|
fprintf(f,"};\n");
|
||
|
|
||
|
fclose(f);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/****************************************************************\
|
||
|
init()
|
||
|
\****************************************************************/
|
||
|
void initViewerConfig(const char * configFileName)
|
||
|
{
|
||
|
FILE * f = fopen(configFileName,"rt");
|
||
|
if(f==NULL)
|
||
|
{
|
||
|
nlwarning("'%s' not found, default values used", configFileName);
|
||
|
writeConfigFile(configFileName);
|
||
|
}
|
||
|
else fclose (f);
|
||
|
|
||
|
try
|
||
|
{
|
||
|
CConfigFile cf;
|
||
|
|
||
|
cf.load(configFileName);
|
||
|
|
||
|
CConfigFile::CVar &cvFullScreen = cf.getVar("FullScreen");
|
||
|
ViewerCfg.Windowed = cvFullScreen.asInt() ? false : true;
|
||
|
|
||
|
CConfigFile::CVar &cvWidth = cf.getVar("Width");
|
||
|
ViewerCfg.Width = cvWidth.asInt();
|
||
|
|
||
|
CConfigFile::CVar &cvHeight = cf.getVar("Height");
|
||
|
ViewerCfg.Height = cvHeight.asInt();
|
||
|
|
||
|
CConfigFile::CVar &cvDepth = cf.getVar("Depth");
|
||
|
ViewerCfg.Depth = cvDepth.asInt();
|
||
|
|
||
|
CConfigFile::CVar &cvPosition = cf.getVar("Position");
|
||
|
nlassert(cvPosition.size()==3);
|
||
|
ViewerCfg.Position.x = cvPosition.asFloat(0);
|
||
|
ViewerCfg.Position.y = cvPosition.asFloat(1);
|
||
|
ViewerCfg.Position.z = cvPosition.asFloat(2);
|
||
|
|
||
|
CConfigFile::CVar &cvEyesHeight = cf.getVar("EyesHeight");
|
||
|
ViewerCfg.EyesHeight = CVector(0,0,cvEyesHeight.asFloat());
|
||
|
|
||
|
CConfigFile::CVar &cvBackColor = cf.getVar("Background");
|
||
|
nlassert(cvBackColor.size()==3);
|
||
|
ViewerCfg.Background.R = cvBackColor.asInt(0);
|
||
|
ViewerCfg.Background.G = cvBackColor.asInt(1);
|
||
|
ViewerCfg.Background.B = cvBackColor.asInt(2);
|
||
|
|
||
|
CConfigFile::CVar &cvZFar = cf.getVar("ZFar");
|
||
|
ViewerCfg.ZFar = cvZFar.asFloat();
|
||
|
|
||
|
CConfigFile::CVar &cvAutoLight = cf.getVar("AutoLight");
|
||
|
ViewerCfg.AutoLight = cvAutoLight.asInt() ? true : false;
|
||
|
|
||
|
CConfigFile::CVar &cvLightDir = cf.getVar("LightDir");
|
||
|
nlassert(cvLightDir.size()==3);
|
||
|
ViewerCfg.LightDir.x = cvLightDir.asFloat(0);
|
||
|
ViewerCfg.LightDir.y = cvLightDir.asFloat(1);
|
||
|
ViewerCfg.LightDir.z = cvLightDir.asFloat(2);
|
||
|
|
||
|
CConfigFile::CVar &cvLandscapeTileNear = cf.getVar("LandscapeTileNear");
|
||
|
ViewerCfg.LandscapeTileNear = cvLandscapeTileNear.asFloat();
|
||
|
|
||
|
CConfigFile::CVar &cvLandscapeThreshold = cf.getVar("LandscapeThreshold");
|
||
|
ViewerCfg.LandscapeThreshold = cvLandscapeThreshold.asFloat();
|
||
|
|
||
|
CConfigFile::CVar &cvLandscapeNoise = cf.getVar("LandscapeNoise");
|
||
|
ViewerCfg.LandscapeNoise = cvLandscapeNoise.asInt() != 0;
|
||
|
|
||
|
CConfigFile::CVar &cvBanksPath = cf.getVar("BanksPath");
|
||
|
ViewerCfg.BanksPath = cvBanksPath.asString();
|
||
|
|
||
|
CConfigFile::CVar &cvTilesPath = cf.getVar("TilesPath");
|
||
|
ViewerCfg.TilesPath = cvTilesPath.asString();
|
||
|
|
||
|
CConfigFile::CVar &cvUseDDS = cf.getVar("UseDDS");
|
||
|
ViewerCfg.UseDDS = cvUseDDS.asInt() ? true : false;
|
||
|
|
||
|
CConfigFile::CVar &cvAllPathRelative = cf.getVar("AllPathRelative");
|
||
|
ViewerCfg.AllPathRelative = cvAllPathRelative.asInt() ? true : false;
|
||
|
|
||
|
CConfigFile::CVar &cvBank = cf.getVar("Bank");
|
||
|
ViewerCfg.Bank = cvBank.asString();
|
||
|
|
||
|
CConfigFile::CVar &cvZonesPath = cf.getVar("ZonesPath");
|
||
|
ViewerCfg.ZonesPath = cvZonesPath.asString();
|
||
|
CPath::addSearchPath(cvZonesPath.asString());
|
||
|
|
||
|
CConfigFile::CVar &cvIgPath = cf.getVar("IgPath");
|
||
|
ViewerCfg.IgPath = cvIgPath.asString();
|
||
|
CPath::addSearchPath(cvIgPath.asString());
|
||
|
|
||
|
CConfigFile::CVar &cvShapePath = cf.getVar("ShapePath");
|
||
|
ViewerCfg.ShapePath = cvShapePath.asString();
|
||
|
CPath::addSearchPath(cvShapePath.asString());
|
||
|
|
||
|
CConfigFile::CVar &cvMapsPath = cf.getVar("MapsPath");
|
||
|
ViewerCfg.MapsPath = cvMapsPath.asString();
|
||
|
CPath::addSearchPath(cvMapsPath.asString());
|
||
|
|
||
|
CConfigFile::CVar &cvFontPath = cf.getVar("FontPath");
|
||
|
ViewerCfg.FontPath = cvFontPath.asString();
|
||
|
|
||
|
CConfigFile::CVar &cvHeightFieldName = cf.getVar("HeightFieldName");
|
||
|
ViewerCfg.HeightFieldName = cvHeightFieldName.asString();
|
||
|
|
||
|
CConfigFile::CVar &cvHeightFieldMaxZ = cf.getVar("HeightFieldMaxZ");
|
||
|
ViewerCfg.HeightFieldMaxZ = cvHeightFieldMaxZ.asFloat();
|
||
|
|
||
|
CConfigFile::CVar &cvHeightFieldOriginX = cf.getVar("HeightFieldOriginX");
|
||
|
ViewerCfg.HeightFieldOriginX = cvHeightFieldOriginX.asFloat();
|
||
|
|
||
|
CConfigFile::CVar &cvHeightFieldOriginY = cf.getVar("HeightFieldOriginY");
|
||
|
ViewerCfg.HeightFieldOriginY = cvHeightFieldOriginY.asFloat();
|
||
|
|
||
|
CConfigFile::CVar &cvHeightFieldSizeX = cf.getVar("HeightFieldSizeX");
|
||
|
ViewerCfg.HeightFieldSizeX = cvHeightFieldSizeX.asFloat();
|
||
|
|
||
|
CConfigFile::CVar &cvHeightFieldSizeY = cf.getVar("HeightFieldSizeY");
|
||
|
ViewerCfg.HeightFieldSizeY = cvHeightFieldSizeY.asFloat();
|
||
|
|
||
|
|
||
|
CConfigFile::CVar &cvLandAmb = cf.getVar("LandAmbient");
|
||
|
nlassert(cvLandAmb.size()==3);
|
||
|
ViewerCfg.LandAmbient.R = cvLandAmb.asInt(0);
|
||
|
ViewerCfg.LandAmbient.G = cvLandAmb.asInt(1);
|
||
|
ViewerCfg.LandAmbient.B = cvLandAmb.asInt(2);
|
||
|
|
||
|
CConfigFile::CVar &cvLandDiff = cf.getVar("LandDiffuse");
|
||
|
nlassert(cvLandDiff.size()==3);
|
||
|
ViewerCfg.LandDiffuse.R = cvLandDiff.asInt(0);
|
||
|
ViewerCfg.LandDiffuse.G = cvLandDiff.asInt(1);
|
||
|
ViewerCfg.LandDiffuse.B = cvLandDiff.asInt(2);
|
||
|
|
||
|
|
||
|
CConfigFile::CVar &cvZones = cf.getVar("Zones");
|
||
|
for(uint i=0; i<cvZones.size(); i++)
|
||
|
{
|
||
|
ViewerCfg.Zones.push_back(cvZones.asString(i));
|
||
|
}
|
||
|
|
||
|
CConfigFile::CVar &cvIgs = cf.getVar("Ig");
|
||
|
for(uint i=0; i<cvIgs.size(); i++)
|
||
|
{
|
||
|
ViewerCfg.Igs.push_back(cvIgs.asString(i));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
catch (EConfigFile &e)
|
||
|
{
|
||
|
nlerror("Problem in config file : %s\n", e.what ());
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/****************************************************************\
|
||
|
MAIN
|
||
|
\****************************************************************/
|
||
|
#ifdef NL_OS_WINDOWS
|
||
|
int WINAPI WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR cmdline, int /* nCmdShow */)
|
||
|
{
|
||
|
#else
|
||
|
int main(int /* argc */, char ** /* argv */)
|
||
|
{
|
||
|
#endif
|
||
|
try
|
||
|
{
|
||
|
NLMISC::CApplicationContext myApplicationContext;
|
||
|
|
||
|
#ifdef NL_OS_UNIX
|
||
|
std::string homeDir = getenv("HOME");
|
||
|
NLMISC::CPath::addSearchPath( homeDir + "/.nel");
|
||
|
#endif // NL_OS_UNIX
|
||
|
|
||
|
NLMISC::CPath::addSearchPath(NL_ZVIEWER_CFG);
|
||
|
|
||
|
initViewerConfig("zviewer.cfg");
|
||
|
|
||
|
// Init NELU
|
||
|
NL3D::CNELU::init(ViewerCfg.Width, ViewerCfg.Height, CViewport(), ViewerCfg.Depth, ViewerCfg.Windowed, NULL, false, false);
|
||
|
NL3D::CNELU::Driver->setWindowTitle(ucstring("NeL ZViewer"));
|
||
|
NL3D::CNELU::Camera->setTransformMode(ITransformable::DirectMatrix);
|
||
|
|
||
|
// Init the font manager
|
||
|
ViewerCfg.TextContext.init (CNELU::Driver, &ViewerCfg.FontManager);
|
||
|
ViewerCfg.TextContext.setFontGenerator(ViewerCfg.FontPath);
|
||
|
ViewerCfg.TextContext.setFontSize(12);
|
||
|
ViewerCfg.FontManager.setMaxMemory(2000000);
|
||
|
|
||
|
displayZones();
|
||
|
|
||
|
// release nelu
|
||
|
NL3D::CNELU::release();
|
||
|
}
|
||
|
catch (Exception &e)
|
||
|
{
|
||
|
nlerror("main trapped an exception: '%s'", e.what ());
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|