1 // Copyright (C) 2008 Tim Moore
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.
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.
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.
17 #include "CameraGroup.hxx"
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>
31 #include <osg/GraphicsContext>
36 #include <osg/Viewport>
38 #include <osgUtil/IntersectionVisitor>
40 #include <osgViewer/GraphicsWindow>
49 ref_ptr<CameraGroup> CameraGroup::_defaultGroup;
51 CameraGroup::CameraGroup(osgViewer::Viewer* viewer) :
56 CameraInfo* CameraGroup::addCamera(unsigned flags, Camera* camera,
58 const Matrix& projection,
59 bool useMasterSceneData)
61 if ((flags & (VIEW_ABSOLUTE | PROJECTION_ABSOLUTE)) != 0)
62 camera->setReferenceFrame(Transform::ABSOLUTE_RF);
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;
72 void CameraGroup::update(const osg::Vec3d& position,
73 const osg::Quat& orientation)
75 FGManipulator *manipulator
76 = dynamic_cast<FGManipulator*>(_viewer->getCameraManipulator());
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
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);
95 camera->setViewMatrix(masterView * slave._viewOffset);
96 if ((info->flags & PROJECTION_ABSOLUTE) != 0)
97 camera->setProjectionMatrix(slave._projectionOffset);
99 camera->setViewMatrix(masterProj * slave._projectionOffset);
103 void CameraGroup::setCameraParameters(float vfov, float aspectRatio)
105 const float zNear = .1f;
106 const float zFar = 120000.0f;
107 _viewer->getCamera()->setProjectionMatrixAsPerspective(vfov,
115 osg::Viewport* buildViewport(const SGPropertyNode* viewportNode)
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);
127 CameraInfo* CameraGroup::buildCamera(const SGPropertyNode* cameraNode)
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;
135 // New style window declaration / definition
136 window = wBuild->buildWindow(windowNode);
138 // Old style: suck window params out of camera block
139 window = wBuild->buildWindow(cameraNode);
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;
153 viewport = buildViewport(viewportNode);
155 const GraphicsContext::Traits *traits = window->gc->getTraits();
156 viewport = new Viewport(0, 0, traits->width, traits->height);
158 camera->setViewport(viewport);
161 const SGPropertyNode* viewNode = cameraNode->getNode("view");
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;
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));
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",
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;
213 pOff.makeOrtho(left, right, bottom, top, zNear, zFar);
214 cameraFlags |= (PROJECTION_ABSOLUTE | ORTHO);
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);
222 return addCamera(cameraFlags, camera, pOff, vOff);
225 CameraInfo* CameraGroup::buildGUICamera(const SGPropertyNode* cameraNode,
226 GraphicsWindow* window)
228 WindowBuilder *wBuild = WindowBuilder::getWindowBuilder();
229 const SGPropertyNode* windowNode = (cameraNode
230 ? cameraNode->getNode("window")
232 static int cameraNum = 0;
235 // New style window declaration / definition
236 window = wBuild->buildWindow(windowNode);
242 Camera* camera = new Camera;
243 camera->setAllowEventFocus(false);
244 camera->setGraphicsContext(window->gc.get());
245 const SGPropertyNode* viewportNode = (cameraNode
246 ? cameraNode->getNode("viewport")
248 Viewport* viewport = 0;
250 viewport = buildViewport(viewportNode);
252 const GraphicsContext::Traits *traits = window->gc->getTraits();
253 viewport = new Viewport(0, 0, traits->width, traits->height);
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);
272 CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer,
273 const SGPropertyNode* gnode)
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);
290 Camera* getGUICamera(CameraGroup* cgroup)
292 CameraGroup::CameraIterator end = cgroup->camerasEnd();
293 CameraGroup::CameraIterator result
294 = std::find_if(cgroup->camerasBegin(), end,
295 FlagTester<CameraInfo>(CameraGroup::GUI));
297 return (*result)->camera.get();
302 bool computeIntersections(const CameraGroup* cgroup,
303 const osgGA::GUIEventAdapter* ea,
304 osgUtil::LineSegmentIntersector::Intersections& intersections)
306 using osgUtil::Intersector;
307 using osgUtil::LineSegmentIntersector;
309 eventToWindowCoords(ea, x, y);
310 // Find camera that contains event
311 for (CameraGroup::ConstCameraIterator iter = cgroup->camerasBegin(),
312 e = cgroup->camerasEnd();
315 const CameraInfo* cinfo = iter->get();
316 if ((cinfo->flags & CameraGroup::DO_INTERSECTION_TEST) == 0)
318 const Camera* camera = cinfo->camera.get();
319 if (camera->getGraphicsContext() != ea->getGraphicsContext())
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))
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();
340 intersections.clear();
344 void warpGUIPointer(CameraGroup* cgroup, int x, int y)
346 using osgViewer::GraphicsWindow;
347 Camera* guiCamera = getGUICamera(cgroup);
350 Viewport* vport = guiCamera->getViewport();
352 = dynamic_cast<GraphicsWindow*>(guiCamera->getGraphicsContext());
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;
362 const GraphicsContext::Traits* traits = gw->getTraits();
363 if (gw->getEventQueue()->getCurrentEventState()->getMouseYOrientation()
364 == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS) {
365 wy = traits->height - wyUp;
369 gw->getEventQueue()->mouseWarped(wx, wy);
370 gw->requestWarpPointer(wx, wy);
371 osgGA::GUIEventAdapter* eventState
372 = cgroup->getViewer()->getEventQueue()->getCurrentEventState();
374 = (eventState->getXmin()
375 + ((wx / double(traits->width))
376 * (eventState->getXmax() - eventState->getXmin())));
378 = (eventState->getYmin()
379 + ((wyUp / double(traits->height))
380 * (eventState->getYmax() - eventState->getYmin())));
381 cgroup->getViewer()->getEventQueue()->mouseWarped(viewerX, viewerY);