root/cafu/trunk/CaTools/TerrainViewer.cpp

Revision 455, 21.3 KB (checked in by Carsten, 4 months ago)

Updated copyright banners (in C++ files).

Line 
1/*
2=================================================================================
3This file is part of Cafu, the open-source game engine and graphics engine
4for multiplayer, cross-platform, real-time 3D action.
5Copyright (C) 2002-2012 Carsten Fuchs Software.
6
7Cafu is free software: you can redistribute it and/or modify it under the terms
8of the GNU General Public License as published by the Free Software Foundation,
9either version 3 of the License, or (at your option) any later version.
10
11Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with Cafu. If not, see <http://www.gnu.org/licenses/>.
17
18For support and more information about Cafu, visit us at <http://www.cafu.de>.
19=================================================================================
20*/
21
22/***************************/
23/*** Cafu Terrain Viewer ***/
24/***************************/
25
26#include <stdio.h>
27#include <float.h>
28#include <string.h>
29
30#include "ConsoleCommands/Console.hpp"
31#include "ConsoleCommands/ConsoleStdout.hpp"
32#include "FileSys/FileManImpl.hpp"
33#include "MaterialSystem/MapComposition.hpp"
34#include "MaterialSystem/MaterialManager.hpp"
35#include "MaterialSystem/MaterialManagerImpl.hpp"
36#include "MaterialSystem/Mesh.hpp"
37#include "MaterialSystem/Renderer.hpp"
38#include "MaterialSystem/TextureMap.hpp"
39#include "Math3D/Plane3.hpp"
40#include "Math3D/Matrix.hpp"
41#include "OpenGL/OpenGLWindow.hpp"
42#include "Templates/Array.hpp"
43#include "Terrain/Terrain.hpp"
44#include "Util/Util.hpp"
45#include "PlatformAux.hpp"
46
47#include "zlib.h"
48
49#ifdef _WIN32
50#define WIN32_LEAN_AND_MEAN
51#include <windows.h>
52#include <direct.h>
53    #if defined(_MSC_VER)
54        #if (_MSC_VER<1300)
55        #define for if (false) ; else for
56    #endif
57    #endif
58#else
59#include <dlfcn.h>
60#define FreeLibrary dlclose
61#endif
62
63
64static cf::ConsoleStdoutT ConsoleStdout;
65cf::ConsoleI* Console=&ConsoleStdout;
66
67static cf::FileSys::FileManImplT FileManImpl;
68cf::FileSys::FileManI* cf::FileSys::FileMan=&FileManImpl;
69
70MaterialManagerI* MaterialManager=NULL;
71
72
73#define DEG2RAD(x) ((3.1415927f / 180.0f) * (x))
74#define SQR(x)     ((x) * (x))
75
76
77std::string BaseDirectoryName="Games/DeathMatch";
78
79
80void Usage()
81{
82    printf("\n");
83    printf("\nUSAGE: TerrainViewer -t=HeightMapName -m=MaterialName [OPTIONS]\n");
84    printf("\n");
85    printf("\nMANDATORY PARAMETERS:\n");
86    printf("\n");
87    printf("-t=MyHeightMap specifies the name of the desired terrain (heightmap image).\n");
88    printf("\n");
89    printf("-m=MyMaterial specifies the name of the desired material, which must be\n");
90    printf("   defined in one of the material script files (see below).\n");
91    printf("\n");
92    printf("\nOPTIONS:\n");
93    printf("\n");
94    printf("-bd=base/dir specifies the base directory from which I look both for material\n");
95    printf("   scripts and the materials associated textures.\n");
96    printf("   The default (if you don't use -bd) is %s\n", BaseDirectoryName.c_str());
97    printf("\n");
98    printf("-ms=MyMatScript.cmat specifies the material script that contains a definition\n");
99    printf("   of \"MyMaterial\" (see above). You may use -ms several times, making me look\n");
100    printf("   into each specified script for a definition of the material.\n");
101    printf("   If you do not use -ms at all, I'll look into ALL material scripts that I can\n");
102    printf("   find in %s/Materials, so you probably don't need it as well.\n", BaseDirectoryName.c_str());
103    printf("\n");
104    printf("-bm runs a simple benchmark. Mostly intended for development purposes.\n");
105    printf("\n");
106
107    exit(0);
108}
109
110
111int main(int ArgC, char* ArgV[])
112{
113    const char*         TerrainName  =NULL;
114    const char*         MaterialName ="wireframe";  // The "wireframe" material is defined in file "meta.cmat".
115    bool                BenchMarkMode=false;
116    bool                UseSOARX     =false;        // SOARX is the new SOAR implementation.
117    ArrayT<const char*> MaterialScriptNames;
118
119
120    printf("\nCafu Engine Terrain Viewer (%s)\n\n", __DATE__);
121
122    // Initialize the FileMan by mounting the default file system.
123    // Note that specifying "./" (instead of "") as the file system description effectively prevents the use of
124    // absolute paths like "D:\abc\someDir\someFile.xy" or "/usr/bin/xy". This however should be fine for this application.
125    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_LOCAL_PATH, "./", "");
126    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/TechDemo.zip", "Games/DeathMatch/Textures/TechDemo/", "Ca3DE");
127    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/SkyDomes.zip", "Games/DeathMatch/Textures/SkyDomes/", "Ca3DE");
128
129    // Process the command line options.
130    for (int ArgNr=1; ArgNr<ArgC; ArgNr++)
131    {
132             if (_strnicmp(ArgV[ArgNr], "-t=" , 3)==0) TerrainName=ArgV[ArgNr]+3;
133        else if (_strnicmp(ArgV[ArgNr], "-m=" , 3)==0) MaterialName=ArgV[ArgNr]+3;
134        else if (_strnicmp(ArgV[ArgNr], "-bd=", 4)==0) BaseDirectoryName=ArgV[ArgNr]+4;
135        else if (_strnicmp(ArgV[ArgNr], "-ms=", 4)==0) MaterialScriptNames.PushBack(ArgV[ArgNr]+4);
136        else if (_stricmp (ArgV[ArgNr], "-bm"    )==0) BenchMarkMode=true;
137        else if (_stricmp (ArgV[ArgNr], "-sx"    )==0) UseSOARX=true;
138        else
139        {
140            printf("Sorry, I don't know what option \"%s\" means.\n", ArgV[ArgNr]);
141            Usage();
142        }
143    }
144
145    if (TerrainName==NULL)
146    {
147        printf("Hmm. Specifying a terrain name wouldn't hurt...\n");
148        printf("Please use the -t option in order to specify the desired terrain!\n");
149        Usage();
150    }
151
152    // Setup the global Material Manager pointer.
153    static MaterialManagerImplT MatManImpl;
154
155    MaterialManager=&MatManImpl;
156
157    // Register the material script files with the material manager.
158    if (MaterialScriptNames.Size()==0)
159    {
160        // The -ms option has not been used, so register all material script files in BaseDirectoryName/Materials.
161        MaterialManager->RegisterMaterialScriptsInDir(BaseDirectoryName+"/Materials", BaseDirectoryName+"/");
162    }
163    else
164    {
165        // Material script files have been specified - register them now.
166        for (unsigned long MSNNr=0; MSNNr<MaterialScriptNames.Size(); MSNNr++)
167            MaterialManager->RegisterMaterialScript(BaseDirectoryName+"/"+MaterialScriptNames[MSNNr], BaseDirectoryName+"/");
168    }
169
170    // Get the desired material.
171    MaterialT* TerrainMaterial=MaterialManager->GetMaterial(MaterialName);
172
173    if (TerrainMaterial==NULL)
174    {
175        printf("Sorry, I have not been able to get material \"%s\"\n", MaterialName);
176        printf("from the registered material script files. Possible causes:\n");
177        printf("- the material is not defined in any of the script files (material name typo?)\n");
178        printf("- the material script file(s) could not be opened (script file name typo?)\n");
179        printf("- the material script file contains bugs, i.e. syntax errors.\n");
180
181        return 0;
182    }
183
184
185    try
186    {
187        const unsigned long FRAMES_FOR_BENCHMARK=1000;
188        const Vector3fT     TerrainResolution(160.0f, 160.0f, 50.0f*255.0f);
189        TerrainT            TerrainNew(TerrainName, TerrainResolution);
190        const double        STEPDIST_FOR_BENCHMARK=TerrainNew.GetSize()*1.2*TerrainResolution.x/FRAMES_FOR_BENCHMARK;
191
192
193        // Open OpenGL-Window.
194        const char* ErrorMsg=SingleOpenGLWindow->Open("Cafu Terrain Viewer 1.2", BenchMarkMode ? 1280 : 800, BenchMarkMode ? 1024 : 600, 32, BenchMarkMode);
195
196        if (ErrorMsg)
197        {
198            printf("\nUnable to open OpenGL window: %s\n", ErrorMsg);
199            return 0;
200        }
201
202
203        // Get the renderer with the highest preference number that is supported.
204        HMODULE RendererDLL;
205        MatSys::Renderer=PlatformAux::GetBestRenderer(RendererDLL);
206
207        if (MatSys::Renderer==NULL || RendererDLL==NULL) { printf("No renderer loaded.\n"); SingleOpenGLWindow->Close(); return 0; }
208        MatSys::Renderer->Initialize();
209
210
211        // Get the TextureMapManager from the RendererDLL.
212        MatSys::TextureMapManagerI* TextureMapManager=PlatformAux::GetTextureMapManager(RendererDLL);
213
214        if (TextureMapManager==NULL) { printf("No TextureMapManager obtained.\n"); FreeLibrary(RendererDLL); SingleOpenGLWindow->Close(); return 0; }
215
216
217        MatSys::RenderMaterialT* TerrainRenderMat=MatSys::Renderer->RegisterMaterial(TerrainMaterial);
218
219        MatSys::Renderer->SetCurrentMaterial(TerrainRenderMat);
220
221
222        // As the terrain shaders require (cmat materials to specify) a lightmap, provide one here.
223        char Data[]={ 255, 255, 255, 255, 255, 255, 0, 0,
224                      255, 255, 255, 255, 255, 255, 0, 0 };
225
226        MatSys::Renderer->SetCurrentLightMap(TextureMapManager->GetTextureMap2D(Data, 2, 2, 3, true, MapCompositionT(MapCompositionT::Linear, MapCompositionT::Linear)));
227        MatSys::Renderer->SetCurrentLightDirMap(NULL);      // The MatSys provides a default for LightDirMaps when NULL is set.
228
229
230        MatSys::Renderer->ClearColor(0.0f, 0.0f, 0.4f, 0.0f);
231
232
233
234        /* This is how it used to be.
235           We now try it via the vertex indices directly... */
236
237        const float res0=160.0;
238        const float res1=160.0;
239
240        // BBmin and BBmax are BBs for the xy-plane, z is not needed here.
241        // Also note that the y-components have been multiplied by -1, or otherwise the texture gets flipped.
242        const float BBmin[2]={ -res0*0.5f*(TerrainNew.GetSize()-1),  res1*0.5f*(TerrainNew.GetSize()-1) };
243        const float BBmax[2]={  res0*0.5f*(TerrainNew.GetSize()-1), -res1*0.5f*(TerrainNew.GetSize()-1) };
244
245        const float Plane1[4]={ 1.0f/(BBmax[0]-BBmin[0]), 0.0f, 0.0f, -BBmin[0]/(BBmax[0]-BBmin[0]) };
246        const float Plane2[4]={ 0.0f, 1.0f/(BBmax[1]-BBmin[1]), 0.0f, -BBmin[1]/(BBmax[1]-BBmin[1]) };
247
248        MatSys::Renderer->SetGenPurposeRenderingParam( 4, Plane1[0]);
249        MatSys::Renderer->SetGenPurposeRenderingParam( 5, Plane1[1]);
250        MatSys::Renderer->SetGenPurposeRenderingParam( 6, Plane1[2]);
251        MatSys::Renderer->SetGenPurposeRenderingParam( 7, Plane1[3]);
252        MatSys::Renderer->SetGenPurposeRenderingParam( 8, Plane2[0]);
253        MatSys::Renderer->SetGenPurposeRenderingParam( 9, Plane2[1]);
254        MatSys::Renderer->SetGenPurposeRenderingParam(10, Plane2[2]);
255        MatSys::Renderer->SetGenPurposeRenderingParam(11, Plane2[3]);
256
257
258
259        TerrainT::ViewInfoT VI;
260
261        VI.cull=true;               // perform view culling when set
262        bool VI_morph=true;         // perform geomorphing when set
263
264        // Master Game Loop
265        TimerT        Timer;
266        unsigned long FrameCounter=0;
267        unsigned long GeomCRC=adler32(0, NULL, 0);  // We use Adler-32 instead of CRC-32, as Adler is faster but just as reliable.
268        Vector3fT     ViewerPos=BenchMarkMode ? Vector3fT(TerrainNew.GetVertices()[0])+Vector3fT(0, 0, 4000.0f) : Vector3fT(0, -500.0f, 1000.0f);
269        float         Heading=BenchMarkMode ? 55.0f : 0.0f;
270        float         Pitch  =BenchMarkMode ? 25.0f : 0.0f;
271
272        while (true)
273        {
274            // Rufe die Nachrichten der Windows-Nachrichtenschlange ab.
275            if (SingleOpenGLWindow->HandleWindowMessages()) break;
276
277            MatSys::Renderer->BeginFrame(Timer.GetSecondsSinceCtor());
278
279         // MatSys::Renderer->GetModifyMatrix(MatSys::RendererI::MODEL_TO_WORLD)=MatrixT();
280
281            MatSys::Renderer->SetMatrix(MatSys::RendererI::WORLD_TO_VIEW, MatrixT::GetRotateXMatrix(-90.0f));
282            MatSys::Renderer->RotateX  (MatSys::RendererI::WORLD_TO_VIEW, Pitch  );
283            MatSys::Renderer->RotateZ  (MatSys::RendererI::WORLD_TO_VIEW, Heading);
284            MatSys::Renderer->Translate(MatSys::RendererI::WORLD_TO_VIEW, -ViewerPos.x, -ViewerPos.y, -ViewerPos.z);
285
286            // if (TextureName==NULL) glColor3f(0.6, 1.0, 0.5);
287            // if (TextureName==NULL) glDisable(GL_POLYGON_OFFSET_FILL);
288            // if (TextureName==NULL) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
289
290
291            const float tau    =4.0;    // Error tolerance in pixel.
292         // const float tau_min=tau;
293         // const float tau_max=VI_morph ? (3.0/2.0)*tau_min : tau_min;
294            // The basic formula for fov_x is from soar/main.c, reshape_callback function, rearranged for fov_x.
295            // The 67.5 is a fixed value from OpenGLWindow.cpp.
296            const float fov_x  =2.0f*atan(float(SingleOpenGLWindow->GetWidth())/float(SingleOpenGLWindow->GetHeight())*tan(DEG2RAD(67.5f)/2.0f));
297            const float kappa  =tau/SingleOpenGLWindow->GetWidth() * fov_x;
298
299            VI.nu    =kappa>0.0 ? 1.0f/kappa : FLT_MAX; // inverse of error tolerance in radians
300            VI.nu_min=2.0f/3.0f*VI.nu;                  // lower morph parameter
301            VI.nu_max=          VI.nu;                  // upper morph parameter
302
303            VI.viewpoint=ViewerPos;
304
305            // Set up VI.viewplanes (clip planes) for view frustum culling.
306            MatrixT mpv=MatSys::Renderer->GetMatrix(MatSys::Renderer->PROJECTION)*MatSys::Renderer->GetMatrixModelView();
307
308            // Compute view frustum planes.
309            for (unsigned long i=0; i<5; i++)
310            {
311                // m can be used to easily minify / shrink the view frustum.
312                // The values should be between 0 and 8: 0 is the default (no minification), 8 is the reasonable maximum.
313                const char  m=0;
314                const float d=(i<4 && m>0) ? 1.0f-0.75f*m/8.0f : 1.0f;
315                float       plane[4];
316
317                for (unsigned long j=0; j<4; j++)
318                    plane[j]=((i & 1) ? mpv.m[i/2][j] : -mpv.m[i/2][j]) - d*mpv.m[3][j];
319
320                const float l=sqrt(SQR(plane[0])+SQR(plane[1])+SQR(plane[2]));
321
322                VI.viewplanes[i]=Plane3fT(Vector3fT(plane[0]/l, plane[1]/l, plane[2]/l), -plane[3]/l);
323            }
324
325
326            static MatSys::MeshT TerrainMesh(MatSys::MeshT::TriangleStrip);
327            TerrainMesh.Vertices.Overwrite();
328
329            if (UseSOARX)
330            {
331                ArrayT<Vector3fT>& VectorStrip=TerrainNew.ComputeVectorStrip(VI);
332
333                // With SOARX, the vector strip begins as usual and as expected with the first vector.
334                for (unsigned long VNr=0; VNr<VectorStrip.Size(); VNr++)
335                {
336                    TerrainMesh.Vertices.PushBackEmpty();
337                    TerrainMesh.Vertices[VNr].SetOrigin(VectorStrip[VNr]);
338
339                    // Update the geometry-CRC. We use Adler-32 instead of CRC-32, as Adler is faster but just as reliable.
340                    GeomCRC=adler32(GeomCRC, (Bytef*)&VectorStrip[VNr].z, sizeof(VectorStrip[VNr].z));
341                }
342            }
343            else
344            {
345                if (VI_morph)
346                {
347                    ArrayT<Vector3fT>& VectorStripNew=TerrainNew.ComputeVectorStripByMorphing(VI);
348
349                    // Note that the first VectorT at VectorStrip[0] must be skipped!
350                    for (unsigned long VNr=1; VNr<VectorStripNew.Size(); VNr++)
351                    {
352                        TerrainMesh.Vertices.PushBackEmpty();
353                        TerrainMesh.Vertices[TerrainMesh.Vertices.Size()-1].SetOrigin(VectorStripNew[VNr]);
354
355                        // Update the geometry-CRC. We use Adler-32 instead of CRC-32, as Adler is faster but just as reliable.
356                        GeomCRC=adler32(GeomCRC, (Bytef*)&VectorStripNew[VNr].z, sizeof(VectorStripNew[VNr].z));
357                    }
358                }
359                else
360                {
361                    ArrayT<unsigned long>& IdxStripNew=TerrainNew.ComputeIndexStripByRefinement(VI);
362
363                    // Note that the first index at IdxStrip[0] must be skipped!
364                    const TerrainT::VertexT* Vertices=TerrainNew.GetVertices();
365
366                    for (unsigned long IdxNr=1; IdxNr<IdxStripNew.Size(); IdxNr++)
367                    {
368                        TerrainMesh.Vertices.PushBackEmpty();
369                        TerrainMesh.Vertices[TerrainMesh.Vertices.Size()-1].SetOrigin(Vertices[IdxStripNew[IdxNr]]);
370
371                        // Update the geometry-CRC. We use Adler-32 instead of CRC-32, as Adler is faster but just as reliable.
372                        GeomCRC=adler32(GeomCRC, (Bytef*)&IdxStripNew[IdxNr], sizeof(IdxStripNew[IdxNr]));
373                    }
374                }
375            }
376
377            MatSys::Renderer->RenderMesh(TerrainMesh);
378
379            // if (TextureName==NULL) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
380            // if (TextureName==NULL) glEnable(GL_POLYGON_OFFSET_FILL);
381
382
383            double DeltaTime=Timer.GetSecondsSinceLastCall();
384
385            float   MoveSpeed=1000.0f*float(DeltaTime);
386            float   RotSpeed =  90.0f*float(DeltaTime);
387
388
389            MatSys::Renderer->EndFrame();
390            SingleOpenGLWindow->SwapBuffers();
391
392            FrameCounter++;
393
394            CaKeyboardEventT KE;
395            bool QuitProgram=false;
396
397            while (SingleOpenGLWindow->GetNextKeyboardEvent(KE)>0)
398            {
399                if (KE.Type!=CaKeyboardEventT::CKE_KEYDOWN) continue;
400                if (KE.Key==CaKeyboardEventT::CK_ESCAPE) QuitProgram=true;
401                if (KE.Key==CaKeyboardEventT::CK_C     ) { VI.cull =!VI.cull;  printf("View frustum culling is %s.\n", VI.cull ? "ON" : "OFF"); }
402                if (KE.Key==CaKeyboardEventT::CK_M     ) { VI_morph=!VI_morph; printf("Geo-morphing is %s\n", VI_morph ? "ON" : "OFF"); }
403            }
404
405            if (QuitProgram) break;
406            if (BenchMarkMode && FrameCounter==FRAMES_FOR_BENCHMARK) break;
407
408
409            if (BenchMarkMode)
410            {
411                const float vx=float(STEPDIST_FOR_BENCHMARK)*sin(Heading/180.0f*3.1415926f);
412                const float vy=float(STEPDIST_FOR_BENCHMARK)*cos(Heading/180.0f*3.1415926f);
413
414                ViewerPos=ViewerPos+Vector3fT(vx, vy, 0);
415            }
416            else
417            {
418                const float vx=MoveSpeed*sin(Heading/180.0f*3.1415926f);
419                const float vy=MoveSpeed*cos(Heading/180.0f*3.1415926f);
420
421                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_UP    ] || SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_W]) ViewerPos=ViewerPos+Vector3fT( vx,  vy, 0);
422                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_DOWN  ] || SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_S]) ViewerPos=ViewerPos+Vector3fT(-vx, -vy, 0);
423                if (                                                                       SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_A]) ViewerPos=ViewerPos+Vector3fT(-vy,  vx, 0);
424                if (                                                                       SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_D]) ViewerPos=ViewerPos+Vector3fT( vy, -vx, 0);
425                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_INSERT] || SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_R]) ViewerPos.z+=MoveSpeed;
426                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_DELETE] || SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_F]) ViewerPos.z-=MoveSpeed;
427                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_LEFT  ]                                                                  ) Heading-=RotSpeed;
428                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_RIGHT ]                                                                  ) Heading+=RotSpeed;
429                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_PGUP  ]                                                                  ) Pitch-=RotSpeed;
430                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_PGDN  ]                                                                  ) Pitch+=RotSpeed;
431                if (SingleOpenGLWindow->GetKeyboardState()[CaKeyboardEventT::CK_END   ]                                                                  ) Pitch=0.0;
432            }
433        }
434
435        const double TotalTime=Timer.GetSecondsSinceCtor();
436        printf("Average frame-rate was: %.2f FPS   (%lu frames in %.2f seconds)\n", double(FrameCounter)/TotalTime, FrameCounter, TotalTime);
437        printf("Geo-morphing    was: %s\n", VI_morph ? " ON" : "OFF");
438        printf("Frustum culling was: %s\n", VI.cull  ? " ON" : "OFF");
439        printf("Geometry CRC    was: 0x%lX\n", GeomCRC);
440
441        // Clean-up.
442        MatSys::Renderer->FreeMaterial(TerrainRenderMat);
443        MatSys::Renderer->Release();
444        MatSys::Renderer=NULL;
445        FreeLibrary(RendererDLL);
446        SingleOpenGLWindow->Close();
447    }
448    catch (const TerrainT::InitError& /*E*/)
449    {
450        printf("\nEither \"%s\" could not be found, not be read,\n", TerrainName);
451        printf("is not square, is smaller than 3 pixels, or not of size 2^n+1.  Sorry.\n");
452    }
453
454    return 0;
455}
Note: See TracBrowser for help on using the browser.