00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <iostream>
00034 #include <fstream>
00035
00036 #include "common/wave_ex.h"
00037 #include "datahash/datahash_text.h"
00038 #include "datahash/datahash_util.h"
00039 #include "hfield/heightfield.h"
00040 #include "perf/perf.h"
00041 #include "pgmppm/pgmppm.h"
00042 #include "util/date.h"
00043 #include "util/file.h"
00044
00045
00046
00047 typedef pgmppm::color_t color_t;
00048
00049
00050 typedef std::map<float, color_t> map_colors_t;
00051
00052
00053
00054
00055 struct texture_t {
00056
00057 texture_t(void) throw() : texture(NULL) { this->clear(); }
00058 ~texture_t(void) throw() { this->clear(); }
00059 void clear(void) throw() {
00060 hfield = NULL;
00061 texelsPerGrid = 0;
00062 xTexels = zTexels = 0;
00063 if (texture) {
00064 delete[] texture;
00065 texture = NULL;
00066 }
00067 colors.clear();
00068 }
00069
00070 void allocate(void) {
00071 ASSERT(!texture, "already allocated?");
00072 ASSERT(hfield, "null");
00073 ASSERT(texelsPerGrid > 0, "bad");
00074 int x = hfield->getWidth();
00075 int z = hfield->getLength();
00076 xTexels = (x - 1) * texelsPerGrid;
00077 zTexels = (z - 1) * texelsPerGrid;
00078
00079 DPRINTF("Texture will be %dx%d", xTexels, zTexels);
00080
00081 nTexels = xTexels * zTexels;
00082 DPRINTF(" %d texels total", nTexels);
00083
00084 texture = new color_t[nTexels];
00085 ASSERT_THROW(texture, "out of memory");
00086 }
00087
00088 color_t& getColor(IN int x, IN int z) const throw() {
00089 ASSERT(x >= 0 && x < xTexels, "bad x: %d", x);
00090 ASSERT(z >= 0 && z < zTexels, "bad z: %d", z);
00091 ASSERT(texture, "null");
00092
00093 int idx = (z * xTexels) + x;
00094 ASSERT(idx >= 0 && idx < nTexels, "bad idx: %d", idx);
00095
00096 return texture[idx];
00097 }
00098
00099
00100 smart_ptr<hfield::Heightfield> hfield;
00101 int texelsPerGrid;
00102 int xTexels;
00103 int zTexels;
00104 int nTexels;
00105 color_t * texture;
00106 map_colors_t colors;
00107 };
00108
00109
00110
00135
00136 static void
00137 verifyColor
00138 (
00139 IN int c,
00140 IN const char * name
00141 )
00142 {
00143 ASSERT_THROW(c >= 0 && c<= 255, "Bad color component: " <<
00144 name << " = " << c);
00145 }
00146
00147
00148
00149 static int
00150 randDelta
00151 (
00152 void
00153 )
00154 throw()
00155 {
00156 const int s_max = 16;
00157 const int s_mod = 2 * s_max + 1;
00158 int r = rand() % s_mod;
00159 return s_max - r;
00160 }
00161
00162
00163
00164 static void
00165 rComp
00166 (
00167 IO int& i
00168 )
00169 throw()
00170 {
00171
00172 i += randDelta();
00173 if (i < 0) {
00174 i = 0;
00175 }
00176 if (i > 255) {
00177 i = 255;
00178 }
00179 }
00180
00181
00182
00183 static color_t
00184 randomize
00185 (
00186 IN const color_t& i_c
00187 )
00188 throw()
00189 {
00190 color_t c = i_c;
00191 rComp(c.red);
00192 rComp(c.green);
00193 rComp(c.blue);
00194 return c;
00195 }
00196
00197
00198
00199 static color_t
00200 getColorForHeight
00201 (
00202 IN texture_t * t,
00203 IN float y
00204 )
00205 {
00206 ASSERT(t, "null");
00207 ASSERT(t->hfield, "null");
00208 ASSERT(y >= 0 && y <= 1, "bad relative y: %f", y);
00209
00210
00211
00212 const color_t * pc = NULL;
00213 for (map_colors_t::const_iterator i = t->colors.begin();
00214 i != t->colors.end(); ++i) {
00215 float h = i->first;
00216
00217 if (h > y) {
00218 break;
00219 }
00220 pc = &i->second;
00221 }
00222 if (pc)
00223 return randomize(*pc);
00224
00225
00226 color_t bad;
00227 bad.red = 255;
00228 bad.green = bad.blue = 0;
00229 return bad;
00230 }
00231
00232
00233
00234 static void
00235 setBaseTexture
00236 (
00237 IN texture_t * t
00238 )
00239 {
00240 perf::Timer timer("setBaseTexture");
00241 ASSERT(t, "null");
00242 ASSERT(t->hfield, "null");
00243 ASSERT(t->texelsPerGrid > 0, "bad");
00244 ASSERT(t->texture, "null");
00245
00246 float xzScale = t->hfield->getXZScale();
00247 xzScale *= (1.0 / t->texelsPerGrid);
00248
00249 short iMin = t->hfield->getMinHeight();
00250 short iMax = t->hfield->getMaxHeight();
00251
00252 float yScale = t->hfield->getYScale();
00253
00254 float yMin = yScale * iMin;
00255 float yMax = yScale * iMax;
00256 DPRINTF("yMin,yMax = %f, %f", yMin, yMax);
00257
00258 float dY = yMax - yMin;
00259 float invDY = 1;
00260 if (dY > 0) {
00261 invDY = 1.0 / dY;
00262 }
00263 DPRINTF("dY, 1/dY = %f, %f", dY, invDY);
00264
00265 color_t * p = t->texture;
00266 for (int iz = 0; iz < t->zTexels; ++iz) {
00267 float z = iz * xzScale;
00268 for (int ix = 0; ix < t->xTexels; ++ix, ++p) {
00269 float x = ix * xzScale;
00270 float y = t->hfield->getHeight(x, z);
00271 float r = (y - yMin) * invDY;
00272
00273 *p = getColorForHeight(t, r);
00274 }
00275 }
00276 }
00277
00278
00279
00280 static void
00281 addColor
00282 (
00283 IO color_t& sum,
00284 IN texture_t * t,
00285 IN int ix,
00286 IN int iz
00287 )
00288 {
00289 color_t c = t->getColor(ix, iz);
00290 sum.red += c.red;
00291 sum.green += c.green;
00292 sum.blue += c.blue;
00293 }
00294
00295
00296
00297 static void
00298 divideColor
00299 (
00300 IO color_t& sum,
00301 IN int count
00302 )
00303 {
00304 ASSERT(count > 0, "BAd count: %d", count);
00305
00306 sum.red /= count;
00307 sum.green /= count;
00308 sum.blue /= count;
00309 }
00310
00311
00312
00313 static void
00314 smooth
00315 (
00316 IN texture_t * t
00317 )
00318 {
00319 perf::Timer timer("smooth");
00320 ASSERT(t, "null");
00321 ASSERT(t->texture, "null");
00322
00323 color_t * newTexture = new color_t[t->nTexels];
00324 ASSERT_THROW(newTexture, "out of memory");
00325
00326 const color_t * p = t->texture;
00327 color_t * q = newTexture;
00328 for (int j = 0; j < t->zTexels; ++j) {
00329 for (int i = 0; i < t->xTexels; ++i, ++p, ++q) {
00330 int nAdj = (rand() % 3);
00331 color_t sum = *p;
00332 sum.red *= nAdj;
00333 sum.green *= nAdj;
00334 sum.blue *= nAdj;
00335
00336 if (i > 0) {
00337 ++nAdj;
00338 addColor(sum, t, i - 1, j);
00339 } else if (i < t->xTexels - 1) {
00340 ++nAdj;
00341 addColor(sum, t, i + 1, j);
00342 }
00343 if (j > 0) {
00344 ++nAdj;
00345 addColor(sum, t, i, j - 1);
00346 } else if (j < t->zTexels - 1) {
00347 ++nAdj;
00348 addColor(sum, t, i, j + 1);
00349 }
00350 divideColor(sum, nAdj);
00351
00352 *q = sum;
00353 }
00354 }
00355
00356
00357 color_t * old = t->texture;
00358 t->texture = newTexture;
00359 delete[] old;
00360 }
00361
00362
00363
00364 static smart_ptr<texture_t>
00365 createTexture
00366 (
00367 IN smart_ptr<hfield::Heightfield>& hfield,
00368 IN const Datahash * params
00369 )
00370 {
00371 ASSERT(hfield, "null");
00372 ASSERT(params, "null");
00373
00374 hfield->dump("just read");
00375
00376 smart_ptr<texture_t> t = new texture_t;
00377 ASSERT_THROW(t, "out of memory");
00378
00379 t->hfield = hfield;
00380
00381
00382 t->texelsPerGrid = getInt(params, "texelsPerGridPoint");
00383 DPRINTF("Using %d texels per heightfield grid point",
00384 t->texelsPerGrid);
00385 ASSERT_THROW(t->texelsPerGrid > 0, "Bad texels per grid point: "
00386 << t->texelsPerGrid);
00387
00388
00389 t->allocate();
00390
00391
00392 Datahash::iterator_t i;
00393 params->getIterator("color", i);
00394 while (const hash_value_t * phv = params->getNextElementUnsafe(i)) {
00395 if (!phv->hash)
00396 continue;
00397 const Datahash * sub = phv->hash;
00398 ASSERT(sub, "null");
00399
00400 float height = getFloat(sub, "height");
00401 ASSERT_THROW(height >= 0 && height <= 1,
00402 "bad color height: " << height);
00403
00404 color_t c;
00405 c.red = getInt(sub, "red");
00406 c.green = getInt(sub, "green");
00407 c.blue = getInt(sub, "blue");
00408
00409 verifyColor(c.red, "red");
00410 verifyColor(c.green, "green");
00411 verifyColor(c.blue, "blue");
00412
00413 t->colors[height] = c;
00414 }
00415 DPRINTF("Read %d color entries", (int) t->colors.size());
00416
00417
00418 setBaseTexture(t);
00419
00420
00421 int nSmooth = 0;
00422 if (params->count("smoothCount")) {
00423 nSmooth = getInt(params, "smoothCount");
00424 }
00425 ASSERT_THROW(nSmooth >= 0, "Bad smooth count: " << nSmooth);
00426 for (int i = 0; i < nSmooth; ++i) {
00427 smooth(t);
00428 }
00429
00430
00431 return t;
00432 }
00433
00434
00435
00436 static color_t
00437 writePixel
00438 (
00439 IN void * context,
00440 IN int x,
00441 IN int z
00442 )
00443 {
00444 const texture_t * t = (const texture_t *) context;
00445 ASSERT(t, "null context");
00446
00447 return t->getColor(x, z);
00448 }
00449
00450
00451
00452 static void
00453 writeTexture
00454 (
00455 IN const texture_t * t,
00456 IN const char * outputFile
00457 )
00458 {
00459 perf::Timer timer("writeTexture");
00460 ASSERT(t, "null");
00461 ASSERT(outputFile, "null");
00462
00463 std::ofstream output(outputFile);
00464 ASSERT_THROW(output.good(), "Failed to open file for writing: "
00465 << outputFile);
00466
00467
00468 pgmppm::writePpm(output, t->xTexels, t->zTexels, 255,
00469 writePixel, (void *) t);
00470 }
00471
00472
00473
00475
00476
00477
00479
00480 int
00481 main
00482 (
00483 IN int argc,
00484 IN const char * argv[]
00485 )
00486 {
00487 ASSERT(3 == argc,
00488 "usage: terraTextureGen <hfield-file> <texture-param-file>");
00489 const char * hfieldFile = argv[1];
00490 const char * paramFile = argv[2];
00491
00492
00493 int retval = 0;
00494 try {
00495 perf::Timer timer("overall program timer");
00496
00497
00498 smart_ptr<nstream::Manager> mgr =
00499 nstream::getFilesystemManager(".");
00500 ASSERT(mgr, "null");
00501
00502
00503 smart_ptr<nstream::Stream> stream =
00504 nstream::openNamedStream(mgr, hfieldFile);
00505 ASSERT(stream, "null");
00506 smart_ptr<hfield::Heightfield> hfield =
00507 hfield::Heightfield::create(stream);
00508 ASSERT_THROW(hfield, "Failed to load hfield");
00509
00510
00511 smart_ptr<Datahash> params = readHashFromTextFile(paramFile);
00512 ASSERT_THROW(params, "Failed to read texture parameter file: "
00513 << paramFile);
00514
00515
00516 smart_ptr<texture_t> texture = createTexture(hfield, params);
00517 writeTexture(texture, hfield->getTexturePath());
00518 } catch (std::exception& e) {
00519 DPRINTF("Exception during startup!");
00520 DPRINTF("%s", e.what());
00521 retval = 1;
00522 } catch (...) {
00523 DPRINTF("Unknown exception during startup!");
00524 retval = 2;
00525 }
00526
00527 perf::dumpTimingSummary(std::cerr);
00528
00529 return retval;
00530 }
00531