aesop-core/tool/terrain/terraHeightGen/main.cpp

Go to the documentation of this file.
00001 /*
00002  * main.cpp
00003  *
00004  * Copyright (C) 2009,2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  */
00031 
00032 // includes --------------------------------------------------------------------
00033 #include <iostream>
00034 #include <fstream>
00035 #include <math.h>
00036 
00037 #include "common/wave_ex.h"
00038 #include "datahash/datahash_text.h"
00039 #include "datahash/datahash_util.h"
00040 #include "geometry/geometry_2d.h"
00041 #include "geometry/geometry_3d.h"
00042 #include "geometry/xform_2d.h"
00043 #include "perf/perf.h"
00044 #include "pgmppm/pgmppm.h"              // PGM/PPM files
00045 #include "util/date.h"
00046 #include "util/file.h"
00047 
00048 
00049 // statics and typedefs --------------------------------------------------------
00050 
00051 static const int s_minGrid              = 2;
00052 
00053 static const char * s_hfieldVersion     = "0.1";
00054 
00055 static const int s_yIntExtent           = 255;
00056 
00057 
00058 // fixed points
00059 typedef std::vector<point3d_t> vec_points_t;
00060 
00061 typedef point2d_t<float> point_t;
00062 typedef rect2d_t<float> rect_t;
00063 
00064 
00065 
00066 // heightfield data used
00067 struct hdata_t {
00068         // constructor, manipulators
00069         hdata_t(void) throw() : yData(NULL) { this->clear(); }
00070         ~hdata_t(void) throw() { this->clear(); }
00071         void clear(void) throw() {
00072                         xGrid = zGrid = 0;
00073                         nFloats = 0;
00074                         xzScale = 0;
00075                         yScale = -1;
00076                         yMin = yMax = 0;
00077                         fixedPoints.clear();
00078                         if (yData) {
00079                                 delete[] yData;
00080                                 yData = NULL;
00081                         }
00082                 }
00083 
00084         void allocate(void) {
00085                         ASSERT(!yData, "Already allocated?");
00086                         nFloats = (xGrid + 1) * (zGrid + 1);
00087                         DPRINTF("Allocating grid of %d floats total",
00088                             nFloats);
00089                         yData = new float[nFloats];
00090                         ASSERT_THROW(yData, "out of memory");
00091                 }
00092 
00093         void setHeightInt(IN int x, IN int z, IN float y) {
00094                         ASSERT(yData, "need to allocate");
00095                         int idx = getIndex(x, z);
00096                         yData[idx] = y;
00097                 }
00098 
00099         void setHeightFloat(IN float x, IN float z, IN float y) {
00100                         int ix = (int)((x / xzScale) + 0.5);
00101                         int iz = (int)((z / xzScale) + 0.5);
00102                         //DPRINTF("  ix,iz = (%d, %d)", ix, iz);
00103                         setHeightInt(ix, iz, y);
00104                 }
00105 
00106         float getHeightInt(IN int x, IN int z) const {
00107                         ASSERT(yData, "need to allocate");
00108                         int idx = getIndex(x, z);
00109                         return yData[idx];
00110                 }
00111 
00112         int getIndex(IN int x, IN int z) const {
00113                         ASSERT(x >= 0 && x <= xGrid, "bad x: %d", x);
00114                         ASSERT(z >= 0 && z <= zGrid, "bad z: %d", z);
00115                         return (z * (xGrid + 1)) + x;
00116                 }
00117 
00118         float getYScale(void) const throw() {
00119                         return yScale;
00120                 }
00121 
00122         float getXZScale(void) const throw() {
00123                         return xzScale;
00124                 }
00125 
00126         // data fields
00127         int             xGrid;          // grid point width in x-direction
00128         int             zGrid;
00129         int             nFloats;
00130         float           xzScale;        // distance between adjacent points
00131         float           yScale;
00132         float           yMin;
00133         float           yMax; 
00134         float *         yData;          // the actual y-Data at each x,z value
00135         vec_points_t    fixedPoints;
00136         vec_points_t    seedPoints;
00137 };
00138 
00139 
00140 
00141 ////////////////////////////////////////////////////////////////////////////////
00142 ///
00143 /// \ingroup terra_tool
00144 /// \defgroup terraHeightGen terraHeightGen - heightmap generation
00145 ///
00146 /// Given an input file, this generates a heightmap, from which you can then
00147 /// generate a texure to get a complete terrain object.
00148 ///
00149 /// Sample usage:
00150 /// \code
00151 ///     % terraHeightGen sample-input.txt sample
00152 /// \endcode
00153 ///
00154 /// That will read the file "sample-input.txt" for parameters, and then
00155 /// output two files:
00156 ///  - <b>sample.hfield</b> This is the hfield file that the Wavepacket GLUT
00157 ///     heightfield library uses.
00158 ///  - <b>sample.pgm</b> This is a PGM file containing height values.
00159 ///
00160 /// Note that <b>sample.ppm</b> is referred to by the hfield file, but the
00161 /// PPM does not exist!  Use terraTextureGen to create the ppm file from these
00162 /// two input files.
00163 ///
00164 ////////////////////////////////////////////////////////////////////////////////
00165 
00166 static float
00167 randX
00168 (
00169 IN float max
00170 )
00171 throw()
00172 {
00173         // given max, returns a random number between -max and +max
00174         static const float s_invMax = 1.0 / RAND_MAX;
00175         return 2.0 * (rand() * s_invMax - 0.5) * max;
00176 }
00177 
00178 
00179 
00180 static float
00181 randMid
00182 (
00183 IN float min,
00184 IN float max
00185 )
00186 throw()
00187 {
00188         // return a random point close to the middle of the min/max
00189         float W = max - min;
00190         float avg = 0.5 * (min + max);
00191 
00192         return avg + randX(0.3 * W);    
00193 }
00194 
00195 
00196 
00197 static void
00198 parsePoints
00199 (
00200 IN hdata_t * hdata,
00201 IN const Datahash * input,
00202 IN const char * name,
00203 IN float yMax,
00204 OUT vec_points_t& vec
00205 )
00206 {
00207         ASSERT(hdata, "null");
00208         ASSERT(input, "null");
00209         ASSERT(name, "null");
00210         vec.clear();
00211 
00212         // any there?
00213         if (!input->count(name)) {
00214                 return;         // nope
00215         }
00216 
00217         // determine some values
00218         float xMax = hdata->xGrid * hdata->xzScale;
00219         float zMax = hdata->zGrid * hdata->xzScale;
00220 
00221         Datahash::iterator_t i;
00222         input->getIterator(name, i);
00223         while (const hash_value_t * phv = input->getNextElementUnsafe(i)) {
00224                 if (phv->hash)
00225                         continue;       // skip complex values
00226                 const char * val = phv->text.c_str();
00227 
00228                 point3d_t fp;
00229                 parsePoint3d(val, fp);
00230                 fp.dump(name);
00231 
00232                 // x,z coordinates should be 0-1
00233                 ASSERT_THROW(fp.x >= 0 && fp.x <= 1, "Bad x: " << fp.x);
00234                 ASSERT_THROW(fp.z >= 0 && fp.z <= 1, "Bad z: " << fp.z);
00235 
00236                 // okay, now scale the x,z coordinates
00237                 fp.x *= xMax;
00238                 fp.z *= zMax;
00239                 fp.y *= yMax;
00240 
00241                 vec.push_back(fp);
00242         }
00243         DPRINTF("Parsed %d points of name '%s'", (int) vec.size(), name);
00244 }
00245 
00246 
00247 
00248 static void
00249 overlayPoints
00250 (
00251 IN hdata_t * hdata,
00252 IN vec_points_t& vec
00253 )
00254 {
00255         perf::Timer timer("overlayFixed");
00256         ASSERT(hdata, "null");
00257 
00258         // run through all points
00259         for (vec_points_t::const_iterator i = vec.begin(); i != vec.end();
00260              ++i) {
00261                 const point3d_t& fp = *i;
00262 
00263         //      fp.dump("Setting this now...");
00264                 hdata->setHeightFloat(fp.x, fp.z, fp.y);
00265         }
00266 }
00267 
00268 
00269 
00270 static void
00271 calculateYScale
00272 (
00273 IN hdata_t * hdata
00274 )
00275 {
00276         ASSERT(hdata, "null");
00277         ASSERT(hdata->yData, "should have allocated by now");
00278         ASSERT(hdata->nFloats > 0, "no data?");
00279 
00280         float yMin = hdata->yData[0];
00281         float yMax = yMin;
00282 
00283         for (int i = 1; i < hdata->nFloats; ++i) {
00284                 float y = hdata->yData[i];
00285                 if (y < yMin) {
00286                         yMin = y;
00287                 } else if (y > yMax) {
00288                         yMax = y;
00289                 }
00290         }
00291 
00292         DPRINTF("Calculated (ymin,ymax) = (%f, %f)", yMin, yMax);
00293         float dY = yMax - yMin;
00294         if (dY <= 0.0) {
00295                 hdata->yScale = 1.0;
00296         } else {
00297                 hdata->yScale = dY / s_yIntExtent;
00298         }
00299         DPRINTF("  Calculated yScale: %f", hdata->getYScale());
00300         hdata->yMin = yMin;
00301         hdata->yMax = yMax;
00302 }
00303 
00304 
00305 
00306 static void
00307 generateFlat
00308 (
00309 IN hdata_t * hdata,
00310 IN const Datahash * algo
00311 )
00312 {
00313         ASSERT(hdata, "null");
00314         ASSERT(algo, "null");
00315 
00316         // get the flat y-value to use
00317         float y = getFloat(algo, "y");
00318         DPRINTF("Flat algorithm is using base y-value: %f", y);
00319 
00320         // set all grid points to flat y value
00321         int xGrid = hdata->xGrid;
00322         int zGrid = hdata->zGrid;
00323         for (int i = 0; i <= xGrid; ++i) {
00324                 for (int j = 0; j <= zGrid; ++j) {
00325                         hdata->setHeightInt(i, j, y);
00326                 }
00327         }
00328 }
00329 
00330 
00331 
00332 static void
00333 iterateFractal
00334 (
00335 IN hdata_t * hdata,
00336 IN int dx,
00337 IN float dy
00338 )
00339 {
00340         ASSERT(hdata, "null");
00341         ASSERT(dx > 0, "bad dx: %d", dx);
00342         ASSERT(dy > 0, "bad dy: %f", dy);
00343 
00344         int W = hdata->xGrid;
00345 
00346         // first loop: go in Z-rows, add X midpoints
00347         int mid = dx / 2;
00348         for (int z = 0; z <= W; z += dx) {
00349                 for (int x = mid; x <= W; x += dx) {
00350                         float y0 = hdata->getHeightInt(x - mid, z);
00351                         float y1 = hdata->getHeightInt(x + mid, z);
00352 //                      float y = hdata->getHeightInt(x, z);
00353                 //      float avg = y0 + y1 + randX(dy) + 2 * y;
00354                         float avg = y0 + y1 + randX(dy);
00355                         hdata->setHeightInt(x, z, 0.5 * avg);
00356                 }
00357         }
00358 
00359         // second loop: go in X-rows, add Z midpoints
00360         for (int x = 0; x <= W; x += dx) {
00361                 for (int z = mid; z <= W; z += dx) {
00362                         float y0 = hdata->getHeightInt(x, z - mid);
00363                         float y1 = hdata->getHeightInt(x, z + mid);
00364 //                      float y = hdata->getHeightInt(x, z);
00365 
00366                         //float avg = y0 + y1 + randX(dy) + 2 * y;
00367                         float avg = y0 + y1 + randX(dy);
00368                         hdata->setHeightInt(x, z, 0.5 * avg);
00369                 }
00370         }
00371 
00372         // third loop: go in middle of both
00373         for (int x = mid; x <= W; x += dx) {
00374                 for (int z = mid; z <= W; z += dx) {
00375                         float y0 = hdata->getHeightInt(x, z - mid);
00376                         float y1 = hdata->getHeightInt(x, z + mid);
00377                         float y2 = hdata->getHeightInt(x - mid, z);
00378                         float y3 = hdata->getHeightInt(x + mid, z);
00379                         //float y = hdata->getHeightInt(x, z);
00380 //                      DPRINTF("y = %f", y);
00381                 //      float avg = y0 + y1 + y2 + y3 + 2 * randX(dy) + 4 * y;
00382                         float avg = y0 + y1 + y2 + y3;
00383                         hdata->setHeightInt(x, z, 0.25 * avg);
00384                 }
00385         }
00386 }
00387 
00388 
00389 
00390 static void
00391 generateFractal
00392 (
00393 IN hdata_t * hdata,
00394 IN const Datahash * algo
00395 )
00396 {
00397         ASSERT(hdata, "null");
00398         ASSERT(algo, "null");
00399 
00400         // has to be square!
00401         ASSERT_THROW(hdata->xGrid == hdata->zGrid,
00402             "Has to be a square grid!");
00403         int W = hdata->xGrid;
00404 
00405         // get important factors
00406         float dy = getFloat(algo, "dy");
00407         ASSERT_THROW(dy >= 0, "Bad dy: " << dy);
00408 
00409         float reduce = getFloat(algo, "reduce");
00410         ASSERT_THROW(reduce >=0 && reduce <= 1,
00411             "Bad reduce: " << reduce);
00412 
00413         // get seed points
00414         vec_points_t seeds;
00415         parsePoints(hdata, algo, "seed", dy, seeds);
00416 
00417         // overlay the fixed points
00418         overlayPoints(hdata, seeds);
00419 
00420         // how many iterations?
00421         int nIter = (int)((log(W) / log(2)) + 0.5);
00422         DPRINTF("W=%d --> %d iterations", W, nIter);
00423 
00424         int dx = W;
00425         while (dx > 1) {
00426                 // okay, do this iteration!
00427                 iterateFractal(hdata, dx, dy);
00428                 dx /= 2;
00429                 dy *= reduce;
00430         }
00431 }
00432 
00433 
00434 
00435 static void
00436 getTransform
00437 (
00438 IN float X,
00439 IN float Z,
00440 OUT xform_2d_t& T
00441 )
00442 {
00443         // NOTE: R can be negative!
00444         T.clear();
00445 
00446         // get origin
00447         float x = randMid(0, 1) * X;
00448         float z = randMid(0, 1) * Z;
00449 
00450         xform_2d_t trans;
00451         trans.setTranslate(-x, -z);
00452 
00453         // now get rotation angle
00454         float angle = randX(M_PI);
00455         xform_2d_t rotate;
00456         rotate.setZRotate(angle);
00457 
00458         T.setToProductOf(rotate, trans);
00459 }
00460 
00461 
00462 
00463 static void
00464 addSplit
00465 (
00466 IN hdata_t * hdata,
00467 IN const xform_2d_t& T,
00468 IN float dy
00469 )
00470 throw()
00471 {
00472         ASSERT(hdata, "NULL");
00473         ASSERT(dy > 0, "Bad dy");
00474 
00475         float xzScale = hdata->xzScale;
00476         int isign = 1 - 2 * (rand() % 2);
00477 
00478         // now walk through all points and adjust based on split 
00479         for (int ix = 0; ix <= hdata->xGrid; ++ix) {
00480                 float x = ix * xzScale;
00481                 for (int iz = 0; iz <= hdata->zGrid; ++iz)  {
00482                         float z = iz * xzScale;
00483 
00484                         point_t p(x, z);
00485                         point_t q;
00486                         T.transformPoint(p, q);
00487         //              DPRINTF("(%f, %f) --> (%f, %f)", p.x, p.y, q.x, q.y);
00488                         float y = hdata->getHeightInt(ix, iz);
00489                         if (q.y < 0) {
00490                                 hdata->setHeightInt(ix, iz, y + isign * dy);
00491                         } else {
00492                                 hdata->setHeightInt(ix, iz, y - isign * dy);
00493                         }
00494                 }
00495         }
00496 }
00497 
00498 
00499 
00500 static void
00501 generateSplit
00502 (
00503 IN hdata_t * hdata,
00504 IN const Datahash * algo
00505 )
00506 {
00507         ASSERT(hdata, "null");
00508         ASSERT(algo, "null");
00509 
00510         float dy = getFloat(algo, "dy");
00511         ASSERT_THROW(dy > 0, "Bad splitting dy: " << dy);
00512 
00513         float nIter = getInt(algo, "nIter");
00514         ASSERT_THROW(nIter > 0, "Bad split iteration count: " << nIter);
00515 
00516         // get other parameters
00517         float xzScale = hdata->xzScale;
00518         float W = hdata->xGrid * xzScale;
00519         float L = hdata->zGrid * xzScale;
00520 
00521         // now walk through iterations
00522         int splitsPerIteration = 1;
00523         for (int n = 0; n < nIter; ++n) {
00524                 for (int i = 0; i < splitsPerIteration; ++i) {
00525                         xform_2d_t T;
00526                         getTransform(W, L, T);
00527                         addSplit(hdata, T, dy);
00528                 }
00529                 splitsPerIteration *= 2;
00530                 dy /= 2;
00531         }
00532 }
00533 
00534 
00535 
00536 static smart_ptr<hdata_t>
00537 createHeightfield
00538 (
00539 IN Datahash * input
00540 )
00541 {
00542         ASSERT(input, "null");
00543 
00544         smart_ptr<hdata_t> hdata = new hdata_t;
00545         ASSERT_THROW(hdata, "out of memory");
00546 
00547         // pull out x,z grid values
00548         hdata->xGrid = getInt(input, "xGrid");
00549         hdata->zGrid = getInt(input, "zGrid");
00550         DPRINTF("Heightfield grid extent is %dx%d", hdata->xGrid, hdata->zGrid);
00551         DPRINTF("  (This means we store a %dx%d grid)",
00552             hdata->xGrid + 1, hdata->zGrid + 1);
00553         ASSERT_THROW(hdata->xGrid >= s_minGrid,
00554             "xGrid is too small: " << hdata->xGrid << " vs. " << s_minGrid);
00555         ASSERT_THROW(hdata->zGrid >= s_minGrid,
00556             "zGrid is too small: " << hdata->zGrid << " vs. " << s_minGrid);
00557 
00558         // pull out scaling factor
00559         hdata->xzScale = getFloat(input, "xzScale");
00560         ASSERT_THROW(hdata->xzScale > 0.0,
00561             "Bad xzScale: " << hdata->xzScale);
00562 
00563         // allocate memory
00564         hdata->allocate();
00565 
00566         // iterate algorithms
00567         Datahash::iterator_t i;
00568         input->getIterator("algorithm", i);
00569         std::string key;
00570         hash_value_t hv;
00571         while (input->getNextElement(i, key, hv)) {
00572                 if (!hv.hash)
00573                         continue;       // skip simple elements
00574                 const Datahash * algo = hv.hash;
00575                 ASSERT(algo, "null");
00576                 const char * type = getString(algo, "type");
00577                 DPRINTF("Terrain generation algorithm: %s", type);
00578                 std::string timerName = "algorithm timer (type=";
00579                 timerName += type;
00580                 timerName += ")";
00581                 {
00582                         perf::Timer timer(timerName.c_str());
00583                         if (!strcmp("flat", type)) {
00584                                 generateFlat(hdata, algo);
00585                         } else if (!strcmp("fractal", type)) {
00586                                 generateFractal(hdata, algo);
00587                         } else if (!strcmp("split", type)) {
00588                                 generateSplit(hdata, algo);
00589                         } else {
00590                                 ASSERT_THROW(false,
00591                                     "Unknown terrain generation algorithm "
00592                                     "type: '" << type << "'");
00593                         }
00594                 }
00595         }
00596 
00597         // all done!
00598         return hdata;
00599 }
00600 
00601 
00602 
00603 static smart_ptr<hdata_t>
00604 createHeightfield
00605 (
00606 IN const char * inputFile
00607 )
00608 {
00609         ASSERT(inputFile, "null");
00610 
00611         smart_ptr<Datahash> input = readHashFromTextFile(inputFile);
00612         ASSERT_THROW(input, "failed to create input hash from file: "
00613             << inputFile);
00614 
00615         smart_ptr<hdata_t> hdata = createHeightfield(input);
00616         ASSERT_THROW(hdata, "Failed to create heightfield data?");
00617 
00618         // now calculate yScale
00619         calculateYScale(hdata);
00620         ASSERT(hdata->getYScale() > 0,
00621             "Should have calculated proper y-scale by now");
00622 
00623         return hdata;
00624 }
00625 
00626 
00627 
00628 static int
00629 writePgmPixel
00630 (
00631 IN void * context,
00632 IN int x,
00633 IN int z
00634 )
00635 {
00636         hdata_t * hdata = (hdata_t *) context;
00637         ASSERT(hdata, "null context?");
00638 
00639         int idx = hdata->getIndex(x, z);
00640         ASSERT(idx >= 0 && idx < hdata->nFloats, "Bad index: %d", idx);
00641 
00642         float y = hdata->yData[idx];
00643         float dy = y - hdata->yMin;
00644         int iy = int((dy / hdata->getYScale()) + 0.5);
00645 //      DPRINTF(" h(%d,%d) = %f --> dy = %f --> %d", x, z, y, dy, iy);
00646         return iy;
00647 }
00648 
00649 
00650 
00651 static void
00652 writeHeightfield
00653 (
00654 IN const hdata_t * hdata,
00655 IN const char * outputName
00656 )
00657 {
00658         perf::Timer timer("writeHeightfield");
00659         ASSERT(hdata, "null");
00660         ASSERT(outputName, "null");
00661 
00662         std::string hfieldFilename = outputName;
00663         hfieldFilename += ".hfield";
00664 
00665         std::string pgmFilename = outputName;
00666         pgmFilename += ".pgm";
00667 
00668         std::string date;
00669         getDisplayableDateFromNetTime(time(NULL), date);
00670 
00671         DPRINTF("Writing heightfield metadata to file: %s",
00672             hfieldFilename.c_str());
00673 
00674         std::ofstream output(hfieldFilename.c_str());
00675         ASSERT_THROW(output.good(), "Failed to open file for writing: "
00676             << hfieldFilename);
00677         output << "#\n";
00678         output << "# " << hfieldFilename << "\n";
00679         output << "#\n";
00680         output << "# This is an auto-generated hfield file.\n";
00681         output << "#\n";
00682         output << "# Generated: " << date << "\n";
00683         output << "#\n";
00684         output << "\n";
00685         output << "hfieldVersion\t" << s_hfieldVersion << "\n";
00686         output << "heightfield {\n";
00687         output << "\tyScale\t" << hdata->getYScale() << "\t# meters per y-tick\n";
00688         output << "\txzScale\t" << hdata->getXZScale() << "\t# meters per grid point\n";
00689         output << "\n";
00690         output << "\t# relative paths to heightfield and texture files\n";
00691         output << "\thfieldFile\t" << pgmFilename << "\n";
00692         output << "\ttextureFile\t" << outputName << ".ppm\n";
00693         output << "}\n";
00694 
00695         // okay, now write output PGM file
00696         std::ofstream pgmOut(pgmFilename.c_str());
00697         ASSERT_THROW(pgmOut.good(), "Failed to open file for writing: "
00698             << pgmFilename);
00699         pgmppm::writePgm(pgmOut, hdata->xGrid + 1, hdata->zGrid + 1,
00700             255, writePgmPixel, (void *) hdata);
00701 }
00702 
00703 
00704 
00705 ////////////////////////////////////////////////////////////////////////////////
00706 //
00707 //      entry point
00708 //
00709 ////////////////////////////////////////////////////////////////////////////////
00710 
00711 int
00712 main
00713 (
00714 IN int argc,
00715 IN const char * argv[]
00716 )
00717 {
00718         ASSERT(3 == argc, "usage: terraHeightGen <input-file> <output-name>");
00719         const char * inputFile = argv[1];
00720         const char * outputName = argv[2];
00721 
00722         // start everything up
00723         int retval = 0;
00724         try {
00725                 perf::Timer timer("overall program timer");
00726                 srand(time(NULL));
00727                 smart_ptr<hdata_t> hdata = createHeightfield(inputFile);
00728                 ASSERT(hdata, "null");
00729                 writeHeightfield(hdata, outputName);
00730         } catch (std::exception& e) {
00731                 DPRINTF("Exception during startup!");
00732                 DPRINTF("%s", e.what());
00733                 retval = 1;
00734         } catch (...) {
00735                 DPRINTF("Unknown exception during startup!");
00736                 retval = 2;
00737         }
00738 
00739         perf::dumpTimingSummary(std::cerr);
00740 
00741         return retval;
00742 }
00743