00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #ifndef INVT_HAVE_LIBGLUT
00049
00050 #include "Robots/LoBot/ui/LoMainWindow.H"
00051 #include "Robots/LoBot/misc/LoExcept.H"
00052
00053 namespace lobot {
00054
00055 MainWindow::MainWindow()
00056 {
00057 throw missing_libs(MISSING_OPENGL) ;
00058 }
00059
00060
00061
00062
00063
00064
00065 }
00066
00067 #else // OpenGL and GLUT available ==> the real McCoy
00068
00069
00070
00071
00072 #include "Robots/LoBot/ui/LoMainWindow.H"
00073 #include "Robots/LoBot/ui/LoRenderBuffer.H"
00074
00075 #include "Robots/LoBot/LoApp.H"
00076 #include "Robots/LoBot/config/LoConfigHelpers.H"
00077
00078 #include "Robots/LoBot/thread/LoShutdown.H"
00079 #include "Robots/LoBot/thread/LoPause.H"
00080
00081 #include "Robots/LoBot/util/LoFile.H"
00082 #include "Robots/LoBot/util/LoString.H"
00083 #include "Robots/LoBot/util/LoMath.H"
00084 #include "Robots/LoBot/util/LoTime.H"
00085
00086 #include "Robots/LoBot/misc/LoExcept.H"
00087 #include "Robots/LoBot/misc/singleton.hh"
00088
00089
00090 #include "Util/log.H"
00091
00092
00093 #ifdef INVT_HAVE_LIBDEVIL
00094 #include <IL/il.h>
00095 #endif
00096
00097
00098 #include <GL/glut.h>
00099
00100
00101 #include <boost/bind.hpp>
00102
00103
00104 #include <iomanip>
00105 #include <sstream>
00106 #include <string>
00107 #include <algorithm>
00108 #include <functional>
00109
00110
00111 #include <sys/stat.h>
00112
00113
00114
00115 namespace lobot {
00116
00117
00118
00119 namespace {
00120
00121
00122
00123 class Params : public singleton<Params> {
00124
00125 Params() ;
00126 friend class singleton<Params> ;
00127
00128
00129
00130 std::string m_title ;
00131
00132
00133
00134
00135
00136 bool m_screen_capture ;
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146 std::string m_sc_dir ;
00147
00148
00149
00150
00151
00152
00153 int m_sc_len ;
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165 std::string m_sc_fmt ;
00166
00167
00168 bool m_sc_png ;
00169
00170
00171
00172
00173
00174
00175
00176
00177 float m_initial_zoom ;
00178
00179
00180
00181
00182
00183
00184 float m_zoom_drag_factor ;
00185
00186
00187
00188
00189
00190 int m_update_frequency ;
00191
00192 public:
00193
00194
00195 static const std::string& title() {return instance().m_title ;}
00196 static const std::string& sc_dir() {return instance().m_sc_dir ;}
00197 static const std::string& sc_fmt() {return instance().m_sc_fmt ;}
00198 static int sc_len() {return instance().m_sc_len ;}
00199 static bool sc_png() {return instance().m_sc_png ;}
00200 static bool screen_capture() {return instance().m_screen_capture ;}
00201 static float initial_zoom() {return instance().m_initial_zoom ;}
00202 static float zoom_drag_factor() {return instance().m_zoom_drag_factor ;}
00203 static int update_frequency() {return instance().m_update_frequency ;}
00204
00205 } ;
00206
00207
00208 Params::Params()
00209 : m_title(ui_conf<std::string>("title", "Robolocust")),
00210 m_screen_capture(ui_conf("screen_capture", false)),
00211 m_sc_dir(ui_conf<std::string>("screen_capture_dir", "/tmp/lobot-frames-")
00212 + startup_timestamp_str()),
00213 m_sc_len(clamp(ui_conf("screen_capture_len", 6), 3, 9)),
00214 m_sc_fmt(downstring(ui_conf<std::string>("screen_capture_fmt", "png"))),
00215 m_sc_png(m_sc_fmt == "png"),
00216 m_initial_zoom(clamp(ui_conf("initial_zoom", 1.0f), 0.1f, 5.0f)),
00217 m_zoom_drag_factor(clamp(ui_conf("zoom_drag_factor", 0.1f), 0.01f, 2.5f)),
00218 m_update_frequency(clamp(ui_conf("update_frequency", 250), 100, 60000))
00219 {
00220 if (m_screen_capture && (mkdir(m_sc_dir.c_str(), 0755) != 0))
00221 LERROR("failed to create directory \"%s\"", m_sc_dir.c_str()) ;
00222 }
00223
00224 }
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234 MainWindow::MainWindow()
00235 : m_window(0), m_render_buffer(0), m_width(-1), m_height(-1),
00236 m_frame_number(0),
00237 m_drag_button(-1), m_drag_modifiers(-1)
00238 {
00239 start("lobot_ui", false) ;
00240 }
00241
00242
00243
00244
00245
00246 namespace {
00247
00248
00249
00250
00251
00252
00253 class calc_size {
00254 int left, right, bottom, top ;
00255 public:
00256 calc_size() ;
00257 void operator()(Drawable*) ;
00258
00259 int width() const {return right - left ;}
00260 int height() const {return bottom - top ;}
00261 } ;
00262
00263 calc_size::calc_size()
00264 : left (std::numeric_limits<int>::max()),
00265 right (std::numeric_limits<int>::min()),
00266 bottom(std::numeric_limits<int>::min()),
00267 top (std::numeric_limits<int>::max())
00268 {}
00269
00270 void calc_size::operator()(Drawable* d)
00271 {
00272 if (d->visible()) {
00273 Drawable::Geometry g = d->geometry() ;
00274 left = std::min(left, g.x) ;
00275 right = std::max(right, g.x + g.width) ;
00276 bottom = std::max(bottom, g.y + g.height) ;
00277 top = std::min(top, g.y) ;
00278 }
00279 }
00280
00281 }
00282
00283
00284
00285 void MainWindow::run()
00286 {
00287 try
00288 {
00289 App::wait_for_init() ;
00290
00291 int argc = App::argc() ;
00292 glutInit(& argc, const_cast<char**>(App::argv())) ;
00293 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE) ;
00294
00295 #ifdef INVT_HAVE_LIBDEVIL
00296 ilInit() ;
00297 #endif
00298
00299
00300
00301 {
00302 AutoMutex M(m_window_mutex) ;
00303 m_window = glutCreateWindow(Params::title().c_str()) ;
00304 }
00305
00306 typedef MainWindow me ;
00307 m_keymap['r'] = & me::reset_zoom_pan ;
00308 m_keymap['p'] = & me::pause ;
00309 m_keymap['q'] = & me::quit ;
00310 m_keymap['Q'] = & me::quit ;
00311 m_keymap[27] = & me::quit ;
00312 m_keymap[ 3] = & me::quit ;
00313 m_keymap[ 4] = & me::quit ;
00314 m_keymap[17] = & me::quit ;
00315 m_keymap[24] = & me::quit ;
00316
00317 m_drag_prev[0] = m_drag_prev[1] = -1 ;
00318
00319 calc_size s =
00320 std::for_each(m_drawables.begin(), m_drawables.end(), calc_size()) ;
00321 m_width = s.width() ;
00322 m_height = s.height() ;
00323
00324 m_render_buffer = new RenderBuffer(m_width, m_height) ;
00325
00326 glutReshapeFunc(reshape_callback) ;
00327 glutDisplayFunc(render_callback) ;
00328 glutKeyboardFunc(keyboard_callback) ;
00329 glutMouseFunc(click_callback) ;
00330 glutMotionFunc(drag_callback) ;
00331 glutIdleFunc(idle_callback) ;
00332 setup_timer() ;
00333
00334
00335
00336
00337
00338 {
00339 AutoMutex M(m_drawables_mutex) ;
00340 std::for_each(m_drawables.begin(), m_drawables.end(),
00341 std::mem_fun(& Drawable::gl_init)) ;
00342 }
00343
00344 glutMainLoop() ;
00345 }
00346 catch (std::exception& e)
00347 {
00348 LERROR("%s", e.what()) ;
00349 quit() ;
00350 }
00351 }
00352
00353
00354
00355
00356
00357 void MainWindow::push_back(Drawable* d)
00358 {
00359 if (d)
00360 {
00361
00362
00363
00364
00365
00366
00367
00368 {
00369 AutoMutex W(m_window_mutex) ;
00370 if (m_window)
00371 d->gl_init() ;
00372 }
00373
00374 AutoMutex D(m_drawables_mutex) ;
00375 m_drawables.push_back(d) ;
00376 }
00377 }
00378
00379
00380
00381
00382 void MainWindow::setup_timer()
00383 {
00384 glutTimerFunc(Params::update_frequency(), timer_callback, 0) ;
00385 }
00386
00387
00388
00389 void MainWindow::update()
00390 {
00391 glutPostRedisplay() ;
00392 if (Pause::is_clear())
00393 setup_timer() ;
00394 }
00395
00396
00397
00398 namespace {
00399
00400 class render_drawable {
00401 int W, H ;
00402 public:
00403 render_drawable(int ui_width, int ui_height) ;
00404 void operator()(Drawable*) const ;
00405 } ;
00406
00407 render_drawable::render_drawable(int w, int h)
00408 : W(w), H(h)
00409 {}
00410
00411 void render_drawable::operator()(Drawable* d) const
00412 {
00413 if (d->invisible())
00414 return ;
00415
00416 Drawable::Geometry g = d->geometry() ;
00417 glViewport(g.x, H - (g.y + g.height), g.width, g.height) ;
00418
00419 d->render() ;
00420
00421
00422 if (d->border()) {
00423 glMatrixMode(GL_PROJECTION) ;
00424 glPushMatrix() ;
00425 glLoadIdentity() ;
00426 gluOrtho2D(0, g.width, 0, g.height) ;
00427
00428 glMatrixMode(GL_MODELVIEW) ;
00429 glPushMatrix() ;
00430 glLoadIdentity() ;
00431
00432 glPushAttrib(GL_CURRENT_BIT) ;
00433 glColor3fv(d->border_color().rgb()) ;
00434 glBegin(GL_LINE_LOOP) ;
00435 glVertex2i(1, 1) ;
00436 glVertex2i(g.width - 1, 1) ;
00437 glVertex2i(g.width - 1, g.height - 1) ;
00438 glVertex2i(1, g.height - 1) ;
00439 glEnd() ;
00440 glPopAttrib() ;
00441
00442 glMatrixMode(GL_PROJECTION) ;
00443 glPopMatrix() ;
00444 glMatrixMode(GL_MODELVIEW) ;
00445 glPopMatrix() ;
00446 }
00447 }
00448
00449 }
00450
00451
00452
00453 void MainWindow::render()
00454 {
00455 m_render_buffer->setup() ;
00456
00457 static bool first_time = true ;
00458 if (first_time)
00459 {
00460 reset_zoom_pan() ;
00461 AutoMutex M(m_drawables_mutex) ;
00462 std::for_each(m_drawables.begin(), m_drawables.end(),
00463 std::bind2nd(std::mem_fun(& Drawable::zoom_by),
00464 Params::initial_zoom() - 1)) ;
00465 first_time = false ;
00466 }
00467
00468 glClear(GL_COLOR_BUFFER_BIT) ;
00469
00470
00471 {
00472 AutoMutex M(m_drawables_mutex) ;
00473 std::for_each(m_drawables.begin(), m_drawables.end(),
00474 render_drawable(m_width, m_height)) ;
00475 }
00476
00477
00478
00479
00480 if (Params::screen_capture())
00481 m_capture_queue.push(new ScreenCapture(m_frame_number++,
00482 m_width, m_height,
00483 m_render_buffer->pixels(),
00484 m_render_buffer->size())) ;
00485
00486 m_render_buffer->to_screen() ;
00487 glutSwapBuffers() ;
00488 }
00489
00490
00491
00492
00493 void MainWindow::save_screenshot(const std::string& file_name) const
00494 {
00495 ScreenCapture sc(file_name, m_width, m_height,
00496 m_render_buffer->pixels(), m_render_buffer->size()) ;
00497 sc.save() ;
00498 }
00499
00500
00501
00502
00503
00504
00505
00506
00507 static void fix_alpha_values(unsigned char* start, unsigned char* end)
00508 {
00509 for (unsigned char* p = start + 3; p < end; p += 4)
00510 *p = 255 ;
00511 }
00512
00513
00514
00515 MainWindow::ScreenCapture::
00516 ScreenCapture(const std::string& file_name,
00517 int w, int h, const unsigned char* data, int n)
00518 : m_name(file_name), m_width(w), m_height(h), m_data(data, data + n)
00519 {
00520 if (downstring(extension(m_name)) == "png")
00521 fix_alpha_values(&m_data[0], &m_data[0] + n) ;
00522 }
00523
00524
00525
00526 MainWindow::ScreenCapture::
00527 ScreenCapture(int frame_number, int w, int h, const unsigned char* data, int n)
00528 : m_width(w), m_height(h), m_data(data, data + n)
00529 {
00530 using std::setfill ; using std::setw ;
00531 std::ostringstream file_name ;
00532 file_name << Params::sc_dir() << "/frame"
00533 << setfill('0') << setw(Params::sc_len()) << frame_number
00534 << '.' << Params::sc_fmt() ;
00535 m_name = file_name.str() ;
00536
00537 if (Params::sc_png())
00538 fix_alpha_values(&m_data[0], &m_data[0] + n) ;
00539 }
00540
00541
00542
00543
00544
00545
00546 void MainWindow::ScreenCapture::save() const
00547 {
00548 #ifdef INVT_HAVE_LIBDEVIL
00549 ILuint image ;
00550 ilGenImages(1, &image) ;
00551 ilBindImage(image) ;
00552
00553 unsigned char* data = const_cast<unsigned char*>(&m_data[0]) ;
00554 ilTexImage(m_width, m_height, 1, 4, IL_BGRA, IL_UNSIGNED_BYTE, data) ;
00555 ilSaveImage(const_cast<char*>(m_name.c_str())) ;
00556 if (ilGetError() != IL_NO_ERROR)
00557 LERROR("error writing \"%s\"", m_name.c_str()) ;
00558
00559 ilDeleteImages(1, &image) ;
00560 #else
00561 throw missing_libs(MISSING_LIBDEVIL) ;
00562 #endif
00563 }
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573 void MainWindow::dump_next_frame()
00574 {
00575 ScreenCapture* frame = m_capture_queue.front() ;
00576 m_capture_queue.pop() ;
00577 frame->save() ;
00578 delete frame ;
00579 }
00580
00581
00582
00583
00584
00585
00586
00587 void MainWindow::reshape(int W, int H)
00588 {
00589 if (W == m_width && H == m_height)
00590 return ;
00591 glutReshapeWindow(m_width, m_height) ;
00592 }
00593
00594
00595
00596
00597 void MainWindow::handle_key(unsigned char key)
00598 {
00599
00600 KeyMap::iterator handler = m_keymap.find(key) ;
00601 if (handler != m_keymap.end())
00602 (this->*(handler->second))() ;
00603
00604
00605
00606 {
00607 AutoMutex M(m_drawables_mutex) ;
00608 std::for_each(m_drawables.begin(), m_drawables.end(),
00609 std::bind2nd(std::mem_fun(&Drawable::keypress), key)) ;
00610 }
00611
00612
00613 glutPostRedisplay() ;
00614 }
00615
00616 void MainWindow::reset_zoom_pan()
00617 {
00618 AutoMutex M(m_drawables_mutex) ;
00619 std::for_each(m_drawables.begin(), m_drawables.end(),
00620 std::mem_fun(& Drawable::reset_zoom_pan)) ;
00621 }
00622
00623 void MainWindow::pause()
00624 {
00625 Pause::toggle() ;
00626 if (Pause::is_clear())
00627 setup_timer() ;
00628 }
00629
00630 void MainWindow::quit()
00631 {
00632 AutoMutex M(m_drawables_mutex) ;
00633 std::for_each(m_drawables.begin(), m_drawables.end(),
00634 std::mem_fun(& Drawable::gl_cleanup)) ;
00635
00636 glutDestroyWindow(m_window) ;
00637 Shutdown::signal() ;
00638 pthread_exit(0) ;
00639 }
00640
00641
00642
00643 void MainWindow::left_click(int state, int modifiers, int x, int y)
00644 {
00645 switch (state)
00646 {
00647 case GLUT_DOWN:
00648 m_drag_button = GLUT_LEFT_BUTTON ;
00649 m_drag_modifiers = modifiers ;
00650 m_drag_prev[0] = x ;
00651 m_drag_prev[1] = y ;
00652 break ;
00653 case GLUT_UP:
00654 m_drag_button = -1 ;
00655 m_drag_modifiers = -1 ;
00656 m_drag_prev[0] = -1 ;
00657 m_drag_prev[1] = -1 ;
00658 break ;
00659 }
00660 }
00661
00662 void MainWindow::middle_click(int state, int modifiers, int x, int y)
00663 {
00664 switch (state)
00665 {
00666 case GLUT_DOWN:
00667 m_drag_button = GLUT_MIDDLE_BUTTON ;
00668 m_drag_modifiers = modifiers ;
00669 m_drag_prev[0] = x ;
00670 m_drag_prev[1] = y ;
00671 break ;
00672 case GLUT_UP:
00673 m_drag_button = -1 ;
00674 m_drag_modifiers = -1 ;
00675 m_drag_prev[0] = -1 ;
00676 m_drag_prev[1] = -1 ;
00677 break ;
00678 }
00679 }
00680
00681 void MainWindow::right_click(int state, int modifiers, int x, int y)
00682 {
00683 switch (state)
00684 {
00685 case GLUT_DOWN:
00686 m_drag_button = GLUT_RIGHT_BUTTON ;
00687 m_drag_modifiers = modifiers ;
00688 m_drag_prev[0] = x ;
00689 m_drag_prev[1] = y ;
00690 break ;
00691 case GLUT_UP:
00692 m_drag_button = -1 ;
00693 m_drag_modifiers = -1 ;
00694 m_drag_prev[0] = -1 ;
00695 m_drag_prev[1] = -1 ;
00696 break ;
00697 }
00698 }
00699
00700 void MainWindow::left_drag(int x, int y)
00701 {
00702 if (m_drag_modifiers & GLUT_ACTIVE_SHIFT)
00703 {
00704 const float dz = (y - m_drag_prev[1]) * Params::zoom_drag_factor() ;
00705 AutoMutex M(m_drawables_mutex) ;
00706 std::for_each(m_drawables.begin(), m_drawables.end(),
00707 std::bind2nd(std::mem_fun(& Drawable::zoom_by), -dz)) ;
00708 }
00709 else
00710 {
00711 AutoMutex M(m_drawables_mutex) ;
00712 std::for_each(m_drawables.begin(), m_drawables.end(),
00713 boost::bind(& Drawable::pan, _1,
00714 x, y, m_drag_prev[0], m_drag_prev[1])) ;
00715 }
00716
00717 m_drag_prev[0] = x ;
00718 m_drag_prev[1] = y ;
00719
00720 glutPostRedisplay() ;
00721 }
00722
00723 void MainWindow::middle_drag(int x, int y)
00724 {
00725
00726 {
00727 const float dz = (y - m_drag_prev[1]) * Params::zoom_drag_factor() ;
00728 AutoMutex M(m_drawables_mutex) ;
00729 std::for_each(m_drawables.begin(), m_drawables.end(),
00730 std::bind2nd(std::mem_fun(& Drawable::zoom_by), -dz)) ;
00731 }
00732
00733 m_drag_prev[0] = x ;
00734 m_drag_prev[1] = y ;
00735
00736 glutPostRedisplay() ;
00737 }
00738
00739 void MainWindow::right_drag(int, int)
00740 {
00741 }
00742
00743
00744
00745 void MainWindow::reshape_callback(int width, int height)
00746 {
00747 instance().reshape(width, height) ;
00748 }
00749
00750 void MainWindow::render_callback()
00751 {
00752 instance().render() ;
00753 }
00754
00755 void MainWindow::keyboard_callback(unsigned char key, int, int)
00756 {
00757 instance().handle_key(key) ;
00758 }
00759
00760 void MainWindow::click_callback(int button, int state, int x, int y)
00761 {
00762 switch (button)
00763 {
00764 case GLUT_LEFT_BUTTON:
00765 instance().left_click(state, glutGetModifiers(), x, y) ;
00766 break ;
00767 case GLUT_MIDDLE_BUTTON:
00768 instance().middle_click(state, glutGetModifiers(), x, y) ;
00769 break ;
00770 case GLUT_RIGHT_BUTTON:
00771 instance().right_click(state, glutGetModifiers(), x, y) ;
00772 break ;
00773 }
00774 }
00775
00776 void MainWindow::drag_callback(int x, int y)
00777 {
00778 switch (instance().m_drag_button)
00779 {
00780 case GLUT_LEFT_BUTTON:
00781 instance().left_drag(x, y) ;
00782 break ;
00783 case GLUT_MIDDLE_BUTTON:
00784 instance().middle_drag(x, y) ;
00785 break ;
00786 case GLUT_RIGHT_BUTTON:
00787 instance().right_drag(x, y) ;
00788 break ;
00789 }
00790 }
00791
00792
00793
00794
00795
00796 void MainWindow::timer_callback(int)
00797 {
00798 instance().update() ;
00799 }
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811 void MainWindow::idle_callback()
00812 {
00813 MainWindow& W = instance() ;
00814 if (Shutdown::signaled())
00815 {
00816 AutoMutex M(W.m_drawables_mutex) ;
00817 std::for_each(W.m_drawables.begin(), W.m_drawables.end(),
00818 std::mem_fun(& Drawable::gl_cleanup)) ;
00819
00820 glutDestroyWindow(W.m_window) ;
00821 pthread_exit(0) ;
00822 }
00823 if (! W.m_capture_queue.empty())
00824 W.dump_next_frame() ;
00825 }
00826
00827
00828
00829 MainWindow::~MainWindow()
00830 {
00831 delete m_render_buffer ;
00832 while (! m_capture_queue.empty())
00833 dump_next_frame() ;
00834 }
00835
00836
00837
00838 }
00839
00840 #endif // #ifndef INVT_HAVE_LIBGLUT
00841
00842
00843
00844
00845