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