]> git.mxchange.org Git - flightgear.git/blob - src/Main/CameraGroup.cxx
James Turner: Improved runway management code:
[flightgear.git] / src / Main / CameraGroup.cxx
1 // Copyright (C) 2008  Tim Moore
2 //
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License as
5 // published by the Free Software Foundation; either version 2 of the
6 // License, or (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful, but
9 // WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16
17 #include "CameraGroup.hxx"
18
19 #include "globals.hxx"
20 #include "renderer.hxx"
21 #include "FGManipulator.hxx"
22 #include "WindowBuilder.hxx"
23 #include "WindowSystemAdapter.hxx"
24 #include <simgear/props/props.hxx>
25
26 #include <algorithm>
27 #include <cstring>
28 #include <string>
29
30 #include <osg/Camera>
31 #include <osg/GraphicsContext>
32 #include <osg/Math>
33 #include <osg/Matrix>
34 #include <osg/Quat>
35 #include <osg/Vec3d>
36 #include <osg/Viewport>
37
38 #include <osgUtil/IntersectionVisitor>
39
40 #include <osgViewer/GraphicsWindow>
41
42 namespace flightgear
43 {
44 using namespace osg;
45
46 using std::strcmp;
47 using std::string;
48
49 ref_ptr<CameraGroup> CameraGroup::_defaultGroup;
50
51 CameraGroup::CameraGroup(osgViewer::Viewer* viewer) :
52     _viewer(viewer)
53 {
54 }
55
56 CameraInfo* CameraGroup::addCamera(unsigned flags, Camera* camera,
57                                    const Matrix& view,
58                                    const Matrix& projection,
59                                    bool useMasterSceneData)
60 {
61     if ((flags & (VIEW_ABSOLUTE | PROJECTION_ABSOLUTE)) != 0)
62         camera->setReferenceFrame(Transform::ABSOLUTE_RF);
63     else
64         camera->setReferenceFrame(Transform::RELATIVE_RF);
65     CameraInfo* info = new CameraInfo(flags, camera);
66     _cameras.push_back(info);
67     _viewer->addSlave(camera, view, projection, useMasterSceneData);
68     info->slaveIndex = _viewer->getNumSlaves() - 1;
69     return info;
70 }
71
72 void CameraGroup::update(const osg::Vec3d& position,
73                          const osg::Quat& orientation)
74 {
75     FGManipulator *manipulator
76         = dynamic_cast<FGManipulator*>(_viewer->getCameraManipulator());
77     if (!manipulator)
78         return;
79     manipulator->setPosition(position);
80     manipulator->setAttitude(orientation);
81     const Matrix masterView(manipulator->getInverseMatrix());
82     const Matrix& masterProj = _viewer->getCamera()->getProjectionMatrix();
83     for (CameraList::iterator i = _cameras.begin(); i != _cameras.end(); ++i) {
84         const CameraInfo* info = i->get();
85         if ((info->flags & (VIEW_ABSOLUTE | PROJECTION_ABSOLUTE)) == 0) {
86             // Camera has relative reference frame and is updated by
87             // osg::View.
88             continue;
89         }
90         const View::Slave& slave = _viewer->getSlave(info->slaveIndex);
91         Camera* camera = info->camera.get();
92         if ((info->flags & VIEW_ABSOLUTE) != 0)
93             camera->setViewMatrix(slave._viewOffset);
94         else
95             camera->setViewMatrix(masterView * slave._viewOffset);
96         if ((info->flags & PROJECTION_ABSOLUTE) != 0)
97             camera->setProjectionMatrix(slave._projectionOffset);
98         else
99             camera->setViewMatrix(masterProj * slave._projectionOffset);
100     }
101 }
102
103 void CameraGroup::setCameraParameters(float vfov, float aspectRatio)
104 {
105     const float zNear = .1f;
106     const float zFar = 120000.0f;
107     _viewer->getCamera()->setProjectionMatrixAsPerspective(vfov,
108                                                            1.0f / aspectRatio,
109                                                            zNear, zFar);
110 }
111 }
112
113 namespace
114 {
115 osg::Viewport* buildViewport(const SGPropertyNode* viewportNode)
116 {
117     double x = viewportNode->getDoubleValue("x", 0.0);
118     double y = viewportNode->getDoubleValue("y", 0.0);
119     double width = viewportNode->getDoubleValue("width", 0.0);
120     double height = viewportNode->getDoubleValue("height", 0.0);
121     return new osg::Viewport(x, y, width, height);
122 }
123 }
124
125 namespace flightgear
126 {
127 CameraInfo* CameraGroup::buildCamera(const SGPropertyNode* cameraNode)
128 {
129     WindowBuilder *wBuild = WindowBuilder::getWindowBuilder();
130     const SGPropertyNode* windowNode = cameraNode->getNode("window");
131     GraphicsWindow* window = 0;
132     static int cameraNum = 0;
133     int cameraFlags = DO_INTERSECTION_TEST;
134     if (windowNode) {
135         // New style window declaration / definition
136         window = wBuild->buildWindow(windowNode);
137     } else {
138         // Old style: suck window params out of camera block
139         window = wBuild->buildWindow(cameraNode);
140     }
141     if (!window) {
142         return 0;
143     }
144     Camera* camera = new Camera;
145     camera->setAllowEventFocus(false);
146     camera->setGraphicsContext(window->gc.get());
147     // If a viewport isn't set on the camera, then it's hard to dig it
148     // out of the SceneView objects in the viewer, and the coordinates
149     // of mouse events are somewhat bizzare.
150     const SGPropertyNode* viewportNode = cameraNode->getNode("viewport");
151     Viewport* viewport = 0;
152     if (viewportNode) {
153         viewport = buildViewport(viewportNode);
154     } else {
155         const GraphicsContext::Traits *traits = window->gc->getTraits();
156         viewport = new Viewport(0, 0, traits->width, traits->height);
157     }
158     camera->setViewport(viewport);
159     osg::Matrix pOff;
160     osg::Matrix vOff;
161     const SGPropertyNode* viewNode = cameraNode->getNode("view");
162     if (viewNode) {
163         double heading = viewNode->getDoubleValue("heading-deg", 0.0);
164         double pitch = viewNode->getDoubleValue("pitch-deg", 0.0);
165         double roll = viewNode->getDoubleValue("roll-deg", 0.0);
166         double x = viewNode->getDoubleValue("x", 0.0);
167         double y = viewNode->getDoubleValue("y", 0.0);
168         double z = viewNode->getDoubleValue("z", 0.0);
169         // Build a view matrix, which is the inverse of a model
170         // orientation matrix.
171         vOff = (Matrix::translate(-x, -y, -z)
172                 * Matrix::rotate(-DegreesToRadians(heading),
173                                  Vec3d(0.0, 1.0, 0.0),
174                                  -DegreesToRadians(pitch),
175                                  Vec3d(1.0, 0.0, 0.0),
176                                  -DegreesToRadians(roll),
177                                  Vec3d(0.0, 0.0, 1.0)));
178         if (viewNode->getBoolValue("absolute", false))
179             cameraFlags |= VIEW_ABSOLUTE;
180     } else {
181         // Old heading parameter, works in the opposite direction
182         double heading = cameraNode->getDoubleValue("heading-deg", 0.0);
183         vOff.makeRotate(DegreesToRadians(heading), osg::Vec3(0, 1, 0));
184     }
185     const SGPropertyNode* projectionNode = 0;
186     if ((projectionNode = cameraNode->getNode("perspective")) != 0) {
187         double fovy = projectionNode->getDoubleValue("fovy-deg", 55.0);
188         double aspectRatio = projectionNode->getDoubleValue("aspect-ratio",
189                                                             1.0);
190         double zNear = projectionNode->getDoubleValue("near", 0.0);
191         double zFar = projectionNode->getDoubleValue("far", 0.0);
192         double offsetX = projectionNode->getDoubleValue("offset-x", 0.0);
193         double offsetY = projectionNode->getDoubleValue("offset-y", 0.0);
194         double tan_fovy = tan(DegreesToRadians(fovy*0.5));
195         double right = tan_fovy * aspectRatio * zNear + offsetX;
196         double left = -tan_fovy * aspectRatio * zNear + offsetX;
197         double top = tan_fovy * zNear + offsetY;
198         double bottom = -tan_fovy * zNear + offsetY;
199         pOff.makeFrustum(left, right, bottom, top, zNear, zFar);
200         cameraFlags |= PROJECTION_ABSOLUTE;
201     } else if ((projectionNode = cameraNode->getNode("frustum")) != 0
202                || (projectionNode = cameraNode->getNode("ortho")) != 0) {
203         double top = projectionNode->getDoubleValue("top", 0.0);
204         double bottom = projectionNode->getDoubleValue("bottom", 0.0);
205         double left = projectionNode->getDoubleValue("left", 0.0);
206         double right = projectionNode->getDoubleValue("right", 0.0);
207         double zNear = projectionNode->getDoubleValue("near", 0.0);
208         double zFar = projectionNode->getDoubleValue("far", 0.0);
209         if (cameraNode->getNode("frustum")) {
210             pOff.makeFrustum(left, right, bottom, top, zNear, zFar);
211             cameraFlags |= PROJECTION_ABSOLUTE;
212         } else {
213             pOff.makeOrtho(left, right, bottom, top, zNear, zFar);
214             cameraFlags |= (PROJECTION_ABSOLUTE | ORTHO);
215         }
216     } else {
217         // old style shear parameters
218         double shearx = cameraNode->getDoubleValue("shear-x", 0);
219         double sheary = cameraNode->getDoubleValue("shear-y", 0);
220         pOff.makeTranslate(-shearx, -sheary, 0);
221     }
222     return addCamera(cameraFlags, camera, pOff, vOff);
223 }
224
225 CameraInfo* CameraGroup::buildGUICamera(const SGPropertyNode* cameraNode,
226                                         GraphicsWindow* window)
227 {
228     WindowBuilder *wBuild = WindowBuilder::getWindowBuilder();
229     const SGPropertyNode* windowNode = (cameraNode
230                                         ? cameraNode->getNode("window")
231                                         : 0);
232     static int cameraNum = 0;
233     if (!window) {
234         if (windowNode) {
235             // New style window declaration / definition
236             window = wBuild->buildWindow(windowNode);
237             
238         } else {
239             return 0;
240         }
241     }
242     Camera* camera = new Camera;
243     camera->setAllowEventFocus(false);
244     camera->setGraphicsContext(window->gc.get());
245     const SGPropertyNode* viewportNode = (cameraNode
246                                           ? cameraNode->getNode("viewport")
247                                           : 0);
248     Viewport* viewport = 0;
249     if (viewportNode) {
250         viewport = buildViewport(viewportNode);
251     } else {
252         const GraphicsContext::Traits *traits = window->gc->getTraits();
253         viewport = new Viewport(0, 0, traits->width, traits->height);
254     }
255     camera->setViewport(viewport);
256     // XXX Camera needs to be drawn last; eventually the render order
257     // should be assigned by a camera manager.
258     camera->setRenderOrder(osg::Camera::POST_RENDER, 100);
259         camera->setClearMask(0);
260     camera->setInheritanceMask(CullSettings::ALL_VARIABLES
261                                & ~(CullSettings::COMPUTE_NEAR_FAR_MODE
262                                    | CullSettings::CULLING_MODE));
263     camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
264     camera->setCullingMode(osg::CullSettings::NO_CULLING);
265     camera->setProjectionResizePolicy(Camera::FIXED);
266     camera->setReferenceFrame(Transform::ABSOLUTE_RF);
267     const int cameraFlags = GUI;
268     return addCamera(cameraFlags, camera,
269                      Matrixd::identity(), Matrixd::identity(), false);
270 }
271
272 CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer,
273                                            const SGPropertyNode* gnode)
274 {
275     CameraGroup* cgroup = new CameraGroup(viewer);
276     for (int i = 0; i < gnode->nChildren(); ++i) {
277         const SGPropertyNode* pNode = gnode->getChild(i);
278         const char* name = pNode->getName();
279         if (!strcmp(name, "camera")) {
280             cgroup->buildCamera(pNode);
281         } else if (!strcmp(name, "window")) {
282             WindowBuilder::getWindowBuilder()->buildWindow(pNode);
283         } else if (!strcmp(name, "gui")) {
284             cgroup->buildGUICamera(pNode);
285         }
286     }
287     return cgroup;
288 }
289
290 Camera* getGUICamera(CameraGroup* cgroup)
291 {
292     CameraGroup::CameraIterator end = cgroup->camerasEnd();
293     CameraGroup::CameraIterator result
294         = std::find_if(cgroup->camerasBegin(), end,
295                        FlagTester<CameraInfo>(CameraGroup::GUI));
296     if (result != end)
297         return (*result)->camera.get();
298     else
299         return 0;
300 }
301
302 bool computeIntersections(const CameraGroup* cgroup,
303                           const osgGA::GUIEventAdapter* ea,
304                           osgUtil::LineSegmentIntersector::Intersections& intersections)
305 {
306     using osgUtil::Intersector;
307     using osgUtil::LineSegmentIntersector;
308     double x, y;
309     eventToWindowCoords(ea, x, y);
310     // Find camera that contains event
311     for (CameraGroup::ConstCameraIterator iter = cgroup->camerasBegin(),
312              e = cgroup->camerasEnd();
313          iter != e;
314          ++iter) {
315         const CameraInfo* cinfo = iter->get();
316         if ((cinfo->flags & CameraGroup::DO_INTERSECTION_TEST) == 0)
317             continue;
318         const Camera* camera = cinfo->camera.get();
319         if (camera->getGraphicsContext() != ea->getGraphicsContext())
320             continue;
321         const Viewport* viewport = camera->getViewport();
322         double epsilon = 0.5;
323         if (!(x >= viewport->x() - epsilon
324               && x < viewport->x() + viewport->width() -1.0 + epsilon
325               && y >= viewport->y() - epsilon
326               && y < viewport->y() + viewport->height() -1.0 + epsilon))
327             continue;
328         LineSegmentIntersector::CoordinateFrame cf = Intersector::WINDOW;
329         ref_ptr<LineSegmentIntersector> picker
330             = new LineSegmentIntersector(cf, x, y);
331         osgUtil::IntersectionVisitor iv(picker.get());
332         const_cast<Camera*>(camera)->accept(iv);
333         if (picker->containsIntersections()) {
334             intersections = picker->getIntersections();
335             return true;
336         } else {
337             break;
338         }
339     }
340     intersections.clear();
341     return false;
342 }
343
344 void warpGUIPointer(CameraGroup* cgroup, int x, int y)
345 {
346     using osgViewer::GraphicsWindow;
347     Camera* guiCamera = getGUICamera(cgroup);
348     if (!guiCamera)
349         return;
350     Viewport* vport = guiCamera->getViewport();
351     GraphicsWindow* gw
352         = dynamic_cast<GraphicsWindow*>(guiCamera->getGraphicsContext());
353     if (!gw)
354         return;
355     globals->get_renderer()->getManipulator()->setMouseWarped();    
356     // Translate the warp request into the viewport of the GUI camera,
357     // send the request to the window, then transform the coordinates
358     // for the Viewer's event queue.
359     double wx = x + vport->x();
360     double wyUp = vport->height() + vport->y() - y;
361     double wy;
362     const GraphicsContext::Traits* traits = gw->getTraits();
363     if (gw->getEventQueue()->getCurrentEventState()->getMouseYOrientation()
364         == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS) {
365         wy = traits->height - wyUp;
366     } else {
367         wy = wyUp;
368     }
369     gw->getEventQueue()->mouseWarped(wx, wy);
370     gw->requestWarpPointer(wx, wy);
371     osgGA::GUIEventAdapter* eventState
372         = cgroup->getViewer()->getEventQueue()->getCurrentEventState();
373     double viewerX
374         = (eventState->getXmin()
375            + ((wx / double(traits->width))
376               * (eventState->getXmax() - eventState->getXmin())));
377     double viewerY
378         = (eventState->getYmin()
379            + ((wyUp / double(traits->height))
380               * (eventState->getYmax() - eventState->getYmin())));
381     cgroup->getViewer()->getEventQueue()->mouseWarped(viewerX, viewerY);
382 }
383 }