terrain/test/test.cpp

Go to the documentation of this file.
00001 /*
00002  * test.cpp
00003  *
00004  * Copyright (C) 2008,2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  * Program to test the terrain library.
00008  */
00009 
00010 // includes --------------------------------------------------------------------
00011 #include <fstream>
00012 #include <math.h>
00013 
00014 #include "common/wave_ex.h"
00015 #include "glut-font/glut-font.h"
00016 #include "perf/perf.h"
00017 #include "terrain/terrain.h"
00018 #include "util/file.h"
00019 #include "wave-glut/glut-state.h"
00020 #include "wave-glut/glut_2d.h"
00021 #include "wave-glut/wave-glut.h"
00022 
00023 
00024 static const int s_defaultWidth                 = 800;
00025 static const int s_defaultHeight                = 600;
00026 
00027 static int s_screenWidth                        = s_defaultWidth;
00028 static int s_screenHeight                       = s_defaultHeight;
00029 
00030 static const float s_twoPi                      = 2.0 * M_PI;
00031 
00032 typedef short int height_t;
00033 
00034 static int s_winid                              = -1;
00035 //static int s_wmode                            = 0;
00036 
00037 // input delay
00038 static const int s_delay                        = 100;
00039 
00040 static int s_wmode                              = 0;
00041 
00042 // lighting data
00043 static point3d_t s_lightPosition(0.0, 0.0, 0.0);
00044 static point3d_t s_lightVelocity(0.0, 0.0, -0.01);
00045 
00046 static int s_retval                             = 0;
00047 
00048 // terrain
00049 static smart_ptr<hfield::Heightfield> s_hfield;
00050 static smart_ptr<glut::Renderable> s_terrain;
00051 
00052 
00053 ////////////////////////////////////////////////////////////////////////////////
00054 //
00055 //      static helper methods
00056 //
00057 ////////////////////////////////////////////////////////////////////////////////
00058 
00059 static void
00060 onExit
00061 (
00062 void
00063 )
00064 {
00065         // shut down windows etc
00066         glutDestroyWindow(s_winid);
00067 
00068         // display timing information
00069         std::string summary;
00070         perf::getTimingSummary(summary);
00071         DPRINTF("Timers:\n%s\n", summary.c_str());
00072 
00073         // exit!
00074         exit(s_retval);
00075 }
00076 
00077 
00078 
00079 static void
00080 setTerrain
00081 (
00082 IN const char * hfield_file
00083 )
00084 {
00085         perf::Timer timer("setTerrain");
00086         ASSERT(hfield_file, "null");
00087 
00088         // filesystem
00089         smart_ptr<nstream::Manager> mgr =
00090             nstream::getFilesystemManager("");
00091         ASSERT(mgr, "null");
00092 
00093         // create heightfield
00094         smart_ptr<nstream::Stream> stream =
00095             nstream::openNamedStream(mgr, hfield_file);
00096         ASSERT(stream, "null");
00097         s_hfield = hfield::Heightfield::create(stream);
00098         ASSERT(s_hfield, "Failed to load heightfield from file");
00099         s_hfield->dump(hfield_file);
00100 
00101         // create terrain object (hfield renderer) from hfield object
00102         s_terrain = glut::createTerrain(s_hfield);
00103         ASSERT(s_terrain, "failed to create terrain object");
00104 }
00105 
00106 
00107 
00108 static void
00109 setLight
00110 (
00111 IN float t
00112 )
00113 {
00114         glEnable(GL_LIGHTING);
00115         glEnable(GL_LIGHT0);
00116         glShadeModel(GL_SMOOTH);
00117 
00118         // first light: ambient white light
00119         float lightModel[] = { 0.2, 0.2, 0.2, 1.0 };
00120         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightModel);
00121 
00122         float lightPos[] = { sin(t), cos(t), 0, 0 };
00123         glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
00124 
00125         float lightAmbient[] = { 0, 0, 0, 1 };
00126         glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
00127 
00128         float lightDiffuse[] = { 1, 1, 1, 1 };
00129         glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
00130         glLightfv(GL_LIGHT0, GL_SPECULAR, lightDiffuse);
00131 }
00132 
00133 
00134 
00135 static void
00136 onDisplay
00137 (
00138 IN glut::RenderQueue * rq
00139 )
00140 {
00141         perf::Timer timer("onDisplay");
00142         ASSERT(rq, "null");
00143 
00144         static float t = 0.0;
00145         const float dt = 0.002;
00146         t += dt;
00147 
00148         float xzScale = s_hfield->getXZScale();
00149         float W = xzScale * s_hfield->getWidth();
00150         float H = xzScale * s_hfield->getLength();
00151         
00152         float T = s_hfield->getMaxHeight() - s_hfield->getMinHeight();
00153 
00154         // update position based on controller
00155         float K = 2;
00156         point3d_t pos(sin(K * t), 0, sin((37 / 17) * K * (t + 0.2)));
00157         pos = 0.8 * W * pos;
00158 
00159         // set up position etc
00160         glut::render_context_t rc;
00161         rc.viewer.setPosition(pos);
00162         point3d_t vpos = rc.viewer.getPosition();
00163 //      float min_y = s_hfield->getHeight(vpos.x, vpos.z) + 100.0;
00164 
00165         float hX = vpos.x + 0.5 * W;    // x-coordinate relative to hfield
00166         float hZ = vpos.z + 0.5 * H;    // z-coordinate relative to hfield
00167         float min_y = s_hfield->getHeight(hX, hZ) + 0.1 * (1.0 + sin(0.8773 * t)) * (W + 0.25 * T);
00168         float dy = (min_y - vpos.y);
00169         rc.viewer.move(0, 0, dy);
00170 //      camera.dump("camera");
00171 
00172 //      viewer.getPosition().dump("position");
00173 
00174         vpos = rc.viewer.getPosition();
00175         float R2 = dotProduct(vpos, vpos);
00176         float R = sqrt(R2);
00177 
00178         // always look at center!
00179         float phi = atan2(-vpos.x, -vpos.z);
00180         while (phi < 0.0) {
00181                 phi += s_twoPi;
00182         }
00183         while (phi > s_twoPi) {
00184                 phi -= s_twoPi;
00185         }
00186 
00187         float rho = asin(-vpos.y / R);
00188 //      DPRINTF("x=%8.2f   z=%8.2f   y=%8.2f   phi=%5.2f   rho=%5.2f",
00189 //          vpos.x, vpos.z, vpos.y,  phi, rho);
00190 
00191 //      float phi = 0;
00192         rc.viewer.setYRotation(phi);
00193         rc.viewer.setUpRotation(rho);
00194 
00195 //      point3d_t facing = viewer.getFacing();
00196 //      facing.dump("facing");
00197 
00198         // fps
00199         static glut::fps_t myFps(perf::getNow());
00200         myFps.tick(perf::getNow());
00201 
00202         //DPRINTF("On display...");
00203         // capabilities
00204         glEnable(GL_DEPTH_TEST);
00205 
00206         // clear buffers (takes no effect until drawing much later)
00207         glClearColor(0.2, 0.4, 0.8, 1.0);
00208         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
00209 
00210         if (s_wmode) {
00211                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
00212         } else {
00213                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00214         }
00215 
00216         // set perspective viewing (camera + projection)
00217         glut::setOpenGLProjection(rc.camera);
00218         glut::setOpenGLViewer(rc.viewer);
00219 
00220         // set up the lights (use default modelview transformation)
00221         glColor3f(1.0, 1.0, 1.0);
00222         setLight(t);
00223 
00224         // draw terrain
00225         glEnable(GL_COLOR_MATERIAL);
00226         s_terrain->render(rc, rq);
00227         glDisable(GL_COLOR_MATERIAL);
00228 
00229         // clear capabilities (just before text)
00230         glDisable(GL_LIGHTING);
00231         glDisable(GL_DEPTH_TEST);
00232         {
00233                 perf::Timer timer("display-2d");
00234         // draw text
00235         // first, switch to orthographic projection
00236         glut::push2D(s_screenWidth, s_screenHeight);
00237         glColor3f(0, 0, 0);
00238         glut::Font * font = glut::getDefaultFont();
00239         char buffer[1024];
00240         sprintf(buffer, "%5.1f FPS", myFps.getFPS());
00241         font->display(10, 15, 0.0, buffer);
00242         glut::pop2D();
00243         }
00244 }
00245 
00246 
00247 /*
00248 static void
00249 onKeys
00250 (
00251 IN byte_t key,
00252 IN int x,
00253 IN int y
00254 )
00255 {
00256         DPRINTF("Pressed %d = '%c' at (%d, %d)", key, key, x, y);
00257 
00258         int mods = glutGetModifiers();
00259         if (GLUT_ACTIVE_SHIFT & mods) {
00260                 DPRINTF("  'shift' is pressed");
00261         } else if (GLUT_ACTIVE_CTRL & mods) {
00262                 DPRINTF("  'ctrl' is pressed");
00263         } else if (GLUT_ACTIVE_ALT & mods) {
00264                 DPRINTF("  'alt' is pressed");
00265         }
00266 }
00267 */
00268 
00269 
00270 
00271 class TestHost : public glut::Host {
00272 public:
00273         virtual ~TestHost(void) throw() { }
00274 
00275         // glut::Host class interface methods ----------------------------------
00276         void init(void) {
00277                         setTerrain(m_hfieldFilename.c_str());
00278 
00279                         int nSlots = 8;
00280                         m_rQueue = glut::RenderQueue::create(nSlots);
00281                         ASSERT(m_rQueue, "null");
00282                 }
00283         void display(IN int w, IN int h) {
00284                         onDisplay(m_rQueue);
00285                 }
00286         int shutdown(void) {
00287                         onExit();
00288                         return 0;
00289                 }
00290         eBehavior keyboard(IN int key, IN int mods) {
00291                         switch (key) {
00292                         case 'w':
00293                                 s_wmode = 1 - s_wmode;
00294                                 break;
00295 
00296                         case 27:
00297                         case 'q':
00298                                 return eExit;
00299                         }
00300 
00301                         return eContinue;
00302                 }
00303 
00304         // static factory methods ----------------------------------------------
00305         static smart_ptr<glut::Host> create(IN const char * hfieldFilename) {
00306                         ASSERT(hfieldFilename, "null");
00307 
00308                         smart_ptr<TestHost> local = new TestHost;
00309                         ASSERT(local, "out of memory");
00310 
00311                         local->m_hfieldFilename = hfieldFilename;
00312 
00313                         return local;
00314                 }
00315 
00316 private:
00317         // private member data -------------------------------------------------
00318         std::string             m_hfieldFilename;
00319         smart_ptr<glut::RenderQueue> m_rQueue;
00320 };
00321 
00322 
00323 
00324 ////////////////////////////////////////////////////////////////////////////////
00325 //
00326 //      entry point
00327 //
00328 ////////////////////////////////////////////////////////////////////////////////
00329 
00330 int
00331 main
00332 (
00333 IN int argc,
00334 IN const char * argv[]
00335 )
00336 {
00337         ASSERT(2 == argc,
00338             "usage: terrain-test <hfield-file>");
00339 
00340         const char * hfield_file = argv[1];
00341 
00342         try {
00343 
00344                 smart_ptr<glut::Host> host = TestHost::create(hfield_file);
00345                 ASSERT(host, "NULL");
00346 
00347                 std::string title = "Terrain Test: ";
00348                 title += GetFilename(hfield_file);
00349 
00350                 glut::start(argc, argv, s_defaultWidth, s_defaultHeight,
00351                     title.c_str(), NULL, host);
00352 
00353         } catch (std::exception& e) {
00354                 DPRINTF("Exception: %s", e.what());
00355                 s_retval = 1;
00356                 onExit();
00357         }
00358 
00359         return 0;
00360 }
00361