1 // This is brl/bbas/bgui3d/bgui3d_examiner_tableau.cxx
2 #include <iostream>
3 #include <algorithm>
4 #include "bgui3d_examiner_tableau.h"
5 //:
6 // \file
7 
8 #include <cassert>
9 #ifdef _MSC_VER
10 #  include "vcl_msvc_warnings.h"
11 #endif
12 
13 #include "vgui/vgui_menu.h"
14 #include "vgui/vgui_command.h"
15 #include "vgui/vgui_gl.h"
16 #include "vgui/vgui_utils.h"
17 
18 #include <Inventor/actions/SoSearchAction.h>
19 #include <Inventor/projectors/SbSphereSheetProjector.h>
20 #include <Inventor/actions/SoRayPickAction.h>
21 #include <Inventor/SoPickedPoint.h>
22 
23 #include <Inventor/SbLinear.h>
24 #include <Inventor/sensors/SoTimerSensor.h>
25 #include <Inventor/misc/SoChildList.h>
26 #include <Inventor/actions/SoGetBoundingBoxAction.h>
27 #include <Inventor/nodes/SoPerspectiveCamera.h>
28 
29 
30 static void seeksensorCB(void * data, SoSensor * s);
31 
32 // Bitmap representations of an "X", a "Y" and a "Z" for the axis cross.
33 static GLubyte xbmp[] = { 0x11,0x11,0x0a,0x04,0x0a,0x11,0x11 };
34 static GLubyte ybmp[] = { 0x04,0x04,0x04,0x04,0x0a,0x11,0x11 };
35 static GLubyte zbmp[] = { 0x1f,0x10,0x08,0x04,0x02,0x01,0x1f };
36 
37 int count = 0;
38 SbVec3f d;
39 
40 //: Constructor
bgui3d_examiner_tableau(SoNode * scene_root)41 bgui3d_examiner_tableau::bgui3d_examiner_tableau(SoNode * scene_root)
42  : bgui3d_fullviewer_tableau(scene_root),
43    last_down_button_(vgui_BUTTON_NULL), last_timestamp_(0), seek_distance_(SEEK_HALF),
44    scale_(1.0f),
45    axis_visible_(true)
46 {
47   interaction_type_ = CAMERA;
48   spin_projector_ = new SbSphereSheetProjector(SbSphere(SbVec3f(0, 0, 0), 0.8f));
49   SbViewVolume volume;
50   volume.ortho(-1, 1, -1, 1, -1, 1);
51   spin_projector_->setViewVolume( volume );
52   seekSensor_ = new SoTimerSensor( seeksensorCB, this );
53   find_scale();
54 }
55 
56 
57 //: Destructor
~bgui3d_examiner_tableau()58 bgui3d_examiner_tableau::~bgui3d_examiner_tableau()
59 {
60   // Ming: no need to delete the SbSphereSheetProjector object
61   // delete spin_projector_;
62 }
63 
64 
type_name() const65 std::string bgui3d_examiner_tableau::type_name() const {return "bgui3d_examiner_tableau";}
66 
67 //: Handle the events coming in
68 // - Left mouse = spin
69 // - Middle mouse = pan
70 // - Middle mouse with control = zoom (dolly)
71 // - 's' and then click = seeks 2/3 of the distance to the object
72 // - 'i' toggles idle events
73 // - 'h' puts camera to the current view
74 // - 'h' with control sets the home view to the current view
75 // - 'v' views the entire scene
76 // - 'n' go to the next camera
77 // - 'p' go to the previous camera
78 // - The cursors pan
79 // - The cursors with 'control' spin
80 // - The up and down cursors with shift zoom (dolly)
81 
handle(const vgui_event & e)82 bool bgui3d_examiner_tableau::handle(const vgui_event& e)
83 {
84   // to deal with multiple tableaux in a grid
85   if ( e.type == vgui_LEAVE )
86     reset_log();
87   if ( e.type == vgui_ENTER )
88     post_idle_request();
89 
90   // ALWAYS DO KEYPRESSES
91   if ( e.type == vgui_KEY_PRESS )
92   {
93     SbVec2f middle(0.5f, 0.5f);
94     SbVec2f left(0.4f, 0.5f);
95     SbVec2f right(0.6f, 0.5f);
96     SbVec2f up(0.5f, 0.6f);
97     SbVec2f down(0.5f, 0.4f);
98     switch (e.key)
99     {
100      case 'v':
101       find_scale();
102       view_all();
103       break;
104 
105      case 'h':
106       if (e.modifier == vgui_CTRL)
107         save_home_position();
108       else
109         reset_to_home_position();
110       break;
111 
112      case 'n': {
113       int next = camera_group_->whichChild.getValue()+1;
114       if (next < camera_group_->getChildren()->getLength()) {
115         camera_group_->whichChild.setValue(next);
116         set_camera((SoCamera*)(*camera_group_->getChildren())[next]);
117       }
118       break; }
119 
120      case 'p': {
121       int prev = camera_group_->whichChild.getValue()-1;
122       if (prev>=0) {
123         camera_group_->whichChild.setValue(prev);
124         set_camera((SoCamera*)(*camera_group_->getChildren())[prev]);
125       }
126       break; }
127 
128      case vgui_CURSOR_LEFT:
129       if (e.modifier == vgui_CTRL)
130         spin(left, middle);
131       else if (e.modifier == vgui_MODIFIER_NULL) // rotate left
132       {
133         SbRotation camrot = scene_camera_->orientation.getValue();
134         SbVec3f upvec(0, 1, 0); // init to default up vector
135         camrot.multVec(upvec, upvec);
136         SbRotation rotation(upvec, 0.025f );
137         scene_camera_->orientation = camrot*rotation;
138       }
139       break;
140 
141      case vgui_CURSOR_RIGHT:
142       if (e.modifier == vgui_CTRL)
143         spin(right, middle);
144       else if (e.modifier == vgui_MODIFIER_NULL) // rotate right
145       {
146         SbRotation camrot = scene_camera_->orientation.getValue();
147         SbVec3f upvec(0, 1, 0); // init to default up vector
148         camrot.multVec(upvec, upvec);
149         SbRotation rotation(upvec, -0.025f );
150         scene_camera_->orientation = camrot*rotation;
151       }
152       break;
153 
154      case vgui_CURSOR_UP:
155       if (e.modifier == vgui_CTRL)
156         spin(up, middle);
157       else if (e.modifier == vgui_MODIFIER_NULL) // move forward
158       {
159         SbRotation camrot = scene_camera_->orientation.getValue();
160         SbVec3f lookat(0, 0, -1); // init to default view direction vector
161         camrot.multVec(lookat, lookat);
162         lookat *= scale_*0.025f;
163         SbVec3f pos = scene_camera_->position.getValue();
164         pos += lookat;
165         scene_camera_->position = pos;
166         float foc = scene_camera_->focalDistance.getValue();
167         foc -= lookat.length();
168         scene_camera_->focalDistance = foc;
169       }
170       else if (e.modifier == vgui_SHIFT)
171         zoom(0.025f*scale_);
172       break;
173 
174      case vgui_CURSOR_DOWN:
175       if (e.modifier == vgui_CTRL)
176         spin(down, middle);
177       else if (e.modifier == vgui_MODIFIER_NULL) // move backward
178       {
179         SbRotation camrot = scene_camera_->orientation.getValue();
180         SbVec3f lookat(0, 0, -1); // init to default view direction vector
181         camrot.multVec(lookat, lookat);
182         SbVec3f pos = scene_camera_->position.getValue();
183         pos -= lookat;
184         scene_camera_->position = pos;
185         float foc = scene_camera_->focalDistance.getValue();
186         foc += lookat.length();
187         scene_camera_->focalDistance = foc;
188       }
189       else if (e.modifier == vgui_SHIFT)
190         zoom(-0.025f*scale_);
191       break;
192      default:
193       break;
194     }
195   }
196 
197   // ONLY IF CAMERA INTERACTION MODE
198   if ( interaction_type_ == CAMERA )
199   {
200     const SbVec2s viewport_size(get_viewport_region().getViewportSizePixels());
201     const SbVec2s viewport_origin(get_viewport_region().getViewportOriginPixels());
202     const SbVec2s curr_pos = SbVec2s(e.wx, e.wy) - viewport_origin;
203     float aspect_ratio = get_viewport_region().getViewportAspectRatio();
204 
205     if ( e.type == vgui_KEY_PRESS && e.key == 's' )
206     {
207       last_process_ = SEEK;
208     }
209     // SEEK
210     else if ( last_process_ == SEEK && e.type == vgui_MOUSE_DOWN )
211     {
212       reset_log();
213       seek_to_point(curr_pos);
214       last_process_ = IDLE;
215     }
216     // MOUSE DOWN
217     else if ( e.type == vgui_MOUSE_DOWN )
218     {
219       reset_log();
220       last_down_button_ = e.button;
221       if ( e.modifier == vgui_CTRL)
222       {
223         interaction_type_ = SCENEGRAPH;
224         bool b = bgui3d_fullviewer_tableau::handle(e);
225         interaction_type_ = CAMERA;
226         return b;
227       }
228     }
229 
230     // MOUSE UP
231     else if ( e.type == vgui_MOUSE_UP )
232     {
233       last_down_button_ = vgui_BUTTON_NULL;
234       last_process_ = IDLE;
235       if ( e.timestamp - last_timestamp_ > 100 )
236         reset_log();
237 
238       if ( e.modifier == vgui_CTRL)
239       {
240         interaction_type_ = SCENEGRAPH;
241         bool b = bgui3d_fullviewer_tableau::handle(e);
242         interaction_type_ = CAMERA;
243         return b;
244       }
245     }
246 
247     // MOUSE MOTION
248     else if ( e.type == vgui_MOUSE_MOTION )
249     {
250       if ( e.modifier == vgui_CTRL && last_down_button_ == vgui_LEFT)
251       {
252         interaction_type_ = SCENEGRAPH;
253         //bool b =
254         bgui3d_fullviewer_tableau::handle(e);
255         interaction_type_ = CAMERA;
256         //return b;
257       }
258       const SbVec2f last_pos_norm = last_pos_;
259       const SbVec2s curr_pos = SbVec2s(e.wx, e.wy) - viewport_origin;
260       const SbVec2f curr_pos_norm((float) curr_pos[0] / (float) std::max((int)(viewport_size[0] - 1), 1),
261                                   (float) curr_pos[1] / (float) std::max((int)(viewport_size[1] - 1), 1));
262 
263       last_pos_ = curr_pos_norm;
264 
265       // MOUSE DOWN HANDLING
266       switch (last_down_button_)
267       {
268        case vgui_LEFT:
269         if ( e.modifier != vgui_CTRL )
270         {
271           spin(curr_pos_norm, last_pos_norm);
272           update_log( curr_pos_norm );
273 
274           last_process_ = DRAG;
275         }
276         break;
277 
278        case vgui_MIDDLE:
279         if (e.modifier == vgui_CTRL)
280         {
281           zoom( (curr_pos_norm[1] - last_pos_norm[1]) * scale_);
282           last_process_ = ZOOM;
283         }
284         else
285         {
286           pan(curr_pos_norm, last_pos_norm, aspect_ratio);
287           last_process_ = PAN;
288         }
289         break;
290 
291        default:
292         break;
293       }
294 
295       last_timestamp_ = e.timestamp;
296     }
297     if ( idle_enabled_ )
298     {
299       idle();
300     }
301   }
302   set_clipping_planes();
303   return bgui3d_fullviewer_tableau::handle(e);
304 }
305 
306 
307 //: Determine the scale of the scene
find_scale()308 void bgui3d_examiner_tableau::find_scale()
309 {
310   SoGetBoundingBoxAction action( get_viewport_region() );
311   action.apply( scene_root_ );
312   SbBox3f box = action.getBoundingBox();
313 
314   float dx, dy, dz;
315   box.getSize(dx,dy,dz);
316 
317   scale_ = dx;
318   if (dy > scale_) scale_ = dy;
319   if (dz > scale_) scale_ = dz;
320 }
321 
322 
323 //: When idle, spin the scene based on the log
idle()324 bool bgui3d_examiner_tableau::idle()
325 {
326   if ( idle_enabled_ && last_process_ == IDLE && log_.size >2  )
327   {
328     SbVec2f p = log_.pos3 + (log_.pos1 - log_.pos3)/5.0;
329     spin(p, log_.pos3);
330     request_render();
331   }
332   return bgui3d_fullviewer_tableau::idle();
333 }
334 
335 //----------------------------------------------------------------------------
336 //: A vgui command used to toggle axis visibility
337 class bgui3d_axis_visible_command : public vgui_command
338 {
339  public:
bgui3d_axis_visible_command(bgui3d_examiner_tableau * tab)340   bgui3d_axis_visible_command(bgui3d_examiner_tableau* tab)
341    : bgui3d_examiner_tab(tab) {}
execute()342   void execute()
343   {
344     bool visible = bgui3d_examiner_tab->axis_visible();
345     bgui3d_examiner_tab->set_axis_visible(!visible);
346   }
347 
348   bgui3d_examiner_tableau *bgui3d_examiner_tab;
349 };
350 
351 
352 //----------------------------------------------------------------------------
353 //: A vgui command used to toggle interaction type
354 class bgui3d_seek_distance_command : public vgui_command
355 {
356  public:
bgui3d_seek_distance_command(bgui3d_examiner_tableau * tab,bgui3d_examiner_tableau::SeekDistance seek)357   bgui3d_seek_distance_command(bgui3d_examiner_tableau* tab,
358                                bgui3d_examiner_tableau::SeekDistance seek)
359     : bgui3d_examiner_tab(tab), seek_distance(seek) {}
execute()360   void execute()
361   {
362     bgui3d_examiner_tab->set_seek_distance(seek_distance);
363   }
364 
365   bgui3d_examiner_tableau *bgui3d_examiner_tab;
366   bgui3d_examiner_tableau::SeekDistance seek_distance;
367 };
368 
369 //: Builds a popup menu
get_popup(const vgui_popup_params & params,vgui_menu & menu)370 void bgui3d_examiner_tableau::get_popup(const vgui_popup_params& params,
371                                         vgui_menu &menu)
372 {
373   bgui3d_fullviewer_tableau::get_popup(params, menu);
374 
375   std::string axis_item;
376   if ( this->axis_visible() )
377     axis_item = "Disable Axis";
378   else
379     axis_item = "Enable Axis";
380 
381   menu.add(axis_item, new bgui3d_axis_visible_command(this));
382 
383   std::string check_on = "[x]";
384   std::string check_off = "[ ]";
385 
386   vgui_menu seek_menu;
387   SeekDistance seek = seek_distance_;
388 
389   seek_menu.add(((seek==SEEK_FAR)?check_on:check_off) + " 3/4 ",
390                 new bgui3d_seek_distance_command(this,SEEK_FAR));
391   seek_menu.add(((seek==SEEK_HALF)?check_on:check_off) + " 1/2",
392                 new bgui3d_seek_distance_command(this,SEEK_HALF));
393   seek_menu.add(((seek==SEEK_NEAR)?check_on:check_off) + " 1/4",
394                 new bgui3d_seek_distance_command(this,SEEK_NEAR));
395   seek_menu.add(((seek==SEEK_ZERO)?check_on:check_off) + " 0 (focus)",
396                 new bgui3d_seek_distance_command(this,SEEK_ZERO));
397 
398   menu.add( "Seek Distance", seek_menu );
399 }
400 
401 //: Pan the camera
pan(const SbVec2f & currpos,const SbVec2f & prevpos,const float aspect_ratio)402 void bgui3d_examiner_tableau::pan(const SbVec2f& currpos, const SbVec2f &prevpos, const float aspect_ratio)
403 {
404   if (scene_camera_ == NULL) return; // can happen for empty scenegraph
405 
406   if (currpos == prevpos) return; // useless invocation
407 
408   SbViewVolume vv = scene_camera_->getViewVolume(aspect_ratio);
409   SbPlane panningplane = vv.getPlane(scene_camera_->focalDistance.getValue());
410 
411   vv = scene_camera_->getViewVolume(aspect_ratio);
412   SbLine line;
413   vv.projectPointToLine(currpos, line);
414   SbVec3f current_planept;
415   panningplane.intersect(line, current_planept);
416   vv.projectPointToLine(prevpos, line);
417   SbVec3f old_planept;
418   panningplane.intersect(line, old_planept);
419 
420   scene_camera_->position = scene_camera_->position.getValue() - (current_planept - old_planept);
421 }
422 
423 
424 //: Zoom (actually dolly) the camera
zoom(float diffvalue)425 void bgui3d_examiner_tableau::zoom(float diffvalue)
426 {
427   if (scene_camera_ == NULL) return; // can happen for empty scenegraph
428 
429   SbVec3f direction;
430   scene_camera_->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
431   scene_camera_->position = diffvalue*direction + scene_camera_->position.getValue();
432   scene_camera_->focalDistance = -diffvalue*direction.length() + scene_camera_->focalDistance.getValue();
433 }
434 
435 //: Spin the scene based on the current mouse position and the last mouse position
spin(const SbVec2f & currpos,const SbVec2f & prevpos)436 void bgui3d_examiner_tableau::spin(const SbVec2f& currpos, const SbVec2f &prevpos)
437 {
438   spin_projector_->project(prevpos);
439   SbRotation r;
440   spin_projector_->projectAndGetRotation(currpos, r);
441   r.invert();
442   reorient_camera(r);
443 }
444 
445 //: Reorient the camera based on specified rotation
reorient_camera(const SbRotation & rot)446 void bgui3d_examiner_tableau::reorient_camera(const SbRotation & rot)
447 {
448   SoCamera * cam = scene_camera_;
449   if (cam == NULL) return;
450 
451   // Find global coordinates of focal point.
452   SbVec3f direction;
453   cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
454   SbVec3f focalpoint = cam->position.getValue() +
455                   cam->focalDistance.getValue() * direction;
456 
457   // Set new orientation value by accumulating the new rotation.
458   cam->orientation = rot * cam->orientation.getValue();
459   SbVec3f lookat(0, 0, -1); // init to default view direction vector
460 
461   cam->orientation.getValue().multVec(lookat, lookat);
462 
463   // Reposition camera so we are still pointing at the same old focal point.
464   cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
465   SbVec3f distance = cam->focalDistance.getValue() * direction;
466 
467   cam->position = focalpoint - distance;
468 }
469 
470 
471 //: Update the log so that it can keep track of where the mouse has been
update_log(SbVec2f pos)472 void bgui3d_examiner_tableau::update_log(SbVec2f pos)
473 {
474   log_.size++;
475   log_.pos3 = log_.pos2;
476   log_.pos2 = log_.pos1;
477   log_.pos1 = pos;
478 }
479 
480 //: Reset the log
reset_log()481 void bgui3d_examiner_tableau::reset_log()
482 {
483   log_.size = 0;
484 }
485 
486 
487 static void
seeksensorCB(void * data,SoSensor * s)488 seeksensorCB(void * data, SoSensor * s)
489 {
490   SbTime currenttime = SbTime::getTimeOfDay();
491 
492   bgui3d_examiner_tableau * thisp = (bgui3d_examiner_tableau *)data;
493   SoTimerSensor * sensor = (SoTimerSensor *)s;
494 
495   float t = float((currenttime - sensor->getBaseTime()).getValue()) / 1.0f;
496   if ((t > 1.0f) || (t + sensor->getInterval().getValue() > 1.0f)) t = 1.0f;
497   SbBool end = (t == 1.0f);
498 
499   t = float(1 - std::cos(M_PI*t)) * 0.5f;
500 
501   thisp->camera_node()->position = thisp->fromPos_ + (thisp->toPos_ - thisp->fromPos_) * t;
502   thisp->camera_node()->orientation = SbRotation::slerp( thisp->fromRot_, thisp->toRot_, t);
503   if ( end )
504     s->unschedule();
505 }
506 
507 // Seek to a specified point on the screen
seek_to_point(SbVec2s pos)508 void bgui3d_examiner_tableau::seek_to_point( SbVec2s pos )
509 {
510   if (! scene_camera_)
511     return;
512 
513   // SoRayPickAction needs the viewport to have origin (0,0)
514   SbViewportRegion v = get_viewport_region();
515   v.setViewportPixels( SbVec2s(0,0), v.getViewportSizePixels() );
516 
517   SoRayPickAction rpaction( v );
518   rpaction.setPoint( pos );
519   rpaction.setRadius( 1 );
520   rpaction.apply( user_scene_root_ );
521 
522   SoPickedPoint * picked = rpaction.getPickedPoint();
523   if (!picked)
524     return;
525   float factor;
526   switch (seek_distance_)
527   {
528    case SEEK_FAR:
529     factor = 0.75f;
530     break;
531    case SEEK_HALF:
532     factor = 0.5f;
533     break;
534    case SEEK_NEAR:
535    default:
536     factor = 0.25f;
537     break;
538    case SEEK_ZERO:
539     factor = 0.0f;
540     break;
541   }
542 
543   SbVec3f hitpoint = picked->getPoint();
544   SbVec3f cameraposition = scene_camera_->position.getValue();
545   SbVec3f diff = hitpoint - cameraposition;
546   fromPos_ = cameraposition;
547   toPos_ = cameraposition += factor*diff;
548 
549   SbRotation camrot = scene_camera_->orientation.getValue();
550   SbVec3f lookat(0, 0, -1); // init to default view direction vector
551   camrot.multVec(lookat, lookat);
552   SbRotation rot(lookat, diff);
553 
554   fromRot_ = camrot;
555   toRot_ = camrot*rot;
556 
557   scene_camera_->focalDistance = diff.length()*(1.0f-factor);
558   seekSensor_->setBaseTime( SbTime::getTimeOfDay() );
559   seekSensor_->schedule();
560 }
561 
562 
563 //: Set the visibility of the axis cross
564 void
set_axis_visible(bool state)565 bgui3d_examiner_tableau::set_axis_visible(bool state)
566 {
567   axis_visible_ = state;
568 }
569 
570 
571 //: Return true if the axis cross is visible
572 bool
axis_visible() const573 bgui3d_examiner_tableau::axis_visible() const
574 {
575   return axis_visible_;
576 }
577 
578 
579 //: Changes the distance the viewer goes when seeking
580 void
set_seek_distance(SeekDistance seek)581 bgui3d_examiner_tableau::set_seek_distance( SeekDistance seek )
582 {
583   seek_distance_ = seek;
584 }
585 
586 
587 //: Render the scene graph (called on draw events)
588 bool
render()589 bgui3d_examiner_tableau::render()
590 {
591   // call the super class method
592   bool result = bgui3d_fullviewer_tableau::render();
593   if (!result)
594     return false;
595 
596   if (axis_visible_)
597     this->draw_axis_cross();
598 
599   return true;
600 }
601 
602 
603 void
draw_axis_cross()604 bgui3d_examiner_tableau::draw_axis_cross()
605 {
606   // FIXME: convert this to a superimposition scenegraph instead of
607   // OpenGL calls. 20020603 mortene.
608 
609   // Store GL state.
610   glPushAttrib(GL_ALL_ATTRIB_BITS);
611   GLfloat depthrange[2];
612   glGetFloatv(GL_DEPTH_RANGE, depthrange);
613   GLdouble projectionmatrix[16];
614   glGetDoublev(GL_PROJECTION_MATRIX, projectionmatrix);
615 
616   glDepthFunc(GL_ALWAYS);
617   glDepthMask(GL_TRUE);
618   glDepthRange(0, 0);
619   glEnable(GL_DEPTH_TEST);
620   glDisable(GL_LIGHTING);
621   glEnable(GL_COLOR_MATERIAL);
622   glDisable(GL_BLEND); // Kills transparency.
623 
624   // Set the viewport in the OpenGL canvas. Dimensions are calculated
625   // as a percentage of the total canvas size.
626   GLint vp[4];
627   vgui_utils::get_glViewport(vp);
628   const int view_x = vp[2];
629   const int view_y = vp[3];
630   const int pixelarea = int(0.25f * std::min(view_x, view_y));
631   // lower right of canvas
632   vgui_utils::set_glViewport(vp[0]+vp[2] - pixelarea, vp[1], pixelarea, pixelarea);
633 
634   // Set up the projection matrix.
635   glMatrixMode(GL_PROJECTION);
636   glLoadIdentity();
637 
638   const float NEARVAL = 0.1f;
639   const float FARVAL = 10.0f;
640   const float dim = NEARVAL * float(std::tan(M_PI / 8.0)); // FOV is 45 (45/360 = 1/8)
641   glFrustum(-dim, dim, -dim, dim, NEARVAL, FARVAL);
642 
643   // Set up the model matrix.
644   glMatrixMode(GL_MODELVIEW);
645   glPushMatrix();
646   SbMatrix mx;
647   SoCamera * cam = this->camera_node();
648 
649   // If there is no camera (like for an empty scene, for instance),
650   // just use an identity rotation.
651   if (cam) { mx = cam->orientation.getValue(); }
652   else { mx = SbMatrix::identity(); }
653 
654   mx = mx.inverse();
655   mx[3][2] = -3.5; // Translate away from the projection point (along z axis).
656   glLoadMatrixf((float *)mx);
657 
658   // Find unit vector end points.
659   SbMatrix px;
660   glGetFloatv(GL_PROJECTION_MATRIX, (float *)px);
661   SbMatrix comb = mx.multRight(px);
662 
663   SbVec3f xpos;
664   comb.multVecMatrix(SbVec3f(1,0,0), xpos);
665   xpos[0] = (1 + xpos[0]) * view_x/2;
666   xpos[1] = (1 + xpos[1]) * view_y/2;
667   SbVec3f ypos;
668   comb.multVecMatrix(SbVec3f(0,1,0), ypos);
669   ypos[0] = (1 + ypos[0]) * view_x/2;
670   ypos[1] = (1 + ypos[1]) * view_y/2;
671   SbVec3f zpos;
672   comb.multVecMatrix(SbVec3f(0,0,1), zpos);
673   zpos[0] = (1 + zpos[0]) * view_x/2;
674   zpos[1] = (1 + zpos[1]) * view_y/2;
675 
676   // Render the cross.
677   {
678     vgui_utils::set_glLineWidth(2.0);
679 
680     enum { XAXIS, YAXIS, ZAXIS };
681     int idx[3] = { XAXIS, YAXIS, ZAXIS };
682     float val[3] = { xpos[2], ypos[2], zpos[2] };
683 
684     // Bubble sort.. :-}
685     if (val[0] < val[1]) { std::swap(val[0], val[1]); std::swap(idx[0], idx[1]); }
686     if (val[1] < val[2]) { std::swap(val[1], val[2]); std::swap(idx[1], idx[2]); }
687     if (val[0] < val[1]) { std::swap(val[0], val[1]); std::swap(idx[0], idx[1]); }
688     assert((val[0] >= val[1]) && (val[1] >= val[2])); // Just checking..
689 
690     for (int i=0; i < 3; i++) {
691       glPushMatrix();
692       if (idx[i] == XAXIS) {                     // X axis.
693         glColor3f(0.500f, 0.125f, 0.125f);
694       }
695       else if (idx[i] == YAXIS) {                // Y axis.
696         glRotatef(90, 0, 0, 1);
697         glColor3f(0.125f, 0.500f, 0.125f);
698       }
699       else {                                     // Z axis.
700         glRotatef(-90, 0, 1, 0);
701         glColor3f(0.125f, 0.125f, 0.500f);
702       }
703       this->draw_arrow();
704       glPopMatrix();
705     }
706   }
707 
708   // Render axis notation letters ("X", "Y", "Z").
709   glMatrixMode(GL_PROJECTION);
710   glLoadIdentity();
711   glOrtho(0, view_x, 0, view_y, -1, 1);
712 
713   glMatrixMode(GL_MODELVIEW);
714   glLoadIdentity();
715 
716   GLint unpack;
717   glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack);
718   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
719 
720   glColor3fv(SbVec3f(0.8f, 0.8f, 0.0f).getValue());
721 
722   glRasterPos2d(xpos[0], xpos[1]);
723   vgui_utils::draw_glBitmap(8, 7, 0, 0, 0, 0, xbmp);
724   glRasterPos2d(ypos[0], ypos[1]);
725   vgui_utils::draw_glBitmap(8, 7, 0, 0, 0, 0, ybmp);
726   glRasterPos2d(zpos[0], zpos[1]);
727   vgui_utils::draw_glBitmap(8, 7, 0, 0, 0, 0, zbmp);
728 
729   glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
730   glPopMatrix();
731 
732   // Reset original state.
733 
734   // FIXME: are these 3 lines really necessary, as we push
735   // GL_ALL_ATTRIB_BITS at the start? 20000604 mortene.
736   glDepthRange(depthrange[0], depthrange[1]);
737   glMatrixMode(GL_PROJECTION);
738   glLoadMatrixd(projectionmatrix);
739 
740   glPopAttrib();
741 }
742 
743 //: Draw an arrow for the axis representation directly through OpenGL.
744 void
draw_arrow()745 bgui3d_examiner_tableau::draw_arrow()
746 {
747   glBegin(GL_LINES);
748   glVertex3f(0.0f, 0.0f, 0.0f);
749   glVertex3f(1.0f, 0.0f, 0.0f);
750   glEnd();
751   glDisable(GL_CULL_FACE);
752   glBegin(GL_TRIANGLES);
753   glVertex3f(1.0f, 0.0f, 0.0f);
754   glVertex3f(1.0f - 1.0f / 3.0f, +0.5f / 4.0f, 0.0f);
755   glVertex3f(1.0f - 1.0f / 3.0f, -0.5f / 4.0f, 0.0f);
756   glVertex3f(1.0f, 0.0f, 0.0f);
757   glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, +0.5f / 4.0f);
758   glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, -0.5f / 4.0f);
759   glEnd();
760   glBegin(GL_QUADS);
761   glVertex3f(1.0f - 1.0f / 3.0f, +0.5f / 4.0f, 0.0f);
762   glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, +0.5f / 4.0f);
763   glVertex3f(1.0f - 1.0f / 3.0f, -0.5f / 4.0f, 0.0f);
764   glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, -0.5f / 4.0f);
765   glEnd();
766 }
767