]> git.mxchange.org Git - flightgear.git/blob - src/Main/CameraGroup.cxx
4c00ab6db4e1509e9af91a313b7bcc1babca0c73
[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     CameraInfo* result = addCamera(cameraFlags, camera, Matrixd::identity(),
269                                    Matrixd::identity(), false);
270     // Disable statistics for the GUI camera.
271     result->camera->setStats(0);
272     return result;
273 }
274
275 CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer,
276                                            const SGPropertyNode* gnode)
277 {
278     CameraGroup* cgroup = new CameraGroup(viewer);
279     for (int i = 0; i < gnode->nChildren(); ++i) {
280         const SGPropertyNode* pNode = gnode->getChild(i);
281         const char* name = pNode->getName();
282         if (!strcmp(name, "camera")) {
283             cgroup->buildCamera(pNode);
284         } else if (!strcmp(name, "window")) {
285             WindowBuilder::getWindowBuilder()->buildWindow(pNode);
286         } else if (!strcmp(name, "gui")) {
287             cgroup->buildGUICamera(pNode);
288         }
289     }
290     return cgroup;
291 }
292
293 Camera* getGUICamera(CameraGroup* cgroup)
294 {
295     CameraGroup::CameraIterator end = cgroup->camerasEnd();
296     CameraGroup::CameraIterator result
297         = std::find_if(cgroup->camerasBegin(), end,
298                        FlagTester<CameraInfo>(CameraGroup::GUI));
299     if (result != end)
300         return (*result)->camera.get();
301     else
302         return 0;
303 }
304
305 bool computeIntersections(const CameraGroup* cgroup,
306                           const osgGA::GUIEventAdapter* ea,
307                           osgUtil::LineSegmentIntersector::Intersections& intersections)
308 {
309     using osgUtil::Intersector;
310     using osgUtil::LineSegmentIntersector;
311     double x, y;
312     eventToWindowCoords(ea, x, y);
313     // Find camera that contains event
314     for (CameraGroup::ConstCameraIterator iter = cgroup->camerasBegin(),
315              e = cgroup->camerasEnd();
316          iter != e;
317          ++iter) {
318         const CameraInfo* cinfo = iter->get();
319         if ((cinfo->flags & CameraGroup::DO_INTERSECTION_TEST) == 0)
320             continue;
321         const Camera* camera = cinfo->camera.get();
322         if (camera->getGraphicsContext() != ea->getGraphicsContext())
323             continue;
324         const Viewport* viewport = camera->getViewport();
325         double epsilon = 0.5;
326         if (!(x >= viewport->x() - epsilon
327               && x < viewport->x() + viewport->width() -1.0 + epsilon
328               && y >= viewport->y() - epsilon
329               && y < viewport->y() + viewport->height() -1.0 + epsilon))
330             continue;
331         LineSegmentIntersector::CoordinateFrame cf = Intersector::WINDOW;
332         ref_ptr<LineSegmentIntersector> picker
333             = new LineSegmentIntersector(cf, x, y);
334         osgUtil::IntersectionVisitor iv(picker.get());
335         const_cast<Camera*>(camera)->accept(iv);
336         if (picker->containsIntersections()) {
337             intersections = picker->getIntersections();
338             return true;
339         } else {
340             break;
341         }
342     }
343     intersections.clear();
344     return false;
345 }
346
347 void warpGUIPointer(CameraGroup* cgroup, int x, int y)
348 {
349     using osgViewer::GraphicsWindow;
350     Camera* guiCamera = getGUICamera(cgroup);
351     if (!guiCamera)
352         return;
353     Viewport* vport = guiCamera->getViewport();
354     GraphicsWindow* gw
355         = dynamic_cast<GraphicsWindow*>(guiCamera->getGraphicsContext());
356     if (!gw)
357         return;
358     globals->get_renderer()->getManipulator()->setMouseWarped();    
359     // Translate the warp request into the viewport of the GUI camera,
360     // send the request to the window, then transform the coordinates
361     // for the Viewer's event queue.
362     double wx = x + vport->x();
363     double wyUp = vport->height() + vport->y() - y;
364     double wy;
365     const GraphicsContext::Traits* traits = gw->getTraits();
366     if (gw->getEventQueue()->getCurrentEventState()->getMouseYOrientation()
367         == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS) {
368         wy = traits->height - wyUp;
369     } else {
370         wy = wyUp;
371     }
372     gw->getEventQueue()->mouseWarped(wx, wy);
373     gw->requestWarpPointer(wx, wy);
374     osgGA::GUIEventAdapter* eventState
375         = cgroup->getViewer()->getEventQueue()->getCurrentEventState();
376     double viewerX
377         = (eventState->getXmin()
378            + ((wx / double(traits->width))
379               * (eventState->getXmax() - eventState->getXmin())));
380     double viewerY
381         = (eventState->getYmin()
382            + ((wyUp / double(traits->height))
383               * (eventState->getYmax() - eventState->getYmin())));
384     cgroup->getViewer()->getEventQueue()->mouseWarped(viewerX, viewerY);
385 }
386 }