]> git.mxchange.org Git - flightgear.git/blob - src/Main/CameraGroup.cxx
Work on making 2D panels act like standard scene-graph elements for picking and drawing.
[flightgear.git] / src / Main / CameraGroup.cxx
1 // Copyright (C) 2008  Tim Moore
2 // Copyright (C) 2011  Mathias Froehlich
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License as
6 // published by the Free Software Foundation; either version 2 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17
18 #ifdef HAVE_CONFIG_H
19 #  include <config.h>
20 #endif
21
22 #include "CameraGroup.hxx"
23
24 #include "fg_props.hxx"
25 #include "globals.hxx"
26 #include "renderer.hxx"
27 #include "FGEventHandler.hxx"
28 #include "WindowBuilder.hxx"
29 #include "WindowSystemAdapter.hxx"
30 #include <simgear/props/props.hxx>
31 #include <simgear/structure/OSGUtils.hxx>
32 #include <simgear/structure/OSGVersion.hxx>
33 #include <simgear/scene/material/EffectCullVisitor.hxx>
34 #include <simgear/scene/util/RenderConstants.hxx>
35
36 #include <algorithm>
37 #include <cstring>
38 #include <string>
39
40 #include <osg/Camera>
41 #include <osg/Geometry>
42 #include <osg/GraphicsContext>
43 #include <osg/io_utils>
44 #include <osg/Math>
45 #include <osg/Matrix>
46 #include <osg/Notify>
47 #include <osg/Program>
48 #include <osg/Quat>
49 #include <osg/TexMat>
50 #include <osg/Vec3d>
51 #include <osg/Viewport>
52
53 #include <osgUtil/IntersectionVisitor>
54
55 #include <osgViewer/GraphicsWindow>
56 #include <osgViewer/Renderer>
57
58 static osg::Matrix
59 invert(const osg::Matrix& matrix)
60 {
61     return osg::Matrix::inverse(matrix);
62 }
63
64 /// Returns the zoom factor of the master camera.
65 /// The reference fov is the historic 55 deg
66 static double
67 zoomFactor()
68 {
69     double fov = fgGetDouble("/sim/current-view/field-of-view", 55);
70     if (fov < 1)
71         fov = 1;
72     return tan(55*0.5*SG_DEGREES_TO_RADIANS)/tan(fov*0.5*SG_DEGREES_TO_RADIANS);
73 }
74
75 static osg::Vec2d
76 preMult(const osg::Vec2d& v, const osg::Matrix& m)
77 {
78   osg::Vec3d tmp = m.preMult(osg::Vec3(v, 0));
79   return osg::Vec2d(tmp[0], tmp[1]);
80 }
81
82 static osg::Matrix
83 relativeProjection(const osg::Matrix& P0, const osg::Matrix& R, const osg::Vec2d ref[2],
84                    const osg::Matrix& pP, const osg::Matrix& pR, const osg::Vec2d pRef[2])
85 {
86   // Track the way from one projection space to the other:
87   // We want
88   //  P = T*S*P0
89   // where P0 is the projection template sensible for the given window size,
90   // T is a translation matrix and S a scale matrix.
91   // We need to determine T and S so that the reference points in the parents
92   // projection space match the two reference points in this cameras projection space.
93
94   // Starting from the parents camera projection space, we get into this cameras
95   // projection space by the transform matrix:
96   //  P*R*inv(pP*pR) = T*S*P0*R*inv(pP*pR)
97   // So, at first compute that matrix without T*S and determine S and T from that
98
99   // Ok, now osg uses the inverse matrix multiplication order, thus:
100   osg::Matrix PtoPwithoutTS = invert(pR*pP)*R*P0;
101   // Compute the parents reference points in the current projection space
102   // without the yet unknown T and S
103   osg::Vec2d pRefInThis[2] = {
104     preMult(pRef[0], PtoPwithoutTS),
105     preMult(pRef[1], PtoPwithoutTS)
106   };
107
108   // To get the same zoom, rescale to match the parents size
109   double s = (ref[0] - ref[1]).length()/(pRefInThis[0] - pRefInThis[1]).length();
110   osg::Matrix S = osg::Matrix::scale(s, s, 1);
111
112   // For the translation offset, incorporate the now known scale
113   // and recompute the position ot the first reference point in the
114   // currents projection space without the yet unknown T.
115   pRefInThis[0] = preMult(pRef[0], PtoPwithoutTS*S);
116   // The translation is then the difference of the reference points
117   osg::Matrix T = osg::Matrix::translate(osg::Vec3d(ref[0] - pRefInThis[0], 0));
118
119   // Compose and return the desired final projection matrix
120   return P0*S*T;
121 }
122
123 namespace flightgear
124 {
125 using namespace osg;
126
127 using std::strcmp;
128 using std::string;
129
130 ref_ptr<CameraGroup> CameraGroup::_defaultGroup;
131
132 CameraGroup::CameraGroup(osgViewer::Viewer* viewer) :
133     _viewer(viewer)
134 {
135 }
136
137 }
138
139 namespace
140 {
141 using namespace osg;
142
143 // Given a projection matrix, return a new one with the same frustum
144 // sides and new near / far values.
145
146 void makeNewProjMat(Matrixd& oldProj, double znear,
147                                        double zfar, Matrixd& projection)
148 {
149     projection = oldProj;
150     // Slightly inflate the near & far planes to avoid objects at the
151     // extremes being clipped out.
152     znear *= 0.999;
153     zfar *= 1.001;
154
155     // Clamp the projection matrix z values to the range (near, far)
156     double epsilon = 1.0e-6;
157     if (fabs(projection(0,3)) < epsilon &&
158         fabs(projection(1,3)) < epsilon &&
159         fabs(projection(2,3)) < epsilon) {
160         // Projection is Orthographic
161         epsilon = -1.0/(zfar - znear); // Used as a temp variable
162         projection(2,2) = 2.0*epsilon;
163         projection(3,2) = (zfar + znear)*epsilon;
164     } else {
165         // Projection is Perspective
166         double trans_near = (-znear*projection(2,2) + projection(3,2)) /
167             (-znear*projection(2,3) + projection(3,3));
168         double trans_far = (-zfar*projection(2,2) + projection(3,2)) /
169             (-zfar*projection(2,3) + projection(3,3));
170         double ratio = fabs(2.0/(trans_near - trans_far));
171         double center = -0.5*(trans_near + trans_far);
172
173         projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0,
174                                          0.0, 1.0, 0.0, 0.0,
175                                          0.0, 0.0, ratio, 0.0,
176                                          0.0, 0.0, center*ratio, 1.0));
177     }
178 }
179
180 void installCullVisitor(Camera* camera)
181 {
182     osgViewer::Renderer* renderer
183         = static_cast<osgViewer::Renderer*>(camera->getRenderer());
184     for (int i = 0; i < 2; ++i) {
185         osgUtil::SceneView* sceneView = renderer->getSceneView(i);
186 #if SG_OSG_VERSION_LESS_THAN(3,0,0)
187         sceneView->setCullVisitor(new simgear::EffectCullVisitor);
188 #else
189         osg::ref_ptr<osgUtil::CullVisitor::Identifier> identifier;
190         identifier = sceneView->getCullVisitor()->getIdentifier();
191         sceneView->setCullVisitor(new simgear::EffectCullVisitor);
192         sceneView->getCullVisitor()->setIdentifier(identifier.get());
193
194         identifier = sceneView->getCullVisitorLeft()->getIdentifier();
195         sceneView->setCullVisitorLeft(sceneView->getCullVisitor()->clone());
196         sceneView->getCullVisitorLeft()->setIdentifier(identifier.get());
197
198         identifier = sceneView->getCullVisitorRight()->getIdentifier();
199         sceneView->setCullVisitorRight(sceneView->getCullVisitor()->clone());
200         sceneView->getCullVisitorRight()->setIdentifier(identifier.get());
201 #endif
202     }
203 }
204 }
205
206 namespace flightgear
207 {
208 void updateCameras(const CameraInfo* info)
209 {
210     if (info->camera.valid())
211         info->camera->getViewport()->setViewport(info->x, info->y,
212                                                  info->width, info->height);
213     if (info->farCamera.valid())
214         info->farCamera->getViewport()->setViewport(info->x, info->y,
215                                                     info->width, info->height);
216 }
217
218 CameraInfo* CameraGroup::addCamera(unsigned flags, Camera* camera,
219                                    const Matrix& view,
220                                    const Matrix& projection,
221                                    bool useMasterSceneData)
222 {
223     CameraInfo* info = new CameraInfo(flags);
224     // The camera group will always update the camera
225     camera->setReferenceFrame(Transform::ABSOLUTE_RF);
226
227     Camera* farCamera = 0;
228     if ((flags & (GUI | ORTHO)) == 0) {
229         farCamera = new Camera;
230         farCamera->setAllowEventFocus(camera->getAllowEventFocus());
231         farCamera->setGraphicsContext(camera->getGraphicsContext());
232         farCamera->setCullingMode(camera->getCullingMode());
233         farCamera->setInheritanceMask(camera->getInheritanceMask());
234         farCamera->setReferenceFrame(Transform::ABSOLUTE_RF);
235         // Each camera's viewport is written when the window is
236         // resized; if the the viewport isn't copied here, it gets updated
237         // twice and ends up with the wrong value.
238         farCamera->setViewport(simgear::clone(camera->getViewport()));
239         farCamera->setDrawBuffer(camera->getDrawBuffer());
240         farCamera->setReadBuffer(camera->getReadBuffer());
241         farCamera->setRenderTargetImplementation(
242             camera->getRenderTargetImplementation());
243         const Camera::BufferAttachmentMap& bufferMap
244             = camera->getBufferAttachmentMap();
245         if (bufferMap.count(Camera::COLOR_BUFFER) != 0) {
246             farCamera->attach(
247                 Camera::COLOR_BUFFER,
248                 bufferMap.find(Camera::COLOR_BUFFER)->second._texture.get());
249         }
250         _viewer->addSlave(farCamera, projection, view, useMasterSceneData);
251         installCullVisitor(farCamera);
252         info->farCamera = farCamera;
253         info->farSlaveIndex = _viewer->getNumSlaves() - 1;
254         farCamera->setRenderOrder(Camera::POST_RENDER, info->farSlaveIndex);
255         camera->setCullMask(camera->getCullMask() & ~simgear::BACKGROUND_BIT);
256         camera->setClearMask(GL_DEPTH_BUFFER_BIT);
257     }
258     _viewer->addSlave(camera, projection, view, useMasterSceneData);
259     installCullVisitor(camera);
260     info->camera = camera;
261     info->slaveIndex = _viewer->getNumSlaves() - 1;
262     camera->setRenderOrder(Camera::POST_RENDER, info->slaveIndex);
263     _cameras.push_back(info);
264     return info;
265 }
266
267 void CameraGroup::update(const osg::Vec3d& position,
268                          const osg::Quat& orientation)
269 {
270     const Matrix masterView(osg::Matrix::translate(-position)
271                             * osg::Matrix::rotate(orientation.inverse()));
272     _viewer->getCamera()->setViewMatrix(masterView);
273     const Matrix& masterProj = _viewer->getCamera()->getProjectionMatrix();
274     double masterZoomFactor = zoomFactor();
275     for (CameraList::iterator i = _cameras.begin(); i != _cameras.end(); ++i) {
276         const CameraInfo* info = i->get();
277         const View::Slave& slave = _viewer->getSlave(info->slaveIndex);
278 #if SG_OSG_VERSION_LESS_THAN(3,0,0)
279         // refreshes camera viewports (for now)
280         updateCameras(info);
281 #endif
282         Camera* camera = info->camera.get();
283         Matrix viewMatrix;
284       
285         if (info->flags & GUI) {
286           viewMatrix = osg::Matrix(); // identifty transform on the GUI camera
287         } else if ((info->flags & VIEW_ABSOLUTE) != 0)
288             viewMatrix = slave._viewOffset;
289         else
290             viewMatrix = masterView * slave._viewOffset;
291         camera->setViewMatrix(viewMatrix);
292         Matrix projectionMatrix;
293         
294         if (info->flags & GUI) {
295           projectionMatrix = osg::Matrix::ortho2D(0, info->width, 0, info->height);
296         } else if ((info->flags & PROJECTION_ABSOLUTE) != 0) {
297             if (info->flags & ENABLE_MASTER_ZOOM) {
298                 if (info->relativeCameraParent < _cameras.size()) {
299                     // template projection matrix and view matrix of the current camera
300                     osg::Matrix P0 = slave._projectionOffset;
301                     osg::Matrix R = viewMatrix;
302
303                     // The already known projection and view matrix of the parent camera
304                     const CameraInfo* parentInfo = _cameras[info->relativeCameraParent].get();
305                     osg::Matrix pP = parentInfo->camera->getProjectionMatrix();
306                     osg::Matrix pR = parentInfo->camera->getViewMatrix();
307                     
308                     // And the projection matrix derived from P0 so that the reference points match
309                     projectionMatrix = relativeProjection(P0, R, info->thisReference,
310                                                           pP, pR, info->parentReference);
311                     
312                 } else {
313                     // We want to zoom, so take the original matrix and apply the zoom to it.
314                     projectionMatrix = slave._projectionOffset;
315                     projectionMatrix.postMultScale(osg::Vec3d(masterZoomFactor, masterZoomFactor, 1));
316                 }
317             } else {
318                 projectionMatrix = slave._projectionOffset;
319             }
320         } else {
321             projectionMatrix = masterProj * slave._projectionOffset;
322         }
323
324         if (!info->farCamera.valid()) {
325             camera->setProjectionMatrix(projectionMatrix);
326         } else {
327             Camera* farCamera = info->farCamera.get();
328             farCamera->setViewMatrix(viewMatrix);
329             double left, right, bottom, top, parentNear, parentFar;
330             projectionMatrix.getFrustum(left, right, bottom, top,
331                                         parentNear, parentFar);
332             if ((info->flags & FIXED_NEAR_FAR) == 0) {
333                 parentNear = _zNear;
334                 parentFar = _zFar;
335             }
336             if (parentFar < _nearField || _nearField == 0.0f) {
337                 camera->setProjectionMatrix(projectionMatrix);
338                 camera->setCullMask(camera->getCullMask()
339                                     | simgear::BACKGROUND_BIT);
340                 camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
341                 farCamera->setNodeMask(0);
342             } else {
343                 Matrix nearProj, farProj;
344                 makeNewProjMat(projectionMatrix, parentNear, _nearField,
345                                nearProj);
346                 makeNewProjMat(projectionMatrix, _nearField, parentFar,
347                                farProj);
348                 camera->setProjectionMatrix(nearProj);
349                 camera->setCullMask(camera->getCullMask()
350                                     & ~simgear::BACKGROUND_BIT);
351                 camera->setClearMask(GL_DEPTH_BUFFER_BIT);
352                 farCamera->setProjectionMatrix(farProj);
353                 farCamera->setNodeMask(camera->getNodeMask());
354             }
355         }
356     }
357 }
358
359 void CameraGroup::setCameraParameters(float vfov, float aspectRatio)
360 {
361     if (vfov != 0.0f && aspectRatio != 0.0f)
362         _viewer->getCamera()
363             ->setProjectionMatrixAsPerspective(vfov,
364                                                1.0f / aspectRatio,
365                                                _zNear, _zFar);
366 }
367     
368 double CameraGroup::getMasterAspectRatio() const
369 {
370     if (_cameras.empty())
371         return 0.0;
372     
373     const CameraInfo* info = _cameras.front();
374     
375     const osg::Viewport* viewport = info->camera->getViewport();
376     if (!viewport) {
377         return 0.0;
378     }
379     
380     return static_cast<double>(viewport->height()) / viewport->width();
381 }
382     
383 }
384
385 namespace
386 {
387 // A raw value for property nodes that references a class member via
388 // an osg::ref_ptr.
389 template<class C, class T>
390 class RefMember : public SGRawValue<T>
391 {
392 public:
393     RefMember (C *obj, T C::*ptr)
394         : _obj(obj), _ptr(ptr) {}
395     virtual ~RefMember () {}
396     virtual T getValue () const
397     {
398         return _obj.get()->*_ptr;
399     }
400     virtual bool setValue (T value)
401     {
402         _obj.get()->*_ptr = value;
403         return true;
404     }
405     virtual SGRawValue<T> * clone () const
406     {
407         return new RefMember(_obj.get(), _ptr);
408     }
409 private:
410     ref_ptr<C> _obj;
411     T C::* const _ptr;
412 };
413
414 template<typename C, typename T>
415 RefMember<C, T> makeRefMember(C *obj, T C::*ptr)
416 {
417     return RefMember<C, T>(obj, ptr);
418 }
419
420 template<typename C, typename T>
421 void bindMemberToNode(SGPropertyNode* parent, const char* childName,
422                       C* obj, T C::*ptr, T value)
423 {
424     SGPropertyNode* valNode = parent->getNode(childName);
425     RefMember<C, T> refMember = makeRefMember(obj, ptr);
426     if (!valNode) {
427         valNode = parent->getNode(childName, true);
428         valNode->tie(refMember, false);
429         setValue(valNode, value);
430     } else {
431         valNode->tie(refMember, true);
432     }
433 }
434
435 void buildViewport(flightgear::CameraInfo* info, SGPropertyNode* viewportNode,
436                    const osg::GraphicsContext::Traits *traits)
437 {
438     using namespace flightgear;
439     bindMemberToNode(viewportNode, "x", info, &CameraInfo::x, 0.0);
440     bindMemberToNode(viewportNode, "y", info, &CameraInfo::y, 0.0);
441     bindMemberToNode(viewportNode, "width", info, &CameraInfo::width,
442                      static_cast<double>(traits->width));
443     bindMemberToNode(viewportNode, "height", info, &CameraInfo::height,
444                      static_cast<double>(traits->height));
445 }
446 }
447
448 namespace flightgear
449 {
450
451 // Mostly copied from osg's osgViewer/View.cpp
452
453 static osg::Geometry* createParoramicSphericalDisplayDistortionMesh(
454     const Vec3& origin, const Vec3& widthVector, const Vec3& heightVector,
455     double sphere_radius, double collar_radius,
456     Image* intensityMap = 0, const Matrix& projectorMatrix = Matrix())
457 {
458     osg::Vec3d center(0.0,0.0,0.0);
459     osg::Vec3d eye(0.0,0.0,0.0);
460
461     double distance = sqrt(sphere_radius*sphere_radius - collar_radius*collar_radius);
462     bool flip = false;
463     bool texcoord_flip = false;
464
465 #if 0
466     osg::Vec3d projector = eye - osg::Vec3d(0.0,0.0, distance);
467
468     OSG_INFO<<"createParoramicSphericalDisplayDistortionMesh : Projector position = "<<projector<<std::endl;
469     OSG_INFO<<"createParoramicSphericalDisplayDistortionMesh : distance = "<<distance<<std::endl;
470 #endif
471     // create the quad to visualize.
472     osg::Geometry* geometry = new osg::Geometry();
473
474     geometry->setSupportsDisplayList(false);
475
476     osg::Vec3 xAxis(widthVector);
477     float width = widthVector.length();
478     xAxis /= width;
479
480     osg::Vec3 yAxis(heightVector);
481     float height = heightVector.length();
482     yAxis /= height;
483
484     int noSteps = 160;
485
486     osg::Vec3Array* vertices = new osg::Vec3Array;
487     osg::Vec2Array* texcoords0 = new osg::Vec2Array;
488     osg::Vec2Array* texcoords1 = intensityMap==0 ? new osg::Vec2Array : 0;
489     osg::Vec4Array* colors = new osg::Vec4Array;
490
491 #if 0
492     osg::Vec3 bottom = origin;
493     osg::Vec3 dx = xAxis*(width/((float)(noSteps-2)));
494     osg::Vec3 dy = yAxis*(height/((float)(noSteps-1)));
495 #endif
496     osg::Vec3 top = origin + yAxis*height;
497
498     osg::Vec3 screenCenter = origin + widthVector*0.5f + heightVector*0.5f;
499     float screenRadius = heightVector.length() * 0.5f;
500
501     geometry->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
502
503     for(int i=0;i<noSteps;++i)
504     {
505         //osg::Vec3 cursor = bottom+dy*(float)i;
506         for(int j=0;j<noSteps;++j)
507         {
508             osg::Vec2 texcoord(double(i)/double(noSteps-1), double(j)/double(noSteps-1));
509             double theta = texcoord.x() * 2.0 * osg::PI;
510             double phi = (1.0-texcoord.y()) * osg::PI;
511
512             if (texcoord_flip) texcoord.y() = 1.0f - texcoord.y();
513
514             osg::Vec3 pos(sin(phi)*sin(theta), sin(phi)*cos(theta), cos(phi));
515             pos = pos*projectorMatrix;
516
517             double alpha = atan2(pos.x(), pos.y());
518             if (alpha<0.0) alpha += 2.0*osg::PI;
519
520             double beta = atan2(sqrt(pos.x()*pos.x() + pos.y()*pos.y()), pos.z());
521             if (beta<0.0) beta += 2.0*osg::PI;
522
523             double gamma = atan2(sqrt(double(pos.x()*pos.x() + pos.y()*pos.y())), double(pos.z()+distance));
524             if (gamma<0.0) gamma += 2.0*osg::PI;
525
526
527             osg::Vec3 v = screenCenter + osg::Vec3(sin(alpha)*gamma*2.0/osg::PI, -cos(alpha)*gamma*2.0/osg::PI, 0.0f)*screenRadius;
528
529             if (flip)
530                 vertices->push_back(osg::Vec3(v.x(), top.y()-(v.y()-origin.y()),v.z()));
531             else
532                 vertices->push_back(v);
533
534             texcoords0->push_back( texcoord );
535
536             osg::Vec2 texcoord1(alpha/(2.0*osg::PI), 1.0f - beta/osg::PI);
537             if (intensityMap)
538             {
539                 colors->push_back(intensityMap->getColor(texcoord1));
540             }
541             else
542             {
543                 colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
544                 if (texcoords1) texcoords1->push_back( texcoord1 );
545             }
546
547
548         }
549     }
550
551
552     // pass the created vertex array to the points geometry object.
553     geometry->setVertexArray(vertices);
554
555     geometry->setColorArray(colors);
556     geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
557
558     geometry->setTexCoordArray(0,texcoords0);
559     if (texcoords1) geometry->setTexCoordArray(1,texcoords1);
560
561     osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES);
562     geometry->addPrimitiveSet(elements);
563
564     for(int i=0;i<noSteps-1;++i)
565     {
566         for(int j=0;j<noSteps-1;++j)
567         {
568             int i1 = j+(i+1)*noSteps;
569             int i2 = j+(i)*noSteps;
570             int i3 = j+1+(i)*noSteps;
571             int i4 = j+1+(i+1)*noSteps;
572
573             osg::Vec3& v1 = (*vertices)[i1];
574             osg::Vec3& v2 = (*vertices)[i2];
575             osg::Vec3& v3 = (*vertices)[i3];
576             osg::Vec3& v4 = (*vertices)[i4];
577
578             if ((v1-screenCenter).length()>screenRadius) continue;
579             if ((v2-screenCenter).length()>screenRadius) continue;
580             if ((v3-screenCenter).length()>screenRadius) continue;
581             if ((v4-screenCenter).length()>screenRadius) continue;
582
583             elements->push_back(i1);
584             elements->push_back(i2);
585             elements->push_back(i3);
586
587             elements->push_back(i1);
588             elements->push_back(i3);
589             elements->push_back(i4);
590         }
591     }
592
593     return geometry;
594 }
595
596 void CameraGroup::buildDistortionCamera(const SGPropertyNode* psNode,
597                                         Camera* camera)
598 {
599     const SGPropertyNode* texNode = psNode->getNode("texture");
600     if (!texNode) {
601         // error
602         return;
603     }
604     string texName = texNode->getStringValue();
605     TextureMap::iterator itr = _textureTargets.find(texName);
606     if (itr == _textureTargets.end()) {
607         // error
608         return;
609     }
610     Viewport* viewport = camera->getViewport();
611     float width = viewport->width();
612     float height = viewport->height();
613     TextureRectangle* texRect = itr->second.get();
614     double radius = psNode->getDoubleValue("radius", 1.0);
615     double collar = psNode->getDoubleValue("collar", 0.45);
616     Geode* geode = new Geode();
617     geode->addDrawable(createParoramicSphericalDisplayDistortionMesh(
618                            Vec3(0.0f,0.0f,0.0f), Vec3(width,0.0f,0.0f),
619                            Vec3(0.0f,height,0.0f), radius, collar));
620
621     // new we need to add the texture to the mesh, we do so by creating a
622     // StateSet to contain the Texture StateAttribute.
623     StateSet* stateset = geode->getOrCreateStateSet();
624     stateset->setTextureAttributeAndModes(0, texRect, StateAttribute::ON);
625     stateset->setMode(GL_LIGHTING, StateAttribute::OFF);
626
627     TexMat* texmat = new TexMat;
628     texmat->setScaleByTextureRectangleSize(true);
629     stateset->setTextureAttributeAndModes(0, texmat, osg::StateAttribute::ON);
630 #if 0
631     if (!applyIntensityMapAsColours && intensityMap)
632     {
633         stateset->setTextureAttributeAndModes(1, new osg::Texture2D(intensityMap), osg::StateAttribute::ON);
634     }
635 #endif
636     // add subgraph to render
637     camera->addChild(geode);
638     camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
639     camera->setClearColor(osg::Vec4(0.0, 0.0, 0.0, 1.0));
640     camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
641     camera->setCullingMode(osg::CullSettings::NO_CULLING);
642     camera->setName("DistortionCorrectionCamera");
643 }
644
645 CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode)
646 {
647     WindowBuilder *wBuild = WindowBuilder::getWindowBuilder();
648     const SGPropertyNode* windowNode = cameraNode->getNode("window");
649     GraphicsWindow* window = 0;
650     int cameraFlags = DO_INTERSECTION_TEST;
651     if (windowNode) {
652         // New style window declaration / definition
653         window = wBuild->buildWindow(windowNode);
654     } else {
655         // Old style: suck window params out of camera block
656         window = wBuild->buildWindow(cameraNode);
657     }
658     if (!window) {
659         return 0;
660     }
661     Camera* camera = new Camera;
662     camera->setAllowEventFocus(false);
663     camera->setGraphicsContext(window->gc.get());
664     camera->setViewport(new Viewport);
665     camera->setCullingMode(CullSettings::SMALL_FEATURE_CULLING
666                            | CullSettings::VIEW_FRUSTUM_CULLING);
667     camera->setInheritanceMask(CullSettings::ALL_VARIABLES
668                                & ~(CullSettings::CULL_MASK
669                                    | CullSettings::CULLING_MODE
670 #if defined(HAVE_CULLSETTINGS_CLEAR_MASK)
671                                    | CullSettings::CLEAR_MASK
672 #endif
673                                    ));
674
675     osg::Matrix vOff;
676     const SGPropertyNode* viewNode = cameraNode->getNode("view");
677     if (viewNode) {
678         double heading = viewNode->getDoubleValue("heading-deg", 0.0);
679         double pitch = viewNode->getDoubleValue("pitch-deg", 0.0);
680         double roll = viewNode->getDoubleValue("roll-deg", 0.0);
681         double x = viewNode->getDoubleValue("x", 0.0);
682         double y = viewNode->getDoubleValue("y", 0.0);
683         double z = viewNode->getDoubleValue("z", 0.0);
684         // Build a view matrix, which is the inverse of a model
685         // orientation matrix.
686         vOff = (Matrix::translate(-x, -y, -z)
687                 * Matrix::rotate(-DegreesToRadians(heading),
688                                  Vec3d(0.0, 1.0, 0.0),
689                                  -DegreesToRadians(pitch),
690                                  Vec3d(1.0, 0.0, 0.0),
691                                  -DegreesToRadians(roll),
692                                  Vec3d(0.0, 0.0, 1.0)));
693         if (viewNode->getBoolValue("absolute", false))
694             cameraFlags |= VIEW_ABSOLUTE;
695     } else {
696         // Old heading parameter, works in the opposite direction
697         double heading = cameraNode->getDoubleValue("heading-deg", 0.0);
698         vOff.makeRotate(DegreesToRadians(heading), osg::Vec3(0, 1, 0));
699     }
700     // Configuring the physical dimensions of a monitor
701     SGPropertyNode* viewportNode = cameraNode->getNode("viewport", true);
702     double physicalWidth = viewportNode->getDoubleValue("width", 1024);
703     double physicalHeight = viewportNode->getDoubleValue("height", 768);
704     double bezelHeightTop = 0;
705     double bezelHeightBottom = 0;
706     double bezelWidthLeft = 0;
707     double bezelWidthRight = 0;
708     const SGPropertyNode* physicalDimensionsNode = 0;
709     if ((physicalDimensionsNode = cameraNode->getNode("physical-dimensions")) != 0) {
710         physicalWidth = physicalDimensionsNode->getDoubleValue("width", physicalWidth);
711         physicalHeight = physicalDimensionsNode->getDoubleValue("height", physicalHeight);
712         const SGPropertyNode* bezelNode = 0;
713         if ((bezelNode = physicalDimensionsNode->getNode("bezel")) != 0) {
714             bezelHeightTop = bezelNode->getDoubleValue("top", bezelHeightTop);
715             bezelHeightBottom = bezelNode->getDoubleValue("bottom", bezelHeightBottom);
716             bezelWidthLeft = bezelNode->getDoubleValue("left", bezelWidthLeft);
717             bezelWidthRight = bezelNode->getDoubleValue("right", bezelWidthRight);
718         }
719     }
720     osg::Matrix pOff;
721     unsigned parentCameraIndex = ~0u;
722     osg::Vec2d parentReference[2];
723     osg::Vec2d thisReference[2];
724     SGPropertyNode* projectionNode = 0;
725     if ((projectionNode = cameraNode->getNode("perspective")) != 0) {
726         double fovy = projectionNode->getDoubleValue("fovy-deg", 55.0);
727         double aspectRatio = projectionNode->getDoubleValue("aspect-ratio",
728                                                             1.0);
729         double zNear = projectionNode->getDoubleValue("near", 0.0);
730         double zFar = projectionNode->getDoubleValue("far", zNear + 20000);
731         double offsetX = projectionNode->getDoubleValue("offset-x", 0.0);
732         double offsetY = projectionNode->getDoubleValue("offset-y", 0.0);
733         double tan_fovy = tan(DegreesToRadians(fovy*0.5));
734         double right = tan_fovy * aspectRatio * zNear + offsetX;
735         double left = -tan_fovy * aspectRatio * zNear + offsetX;
736         double top = tan_fovy * zNear + offsetY;
737         double bottom = -tan_fovy * zNear + offsetY;
738         pOff.makeFrustum(left, right, bottom, top, zNear, zFar);
739         cameraFlags |= PROJECTION_ABSOLUTE;
740         if (projectionNode->getBoolValue("fixed-near-far", true))
741             cameraFlags |= FIXED_NEAR_FAR;
742     } else if ((projectionNode = cameraNode->getNode("frustum")) != 0
743                || (projectionNode = cameraNode->getNode("ortho")) != 0) {
744         double top = projectionNode->getDoubleValue("top", 0.0);
745         double bottom = projectionNode->getDoubleValue("bottom", 0.0);
746         double left = projectionNode->getDoubleValue("left", 0.0);
747         double right = projectionNode->getDoubleValue("right", 0.0);
748         double zNear = projectionNode->getDoubleValue("near", 0.0);
749         double zFar = projectionNode->getDoubleValue("far", zNear + 20000);
750         if (cameraNode->getNode("frustum")) {
751             pOff.makeFrustum(left, right, bottom, top, zNear, zFar);
752             cameraFlags |= PROJECTION_ABSOLUTE;
753         } else {
754             pOff.makeOrtho(left, right, bottom, top, zNear, zFar);
755             cameraFlags |= (PROJECTION_ABSOLUTE | ORTHO);
756         }
757         if (projectionNode->getBoolValue("fixed-near-far", true))
758             cameraFlags |= FIXED_NEAR_FAR;
759     } else if ((projectionNode = cameraNode->getNode("master-perspective")) != 0) {
760         double zNear = projectionNode->getDoubleValue("eye-distance", 0.4*physicalWidth);
761         double xoff = projectionNode->getDoubleValue("x-offset", 0);
762         double yoff = projectionNode->getDoubleValue("y-offset", 0);
763         double left = -0.5*physicalWidth - xoff;
764         double right = 0.5*physicalWidth - xoff;
765         double bottom = -0.5*physicalHeight - yoff;
766         double top = 0.5*physicalHeight - yoff;
767         pOff.makeFrustum(left, right, bottom, top, zNear, zNear*1000);
768         cameraFlags |= PROJECTION_ABSOLUTE | ENABLE_MASTER_ZOOM;
769     } else if ((projectionNode = cameraNode->getNode("right-of-perspective"))
770                || (projectionNode = cameraNode->getNode("left-of-perspective"))
771                || (projectionNode = cameraNode->getNode("above-perspective"))
772                || (projectionNode = cameraNode->getNode("below-perspective"))
773                || (projectionNode = cameraNode->getNode("reference-points-perspective"))) {
774         std::string name = projectionNode->getStringValue("parent-camera");
775         for (unsigned i = 0; i < _cameras.size(); ++i) {
776             if (_cameras[i]->name != name)
777                 continue;
778             parentCameraIndex = i;
779         }
780         if (_cameras.size() <= parentCameraIndex) {
781             SG_LOG(SG_VIEW, SG_ALERT, "CameraGroup::buildCamera: "
782                    "failed to find parent camera for relative camera!");
783             return 0;
784         }
785         const CameraInfo* parentInfo = _cameras[parentCameraIndex].get();
786         if (projectionNode->getNameString() == "right-of-perspective") {
787             double tmp = (parentInfo->physicalWidth + 2*parentInfo->bezelWidthRight)/parentInfo->physicalWidth;
788             parentReference[0] = osg::Vec2d(tmp, -1);
789             parentReference[1] = osg::Vec2d(tmp, 1);
790             tmp = (physicalWidth + 2*bezelWidthLeft)/physicalWidth;
791             thisReference[0] = osg::Vec2d(-tmp, -1);
792             thisReference[1] = osg::Vec2d(-tmp, 1);
793         } else if (projectionNode->getNameString() == "left-of-perspective") {
794             double tmp = (parentInfo->physicalWidth + 2*parentInfo->bezelWidthLeft)/parentInfo->physicalWidth;
795             parentReference[0] = osg::Vec2d(-tmp, -1);
796             parentReference[1] = osg::Vec2d(-tmp, 1);
797             tmp = (physicalWidth + 2*bezelWidthRight)/physicalWidth;
798             thisReference[0] = osg::Vec2d(tmp, -1);
799             thisReference[1] = osg::Vec2d(tmp, 1);
800         } else if (projectionNode->getNameString() == "above-perspective") {
801             double tmp = (parentInfo->physicalHeight + 2*parentInfo->bezelHeightTop)/parentInfo->physicalHeight;
802             parentReference[0] = osg::Vec2d(-1, tmp);
803             parentReference[1] = osg::Vec2d(1, tmp);
804             tmp = (physicalHeight + 2*bezelHeightBottom)/physicalHeight;
805             thisReference[0] = osg::Vec2d(-1, -tmp);
806             thisReference[1] = osg::Vec2d(1, -tmp);
807         } else if (projectionNode->getNameString() == "below-perspective") {
808             double tmp = (parentInfo->physicalHeight + 2*parentInfo->bezelHeightBottom)/parentInfo->physicalHeight;
809             parentReference[0] = osg::Vec2d(-1, -tmp);
810             parentReference[1] = osg::Vec2d(1, -tmp);
811             tmp = (physicalHeight + 2*bezelHeightTop)/physicalHeight;
812             thisReference[0] = osg::Vec2d(-1, tmp);
813             thisReference[1] = osg::Vec2d(1, tmp);
814         } else if (projectionNode->getNameString() == "reference-points-perspective") {
815             SGPropertyNode* parentNode = projectionNode->getNode("parent", true);
816             SGPropertyNode* thisNode = projectionNode->getNode("this", true);
817             SGPropertyNode* pointNode;
818
819             pointNode = parentNode->getNode("point", 0, true);
820             parentReference[0][0] = pointNode->getDoubleValue("x", 0)*2/parentInfo->physicalWidth;
821             parentReference[0][1] = pointNode->getDoubleValue("y", 0)*2/parentInfo->physicalHeight;
822             pointNode = parentNode->getNode("point", 1, true);
823             parentReference[1][0] = pointNode->getDoubleValue("x", 0)*2/parentInfo->physicalWidth;
824             parentReference[1][1] = pointNode->getDoubleValue("y", 0)*2/parentInfo->physicalHeight;
825
826             pointNode = thisNode->getNode("point", 0, true);
827             thisReference[0][0] = pointNode->getDoubleValue("x", 0)*2/physicalWidth;
828             thisReference[0][1] = pointNode->getDoubleValue("y", 0)*2/physicalHeight;
829             pointNode = thisNode->getNode("point", 1, true);
830             thisReference[1][0] = pointNode->getDoubleValue("x", 0)*2/physicalWidth;
831             thisReference[1][1] = pointNode->getDoubleValue("y", 0)*2/physicalHeight;
832         }
833
834         pOff = osg::Matrix::perspective(45, physicalWidth/physicalHeight, 1, 20000);
835         cameraFlags |= PROJECTION_ABSOLUTE | ENABLE_MASTER_ZOOM;
836     } else {
837         // old style shear parameters
838         double shearx = cameraNode->getDoubleValue("shear-x", 0);
839         double sheary = cameraNode->getDoubleValue("shear-y", 0);
840         pOff.makeTranslate(-shearx, -sheary, 0);
841     }
842     const SGPropertyNode* textureNode = cameraNode->getNode("texture");
843     if (textureNode) {
844         string texName = textureNode->getStringValue("name");
845         int tex_width = textureNode->getIntValue("width");
846         int tex_height = textureNode->getIntValue("height");
847         TextureRectangle* texture = new TextureRectangle;
848
849         texture->setTextureSize(tex_width, tex_height);
850         texture->setInternalFormat(GL_RGB);
851         texture->setFilter(Texture::MIN_FILTER, Texture::LINEAR);
852         texture->setFilter(Texture::MAG_FILTER, Texture::LINEAR);
853         texture->setWrap(Texture::WRAP_S, Texture::CLAMP_TO_EDGE);
854         texture->setWrap(Texture::WRAP_T, Texture::CLAMP_TO_EDGE);
855         camera->setDrawBuffer(GL_FRONT);
856         camera->setReadBuffer(GL_FRONT);
857         camera->setRenderTargetImplementation(Camera::FRAME_BUFFER_OBJECT);
858         camera->attach(Camera::COLOR_BUFFER, texture);
859         _textureTargets[texName] = texture;
860     } else {
861         camera->setDrawBuffer(GL_BACK);
862         camera->setReadBuffer(GL_BACK);
863     }
864     const SGPropertyNode* psNode = cameraNode->getNode("panoramic-spherical");
865     bool useMasterSceneGraph = !psNode;
866     CameraInfo* info = addCamera(cameraFlags, camera, vOff, pOff,
867                                  useMasterSceneGraph);
868     info->name = cameraNode->getStringValue("name");
869     info->physicalWidth = physicalWidth;
870     info->physicalHeight = physicalHeight;
871     info->bezelHeightTop = bezelHeightTop;
872     info->bezelHeightBottom = bezelHeightBottom;
873     info->bezelWidthLeft = bezelWidthLeft;
874     info->bezelWidthRight = bezelWidthRight;
875     info->relativeCameraParent = parentCameraIndex;
876     info->parentReference[0] = parentReference[0];
877     info->parentReference[1] = parentReference[1];
878     info->thisReference[0] = thisReference[0];
879     info->thisReference[1] = thisReference[1];
880     // If a viewport isn't set on the camera, then it's hard to dig it
881     // out of the SceneView objects in the viewer, and the coordinates
882     // of mouse events are somewhat bizzare.
883     buildViewport(info, viewportNode, window->gc->getTraits());
884     updateCameras(info);
885     // Distortion camera needs the viewport which is created by addCamera().
886     if (psNode) {
887         info->flags = info->flags | VIEW_ABSOLUTE;
888         buildDistortionCamera(psNode, camera);
889     }
890     return info;
891 }
892
893 CameraInfo* CameraGroup::buildGUICamera(SGPropertyNode* cameraNode,
894                                         GraphicsWindow* window)
895 {
896     WindowBuilder *wBuild = WindowBuilder::getWindowBuilder();
897     const SGPropertyNode* windowNode = (cameraNode
898                                         ? cameraNode->getNode("window")
899                                         : 0);
900     if (!window && windowNode) {
901       // New style window declaration / definition
902       window = wBuild->buildWindow(windowNode);
903     }
904
905     if (!window) { // buildWindow can fail
906       SG_LOG(SG_VIEW, SG_WARN, "CameraGroup::buildGUICamera: failed to build a window");
907       return NULL;
908     }
909
910     Camera* camera = new Camera;
911     camera->setAllowEventFocus(false);
912     camera->setGraphicsContext(window->gc.get());
913     camera->setViewport(new Viewport);
914     // XXX Camera needs to be drawn last; eventually the render order
915     // should be assigned by a camera manager.
916     camera->setRenderOrder(osg::Camera::POST_RENDER, 100);
917         camera->setClearMask(0);
918     camera->setInheritanceMask(CullSettings::ALL_VARIABLES
919                                & ~(CullSettings::COMPUTE_NEAR_FAR_MODE
920                                    | CullSettings::CULLING_MODE
921 #if defined(HAVE_CULLSETTINGS_CLEAR_MASK)
922                                    | CullSettings::CLEAR_MASK
923 #endif
924                                    ));
925     camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
926     camera->setCullingMode(osg::CullSettings::NO_CULLING);
927     camera->setProjectionResizePolicy(Camera::FIXED);
928     camera->setReferenceFrame(Transform::ABSOLUTE_RF);
929     const int cameraFlags = GUI | DO_INTERSECTION_TEST;
930     CameraInfo* result = addCamera(cameraFlags, camera, Matrixd::identity(),
931                                    Matrixd::identity(), false);
932     SGPropertyNode* viewportNode = cameraNode->getNode("viewport", true);
933     buildViewport(result, viewportNode, window->gc->getTraits());
934
935     // Disable statistics for the GUI camera.
936     result->camera->setStats(0);
937     updateCameras(result);
938     return result;
939 }
940
941 CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer,
942                                            SGPropertyNode* gnode)
943 {
944     CameraGroup* cgroup = new CameraGroup(viewer);
945     for (int i = 0; i < gnode->nChildren(); ++i) {
946         SGPropertyNode* pNode = gnode->getChild(i);
947         const char* name = pNode->getName();
948         if (!strcmp(name, "camera")) {
949             cgroup->buildCamera(pNode);
950         } else if (!strcmp(name, "window")) {
951             WindowBuilder::getWindowBuilder()->buildWindow(pNode);
952         } else if (!strcmp(name, "gui")) {
953             cgroup->buildGUICamera(pNode);
954         }
955     }
956     bindMemberToNode(gnode, "znear", cgroup, &CameraGroup::_zNear, .1f);
957     bindMemberToNode(gnode, "zfar", cgroup, &CameraGroup::_zFar, 120000.0f);
958     bindMemberToNode(gnode, "near-field", cgroup, &CameraGroup::_nearField,
959                      100.0f);
960     return cgroup;
961 }
962
963 void CameraGroup::setCameraCullMasks(Node::NodeMask nm)
964 {
965     for (CameraIterator i = camerasBegin(), e = camerasEnd(); i != e; ++i) {
966         CameraInfo* info = i->get();
967         if (info->flags & GUI)
968             continue;
969         if (info->farCamera.valid() && info->farCamera->getNodeMask() != 0) {
970             info->camera->setCullMask(nm & ~simgear::BACKGROUND_BIT);
971             info->camera->setCullMaskLeft(nm & ~simgear::BACKGROUND_BIT);
972             info->camera->setCullMaskRight(nm & ~simgear::BACKGROUND_BIT);
973             info->farCamera->setCullMask(nm);
974             info->farCamera->setCullMaskLeft(nm);
975             info->farCamera->setCullMaskRight(nm);
976         } else {
977             info->camera->setCullMask(nm);
978             info->camera->setCullMaskLeft(nm);
979             info->camera->setCullMaskRight(nm);
980         }
981     }
982 }
983
984 void CameraGroup::resized()
985 {
986     for (CameraIterator i = camerasBegin(), e = camerasEnd(); i != e; ++i) {
987         CameraInfo *info = i->get();
988         const Viewport* viewport = info->camera->getViewport();
989         info->x = viewport->x();
990         info->y = viewport->y();
991         info->width = viewport->width();
992         info->height = viewport->height();
993     }
994 }
995
996 const CameraInfo* CameraGroup::getGUICamera() const
997 {
998     ConstCameraIterator result
999         = std::find_if(camerasBegin(), camerasEnd(),
1000                    FlagTester<CameraInfo>(GUI));
1001     if (result == camerasEnd()) {
1002         return NULL;
1003     }
1004
1005     return *result;
1006 }
1007   
1008 Camera* getGUICamera(CameraGroup* cgroup)
1009 {
1010     const CameraInfo* info = cgroup->getGUICamera();
1011     if (!info) {
1012         return NULL;
1013     }
1014     
1015     return info->camera.get();
1016 }
1017
1018 static bool computeCameraIntersection(const CameraInfo* cinfo,
1019                                       const osgGA::GUIEventAdapter* ea,
1020                                       osgUtil::LineSegmentIntersector::Intersections& intersections)
1021 {
1022   using osgUtil::Intersector;
1023   using osgUtil::LineSegmentIntersector;
1024   double x, y;
1025   eventToWindowCoords(ea, x, y);
1026   
1027   if (!(cinfo->flags & CameraGroup::DO_INTERSECTION_TEST))
1028     return false;
1029   
1030   const Camera* camera = cinfo->camera.get();
1031   if (camera->getGraphicsContext() != ea->getGraphicsContext())
1032     return false;
1033   
1034   const Viewport* viewport = camera->getViewport();
1035   double epsilon = 0.5;
1036   if (!(x >= viewport->x() - epsilon
1037         && x < viewport->x() + viewport->width() -1.0 + epsilon
1038         && y >= viewport->y() - epsilon
1039         && y < viewport->y() + viewport->height() -1.0 + epsilon))
1040     return false;
1041   
1042   Vec4d start(x, y, 0.0, 1.0);
1043   Vec4d end(x, y, 1.0, 1.0);
1044   Matrix windowMat = viewport->computeWindowMatrix();
1045   Matrix startPtMat = Matrix::inverse(camera->getProjectionMatrix()
1046                                       * windowMat);
1047   Matrix endPtMat;
1048   if (!cinfo->farCamera.valid() || cinfo->farCamera->getNodeMask() == 0)
1049     endPtMat = startPtMat;
1050   else
1051     endPtMat = Matrix::inverse(cinfo->farCamera->getProjectionMatrix()
1052                                * windowMat);
1053   start = start * startPtMat;
1054   start /= start.w();
1055   end = end * endPtMat;
1056   end /= end.w();
1057   ref_ptr<LineSegmentIntersector> picker
1058   = new LineSegmentIntersector(Intersector::VIEW,
1059                                Vec3d(start.x(), start.y(), start.z()),
1060                                Vec3d(end.x(), end.y(), end.z()));
1061   osgUtil::IntersectionVisitor iv(picker.get());
1062   const_cast<Camera*>(camera)->accept(iv);
1063   if (picker->containsIntersections()) {
1064     intersections = picker->getIntersections();
1065     return true;
1066   }
1067   
1068   return false;
1069 }
1070   
1071 bool computeIntersections(const CameraGroup* cgroup,
1072                           const osgGA::GUIEventAdapter* ea,
1073                           osgUtil::LineSegmentIntersector::Intersections& intersections)
1074 {
1075     // test the GUI first
1076     const CameraInfo* guiCamera = cgroup->getGUICamera();
1077     if (guiCamera && computeCameraIntersection(guiCamera, ea, intersections))
1078         return true;
1079     
1080     // Find camera that contains event
1081     for (CameraGroup::ConstCameraIterator iter = cgroup->camerasBegin(),
1082              e = cgroup->camerasEnd();
1083          iter != e;
1084          ++iter) {
1085         const CameraInfo* cinfo = iter->get();
1086         if (cinfo == guiCamera)
1087             continue;
1088         
1089         if (computeCameraIntersection(cinfo, ea, intersections))
1090             return true;
1091     }
1092   
1093     intersections.clear();
1094     return false;
1095 }
1096
1097 void warpGUIPointer(CameraGroup* cgroup, int x, int y)
1098 {
1099     using osgViewer::GraphicsWindow;
1100     Camera* guiCamera = getGUICamera(cgroup);
1101     if (!guiCamera)
1102         return;
1103     Viewport* vport = guiCamera->getViewport();
1104     GraphicsWindow* gw
1105         = dynamic_cast<GraphicsWindow*>(guiCamera->getGraphicsContext());
1106     if (!gw)
1107         return;
1108     globals->get_renderer()->getEventHandler()->setMouseWarped();
1109     // Translate the warp request into the viewport of the GUI camera,
1110     // send the request to the window, then transform the coordinates
1111     // for the Viewer's event queue.
1112     double wx = x + vport->x();
1113     double wyUp = vport->height() + vport->y() - y;
1114     double wy;
1115     const GraphicsContext::Traits* traits = gw->getTraits();
1116     if (gw->getEventQueue()->getCurrentEventState()->getMouseYOrientation()
1117         == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS) {
1118         wy = traits->height - wyUp;
1119     } else {
1120         wy = wyUp;
1121     }
1122     gw->getEventQueue()->mouseWarped(wx, wy);
1123     gw->requestWarpPointer(wx, wy);
1124     osgGA::GUIEventAdapter* eventState
1125         = cgroup->getViewer()->getEventQueue()->getCurrentEventState();
1126     double viewerX
1127         = (eventState->getXmin()
1128            + ((wx / double(traits->width))
1129               * (eventState->getXmax() - eventState->getXmin())));
1130     double viewerY
1131         = (eventState->getYmin()
1132            + ((wyUp / double(traits->height))
1133               * (eventState->getYmax() - eventState->getYmin())));
1134     cgroup->getViewer()->getEventQueue()->mouseWarped(viewerX, viewerY);
1135 }
1136 }