1 // This is brl/bbas/bgui3d/bgui3d_viewer_tableau.cxx
2 #include <iostream>
3 #include <cmath>
4 #include "bgui3d_viewer_tableau.h"
5 //:
6 // \file
7 
8 #include <cassert>
9 #ifdef _MSC_VER
10 #  include "vcl_msvc_warnings.h"
11 #endif
12 #include "bgui3d_algo.h"
13 #include "vgui/vgui_gl.h"
14 #include "vgui/vgui_utils.h"
15 #include "vnl/vnl_quaternion.h"
16 #include "vnl/vnl_double_3x3.h"
17 #include "vnl/vnl_double_3x4.h"
18 #include "vnl/vnl_double_3.h"
19 #include "vnl/vnl_det.h"
20 
21 #include "vgl/vgl_point_2d.h"
22 #include "vgl/vgl_point_3d.h"
23 #include <vgl/algo/vgl_rotation_3d.h>
24 #include "vpgl/vpgl_perspective_camera.h"
25 
26 #include <Inventor/nodes/SoOrthographicCamera.h>
27 #include <Inventor/nodes/SoPerspectiveCamera.h>
28 
29 #include <Inventor/nodes/SoSeparator.h>
30 #include <Inventor/SoDB.h>
31 #include <Inventor/SbViewportRegion.h>
32 #include <Inventor/nodes/SoNode.h>
33 #include <Inventor/nodes/SoSwitch.h>
34 #include <Inventor/VRMLnodes/SoVRMLViewpoint.h>
35 #include <Inventor/SoSceneManager.h>
36 
37 #include <Inventor/nodes/SoDirectionalLight.h>
38 #include <Inventor/actions/SoSearchAction.h>
39 #include <Inventor/nodekits/SoBaseKit.h>
40 #include <Inventor/actions/SoGetBoundingBoxAction.h>
41 #include <Inventor/actions/SoGetMatrixAction.h>
42 #include <Inventor/nodes/SoText2.h>
43 
44 #include <Inventor/nodes/SoTranslation.h>
45 #include <Inventor/sensors/SoAlarmSensor.h>
46 
47 //: Constructor
bgui3d_viewer_tableau(SoNode * scene_root)48 bgui3d_viewer_tableau::bgui3d_viewer_tableau(SoNode * scene_root)
49  : bgui3d_tableau(NULL),
50    user_scene_root_(NULL),
51    camera_group_(NULL),
52    scene_camera_(NULL),
53    stored_camera_(NULL),
54    headlight_(NULL)
55 {
56   this->set_scene_root(scene_root);
57 }
58 
59 
60 //: Destructor
~bgui3d_viewer_tableau()61 bgui3d_viewer_tableau::~bgui3d_viewer_tableau()
62 {
63   if (scene_camera_)
64     scene_camera_->unref();
65   if (headlight_)
66     headlight_->unref();
67 }
68 
69 
70 void
set_scene_root(SoNode * scene_root)71 bgui3d_viewer_tableau::set_scene_root(SoNode* scene_root)
72 {
73   if (!scene_root)
74     bgui3d_tableau::set_scene_root(scene_root);
75 
76   user_scene_root_ = scene_root;
77 
78   // Build the super scene graph
79   SoSeparator *super_root = new SoSeparator;
80   super_root->setName("bgui3d_viewer_root");
81 
82   // Create a headlight if one does not exist
83   // By inserting this before any scenegraph camera, the
84   // light will always be pointing in the correct direction.
85   if (!headlight_) {
86     headlight_ = new SoDirectionalLight;
87     headlight_->direction.setValue(1, -1, -10);
88     headlight_->setName("headlight");
89     headlight_->ref();
90   }
91   super_root->addChild(headlight_);
92 
93   // add the text and its translation to shift it to the top right
94   SoTranslation* trans = new SoTranslation;
95   trans->translation = SbVec3f(-.98f,.93f,0);
96   super_root->addChild( trans );
97 
98   text_ = new SoText2;
99   text_->string.deleteValues(0, -1); // empty the string
100   super_root->addChild( text_ );
101 
102   //: Make a group of all the cameras in the scene
103   camera_group_ = new SoSwitch;
104   camera_group_->setName("bgui3dCameraGroup");
105   super_root->addChild(camera_group_);
106 
107   // Make the default Camera
108   SoCamera* camera = (SoCamera*)SoPerspectiveCamera::getClassTypeId().createInstance();
109   camera->setName("Examiner_Camera");
110   camera_group_->addChild(camera);
111   camera->nearDistance = 0.5f;
112   camera->farDistance = 1000.0f;
113 
114   // Set the camera to view the whole scene
115   camera->viewAll(scene_root, get_viewport_region());
116 
117   // find existing VRML viewpoints in the scene and make cameras
118   this->collect_vrml_cameras(scene_root);
119 
120   // find and used the first user scene camera (if it exists)
121   std::vector<SoCamera*> user_cams = find_cameras(user_scene_root_);
122   if (!user_cams.empty()) {
123     camera_group_->whichChild.setValue(-1);
124     this->set_camera(user_cams[0]);
125   }
126   else {
127     // if not, use the first examiner camera
128     assert(camera_group_->getChildren()->getLength() > 0);
129 
130     if (camera_group_->whichChild.getValue() < 0)
131       camera_group_->whichChild.setValue(0);
132     int cam_idx = camera_group_->whichChild.getValue();
133 
134     SoChildList* list = camera_group_->getChildren();
135     this->set_camera((SoCamera*)(*list)[cam_idx]);
136   }
137 
138   // The users scene should be added last
139   if (scene_root)
140     super_root->addChild(scene_root);
141 
142   bgui3d_tableau::set_scene_root(super_root);
143 
144   save_home_position();
145 }
146 
147 
148 //--------------Camera Methods--------------------
149 
150 //: Set the camera viewing the scene
151 // \note this does not add the camera to a scene graph
152 void
set_camera(SoCamera * camera)153 bgui3d_viewer_tableau::set_camera(SoCamera *camera)
154 {
155   if (scene_camera_) {
156     scene_camera_->unref();
157   }
158 
159   scene_camera_ = camera;
160 
161   if (scene_camera_) {
162     scene_camera_->ref();
163     SoType cam_type = scene_camera_->getTypeId();
164     if ( cam_type == SoOrthographicCamera::getClassTypeId() )
165       camera_type_ = ORTHOGONAL;
166     else
167       camera_type_ = PERSPECTIVE;
168   }
169 }
170 
171 
172 //: Set the scene camera
173 // Generate an SoCamera from a camera matrix and use it
174 // \note Only handles perspective cameras for now
175 bool
set_camera(const vpgl_proj_camera<double> & camera)176 bgui3d_viewer_tableau::set_camera(const vpgl_proj_camera<double>& camera)
177 {
178   SoPerspectiveCamera* new_cam = new SoPerspectiveCamera;
179   new_cam->ref();
180   new_cam->setName("Camera_Matrix");
181 
182   vnl_double_3x3 K, R;
183   vnl_double_3 t;
184 
185   vnl_double_3x4 cam = camera.get_matrix();
186   if (vnl_det(vnl_double_3x3(cam.extract(3,3))) < 0)
187     cam *= -1.0;
188   if ( bgui3d_decompose_camera(cam, K, R, t) ) {
189     new_cam->aspectRatio = float(K[0][2]/K[1][2]);
190     new_cam->heightAngle = float(2*std::atan2(K[1][2],K[1][1]));
191 
192     vnl_double_3 C = -R.transpose()*t;
193     new_cam->position.setValue( float(C[0]), float(C[1]), float(C[2]) );
194 
195     // the identity camera requires a 180 degree rotation about the
196     // x-axis to project to images with the origin in the upper left
197     R.scale_row(1,-1);
198     R.scale_row(2,-1);
199 
200     // create a rotation matrix
201     SbMatrix rot = SbMatrix::identity();
202 
203     for (int i=0; i<3; ++i)
204       for ( int j=0; j<3; ++j)
205         rot[j][i] = float(R[j][i]);
206 
207     new_cam->orientation.setValue(SbRotation(rot));
208 
209     new_cam->nearDistance = 1.0f;
210     new_cam->farDistance = 1000.0f;
211   }
212 
213   if ( user_scene_root_ ) {
214     camera_group_->addChild(new_cam);
215     int num_cameras = camera_group_->getChildren()->getLength();
216     this->select_camera(num_cameras -1);
217     this->set_camera(new_cam);
218 
219     this->set_clipping_planes();
220   }
221 
222   new_cam->unref();
223 
224   return true;
225 }
226 
227 
228 //: Get the scene camera.
229 // Creates a vpgl camera (either perspective or affine) from the active SoCamera
230 std::unique_ptr<vpgl_proj_camera<double> >
camera() const231 bgui3d_viewer_tableau::camera() const
232 {
233   if (!scene_camera_)
234     return std::unique_ptr<vpgl_proj_camera<double> >(nullptr);
235 
236   const SbVec3f& t_vec = scene_camera_->position.getValue();
237   vnl_double_3 t(t_vec[0], t_vec[1], t_vec[2]);
238 
239   float q1,q2,q3,q4;
240   scene_camera_->orientation.getValue().getValue(q1,q2,q3,q4);
241   // inverse and rotate 180 degrees around Z
242   vgl_rotation_3d<double> R(vnl_quaternion<double>(q2,-q1,q4,q3));
243 
244   GLint vp[4];
245   vgui_utils::get_glViewport(vp);
246   unsigned width = vp[2];
247   unsigned height = vp[3];
248 
249   switch (camera_type_)
250   {
251    case PERSPECTIVE: {
252     SoPerspectiveCamera* cam = (SoPerspectiveCamera*)scene_camera_;
253     double f = 1.0/(std::tan(cam->heightAngle.getValue()/2.0));
254     double sx = 1.0, sy = 1.0;
255     if (width < height)
256       sy = double(width)/height;
257     else
258       sx = double(height)/width;
259     vgl_point_2d<double> p(0, 0);
260     vpgl_calibration_matrix<double> K(f,p,sx,sy);
261     vgl_point_3d<double> c(t[0],t[1],t[2]);
262     return std::unique_ptr<vpgl_proj_camera<double> >
263            ( new vpgl_perspective_camera<double>(K,c,R) );
264     }
265    case ORTHOGONAL:
266 #if 0
267     SoOrthographicCamera* cam = (SoOrthographicCamera*)scene_camera_;
268     double h = cam->height.getValue();
269 #endif // 0
270     std::cerr << "WARNING: not implemented yet\n";
271     return std::unique_ptr<vpgl_proj_camera<double> >(nullptr);
272    default:
273     std::cerr << "WARNING: no such camera_type_\n";
274     return std::unique_ptr<vpgl_proj_camera<double> >(nullptr);
275   }
276 }
277 
278 
279 //: Select the active camera by index.
280 // A negative index selects the first user scene camera
281 void
select_camera(int camera_index)282 bgui3d_viewer_tableau::select_camera(int camera_index)
283 {
284   int num_cameras = camera_group_->getChildren()->getLength();
285 
286   if (camera_index >= 0 && camera_index < num_cameras) {
287     if (camera_index != camera_group_->whichChild.getValue()) {
288       camera_group_->whichChild.setValue(camera_index);
289       SoChildList* list = camera_group_->getChildren();
290       this->set_camera((SoCamera*)(*list)[camera_index]);
291     }
292   }
293   else {
294     std::vector<SoCamera*> user_cams = find_cameras(user_scene_root_);
295     if (!user_cams.empty()) {
296       camera_group_->whichChild.setValue(-1);
297       this->set_camera(user_cams[0]);
298     }
299   }
300 }
301 
302 
303 //: Return the camera viewing the scene
304 SoCamera*
camera_node() const305 bgui3d_viewer_tableau::camera_node() const
306 {
307   // FIX ME
308   return scene_camera_;
309 }
310 
311 
312 //: Set the camera type (Perspective or Orthogonal)
313 void
set_camera_type(camera_type_enum type)314 bgui3d_viewer_tableau::set_camera_type(camera_type_enum type)
315 {
316   SoType ptype = SoPerspectiveCamera::getClassTypeId();
317   SoType otype = SoOrthographicCamera::getClassTypeId();
318   SoCamera* newCamera;
319   if ( camera_type_ != type )
320   {
321     if ( camera_type_ == PERSPECTIVE && type == ORTHOGONAL )
322     {
323       newCamera = (SoCamera *)otype.createInstance();
324       convertPerspective2Ortho((SoPerspectiveCamera*)scene_camera_, (SoOrthographicCamera*)newCamera);
325     }
326     else if ( camera_type_ == ORTHOGONAL && type == PERSPECTIVE )
327     {
328       newCamera = (SoCamera *)ptype.createInstance();
329       convertOrtho2Perspective((SoOrthographicCamera*)scene_camera_, (SoPerspectiveCamera*)newCamera);
330     }
331     else
332       assert(!"This camera type is not supported");
333 
334     newCamera->ref();
335     std::vector<SoGroup *> cameraparents = get_parents_of_node(this->scene_camera_);
336     for (std::vector<SoGroup *>::iterator cp = cameraparents.begin(); cp != cameraparents.end(); ++cp)
337     {
338       (*cp)->replaceChild((*cp)->findChild(this->scene_camera_), newCamera);
339     }
340 
341     this->set_camera(newCamera);
342 
343     camera_group_->whichChild.setValue(camera_group_->findChild(this->scene_camera_));
344     newCamera->unref();
345   }
346 }
347 
348 
349 //: Return the camera type (Perspective or Orthogonal)
350 bgui3d_viewer_tableau::camera_type_enum
camera_type() const351 bgui3d_viewer_tableau::camera_type() const
352 {
353   return camera_type_;
354 }
355 
356 
357 //: Toggle the camera type between Perspective and Orthogonal
358 void
toggle_camera_type()359 bgui3d_viewer_tableau::toggle_camera_type()
360 {
361   if (camera_type_ == ORTHOGONAL)
362     set_camera_type(PERSPECTIVE);
363   else
364     set_camera_type(ORTHOGONAL);
365 }
366 
367 
368 //: Adjust the camera to view the entire scene
369 void
view_all()370 bgui3d_viewer_tableau::view_all()
371 {
372   scene_camera_->viewAll( user_scene_root_, get_viewport_region() );
373 }
374 
375 
376 //: Save the current camera as the home position
377 void
save_home_position()378 bgui3d_viewer_tableau::save_home_position()
379 {
380   if (! this->scene_camera_) return; // probably a scene-less viewer
381 
382   // We use SoType::createInstance() to store a copy of the camera,
383   // not just assuming it's either a perspective or an orthographic
384   // camera.
385 
386   SoType t = this->scene_camera_->getTypeId();
387   assert(t.isDerivedFrom(SoNode::getClassTypeId()));
388   assert(t.canCreateInstance());
389 
390   if (this->stored_camera_)
391     this->stored_camera_->unref();
392 
393   this->stored_camera_ = (SoNode *)t.createInstance();
394   this->stored_camera_->ref();
395   this->stored_camera_->copyContents(this->scene_camera_, false);
396 }
397 
398 
399 //: Restore the saved home position of the camera
400 void
reset_to_home_position()401 bgui3d_viewer_tableau::reset_to_home_position()
402 {
403   if (! this->scene_camera_) { return; } // probably a scene-less viewer
404   if (! this->stored_camera_) { return; }
405 
406   SoType t = this->scene_camera_->getTypeId();
407   SoType s = this->stored_camera_->getTypeId();
408 
409   // most common case
410   if (t == s) {
411     this->scene_camera_->copyContents(this->stored_camera_, false);
412   }
413   // handle common case #1
414   else if (t == SoOrthographicCamera::getClassTypeId() &&
415            s == SoPerspectiveCamera::getClassTypeId()) {
416     this->convertPerspective2Ortho((SoPerspectiveCamera *)this->stored_camera_,
417                                    (SoOrthographicCamera *)this->scene_camera_);
418     camera_type_ = ORTHOGONAL;
419   }
420   // handle common case #2
421   else if (t == SoPerspectiveCamera::getClassTypeId() &&
422            s == SoOrthographicCamera::getClassTypeId()) {
423     this->convertOrtho2Perspective((SoOrthographicCamera *)this->stored_camera_,
424                                    (SoPerspectiveCamera *)this->scene_camera_);
425     camera_type_ = PERSPECTIVE;
426   }
427   // otherwise, cameras have changed in ways we don't understand since
428   // the last saveHomePosition() invocation, and so we're just going
429   // to ignore the reset request
430 }
431 
432 //-------------------------------------------------
433 
434 //-------------Headlight Methods-------------------
435 
436 //: Activate a headlight
437 void
set_headlight(bool enable)438 bgui3d_viewer_tableau::set_headlight(bool enable)
439 {
440   headlight_->on = enable;
441 }
442 
443 
444 //: Is the headlight active
445 bool
is_headlight() const446 bgui3d_viewer_tableau::is_headlight() const
447 {
448   return headlight_->on.getValue() != 0;
449 }
450 
451 
452 //: Return the headlight
453 SoDirectionalLight*
headlight() const454 bgui3d_viewer_tableau::headlight() const
455 {
456   return headlight_;
457 }
458 
459 //-------------------------------------------------
460 
461 //-------------Text2 Methods-------------------
462 
setTextCallback(void * data,SoSensor *)463 static void setTextCallback( void *data, SoSensor * /*sensor*/ )
464 {
465   ((SoText2*)data)->string.deleteValues(0, 1);
466 }
467 
setText(const std::string & string)468 void bgui3d_viewer_tableau::setText( const std::string& string )
469 {
470   int numStrings = text_->string.getNum();
471   text_->string.set1Value( numStrings, string.c_str() );
472   SoAlarmSensor* alarm = new SoAlarmSensor( setTextCallback, text_ );
473   alarm->setTimeFromNow( 7.0 );
474   alarm->schedule();
475 }
476 
477 //---------------------------------------------------
478 
479 //: convert camera to perspective
480 void
convertOrtho2Perspective(const SoOrthographicCamera * in,SoPerspectiveCamera * out)481 bgui3d_viewer_tableau::convertOrtho2Perspective(const SoOrthographicCamera * in,
482                                                 SoPerspectiveCamera * out)
483 {
484   out->aspectRatio.setValue(in->aspectRatio.getValue());
485   out->focalDistance.setValue(in->focalDistance.getValue());
486   out->orientation.setValue(in->orientation.getValue());
487   out->position.setValue(in->position.getValue());
488   out->viewportMapping.setValue(in->viewportMapping.getValue());
489   out->setName(in->getName());
490 
491   float focaldist = in->focalDistance.getValue();
492 
493   // focalDistance==0.0f happens for empty scenes.
494   if (focaldist != 0.0f) {
495     out->heightAngle = 2.0f * (float)std::atan(in->height.getValue() / 2.0 / focaldist);
496   }
497   else {
498     // 45?is the default value of this field in SoPerspectiveCamera.
499     out->heightAngle = (float)(M_PI / 4.0);
500   }
501 }
502 
503 
504 //: convert camera to orthographic
505 void
convertPerspective2Ortho(const SoPerspectiveCamera * in,SoOrthographicCamera * out)506 bgui3d_viewer_tableau::convertPerspective2Ortho(const SoPerspectiveCamera * in,
507                                                 SoOrthographicCamera * out)
508 {
509   out->aspectRatio.setValue(in->aspectRatio.getValue());
510   out->focalDistance.setValue(in->focalDistance.getValue());
511   out->orientation.setValue(in->orientation.getValue());
512   out->position.setValue(in->position.getValue());
513   out->viewportMapping.setValue(in->viewportMapping.getValue());
514   out->setName(in->getName());
515 
516   float focaldist = in->focalDistance.getValue();
517 
518   out->height = 2.0f * focaldist * (float)std::tan(in->heightAngle.getValue() / 2.0);
519 }
520 
521 void
set_clipping_planes()522 bgui3d_viewer_tableau::set_clipping_planes()
523 {
524   SoGetBoundingBoxAction action( get_viewport_region() );
525 
526   action.apply( scene_root_ );
527 
528   SbXfBox3f xbox = action.getXfBoundingBox();
529 
530   SbMatrix cammat;
531 
532   SoSearchAction searchaction;
533   searchaction.reset();
534   searchaction.setSearchingAll(TRUE);
535   searchaction.setInterest(SoSearchAction::FIRST);
536   searchaction.setNode(scene_camera_);
537   searchaction.apply(scene_root_);
538 
539   SoGetMatrixAction matrixaction(get_viewport_region());
540   SbMatrix inverse = SbMatrix::identity();
541   if (searchaction.getPath()) {
542     matrixaction.apply(searchaction.getPath());
543     inverse = matrixaction.getInverse();
544   }
545 
546   xbox.transform(inverse);
547 
548   SbMatrix mat;
549   mat.setTranslate(- scene_camera_->position.getValue());
550   xbox.transform(mat);
551   mat = scene_camera_->orientation.getValue().inverse();
552   xbox.transform(mat);
553   SbBox3f box = xbox.project();
554 
555   // Bounding box was calculated in camera space, so we need to "flip"
556   // the box (because camera is pointing in the (0,0,-1) direction
557   // from origo.
558   float nearval = -box.getMax()[2];
559   float farval = -box.getMin()[2];
560 
561   // FIXME: what if we have a weird scale transform in the scenegraph?
562   // Could we end up with nearval > farval then? Investigate, then
563   // either use an assert() (if it can't happen) or an SoWinSwap()
564   // (to handle it). 20020116 mortene.
565 
566   // Check if scene is completely behind us.
567   if (farval <= 0.0f) { return; }
568 
569   if ( scene_camera_->isOfType(SoPerspectiveCamera::getClassTypeId())) {
570     // Disallow negative and small near clipping plane distance.
571 
572     float nearlimit; // the smallest value allowed for nearval
573 #if 0
574     if (this->autoclipstrategy == SoWinViewer::CONSTANT_NEAR_PLANE) {
575       nearlimit = this->autoclipvalue;
576     }
577     else {
578       assert(this->autoclipstrategy == SoWinViewer::VARIABLE_NEAR_PLANE);
579 #endif // 0
580       // From glFrustum() documentation: Depth-buffer precision is
581       // affected by the values specified for znear and zfar. The
582       // greater the ratio of zfar to znear is, the less effective the
583       // depth buffer will be at distinguishing between surfaces that
584       // are near each other. If r = far/near, roughly log (2) r bits
585       // of depth buffer precision are lost. Because r approaches
586       // infinity as znear approaches zero, you should never set znear
587       // to zero.
588       GLint depthbits[1];
589       // assume 16-bit depth
590       // it is unsafe to use GL functions here because a GL context
591       // might not have been created yet.
592       depthbits[0] = 16;
593 #if 0
594       glGetIntegerv(GL_DEPTH_BITS, depthbits);
595 #endif // 0
596 
597       int use_bits = (int) (float(depthbits[0]) * 0.4f + 0.001f);
598       float r = (float)(1 << use_bits);
599       nearlimit = farval / r;
600 #if 0
601     }
602 #endif // 0
603 
604     if (nearlimit >= farval) {
605       // (The "5000" magic constant was found by fiddling around a bit
606       // on an OpenGL implementation with a 16-bit depth-buffer
607       // resolution, adjusting to find something that would work well
608       // with both a very "stretched" / deep scene and a more compact
609       // single-model one.)
610       nearlimit = farval / 5000.0f;
611     }
612 
613     // adjust the near plane if the value is too small.
614     if (nearval < nearlimit) {
615       nearval = nearlimit;
616     }
617 
618 #if 0
619     if (this->autoclipcb) {
620       SbVec2f nearfar;
621       nearfar[0] = nearval;
622       nearfar[1] = farval;
623 
624       nearfar = this->autoclipcb(this->autoclipuserdata, nearfar);
625 
626       nearval = nearfar[0];
627       farval = nearfar[1];
628     }
629 #endif // 0
630   }
631   // Some slack around the bounding box, in case the scene fits
632   // exactly inside it. This is done to minimize the chance of
633   // artefacts caused by the limitation of the z-buffer
634   // resolution. One common artefact if this is not done is that the
635   // near clipping plane cuts into the corners of the model as it's
636   // rotated.
637   const float SLACK = 0.001f;
638 
639   // FrustumCamera can be found in the SmallChange CVS module. We
640   // should not change the nearDistance for this camera, as this will
641   // modify the frustum.
642   if (scene_camera_->getTypeId().getName() == "FrustumCamera") {
643     nearval = scene_camera_->nearDistance.getValue();
644     farval *= (1.0f + SLACK);
645     if (farval <= nearval) {
646       // nothing is visible, so just set farval to som value > nearval.
647       farval = nearval + 10.0f;
648     }
649     scene_camera_->farDistance = farval;
650   }
651   else {
652     scene_camera_->nearDistance = nearval * (1.0f - SLACK);
653     scene_camera_->farDistance = farval * (1.0f + SLACK);
654   }
655 }
656 
657 
658 std::vector<SoGroup*>
get_parents_of_node(SoNode * node)659 bgui3d_viewer_tableau::get_parents_of_node(SoNode * node)
660 {
661   SbBool oldsearch = SoBaseKit::isSearchingChildren();
662   SoBaseKit::setSearchingChildren(TRUE);
663 
664   assert(node && "get_parent_of_node() called with null argument");
665 
666   SoSearchAction search;
667   search.setSearchingAll(TRUE);
668   search.setNode(node);
669   search.setInterest(SoSearchAction::ALL);
670   search.apply(this->scene_root());
671   SoPathList & pl = search.getPaths();
672 
673   std::vector<SoGroup*> parents;
674   for (int i = 0; i < pl.getLength(); ++i) {
675     SoFullPath * p = (SoFullPath*) pl[i];
676     if (p->getLength() > 0)
677       parents.push_back((SoGroup*)p->getNodeFromTail(1));
678   }
679   SoBaseKit::setSearchingChildren(oldsearch);
680   return parents;
681 }
682 
683 
684 std::vector<SoCamera*>
find_cameras(SoNode * root) const685 bgui3d_viewer_tableau::find_cameras(SoNode* root) const
686 {
687   assert(camera_group_);
688   SoSearchAction sa;
689 
690   // Search for existing cameras
691   sa.setType(SoCamera::getClassTypeId());
692   sa.setInterest(SoSearchAction::ALL);
693   sa.setSearchingAll(TRUE);
694   sa.apply(root);
695   SoPathList & pl = sa.getPaths();
696 
697   std::vector<SoCamera*> cameras;
698   for (int i = 0; i < pl.getLength(); ++i) {
699     SoFullPath * p = (SoFullPath*) pl[i];
700     if (p->getTail()->isOfType(SoCamera::getClassTypeId())) {
701       SoCamera * camera = (SoCamera*) p->getTail();
702       cameras.push_back(camera);
703     }
704   }
705   sa.reset();
706 
707   return cameras;
708 }
709 
710 
711 //: Find the VRML viewpoint nodes in the scenegraph and make camera.
712 // The cameras are added to the camera group (outside the user scene)
collect_vrml_cameras(SoNode * root) const713 void bgui3d_viewer_tableau::collect_vrml_cameras(SoNode* root) const
714 {
715   assert(camera_group_);
716   SoSearchAction sa;
717 
718   // Search for VRML viewpoints and create cameras
719   sa.setType(SoVRMLViewpoint::getClassTypeId());
720   sa.setInterest(SoSearchAction::ALL);
721   sa.setSearchingAll(TRUE);
722   sa.apply(root);
723   SoPathList & pl2 = sa.getPaths();
724 
725   for (int i = 0; i < pl2.getLength(); ++i) {
726     SoFullPath * p = (SoFullPath*) pl2[i];
727     if (p->getTail()->isOfType(SoVRMLViewpoint::getClassTypeId())) {
728       SoVRMLViewpoint * vp = (SoVRMLViewpoint*) p->getTail();
729       SoPerspectiveCamera * camera = (SoPerspectiveCamera *)
730           SoPerspectiveCamera::getClassTypeId().createInstance();
731       camera->nearDistance = 0.5f;
732       camera->farDistance = 5000.0f;
733       camera->position = vp->position;
734       camera->orientation = vp->orientation;
735       camera->heightAngle = vp->fieldOfView;
736       camera->setName(vp->description.getValue());
737       camera_group_->addChild( camera );
738     }
739   }
740   sa.reset();
741 }
742