wave-glut.cpp

Go to the documentation of this file.
00001 /*
00002  * wave-glut.cpp
00003  *
00004  * Copyright (C) 2009,2010  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  * See wave-glut.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "wave-glut.h"                  // always include our own header first!
00036 
00037 #include "common/wave_ex.h"
00038 #include "perf/perf.h"
00039 #include "threadsafe/smart_ptr.h"
00040 #include "threadsafe/threadsafe.h"
00041 #include "threadsafe/threadsafe_map.h"
00042 
00043 
00044 namespace glut {
00045 
00046 
00047 // interface destructors
00048 Host::~Host(void) throw() { }
00049 Task::~Task(void) throw() { }
00050 
00051 
00052 struct task_record_t {
00053         // constructor, manipulators
00054         task_record_t(void) throw() { this->clear(); }
00055         void clear(void) throw() {
00056                         task = NULL;
00057                         exception = "";
00058                         needThrow = false;
00059                 }
00060 
00061         // data fields
00062         Task *          task;
00063         std::string     exception;
00064         bool            needThrow;
00065 };
00066 
00067 
00068 
00069 // statics
00070 typedef threadsafe_map<thread_id_t, smart_ptr<task_record_t> > task_map_t;
00071 
00072 static task_map_t s_pendingTasks;
00073 static task_map_t s_completedTasks;
00074 
00075 // weirdly, WIN32 builds have a token named "s_host" defined already (!?)
00076 #ifdef s_host
00077 #undef s_host
00078 #endif  // s_host
00079 
00080 static smart_ptr<Host> s_host;
00081 static bool s_fullscreen                = false;
00082 static thread_id_t s_glutThreadId       = 0;
00083 
00084 // glut window height, width
00085 static int s_width                      = 0;
00086 static int s_height                     = 0;
00087 
00088 
00089 ////////////////////////////////////////////////////////////////////////////////
00090 //
00091 //      Task-related methods
00092 //
00093 ////////////////////////////////////////////////////////////////////////////////
00094 
00095 void
00096 processTasks
00097 (
00098 void
00099 )
00100 {
00101         // loop through and process all pending tasks
00102         task_map_t::iterator_t i;
00103         s_pendingTasks.getIterator(i);
00104         thread_id_t t_id;               // thread identifier
00105         smart_ptr<task_record_t> trec;
00106         while (s_pendingTasks.getNextElement(i, t_id, trec)) {
00107                 ASSERT(trec, "null task record in map");
00108                 ASSERT(trec->task, "null task in record");
00109 
00110                 try {
00111                         trec->task->doTask();
00112                 } catch (std::exception& e) {
00113                         trec->needThrow = true;
00114                         trec->exception = e.what();
00115                 }
00116 
00117                 // remove from pending map, add to completed
00118                 // NOTE: order is important!  There is a slim chance that a
00119                 // waiting thread could pull the completed task right away.
00120                 // If we nuke the pending task afterwards, there is a chance
00121                 // that the same thread will attempt to re-insert.  By adding
00122                 // to the completed tasks last, we ensure that the waiting
00123                 // thread won't do anything until we are done.
00124                 s_pendingTasks.remove(t_id);
00125                 s_completedTasks.insert(t_id, trec);
00126         }
00127 }
00128 
00129 
00130 ////////////////////////////////////////////////////////////////////////////////
00131 //
00132 //      static helper methods (glut callbacks)
00133 //
00134 ////////////////////////////////////////////////////////////////////////////////
00135 
00136 static void
00137 onExit
00138 (
00139 void
00140 )
00141 {
00142         // exit game mode
00143         if (s_fullscreen) {
00144                 perf::Timer timer("glutLeaveGameMode");
00145                 glutLeaveGameMode();
00146         }
00147 
00148         // have the host shut down, and destroy it
00149         ASSERT(s_host, "null");
00150         int retval = 1;
00151         try {
00152                 retval = s_host->shutdown();
00153         } catch (std::exception& e) {
00154                 DPRINTF("Exception during shutdown!");
00155                 DPRINTF("%s", e.what());
00156         }
00157         s_host = NULL;
00158 
00159         exit(retval);
00160 }
00161 
00162 
00163 
00164 static void
00165 onDisplay
00166 (
00167 void
00168 )
00169 {
00170         perf::Timer timer("glut::onDisplay");
00171         ASSERT(s_host, "null");
00172 
00173         // set up the viewport
00174         glViewport(0, 0, s_width, s_height);
00175 
00176         // tell the host
00177         try {
00178                 s_host->display(s_width, s_height);
00179         } catch (std::exception& e) {
00180                 DPRINTF("Exception while rendering!");
00181                 DPRINTF("%s", e.what());
00182         }
00183 
00184         // swap buffers out
00185         glutSwapBuffers();
00186 }
00187 
00188 
00189 
00190 static void
00191 onIdle
00192 (
00193 void
00194 )
00195 {
00196         perf::Timer timer("glut::onIdle");
00197         ASSERT(s_host, "null");
00198 
00199         // process any pending tasks
00200         glut::processTasks();
00201 
00202         // keep track of time delta since last onIdle() call
00203         static perf::time_t s_time = perf::getNow();
00204         perf::time_t now = perf::getNow();
00205         now.decrement(s_time);
00206         float dt = now.getSeconds();
00207         if (dt < 0.0) {
00208                 dt = 0.0;
00209         }
00210         now.increment(s_time);
00211         s_time = now;
00212 
00213         Host::eBehavior behave = Host::eContinue;
00214         try {
00215                 behave = s_host->tick(dt);
00216         } catch (std::exception& e) {
00217                 behave = Host::eExit;
00218                 DPRINTF("Exception during tick!");
00219                 DPRINTF("%s", e.what());
00220         }
00221 
00222         if (Host::eContinue != behave)
00223                 onExit();
00224 
00225         // request redraw
00226         glutPostRedisplay();
00227 }
00228 
00229 
00230 
00231 static void
00232 onMouseButton
00233 (
00234 IN int button,
00235 IN int state,
00236 IN int x,
00237 IN int y
00238 )
00239 {
00240         ASSERT(s_host, "null");
00241 
00242         Host::eBehavior behave = Host::eContinue;
00243         try {
00244                 behave = s_host->mouseButton(button, state, x, y);
00245         } catch (std::exception& e) {
00246                 DPRINTF("Exception procesing mouse button event");
00247                 DPRINTF("%s", e.what());
00248         }
00249 
00250         if (Host::eContinue != behave) {
00251                 onExit();
00252         }
00253 }
00254 
00255 
00256 
00257 static void
00258 onMouseMove
00259 (
00260 IN int x,
00261 IN int y
00262 )
00263 {
00264         ASSERT(s_host, "null");
00265 
00266         Host::eBehavior behave = Host::eContinue;
00267         try {
00268                 behave = s_host->mouseMove(x, y);
00269         } catch (std::exception& e) {
00270                 DPRINTF("Exception procesing mouse button event");
00271                 DPRINTF("%s", e.what());
00272         }
00273 
00274         if (Host::eContinue != behave) {
00275                 onExit();
00276         }
00277 }
00278 
00279 
00280 
00281 static void
00282 onKeyboard
00283 (
00284 IN byte_t key,
00285 IN int x,
00286 IN int y
00287 )
00288 {
00289         ASSERT(s_host, "null");
00290 
00291         int mods = glutGetModifiers();
00292 
00293         Host::eBehavior behave = Host::eContinue;
00294         try {
00295                 behave = s_host->keyboard(key, mods);
00296         } catch (std::exception& e) {
00297                 DPRINTF("Exception processing keyboard event!");
00298                 DPRINTF("%s", e.what());
00299         }
00300 
00301         if (Host::eContinue != behave)
00302                 onExit();
00303 }
00304 
00305 
00306 
00307 static void
00308 onSpecialKeys
00309 (
00310 IN int key,
00311 IN int x,
00312 IN int y
00313 )
00314 {
00315         ASSERT(s_host, "null");
00316 
00317         int mods = glutGetModifiers();
00318 
00319         Host::eBehavior behave = Host::eContinue;
00320         try {
00321                 behave = s_host->specialKey(key, mods);
00322         } catch (std::exception& e) {
00323                 DPRINTF("Exception processing special key!");
00324                 DPRINTF("%s", e.what());
00325         }
00326 
00327         if (Host::eContinue != behave)
00328                 onExit();
00329 }
00330 
00331 
00332 
00333 static void
00334 onReshape
00335 (
00336 IN int w,
00337 IN int h
00338 )
00339 {
00340         DPRINTF("Window size: %d x %d", w, h);
00341 
00342         s_width = w;
00343         s_height = h;
00344 }
00345 
00346 
00347 
00348 ////////////////////////////////////////////////////////////////////////////////
00349 //
00350 //      public API (glut setup + running)
00351 //
00352 ////////////////////////////////////////////////////////////////////////////////
00353 
00354 void
00355 start
00356 (
00357 IN int argc,
00358 IN const char * argv[],
00359 IN int width,
00360 IN int height,
00361 IN const char * title,
00362 IN const char * gameModeString,
00363 IN smart_ptr<Host>& host
00364 )
00365 {
00366         ASSERT(width > 0, "Bad width: %d", width);
00367         ASSERT(height > 0, "Bad height: %d", height);
00368         ASSERT(title, "null");
00369         // ASSERT(gameModeString) -- can be null!
00370         ASSERT(host, "null");
00371 
00372         // any standard validation we need to perform?
00373         ASSERT(sizeof(float) == sizeof(GLfloat),
00374             "Big problems: we don't agree with OpenGL on sizeof(float)!");
00375         ASSERT(sizeof(double) == sizeof(GLdouble),
00376             "Big problems: we don't agree with OpenGL on sizeof(double)!");
00377         ASSERT(sizeof(byte_t) == sizeof(GLbyte),
00378             "Big problems: we don't agree with OpenGL on sizeof(byte)!");
00379         ASSERT(sizeof(int) == sizeof(GLuint),
00380             "Big problems: we don't agree with OpenGL on sizeof(int)!");
00381 
00382         // remember host
00383         ASSERT(!s_host, "Already have a host");
00384         s_host = host;
00385 
00386         // remember thread
00387         s_glutThreadId = getCurrentThreadId();
00388 
00389         // start glut (double-buffering, alpha support, depth buffer)
00390         glutInit(&argc, (char **) argv);
00391         glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
00392 
00393         // try fullscreen if asked, and then fall back to windowed mode
00394         ASSERT(!s_fullscreen, "already in fullscreen mode?");
00395         if (0 && gameModeString) {
00396                 DPRINTF("game mode string: '%s'", gameModeString);
00397                 {
00398                         perf::Timer timer("glutEnterGameMode");
00399                         glutGameModeString(gameModeString);
00400                         s_fullscreen = glutEnterGameMode() ? true : false;
00401                 }
00402                 if (!s_fullscreen) {
00403                         DPRINTF("Failed to enter game mode!");
00404                         DPRINTF("  Falling back to windowed mode...");
00405                 }
00406         }
00407         if (!s_fullscreen) {
00408                 s_width = width;
00409                 s_height = height;
00410                 glutInitWindowSize(width, height);
00411                 int win_id = glutCreateWindow(title);
00412                 ASSERT(win_id > 0, "Bad window id: %d", win_id);
00413         }
00414 
00415         // initialize GLEW
00416         GLenum err = glewInit();
00417         if (GLEW_OK != err) {
00418                 WAVE_EX(wex);
00419                 wex << "Failed to initialize glew: ";
00420                 wex << glewGetErrorString(err);
00421         }
00422 
00423         // register callbacks
00424         glutDisplayFunc(onDisplay);
00425         glutIdleFunc(onIdle);
00426         glutKeyboardFunc(onKeyboard);
00427         glutSpecialFunc(onSpecialKeys);
00428         glutReshapeFunc(onReshape);
00429         glutMouseFunc(onMouseButton);
00430         glutMotionFunc(onMouseMove);
00431         //glutPassiveMotionFunc(onMouseMove);
00432         glutCloseFunc(onExit);
00433 
00434         // sync to vertical retrace
00435 #ifdef WIN32
00436         wglSwapIntervalEXT(1);
00437 #else   // WIN32
00438         glXSwapIntervalSGI(1);
00439 #endif  // WIN32
00440 
00441         // now that we're all initialized, let host know
00442         s_host->init();
00443 
00444         // enter main loop!
00445         glutMainLoop();
00446 }
00447 
00448 
00449 
00450 ////////////////////////////////////////////////////////////////////////////////
00451 //
00452 //      public API (tasks)
00453 //
00454 ////////////////////////////////////////////////////////////////////////////////
00455 
00456 void
00457 requestTask
00458 (
00459 IO Task * task
00460 )
00461 {
00462         ASSERT(task, "null task passed to requestTask()");
00463 
00464         thread_id_t t_id = getCurrentThreadId();  // what is our thread id?
00465         if (t_id == s_glutThreadId) {
00466                 // this is already the open gl thread!  No need to block
00467                 task->doTask();
00468                 return;
00469         }
00470 
00471         // we are not the open GL thread!  Need to synchronize
00472         // First, verify we don't already have a request pending
00473         smart_ptr<task_record_t> trec;
00474         ASSERT(!s_pendingTasks.lookup(t_id, trec),
00475             "Thread already has a pending task?");
00476         ASSERT(!s_completedTasks.lookup(t_id, trec),
00477             "Thread already has a completed task?");
00478 
00479         // okay, add to pending tasks
00480         trec = new task_record_t;
00481         ASSERT(trec, "out of memory");
00482         trec->task = task;
00483         s_pendingTasks.insert(t_id, trec);
00484 
00485         // now block on result!
00486         while (!s_completedTasks.lookup(t_id, trec)) {
00487                 sleep(0);
00488         }
00489         ASSERT(trec, "received null record from completed task map");
00490         s_completedTasks.remove(t_id);
00491 
00492         // should we throw?
00493         if (trec->needThrow) {
00494                 WAVE_EX(wex);
00495                 wex << trec->exception;
00496         }
00497 }
00498 
00499 
00500 
00501 void
00502 drawRectLines
00503 (
00504 IN const rect3d_t& r,
00505 IN const glut_color_t& c
00506 )
00507 throw()
00508 {
00509         glColor4bv((GLbyte *) &c);
00510         glBegin(GL_LINES);
00511         point3d_t p0, p1;
00512         for (int i = 0; i < rect3d_t::eMaxEdges; ++i) {
00513                 r.getEdge(i, p0, p1);
00514                 glVertex3fv((GLfloat *) &p0.x);
00515                 glVertex3fv((GLfloat *) &p1.x);
00516         }
00517         glEnd();
00518 }
00519 
00520 
00521 
00522 void
00523 getModelViewMatrix
00524 (
00525 OUT matrix4_t& T
00526 )
00527 throw()
00528 {
00529         glGetFloatv(GL_MODELVIEW_MATRIX, T.m);
00530         T.transpose();
00531 }
00532 
00533 
00534 
00535 };      // glut namespace
00536