display.cpp
Go to the documentation of this file.
00001 /* This file is part of the Pangolin Project.
00002  * http://github.com/stevenlovegrove/Pangolin
00003  *
00004  * Copyright (c) 2011 Steven Lovegrove, Richard Newcombe
00005  *
00006  * Permission is hereby granted, free of charge, to any person
00007  * obtaining a copy of this software and associated documentation
00008  * files (the "Software"), to deal in the Software without
00009  * restriction, including without limitation the rights to use,
00010  * copy, modify, merge, publish, distribute, sublicense, and/or sell
00011  * copies of the Software, and to permit persons to whom the
00012  * Software is furnished to do so, subject to the following
00013  * conditions:
00014  *
00015  * The above copyright notice and this permission notice shall be
00016  * included in all copies or substantial portions of the Software.
00017  *
00018  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00019  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
00020  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00021  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
00022  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
00023  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00024  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00025  * OTHER DEALINGS IN THE SOFTWARE.
00026  */
00027 
00028 #include <iostream>
00029 #include <sstream>
00030 #include <map>
00031 
00032 #include <boost/function.hpp>
00033 #include <boost/foreach.hpp>
00034 #define foreach BOOST_FOREACH
00035 
00036 #include "platform.h"
00037 #include "display.h"
00038 #include "display_internal.h"
00039 #include "simple_math.h"
00040 
00041 using namespace std;
00042 
00043 namespace pangolin
00044 {
00045 
00046 const int panal_v_margin = 6;
00047 
00048 typedef boost::ptr_unordered_map<string,PangolinGl> ContextMap;
00049 
00050 // Map of active contexts
00051 ContextMap contexts;
00052 
00053 // Context active for current thread
00054 __thread PangolinGl* context = 0;
00055 
00056 PangolinGl::PangolinGl()
00057     : quit(false), mouse_state(0), activeDisplay(0)
00058 {
00059 }
00060 
00061 void BindToContext(std::string name)
00062 {
00063     ContextMap::iterator ic = contexts.find(name);
00064 
00065     if( ic == contexts.end() )
00066     {
00067         // Create and add if not found
00068         ic = contexts.insert( name,new PangolinGl ).first;
00069         context = ic->second;
00070         View& dc = context->base;
00071         dc.left = 0.0;
00072         dc.bottom = 0.0;
00073         dc.top = 1.0;
00074         dc.right = 1.0;
00075         dc.aspect = 0;
00076         dc.handler = &StaticHandler;
00077         context->is_fullscreen = false;
00078 #ifdef HAVE_GLUT
00079         process::Resize(
00080             glutGet(GLUT_WINDOW_WIDTH),
00081             glutGet(GLUT_WINDOW_HEIGHT)
00082         );
00083 #else
00084         process::Resize(640,480);
00085 #endif //HAVE_GLUT
00086     }
00087     else
00088     {
00089         context = ic->second;
00090     }
00091 }
00092 
00093 void Quit()
00094 {
00095     context->quit = true;
00096 }
00097 
00098 bool ShouldQuit()
00099 {
00100 #ifdef HAVE_GLUT
00101     return context->quit || !glutGetWindow();
00102 #else
00103     return context->quit;
00104 #endif
00105 }
00106 
00107 bool HadInput()
00108 {
00109     if( context->had_input > 0 )
00110     {
00111         --context->had_input;
00112         return true;
00113     }
00114     return false;
00115 }
00116 
00117 bool HasResized()
00118 {
00119     if( context->has_resized > 0 )
00120     {
00121         --context->has_resized;
00122         return true;
00123     }
00124     return false;
00125 }
00126 
00127 void RenderViews()
00128 {
00129     DisplayBase().Render();
00130 }
00131 
00132 View& DisplayBase()
00133 {
00134     return context->base;
00135 }
00136 
00137 View& CreateDisplay()
00138 {
00139     int iguid = rand();
00140     std::stringstream ssguid;
00141     ssguid << iguid;
00142     return Display(ssguid.str());
00143 }
00144 
00145 View& Display(const std::string& name)
00146 {
00147     // Get / Create View
00148     boost::ptr_unordered_map<std::string,View>::iterator vi = context->named_managed_views.find(name);
00149     if( vi != context->named_managed_views.end() )
00150     {
00151         return *(vi->second);
00152     }
00153     else
00154     {
00155         View * v = new View();
00156         bool inserted = context->named_managed_views.insert(name, v).second;
00157         if(!inserted) throw exception();
00158         v->handler = &StaticHandler;
00159         context->base.views.push_back(v);
00160         return *v;
00161     }
00162 }
00163 
00164 void RegisterKeyPressCallback(int key, boost::function<void(void)> func)
00165 {
00166     context->keypress_hooks[key] = func;
00167 }
00168 
00169 namespace process
00170 {
00171 unsigned int last_x;
00172 unsigned int last_y;
00173 
00174 void Keyboard( unsigned char key, int x, int y)
00175 {
00176     context->had_input = context->is_double_buffered ? 2 : 1;
00177 
00178     // Force coords to match OpenGl Window Coords
00179     y = context->base.v.h - y;
00180 
00181     if( key == GLUT_KEY_ESCAPE)
00182     {
00183         context->quit = true;
00184     }
00185 #ifdef HAVE_CVARS
00186     else if(key == '`')
00187     {
00188         context->console.ToggleConsole();
00189         // Force refresh for several frames whilst panel opens/closes
00190         context->had_input = 60*2;
00191     }
00192     else if(context->console.IsOpen())
00193     {
00194         // Direct input to console
00195         if( key > 128 )
00196         {
00197             context->console.SpecialFunc(key - 128 );
00198         }
00199         else
00200         {
00201             context->console.KeyboardFunc(key);
00202         }
00203     }
00204 #endif // HAVE_CVARS
00205 #ifdef HAVE_GLUT
00206     else if( key == GLUT_KEY_TAB)
00207     {
00208         if( context->is_fullscreen )
00209         {
00210             glutReshapeWindow(context->windowed_size[0],context->windowed_size[1]);
00211             context->is_fullscreen = false;
00212         }
00213         else
00214         {
00215             glutFullScreen();
00216             context->is_fullscreen = true;
00217         }
00218     }
00219 #endif // HAVE_GLUT
00220     else if(context->keypress_hooks.find(key) != context->keypress_hooks.end() )
00221     {
00222         context->keypress_hooks[key]();
00223     }
00224     else if(context->activeDisplay && context->activeDisplay->handler)
00225     {
00226         context->activeDisplay->handler->Keyboard(*(context->activeDisplay),key,x,y,true);
00227     }
00228 }
00229 
00230 void KeyboardUp(unsigned char key, int x, int y)
00231 {
00232     // Force coords to match OpenGl Window Coords
00233     y = context->base.v.h - y;
00234 
00235     if(context->activeDisplay && context->activeDisplay->handler)
00236     {
00237         context->activeDisplay->handler->Keyboard(*(context->activeDisplay),key,x,y,false);
00238     }
00239 }
00240 
00241 void SpecialFunc(int key, int x, int y)
00242 {
00243     Keyboard(key+128,x,y);
00244 }
00245 
00246 void SpecialFuncUp(int key, int x, int y)
00247 {
00248     KeyboardUp(key+128,x,y);
00249 }
00250 
00251 
00252 void Mouse( int button_raw, int state, int x, int y)
00253 {
00254     last_x = x;
00255     last_y = y;
00256 
00257     const MouseButton button = (MouseButton)(1 << button_raw);
00258     const bool pressed = (state == 0);
00259 
00260     context->had_input = context->is_double_buffered ? 2 : 1;
00261 
00262     // Force coords to match OpenGl Window Coords
00263     y = context->base.v.h - y;
00264 
00265     const bool fresh_input = (context->mouse_state == 0);
00266 
00267     if( pressed )
00268     {
00269         context->mouse_state |= button;
00270     }
00271     else
00272     {
00273         context->mouse_state &= ~button;
00274     }
00275 
00276     if(fresh_input)
00277     {
00278         context->base.handler->Mouse(context->base,button,x,y,pressed,context->mouse_state);
00279     }
00280     else if(context->activeDisplay && context->activeDisplay->handler)
00281     {
00282         context->activeDisplay->handler->Mouse(*(context->activeDisplay),button,x,y,pressed,context->mouse_state);
00283     }
00284 }
00285 
00286 void MouseMotion( int x, int y)
00287 {
00288     last_x = x;
00289     last_y = y;
00290 
00291     context->had_input = context->is_double_buffered ? 2 : 1;
00292 
00293     // Force coords to match OpenGl Window Coords
00294     y = context->base.v.h - y;
00295 
00296     if( context->activeDisplay)
00297     {
00298         if( context->activeDisplay->handler )
00299             context->activeDisplay->handler->MouseMotion(*(context->activeDisplay),x,y,context->mouse_state);
00300     }
00301     else
00302     {
00303         context->base.handler->MouseMotion(context->base,x,y,context->mouse_state);
00304     }
00305 }
00306 
00307 void PassiveMouseMotion(int x, int y)
00308 {
00309     last_x = x;
00310     last_y = y;
00311 }
00312 
00313 void Resize( int width, int height )
00314 {
00315     if( !context->is_fullscreen )
00316     {
00317         context->windowed_size[0] = width;
00318         context->windowed_size[1] = width;
00319     }
00320     // TODO: Fancy display managers seem to cause this to mess up?
00321     context->had_input = 20; //context->is_double_buffered ? 2 : 1;
00322     context->has_resized = 20; //context->is_double_buffered ? 2 : 1;
00323     Viewport win(0,0,width,height);
00324     context->base.Resize(win);
00325 }
00326 
00327 void Scroll(float x, float y)
00328 {
00329     cout << "Scroll: " << x << ", " << y << endl;
00330 
00331     if(x==0)
00332     {
00333         Mouse(y>0?3:4,0, last_x, last_y);
00334         context->mouse_state &= !MouseWheelUp;
00335         context->mouse_state &= !MouseWheelDown;
00336     }
00337 }
00338 
00339 void Zoom(float m)
00340 {
00341     cout << "Zoom: " << m << endl;
00342 }
00343 }
00344 
00345 #ifdef HAVE_GLUT
00346 void PangoGlutRedisplay()
00347 {
00348     glutPostRedisplay();
00349 
00350 //      RenderViews();
00351 //      FinishGlutFrame();
00352 }
00353 
00354 void TakeGlutCallbacks()
00355 {
00356     glutKeyboardFunc(&process::Keyboard);
00357     glutKeyboardUpFunc(&process::KeyboardUp);
00358     glutReshapeFunc(&process::Resize);
00359     glutMouseFunc(&process::Mouse);
00360     glutMotionFunc(&process::MouseMotion);
00361     glutPassiveMotionFunc(&process::PassiveMouseMotion);
00362     glutSpecialFunc(&process::SpecialFunc);
00363     glutSpecialUpFunc(&process::SpecialFuncUp);
00364 
00365 #ifdef HAVE_APPLE_OPENGL_FRAMEWORK
00366     glutDisplayFunc(&PangoGlutRedisplay);
00367 
00368     // Attempt to register special smooth scroll callback
00369     // https://github.com/nanoant/osxglut
00370     typedef void (*glutScrollFunc_t)(void (*)(float, float));
00371     glutScrollFunc_t glutScrollFunc = (glutScrollFunc_t)glutGetProcAddress("glutScrollFunc");
00372     if(glutScrollFunc)
00373     {
00374         glutScrollFunc(&process::Scroll);
00375     }
00376     typedef void (*glutZoomFunc_t)(void (*)(float));
00377     glutZoomFunc_t glutZoomFunc = (glutZoomFunc_t)glutGetProcAddress("glutZoomFunc");
00378     if(glutZoomFunc)
00379     {
00380         cout << "Registering zoom func" << endl;
00381         glutZoomFunc(&process::Zoom);
00382     }
00383 
00384 #endif
00385 }
00386 
00387 void CreateGlutWindowAndBind(string window_title, int w, int h, unsigned int mode)
00388 {
00389 #ifdef HAVE_FREEGLUT
00390     if( glutGet(GLUT_INIT_STATE) == 0)
00391 #endif
00392     {
00393         int argc = 0;
00394         glutInit(&argc, 0);
00395         glutInitDisplayMode(mode);
00396     }
00397     glutInitWindowSize(w,h);
00398     glutCreateWindow(window_title.c_str());
00399     BindToContext(window_title);
00400 
00401 #ifdef HAVE_FREEGLUT
00402     glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
00403 #endif
00404 
00405     context->is_double_buffered = mode & GLUT_DOUBLE;
00406     TakeGlutCallbacks();
00407 }
00408 
00409 void FinishGlutFrame()
00410 {
00411     RenderViews();
00412     DisplayBase().Activate();
00413     Viewport::DisableScissor();
00414 #ifdef HAVE_CVARS
00415     context->console.RenderConsole();
00416 #endif // HAVE_CVARS
00417     SwapGlutBuffersProcessGlutEvents();
00418 }
00419 
00420 void SwapGlutBuffersProcessGlutEvents()
00421 {
00422     glutSwapBuffers();
00423 
00424 #ifdef HAVE_FREEGLUT
00425     glutMainLoopEvent();
00426 #endif
00427 
00428 #ifdef HAVE_GLUT_APPLE_FRAMEWORK
00429     glutCheckLoop();
00430 #endif
00431 }
00432 #endif // HAVE_GLUT
00433 
00434 void Viewport::Activate() const
00435 {
00436     glViewport(l,b,w,h);
00437 }
00438 
00439 void Viewport::Scissor() const
00440 {
00441     glEnable(GL_SCISSOR_TEST);
00442     glScissor(l,b,w,h);
00443 }
00444 
00445 void Viewport::ActivateAndScissor() const
00446 {
00447     glViewport(l,b,w,h);
00448     glEnable(GL_SCISSOR_TEST);
00449     glScissor(l,b,w,h);
00450 }
00451 
00452 
00453 void Viewport::DisableScissor()
00454 {
00455     glDisable(GL_SCISSOR_TEST);
00456 }
00457 
00458 bool Viewport::Contains(int x, int y) const
00459 {
00460     return l <= x && x < (l+w) && b <= y && y < (b+h);
00461 }
00462 
00463 Viewport Viewport::Inset(int i) const
00464 {
00465     return Viewport(l+i, b+i, w-2*i, h-2*i);
00466 }
00467 
00468 Viewport Viewport::Inset(int horiz, int vert) const
00469 {
00470     return Viewport(l+horiz, b+vert, w-horiz, h-vert);
00471 }
00472 
00473 void OpenGlMatrix::Load() const
00474 {
00475     glLoadMatrixd(m);
00476 }
00477 
00478 void OpenGlMatrix::Multiply() const
00479 {
00480     glMultMatrixd(m);
00481 }
00482 
00483 void OpenGlMatrix::SetIdentity()
00484 {
00485     m[0] = 1.0f;
00486     m[1] = 0.0f;
00487     m[2] = 0.0f;
00488     m[3] = 0.0f;
00489     m[4] = 0.0f;
00490     m[5] = 1.0f;
00491     m[6] = 0.0f;
00492     m[7] = 0.0f;
00493     m[8] = 0.0f;
00494     m[9] = 0.0f;
00495     m[10] = 1.0f;
00496     m[11] = 0.0f;
00497     m[12] = 0.0f;
00498     m[13] = 0.0f;
00499     m[14] = 0.0f;
00500     m[15] = 1.0f;
00501 }
00502 
00503 void OpenGlRenderState::Apply() const
00504 {
00505     // Apply any stack matrices we have
00506     for(map<OpenGlStack,OpenGlMatrix>::const_iterator i = stacks.begin(); i != stacks.end(); ++i )
00507     {
00508         glMatrixMode(i->first);
00509         i->second.Load();
00510     }
00511 
00512     // Leave in MODEVIEW mode
00513     glMatrixMode(GL_MODELVIEW);
00514 }
00515 
00516 OpenGlRenderState::OpenGlRenderState()
00517 {
00518 }
00519 
00520 OpenGlRenderState::OpenGlRenderState(const OpenGlMatrix& projection_matrix)
00521 {
00522     stacks[GlProjectionStack] = projection_matrix;
00523     stacks[GlModelViewStack] = IdentityMatrix();
00524 }
00525 
00526 OpenGlRenderState::OpenGlRenderState(const OpenGlMatrix& projection_matrix, const OpenGlMatrix& modelview_matrx)
00527 {
00528     stacks[GlProjectionStack] = projection_matrix;
00529     stacks[GlModelViewStack] = modelview_matrx;
00530 }
00531 
00532 void OpenGlRenderState::ApplyIdentity()
00533 {
00534     glMatrixMode(GL_PROJECTION);
00535     glLoadIdentity();
00536     glMatrixMode(GL_MODELVIEW);
00537     glLoadIdentity();
00538 }
00539 
00540 void OpenGlRenderState::ApplyWindowCoords()
00541 {
00542     context->base.v.Activate();
00543     glMatrixMode(GL_PROJECTION);
00544     glLoadIdentity();
00545     gluOrtho2D(0, context->base.v.w, 0, context->base.v.h);
00546     glMatrixMode(GL_MODELVIEW);
00547     glLoadIdentity();
00548 }
00549 
00550 OpenGlRenderState& OpenGlRenderState::SetProjectionMatrix(OpenGlMatrix spec)
00551 {
00552     stacks[GlProjectionStack] = spec;
00553     return *this;
00554 }
00555 
00556 OpenGlRenderState& OpenGlRenderState::SetModelViewMatrix(OpenGlMatrix spec)
00557 {
00558     stacks[GlModelViewStack] = spec;
00559     return *this;
00560 }
00561 
00562 OpenGlRenderState& OpenGlRenderState::Set(OpenGlMatrixSpec spec)
00563 {
00564     stacks[spec.type] = spec;
00565     return *this;
00566 }
00567 
00568 OpenGlMatrix& OpenGlRenderState::GetProjectionMatrix()
00569 {
00570     return stacks[GlProjectionStack];
00571 }
00572 
00573 OpenGlMatrix OpenGlRenderState::GetProjectionMatrix() const
00574 {
00575     std::map<OpenGlStack,OpenGlMatrix>::const_iterator i = stacks.find(GlProjectionStack);
00576     if( i == stacks.end() )
00577     {
00578         return IdentityMatrix();
00579     }
00580     else
00581     {
00582         return i->second;
00583     }
00584 }
00585 
00586 OpenGlMatrix& OpenGlRenderState::GetModelViewMatrix()
00587 {
00588     return stacks[GlModelViewStack];
00589 }
00590 
00591 OpenGlMatrix OpenGlRenderState::GetModelViewMatrix() const
00592 {
00593     std::map<OpenGlStack,OpenGlMatrix>::const_iterator i = stacks.find(GlModelViewStack);
00594     if( i == stacks.end() )
00595     {
00596         return IdentityMatrix();
00597     }
00598     else
00599     {
00600         return i->second;
00601     }
00602 }
00603 
00604 int AttachAbs( int low, int high, Attach a)
00605 {
00606     if( a.unit == Pixel ) return low + a.p;
00607     if( a.unit == ReversePixel ) return high - a.p;
00608     return low + a.p * (high - low);
00609 }
00610 
00611 float AspectAreaWithinTarget(double target, double test)
00612 {
00613     if( test < target )
00614         return test / target;
00615     else
00616         return target / test;
00617 }
00618 
00619 void View::Resize(const Viewport& p)
00620 {
00621     // Compute Bounds based on specification
00622     v.l = AttachAbs(p.l,p.r(),left);
00623     v.b = AttachAbs(p.b,p.t(),bottom);
00624     int r = AttachAbs(p.l,p.r(),right);
00625     int t = AttachAbs(p.b,p.t(),top);
00626 
00627     // Make sure left and right, top and bottom are correct order
00628     if( t < v.b ) swap(t,v.b);
00629     if( r < v.l ) swap(r,v.l);
00630 
00631     v.w = r - v.l;
00632     v.h = t - v.b;
00633 
00634     vp = v;
00635 
00636     // Adjust based on aspect requirements
00637     if( aspect != 0 )
00638     {
00639         const float current_aspect = (float)v.w / (float)v.h;
00640         if( aspect > 0 )
00641         {
00642             // Fit to space
00643             if( current_aspect < aspect )
00644             {
00645                 //Adjust height
00646                 const int nh = (int)(v.w / aspect);
00647                 v.b += vlock == LockBottom ? 0 : (vlock == LockCenter ? (v.h-nh)/2 : (v.h-nh) );
00648                 v.h = nh;
00649             }
00650             else if( current_aspect > aspect )
00651             {
00652                 //Adjust width
00653                 const int nw = (int)(v.h * aspect);
00654                 v.l += hlock == LockLeft? 0 : (hlock == LockCenter ? (v.w-nw)/2 : (v.w-nw) );
00655                 v.w = nw;
00656             }
00657         }
00658         else
00659         {
00660             // Overfit
00661             double true_aspect = -aspect;
00662             if( current_aspect < true_aspect )
00663             {
00664                 //Adjust width
00665                 const int nw = (int)(v.h * true_aspect);
00666                 v.l += hlock == LockLeft? 0 : (hlock == LockCenter ? (v.w-nw)/2 : (v.w-nw) );
00667                 v.w = nw;
00668             }
00669             else if( current_aspect > true_aspect )
00670             {
00671                 //Adjust height
00672                 const int nh = (int)(v.w / true_aspect);
00673                 v.b += vlock == LockBottom ? 0 : (vlock == LockCenter ? (v.h-nh)/2 : (v.h-nh) );
00674                 v.h = nh;
00675             }
00676         }
00677     }
00678 
00679     ResizeChildren();
00680 }
00681 
00682 void View::ResizeChildren()
00683 {
00684     if( layout == LayoutOverlay )
00685     {
00686         foreach(View* i, views)
00687         i->Resize(v);
00688     }
00689     else if( layout == LayoutVertical )
00690     {
00691         // Allocate space incrementally
00692         Viewport space = v.Inset(panal_v_margin);
00693         int num_children = 0;
00694         foreach(View* i, views )
00695         {
00696             num_children++;
00697             if(scroll_offset > num_children )
00698             {
00699                 i->show = false;
00700             }
00701             else
00702             {
00703                 i->show = true;
00704                 i->Resize(space);
00705                 space.h = i->v.b - panal_v_margin - space.b;
00706             }
00707         }
00708     }
00709     else if(layout == LayoutHorizontal )
00710     {
00711         // Allocate space incrementally
00712         const int margin = 8;
00713         Viewport space = v.Inset(margin);
00714         foreach(View* i, views )
00715         {
00716             i->Resize(space);
00717             space.w = i->v.l + margin + space.l;
00718         }
00719     }
00720     else if(layout == LayoutEqual )
00721     {
00722         // TODO: Make this neater, and make fewer assumptions!
00723         if( views.size() > 0 )
00724         {
00725             const double this_a = abs(v.aspect());
00726             const double child_a = abs(views[0]->aspect);
00727             double a = views.size()*child_a;
00728             double area = AspectAreaWithinTarget(this_a, a);
00729 
00730             int cols = views.size()-1;
00731             for(; cols > 0; --cols)
00732             {
00733                 const int rows = views.size() / cols + (views.size() % cols == 0 ? 0 : 1);
00734                 const double na = cols * child_a / rows;
00735                 const double new_area = views.size()*AspectAreaWithinTarget(this_a,na)/(rows*cols);
00736                 if( new_area <= area )
00737                     break;
00738                 area = new_area;
00739                 a = na;
00740             }
00741 
00742             cols++;
00743             const int rows = views.size() / cols + (views.size() % cols == 0 ? 0 : 1);
00744             int cw,ch;
00745             if( a > this_a )
00746             {
00747                 cw = v.w / cols;
00748                 ch = cw / child_a; //v.h / rows;
00749             }
00750             else
00751             {
00752                 ch = v.h / rows;
00753                 cw = ch * child_a;
00754             }
00755 
00756             for( unsigned int i=0; i< views.size(); ++i )
00757             {
00758                 int c = i % cols;
00759                 int r = i / cols;
00760                 Viewport space(v.l + c*cw, v.t() - (r+1)*ch, cw,ch);
00761                 views[i]->Resize(space);
00762             }
00763         }
00764     }
00765 
00766 }
00767 
00768 void View::Render()
00769 {
00770     if(!extern_draw_function.empty())
00771     {
00772         extern_draw_function(*this);
00773     }
00774     RenderChildren();
00775 }
00776 
00777 void View::RenderChildren()
00778 {
00779     foreach(View* v, views)
00780     if(v->show) v->Render();
00781 }
00782 
00783 void View::Activate() const
00784 {
00785     v.Activate();
00786 }
00787 
00788 void View::ActivateAndScissor() const
00789 {
00790     vp.Scissor();
00791     v.Activate();
00792 }
00793 
00794 void View::ActivateScissorAndClear() const
00795 {
00796     vp.Scissor();
00797     v.Activate();
00798     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00799 }
00800 
00801 void View::Activate(const OpenGlRenderState& state ) const
00802 {
00803     v.Activate();
00804     state.Apply();
00805 }
00806 
00807 void View::ActivateAndScissor(const OpenGlRenderState& state) const
00808 {
00809     vp.Scissor();
00810     v.Activate();
00811     state.Apply();
00812 }
00813 
00814 void View::ActivateScissorAndClear(const OpenGlRenderState& state ) const
00815 {
00816     vp.Scissor();
00817     v.Activate();
00818     state.Apply();
00819     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00820 }
00821 
00822 GLfloat View::GetClosestDepth(int x, int y, int radius) const
00823 {
00824     glReadBuffer(GL_FRONT);
00825     const int zl = (radius*2+1);
00826     const int zsize = zl*zl;
00827     GLfloat zs[zsize];
00828     glReadPixels(x-radius,y-radius,zl,zl,GL_DEPTH_COMPONENT,GL_FLOAT,zs);
00829     const GLfloat mindepth = *(std::min_element(zs,zs+zsize));
00830     return mindepth;
00831 }
00832 
00833 void View::GetObjectCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, double& x, double& y, double& z) const
00834 {
00835     const GLint viewport[4] = {v.l,v.b,v.w,v.h};
00836     const OpenGlMatrix proj = cam_state.GetProjectionMatrix();
00837     const OpenGlMatrix mv = cam_state.GetModelViewMatrix();
00838     gluUnProject(winx, winy, winzdepth, mv.m, proj.m, viewport, &x, &y, &z);
00839 }
00840 
00841 void View::GetCamCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, double& x, double& y, double& z) const
00842 {
00843     const GLint viewport[4] = {v.l,v.b,v.w,v.h};
00844     const OpenGlMatrix proj = cam_state.GetProjectionMatrix();
00845     gluUnProject(winx, winy, winzdepth, Identity4d, proj.m, viewport, &x, &y, &z);
00846 }
00847 
00848 View& View::SetFocus()
00849 {
00850     context->activeDisplay = this;
00851     return *this;
00852 }
00853 
00854 View& View::SetBounds(Attach bottom, Attach top,  Attach left, Attach right, bool keep_aspect)
00855 {
00856     SetBounds(top,bottom,left,right,0.0);
00857     aspect = keep_aspect ? v.aspect() : 0;
00858     return *this;
00859 }
00860 
00861 View& View::SetBounds(Attach bottom, Attach top,  Attach left, Attach right, double aspect)
00862 {
00863     this->left = left;
00864     this->top = top;
00865     this->right = right;
00866     this->bottom = bottom;
00867     this->aspect = aspect;
00868     this->Resize(context->base.v);
00869     return *this;
00870 }
00871 
00872 View& View::SetAspect(double aspect)
00873 {
00874     this->aspect = aspect;
00875     this->Resize(context->base.v);
00876     return *this;
00877 }
00878 
00879 View& View::SetLock(Lock horizontal, Lock vertical )
00880 {
00881     vlock = vertical;
00882     hlock = horizontal;
00883     return *this;
00884 }
00885 
00886 View& View::SetLayout(Layout l)
00887 {
00888     layout = l;
00889     return *this;
00890 }
00891 
00892 
00893 View& View::AddDisplay(View& child)
00894 {
00895     // detach child from any other view, and add to this
00896     vector<View*>::iterator f = std::find(
00897                                     context->base.views.begin(), context->base.views.end(), &child
00898                                 );
00899 
00900     if( f != context->base.views.end() )
00901         context->base.views.erase(f);
00902 
00903     views.push_back(&child);
00904     return *this;
00905 }
00906 
00907 View& View::operator[](int i)
00908 {
00909     return *views[i];
00910 }
00911 
00912 View& View::SetHandler(Handler* h)
00913 {
00914     handler = h;
00915     return *this;
00916 }
00917 
00918 View& View::SetDrawFunction(const boost::function<void(View&)>& drawFunc)
00919 {
00920     extern_draw_function = drawFunc;
00921     return *this;
00922 }
00923 
00924 View* FindChild(View& parent, int x, int y)
00925 {
00926     // Find in reverse order to mirror draw order
00927     for( vector<View*>::const_reverse_iterator i = parent.views.rbegin(); i != parent.views.rend(); ++i )
00928         if( (*i)->show && (*i)->vp.Contains(x,y) )
00929             return (*i);
00930     return 0;
00931 }
00932 
00933 void Handler::Keyboard(View& d, unsigned char key, int x, int y, bool pressed)
00934 {
00935     View* child = FindChild(d,x,y);
00936     if( child)
00937     {
00938         context->activeDisplay = child;
00939         if( child->handler)
00940             child->handler->Keyboard(*child,key,x,y,pressed);
00941     }
00942 }
00943 
00944 void Handler::Mouse(View& d, MouseButton button, int x, int y, bool pressed, int button_state)
00945 {
00946     View* child = FindChild(d,x,y);
00947     if( child )
00948     {
00949         context->activeDisplay = child;
00950         if( child->handler)
00951             child->handler->Mouse(*child,button,x,y,pressed,button_state);
00952     }
00953 }
00954 
00955 void Handler::MouseMotion(View& d, int x, int y, int button_state)
00956 {
00957     View* child = FindChild(d,x,y);
00958     if( child )
00959     {
00960         context->activeDisplay = child;
00961         if( child->handler)
00962             child->handler->MouseMotion(*child,x,y,button_state);
00963     }
00964 }
00965 
00966 void HandlerScroll::Mouse(View& d, MouseButton button, int x, int y, bool pressed, int button_state)
00967 {
00968     if( button == button_state && (button == MouseWheelUp || button == MouseWheelDown) )
00969     {
00970         if( button == MouseWheelUp) d.scroll_offset   -= 1;
00971         if( button == MouseWheelDown) d.scroll_offset += 1;
00972         d.scroll_offset = max(0, min(d.scroll_offset, (int)d.views.size()) );
00973         d.ResizeChildren();
00974     }
00975     else
00976     {
00977         Handler::Mouse(d,button,x,y,pressed,button_state);
00978     }
00979 
00980 }
00981 
00982 void Handler3D::Keyboard(View&, unsigned char key, int x, int y, bool pressed)
00983 {
00984     // TODO: hooks for reset / changing mode (perspective / ortho etc)
00985 }
00986 
00987 void Handler3D::Mouse(View& display, MouseButton button, int x, int y, bool pressed, int button_state)
00988 {
00989     // mouse down
00990     last_pos[0] = x;
00991     last_pos[1] = y;
00992 
00993     double T_nc[3*4];
00994     LieSetIdentity(T_nc);
00995 
00996     if( pressed && cam_state->stacks.find(GlProjectionStack) != cam_state->stacks.end() )
00997     {
00998         const GLfloat mindepth = display.GetClosestDepth(x,y,hwin);
00999         last_z = mindepth != 1 ? mindepth : last_z;
01000 
01001         if( last_z != 1 )
01002         {
01003             display.GetCamCoordinates(*cam_state, x, y, last_z, rot_center[0], rot_center[1], rot_center[2]);
01004         }
01005         else
01006         {
01007             SetZero<3,1>(rot_center);
01008         }
01009 
01010         if( button == MouseWheelUp || button == MouseWheelDown)
01011         {
01012 
01013             LieSetIdentity(T_nc);
01014             const double t[] = { 0,0,(button==MouseWheelUp?1:-1)*100*tf};
01015             LieSetTranslation<>(T_nc,t);
01016             if( !(button_state & MouseButtonRight) && !(rot_center[0]==0 && rot_center[1]==0 && rot_center[2]==0) )
01017             {
01018                 LieSetTranslation<>(T_nc,rot_center);
01019                 MatMul<3,1>(T_nc+(3*3),(button==MouseWheelUp?-1.0:1.0)/5.0);
01020             }
01021             OpenGlMatrix& spec = cam_state->stacks[GlModelViewStack];
01022             LieMul4x4bySE3<>(spec.m,T_nc,spec.m);
01023         }
01024     }
01025 }
01026 
01027 // Direction vector for each AxisDirection enum
01028 const static GLdouble AxisDirectionVector[][3] =
01029 {
01030     {0,0,0},
01031     {-1,0,0}, {1,0,0},
01032     {0,-1,0}, {0,1,0},
01033     {0,0,-1}, {0,0,1}
01034 };
01035 
01036 void Handler3D::MouseMotion(View& display, int x, int y, int button_state)
01037 {
01038     const int delta[2] = {(x-last_pos[0]),(y-last_pos[1])};
01039     const float mag = delta[0]*delta[0] + delta[1]*delta[1];
01040 
01041     // TODO: convert delta to degrees based of fov
01042     // TODO: make transformation with respect to cam spec
01043 
01044     if( mag < 50*50 )
01045     {
01046         OpenGlMatrix& mv = cam_state->GetModelViewMatrix();
01047         const GLdouble* up = AxisDirectionVector[enforce_up];
01048         double T_nc[3*4];
01049         LieSetIdentity(T_nc);
01050         bool rotation_changed = false;
01051 
01052         if( button_state == MouseButtonMiddle )
01053         {
01054             // Middle Drag: in plane translate
01055             Rotation<>(T_nc,-delta[1]*0.01, -delta[0]*0.01, 0.0);
01056         }
01057         else if( button_state == MouseButtonLeft )
01058         {
01059             // Left Drag: in plane translate
01060             if( last_z != 1 )
01061             {
01062                 GLdouble np[3];
01063                 display.GetCamCoordinates(*cam_state,x,y,last_z, np[0], np[1], np[2]);
01064                 const double t[] = { np[0] - rot_center[0], np[1] - rot_center[1], 0};
01065                 LieSetTranslation<>(T_nc,t);
01066                 std::copy(np,np+3,rot_center);
01067             }
01068             else
01069             {
01070                 const double t[] = { -10*delta[0]*tf, 10*delta[1]*tf, 0};
01071                 LieSetTranslation<>(T_nc,t);
01072             }
01073         }
01074         else if( button_state == (MouseButtonLeft | MouseButtonRight) )
01075         {
01076             // Left and Right Drag: in plane rotate about object
01077 //        Rotation<>(T_nc,0.0,0.0, delta[0]*0.01);
01078 
01079             double T_2c[3*4];
01080             Rotation<>(T_2c,0.0,0.0, delta[0]*0.01);
01081             double mrotc[3];
01082             MatMul<3,1>(mrotc, rot_center, -1.0);
01083             LieApplySO3<>(T_2c+(3*3),T_2c,mrotc);
01084             double T_n2[3*4];
01085             LieSetIdentity<>(T_n2);
01086             LieSetTranslation<>(T_n2,rot_center);
01087             LieMulSE3(T_nc, T_n2, T_2c );
01088             rotation_changed = true;
01089         }
01090         else if( button_state == MouseButtonRight)
01091         {
01092             double aboutx = -0.01 * delta[1];
01093             double abouty =  0.01 * delta[0];
01094 
01095             if(enforce_up)
01096             {
01097                 // Special case if view direction is parallel to up vector
01098                 const double updotz = mv.m[2]*up[0] + mv.m[6]*up[1] + mv.m[10]*up[2];
01099                 if(updotz > 0.98) aboutx = std::min(aboutx,0.0);
01100                 if(updotz <-0.98) aboutx = std::max(aboutx,0.0);
01101                 // Module rotation around y so we don't spin too fast!
01102                 abouty *= (1-0.6*abs(updotz));
01103             }
01104 
01105             // Right Drag: object centric rotation
01106             double T_2c[3*4];
01107             Rotation<>(T_2c, aboutx, abouty, 0.0);
01108             double mrotc[3];
01109             MatMul<3,1>(mrotc, rot_center, -1.0);
01110             LieApplySO3<>(T_2c+(3*3),T_2c,mrotc);
01111             double T_n2[3*4];
01112             LieSetIdentity<>(T_n2);
01113             LieSetTranslation<>(T_n2,rot_center);
01114             LieMulSE3(T_nc, T_n2, T_2c );
01115             rotation_changed = true;
01116         }
01117 
01118         LieMul4x4bySE3<>(mv.m,T_nc,mv.m);
01119 
01120         if(enforce_up != AxisNone && rotation_changed)
01121         {
01122             EnforceUpT_cw(mv.m, up);
01123         }
01124     }
01125 
01126     last_pos[0] = x;
01127     last_pos[1] = y;
01128 }
01129 
01130 // Use OpenGl's default frame of reference
01131 OpenGlMatrixSpec ProjectionMatrix(int w, int h, double fu, double fv, double u0, double v0, double zNear, double zFar )
01132 {
01133     return ProjectionMatrixRUB_BottomLeft(w,h,fu,fv,u0,v0,zNear,zFar);
01134 }
01135 
01136 OpenGlMatrixSpec ProjectionMatrixOrthographic(double t, double b, double l, double r, double n, double f )
01137 {
01138     OpenGlMatrixSpec P;
01139     P.type = GlProjectionStack;
01140     std::fill_n(P.m,4*4,0);
01141 
01142     P.m[0] = 2/(r-l);
01143     P.m[1] = 0;
01144     P.m[2] = 0;
01145     P.m[3] = 0;
01146 
01147     P.m[4] = 0;
01148     P.m[5] = 2/(t-b);
01149     P.m[6] = 0;
01150     P.m[7] = 0;
01151 
01152     P.m[8] = 0;
01153     P.m[9] = 0;
01154     P.m[10] = -2/(f-n);
01155     P.m[11] = 0;
01156 
01157     P.m[12] = -(r+l)/(r-l);
01158     P.m[13] = -(t+b)/(t-b);
01159     P.m[14] = -(f+n)/(f-n);
01160     P.m[15] = 1;
01161 
01162     return P;
01163 }
01164 
01165 
01166 // Camera Axis:
01167 //   X - Right, Y - Up, Z - Back
01168 // Image Origin:
01169 //   Bottom Left
01170 // Caution: Principal point defined with respect to image origin (0,0) at
01171 //          top left of top-left pixel (not center, and in different frame
01172 //          of reference to projection function image)
01173 OpenGlMatrixSpec ProjectionMatrixRUB_BottomLeft(int w, int h, double fu, double fv, double u0, double v0, double zNear, double zFar )
01174 {
01175     // http://www.songho.ca/opengl/gl_projectionmatrix.html
01176     const double L = +(u0) * zNear / -fu;
01177     const double T = +(v0) * zNear / fv;
01178     const double R = -(w-u0) * zNear / -fu;
01179     const double B = -(h-v0) * zNear / fv;
01180 
01181     OpenGlMatrixSpec P;
01182     P.type = GlProjectionStack;
01183     std::fill_n(P.m,4*4,0);
01184 
01185     P.m[0*4+0] = 2 * zNear / (R-L);
01186     P.m[1*4+1] = 2 * zNear / (T-B);
01187     P.m[2*4+2] = -(zFar +zNear) / (zFar - zNear);
01188     P.m[2*4+0] = (R+L)/(R-L);
01189     P.m[2*4+1] = (T+B)/(T-B);
01190     P.m[2*4+3] = -1.0;
01191     P.m[3*4+2] =  -(2*zFar*zNear)/(zFar-zNear);
01192 
01193     return P;
01194 }
01195 
01196 // Camera Axis:
01197 //   X - Right, Y - Down, Z - Forward
01198 // Image Origin:
01199 //   Top Left
01200 // Pricipal point specified with image origin (0,0) at top left of top-left pixel (not center)
01201 OpenGlMatrixSpec ProjectionMatrixRDF_TopLeft(int w, int h, double fu, double fv, double u0, double v0, double zNear, double zFar )
01202 {
01203     // http://www.songho.ca/opengl/gl_projectionmatrix.html
01204     const double L = -(u0) * zNear / fu;
01205     const double R = +(w-u0) * zNear / fu;
01206     const double T = -(v0) * zNear / fv;
01207     const double B = +(h-v0) * zNear / fv;
01208 
01209     OpenGlMatrixSpec P;
01210     P.type = GlProjectionStack;
01211     std::fill_n(P.m,4*4,0);
01212 
01213     P.m[0*4+0] = 2 * zNear / (R-L);
01214     P.m[1*4+1] = 2 * zNear / (T-B);
01215 
01216     P.m[2*4+0] = (R+L)/(L-R);
01217     P.m[2*4+1] = (T+B)/(B-T);
01218     P.m[2*4+2] = (zFar +zNear) / (zFar - zNear);
01219     P.m[2*4+3] = 1.0;
01220 
01221     P.m[3*4+2] =  (2*zFar*zNear)/(zNear - zFar);
01222     return P;
01223 }
01224 
01225 // Camera Axis:
01226 //   X - Right, Y - Down, Z - Forward
01227 // Image Origin:
01228 //   Bottom Left
01229 // Pricipal point specified with image origin (0,0) at top left of top-left pixel (not center)
01230 OpenGlMatrixSpec ProjectionMatrixRDF_BottomLeft(int w, int h, double fu, double fv, double u0, double v0, double zNear, double zFar )
01231 {
01232     // http://www.songho.ca/opengl/gl_projectionmatrix.html
01233     const double L = -(u0) * zNear / fu;
01234     const double R = +(w-u0) * zNear / fu;
01235     const double B = -(v0) * zNear / fv;
01236     const double T = +(h-v0) * zNear / fv;
01237 
01238     OpenGlMatrixSpec P;
01239     P.type = GlProjectionStack;
01240     std::fill_n(P.m,4*4,0);
01241 
01242     P.m[0*4+0] = 2 * zNear / (R-L);
01243     P.m[1*4+1] = 2 * zNear / (T-B);
01244 
01245     P.m[2*4+0] = (R+L)/(L-R);
01246     P.m[2*4+1] = (T+B)/(B-T);
01247     P.m[2*4+2] = (zFar +zNear) / (zFar - zNear);
01248     P.m[2*4+3] = 1.0;
01249 
01250     P.m[3*4+2] =  (2*zFar*zNear)/(zNear - zFar);
01251     return P;
01252 }
01253 
01254 OpenGlMatrix ModelViewLookAt(double ex, double ey, double ez, double lx, double ly, double lz, double ux, double uy, double uz)
01255 {
01256     OpenGlMatrix mat;
01257     GLdouble* m = mat.m;
01258 
01259     const double u_o[3] = {ux,uy,uz};
01260 
01261     GLdouble x[3], y[3];
01262     GLdouble z[] = {ex - lx, ey - ly, ez - lz};
01263     Normalise<3>(z);
01264 
01265     CrossProduct(x,u_o,z);
01266     CrossProduct(y,z,x);
01267 
01268     Normalise<3>(x);
01269     Normalise<3>(y);
01270 
01271 #define M(row,col)  m[col*4+row]
01272     M(0,0) = x[0];
01273     M(0,1) = x[1];
01274     M(0,2) = x[2];
01275     M(1,0) = y[0];
01276     M(1,1) = y[1];
01277     M(1,2) = y[2];
01278     M(2,0) = z[0];
01279     M(2,1) = z[1];
01280     M(2,2) = z[2];
01281     M(3,0) = 0.0;
01282     M(3,1) = 0.0;
01283     M(3,2) = 0.0;
01284     M(0,3) = -(M(0,0)*ex + M(0,1)*ey + M(0,2)*ez);
01285     M(1,3) = -(M(1,0)*ex + M(1,1)*ey + M(1,2)*ez);
01286     M(2,3) = -(M(2,0)*ex + M(2,1)*ey + M(2,2)*ez);
01287     M(3,3) = 1.0;
01288 #undef M
01289 
01290     return mat;
01291 }
01292 
01293 OpenGlMatrix ModelViewLookAt(double ex, double ey, double ez, double lx, double ly, double lz, AxisDirection up)
01294 {
01295     const double* u = AxisDirectionVector[up];
01296     return ModelViewLookAt(ex,ey,ez,lx,ly,lz,u[0],u[1],u[2]);
01297 }
01298 
01299 OpenGlMatrix IdentityMatrix()
01300 {
01301     OpenGlMatrix P;
01302     std::fill_n(P.m,4*4,0);
01303     for( int i=0; i<4; ++i ) P.m[i*4+i] = 1;
01304     return P;
01305 }
01306 
01307 OpenGlMatrixSpec IdentityMatrix(OpenGlStack type)
01308 {
01309     OpenGlMatrixSpec P;
01310     P.type = type;
01311     std::fill_n(P.m,4*4,0);
01312     for( int i=0; i<4; ++i ) P.m[i*4+i] = 1;
01313     return P;
01314 }
01315 
01316 OpenGlMatrixSpec  negIdentityMatrix(OpenGlStack type)
01317 {
01318     OpenGlMatrixSpec P;
01319     P.type = type;
01320     std::fill_n(P.m,4*4,0);
01321     for( int i=0; i<4; ++i ) P.m[i*4+i] = -1;
01322 
01323     P.m[3*4+3] =1;
01324     return P;
01325 }
01326 
01327 void DrawTextureToViewport(GLuint texid)
01328 {
01329     OpenGlRenderState::ApplyIdentity();
01330     glBindTexture(GL_TEXTURE_2D, texid);
01331     glEnable(GL_TEXTURE_2D);
01332     glBegin(GL_QUADS);
01333     glTexCoord2f(0, 0);
01334     glVertex2d(-1,-1);
01335     glTexCoord2f(1, 0);
01336     glVertex2d(1,-1);
01337     glTexCoord2f(1, 1);
01338     glVertex2d(1,1);
01339     glTexCoord2f(0, 1);
01340     glVertex2d(-1,1);
01341     glEnd();
01342     glDisable(GL_TEXTURE_2D);
01343 }
01344 
01345 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines


pangolin_wrapper
Author(s): Todor Stoyanov
autogenerated on Wed Feb 13 2013 14:03:25