libmini-test.cpp

Go to the documentation of this file.
00001 /*
00002  * libmini-test.cpp
00003  *
00004  * Copyright (C) 2008,2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  * Program to test the libmini library (terrain).
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 "pgmppm/pgmppm.h"
00018 #include "wave-glut/camera.h"
00019 #include "wave-glut/glut_2d.h"
00020 #include "wave-glut/wave-glut.h"
00021 
00022 // include this last!
00023 #include "terrain/mini.h"
00024 
00025 static const int s_defaultWidth                 = 800;
00026 static const int s_defaultHeight                = 600;
00027 
00028 static int s_screenWidth                        = s_defaultWidth;
00029 static int s_screenHeight                       = s_defaultHeight;
00030 
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 static int s_ignoreInput                        = s_delay;
00040 
00041 // map data
00042 static minitile * s_tileset                     = NULL;
00043 static minicache * s_cache                      = NULL;
00044 static int s_updates                            = 5;
00045 
00046 
00047 static point3d_t s_lightPosition(0.0, 0.0, 0.0);
00048 static point3d_t s_lightVelocity(0.0, 0.0, -0.01);
00049 
00050 static int s_retval                             = 0;
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 bool
00080 notifyPgmSize
00081 (
00082 IN void * ctx,
00083 IN int width,
00084 IN int height,
00085 IN int max_val
00086 )
00087 {
00088         int * pWidth = (int *) ctx;
00089         ASSERT(pWidth, "bad context");
00090         ASSERT(width > 0, "Bad pgm width: %d", width);
00091         ASSERT(height == width, "require height == width!");
00092 
00093         *pWidth = width;
00094 
00095         return false;   // don't bother loading the rest of the file!
00096 }
00097 
00098 
00099 
00100 static void
00101 notifyPgmPixel
00102 (
00103 IN void * ctx,
00104 IN int x,
00105 IN int y,
00106 IN int height
00107 )
00108 {
00109         // nothing to do!
00110 }
00111 
00112 
00113 
00114 static void
00115 setTerrain
00116 (
00117 IN const char * pgm_file,
00118 IN const char * ppm_file
00119 )
00120 {
00121         perf::Timer timer("setTerrain");
00122         ASSERT(pgm_file, "null");
00123         ASSERT(ppm_file, "null");
00124 
00125         DPRINTF("About to init...");
00126         float xzScale = 10.0;           // 10m
00127         float yScale = 1;               // 1m
00128 
00129         const char * hfield_files[] = { pgm_file };
00130         const char * texture_files[] = { ppm_file };
00131 
00132         // load pgm to determine size
00133         std::ifstream infile(hfield_files[0]);
00134         if (!infile.good()) {
00135                 WAVE_EX(wex);
00136                 wex << "Failed to open terrain file for reading: ";
00137                 wex << hfield_files[0];
00138         }
00139         int width = -1;
00140         pgmppm::readPgm(infile, notifyPgmSize, notifyPgmPixel, &width);
00141         DPRINTF("PGM image width: %d pixels", width);
00142         ASSERT(width > 0, "Bad pgm width: %d", width);
00143 
00144         // dimension of heightfield?
00145         float dim = (width - 1) * xzScale;
00146 
00147         // okay, create minitile object
00148         int nRows = 1;
00149         int nCols = 1;
00150         s_tileset = new minitile((const unsigned char **) hfield_files,
00151                                  (const unsigned char **) texture_files,
00152                                  nRows, nCols,
00153                                  dim, dim, yScale);
00154         ASSERT(s_tileset, "null");
00155 
00156         s_cache = new minicache();
00157         ASSERT(s_cache, "out of memory");
00158         s_cache->attach(s_tileset);
00159 }
00160 
00161 
00162 
00163 static void
00164 addLight
00165 (
00166 IN int gl_enum,
00167 IN const point3d_t& position
00168 )
00169 {
00170         float x = position.x;
00171         float y = position.y;
00172         float z = position.z;
00173 
00174         float lightPos[] = { x, y, z, 1.0 };
00175         glLightfv(gl_enum, GL_POSITION, lightPos);
00176 
00177         float lightAmbient[] = { 0.2, 0.2, 0.2, 1.0 };
00178         glLightfv(gl_enum, GL_AMBIENT, lightAmbient);
00179 
00180         float str = 1.0;
00181         float lightDiffuse[] = { str, str / 3, str / 9, 1.0 };
00182         glLightfv(gl_enum, GL_DIFFUSE, lightDiffuse);
00183         glLightfv(gl_enum, GL_SPECULAR, lightDiffuse);
00184 
00185         glLightf(gl_enum, GL_QUADRATIC_ATTENUATION, .02);
00186 
00187         glColor3f(str, str / 3, str / 9);
00188         glPushMatrix();
00189         glTranslatef(x, y, z);
00190         glutSolidSphere(0.5, 8, 8);
00191         glPopMatrix();
00192 }
00193 
00194 
00195 
00196 static void
00197 setLights
00198 (
00199 IN const glut::Viewer& walker
00200 )
00201 {
00202         // first light: ambient white light
00203         float lightAmbient[] = { 0.5, 0.5, 0.5, 1.0 };
00204         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightAmbient);
00205 
00206         // other lights--moving
00207         s_lightPosition.increment(s_lightVelocity);
00208         s_lightPosition.y = mini::getheight(s_lightPosition.x, s_lightPosition.z);
00209         s_lightPosition.y += 5.0;
00210         addLight(GL_LIGHT0, s_lightPosition);
00211 
00212         addLight(GL_LIGHT1, walker.getPosition());
00213 
00214         glEnable(GL_LIGHTING);
00215         glEnable(GL_LIGHT0);
00216         glEnable(GL_LIGHT1);
00217 }
00218 
00219 
00220 
00221 static void
00222 debugInfo
00223 (
00224 IN const glut::Viewer& viewer
00225 )
00226 {
00227         DPRINTF("Debug Information");
00228         point3d_t v;
00229 
00230         v = viewer.getPosition();
00231         v.dump("  Viewer is at");
00232         DPRINTF("  mini says height at camera is: %f",
00233             mini::getheight(v.x, v.z));
00234 
00235         v = viewer.getFacing();
00236         v.dump("  Viewer is facing");
00237 }
00238 
00239 
00240 
00241 static void
00242 updatePosition
00243 (
00244 IO glut::Viewer& view
00245 )
00246 {
00247         // get the current state of the first joystick
00248 /*
00249         DPRINTF("stick1: (%4d, %4d)    stick2: (%4d, %4d)",
00250             joy.stick1.x, joy.stick1.y, joy.stick2.x, joy.stick2.y);
00251  */
00252 
00253 /*
00254         // first axis: viewpoint
00255         float dr = 0.02;
00256         float dy_rot = (-joy.stick1.x * dr) / lib3d::eInput_MaxAxis;
00257         float dz_rot = (joy.stick1.y * dr) / lib3d::eInput_MaxAxis;
00258         view.rotateY(dy_rot);
00259         view.rotateUp(dz_rot);
00260 
00261         // second axis: motion
00262         float step = 0.1;
00263         float forward = (joy.stick2.y * step) / lib3d::eInput_MaxAxis;
00264         float sideways = (joy.stick2.x * step) / lib3d::eInput_MaxAxis;
00265         view.move(forward, sideways, 0.0);
00266 
00267         // get new location
00268         point3d_t pos = view.getPosition();
00269 //      pos.dump("position");
00270 
00271 */
00272         // ignore input?
00273         if (s_ignoreInput) {
00274                 s_ignoreInput--;
00275                 return;
00276         }
00277 /*
00278         // shoot out light?
00279         if (lib3d::eInput_Bumper1Top & joy.buttons) {
00280                 s_lightPosition = pos;
00281                 s_lightVelocity = view.getFacing();
00282                 s_lightVelocity.scale(0.1);
00283                 s_ignoreInput = s_delay;
00284         }
00285 
00286         // shoot out cube?
00287         if (lib3d::eInput_Bumper1Bottom & joy.buttons) {
00288                 point3d_t pos = view.getPosition();
00289                 pos.y += 2.0;
00290                 point3d_t facing = view.getFacing();
00291                 shootCube(pos, facing);
00292                 s_ignoreInput = s_delay;
00293         }
00294 
00295 */
00296         // debug?
00297         if (0) {
00298                 debugInfo(view);
00299                 s_ignoreInput = s_delay;
00300         }
00301 }
00302 
00303 
00304 
00305 static void
00306 onDisplay
00307 (
00308 void
00309 )
00310 {
00311         perf::Timer timer("onDisplay");
00312 
00313         static glut::Viewer viewer;
00314         static glut::camera_t camera;
00315         static float t = 0.0;
00316         const float dt = 0.002;
00317         t += dt;
00318 
00319         // update position based on controller
00320         updatePosition(viewer);
00321         viewer.setPosition(point3d_t(1000.0 * sin(0.1 * t), 0, 1000.0 * cos(0.1 * t)));
00322         point3d_t vpos = viewer.getPosition();
00323         float min_y = mini::getheight(vpos.x, vpos.z) + 10.0;
00324         float dy = (min_y - vpos.y);
00325         if (dy > 0.0) {
00326                 viewer.move(0, 0, dy);
00327         }
00328 //      camera.dump("camera");
00329 
00330         viewer.rotateY(0.5 * dt);
00331 
00332         // fps
00333         static glut::fps_t myFps(perf::getNow());
00334         myFps.tick(perf::getNow());
00335 
00336         //DPRINTF("On display...");
00337         // capabilities
00338         glEnable(GL_DEPTH_TEST);
00339         glEnable(GL_COLOR_MATERIAL);
00340 
00341         // clear buffers (takes no effect until drawing much later)
00342         glClearColor(0.2, 0.4, 0.8, 1.0);
00343         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
00344 
00345         // set perspective viewing (camera + projection)
00346         glut::setOpenGLProjection(camera);
00347         glut::setOpenGLViewer(viewer);
00348 
00349         // set up the lights (use default modelview transformation)
00350         glColor3f(1.0, 1.0, 1.0);
00351         if (s_updates) {
00352                 glut::Viewer bogus = viewer;
00353                 bogus.move(100.0, 0, 0);
00354                 setLights(bogus);
00355         }
00356 
00357         // set up for drawing
00358         float resolution = 1000.0;
00359         point3d_t view = viewer.getFacing();
00360         point3d_t eye = viewer.getPosition();
00361         point3d_t up = viewer.getUp();
00362 
00363         glNormal3f(0.0, 1.0, 0.0);
00364         s_tileset->draw(resolution,
00365                             eye.x, eye.y, eye.z,
00366 //                          eye.x, eye.y, eye.z,
00367                             view.x, view.y, view.z,
00368                             up.x, up.y, up.z,
00369                             camera.getFovy(),
00370                             camera.getAspect(),
00371                             camera.getZNear(),
00372                             camera.getZFar(),
00373                             s_updates);
00374 
00375         // actually draw
00376         s_cache->rendercache();
00377 
00378         // draw text
00379         // first, switch to orthographic projection
00380         glut::push2D(s_screenWidth, s_screenHeight);
00381         glColor3f(0, 0, 0);
00382         glut::Font * font = glut::getDefaultFont();
00383         char buffer[1024];
00384         sprintf(buffer, "%5.1f FPS", myFps.getFPS());
00385         font->display(10, 15, 0.0, buffer);
00386         glut::pop2D();
00387 
00388         // clear capabilities (just before exit!)
00389         glDisable(GL_COLOR_MATERIAL);
00390         glDisable(GL_DEPTH_TEST);
00391 
00392         // last thing--swap the buffers out
00393         glutSwapBuffers();
00394 }
00395 
00396 
00397 
00398 static void
00399 onIdle
00400 (
00401 void
00402 )
00403 {
00404         glutPostRedisplay();
00405 }
00406 
00407 
00408 
00409 static void
00410 onKeys
00411 (
00412 IN byte_t key,
00413 IN int x,
00414 IN int y
00415 )
00416 {
00417         DPRINTF("Pressed %d = '%c' at (%d, %d)", key, key, x, y);
00418 
00419         int mods = glutGetModifiers();
00420         if (GLUT_ACTIVE_SHIFT & mods) {
00421                 DPRINTF("  'shift' is pressed");
00422         } else if (GLUT_ACTIVE_CTRL & mods) {
00423                 DPRINTF("  'ctrl' is pressed");
00424         } else if (GLUT_ACTIVE_ALT & mods) {
00425                 DPRINTF("  'alt' is pressed");
00426         }
00427 
00428         if ('w' == key) {
00429                 s_wmode = 1 - s_wmode;
00430                 if (s_wmode) {
00431                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
00432                 } else {
00433                         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00434                 }
00435         }
00436 
00437         if (27 == key || 'q' == key || 'Q' == key) {
00438                 onExit();
00439         }
00440 }
00441 
00442 
00443 
00444 static void
00445 onSpecial
00446 (
00447 IN int key,
00448 IN int x,
00449 IN int y
00450 )
00451 {
00452         DPRINTF("Pressed 0x%02x at (%d, %d)", key, x, y);
00453 }
00454 
00455 
00456 
00457 static void
00458 onReshape
00459 (
00460 IN int w,
00461 IN int h
00462 )
00463 {
00464         DPRINTF("Screen size: %d x %d", w, h);
00465         s_screenWidth = w;
00466         s_screenHeight = h;
00467 
00468         glViewport(0, 0, w, h);
00469 }
00470 
00471 
00472 
00473 ////////////////////////////////////////////////////////////////////////////////
00474 //
00475 //      entry point
00476 //
00477 ////////////////////////////////////////////////////////////////////////////////
00478 
00479 int
00480 main
00481 (
00482 IN int argc,
00483 IN const char * argv[]
00484 )
00485 {
00486         ASSERT(3 == argc,
00487             "usage: mini-test <pgm-file> <ppm-file>");
00488 
00489         const char * pgm_file = argv[1];
00490         const char * ppm_file = argv[2];
00491 
00492         // initialize GLUT
00493         glutInit(&argc, (char **) argv);
00494 
00495         // initialize windows etc.
00496         glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
00497         glutInitWindowSize(s_defaultWidth, s_defaultHeight);
00498         s_winid = glutCreateWindow("test_glut");
00499         ASSERT(s_winid > 0, "Bad window id: %d", s_winid);
00500 
00501         // callbacks
00502         glutDisplayFunc(onDisplay);
00503         glutIdleFunc(onIdle);
00504         glutKeyboardFunc(onKeys);
00505         glutSpecialFunc(onSpecial);
00506         glutReshapeFunc(onReshape);
00507         glutCloseFunc(onExit);
00508 
00509         // NOTE: libmini apparently requires glut to be initialized before
00510         //      some of its library calls!  So initialize GL/GLUT first.
00511         try {
00512 
00513                 setTerrain(pgm_file, ppm_file);
00514 
00515         } catch (std::exception& e) {
00516                 DPRINTF("Exception: %s", e.what());
00517                 s_retval = 1;
00518                 onExit();
00519         }
00520 
00521         // joysticks
00522 
00523         // main loop
00524         glutMainLoop();
00525 
00526         return 0;
00527 }
00528