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
00034
00035
00036 #include "heightfield.h"
00037
00038 #include "common/wave_ex.h"
00039
00040 #include "datahash/datahash_util.h"
00041 #include "pgmppm/pgmppm.h"
00042 #include "nstream/nstream.h"
00043 #include "util/file.h"
00044 #include "util/token_stream.h"
00045
00046
00047 namespace hfield {
00048
00049
00050
00051 Heightfield::~Heightfield(void) throw() { }
00052
00053
00054 typedef std::vector<short> vec_short_t;
00055
00056
00057
00058
00059
00060
00061
00062
00063 class HField : public Heightfield {
00064 public:
00065 ~HField(void) throw() { }
00066
00067
00068 void initialize(IO nstream::Stream * stream);
00069
00070
00071 void dump(IN const char * txt) const throw();
00072 int getWidth(void) const throw() { return m_width; }
00073 int getLength(void) const throw() { return m_length; }
00074 float getXZScale(void) const throw() { return m_xzScale; }
00075 float getYScale(void) const throw() { return m_yScale; }
00076 short getMinHeight(void) const throw() { return m_minHeight; }
00077 short getMaxHeight(void) const throw() { return m_maxHeight; }
00078 const short * getHeightArray(void) const throw() { return &m_hfield[0]; }
00079 smart_ptr<nstream::Manager> getStreamManager(void) throw() { return m_mgr; }
00080 const char * getHeightfieldPath(void) const throw();
00081 const char * getTexturePath(void) const throw();
00082 float getHeight(IN float x, IN float z) const throw();
00083
00084 private:
00085
00086 static bool notifySize(IN void * context,
00087 IN int width,
00088 IN int length,
00089 IN int max_val);
00090 static void writePixel(IN void * context,
00091 IN int x,
00092 IN int y,
00093 IN int height);
00094 float getRawHeight(IN int gridX,
00095 IN int gridZ) const throw();
00096
00097
00098 int m_width;
00099 int m_length;
00100 float m_yScale;
00101 float m_xzScale;
00102 long m_nPoints;
00103 short m_minHeight;
00104 short m_maxHeight;
00105 std::string m_heightfieldFile;
00106 std::string m_textureFile;
00107 smart_ptr<nstream::Manager> m_mgr;
00108 vec_short_t m_hfield;
00109 };
00110
00111
00112
00113 void
00114 HField::initialize
00115 (
00116 IO nstream::Stream * pstream
00117 )
00118 {
00119 ASSERT(pstream, "null");
00120 ASSERT(pstream->good(), "bad?");
00121
00122 std::istream& stream = pstream->getStream();
00123
00124
00125 smart_ptr<nstream::File> file = pstream->getFile();
00126 ASSERT_THROW(file, "stream has no file?");
00127 m_mgr = file->getManager();
00128 ASSERT_THROW(m_mgr, "stream has no manager?");
00129
00130
00131 expectToken(stream, "hfieldVersion");
00132 expectToken(stream, "0.1");
00133
00134
00135 expectToken(stream, "heightfield");
00136
00137
00138 expectToken(stream, "{");
00139 std::string token;
00140
00141
00142 expectToken(stream, "yScale");
00143 m_yScale = readFloat(stream, token);
00144 ASSERT(m_yScale > 0.0, "Bad y-scale: %f", m_yScale);
00145
00146 expectToken(stream, "xzScale");
00147 m_xzScale = readFloat(stream, token);
00148 ASSERT(m_xzScale > 0.0, "Bad xz-scale: %f", m_xzScale);
00149
00150
00151 expectToken(stream, "hfieldFile");
00152 getNextToken(stream, token);
00153 const char * filename = token.c_str();
00154 const char * ext = GetExtension(filename);
00155 if (!ext || strcmp("pgm", ext)) {
00156 WAVE_EX(wex);
00157 wex << "Invalid heightfield filename: " << filename;
00158 }
00159
00160
00161 smart_ptr<nstream::Stream> pgmStream =
00162 nstream::getStreamRelativeTo(pstream, filename);
00163 ASSERT_THROW(pgmStream, "Cannot open heightfield PGM: " << filename);
00164
00165
00166 std::istream& infile = pgmStream->getStream();
00167 if (!pgmppm::readPgm(infile, notifySize, writePixel, this)) {
00168 WAVE_EX(wex);
00169 wex << "Error reading PGM file: ";
00170 wex << filename;
00171 }
00172
00173
00174 m_heightfieldFile = nstream::getStreamName(pgmStream);
00175
00176
00177 expectToken(stream, "textureFile");
00178 getNextToken(stream, token);
00179 m_textureFile =
00180 getPathRelativeTo(m_heightfieldFile.c_str(), token.c_str());
00181
00182
00183 expectToken(stream, "}");
00184 }
00185
00186
00187
00188 bool
00189 HField::notifySize
00190 (
00191 IN void * ctx,
00192 IN int width,
00193 IN int length,
00194 IN int max_val
00195 )
00196 {
00197 HField * pThis = (HField *) ctx;
00198 ASSERT(pThis, "null context");
00199 ASSERT(width > 0, "Bad width: %d", width);
00200 ASSERT(length > 0, "Bad length: %d", length);
00201 ASSERT(max_val > 0, "bad max_val: %d", max_val);
00202
00203 pThis->m_width = width;
00204 pThis->m_length = length;
00205 pThis->m_nPoints = width * length;
00206
00207 pThis->m_hfield.clear();
00208 pThis->m_hfield.resize(pThis->m_nPoints);
00209
00210 return true;
00211 }
00212
00213
00214
00215 void
00216 HField::writePixel
00217 (
00218 IN void * ctx,
00219 IN int x,
00220 IN int y,
00221 IN int height
00222 )
00223 {
00224 HField * pThis = (HField *) ctx;
00225 ASSERT(pThis, "null context");
00226 ASSERT(x >= 0 && x < pThis->m_width, "Bad x: %d", x);
00227 ASSERT(y >= 0 && y < pThis->m_length, "Bad y: %d", y);
00228
00229 short s = (short) height;
00230
00231 if (!x && !y) {
00232 pThis->m_minHeight = s;
00233 pThis->m_maxHeight = s;
00234 } else {
00235 if (s < pThis->m_minHeight) {
00236 pThis->m_minHeight = s;
00237 }
00238 if (s > pThis->m_maxHeight) {
00239 pThis->m_maxHeight = s;
00240 }
00241 }
00242
00243 int idx = (y * pThis->m_width) + x;
00244
00245 pThis->m_hfield[idx] = s;
00246 }
00247
00248
00249
00250 void
00251 HField::dump
00252 (
00253 IN const char * txt
00254 )
00255 const
00256 throw()
00257 {
00258 DPRINTF("Heightfield: %s", txt);
00259 DPRINTF(" heightfieldFile: %s", m_heightfieldFile.c_str());
00260 DPRINTF(" textureFile: %s", m_textureFile.c_str());
00261 DPRINTF(" width (x grid extent): %d", m_width);
00262 DPRINTF(" length (z grid extent): %d", m_length);
00263 DPRINTF(" yScale: %f", m_yScale);
00264 DPRINTF(" xzScale: %f", m_xzScale);
00265 DPRINTF(" minHeight: %d", m_minHeight);
00266 DPRINTF(" maxHeight: %d", m_maxHeight);
00267 DPRINTF(" Derived width (x): %f", m_xzScale * (m_width - 1));
00268 DPRINTF(" Derived length (z): %f", m_xzScale * (m_length - 1));
00269 DPRINTF(" Derived min Y: %f", m_yScale * m_minHeight);
00270 DPRINTF(" Derived max Y: %f", m_yScale * m_maxHeight);
00271 }
00272
00273
00274
00275 const char *
00276 HField::getHeightfieldPath
00277 (
00278 void
00279 )
00280 const
00281 throw()
00282 {
00283 return m_heightfieldFile.c_str();
00284 }
00285
00286
00287
00288 const char *
00289 HField::getTexturePath
00290 (
00291 void
00292 )
00293 const
00294 throw()
00295 {
00296 return m_textureFile.c_str();
00297 }
00298
00299
00300
00301 float
00302 HField::getHeight
00303 (
00304 IN float x,
00305 IN float z
00306 )
00307 const
00308 throw()
00309 {
00310 ASSERT(m_xzScale > 0, "Bad xz-scale: %f", m_xzScale);
00311
00312 float fX = x / m_xzScale;
00313 float fZ = z / m_xzScale;
00314
00315 int iX = (int) fX;
00316 int iZ = (int) fZ;
00317
00318 if (x < 0)
00319 iX -= 1;
00320 if (z < 0)
00321 iZ -= 1;
00322
00323 float qX = fX - iX;
00324 float qZ = fZ - iZ;
00325
00326 ASSERT(qX >= 0.0 && qX <= 1.0, "Bad qX: %f", qX);
00327 ASSERT(qZ >= 0.0 && qZ <= 1.0, "Bad qZ: %f", qZ);
00328
00329 qX = 1.0 - qX;
00330 qZ = 1.0 - qZ;
00331
00332
00333
00334
00335
00336
00337
00338
00339 float total = 0.0;
00340 total += qX * qZ * this->getRawHeight(iX, iZ);
00341 total += qX * (1.0 - qZ) * this->getRawHeight(iX, iZ + 1);
00342 total += (1.0 - qX) * qZ * this->getRawHeight(iX + 1, iZ);
00343 total += (1.0 - qX) * (1.0 - qZ) * this->getRawHeight(iX + 1, iZ + 1);
00344
00345 return total * m_yScale;
00346 }
00347
00348
00349 float
00350 HField::getRawHeight
00351 (
00352 IN int gridX,
00353 IN int gridZ
00354 )
00355 const
00356 throw()
00357 {
00358 if (gridX < 0)
00359 gridX = 0;
00360 if (gridX >= m_width)
00361 gridX = m_width - 1;
00362
00363 if (gridZ < 0)
00364 gridZ = 0;
00365 if (gridZ >= m_length)
00366 gridZ = m_length - 1;
00367
00368 const short * p = this->getHeightArray();
00369 ASSERT(p, "null");
00370
00371 return *(p + gridX + m_width * gridZ);
00372 }
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382 smart_ptr<Heightfield>
00383 Heightfield::create
00384 (
00385 IN nstream::Stream * stream
00386 )
00387 {
00388 ASSERT(stream, "null");
00389 ASSERT(stream->good(), "bad?");
00390
00391 smart_ptr<HField> local = new HField;
00392 ASSERT(local, "out of memory");
00393
00394 local->initialize(stream);
00395
00396 return local;
00397 }
00398
00399
00400
00401 };
00402