KJB
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gui_viewer.h
Go to the documentation of this file.
1 /* $Id: gui_viewer.h 18278 2014-11-25 01:42:10Z ksimek $ */
2 /* {{{=========================================================================== *
3  |
4  | Copyright (c) 1994-2011 by Kobus Barnard (author)
5  |
6  | Personal and educational use of this code is granted, provided that this
7  | header is kept intact, and that the authorship is not misrepresented, that
8  | its use is acknowledged in publications, and relevant papers are cited.
9  |
10  | For other use contact the author (kobus AT cs DOT arizona DOT edu).
11  |
12  | Please note that the code in this file has not necessarily been adequately
13  | tested. Naturally, there is no guarantee of performance, support, or fitness
14  | for any particular task. Nonetheless, I am interested in hearing about
15  | problems that you encounter.
16  |
17  | Author: Kyle Simek
18  * =========================================================================== }}}*/
19 
20 // vim: tabstop=4 shiftwidth=4 foldmethod=marker
21 
22 #ifndef KJB_GR_GENERIC_VIEWER_H
23 #define KJB_GR_GENERIC_VIEWER_H
24 
25 #ifdef KJB_HAVE_OPENGL
26 #include <gui_cpp/gui_trackball.h>
27 #include <gui_cpp/gui_overlay.h>
28 #include <gui_cpp/gui_selectable.h>
30 #include <gui_cpp/gui_window.h>
31 
32 #ifdef KJB_HAVE_GLUT
33 #include <gr_cpp/gr_glut.h>
34 #endif
35 
36 #include <g_cpp/g_camera.h>
37 #include <gr_cpp/gr_opengl.h>
39 #include <gr_cpp/gr_concept.h>
40 
41 #include <gr_cpp/gr_renderable.h>
43 
44 #include <vector>
45 #include <list>
46 #include <utility>
47 #include <set>
48 #include <boost/function.hpp>
49 #include <boost/foreach.hpp>
50 #include <boost/utility/enable_if.hpp>
51 #include <boost/type_traits.hpp>
52 #include <boost/ref.hpp>
53 #include <boost/any.hpp>
54 #include <boost/concept_check.hpp>
55 #include <boost/shared_ptr.hpp>
56 #include <boost/optional.hpp>
57 #include <boost/none.hpp>
58 #include <boost/utility/in_place_factory.hpp>
59 
60 namespace kjb
61 {
62 namespace gui
63 {
64 
92 class Viewer
93 {
94 private:
95  // PRIVATE TYPES
96  typedef Viewer Self;
97  enum Stereo_mode {no_stereo, horizontal_stereo, anaglyph_stereo, anaglyph_swap_stereo};
98 
99  // overlay management
100  typedef boost::shared_ptr<Overlay> Overlay_ptr;
101  typedef std::list<boost::any> Overlay_storage_list;
102  typedef Overlay_storage_list::iterator Overlay_storage_iterator;
103  typedef std::map<Overlay*, Overlay_storage_iterator> Overlay_ownership_map;
104  typedef std::list<Overlay_ptr> Overlay_list;
105  typedef Overlay_list::iterator Overlay_iterator;
106 
107 
108  // listener management
109  typedef boost::function4<bool, int, int, int, int> Mouse_listener;
110  typedef std::list<Mouse_listener> Mouse_listeners;
111 
112  typedef boost::function2<bool, int, int> Motion_listener;
113  typedef std::list<Motion_listener> Motion_listeners;
114 
115  typedef boost::function3<bool, unsigned int, int, int> Keyboard_listener;
116  typedef std::list<Keyboard_listener> Keyboard_listeners;
117 
118  typedef boost::function3<bool, int, int, int> Special_listener;
119  typedef std::list<Special_listener> Special_listeners;
120 
121  typedef std::list<boost::shared_ptr<Selectable> > Selectables;
122 public:
123  typedef Keyboard_listeners::iterator Keyboard_listener_iterator;
124  typedef Special_listeners::iterator Special_listener_iterator;
125  typedef Motion_listeners::iterator Motion_listener_iterator;
126  typedef Mouse_listeners::iterator Mouse_listener_iterator;
127  typedef Selectables::iterator Selectable_iterator;
128 private:
129  struct Event_listener_iterators
130  {
131  Mouse_listener_iterator mouse;
132  Mouse_listener_iterator mouse_double;
133  Motion_listener_iterator motion;
134  Motion_listener_iterator passive_motion;
135  Keyboard_listener_iterator keyboard;
136  Special_listener_iterator special;
137 // Selectable_iterator selectable;
138  };
139  typedef std::map<Event_listener*, Event_listener_iterators> Event_listeners;
140 public:
141  typedef Event_listeners::iterator Event_listener_iterator;
142 private:
143  typedef std::list<boost::shared_ptr<Window> > Window_stack;
144  struct Window_iterators
145  {
146  Overlay_iterator overlay;
147  Event_listener_iterator events;
148  Window_stack::iterator window_stack;
149  };
150  typedef std::map<Window*, Window_iterators> Windows;
151 public:
152  typedef Windows::iterator Window_iterator;
153 
154 private:
155  // Callback management
156 
157  // Render callbacks
158  typedef boost::function0<void> Callback;
159  typedef std::list<Callback> Callback_list;
160  typedef std::map<const Callback*, char> Render_visibility_map;
161 
162  // Reshape callbacks
163  typedef boost::function2<void, int, int> Reshape_callback;
164  typedef std::list<Reshape_callback> Reshape_callback_list;
165 public:
166  typedef Callback_list::iterator Render_callback_iterator;
167  typedef Callback_list::const_iterator Render_callback_const_iterator;
168 
169  typedef Reshape_callback_list::iterator Reshape_callback_iterator;
170  typedef Reshape_callback_list::const_iterator Reshape_callback_const_iterator;
171 private:
172  // timer management
173 
174 
175  struct Timer_entry
176  {
177  Timer_entry(
178  boost::function0<void> f_,
179  unsigned int period_,
180  bool redraw_,
181  Self* viewer_) :
182  f(f_),
183  period(period_),
184  redraw(redraw_),
185  viewer(viewer_)
186  {}
187 
188  boost::function0<void> f;
189  unsigned int period;
190  bool redraw;
191  Self* viewer;
192  };
193 
194  // ANIMATION MANAGEMENT
195  struct Camera_tween
196  {
197  Perspective_camera src;
198  Perspective_camera dest;
199  size_t elapsed;
200  size_t duration;
201  };
202 
203  struct null_deleter
204  {
205  void operator()(void const*) const
206  { }
207  };
208 public:
209  Viewer(size_t width = 300, size_t height = 300) :
210  trackball_(),
211  click_time_(),
212  display_origin_(false),
213  render_callbacks_(),
214  reshape_callbacks_(),
215  render_visibility_(),
216  overlays_(),
217  overlay_storage_(),
218  overlay_ownership_(),
219  overlay_attachments_(),
220  event_listeners_(),
221  windows_(),
222  window_stack_(),
223  front_mouse_listeners_(),
224  back_mouse_listeners_(),
225  mouse_double_listeners_(),
226  motion_listeners_(),
227  passive_motion_listeners_(),
228  keyboard_listeners_(),
229  special_listeners_(),
230  selectables_(),
231  selectable_indices_(),
232  old_rotation_origin_(),
233  base_width_(width),
234  base_height_(height),
235  width_(width),
236  height_(height),
237  resize_ratio_(1.0),
238  bg_mode_(gradient_bg),
239  bg_color_(1.0, 1.0, 1.0, 1.0),
240  bg_gradient_bot_(128.0/255.0, 128.0/255.0, 128.0/255.0, 1.0),
241  bg_gradient_top_(0.0, 0.0, 0.0, 1.0),
242  bg_image_(),
243  camera_tween_(),
244  stereo_mode_(no_stereo),
245  stereo_eye_offset_(),
246  stereo_pixel_offset_()
247  {
248 
249  glEnable(GL_LIGHTING);
250  glEnable(GL_LIGHT0);
251  glEnable(GL_DEPTH_TEST);
252 
253  trackball_.attach(*this);
254  }
255 
256 #ifdef KJB_HAVE_GLUT
257 
260  void attach(kjb::opengl::Glut_window& wnd)
261  {
262  using namespace boost;
263  wnd.set_size(width(), height());
264 
265  wnd.set_display_callback(boost::bind(&Viewer::display, this));
266  wnd.set_reshape_callback(boost::bind(&Viewer::reshape, this, _1, _2));
267  wnd.set_keyboard_callback(boost::bind(&Self::process_keyboard_, this, _1, _2, _3));
268  wnd.set_mouse_callback(boost::bind(&Self::process_mouse_, this, _1, _2, _3, _4));
269  wnd.set_motion_callback(boost::bind(&Self::process_motion_, this, _1, _2));
270  wnd.set_passive_motion_callback(boost::bind(&Self::process_passive_motion_, this, _1, _2));
271 
272  add_timer(boost::bind(&Self::tick, this), TICK_PERIOD);
273 
274  trackball_.update_viewport();
275  }
276 #endif
277 
278  inline void set_extrinsic_matrix(const kjb::Matrix& m)
279  {
280  trackball_.set_extrinsic(m);
281  }
282 
283  void set_clipping_planes(double near, double far)
284  {
285  trackball_.set_clipping_planes(near, far);
286  }
287 
299  inline void set_camera(const kjb::Perspective_camera& cam, size_t image_height)
300  {
301  trackball_.set_camera(cam, image_height);
302  }
303 
313  inline void set_camera(const kjb::Perspective_camera& cam)
314  {
315  trackball_.set_camera(cam, height_);
316  }
317 
318  inline kjb::Perspective_camera get_camera() const
319  {
320  return trackball_.get_camera();
321  }
322 
327  inline void animate_camera(const kjb::Perspective_camera& destination_cam, size_t duration_msec)
328  {
329  camera_tween_ = Camera_tween();
330  camera_tween_->src = trackball_.get_camera();
331  camera_tween_->dest = destination_cam;
332  camera_tween_->elapsed = 0;
333  camera_tween_->duration = duration_msec;
334  }
335 
343  inline void set_camera_free_fovy(const kjb::Perspective_camera& cam)
344  {
345  trackball_.set_camera(cam);
346  }
347 
348  void enable_anaglyph_stereo(
349  double screen_distance,
350  double eye_separation,
351  double pixels_per_meter,
352  double world_scale = 1.0,
353  bool swap_left_right = false)
354  {
355 
356  // TODO: cleanup variable names, handle focal length better
357  Stereo_mode stereo_mode = (swap_left_right ? anaglyph_swap_stereo : anaglyph_stereo);
358 
359  setup_stereo(stereo_mode, screen_distance, eye_separation, pixels_per_meter, world_scale);
360  }
361 
362 
363  void enable_horizontal_stereo(
364  double screen_distance,
365  double eye_separation,
366  double pixels_per_meter,
367  double world_scale = 1.0)
368  {
369  // TODO: cleanup variable names, handle focal length better
370 #ifdef KJB_HAVE_GLUT
371  glutFullScreen();
372 #endif
373 
374  setup_stereo(horizontal_stereo, screen_distance, eye_separation, pixels_per_meter, world_scale);
375  }
376 
386  void setup_stereo(
387  Stereo_mode mode,
388  double screen_distance,
389  double eye_separation,
390  double pixels_per_meter,
391  double world_scale = 1.0)
392  {
393  stereo_mode_ = mode;
394 
395  stereo_eye_offset_ = eye_separation/2.0;
396  stereo_pixel_offset_ = pixels_per_meter * stereo_eye_offset_;
397  double stereo_focal_length = screen_distance * pixels_per_meter;
398 
399  Perspective_camera cam = trackball_.get_camera();
400  cam.set_focal_length(stereo_focal_length);
401  cam.set_near(0.25);
402  cam.set_far(250);
403  cam.set_world_scale(world_scale);
404  set_camera(cam);
405  }
406 
408  void reset_camera()
409  {
410  trackball_.reset();
411  }
412 
413  /************* BACKGROUND SETTINGS ***********/
414  void set_background_gradient()
415  {
416  bg_mode_ = gradient_bg;
417  }
418 
419  void set_background_gradient(const kjb::Vector4& bottom, const kjb::Vector4& top)
420  {
421  bg_mode_ = gradient_bg;
422  bg_gradient_bot_ = bottom;
423  bg_gradient_top_ = top;
424  }
425 
426  void set_background_solid(const kjb::Vector4& color)
427  {
428  bg_mode_ = solid_bg;
429  bg_color_ = color;
430  }
431 
439  void set_background_image(const kjb::Image& img)
440  {
441  bg_mode_ = image_bg;
442  bg_image_ = boost::in_place();
443  bg_image_->set(img);
444 
445  bg_image_->bind();
446  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
447  GL_LINEAR);
448  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
449  GL_LINEAR);
450  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
451  bg_image_->unbind();
452  }
453 
465  void set_background_image(const kjb::opengl::Texture& img)
466  {
467  bg_mode_ = image_bg;
468  bg_image_.reset(kjb::opengl::Texture(img));
469  }
470 
471  /********************************************
472  * Rotation origin
473  *
474  * Moving the rotation origin lets you,
475  * rotate around a specific center point. Since
476  * being able to temporarilly move the rotation
477  * center is a common use-case, a push/pop version
478  * is provided, but the stack has maximum depth of 1,
479  * so be careful!
480  */
481 
483  void set_rotation_origin(const Vector3& o)
484  {
485  trackball_.set_object_origin(o);
486  }
487 
489  Vector3 get_rotation_origin() const
490  {
491  return trackball_.get_object_origin();
492  }
493 
507  void push_rotation_origin(const Vector3& o)
508  {
509  // EXPERIMENTAL
510  if(old_rotation_origin_)
511  KJB_THROW(Stack_overflow);
512  old_rotation_origin_ = trackball_.get_object_origin();
513  trackball_.set_object_origin(o);
514  }
515 
523  void pop_rotation_origin()
524  {
525  // EXPERIMENTAL
526  if(!old_rotation_origin_)
527  KJB_THROW(Stack_underflow);
528  trackball_.set_object_origin(*old_rotation_origin_);
529 
530  old_rotation_origin_ = boost::none;
531  }
532 
536  void render_rotation_origin()
537  {
538  // EXPERIMENTAL
539  trackball_.render_object_origin();
540  }
541 
542  void draw()
543  {
544  glMatrixMode(GL_PROJECTION);
545  glPushMatrix();
546  glMatrixMode(GL_MODELVIEW);
547  glPushMatrix();
548 
549  glClearColor(bg_color_[0], bg_color_[1], bg_color_[2], bg_color_[3]);
550  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
551  draw_background();
552 
553  if(stereo_mode_ != no_stereo)
554  draw_stereo();
555  else
556  draw_dispatch(trackball_.get_camera());
557 
558  glMatrixMode(GL_PROJECTION);
559  glPopMatrix();
560  glMatrixMode(GL_MODELVIEW);
561  glPopMatrix();
562  }
563 
564  void draw_background()
565  {
566  switch(bg_mode_)
567  {
568  case gradient_bg:
569  draw_gradient_background();
570  break;
571  default:
572  case solid_bg:
573  break;
574  case image_bg:
575  draw_image_background();
576  break;
577  }
578  }
579 
580  void draw_gradient_background()
581  {
582 
583  glPushAttrib(GL_ENABLE_BIT);
584  glPushAttrib(GL_CURRENT_BIT);
585  glDisable(GL_DEPTH_TEST);
586 
587  glMatrixMode(GL_PROJECTION);
588  glLoadIdentity();
589  glMatrixMode(GL_MODELVIEW);
590  glLoadIdentity();
591 
592  glDisable(GL_LIGHTING);
593  glBegin(GL_QUADS);
594  glColor3ub(127, 127, 255);
595  glVertex2i(-1, -1);
596  glVertex2i( 1, -1);
597  glColor3ub(0, 0, 0);
598  glVertex2i( 1, 1);
599  glVertex2i(-1, 1);
600  glEnd();
601 
602  glPopAttrib();
603  glPopAttrib();
604  }
605 
606 
607  void draw_image_background()
608  {
609  assert(bg_image_);
610 
611  glPushAttrib(GL_ENABLE_BIT);
612  glDisable(GL_DEPTH_TEST);
613  glDisable(GL_LIGHTING);
614 
615  glMatrixMode(GL_PROJECTION);
616  glLoadIdentity();
617  glMatrixMode(GL_MODELVIEW);
618  glLoadIdentity();
619 
620  glEnable( GL_TEXTURE_2D);
621  bg_image_->bind();
622 
623  glBegin(GL_QUADS);
624  glTexCoord2f(0.0, 0.0); glVertex2i(-1, -1);
625  glTexCoord2f(1.0, 0.0); glVertex2i( 1, -1);
626  glTexCoord2f(1.0, 1.0); glVertex2i( 1, 1);
627  glTexCoord2f(0.0, 1.0); glVertex2i(-1, 1);
628  glEnd();
629 
630  bg_image_->unbind();
631 
632  glPopAttrib();
633  }
634 
635  void draw_stereo()
636  {
637  Perspective_camera center = trackball_.get_camera();
638  // ... TODO
639  Perspective_camera left = center;
640  Perspective_camera right = center;
641 
642  Vector cur_center = center.get_world_origin();
643  cur_center.resize(3);
644 
645  // These need to go eleswhere
646  //static size_t w = 1400 / 2;
647  //static size_t h = 1050;
648  //static const double W = 2.4384;
649 
650  //double delta = 0.03; //what is this?
651  //double pixel_delta = delta * w/W;
652 
653  left.set_world_origin(cur_center + Vector(stereo_eye_offset_, 0.0, 0.0));
654  left.set_principal_point(Vector(-stereo_pixel_offset_, 0));
655 
656  right.set_world_origin(cur_center + Vector(-stereo_eye_offset_, 0.0, 0.0));
657  right.set_principal_point(Vector(stereo_pixel_offset_, 0));
658 
659  glPushAttrib(GL_ENABLE_BIT);
660  glPushAttrib(GL_VIEWPORT_BIT);
661  glDisable(GL_SCISSOR_TEST);
662 
663  if(stereo_mode_ == anaglyph_stereo)
664  glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
665  else if(stereo_mode_ == anaglyph_swap_stereo)
666  glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
667  else if(stereo_mode_ == horizontal_stereo)
668  {
669  glEnable(GL_SCISSOR_TEST);
670  glScissor(0, 0, width_/2.0, height_);
671  glViewport(0, 0, width_/2.0, height_);
672  }
673  else
674  {
675  KJB_THROW(Not_implemented);
676  }
677 
678  GL_ETX();
679 
680  draw_dispatch(left);
681 
682  if(stereo_mode_ == anaglyph_stereo)
683  {
684  glClear(GL_DEPTH_BUFFER_BIT);
685  glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
686  }
687  else if(stereo_mode_ == anaglyph_swap_stereo)
688  {
689  glClear(GL_DEPTH_BUFFER_BIT);
690  glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
691  }
692  else if(stereo_mode_ == horizontal_stereo)
693  {
694  glScissor(width_/2.0, 0, width_/2.0, height_);
695  glViewport(width_/2.0, 0, width_/2.0, height_);
696  }
697  else
698  {
699  KJB_THROW(Not_implemented);
700  }
701 
702 
703  GL_ETX();
704 
705  draw_dispatch(right);
706 
707  if(stereo_mode_ == anaglyph_stereo ||
708  stereo_mode_ == anaglyph_swap_stereo)
709  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
710 
711  GL_ETX();
712 
713  glPopAttrib();
714  glPopAttrib();
715  }
716 
717  void draw_dispatch(const kjb::Perspective_camera& cam)
718  {
719  glMatrixMode(GL_PROJECTION);
720  glLoadIdentity();
721  glScalef(resize_ratio_, resize_ratio_, 1.0);
723 
724  glMatrixMode(GL_MODELVIEW);
725  glLoadIdentity();
726  cam.mult_modelview_matrix();
727 
728  if(display_origin_)
729  render_rotation_origin();
730 // trackball_.render();
731 
732 
733  size_t i = 0;
734  BOOST_REVERSE_FOREACH(Callback& render_cb, render_callbacks_)
735  {
736  if(render_visibility_[&render_cb])
737  render_cb();
738  ++i;
739  }
740 
741  BOOST_REVERSE_FOREACH(const boost::shared_ptr<Selectable> selectable, selectables_)
742  {
743  selectable->render(false);
744  }
745 
746  // this modifies modelview and projection
747  render_overlays_();
748  }
749 
750 #ifdef KJB_HAVE_GLUT
751  void display()
752  {
753  draw();
754  glutSwapBuffers();
755  }
756 #endif
757 
758  int width()
759  {
760  // possibly not best to let trackball be
761  // the "keeper of the dimensions", since
762  // the viewer seems like it should be the
763  // owner. But also not good to store
764  // the same values in 2 places. Probably need to
765  // to rethink design at some point...
766 // return trackball_.viewport_width();
767 
768  // EXPERIMENTAL: let viewer own its width
769  return width_;
770  }
771 
772  int height()
773  {
774 // return trackball_.viewport_height();
775 
776  // EXPERIMENTAL: let viewer own its height
777  return height_;
778  }
779 
780  void reshape(int width, int height)
781  {
782  glViewport(0, 0, width, height);
783 
784  width_ = width;
785  height_ = height;
786 
787  resize_ratio_ = std::min(((double) width_)/base_width_, ((double)height_) / base_height_);
788 
789  if(stereo_mode_ == horizontal_stereo)
790  {
791  // stereo_mode implies side-by-side display
792  // ensures fovy is set correctly if fixed_fovy mode is set
793  trackball_.update_viewport(0, 0, width/2.0, height);
794  }
795  else
796  {
797  trackball_.update_viewport(0, 0, width, height);
798  }
799 
800  BOOST_FOREACH(Overlay* overlay, overlay_attachments_)
801  {
802  overlay->set_size(width, height);
803  }
804 
805  BOOST_REVERSE_FOREACH(Reshape_callback& reshape_cb, reshape_callbacks_)
806  {
807  reshape_cb(width, height);
808  }
809  }
810 
814  size_t num_renderables()
815  {
816  return render_callbacks_.size();
817  }
818 
822  Render_callback_iterator add_renderable(const kjb::Renderable* renderable)
823  {
824  return add_render_callback_dispatch_(boost::bind(&kjb::Renderable::render, renderable));
825  }
826 
830  Render_callback_iterator add_renderable(const boost::shared_ptr<kjb::Renderable>& renderable)
831  {
832  return add_render_callback_dispatch_(boost::bind(&kjb::Renderable::render, renderable));
833  }
834 
838  template <class RenderableType>
839  typename boost::enable_if<boost::is_convertible<RenderableType*, kjb::Renderable*>, Render_callback_iterator>::type
840  copy_renderable(const RenderableType& renderable)
841  {
842  BOOST_CONCEPT_ASSERT((boost::Convertible<RenderableType*, kjb::Renderable*>));
843 
844  return add_render_callback_dispatch_(boost::bind(&kjb::Renderable::render, renderable));
845  }
846 
858  template <class Callback_type>
859  Render_callback_iterator add_render_callback(Callback_type callback)
860  {
861  return add_render_callback_dispatch_(callback);
862  }
863 
864  template <class Callback_type>
865  Render_callback_iterator add_render_callback(boost::reference_wrapper<Callback_type> callback)
866  {
867  return add_render_callback_dispatch_(boost::bind(callback));
868  }
869 
870  Render_callback_iterator add_render_callback(const Callback& callback)
871  {
872  return add_render_callback_dispatch_(boost::bind(callback));
873  }
874 
875  Render_callback_iterator add_render_callback_dispatch_(const Callback& cb)
876  {
877  render_callbacks_.push_front(cb);
878  render_visibility_[&render_callbacks_.front()] = true;
879  return render_callbacks_.begin();
880  }
881 
889  template <class Callback>
890  Render_callback_iterator add_render_callback(const Callback* callback)
891  {
892  // TODO this fails in clang if _callback_ is a function pointer. should dispatch this using boost::is_function
893  return add_render_callback(boost::cref(*callback));
894  }
895 
899  void remove_render_callback(Render_callback_iterator it)
900  {
901  render_visibility_.erase(&*it);
902  render_callbacks_.erase(it);
903  }
904 
905 
906  /* Add a reshpae callback function to the end of the callback queue */
907  Reshape_callback_iterator add_reshape_callback(const Reshape_callback& cb)
908  {
909  reshape_callbacks_.push_front(cb);
910  return reshape_callbacks_.begin();
911  }
912 
913  /**************************************************
914  * Overlay management.
915  *
916  * This is a bit more difficult than with renderables. since the
917  * Renderable interface consists of only a single method we can
918  * wrap it in a boost::bind and store it in a collection of
919  * boost::function0's. This also let us accept objects that are
920  * extrinsically renderable (i.e. render(obj) ), and render callbacks
921  *
922  * Overlay interface has multiple methods, so it makes sense to
923  * rely on polymorphism in this case, i.e. store a
924  * collection of pointers to Overlay base objects. However, when
925  * the user wants to copy the object into the viewer, we need to
926  * store the copied object in a list of boost::any's and then
927  * store a pointer to that storage.
928  */
929 
937  template <class Overlay2dType>
938  Overlay_iterator copy_overlay(const Overlay2dType& overlay)
939  {
940  overlay_storage_.push_front(overlay);
941 
942  Overlay_iterator item = add_overlay(&boost::any_cast<Overlay2dType&>(overlay_storage_.front()));
943 
944  // keep track of item so if it is removed later, we can free the associated memory
945  overlay_ownership_[&**item] = overlay_storage_.begin();
946  return item;
947  }
948 
952  Overlay_iterator attach_overlay(Overlay* overlay)
953  {
954  overlay->set_position(0,0);
955  overlay->set_size(width(), height());
956  Overlay_iterator it = add_overlay(overlay);
957  overlay_attachments_.insert(overlay);
958 
959  return it;
960  }
961 
970  Overlay_iterator add_overlay(Overlay* overlay)
971  {
972  // prevent deleter from being called, since we don't own
973  // this object
974  overlays_.push_front(Overlay_ptr(overlay, null_deleter()));
975  return overlays_.begin();
976  }
977 
982  Overlay_iterator add_overlay(Overlay_ptr overlay_ptr)
983  {
984  overlays_.push_front(overlay_ptr);
985  return overlays_.begin();
986  }
987 
992  Overlay_iterator add_overlay_callback(const boost::function0<void>& callback, int x = 0, int y = 0, int width = -1, int height = -1)
993  {
994  return copy_overlay(Overlay_callback_wrapper(x, y, width, height, callback));
995  }
996 
1001  void remove_overlay(Overlay_iterator it)
1002  {
1003  if(overlay_ownership_.count(&**it))
1004  overlay_storage_.erase(overlay_ownership_[&**it]);
1005  overlay_attachments_.erase(&**it);
1006  overlays_.erase(it);
1007  }
1008 
1016  Overlay_iterator raise_overlay(Overlay_iterator it)
1017  {
1018  if(it == overlays_.begin()) return it;
1019 
1020  overlays_.splice(overlays_.begin(), overlays_, it);
1021 
1022  return overlays_.begin();
1023  }
1024 
1026  // VISIBILITY MANAGEMENT
1027  //
1028  // Control the visibility of each renderable item
1030 
1032  void set_renderable_visibility(bool v)
1033  {
1034  BOOST_FOREACH(Render_visibility_map::value_type& pair, render_visibility_)
1035  {
1036  pair.second = v;
1037  }
1038  }
1039 
1041  bool get_renderable_visibility(Render_callback_const_iterator it) const
1042  {
1043  if(render_visibility_.count(&*it) == 0)
1045 
1046  return render_visibility_.at(&*it);
1047  }
1048 
1050  bool get_renderable_visibility(size_t i) const
1051  {
1052  if(i >= render_callbacks_.size())
1054 
1055  // (renderables are stored in reverse order)
1056  i = render_callbacks_.size() - i - 1;
1057  Render_callback_const_iterator it = render_callbacks_.begin();
1058  std::advance(it, i);
1059  return get_renderable_visibility(it);
1060  }
1061 
1063  void set_renderable_visibility(Render_callback_iterator it, bool v)
1064  {
1065  if(render_visibility_.count(&*it) == 0)
1067 
1068  render_visibility_[&*it] = v;
1069  }
1070 
1072  void set_renderable_visibility(size_t i, bool v)
1073  {
1074  if(i >= render_callbacks_.size())
1076 
1077  // (renderables are stored in reverse order)
1078  i = render_callbacks_.size() - i - 1;
1079 
1080  Render_callback_iterator it = render_callbacks_.begin();
1081  std::advance(it, i);
1082  set_renderable_visibility(it, v);
1083  }
1084 
1086  // WINDOWS
1088 
1089  Window_iterator add_window(Window* window)
1090  {
1091  return add_window(boost::shared_ptr<Window>(window, null_deleter()));
1092  }
1093 
1097  Window_iterator add_window(boost::shared_ptr<Window> window)
1098  {
1099  // TODO: remove window if already there
1100  Window_iterators its;
1101 
1102  // if already in stack, remove and move to top
1103  if(windows_.count(window.get()))
1104  remove_window(window.get());
1105 
1106  its.overlay = add_overlay(boost::static_pointer_cast<Overlay>(window));
1107  its.events = add_event_listener(boost::static_pointer_cast<Event_listener>(window));
1108 
1109  // add window to front of stack, old top window must yield focus
1110  window_stack_.front()->yield_focus();
1111  window_stack_.push_front(window);
1112  window->claim_focus();
1113 
1114  its.window_stack = window_stack_.begin();
1115 
1116  // add window
1117  typedef Windows::value_type Pair;
1118  std::pair<Window_iterator, bool> item = windows_.insert(Pair(window.get(), its));
1119  assert(item.second == true); // always insert, not overwrite
1120 
1121  // when close button is pressed, call viewer's remove_window() method
1122  window->set_close_callback(boost::bind(
1123  static_cast<void (Self::*)(const Window_iterator)>(&Self::remove_window),
1124  this, item.first));
1125 
1126  // when window loses focus, tell me to raise it in the render and callback stack
1127  window->set_clicked_callback(boost::bind(
1128  static_cast<void (Self::*)(const Window_iterator)>(&Self::raise_window),
1129  this, item.first));
1130 
1131  return item.first;
1132  }
1133 
1134  void remove_window(const Window_iterator window)
1135  {
1136  remove_event_listener(window->second.events);
1137  remove_overlay(window->second.overlay);
1138  window_stack_.erase(window->second.window_stack);
1139  windows_.erase(window);
1140 
1141 #ifdef TEST
1142  std::cerr << "Window removed. Number of windows: " << windows_.size() << std::endl;
1143 #endif
1144  }
1145 
1146  void remove_window(boost::shared_ptr<Window> window)
1147  {
1148  remove_window(window.get());
1149  }
1150 
1151  void remove_window(Window* window)
1152  {
1153  Window_iterator item = windows_.find(window);
1154  if(item == windows_.end())
1155  KJB_THROW_2(Illegal_argument, "window not found");
1156  remove_window(item);
1157  }
1158 
1159  void raise_window(Window_iterator window)
1160  {
1161  raise_overlay(window->second.overlay);
1162  raise_event_listener(window->second.events);
1163 
1164  // move window to top of stack. Old top yields focus,
1165  // and this window claims focus
1166  window_stack_.front()->yield_focus();
1167  window_stack_.splice(
1168  window_stack_.begin(),
1169  window_stack_,
1170  window->second.window_stack);
1171  window->second.window_stack = window_stack_.begin();
1172  window_stack_.front()->claim_focus();
1173 
1174  redisplay();
1175  }
1176 
1177  /*******************************************************************
1178  * Interaction.
1179  *
1180  * Objects can receive interaction events in two ways:
1181  * 1. Inherit from Event_listener and call viewer.attach(&obj)
1182  * 2. Add listener callbacks to the viewer:
1183  * @code
1184  * void Object::attach(Viewer& viewer)
1185  * {
1186  * viewer.add_mouse_listener(boost::bind(&Object::click, this, _1, _2, _3, _4));
1187  * viewer.add_keyboard_listener(...)
1188  * ...
1189  * }
1190  * @endcode
1191  *
1192  * There's advantages to both; in approach 1. the object needs to know
1193  * nothing about the viewer. In Approach 2, the viewer needs to know
1194  * nothing about the object.
1195  */
1196 
1202  Event_listener_iterator add_event_listener(boost::shared_ptr<Event_listener> listener)
1203  {
1204  // see if user wants to move existing events to the top
1205  if(event_listeners_.count(listener.get()))
1206  remove_event_listener(listener.get());
1207 
1208 // add_selectable(boost::static_pointer_cast<Selectable>(listener)); // this also makes it render
1209 
1210  // add each listener to the callback lists, store the iterators
1211  // so we can remove them later.
1212  Event_listener_iterators iterators;
1213  iterators.mouse = add_mouse_listener(
1214  boost::bind(
1216  listener,
1217  _1,
1218  _2,
1219  _3,
1220  _4));
1221  iterators.mouse_double = add_mouse_double_listener(
1222  boost::bind(
1224  listener,
1225  _1,
1226  _2,
1227  _3,
1228  _4));
1229  iterators.motion = add_motion_listener(
1230  boost::bind(
1232  listener,
1233  _1,
1234  _2));
1235  iterators.passive_motion = add_passive_motion_listener(
1236  boost::bind(
1238  listener,
1239  _1,
1240  _2));
1241  iterators.keyboard = add_keyboard_listener(
1242  boost::bind(
1244  listener,
1245  _1,
1246  _2,
1247  _3));
1248  iterators.special = add_special_listener(
1249  boost::bind(
1251  listener,
1252  _1,
1253  _2,
1254  _3));
1255 
1256  typedef Event_listeners::value_type Pair;
1257  std::pair<Event_listeners::iterator, bool> item = event_listeners_.insert(Pair(listener.get(), iterators));
1258  assert(item.second == true); // always insert, not overwrite
1259  return item.first;
1260  }
1261 
1267  inline Event_listener_iterator add_event_listener(Event_listener* listener)
1268  {
1269  boost::shared_ptr<Event_listener> ptr_wrapper(listener, null_deleter());
1270  return add_event_listener(ptr_wrapper);
1271  }
1272 
1278  void remove_event_listener(const Event_listener_iterator listener)
1279  {
1280  remove_mouse_listener(listener->second.mouse);
1281  remove_mouse_double_listener(listener->second.mouse_double);
1282  remove_motion_listener(listener->second.motion);
1283  remove_passive_motion_listener(listener->second.passive_motion);
1284  remove_keyboard_listener(listener->second.keyboard);
1285  remove_special_listener(listener->second.special);
1286 // remove_selectable(listener->second.selectable);
1287  event_listeners_.erase(listener);
1288  }
1289 
1294  void remove_event_listener(boost::shared_ptr<Event_listener> listener)
1295  {
1296  remove_event_listener(listener.get());
1297  }
1298 
1303  void remove_event_listener(Event_listener* listener)
1304  {
1305  Event_listeners::iterator item = event_listeners_.find(listener);
1306  if(item == event_listeners_.end())
1307  KJB_THROW_2(Illegal_argument, "listener not found");
1308  remove_event_listener(item);
1309  }
1310 
1311  void raise_event_listener(Event_listener_iterator listener)
1312  {
1313  front_mouse_listeners_.splice(
1314  front_mouse_listeners_.begin(),
1315  front_mouse_listeners_,
1316  listener->second.mouse);
1317  listener->second.mouse = front_mouse_listeners_.begin();
1318 
1319  mouse_double_listeners_.splice(
1320  mouse_double_listeners_.begin(),
1321  mouse_double_listeners_,
1322  listener->second.mouse_double);
1323  listener->second.mouse_double = mouse_double_listeners_.begin();
1324 
1325  motion_listeners_.splice(
1326  motion_listeners_.begin(),
1327  motion_listeners_,
1328  listener->second.motion);
1329  listener->second.motion = motion_listeners_.begin();
1330 
1331  passive_motion_listeners_.splice(
1332  passive_motion_listeners_.begin(),
1333  passive_motion_listeners_,
1334  listener->second.passive_motion);
1335  listener->second.passive_motion = passive_motion_listeners_.begin();
1336 
1337  keyboard_listeners_.splice(
1338  keyboard_listeners_.begin(),
1339  keyboard_listeners_,
1340  listener->second.keyboard);
1341  listener->second.keyboard = keyboard_listeners_.begin();
1342 
1343  special_listeners_.splice(
1344  special_listeners_.begin(),
1345  special_listeners_,
1346  listener->second.special);
1347  listener->second.special = special_listeners_.begin();
1348  }
1349 
1350 #ifdef KJB_HAVE_GLUT
1351  template <class Callback>
1352  void add_timer(const Callback& cb, size_t msec, bool redraw = false)
1353  {
1354  boost::function0<void> f;
1355  f = cb;
1356  timers_.push_back(Timer_entry(f, msec, redraw, this));
1357  glutTimerFunc(msec, Self::tick_timer, timers_.size()-1);
1358  }
1359 #endif
1360 
1361  void redisplay()
1362  {
1363 #ifdef KJB_HAVE_GLUT
1364  glutPostRedisplay();
1365 #endif
1366  }
1367 
1368  void key(unsigned int key, int , int )
1369  {
1370  switch(key)
1371  {
1372  case ' ':
1373  reset_camera();
1374  redisplay();
1375  break;
1376  case 'o':
1377  display_origin_ = !display_origin_;
1378  redisplay();
1379  default:
1380  break;
1381  }
1382  }
1383 
1384 
1394  Mouse_listener_iterator
1395  add_mouse_listener(
1396  const boost::function4<bool, int, int, int, int>& callback)
1397  {
1398  front_mouse_listeners_.push_front(callback);
1399  return front_mouse_listeners_.begin();
1400  }
1401 
1409  Mouse_listener_iterator
1410  add_after_mouse_listener(
1411  const boost::function4<bool, int, int, int, int>& callback)
1412  {
1413  back_mouse_listeners_.push_front(callback);
1414  return back_mouse_listeners_.begin();
1415  }
1416 
1420  Mouse_listener_iterator
1421  add_mouse_double_listener(
1422  const boost::function4<bool, int, int, int, int>& callback)
1423  {
1424  mouse_double_listeners_.push_front(callback);
1425  return mouse_double_listeners_.begin();
1426  }
1427 
1431  Motion_listener_iterator
1432  add_motion_listener(
1433  const boost::function2<bool, int, int>& callback)
1434  {
1435  motion_listeners_.push_front(callback);
1436  return motion_listeners_.begin();
1437  }
1438 
1442  Motion_listener_iterator
1443  add_passive_motion_listener(
1444  const boost::function2<bool, int, int>& callback)
1445  {
1446  passive_motion_listeners_.push_front(callback);
1447  return passive_motion_listeners_.begin();
1448  }
1449 
1453  Keyboard_listener_iterator
1454  add_keyboard_listener(
1455  const boost::function3<bool, unsigned int, int, int>& callback)
1456  {
1457  keyboard_listeners_.push_front(callback);
1458  return keyboard_listeners_.begin();
1459  }
1460 
1464  Special_listener_iterator
1465  add_special_listener(
1466  const boost::function3<bool, int, int, int>& callback)
1467  {
1468  special_listeners_.push_front(callback);
1469  return special_listeners_.begin();
1470  }
1471 
1472 
1473 // REMOVING LISTENERS
1474 
1475  void remove_mouse_listener( Mouse_listener_iterator item)
1476  {
1477  front_mouse_listeners_.erase(item);
1478  }
1479 
1480  void remove_after_mouse_listener(Mouse_listener_iterator item)
1481  {
1482  back_mouse_listeners_.erase(item);
1483  }
1484 
1488  void remove_mouse_double_listener(Mouse_listener_iterator item)
1489  {
1490  mouse_double_listeners_.erase(item);
1491  }
1492 
1496  void remove_motion_listener(Motion_listener_iterator item)
1497  {
1498  motion_listeners_.erase(item);
1499  }
1500 
1504  void remove_passive_motion_listener(Motion_listener_iterator item)
1505  {
1506  passive_motion_listeners_.erase(item);
1507  }
1508 
1512  void remove_keyboard_listener(Keyboard_listener_iterator item)
1513  {
1514  keyboard_listeners_.erase(item);
1515  }
1516 
1520  void remove_special_listener(Special_listener_iterator item)
1521  {
1522  special_listeners_.erase(item);
1523  }
1524 
1530  Selectable_iterator
1531  add_selectable(
1532  boost::shared_ptr<Selectable> listener)
1533  {
1534  selectables_.push_front(listener);
1535  selectable_indices_.push_back(selectables_.begin());
1536  return selectables_.begin();
1537  }
1538 
1544  Selectable_iterator
1545  add_selectable(
1546  Selectable* listener)
1547  {
1548  return add_selectable(boost::shared_ptr<Selectable>(listener, null_deleter()));
1549  }
1550 
1551  void remove_selectable(Selectable_iterator item)
1552  {
1553  std::remove(
1554  selectable_indices_.begin(),
1555  selectable_indices_.end(),
1556  item);
1557  selectables_.erase(item);
1558  }
1559 
1563  void tick()
1564  {
1565  tick_camera_tween_();
1566  }
1567 
1568 private:
1569  void process_mouse_(int button, int state, int x, int y)
1570  {
1571  // might eventually be able to use this with something besides glut,
1572  // but would need to replace all the glut macros with something more sensible
1573 #ifdef KJB_HAVE_GLUT
1574  if(state == GLUT_DOWN)
1575  {
1576  static const double DBLCLICK_INTERVAL = 250;
1577  int cur_time = glutGet(GLUT_ELAPSED_TIME);
1578  int elapsed = cur_time - click_time_;
1579  click_time_ = cur_time;
1580  if(elapsed < DBLCLICK_INTERVAL)
1581  return process_mouse_double_(button, state, x, y);
1582  }
1583 #endif
1584 
1585  BOOST_REVERSE_FOREACH(const Mouse_listener& callback, front_mouse_listeners_)
1586  {
1587  if(callback(button, state, x, height() - y - 1))
1588  return;
1589  }
1590 
1591  if(selectables_.size() > 0)
1592  {
1593  if(process_selection_click_(button, state, x, height() - y - 1))
1594  return;
1595  }
1596 
1597  BOOST_REVERSE_FOREACH(const Mouse_listener& callback, back_mouse_listeners_)
1598  {
1599  if(callback(button, state, x, height() - y - 1))
1600  return;
1601  }
1602  }
1603 
1604  void process_mouse_double_(int button, int state, int x, int y)
1605  {
1606  BOOST_REVERSE_FOREACH(const Mouse_listener& callback, mouse_double_listeners_)
1607  {
1608  if(callback(button, state, x, height() - y - 1))
1609  return;
1610  }
1611  }
1612 
1613  void process_motion_(int x, int y)
1614  {
1615  BOOST_REVERSE_FOREACH(const Motion_listener& callback, motion_listeners_)
1616  {
1617  if(callback(x, height() - y - 1))
1618  return;
1619  }
1620  }
1621 
1622  void process_passive_motion_(int x, int y)
1623  {
1624  BOOST_REVERSE_FOREACH(const Motion_listener& callback, passive_motion_listeners_)
1625  {
1626  if(callback(x, height() - y - 1))
1627  return;
1628  }
1629  }
1630 
1631  void process_keyboard_(unsigned int k, int x, int y)
1632  {
1633  BOOST_REVERSE_FOREACH(const Keyboard_listener& callback, keyboard_listeners_)
1634  {
1635  if(callback(k, x, height() - y - 1))
1636  return;
1637  }
1638 
1639  this->key(k, x, height() - y - 1);
1640  }
1641 
1642  // TODO: process_special_
1643 
1644 
1645  bool process_selection_click_(int button, int state, int x, int y)
1646  {
1647  static const size_t BUFSIZE = 512;
1648  GLuint selectBuf[BUFSIZE];
1649  GLint hits;
1650 
1651  glSelectBuffer (BUFSIZE, selectBuf);
1652  (void) glRenderMode (GL_SELECT);
1653 
1654  glInitNames();
1655  glPushName(0);
1656 
1657  int MARGIN = 5;
1658 
1659  glMatrixMode(GL_PROJECTION);
1660  glPushMatrix();
1661  glMatrixMode(GL_MODELVIEW);
1662  glPushMatrix();
1663 
1664  trackball_.prepare_for_picking(x, y, MARGIN, MARGIN);
1665 
1666  // render selection
1667  size_t i = 0;
1668  // objects are stored in reverse
1669  BOOST_REVERSE_FOREACH(boost::shared_ptr<Selectable> selectable, selectables_)
1670  {
1671  glLoadName(i++);
1672  glPushName(0);
1673  selectable->render(true);
1674  glPopName();
1675  }
1676 
1677  glMatrixMode(GL_PROJECTION);
1678  glPopMatrix();
1679  glMatrixMode(GL_MODELVIEW);
1680  glPopMatrix();
1681 
1682  glFlush ();
1683 
1684  hits = glRenderMode (GL_RENDER);
1685 
1686  if(hits == 0)
1687  return false;
1688 
1689  // todo: pass mouse button and state
1690  return process_selection_hits_(hits, selectBuf, button, state);
1691  }
1692 
1693  bool process_selection_hits_(GLint hits, GLuint buffer[], int button, int state)
1694  {
1695  GLuint* ptr = buffer;
1696 
1697  GLuint num_names;
1698  float near_depth;
1699  float far_depth;
1700  GLuint name;
1701 
1702  // dispatch hit info to the clicked item
1703  for(int i = 0; i < hits; i++)
1704  {
1705  num_names = *ptr++;
1706  near_depth = (float) *ptr++/0x7fffffff;
1707  far_depth = (float) *ptr++/0x7fffffff;
1708  name = *ptr++;
1709 
1710  GLuint* next = ptr + num_names-1;
1711  assert(name < selectable_indices_.size());
1712  bool processed = (*selectable_indices_[name])->selection_hit(ptr, next, button, state);
1713 
1714  if(processed)
1715  return true;
1716  ptr = next;
1717  }
1718 
1719  return false;
1720  }
1721 
1722 #ifdef KJB_HAVE_GLUT
1723 
1726  static void tick_timer(int i)
1727  {
1728  const Timer_entry& t = timers_[i];
1729  // execute callback
1730  t.f();
1731 
1732  if(t.redraw)
1733  {
1734  t.viewer->redisplay();
1735  }
1736 
1737  // restart timer
1738  glutTimerFunc(t.period, Self::tick_timer, i);
1739  }
1740 #endif
1741 
1745  void tick_camera_tween_()
1746  {
1747  // TODO: currently only animates extrinsic parameters. Eventually intrinsic parameter tweening would be nice
1748  if(camera_tween_)
1749  {
1750  camera_tween_->elapsed += TICK_PERIOD;
1751  if(camera_tween_->elapsed > camera_tween_->duration)
1752  {
1753  // done tweening
1754  set_camera(camera_tween_->dest);
1755 
1756 // set_extrinsic_matrix(camera_tween_->dest.get_modelview_matrix());
1757  camera_tween_.reset();
1758  }
1759  else
1760  {
1761  // linearly interpolate between cameras' intrinsic matrices
1762  static const bool USE_SLERP = false;
1763 
1764  set_extrinsic_matrix(
1766  camera_tween_->src.get_modelview_matrix(),
1767  camera_tween_->dest.get_modelview_matrix(),
1768  (double)(camera_tween_->elapsed) / camera_tween_->duration,
1769  USE_SLERP));
1770  }
1771  redisplay();
1772  }
1773  }
1774 
1775 
1776 
1777 private:
1781  void render_overlays_()
1782  {
1783  GL_ETX();
1784 
1785  if(overlays_.size() == 0) return;
1786 
1787  glPushAttrib(GL_ENABLE_BIT); // for GL_DEPTH_TEST
1788 
1789  glEnable(GL_BLEND);
1790  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1791 
1792  glDisable(GL_DEPTH_TEST); // we'll use the painter algorithm instead.
1793 
1794  // set up othographic projection
1795  glMatrixMode(GL_PROJECTION);
1796  glLoadIdentity();
1797 
1798  // We want glVertex2d(0.0, 0.0) to represent the bottom-left of the
1799  // viewport, wherever it is. Therefore...
1800  // DO THIS:
1801  ::glOrtho(0, width(), 0, height(), -1, 1);
1802  // NOT THIS:
1803  // glOrtho(vp_x, vp_y, vp_width, vp_height);
1804 
1805  glMatrixMode(GL_MODELVIEW);
1806  glLoadIdentity();
1807 
1808  // render overlays
1809  Overlay_list::reverse_iterator it;
1810  for(it = overlays_.rbegin(); it != overlays_.rend(); ++it)
1811  {
1812  (*it)->render();
1813  }
1814 
1815  glPopAttrib();
1816 
1817  GL_ETX();
1818  }
1819 
1820 private:
1821  // PRIVATE FIELDS
1822 
1823  Trackball trackball_;
1824 
1825  int click_time_;
1826 
1827  bool display_origin_;
1828 
1829  Callback_list render_callbacks_;
1830  Reshape_callback_list reshape_callbacks_;
1831  Render_visibility_map render_visibility_;
1832 
1833  std::list<Overlay_ptr> overlays_;
1834  Overlay_storage_list overlay_storage_;
1835  Overlay_ownership_map overlay_ownership_;
1836  std::set<Overlay*> overlay_attachments_;
1837 
1838  Event_listeners event_listeners_;
1839  Windows windows_;
1840  Window_stack window_stack_;
1841 
1842  Mouse_listeners front_mouse_listeners_;
1843  Mouse_listeners back_mouse_listeners_;
1844  Mouse_listeners mouse_double_listeners_;
1845  Motion_listeners motion_listeners_;
1846  Motion_listeners passive_motion_listeners_;
1847  Keyboard_listeners keyboard_listeners_;
1848  Special_listeners special_listeners_;
1849  Selectables selectables_;
1850  std::vector<Selectable_iterator> selectable_indices_;
1851 
1852  boost::optional<Vector3> old_rotation_origin_;
1853 
1854  size_t base_width_;
1855  size_t base_height_;
1856 
1857  size_t width_;
1858  size_t height_;
1859 
1860  float resize_ratio_;
1861 
1862  enum Bg_mode { solid_bg, gradient_bg, image_bg };
1863 
1864  Bg_mode bg_mode_;
1865  kjb::Vector4 bg_color_;
1866  kjb::Vector4 bg_gradient_bot_;
1867  kjb::Vector4 bg_gradient_top_;
1868  boost::optional<kjb::opengl::Texture> bg_image_;
1869 
1870  // ANIMATION STATE
1871  boost::optional<Camera_tween> camera_tween_;
1872 
1873  // stereo
1874  Stereo_mode stereo_mode_;
1875  double stereo_eye_offset_;
1876  double stereo_pixel_offset_;
1877 
1878  // timers
1879  static std::vector<Timer_entry> timers_;
1880  static const size_t TICK_PERIOD;
1881 
1882 };
1883 
1884 } // namespace opengl
1885 } // namespace kjb
1886 
1887 
1888 namespace kjb
1889 {
1890 namespace opengl
1891 {
1892  // for backward compatibility (temporary)
1893  using kjb::gui::Viewer;
1894 }
1895 }
1896 #endif /* KJB_HAVE_OPENGL */
1897 #endif
1898 
1899 // TODO
1900 // Move trackball functionality out of class; use dependency injection to add as-neded
virtual bool keyboard_event(unsigned int key, int x, int y)=0
virtual bool mouse_double_event(int button, int state, int x, int y)=0
void mult_projection_matrix() const
Definition: perspective_camera.h:369
Vector_d< 3 > Vector3
Definition: g_quaternion.h:37
Object thrown when an index argument exceeds the size of a container.
Definition: l_exception.h:399
virtual void render() const =0
Renders this object with GL.
Abstract class to render this object with GL.
Definition: gr_renderable.h:78
virtual void set_focal_length(double ifocal)
sets the focal length
Definition: perspective_camera.cpp:872
for k
Definition: APPgetLargeConnectedEdges.m:61
virtual bool special_event(int key, int x, int y)=0
height
Definition: APPgetLargeConnectedEdges.m:33
#define KJB_THROW(ex)
Definition: l_exception.h:46
virtual bool mouse_event(int button, int state, int x, int y)=0
Matrix lerp_extrinsic_camera_matrix(const Matrix &m1, const Matrix &m2, double t, bool use_slerp)
Linearly-interpolate a two extrinsic camera matrices.
Definition: g_camera.cpp:128
int GLuint
Definition: gr_opengl_headers.h:64
St_perspective_camera for modeling a perspective camera using the classic Forsyth and Ponce parametri...
Abstract class to render this object with GL.
Definition: perspective_camera.h:93
x
Definition: APPgetLargeConnectedEdges.m:100
virtual bool passive_motion_event(int x, int y)=0
Definition: gui_event_listener.h:33
#define KJB_THROW_2(ex, msg)
Definition: l_exception.h:48
virtual bool motion_event(int x, int y)=0
#define dest(triedge, pointptr)
Definition: triangle.c:938
Int_matrix::Value_type min(const Int_matrix &mat)
Return the minimum value in this matrix.
Definition: l_int_matrix.h:1385
void mult_modelview_matrix() const
Definition: perspective_camera.cpp:359
void glOrtho(Matrix &state, double left, double right, double bottom, double top, double znear, double zfar)
Definition: gr_opengl.cpp:475
Definition: g_quaternion.h:37
get the indices of edges in each direction for i
Definition: APPgetLargeConnectedEdges.m:48
This class implements matrices, in the linear-algebra sense, with real-valued elements.
Definition: m_matrix.h:94
for m
Definition: APPgetLargeConnectedEdges.m:64
Wrapped version of the C struct KJB_image.
Definition: i_image.h:76