glut.cpp

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