]> git.mxchange.org Git - flightgear.git/blob - utils/fgviewer/SlaveCamera.cxx
41fc51669a8ec1d09e4ca63245cae3266ef7272d
[flightgear.git] / utils / fgviewer / SlaveCamera.cxx
1 // Viewer.hxx -- alternative flightgear viewer application
2 //
3 // Copyright (C) 2009 - 2012  Mathias Froehlich
4 //
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.
9 //
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.
14 //
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.
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "SlaveCamera.hxx"
24
25 #include <simgear/scene/util/OsgMath.hxx>
26
27 #include "Viewer.hxx"
28
29 #ifdef FG_HAVE_HLA
30 #include "HLAViewerFederate.hxx"    
31 #include "HLAPerspectiveViewer.hxx"    
32 #endif
33
34 namespace fgviewer  {
35
36 class NoUpdateCallback : public osg::NodeCallback {
37 public:
38     virtual ~NoUpdateCallback()
39     { }
40     virtual void operator()(osg::Node* node, osg::NodeVisitor* nodeVisitor)
41     { }
42 };
43
44 SlaveCamera::SlaveCamera(const std::string& name) :
45     _name(name),
46     _viewport(new osg::Viewport())
47 {
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);
52 }
53
54 SlaveCamera::~SlaveCamera()
55 {
56 }
57
58 bool
59 SlaveCamera::setDrawableName(const std::string& drawableName)
60 {
61     if (_camera.valid())
62         return false;
63     _drawableName = drawableName;
64     return true;
65 }
66
67 bool
68 SlaveCamera::setViewport(const SGVec4i& viewport)
69 {
70     _viewport->setViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
71     _frustum.setAspectRatio(getAspectRatio());
72     return true;
73 }
74
75 bool
76 SlaveCamera::setViewOffset(const osg::Matrix& viewOffset)
77 {
78     _viewOffset = viewOffset;
79     return true;
80 }
81
82 bool
83 SlaveCamera::setViewOffsetDeg(double headingDeg, double pitchDeg, double rollDeg)
84 {
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);
90 }
91
92 bool
93 SlaveCamera::setFrustum(const Frustum& frustum)
94 {
95     _frustum = frustum;
96     return true;
97 }
98
99 void
100 SlaveCamera::setFustumByFieldOfViewDeg(double fieldOfViewDeg)
101 {
102     Frustum frustum(getAspectRatio());
103     frustum.setFieldOfViewDeg(fieldOfViewDeg);
104     setFrustum(frustum);
105 }
106
107 bool
108 SlaveCamera::setRelativeFrustum(const std::string names[2], const SlaveCamera& referenceCameraData,
109                                 const std::string referenceNames[2])
110 {
111     // Track the way from one projection space to the other:
112     // We want
113     //  P = T2*S*T*P0
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.
118     
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
123     
124     // The initial projeciton matrix to build upon
125     osg::Matrix P = Frustum(getAspectRatio()).getMatrix();
126
127     osg::Matrix R = getViewOffset();
128     osg::Matrix pP = referenceCameraData.getFrustum().getMatrix();
129     osg::Matrix pR = referenceCameraData.getViewOffset();
130     
131     // Transform from the reference cameras projection space into this cameras eye space.
132     osg::Matrix pPtoEye = osg::Matrix::inverse(pR*pP)*R;
133     
134     osg::Vec2 pRef[2] = {
135         referenceCameraData.getProjectionReferencePoint(referenceNames[0]),
136         referenceCameraData.getProjectionReferencePoint(referenceNames[1])
137     };
138     
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);
143     
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())
148         return false;
149     osg::Vec2 ref[2] = {
150         getProjectionReferencePoint(names[0]),
151         getProjectionReferencePoint(names[1])
152     };
153     s = (ref[0] - ref[1]).length()/s;
154     P.postMultScale(osg::Vec3d(s, s, 1));
155     
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));
159     
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.
163     
164     Frustum frustum;
165     if (!frustum.setMatrix(P))
166         return false;
167
168     return setFrustum(frustum);
169 }
170
171 void
172 SlaveCamera::setProjectionReferencePoint(const std::string& name, const osg::Vec2& point)
173 {
174     _referencePointMap[name] = point;
175 }
176
177 osg::Vec2
178 SlaveCamera::getProjectionReferencePoint(const std::string& name) const
179 {
180     NameReferencePointMap::const_iterator i = _referencePointMap.find(name);
181     if (i != _referencePointMap.end())
182         return i->second;
183     return osg::Vec2(0, 0);
184 }
185
186 void
187 SlaveCamera::setMonitorProjectionReferences(double width, double height,
188                                             double bezelTop, double bezelBottom,
189                                             double bezelLeft, double bezelRight)
190 {
191     double left = 1 + 2*bezelLeft/width;
192     double right = 1 + 2*bezelRight/width;
193     
194     double bottom = 1 + 2*bezelBottom/height;
195     double top = 1 + 2*bezelTop/height;
196
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));
201 }
202    
203 osg::Vec3
204 SlaveCamera::getLeftEyeOffset(const Viewer& viewer) const
205 {
206 #ifdef FG_HAVE_HLA
207     const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
208     if (!viewerFederate)
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());
214 #else
215     return osg::Vec3(0, 0, 0);
216 #endif
217 }
218
219 osg::Vec3
220 SlaveCamera::getRightEyeOffset(const Viewer& viewer) const
221 {
222 #ifdef FG_HAVE_HLA
223     const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
224     if (!viewerFederate)
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());
230 #else
231     return osg::Vec3(0, 0, 0);
232 #endif
233 }
234
235 double
236 SlaveCamera::getZoomFactor(const Viewer& viewer) const
237 {
238 #ifdef FG_HAVE_HLA
239     const HLAViewerFederate* viewerFederate = viewer.getViewerFederate();
240     if (!viewerFederate)
241         return 1;
242     const HLAPerspectiveViewer* perspectiveViewer = viewerFederate->getViewer();
243     if (!perspectiveViewer)
244         return 1;
245     return perspectiveViewer->getZoomFactor();
246 #else
247     return 1;
248 #endif
249 }
250
251 osg::Matrix
252 SlaveCamera::getEffectiveViewOffset(const Viewer& viewer) const
253 {
254     // The eye offset in the master cameras coordinates.
255     osg::Vec3 eyeOffset = getLeftEyeOffset(viewer);
256
257     // Transform the eye offset into this slaves coordinates
258     eyeOffset = eyeOffset*getViewOffset();
259     
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);
267     return viewOffset;
268 }
269
270 Frustum
271 SlaveCamera::getEffectiveFrustum(const Viewer& viewer) const
272 {
273     // The eye offset in the master cameras coordinates.
274     osg::Vec3 eyeOffset = getLeftEyeOffset(viewer);
275     
276     // Transform the eye offset into this slaves coordinates
277     eyeOffset = eyeOffset*getViewOffset();
278     
279     /// FIXME read that from external
280     osg::Vec3 zoomScaleCenter(0, 0, -1);
281     double zoomFactor = getZoomFactor(viewer);
282     
283     /// Transform into the local cameras orientation.
284     zoomScaleCenter = getViewOffset().preMult(zoomScaleCenter);
285
286     // The unmodified frustum
287     Frustum frustum = getFrustum();
288
289     // For unresized views this is a noop
290     frustum.setAspectRatio(getAspectRatio());
291
292     // need to correct this for the eye position within the projection system
293     frustum.translate(-eyeOffset);
294
295     // Scale the whole geometric extent of the projection surfaces by the zoom factor
296     frustum.scale(1/zoomFactor, zoomScaleCenter);
297     
298     return frustum;
299 }
300
301 bool
302 SlaveCamera::realize(Viewer& viewer)
303 {
304     if (_camera.valid())
305         return false;
306     _camera = _realizeImplementation(viewer);
307     return _camera.valid();
308 }
309     
310 bool
311 SlaveCamera::update(Viewer& viewer)
312 {
313     return _updateImplementation(viewer);
314 }
315     
316 osg::Camera*
317 SlaveCamera::_realizeImplementation(Viewer& viewer)
318 {
319     Drawable* drawable = viewer.getDrawable(_drawableName);
320     if (!drawable)
321         return 0;
322     osg::GraphicsContext* graphicsContext = drawable->getGraphicsContext();
323     if (!graphicsContext)
324         return 0;
325
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);
331     
332     // Not seriously consider someting different
333     camera->setDrawBuffer(GL_BACK);
334     camera->setReadBuffer(GL_BACK);
335
336     camera->setUpdateCallback(new NoUpdateCallback);
337     
338     return camera;
339 }
340
341 bool
342 SlaveCamera::_updateImplementation(Viewer& viewer)
343 {
344     if (!_camera.valid())
345         return false;
346     return true;
347 }
348
349 } // namespace fgviewer