1 // Viewer.hxx -- alternative flightgear viewer application
3 // Copyright (C) 2009 - 2012 Mathias Froehlich
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "SlaveCamera.hxx"
25 #include <simgear/scene/util/OsgMath.hxx>
30 #include "HLAViewerFederate.hxx"
31 #include "HLAPerspectiveViewer.hxx"
36 class NoUpdateCallback : public osg::NodeCallback {
38 virtual ~NoUpdateCallback()
40 virtual void operator()(osg::Node* node, osg::NodeVisitor* nodeVisitor)
44 SlaveCamera::SlaveCamera(const std::string& name) :
46 _viewport(new osg::Viewport())
48 _referencePointMap["lowerLeft"] = osg::Vec2(-1, -1);
49 _referencePointMap["lowerRight"] = osg::Vec2(1, -1);
50 _referencePointMap["upperRight"] = osg::Vec2(1, 1);
51 _referencePointMap["upperLeft"] = osg::Vec2(-1, 1);
54 SlaveCamera::~SlaveCamera()
59 SlaveCamera::setDrawableName(const std::string& drawableName)
63 _drawableName = drawableName;
68 SlaveCamera::setViewport(const SGVec4i& viewport)
70 _viewport->setViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
71 _frustum.setAspectRatio(getAspectRatio());
76 SlaveCamera::setViewOffset(const osg::Matrix& viewOffset)
78 _viewOffset = viewOffset;
83 SlaveCamera::setViewOffsetDeg(double headingDeg, double pitchDeg, double rollDeg)
85 osg::Matrix viewOffset = osg::Matrix::identity();
86 viewOffset.postMultRotate(osg::Quat(SGMiscd::deg2rad(headingDeg), osg::Vec3(0, 1, 0)));
87 viewOffset.postMultRotate(osg::Quat(SGMiscd::deg2rad(pitchDeg), osg::Vec3(-1, 0, 0)));
88 viewOffset.postMultRotate(osg::Quat(SGMiscd::deg2rad(rollDeg), osg::Vec3(0, 0, 1)));
89 return setViewOffset(viewOffset);
93 SlaveCamera::setFrustum(const Frustum& frustum)
100 SlaveCamera::setFustumByFieldOfViewDeg(double fieldOfViewDeg)
102 Frustum frustum(getAspectRatio());
103 frustum.setFieldOfViewDeg(fieldOfViewDeg);
108 SlaveCamera::setRelativeFrustum(const std::string names[2], const SlaveCamera& referenceCameraData,
109 const std::string referenceNames[2])
111 // Track the way from one projection space to the other:
114 // where P0 is the projection template sensible for the given window size,
115 // S a scale matrix and T is a translation matrix.
116 // We need to determine T and S so that the reference points in the parents
117 // projection space match the two reference points in this cameras projection space.
119 // Starting from the parents camera projection space, we get into this cameras
120 // projection space by the transform matrix:
121 // P*R*inv(pP*pR) = T2*S*T*P0*R*inv(pP*pR)
122 // So, at first compute that matrix without T2*S*T and determine S and T* from that
124 // The initial projeciton matrix to build upon
125 osg::Matrix P = Frustum(getAspectRatio()).getMatrix();
127 osg::Matrix R = getViewOffset();
128 osg::Matrix pP = referenceCameraData.getFrustum().getMatrix();
129 osg::Matrix pR = referenceCameraData.getViewOffset();
131 // Transform from the reference cameras projection space into this cameras eye space.
132 osg::Matrix pPtoEye = osg::Matrix::inverse(pR*pP)*R;
134 osg::Vec2 pRef[2] = {
135 referenceCameraData.getProjectionReferencePoint(referenceNames[0]),
136 referenceCameraData.getProjectionReferencePoint(referenceNames[1])
139 // The first reference point transformed to this cameras projection space
140 osg::Vec3d pRefInThis0 = P.preMult(pPtoEye.preMult(osg::Vec3d(pRef[0], 1)));
141 // Translate this proejction matrix so that the first reference point is at the origin
142 P.postMultTranslate(-pRefInThis0);
144 // Transform the second reference point and get the scaling correct.
145 osg::Vec3d pRefInThis1 = P.preMult(pPtoEye.preMult(osg::Vec3d(pRef[1], 1)));
146 double s = osg::Vec2d(pRefInThis1[0], pRefInThis1[1]).length();
147 if (s <= std::numeric_limits<double>::min())
150 getProjectionReferencePoint(names[0]),
151 getProjectionReferencePoint(names[1])
153 s = (ref[0] - ref[1]).length()/s;
154 P.postMultScale(osg::Vec3d(s, s, 1));
156 // The first reference point still maps to the origin in this projection space.
157 // Translate the origin to the desired first reference point.
158 P.postMultTranslate(osg::Vec3d(ref[0], 1));
160 // Now osg::Matrix::inverse(pR*pP)*R*P should map pRef[i] exactly onto ref[i] for i = 0, 1.
161 // Note that osg::Matrix::inverse(pR*pP)*R*P should exactly map pRef[0] at the near plane
162 // to ref[0] at the near plane. The far plane is not taken care of.
165 if (!frustum.setMatrix(P))
168 return setFrustum(frustum);
172 SlaveCamera::setProjectionReferencePoint(const std::string& name, const osg::Vec2& point)
174 _referencePointMap[name] = point;
178 SlaveCamera::getProjectionReferencePoint(const std::string& name) const
180 NameReferencePointMap::const_iterator i = _referencePointMap.find(name);
181 if (i != _referencePointMap.end())
183 return osg::Vec2(0, 0);
187 SlaveCamera::setMonitorProjectionReferences(double width, double height,
188 double bezelTop, double bezelBottom,
189 double bezelLeft, double bezelRight)
191 double left = 1 + 2*bezelLeft/width;
192 double right = 1 + 2*bezelRight/width;
194 double bottom = 1 + 2*bezelBottom/height;
195 double top = 1 + 2*bezelTop/height;
197 setProjectionReferencePoint("lowerLeft", osg::Vec2(-left, -bottom));
198 setProjectionReferencePoint("lowerRight", osg::Vec2(right, -bottom));
199 setProjectionReferencePoint("upperRight", osg::Vec2(right, top));
200 setProjectionReferencePoint("upperLeft", osg::Vec2(-left, top));
204 SlaveCamera::getLeftEyeOffset(const Viewer& viewer) const
207 const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
209 return osg::Vec3(0, 0, 0);
210 const HLAPerspectiveViewer* perspectiveViewer = viewerFederate->getViewer();
211 if (!perspectiveViewer)
212 return osg::Vec3(0, 0, 0);
213 return toOsg(perspectiveViewer->getLeftEyeOffset());
215 return osg::Vec3(0, 0, 0);
220 SlaveCamera::getRightEyeOffset(const Viewer& viewer) const
223 const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
225 return osg::Vec3(0, 0, 0);
226 const HLAPerspectiveViewer* perspectiveViewer = viewerFederate->getViewer();
227 if (!perspectiveViewer)
228 return osg::Vec3(0, 0, 0);
229 return toOsg(perspectiveViewer->getRightEyeOffset());
231 return osg::Vec3(0, 0, 0);
236 SlaveCamera::getZoomFactor(const Viewer& viewer) const
239 const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
242 const HLAPerspectiveViewer* perspectiveViewer = viewerFederate->getViewer();
243 if (!perspectiveViewer)
245 return perspectiveViewer->getZoomFactor();
252 SlaveCamera::getEffectiveViewOffset(const Viewer& viewer) const
254 // The eye offset in the master cameras coordinates.
255 osg::Vec3 eyeOffset = getLeftEyeOffset(viewer);
257 // Transform the eye offset into this slaves coordinates
258 eyeOffset = eyeOffset*getViewOffset();
260 // The slaves view matrix is composed of the master matrix
261 osg::Matrix viewOffset = viewer.getCamera()->getViewMatrix();
262 // ... its view offset ...
263 viewOffset.postMult(getViewOffset());
264 // ... and the inverse of the eye offset that is required
265 // to keep the world at the same position wrt the projection walls
266 viewOffset.postMultTranslate(-eyeOffset);
271 SlaveCamera::getEffectiveFrustum(const Viewer& viewer) const
273 // The eye offset in the master cameras coordinates.
274 osg::Vec3 eyeOffset = getLeftEyeOffset(viewer);
276 // Transform the eye offset into this slaves coordinates
277 eyeOffset = eyeOffset*getViewOffset();
279 /// FIXME read that from external
280 osg::Vec3 zoomScaleCenter(0, 0, -1);
281 double zoomFactor = getZoomFactor(viewer);
283 /// Transform into the local cameras orientation.
284 zoomScaleCenter = getViewOffset().preMult(zoomScaleCenter);
286 // The unmodified frustum
287 Frustum frustum = getFrustum();
289 // For unresized views this is a noop
290 frustum.setAspectRatio(getAspectRatio());
292 // need to correct this for the eye position within the projection system
293 frustum.translate(-eyeOffset);
295 // Scale the whole geometric extent of the projection surfaces by the zoom factor
296 frustum.scale(1/zoomFactor, zoomScaleCenter);
302 SlaveCamera::realize(Viewer& viewer)
306 _camera = _realizeImplementation(viewer);
307 return _camera.valid();
311 SlaveCamera::update(Viewer& viewer)
313 return _updateImplementation(viewer);
317 SlaveCamera::_realizeImplementation(Viewer& viewer)
319 Drawable* drawable = viewer.getDrawable(_drawableName);
322 osg::GraphicsContext* graphicsContext = drawable->getGraphicsContext();
323 if (!graphicsContext)
326 osg::Camera* camera = new osg::Camera;
327 camera->setName(getName());
328 camera->setGraphicsContext(graphicsContext);
329 camera->setViewport(_viewport.get());
330 camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
332 // Not seriously consider someting different
333 camera->setDrawBuffer(GL_BACK);
334 camera->setReadBuffer(GL_BACK);
336 camera->setUpdateCallback(new NoUpdateCallback);
342 SlaveCamera::_updateImplementation(Viewer& viewer)
344 if (!_camera.valid())
349 } // namespace fgviewer