heightfield.cpp

Go to the documentation of this file.
00001 /*
00002  * heightfield.cpp
00003  *
00004  * Copyright (C) 2008,2009  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  * Implementation of a basic heightfield object.
00032  * See heightfield.h
00033  */
00034 
00035 // includes --------------------------------------------------------------------
00036 #include "heightfield.h"                // always include our own header first!
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 // interface destructor implementation
00051 Heightfield::~Heightfield(void) throw() { }
00052 
00053 
00054 typedef std::vector<short> vec_short_t;
00055 
00056 
00057 ////////////////////////////////////////////////////////////////////////////////
00058 //
00059 //      static helper methods
00060 //
00061 ////////////////////////////////////////////////////////////////////////////////
00062 
00063 class HField : public Heightfield {
00064 public:
00065         ~HField(void) throw() { }
00066 
00067         // public class methods ------------------------------------------------
00068         void initialize(IO nstream::Stream * stream);
00069 
00070         // hfield::Heightfield class interface methods -------------------------
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         // private helper methods ----------------------------------------------
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         // private data members ------------------------------------------------
00098         int                     m_width;
00099         int                     m_length;
00100         float                   m_yScale;       // height increments
00101         float                   m_xzScale;      // grid point spacing
00102         long                    m_nPoints;      // # points in grid (w * h)
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;       // points (16-bit resolution)
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         // get the manager
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         // first token should be version tag!
00131         expectToken(stream, "hfieldVersion");
00132         expectToken(stream, "0.1");
00133 
00134         // next token should be the heightfield definition
00135         expectToken(stream, "heightfield");
00136 
00137         // should see an opening bracket
00138         expectToken(stream, "{");
00139         std::string token;
00140 
00141         // load the heightfield data
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         // read heightfield data from PGM file
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         // get stream for the PGM file, relative to this stream
00161         smart_ptr<nstream::Stream> pgmStream =
00162             nstream::getStreamRelativeTo(pstream, filename);
00163         ASSERT_THROW(pgmStream, "Cannot open heightfield PGM: " << filename);
00164 
00165         // read PGM file
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         // save heighfield path
00174         m_heightfieldFile = nstream::getStreamName(pgmStream);
00175 
00176         // note but do not read the texture file
00177         expectToken(stream, "textureFile");
00178         getNextToken(stream, token);
00179         m_textureFile =
00180             getPathRelativeTo(m_heightfieldFile.c_str(), token.c_str());
00181 
00182         // all done!
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         DPRINTF("x,z = (%f, %f)", x, z);
00334         DPRINTF("  iX=%d   fX=%f   qX=%f", iX, fX, qX);
00335         DPRINTF("  iZ=%d   fZ=%f   qZ=%f", iZ, fZ, qZ);
00336         exit(0);
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 //      public API
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 };      // hfield namespace
00402