diff --git a/docs-mini/README.multiscreen b/docs-mini/README.multiscreen new file mode 100644 index 000000000..9954f13f9 --- /dev/null +++ b/docs-mini/README.multiscreen @@ -0,0 +1,315 @@ +The Open Scene Graph library, which current FlightGear uses for its 3D +graphics, provides excellent support for multiple views of a +scene. FlightGear uses the osgViewer::Viewer class, which implements a +"master" camera with "slave" cameras that are offset from the master's +position and orientation. FlightGear provides the "camera group" +abstraction which allows the configuration of slave cameras via the +property tree. + +Slave cameras can be mapped to windows that are open on different +screens, or all in one window, or a combination of those two schemes, +according to the video hardware capabilities of a machine. It is not +advisable to open more than one window on a single graphics card due +to the added cost of OpenGL context switching between the +windows. Usually, multiple monitors attached to a single graphics card +are mapped to different pieces of the same desktop, so a window can be +opened that spans all the monitors. This is implemented by Nvidia's +TwinView technology and the Matrox TripleHead2Go hardware. + +The camera group is configured by the /sim/rendering/camera-group node +in the property tree. It can be set up by, among other things, XML in +preferences.xml or in an XML file specified on the command line with +the --config option. + +Here are the XML tags for defining camera groups. + +camera-group +For the moment there can be only one camera group. It can contain +window, camera, or gui tags. + + window + A window defines a graphics window. It can be at the camera-group + level or defined within a camera. The window contains these tags: + + name - string + The name of the window which might be displayed in the window's + title bar. It is also used to refer to a previously defined + window. A window can contain just a name node, in which case + the whole window definition refers to a previously defined window. + + host-name - string + The name of the host on which the window is opened. Usually this is + empty. + + display - int + The display number on which the window is opened. + + screen - int + The screen number on which the window is opened. + + x, y - int + The location on the screen at which the window is opened. This is in + the window system coordinates, which usually puts 0,0 at the upper + left of the screen XXX check this for Windows. + + width, height - int + The dimensions of the window. + + decoration - bool + Whether the window manager should decorate the window. + + fullscreen - bool + Shorthand for a window that occupies the entire screen with no + decoration. + + camera + The camera node contains viewing parameters. + + window + This specifies the window which displays the camera. Either it + contains just a name that refers to a previous window definition, or + it is a full window definition. + + viewport + The viewport positions a camera within a window. It is most useful + when several cameras share a window. + + x, y - int + The position of the lower left corner of the viewport, in y-up + coordinates. + + width, height - int + The dimensions of the viewport + + view + The view node specifies the origin and direction of the camera in + relation to the whole camera group. The coordinate system is +y up, + -z forward in the direction of the camera group view. This is the + same as the OpenGL viewing coordinates. + + x,y,z - double + Coordinates of the view origin. + + heading-deg, pitch-deg, roll-deg - double + Orientation of the view in degrees. These are specified using the + right-hand rule, so a positive heading turns the view to the left, + a positive roll rolls the view to the left. + + perspective + This node is one way of specifying the viewing volume camera + parameters. It corresponds to the OpenGL gluPerspective function. + + fovy-deg - double + The vertical field-of-view + + aspect-ratio - double + Aspect ratio of camera rectangle (not the ratio between the + vertical and horizontal fields of view). + + near, far - double + The near and far planes, in meters from the camera eye point. Note + that FlightGear assumes that the far plane is far away, currently + 120km. The far plane specified here will be respected, but the sky + and other background elements may not be drawn if the view plane is + closer than 120km. + + offset-x, offset-y - double + Offsets of the viewing volume specified by the other parameters in + the near plane, in meters. + + frustum + This specifies the perspective viewing volume using values for the near + and far planes and coordinates of the viewing rectangle in the near + plane. + + left, bottom - double + right, top - double + The coordinates of the viewing rectangle. + + near, far - double + The near and far planes, in meters from the camera eye point. + + ortho + This specifies an orthographic view. The parameters are the sames as + the frustum node's. + + gui + This is a special camera node that displays the 2D GUI. + + viewport + This specifies the position and dimensions of the GUI within a + window, *however* at the moment the origin must be at 0,0. + +Here's an example that uses a single window mapped across 3 +displays. The displays are in a video wall configuration in a +horizontal row. + + + + + + + wide + + 0 + 0 + 3840 + 1024 + false + + + + wide + + + 0 + 0 + 1280 + 1024 + + + 0 + + + 0.133 + -0.133 + -.5004 + -.1668 + 0.4 + 120000.0 + + + + + wide + + + 1280 + 0 + 1280 + 1024 + + + 0 + + + 0.133 + -0.133 + -.1668 + .1668 + 0.4 + 120000.0 + + + + + wide + + + 2560 + 0 + 1280 + 1024 + + + 0 + + + 0.133 + -0.133 + .1668 + .5004 + 0.4 + 120000.0 + + + + + wide + + + + + + + +Here's a complete example that uses a seperate window on each +display. The displays are arranged in a shallow arc with the left and +right displays at a 45.3 degree angle to the center display because, +at the assumed screen dimensions, the horizontal field of view of one +display is 45.3 degrees. Each camera has its own window definition; +the center window is given the name "main" so that the GUI definition +can refer to it. Note that the borders of the displays are not +accounted for. + + + + + + + + + 0 + 0 + true + + + 45.3 + + + 0.133 + -0.133 + -.1668 + .1668 + 0.4 + 120000.0 + + + + + main + + 0 + 1 + true + + + 0 + + + 0.133 + -0.133 + -.1668 + .1668 + 0.4 + 120000.0 + + + + + + 0 + 2 + true + + + -45.3 + + + 0.133 + -0.133 + -.1668 + .1668 + 0.4 + 120000.0 + + + + + main + + + + + + diff --git a/src/GUI/gui.cxx b/src/GUI/gui.cxx index 77c98d9ed..2e8305ba4 100644 --- a/src/GUI/gui.cxx +++ b/src/GUI/gui.cxx @@ -49,7 +49,8 @@ #include "gui.h" #include "layout.hxx" -using namespace osg; +#include + using namespace flightgear; puFont guiFnt = 0; @@ -67,7 +68,7 @@ public: GUIInitOperation() : GraphicsContextOperation(std::string("GUI init")) { } - void run(GraphicsContext* gc) + void run(osg::GraphicsContext* gc) { WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA(); wsa->puInitialize(); @@ -96,21 +97,21 @@ public: } }; -ref_ptr initOp; +osg::ref_ptr initOp; } -void guiStartInit() +void guiStartInit(osg::GraphicsContext* gc) { - initOp = new GUIInitOperation; - WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA(); - GraphicsContext* gc = wsa->getGUIGraphicsContext(); - gc->add(initOp.get()); + if (gc) { + initOp = new GUIInitOperation; + gc->add(initOp.get()); + } } bool guiFinishInit() { if (!initOp.valid()) - return false; + return true; if (!initOp->isFinished()) return false; initOp = 0; diff --git a/src/GUI/gui.h b/src/GUI/gui.h index aeb786167..5b8478e95 100644 --- a/src/GUI/gui.h +++ b/src/GUI/gui.h @@ -35,9 +35,12 @@ #define TR_HIRES_SNAP 1 - +namespace osg +{ +class GraphicsContext; +} // gui.cxx -extern void guiStartInit(); +extern void guiStartInit(osg::GraphicsContext*); extern bool guiFinishInit(); extern void mkDialog(const char *txt); extern void guiErrorMessage(const char *txt); diff --git a/src/Input/input.cxx b/src/Input/input.cxx index 7197b27f9..af71173b6 100644 --- a/src/Input/input.cxx +++ b/src/Input/input.cxx @@ -342,7 +342,7 @@ FGInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const o // The nearest one is the first one and the deepest // (the most specialized one in the scenegraph) is the first. std::vector pickList; - if (FGRenderer::pick(x, y, pickList, ea)) { + if (FGRenderer::pick(pickList, ea)) { std::vector::const_iterator i; for (i = pickList.begin(); i != pickList.end(); ++i) { if (i->callback->buttonPressed(b, i->info)) { diff --git a/src/Main/CameraGroup.cxx b/src/Main/CameraGroup.cxx new file mode 100644 index 000000000..b5298d637 --- /dev/null +++ b/src/Main/CameraGroup.cxx @@ -0,0 +1,383 @@ +// Copyright (C) 2008 Tim Moore +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "CameraGroup.hxx" + +#include "globals.hxx" +#include "renderer.hxx" +#include "FGManipulator.hxx" +#include "WindowBuilder.hxx" +#include "WindowSystemAdapter.hxx" +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace flightgear +{ +using namespace osg; + +using std::strcmp; +using std::string; + +ref_ptr CameraGroup::_defaultGroup; + +CameraGroup::CameraGroup(osgViewer::Viewer* viewer) : + _viewer(viewer) +{ +} + +CameraInfo* CameraGroup::addCamera(unsigned flags, Camera* camera, + const Matrix& view, + const Matrix& projection, + bool useMasterSceneData) +{ + if ((flags & (VIEW_ABSOLUTE | PROJECTION_ABSOLUTE)) != 0) + camera->setReferenceFrame(Transform::ABSOLUTE_RF); + else + camera->setReferenceFrame(Transform::RELATIVE_RF); + CameraInfo* info = new CameraInfo(flags, camera); + _cameras.push_back(info); + _viewer->addSlave(camera, view, projection, useMasterSceneData); + info->slaveIndex = _viewer->getNumSlaves() - 1; + return info; +} + +void CameraGroup::update(const osg::Vec3d& position, + const osg::Quat& orientation) +{ + FGManipulator *manipulator + = dynamic_cast(_viewer->getCameraManipulator()); + if (!manipulator) + return; + manipulator->setPosition(position); + manipulator->setAttitude(orientation); + const Matrix masterView(manipulator->getInverseMatrix()); + const Matrix& masterProj = _viewer->getCamera()->getProjectionMatrix(); + for (CameraList::iterator i = _cameras.begin(); i != _cameras.end(); ++i) { + const CameraInfo* info = i->get(); + if ((info->flags & (VIEW_ABSOLUTE | PROJECTION_ABSOLUTE)) == 0) { + // Camera has relative reference frame and is updated by + // osg::View. + continue; + } + const View::Slave& slave = _viewer->getSlave(info->slaveIndex); + Camera* camera = info->camera.get(); + if ((info->flags & VIEW_ABSOLUTE) != 0) + camera->setViewMatrix(slave._viewOffset); + else + camera->setViewMatrix(masterView * slave._viewOffset); + if ((info->flags & PROJECTION_ABSOLUTE) != 0) + camera->setProjectionMatrix(slave._projectionOffset); + else + camera->setViewMatrix(masterProj * slave._projectionOffset); + } +} + +void CameraGroup::setCameraParameters(float vfov, float aspectRatio) +{ + const float zNear = .1f; + const float zFar = 120000.0f; + _viewer->getCamera()->setProjectionMatrixAsPerspective(vfov, + 1.0f / aspectRatio, + zNear, zFar); +} +} + +namespace +{ +osg::Viewport* buildViewport(const SGPropertyNode* viewportNode) +{ + double x = viewportNode->getDoubleValue("x", 0.0); + double y = viewportNode->getDoubleValue("y", 0.0); + double width = viewportNode->getDoubleValue("width", 0.0); + double height = viewportNode->getDoubleValue("height", 0.0); + return new osg::Viewport(x, y, width, height); +} +} + +namespace flightgear +{ +CameraInfo* CameraGroup::buildCamera(const SGPropertyNode* cameraNode) +{ + WindowBuilder *wBuild = WindowBuilder::getWindowBuilder(); + const SGPropertyNode* windowNode = cameraNode->getNode("window"); + GraphicsWindow* window = 0; + static int cameraNum = 0; + int cameraFlags = DO_INTERSECTION_TEST; + if (windowNode) { + // New style window declaration / definition + window = wBuild->buildWindow(windowNode); + } else { + // Old style: suck window params out of camera block + window = wBuild->buildWindow(cameraNode); + } + if (!window) { + return 0; + } + Camera* camera = new Camera; + camera->setAllowEventFocus(false); + camera->setGraphicsContext(window->gc.get()); + // If a viewport isn't set on the camera, then it's hard to dig it + // out of the SceneView objects in the viewer, and the coordinates + // of mouse events are somewhat bizzare. + const SGPropertyNode* viewportNode = cameraNode->getNode("viewport"); + Viewport* viewport = 0; + if (viewportNode) { + viewport = buildViewport(viewportNode); + } else { + const GraphicsContext::Traits *traits = window->gc->getTraits(); + viewport = new Viewport(0, 0, traits->width, traits->height); + } + camera->setViewport(viewport); + osg::Matrix pOff; + osg::Matrix vOff; + const SGPropertyNode* viewNode = cameraNode->getNode("view"); + if (viewNode) { + double heading = viewNode->getDoubleValue("heading-deg", 0.0); + double pitch = viewNode->getDoubleValue("pitch-deg", 0.0); + double roll = viewNode->getDoubleValue("roll-deg", 0.0); + double x = viewNode->getDoubleValue("x", 0.0); + double y = viewNode->getDoubleValue("y", 0.0); + double z = viewNode->getDoubleValue("z", 0.0); + // Build a view matrix, which is the inverse of a model + // orientation matrix. + vOff = (Matrix::translate(-x, -y, -z) + * Matrix::rotate(-DegreesToRadians(heading), + Vec3d(0.0, 1.0, 0.0), + -DegreesToRadians(pitch), + Vec3d(1.0, 0.0, 0.0), + -DegreesToRadians(roll), + Vec3d(0.0, 0.0, 1.0))); + if (viewNode->getBoolValue("absolute", false)) + cameraFlags |= VIEW_ABSOLUTE; + } else { + // Old heading parameter, works in the opposite direction + double heading = cameraNode->getDoubleValue("heading-deg", 0.0); + vOff.makeRotate(DegreesToRadians(heading), osg::Vec3(0, 1, 0)); + } + const SGPropertyNode* projectionNode = 0; + if ((projectionNode = cameraNode->getNode("perspective")) != 0) { + double fovy = projectionNode->getDoubleValue("fovy-deg", 55.0); + double aspectRatio = projectionNode->getDoubleValue("aspect-ratio", + 1.0); + double zNear = projectionNode->getDoubleValue("near", 0.0); + double zFar = projectionNode->getDoubleValue("far", 0.0); + double offsetX = projectionNode->getDoubleValue("offset-x", 0.0); + double offsetY = projectionNode->getDoubleValue("offset-y", 0.0); + double tan_fovy = tan(DegreesToRadians(fovy*0.5)); + double right = tan_fovy * aspectRatio * zNear + offsetX; + double left = -tan_fovy * aspectRatio * zNear + offsetX; + double top = tan_fovy * zNear + offsetY; + double bottom = -tan_fovy * zNear + offsetY; + pOff.makeFrustum(left, right, bottom, top, zNear, zFar); + cameraFlags |= PROJECTION_ABSOLUTE; + } else if ((projectionNode = cameraNode->getNode("frustum")) != 0 + || (projectionNode = cameraNode->getNode("ortho")) != 0) { + double top = projectionNode->getDoubleValue("top", 0.0); + double bottom = projectionNode->getDoubleValue("bottom", 0.0); + double left = projectionNode->getDoubleValue("left", 0.0); + double right = projectionNode->getDoubleValue("right", 0.0); + double zNear = projectionNode->getDoubleValue("near", 0.0); + double zFar = projectionNode->getDoubleValue("far", 0.0); + if (cameraNode->getNode("frustum")) { + pOff.makeFrustum(left, right, bottom, top, zNear, zFar); + cameraFlags |= PROJECTION_ABSOLUTE; + } else { + pOff.makeOrtho(left, right, bottom, top, zNear, zFar); + cameraFlags |= (PROJECTION_ABSOLUTE | ORTHO); + } + } else { + // old style shear parameters + double shearx = cameraNode->getDoubleValue("shear-x", 0); + double sheary = cameraNode->getDoubleValue("shear-y", 0); + pOff.makeTranslate(-shearx, -sheary, 0); + } + return addCamera(cameraFlags, camera, pOff, vOff); +} + +CameraInfo* CameraGroup::buildGUICamera(const SGPropertyNode* cameraNode, + GraphicsWindow* window) +{ + WindowBuilder *wBuild = WindowBuilder::getWindowBuilder(); + const SGPropertyNode* windowNode = (cameraNode + ? cameraNode->getNode("window") + : 0); + static int cameraNum = 0; + if (!window) { + if (windowNode) { + // New style window declaration / definition + window = wBuild->buildWindow(windowNode); + + } else { + return 0; + } + } + Camera* camera = new Camera; + camera->setAllowEventFocus(false); + camera->setGraphicsContext(window->gc.get()); + const SGPropertyNode* viewportNode = (cameraNode + ? cameraNode->getNode("viewport") + : 0); + Viewport* viewport = 0; + if (viewportNode) { + viewport = buildViewport(viewportNode); + } else { + const GraphicsContext::Traits *traits = window->gc->getTraits(); + viewport = new Viewport(0, 0, traits->width, traits->height); + } + camera->setViewport(viewport); + // XXX Camera needs to be drawn last; eventually the render order + // should be assigned by a camera manager. + camera->setRenderOrder(osg::Camera::POST_RENDER, 100); + camera->setClearMask(0); + camera->setInheritanceMask(CullSettings::ALL_VARIABLES + & ~(CullSettings::COMPUTE_NEAR_FAR_MODE + | CullSettings::CULLING_MODE)); + camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + camera->setCullingMode(osg::CullSettings::NO_CULLING); + camera->setProjectionResizePolicy(Camera::FIXED); + camera->setReferenceFrame(Transform::ABSOLUTE_RF); + const int cameraFlags = GUI; + return addCamera(cameraFlags, camera, + Matrixd::identity(), Matrixd::identity(), false); +} + +CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer, + const SGPropertyNode* gnode) +{ + CameraGroup* cgroup = new CameraGroup(viewer); + for (int i = 0; i < gnode->nChildren(); ++i) { + const SGPropertyNode* pNode = gnode->getChild(i); + const char* name = pNode->getName(); + if (!strcmp(name, "camera")) { + cgroup->buildCamera(pNode); + } else if (!strcmp(name, "window")) { + WindowBuilder::getWindowBuilder()->buildWindow(pNode); + } else if (!strcmp(name, "gui")) { + cgroup->buildGUICamera(pNode); + } + } + return cgroup; +} + +Camera* getGUICamera(CameraGroup* cgroup) +{ + CameraGroup::CameraIterator end = cgroup->camerasEnd(); + CameraGroup::CameraIterator result + = std::find_if(cgroup->camerasBegin(), end, + FlagTester(CameraGroup::GUI)); + if (result != end) + return (*result)->camera.get(); + else + return 0; +} + +bool computeIntersections(const CameraGroup* cgroup, + const osgGA::GUIEventAdapter* ea, + osgUtil::LineSegmentIntersector::Intersections& intersections) +{ + using osgUtil::Intersector; + using osgUtil::LineSegmentIntersector; + double x, y; + eventToWindowCoords(ea, x, y); + // Find camera that contains event + for (CameraGroup::ConstCameraIterator iter = cgroup->camerasBegin(), + e = cgroup->camerasEnd(); + iter != e; + ++iter) { + const CameraInfo* cinfo = iter->get(); + if ((cinfo->flags & CameraGroup::DO_INTERSECTION_TEST) == 0) + continue; + const Camera* camera = cinfo->camera.get(); + if (camera->getGraphicsContext() != ea->getGraphicsContext()) + continue; + const Viewport* viewport = camera->getViewport(); + double epsilon = 0.5; + if (!(x >= viewport->x() - epsilon + && x < viewport->x() + viewport->width() -1.0 + epsilon + && y >= viewport->y() - epsilon + && y < viewport->y() + viewport->height() -1.0 + epsilon)) + continue; + LineSegmentIntersector::CoordinateFrame cf = Intersector::WINDOW; + ref_ptr picker + = new LineSegmentIntersector(cf, x, y); + osgUtil::IntersectionVisitor iv(picker.get()); + const_cast(camera)->accept(iv); + if (picker->containsIntersections()) { + intersections = picker->getIntersections(); + return true; + } else { + break; + } + } + intersections.clear(); + return false; +} + +void warpGUIPointer(CameraGroup* cgroup, int x, int y) +{ + using osgViewer::GraphicsWindow; + Camera* guiCamera = getGUICamera(cgroup); + if (!guiCamera) + return; + Viewport* vport = guiCamera->getViewport(); + GraphicsWindow* gw + = dynamic_cast(guiCamera->getGraphicsContext()); + if (!gw) + return; + globals->get_renderer()->getManipulator()->setMouseWarped(); + // Translate the warp request into the viewport of the GUI camera, + // send the request to the window, then transform the coordinates + // for the Viewer's event queue. + double wx = x + vport->x(); + double wyUp = vport->height() + vport->y() - y; + double wy; + const GraphicsContext::Traits* traits = gw->getTraits(); + if (gw->getEventQueue()->getCurrentEventState()->getMouseYOrientation() + == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS) { + wy = traits->height - wyUp; + } else { + wy = wyUp; + } + gw->getEventQueue()->mouseWarped(wx, wy); + gw->requestWarpPointer(wx, wy); + osgGA::GUIEventAdapter* eventState + = cgroup->getViewer()->getEventQueue()->getCurrentEventState(); + double viewerX + = (eventState->getXmin() + + ((wx / double(traits->width)) + * (eventState->getXmax() - eventState->getXmin()))); + double viewerY + = (eventState->getYmin() + + ((wyUp / double(traits->height)) + * (eventState->getYmax() - eventState->getYmin()))); + cgroup->getViewer()->getEventQueue()->mouseWarped(viewerX, viewerY); +} +} diff --git a/src/Main/CameraGroup.hxx b/src/Main/CameraGroup.hxx new file mode 100644 index 000000000..e76f3a5fa --- /dev/null +++ b/src/Main/CameraGroup.hxx @@ -0,0 +1,198 @@ +// Copyright (C) 2008 Tim Moore +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CAMERAGROUP_HXX +#define CAMERAGROUP_HXX 1 + +#include +#include + +#include +#include +#include + +// For osgUtil::LineSegmentIntersector::Intersections, which is a typedef. +#include +namespace osg +{ +class Camera; +} + +namespace osgViewer +{ +class Viewer; +} + +class SGPropertyNode; + +namespace flightgear +{ + +class GraphicsWindow; + +/** A wrapper around osg::Camera that contains some extra information. + */ +struct CameraInfo : public osg::Referenced +{ + CameraInfo(unsigned flags_, osg::Camera* camera_) + : flags(flags_), camera(camera_), slaveIndex(-1) + { + } + /** Properties of the camera. @see CameraGroup::Flags. + */ + unsigned flags; + /** the camera object + */ + osg::ref_ptr camera; + /** Index of this camera in the osgViewer::Viewer slave list. + */ + int slaveIndex; +}; + +class CameraGroup : public osg::Referenced +{ +public: + /** properties of a camera. + */ + enum Flags + { + VIEW_ABSOLUTE = 0x1, /**< The camera view is absolute, not + relative to the master camera. */ + PROJECTION_ABSOLUTE = 0x2, /**< The projection is absolute. */ + ORTHO = 0x4, /**< The projection is orthographic */ + GUI = 0x8, /**< Camera draws the GUI. */ + DO_INTERSECTION_TEST = 0x10 /**< scene intersection tests this + camera. */ + }; + /** Create a camera group associated with an osgViewer::Viewer. + * @param viewer the viewer + */ + CameraGroup(osgViewer::Viewer* viewer); + /** Get the camera group's Viewer. + * @return the viewer + */ + osgViewer::Viewer* getViewer() { return _viewer.get(); } + /** Add a camera to the group. The camera is added to the viewer + * as a slave. See osgViewer::Viewer::addSlave. + * @param flags properties of the camera; see CameraGroup::Flags + * @param projection slave projection matrix + * @param view slave view matrix + * @param useMasterSceneData whether the camera displays the + * viewer's scene data. + * @return a CameraInfo object for the camera. + */ + CameraInfo* addCamera(unsigned flags, osg::Camera* camera, + const osg::Matrix& projection, + const osg::Matrix& view, + bool useMasterSceneData = true); + /** Create an osg::Camera from a property node and add it to the + * camera group. + * @param cameraNode the property node. + * @return a CameraInfo object for the camera. + */ + CameraInfo* buildCamera(const SGPropertyNode* cameraNode); + /** Create a camera from properties that will draw the GUI and add + * it to the camera group. + * @param cameraNode the property node. This can be 0, in which + * case a default GUI camera is created. + * @param window the GraphicsWindow to use for the GUI camera. If + * this is 0, the window is determined from the property node. + * @return a CameraInfo object for the GUI camera. + */ + CameraInfo* buildGUICamera(const SGPropertyNode* cameraNode, + GraphicsWindow* window = 0); + /** Update the view for the camera group. + * @param position the world position of the view + * @param orientation the world orientation of the view. + */ + void update(const osg::Vec3d& position, const osg::Quat& orientation); + /** Set the parameters of the viewer's master camera. This won't + * affect cameras that have CameraFlags::PROJECTION_ABSOLUTE set. + * XXX Should znear and zfar be settable? + * @param vfov the vertical field of view angle + * @param aspectRatio the master camera's aspect ratio. This + * doesn't actually change the viewport, but should reflect the + * current viewport. + */ + void setCameraParameters(float vfov, float aspectRatio); + /** Set the default CameraGroup, which is the only one that + * matters at this time. + * @param group the group to set. + */ + static void setDefault(CameraGroup* group) { _defaultGroup = group; } + /** Get the default CameraGroup. + * @return the default camera group. + */ + static CameraGroup* getDefault() { return _defaultGroup.get(); } + typedef std::vector > CameraList; + typedef CameraList::iterator CameraIterator; + typedef CameraList::const_iterator ConstCameraIterator; + /** Get iterator for camera vector. The iterator's value is a ref_ptr. + */ + CameraIterator camerasBegin() { return _cameras.begin(); } + /** Get iteator pointing to the end of the camera list. + */ + CameraIterator camerasEnd() { return _cameras.end(); } + ConstCameraIterator camerasBegin() const { return _cameras.begin(); } + ConstCameraIterator camerasEnd() const { return _cameras.end(); } + /** Build a complete CameraGroup from a property node. + * @param viewer the viewer associated with this camera group. + * @param the camera group property node. + */ + static CameraGroup* buildCameraGroup(osgViewer::Viewer* viewer, + const SGPropertyNode* node); +protected: + CameraList _cameras; + osg::ref_ptr _viewer; + static osg::ref_ptr _defaultGroup; +}; + +} + +namespace osgGA +{ +class GUIEventAdapter; +} + +namespace flightgear +{ +/** Get the osg::Camera that draws the GUI, if any, from a camera + * group. + * @param cgroup the camera group + * @return the GUI camera or 0 + */ +osg::Camera* getGUICamera(CameraGroup* cgroup); +/** Choose a camera using an event and do intersection testing on its + * view of the scene. Only cameras with the DO_INTERSECTION_TEST flag + * set are considered. + * @param cgroup the CameraGroup + * @param ea the event containing a window and mouse coordinates + * @param intersections container for the result of intersection + * testing. + * @return true if any intersections are found + */ +bool computeIntersections(const CameraGroup* cgroup, + const osgGA::GUIEventAdapter* ea, + osgUtil::LineSegmentIntersector::Intersections& + intersections); +/** Warp the pointer to coordinates in the GUI camera of a camera group. + * @param cgroup the camera group + * @param x x window coordinate of pointer + * @param y y window coordinate of pointer, in "y down" coordinates. + */ +void warpGUIPointer(CameraGroup* cgroup, int x, int y); +} +#endif diff --git a/src/Main/FGManipulator.cxx b/src/Main/FGManipulator.cxx index 9c9af6805..b6d761f48 100644 --- a/src/Main/FGManipulator.cxx +++ b/src/Main/FGManipulator.cxx @@ -1,16 +1,24 @@ #ifdef HAVE_CONFIG_H # include #endif +#include +#include #include +#include #include + #include #include
+#include "CameraGroup.hxx" #include "FGManipulator.hxx" +#include "WindowSystemAdapter.hxx" #if !defined(X_DISPLAY_MISSING) #define X_DOUBLE_SCROLL_BUG 1 #endif +namespace flightgear +{ const int displayStatsKey = 1; const int printStatsKey = 2; @@ -91,8 +99,10 @@ osg::Node* FGManipulator::getNode() return _node.get(); } +namespace +{ // Translate OSG modifier mask to FG modifier mask. -static int osgToFGModifiers(int modifiers) +int osgToFGModifiers(int modifiers) { int result = 0; if (modifiers & osgGA::GUIEventAdapter::MODKEY_SHIFT) @@ -114,6 +124,7 @@ static int osgToFGModifiers(int modifiers) result |= KEYMOD_HYPER; return result; } +} void FGManipulator::init(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us) @@ -122,29 +133,42 @@ void FGManipulator::init(const osgGA::GUIEventAdapter& ea, (void)handle(ea, us); } -static bool +// Calculate event coordinates in the viewport of the GUI camera, if +// possible. Otherwise return false and (-1, -1). +namespace +{ +bool eventToViewport(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us, int& x, int& y) { x = -1; y = -1; - const osgViewer::Viewer* viewer; - viewer = dynamic_cast(&us); - if (!viewer) + const osg::GraphicsContext* eventGC = ea.getGraphicsContext(); + const osg::GraphicsContext::Traits* traits = eventGC->getTraits(); + osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault()); + if (!guiCamera) return false; - - float lx, ly; - const osg::Camera* camera; - camera = viewer->getCameraContainingPosition(ea.getX(), ea.getY(), lx, ly); - - if (!(camera && fgOSIsMainCamera(camera))) + osg::Viewport* vport = guiCamera->getViewport(); + if (!vport) return false; - - x = int(lx); - y = int(camera->getViewport()->height() - ly); - - return true; + + // Scale x, y to the dimensions of the window + double wx = (((ea.getX() - ea.getXmin()) / (ea.getXmax() - ea.getXmin())) + * (float)traits->width); + double wy = (((ea.getY() - ea.getYmin()) / (ea.getYmax() - ea.getYmin())) + * (float)traits->height); + if (vport->x() <= wx && wx <= vport->x() + vport->width() + && vport->y() <= wy && wy <= vport->y() + vport->height()) { + // Finally, into viewport coordinates. Change y to "increasing + // downwards". + x = wx - vport->x(); + y = vport->height() - (wy - vport->y()); + return true; + } else { + return false; + } +} } bool FGManipulator::handle(const osgGA::GUIEventAdapter& ea, @@ -337,3 +361,33 @@ void FGManipulator::handleStats(osgGA::GUIActionAdapter& us) } } +void eventToWindowCoords(const osgGA::GUIEventAdapter* ea, + double& x, double& y) +{ + using namespace osg; + const GraphicsContext* gc = ea->getGraphicsContext(); + const GraphicsContext::Traits* traits = gc->getTraits() ; + // Scale x, y to the dimensions of the window + x = (((ea->getX() - ea->getXmin()) / (ea->getXmax() - ea->getXmin())) + * (double)traits->width); + y = (((ea->getY() - ea->getYmin()) / (ea->getYmax() - ea->getYmin())) + * (double)traits->height); + if (ea->getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS) + y = (double)traits->height - y; +} + +void eventToWindowCoordsYDown(const osgGA::GUIEventAdapter* ea, + double& x, double& y) +{ + using namespace osg; + const GraphicsContext* gc = ea->getGraphicsContext(); + const GraphicsContext::Traits* traits = gc->getTraits() ; + // Scale x, y to the dimensions of the window + x = (((ea->getX() - ea->getXmin()) / (ea->getXmax() - ea->getXmin())) + * (double)traits->width); + y = (((ea->getY() - ea->getYmin()) / (ea->getYmax() - ea->getYmin())) + * (double)traits->height); + if (ea->getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS) + y = (double)traits->height - y; +} +} diff --git a/src/Main/FGManipulator.hxx b/src/Main/FGManipulator.hxx index 95329c19c..cc1c5f552 100644 --- a/src/Main/FGManipulator.hxx +++ b/src/Main/FGManipulator.hxx @@ -8,6 +8,8 @@ #include "fg_os.hxx" +namespace flightgear +{ class FGManipulator : public osgGA::MatrixManipulator { public: FGManipulator(); @@ -142,4 +144,9 @@ protected: int release_keys[128]; void handleStats(osgGA::GUIActionAdapter& us); }; + +void eventToWindowCoords(const osgGA::GUIEventAdapter* ea, double& x, double& y); +void eventToWindowCoordsYDown(const osgGA::GUIEventAdapter* ea, + double& x, double& y); +} #endif diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index 343d2fb75..0930f3121 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -60,9 +60,11 @@ libMain_a_SOURCES = \ util.cxx util.hxx \ viewer.cxx viewer.hxx \ viewmgr.cxx viewmgr.hxx \ + CameraGroup.cxx CameraGroup.hxx \ FGManipulator.cxx FGManipulator.hxx \ ViewPartitionNode.cxx ViewPartitionNode.hxx \ WindowSystemAdapter.hxx WindowSystemAdapter.cxx \ + WindowBuilder.hxx WindowBuilder.cxx \ $(GFX_CODE) fgfs_SOURCES = bootstrap.cxx diff --git a/src/Main/WindowBuilder.cxx b/src/Main/WindowBuilder.cxx new file mode 100644 index 000000000..aaabbb2db --- /dev/null +++ b/src/Main/WindowBuilder.cxx @@ -0,0 +1,228 @@ +// Copyright (C) 2008 Tim Moore +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "WindowBuilder.hxx" + +#include "WindowSystemAdapter.hxx" +#include "fg_props.hxx" + +#include + +using namespace std; +using namespace osg; + +namespace flightgear +{ +string makeName(const string& prefix, int num) +{ + stringstream stream; + stream << prefix << num; + return stream.str(); +} + +ref_ptr WindowBuilder::windowBuilder; + +void WindowBuilder::initWindowBuilder(bool stencil) +{ + windowBuilder = new WindowBuilder(stencil); +} + +WindowBuilder::WindowBuilder(bool stencil) : defaultCounter(0) +{ + defaultTraits = makeDefaultTraits(stencil); +} + +GraphicsContext::Traits* +WindowBuilder::makeDefaultTraits(bool stencil) +{ + GraphicsContext::WindowingSystemInterface* wsi + = osg::GraphicsContext::getWindowingSystemInterface(); + int w = fgGetInt("/sim/startup/xsize"); + int h = fgGetInt("/sim/startup/ysize"); + int bpp = fgGetInt("/sim/rendering/bits-per-pixel"); + bool alpha = fgGetBool("/sim/rendering/clouds3d-enable"); + bool fullscreen = fgGetBool("/sim/startup/fullscreen"); + + GraphicsContext::Traits* traits = new osg::GraphicsContext::Traits; + traits->readDISPLAY(); + int cbits = (bpp <= 16) ? 5 : 8; + int zbits = (bpp <= 16) ? 16 : 24; + traits->red = traits->green = traits->blue = cbits; + traits->depth = zbits; + if (alpha) + traits->alpha = 8; + if (stencil) + traits->stencil = 8; + traits->doubleBuffer = true; + traits->mipMapGeneration = true; + traits->windowName = "FlightGear"; + // XXX should check per window too. + traits->sampleBuffers = fgGetBool("/sim/rendering/multi-sample-buffers", traits->sampleBuffers); + traits->samples = fgGetBool("/sim/rendering/multi-samples", traits->samples); + traits->vsync = fgGetBool("/sim/rendering/vsync-enable", traits->vsync); + if (fullscreen) { + unsigned width = 0; + unsigned height = 0; + wsi->getScreenResolution(*traits, width, height); + traits->windowDecoration = false; + traits->width = width; + traits->height = height; + traits->supportsResize = false; + } else { + traits->windowDecoration = true; + traits->width = w; + traits->height = h; +#if defined(WIN32) || defined(__APPLE__) + // Ugly Hack, why does CW_USEDEFAULT works like phase of the moon? + // Mac also needs this to show window frame, menubar and Docks + traits->x = 100; + traits->y = 100; +#endif + traits->supportsResize = true; + } + return traits; +} +} + +namespace +{ +// Helper functions that set a value based on a property if it exists, +// returning 1 if the value was set. + +inline int setFromProperty(string& place, const SGPropertyNode* node, + const char* name) +{ + const SGPropertyNode* valNode = node->getNode(name); + if (valNode) { + place = valNode->getStringValue(); + return 1; + } + return 0; +} + +inline int setFromProperty(int& place, const SGPropertyNode* node, + const char* name) +{ + const SGPropertyNode* valNode = node->getNode(name); + if (valNode) { + place = valNode->getIntValue(); + return 1; + } + return 0; +} + +inline int setFromProperty(bool& place, const SGPropertyNode* node, + const char* name) +{ + const SGPropertyNode* valNode = node->getNode(name); + if (valNode) { + place = valNode->getBoolValue(); + return 1; + } + return 0; +} +} + +namespace flightgear +{ +GraphicsWindow* WindowBuilder::buildWindow(const SGPropertyNode* winNode) +{ + GraphicsContext::WindowingSystemInterface* wsi + = osg::GraphicsContext::getWindowingSystemInterface(); + WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA(); + string windowName; + if (winNode->hasChild("window-name")) + windowName = winNode->getStringValue("window-name"); + else if (winNode->hasChild("name")) + windowName = winNode->getStringValue("name"); + GraphicsWindow* result = 0; + if (!windowName.empty()) { + result = wsa->findWindow(windowName); + if (result) + return result; + } + GraphicsContext::Traits* traits + = new GraphicsContext::Traits(*defaultTraits); + int traitsSet = setFromProperty(traits->hostName, winNode, "host-name"); + traitsSet |= setFromProperty(traits->displayNum, winNode, "display"); + traitsSet |= setFromProperty(traits->screenNum, winNode, "screen"); + const SGPropertyNode* fullscreenNode = winNode->getNode("fullscreen"); + if (fullscreenNode && fullscreenNode->getBoolValue()) { + unsigned width = 0; + unsigned height = 0; + wsi->getScreenResolution(*traits, width, height); + traits->windowDecoration = false; + traits->width = width; + traits->height = height; + traits->supportsResize = false; + traitsSet = 1; + } else { + int resizable = 0; + resizable |= setFromProperty(traits->windowDecoration, winNode, + "decoration"); + resizable |= setFromProperty(traits->width, winNode, "width"); + resizable |= setFromProperty(traits->height, winNode, "height"); + if (resizable) { + traits->supportsResize = true; + traitsSet = 1; + } + // Otherwise use default values. + } + traitsSet |= setFromProperty(traits->x, winNode, "x"); + traitsSet |= setFromProperty(traits->y, winNode, "y"); + if (!windowName.empty() && windowName != traits->windowName) { + traits->windowName = windowName; + traitsSet = 1; + } else if (traitsSet) { + traits->windowName = makeName("FlightGear", defaultCounter++); + } + bool drawGUI = false; + traitsSet |= setFromProperty(drawGUI, winNode, "gui"); + if (traitsSet) { + GraphicsContext* gc = GraphicsContext::createGraphicsContext(traits); + if (gc) { + gc->realize(); + GraphicsWindow* window = WindowSystemAdapter::getWSA() + ->registerWindow(gc, traits->windowName); + if (drawGUI) + window->flags |= GraphicsWindow::GUI; + return window; + } else { + return 0; + } + } else { + return getDefaultWindow(); + } +} + +GraphicsWindow* WindowBuilder::getDefaultWindow() +{ + if (defaultWindow.valid()) + return defaultWindow.get(); + GraphicsContext::Traits* traits + = new GraphicsContext::Traits(*defaultTraits); + traits->windowName = "FlightGear"; + GraphicsContext* gc = GraphicsContext::createGraphicsContext(traits); + if (gc) { + gc->realize(); + defaultWindow = WindowSystemAdapter::getWSA() + ->registerWindow(gc, traits->windowName); + return defaultWindow.get(); + } else { + return 0; + } +} +} diff --git a/src/Main/WindowBuilder.hxx b/src/Main/WindowBuilder.hxx new file mode 100644 index 000000000..287d4cee8 --- /dev/null +++ b/src/Main/WindowBuilder.hxx @@ -0,0 +1,72 @@ +// Copyright (C) 2008 Tim Moore +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef FLIGHTGEAR_WINDOWBUILDER_HXX +#define FLIGHTGEAR_WINDOWBUILDER_HXX 1 + +#include +#include +#include + +#include + +class SGPropertyNode; + +namespace flightgear +{ +class GraphicsWindow; +/** Singleton Builder class for creating a GraphicsWindow from property + * nodes. This involves initializing an osg::GraphicsContext::Traits + * structure from the property node values and creating an + * osgViewer::GraphicsWindow. + */ +class WindowBuilder : public osg::Referenced +{ +public: + /** Initialize the singleton window builder. + * @param stencil whether windows should allocate stencil planes + */ + static void initWindowBuilder(bool stencil); + /** Get the singleton window builder + */ + static WindowBuilder* getWindowBuilder() { return windowBuilder.get(); } + /** Create a window from its property node description. + * @param winNode The window's root property node + * @return a graphics window. + */ + GraphicsWindow* buildWindow(const SGPropertyNode* winNode); + /** Get a window whose properties come from FlightGear's + * command line arguments and their defaults. The window is opened + * if it has not been already. + * @return the default graphics window + */ + GraphicsWindow* getDefaultWindow(); +protected: + WindowBuilder(bool stencil); + static osg::GraphicsContext::Traits* makeDefaultTraits(bool stencil); + osg::ref_ptr defaultTraits; + int defaultCounter; + static osg::ref_ptr windowBuilder; + osg::ref_ptr defaultWindow; +}; + +/** Silly function for making the default window and camera + * names. This concatenates a string with in integer. + */ +std::string makeName(const std::string& prefix, int num); + +} +#endif diff --git a/src/Main/WindowSystemAdapter.cxx b/src/Main/WindowSystemAdapter.cxx index 2739cd045..9da3400b1 100644 --- a/src/Main/WindowSystemAdapter.cxx +++ b/src/Main/WindowSystemAdapter.cxx @@ -22,13 +22,18 @@ #include #include +#include "CameraGroup.hxx" #include "WindowSystemAdapter.hxx" +#include +#include +#include + using namespace osg; using namespace std; -using namespace flightgear; - +namespace flightgear +{ ref_ptr WindowSystemAdapter::_wsa; void GraphicsContextOperation::operator()(GraphicsContext* gc) @@ -38,7 +43,7 @@ void GraphicsContextOperation::operator()(GraphicsContext* gc) } WindowSystemAdapter::WindowSystemAdapter() : - _nextWindowID(0), _nextCameraID(0), _isPuInitialized(false) + _nextWindowID(0), _isPuInitialized(false) { } @@ -52,63 +57,25 @@ WindowSystemAdapter::registerWindow(GraphicsContext* gc, return window; } -Camera3D* -WindowSystemAdapter::registerCamera3D(GraphicsWindow* gw, Camera* camera, - const string& cameraName) -{ - Camera3D* camera3D = new Camera3D(gw, camera, cameraName); - cameras.push_back(camera3D); - return camera3D; -} - -GraphicsWindow* -WindowSystemAdapter::getGUIWindow() -{ - WindowVector::const_iterator contextIter - = std::find_if(windows.begin(), windows.end(), - FlagTester(GraphicsWindow::GUI)); - if (contextIter == windows.end()) - return 0; - else - return contextIter->get(); -} - -int -WindowSystemAdapter::getGUIWindowID() -{ - const GraphicsWindow* gw = getGUIWindow(); - if (!gw) - return -1; - else - return gw->id; -} - -GraphicsContext* -WindowSystemAdapter::getGUIGraphicsContext() -{ - GraphicsWindow* gw = getGUIWindow(); - if (!gw) - return 0; - else - return gw->gc.get(); -} - - +// The pu getWindow callback is supposed to return a window ID that +// would allow drawing a GUI on different windows. All that stuff is +// broken in multi-threaded OSG, and we only have one GUI "window" +// anyway, so just return a constant. int WindowSystemAdapter::puGetWindow() { - WindowSystemAdapter* wsa = getWSA(); - return wsa->getGUIWindowID(); + return 1; } void WindowSystemAdapter::puGetWindowSize(int* width, int* height) { - // XXX This will have to be different when multiple cameras share - // a single window. - WindowSystemAdapter* wsa = getWSA(); - const GraphicsContext* gc = wsa->getGUIGraphicsContext(); - const GraphicsContext::Traits *traits = gc->getTraits(); - *width = traits->width; - *height = traits->height; + *width = 0; + *height = 0; + Camera* camera = getGUICamera(CameraGroup::getDefault()); + if (!camera) + return; + Viewport* vport = camera->getViewport(); + *width = (int)vport->width(); + *height = (int)vport->height(); } void WindowSystemAdapter::puInitialize() @@ -116,3 +83,15 @@ void WindowSystemAdapter::puInitialize() puSetWindowFuncs(puGetWindow, 0, puGetWindowSize, 0); puRealInit(); } + +GraphicsWindow* WindowSystemAdapter::findWindow(const string& name) +{ + for (WindowVector::iterator iter = windows.begin(), e = windows.end(); + iter != e; + ++iter) { + if ((*iter)->name == name) + return iter->get(); + } + return 0; +} +} diff --git a/src/Main/WindowSystemAdapter.hxx b/src/Main/WindowSystemAdapter.hxx index 8683632b3..b359f8e97 100644 --- a/src/Main/WindowSystemAdapter.hxx +++ b/src/Main/WindowSystemAdapter.hxx @@ -22,15 +22,20 @@ #include #include -#include #include +#include #include -// Flexible Camera and window support +namespace osg +{ +class GraphicsContext; +} + +// Flexible window support namespace flightgear { -/** A window opened by default or via rendering properties +/** A window with a graphics context and an integer ID */ class GraphicsWindow : public osg::Referenced { @@ -46,42 +51,23 @@ public: /** The window's internal name. */ std::string name; - enum Flags { - /** The GUI (and 2D cockpit) will be drawn on this window. - */ - GUI = 1 - }; + /** A unique ID for the window. + */ int id; - unsigned flags; -}; - -/** Camera associated with a 3d view. The camera might occupy an - * entire window or share one with other cameras. - */ -class Camera3D : public osg::Referenced -{ -public: - Camera3D(GraphicsWindow* window_, osg::Camera* camera_, const std::string& name_, - unsigned flags_ = 0) : - window(window_), camera(camera_), name(name_), flags(flags_) - { - } - osg::ref_ptr window; - osg::ref_ptr camera; - std::string name; enum Flags { - SHARES_WINDOW = 1, /**< Camera shares window with other cameras*/ - MASTER = 2 /**< Camera has same view as master camera*/ + GUI = 1 /**< The GUI (and 2D cockpit) will be drawn on this window. */ }; + /** Flags for the window. + */ unsigned flags; }; typedef std::vector > WindowVector; -typedef std::vector > Camera3DVector; /** * An operation that is run once with a particular GraphicsContext - * current. + * current. It will probably be deferred and may run in a different + * thread. */ class GraphicsContextOperation : public osg::GraphicsOperation { @@ -90,8 +76,15 @@ public: osg::GraphicsOperation(name, false) { } + /** Don't override this! + */ virtual void operator()(osg::GraphicsContext* gc); + /** The body of the operation. + */ virtual void run(osg::GraphicsContext* gc) = 0; + /** Test if the operation has completed. + * @return true if the run() method has finished. + */ bool isFinished() const { return done != 0; } private: SGAtomic done; @@ -99,7 +92,7 @@ private: /** Adapter from windows system / graphics context management API to * functions used by flightgear. This papers over the difference - * between osgViewer Viewer, which handles multiple windows, graphics + * between osgViewer::Viewer, which handles multiple windows, graphics * threads, etc., and the embedded viewer used with GLUT and SDL. */ class WindowSystemAdapter : public osg::Referenced @@ -107,37 +100,35 @@ class WindowSystemAdapter : public osg::Referenced public: WindowSystemAdapter(); virtual ~WindowSystemAdapter() {} + /** Vector of all the registered windows. + */ WindowVector windows; - Camera3DVector cameras; + /** Register a window, assigning it an ID. + * @param gc graphics context + * @param windowName internal name (not displayed) + * @return a graphics window + */ GraphicsWindow* registerWindow(osg::GraphicsContext* gc, const std::string& windowName); - Camera3D* registerCamera3D(GraphicsWindow* gw, osg::Camera* camera, - const std::string& cameraName); - GraphicsWindow* getGUIWindow(); - int getGUIWindowID(); - osg::GraphicsContext* getGUIGraphicsContext(); /** Initialize the plib pui interface library. This might happen *in another thread and may be deferred. */ virtual void puInitialize(); - /** Returns true if pui initialization has finished. + /** Find a window by name. + * @param name the window name + * @return the window or 0 + */ + GraphicsWindow* findWindow(const std::string& name); + /** Get the global WindowSystemAdapter + * @return the adapter */ - template - class FlagTester : public std::unary_function, bool> - { - public: - FlagTester(unsigned flags_) : flags(flags_) {} - bool operator() (const osg::ref_ptr& obj) - { - return (obj->flags & flags) != 0; - } - unsigned flags; - }; static WindowSystemAdapter* getWSA() { return _wsa.get(); } + /** Set the global adapter + * @param wsa the adapter + */ static void setWSA(WindowSystemAdapter* wsa) { _wsa = wsa; } protected: int _nextWindowID; - int _nextCameraID; osg::ref_ptr _puInitOp; bool _isPuInitialized; static osg::ref_ptr _wsa; @@ -146,5 +137,29 @@ protected: static void puGetWindowSize(int* width, int* height); }; + +/** + * Class for testing if flags are set in an object with a flags member. + */ +template +class FlagTester : public std::unary_function, bool> +{ +public: + /** Initialize with flags to test for. + * @param flags logical or of flags to test. + */ + FlagTester(unsigned flags_) : flags(flags_) {} + /** test operator + * @param obj An object with a flags member + * @return true if flags member of obj contains any of the flags + * (bitwise and with flags is nonzero). + */ + bool operator() (const osg::ref_ptr& obj) + { + return (obj->flags & flags) != 0; + } + unsigned flags; +}; + } #endif diff --git a/src/Main/fg_os.cxx b/src/Main/fg_os.cxx index 0ecde2c92..dfce9b2aa 100644 --- a/src/Main/fg_os.cxx +++ b/src/Main/fg_os.cxx @@ -7,6 +7,7 @@ # include #endif +#include #include #include @@ -25,6 +26,7 @@ #include "renderer.hxx" #include "fg_props.hxx" #include "WindowSystemAdapter.hxx" +#include "CameraGroup.hxx" using namespace flightgear; @@ -34,7 +36,6 @@ using namespace flightgear; // static osg::ref_ptr viewer; -static osg::ref_ptr mainCamera; static osg::ref_ptr gw; static int GlutModifiers = 0; @@ -274,16 +275,17 @@ void fgOSOpenWindow(bool stencil) viewer->setDatabasePager(FGScenery::getPagerSingleton()); // now the main camera ... osg::Camera* camera = new osg::Camera; - mainCamera = camera; // If a viewport isn't set on the camera, then it's hard to dig it // out of the SceneView objects in the viewer, and the coordinates // of mouse events are somewhat bizzare. camera->setViewport(new osg::Viewport(0, 0, realw, realh)); camera->setProjectionResizePolicy(osg::Camera::FIXED); - Camera3D* cam3D = wsa->registerCamera3D(window, camera, string("main")); - cam3D->flags |= Camera3D::MASTER; - // Add as a slave for compatibility with the non-embedded osgViewer. - viewer->addSlave(camera); + CameraGroup* cgroup = new CameraGroup(viewer.get()); + cgroup->addCamera(CameraGroup::DO_INTERSECTION_TEST, camera, + osg::Matrixd::identity(), osg::Matrixd::identity(), + true); + cgroup->buildGUICamera(0, window); + CameraGroup::setDefault(cgroup); viewer->setCameraManipulator(globals->get_renderer()->getManipulator()); // Let FG handle the escape key with a confirmation viewer->setKeyEventSetsDone(0); @@ -295,18 +297,3 @@ void fgOSOpenWindow(bool stencil) viewer->setSceneData(new osg::Group); globals->get_renderer()->setViewer(viewer.get()); } - -bool fgOSIsMainCamera(const osg::Camera*) -{ - return true; -} - -bool fgOSIsMainContext(const osg::GraphicsContext*) -{ - return true; -} - -osg::GraphicsContext* fgOSGetMainContext() -{ - return gw.get(); -} diff --git a/src/Main/fg_os.hxx b/src/Main/fg_os.hxx index c12e79b7b..86710b680 100644 --- a/src/Main/fg_os.hxx +++ b/src/Main/fg_os.hxx @@ -84,18 +84,4 @@ void fgRegisterWindowResizeHandler(fgWindowResizeHandler func); void fgRegisterKeyHandler(fgKeyHandler func); void fgRegisterMouseClickHandler(fgMouseClickHandler func); void fgRegisterMouseMotionHandler(fgMouseMotionHandler func); - -bool fgOSIsMainCamera(const osg::Camera* camera); -bool fgOSIsMainContext(const osg::GraphicsContext* context); - -/** Get graphics context of the main camera. This is the principal - * window in multi-window configurations, or the only window in an - * embedded configuration. The GUI will be added to this context. - */ -osg::GraphicsContext* fgOSGetMainContext(); - - - - - #endif // _FG_OS_HXX diff --git a/src/Main/fg_os_osgviewer.cxx b/src/Main/fg_os_osgviewer.cxx index 5f3ecbdba..ee7367ef0 100644 --- a/src/Main/fg_os_osgviewer.cxx +++ b/src/Main/fg_os_osgviewer.cxx @@ -18,10 +18,6 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#ifdef HAVE_CONFIG_H -# include -#endif - #include #include #include @@ -33,6 +29,7 @@ #include #include +#include #include #include #include @@ -50,6 +47,8 @@ #include "util.hxx" #include "globals.hxx" #include "renderer.hxx" +#include "CameraGroup.hxx" +#include "WindowBuilder.hxx" #include "WindowSystemAdapter.hxx" #if (FG_OSG_VERSION >= 19008) @@ -67,113 +66,66 @@ using namespace std; using namespace flightgear; using namespace osg; - - static osg::ref_ptr viewer; static osg::ref_ptr mainCamera; -// Callback to prevent the GraphicsContext resized function from messing -// with the projection matrix of the slave - namespace { -// silly function for making the default window and camera names -std::string makeName(const string& prefix, int num) +// If a camera group isn't specified, build one from the top-level +// camera specs and then add a camera aligned with the master camera +// if it doesn't seem to exist. +CameraGroup* buildDefaultCameraGroup(osgViewer::Viewer* viewer, + const SGPropertyNode* gnode) { - std::stringstream stream; - stream << prefix << num; - return stream.str(); -} - -GraphicsContext::Traits* -makeDefaultTraits(GraphicsContext::WindowingSystemInterface* wsi, bool stencil) -{ - int w = fgGetInt("/sim/startup/xsize"); - int h = fgGetInt("/sim/startup/ysize"); - int bpp = fgGetInt("/sim/rendering/bits-per-pixel"); - bool alpha = fgGetBool("/sim/rendering/clouds3d-enable"); - bool fullscreen = fgGetBool("/sim/startup/fullscreen"); - - GraphicsContext::Traits* traits = new osg::GraphicsContext::Traits; - traits->readDISPLAY(); - int cbits = (bpp <= 16) ? 5 : 8; - int zbits = (bpp <= 16) ? 16 : 24; - traits->red = traits->green = traits->blue = cbits; - traits->depth = zbits; - if (alpha) - traits->alpha = 8; - if (stencil) - traits->stencil = 8; - traits->doubleBuffer = true; - traits->mipMapGeneration = true; - traits->windowName = "FlightGear"; - // XXX should check per window too. - traits->sampleBuffers = fgGetBool("/sim/rendering/multi-sample-buffers", traits->sampleBuffers); - traits->samples = fgGetBool("/sim/rendering/multi-samples", traits->samples); - traits->vsync = fgGetBool("/sim/rendering/vsync-enable", traits->vsync); - if (fullscreen) { - unsigned width = 0; - unsigned height = 0; - wsi->getScreenResolution(*traits, width, height); - traits->windowDecoration = false; - traits->width = width; - traits->height = height; - traits->supportsResize = false; - } else { - traits->windowDecoration = true; - traits->width = w; - traits->height = h; -#if defined(WIN32) || defined(__APPLE__) - // Ugly Hack, why does CW_USEDEFAULT works like phase of the moon? - // Mac also needs this to show window frame, menubar and Docks - traits->x = 100; - traits->y = 100; -#endif - traits->supportsResize = true; + WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA(); + CameraGroup* cgroup = CameraGroup::buildCameraGroup(viewer, gnode); + // Look for a camera with no shear + Camera* masterCamera = 0; + for (CameraGroup::CameraIterator iter = cgroup->camerasBegin(), + e = cgroup->camerasEnd(); + iter != e; + ++iter) { + const View::Slave& slave = viewer->getSlave((*iter)->slaveIndex); + if (slave._projectionOffset.isIdentity()) { + masterCamera = (*iter)->camera.get(); + break; + } } - return traits; -} - -void setTraitsFromProperties(GraphicsContext::Traits* traits, - const SGPropertyNode* winNode, - GraphicsContext::WindowingSystemInterface* wsi) -{ - traits->hostName - = winNode->getStringValue("host-name", traits->hostName.c_str()); - traits->displayNum = winNode->getIntValue("display", traits->displayNum); - traits->screenNum = winNode->getIntValue("screen", traits->screenNum); - if (winNode->getBoolValue("fullscreen", - fgGetBool("/sim/startup/fullscreen"))) { - unsigned width = 0; - unsigned height = 0; - wsi->getScreenResolution(*traits, width, height); - traits->windowDecoration = false; - traits->width = width; - traits->height = height; - traits->supportsResize = false; - } else { - traits->windowDecoration = winNode->getBoolValue("decoration", true); - traits->width = winNode->getIntValue("width", traits->width); - traits->height = winNode->getIntValue("height", traits->height); - traits->supportsResize = true; + if (!masterCamera) { + // No master camera found; better add one. + GraphicsWindow* window + = WindowBuilder::getWindowBuilder()->getDefaultWindow(); + masterCamera = new Camera(); + masterCamera->setGraphicsContext(window->gc.get()); + const GraphicsContext::Traits *traits = window->gc->getTraits(); + masterCamera->setViewport(new Viewport(0, 0, + traits->width, traits->height)); + cgroup->addCamera(CameraGroup::DO_INTERSECTION_TEST, masterCamera, + Matrix(), Matrix()); } - traits->x = winNode->getIntValue("x", traits->x); - traits->y = winNode->getIntValue("y", traits->y); - if (winNode->hasChild("window-name")) - traits->windowName = winNode->getStringValue("window-name"); - else if (winNode->hasChild("name")) - traits->windowName = winNode->getStringValue("name"); + // Find window on which the GUI is drawn. + WindowVector::iterator iter = wsa->windows.begin(); + WindowVector::iterator end = wsa->windows.end(); + for (; iter != end; ++iter) { + if ((*iter)->gc.get() == masterCamera->getGraphicsContext()) + break; + } + if (iter != end) { // Better not happen + (*iter)->flags |= GraphicsWindow::GUI; + cgroup->buildGUICamera(0, iter->get()); + } + return cgroup; +} } - -} //namespace void fgOSOpenWindow(bool stencil) { - osg::GraphicsContext::WindowingSystemInterface* wsi; - wsi = osg::GraphicsContext::getWindowingSystemInterface(); + osg::GraphicsContext::WindowingSystemInterface* wsi + = osg::GraphicsContext::getWindowingSystemInterface(); viewer = new osgViewer::Viewer; viewer->setDatabasePager(FGScenery::getPagerSingleton()); + CameraGroup* cameraGroup = 0; std::string mode; mode = fgGetString("/sim/rendering/multithreading-mode", "SingleThreaded"); if (mode == "AutomaticSelection") @@ -186,111 +138,32 @@ void fgOSOpenWindow(bool stencil) viewer->setThreadingModel(osgViewer::Viewer::CullThreadPerCameraDrawThreadPerContext); else viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded); - osg::ref_ptr traits - = makeDefaultTraits(wsi, stencil); + WindowBuilder::initWindowBuilder(stencil); + WindowBuilder *windowBuilder = WindowBuilder::getWindowBuilder(); - // Ok, first the children. - // that achieves some magic ordering og the slaves so that we end up - // in the main window more often. - // This can be sorted out better when we got rid of glut and sdl. - FGManipulator* manipulator = globals->get_renderer()->getManipulator(); - string defaultName("slave"); - WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA(); - if (fgHasNode("/sim/rendering/camera")) { - SGPropertyNode* renderingNode = fgGetNode("/sim/rendering"); - for (int i = 0; i < renderingNode->nChildren(); ++i) { - SGPropertyNode* cameraNode = renderingNode->getChild(i); - if (strcmp(cameraNode->getName(), "camera") != 0) - continue; - - // get a new copy of the traits struct - osg::ref_ptr cameraTraits; - cameraTraits = new osg::GraphicsContext::Traits(*traits); - double shearx = cameraNode->getDoubleValue("shear-x", 0); - double sheary = cameraNode->getDoubleValue("shear-y", 0); - double heading = cameraNode->getDoubleValue("heading-deg", 0); - setTraitsFromProperties(cameraTraits.get(), cameraNode, wsi); - // FIXME, currently this is too much of a problem to route - // the resize events. When we do no longer need sdl and - // such this can be simplified - cameraTraits->supportsResize = false; - - // ok found a camera configuration, add a new slave if possible - GraphicsContext* gc - = GraphicsContext::createGraphicsContext(cameraTraits.get()); - if (gc) { - gc->realize(); - Camera *camera = new Camera; - camera->setGraphicsContext(gc); - // If a viewport isn't set on the camera, then it's - // hard to dig it out of the SceneView objects in the - // viewer, and the coordinates of mouse events are - // somewhat bizzare. - camera->setViewport(new Viewport(0, 0, cameraTraits->width, - cameraTraits->height)); - const char* cameraName = cameraNode->getStringValue("name"); - string cameraNameString = (cameraName ? string(cameraName) - : makeName(defaultName, i)); - GraphicsWindow* window = wsa->registerWindow(gc, - cameraNameString); - Camera3D* cam3D = wsa->registerCamera3D(window, camera, - cameraNameString); - if (shearx == 0 && sheary == 0) - cam3D->flags |= Camera3D::MASTER; - - osg::Matrix pOff = osg::Matrix::translate(-shearx, -sheary, 0); - osg::Matrix vOff; - vOff.makeRotate(SGMiscd::deg2rad(heading), osg::Vec3(0, 1, 0)); - viewer->addSlave(camera, pOff, vOff); - } else { - SG_LOG(SG_GENERAL, SG_WARN, - "Couldn't create graphics context on " - << cameraTraits->hostName << ":" - << cameraTraits->displayNum - << "." << cameraTraits->screenNum); - } + // Look for windows, camera groups, and the old syntax of + // top-level cameras + const SGPropertyNode* renderingNode = fgGetNode("/sim/rendering"); + for (int i = 0; i < renderingNode->nChildren(); ++i) { + const SGPropertyNode* propNode = renderingNode->getChild(i); + const char* propName = propNode->getName(); + if (!strcmp(propName, "window")) { + windowBuilder->buildWindow(propNode); + } else if (!strcmp(propName, "camera-group")) { + cameraGroup = CameraGroup::buildCameraGroup(viewer.get(), propNode); } } - // now the main camera ... - // XXX mainCamera's purpose is to establish a "main graphics - // context" that can be made current (if necessary). But that - // should be a context established with a window. It's used to - // choose whether to render the GUI and panel camera nodes, but - // that's obsolete because the GUI is rendered in its own - // slave. And it's used to translate mouse event coordinates, but - // that's bogus because mouse clicks should work on any camera. In - // short, mainCamera must die :) - Camera3DVector::iterator citr - = find_if(wsa->cameras.begin(), wsa->cameras.end(), - WindowSystemAdapter::FlagTester(Camera3D::MASTER)); - if (citr == wsa->cameras.end()) { - // Create a camera aligned with the master camera. Eventually - // this will be optional. - Camera* camera = new osg::Camera; - mainCamera = camera; - osg::GraphicsContext* gc - = osg::GraphicsContext::createGraphicsContext(traits.get()); - gc->realize(); - gc->makeCurrent(); - camera->setGraphicsContext(gc); - // If a viewport isn't set on the camera, then it's hard to dig it - // out of the SceneView objects in the viewer, and the coordinates - // of mouse events are somewhat bizzare. - camera->setViewport(new osg::Viewport(0, 0, - traits->width, traits->height)); - GraphicsWindow* window = wsa->registerWindow(gc, string("main")); - window->flags |= GraphicsWindow::GUI; - Camera3D* camera3d = wsa->registerCamera3D(window, camera, - string("main")); - camera3d->flags |= Camera3D::MASTER; - // Why a slave? It seems to be the easiest way to assign cameras, - // for which we've created the graphics context ourselves, to - // the viewer. - viewer->addSlave(camera); - } else { - mainCamera = (*citr)->camera; + if (!cameraGroup) + cameraGroup = buildDefaultCameraGroup(viewer.get(), renderingNode); + Camera* guiCamera = getGUICamera(cameraGroup); + if (guiCamera) { + Viewport* guiViewport = guiCamera->getViewport(); + fgSetInt("/sim/startup/xsize", guiViewport->width()); + fgSetInt("/sim/startup/ysize", guiViewport->height()); } - if (wsa->cameras.size() != 1) { + FGManipulator* manipulator = globals->get_renderer()->getManipulator(); + WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA(); + if (wsa->windows.size() != 1) { manipulator->setResizable(false); } viewer->getCamera()->setProjectionResizePolicy(osg::Camera::FIXED); @@ -300,6 +173,7 @@ void fgOSOpenWindow(bool stencil) // The viewer won't start without some root. viewer->setSceneData(new osg::Group); globals->get_renderer()->setViewer(viewer.get()); + CameraGroup::setDefault(cameraGroup); } static int status = 0; @@ -324,17 +198,7 @@ int fgGetKeyModifiers() void fgWarpMouse(int x, int y) { - globals->get_renderer()->getManipulator()->setMouseWarped(); - // Hack, currently the pointer is just recentered. So, we know the - // relative coordinates ... - if (!mainCamera.valid()) { - viewer->requestWarpPointer(0, 0); - return; - } - float xsize = (float)mainCamera->getGraphicsContext()->getTraits()->width; - float ysize = (float)mainCamera->getGraphicsContext()->getTraits()->height; - viewer->requestWarpPointer(2.0f * (float)x / xsize - 1.0f, - 1.0f - 2.0f * (float)y / ysize); + warpGUIPointer(CameraGroup::getDefault(), x, y); } void fgOSInit(int* argc, char** argv) @@ -393,36 +257,3 @@ int fgGetMouseCursor() { return _cursor; } - -bool fgOSIsMainContext(const osg::GraphicsContext* context) -{ - if (!mainCamera.valid()) - return false; - return context == mainCamera->getGraphicsContext(); -} - -bool fgOSIsMainCamera(const osg::Camera* camera) -{ - if (!camera) - return false; - if (camera == mainCamera.get()) - return true; - if (!viewer.valid()) - return false; - if (camera == viewer->getCamera()) - return true; - return false; -} - -GraphicsContext* fgOSGetMainContext() -{ - WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA(); - WindowVector::iterator contextIter - = std::find_if(wsa->windows.begin(), wsa->windows.end(), - WindowSystemAdapter::FlagTester(GraphicsWindow::GUI)); - if (contextIter == wsa->windows.end()) - return 0; - else - return (*contextIter)->gc.get(); -} - diff --git a/src/Main/fg_os_sdl.cxx b/src/Main/fg_os_sdl.cxx index 917fff9a0..687788e05 100644 --- a/src/Main/fg_os_sdl.cxx +++ b/src/Main/fg_os_sdl.cxx @@ -14,6 +14,7 @@ #include "globals.hxx" #include "renderer.hxx" #include "fg_props.hxx" +#include "CameraGroup.hxx" #include "WindowSystemAdapter.hxx" using namespace flightgear; @@ -34,7 +35,6 @@ static int VidMask = SDL_OPENGL|SDL_RESIZABLE; static void initCursors(); static osg::ref_ptr viewer; -static osg::ref_ptr mainCamera; static osg::ref_ptr gw; void fgOSOpenWindow(bool stencil) @@ -96,16 +96,17 @@ void fgOSOpenWindow(bool stencil) window->flags |= GraphicsWindow::GUI; // now the main camera ... osg::Camera* camera = new osg::Camera; - mainCamera = camera; // If a viewport isn't set on the camera, then it's hard to dig it // out of the SceneView objects in the viewer, and the coordinates // of mouse events are somewhat bizzare. camera->setViewport(new osg::Viewport(0, 0, realw, realh)); camera->setProjectionResizePolicy(osg::Camera::FIXED); - Camera3D* cam3D = wsa->registerCamera3D(window, camera, string("main")); - cam3D->flags |= Camera3D::MASTER; - // Add as a slave for compatibility with the non-embedded osgViewer. - viewer->addSlave(camera); + CameraGroup* cgroup = new CameraGroup(viewer.get()); + cgroup->addCamera(CameraGroup::DO_INTERSECTION_TEST, camera, + osg::Matrixd::identity(), osg::Matrixd::identity(), + true); + cgroup->buildGUICamera(0, window); + CameraGroup::setDefault(cgroup); viewer->setCameraManipulator(globals->get_renderer()->getManipulator()); // Let FG handle the escape key with a confirmation viewer->setKeyEventSetsDone(0); @@ -410,18 +411,3 @@ static void initCursors() cursors[i].hoty); } } - -bool fgOSIsMainCamera(const osg::Camera*) -{ - return true; -} - -bool fgOSIsMainContext(const osg::GraphicsContext*) -{ - return true; -} - -osg::GraphicsContext* fgOSGetMainContext() -{ - return gw.get(); -} diff --git a/src/Main/main.cxx b/src/Main/main.cxx index d2d678761..712eb1a5f 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -34,6 +34,8 @@ #include +#include +#include #include // Class references @@ -68,6 +70,7 @@ #include #include +#include "CameraGroup.hxx" #include "fg_commands.hxx" #include "fg_io.hxx" #include "renderer.hxx" @@ -75,11 +78,15 @@ #include "main.hxx" #include "util.hxx" #include "fg_init.hxx" +#include "WindowSystemAdapter.hxx" + static double real_delta_time_sec = 0.0; double delta_time_sec = 0.0; extern float init_volume; +using namespace flightgear; + // This is a record containing a bit of global housekeeping information FGGeneral general; @@ -650,21 +657,18 @@ static void fgMainLoop( void ) { SG_LOG( SG_ALL, SG_DEBUG, "" ); } - -// This is the top level master main function that is registered as -// our idle funciton - -// The first few passes take care of initialization things (a couple -// per pass) and once everything has been initialized fgMainLoop from -// then on. - -static void fgIdleFunction ( void ) { - if ( idle_state == 0 ) { - idle_state++; - - // This seems to be the absolute earliest in the init sequence - // that these calls will return valid info. Too bad it's after - // we've already created and sized our window. :-( +// Operation for querying OpenGL parameters. This must be done in a +// valid OpenGL context, potentially in another thread. +namespace +{ +struct GeneralInitOperation : public GraphicsContextOperation +{ + GeneralInitOperation() + : GraphicsContextOperation(std::string("General init")) + { + } + void run(osg::GraphicsContext* gc) + { general.set_glVendor( (char *)glGetString ( GL_VENDOR ) ); general.set_glRenderer( (char *)glGetString ( GL_RENDERER ) ); general.set_glVersion( (char *)glGetString ( GL_VERSION ) ); @@ -678,12 +682,42 @@ static void fgIdleFunction ( void ) { glGetIntegerv( GL_DEPTH_BITS, &tmp ); general.set_glDepthBits( tmp ); SG_LOG ( SG_GENERAL, SG_INFO, "Depth buffer bits = " << tmp ); + } +}; +} - // Initialize the user interface so that we can use fonts - guiStartInit(); +// This is the top level master main function that is registered as +// our idle funciton +// The first few passes take care of initialization things (a couple +// per pass) and once everything has been initialized fgMainLoop from +// then on. +static void fgIdleFunction ( void ) { + static osg::ref_ptr genOp; + if ( idle_state == 0 ) { + idle_state++; + // Pick some window on which to do queries. + // XXX Perhaps all this graphics initialization code should be + // moved to renderer.cxx? + genOp = new GeneralInitOperation; + osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault()); + WindowSystemAdapter* wsa = WindowSystemAdapter::getWSA(); + osg::GraphicsContext* gc = 0; + if (guiCamera) + gc = guiCamera->getGraphicsContext(); + if (gc) { + gc->add(genOp.get()); + } else { + wsa->windows[0]->gc->add(genOp.get()); + } + guiStartInit(gc); } else if ( idle_state == 1 ) { + if (genOp.valid()) { + if (!genOp->isFinished()) + return; + genOp = 0; + } if (!guiFinishInit()) return; idle_state++; diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 6286578af..c16f3c690 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -1227,12 +1227,12 @@ where: enum OptionType { OPTION_BOOL, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC }; struct OptionDesc { - char *option; + const char *option; bool has_param; enum OptionType type; - char *property; + const char *property; bool b_param; - char *s_param; + const char *s_param; int (*func)( const char * ); } fgOptionArray[] = { diff --git a/src/Main/renderer.cxx b/src/Main/renderer.cxx index 368dba642..8dd73cae4 100644 --- a/src/Main/renderer.cxx +++ b/src/Main/renderer.cxx @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +97,7 @@ #include "splash.hxx" #include "renderer.hxx" #include "main.hxx" +#include "CameraGroup.hxx" #include "ViewPartitionNode.hxx" // XXX Make this go away when OSG 2.2 is released. @@ -102,6 +105,8 @@ #define UPDATE_VISITOR_IN_VIEWER 1 #endif +using namespace flightgear; + class FGHintUpdateCallback : public osg::StateAttribute::Callback { public: FGHintUpdateCallback(const char* configNode) : @@ -153,8 +158,6 @@ public: { drawImplementation(*renderInfo.getState()); } void drawImplementation(osg::State& state) const { - if (!fgOSIsMainContext(state.getGraphicsContext())) - return; state.setActiveTextureUnit(0); state.setClientActiveTextureUnit(0); @@ -198,8 +201,6 @@ public: { drawImplementation(*renderInfo.getState()); } void drawImplementation(osg::State& state) const { - if (!fgOSIsMainContext(state.getGraphicsContext())) - return; state.setActiveTextureUnit(0); state.setClientActiveTextureUnit(0); state.disableAllVertexArrays(); @@ -406,38 +407,6 @@ FGRenderer::splashinit( void ) { #endif } -namespace -{ -// Create a slave camera that will be used to render a fixed GUI-like -// element. -osg::Camera* -makeSlaveCamera(osg::Camera::RenderOrder renderOrder, int orderNum) -{ - using namespace osg; - Camera* camera = new osg::Camera; - GraphicsContext *gc = fgOSGetMainContext(); - - camera->setRenderOrder(renderOrder, orderNum); - camera->setClearMask(0); - camera->setInheritanceMask(CullSettings::ALL_VARIABLES - & ~(CullSettings::COMPUTE_NEAR_FAR_MODE - | CullSettings::CULLING_MODE)); - camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); - camera->setCullingMode(osg::CullSettings::NO_CULLING); - camera->setGraphicsContext(gc); - // Establish an initial viewport. This may be altered, - // particularly when drawing a 2d panel. - const GraphicsContext::Traits *traits = gc->getTraits(); - camera->setViewport(new Viewport(0, 0, traits->width, traits->height)); - camera->setProjectionResizePolicy(Camera::FIXED); - camera->setReferenceFrame(Transform::ABSOLUTE_RF); - camera->setAllowEventFocus(false); - globals->get_renderer()->getViewer()->addSlave(camera, false); - return camera; -} - -} - void FGRenderer::init( void ) { @@ -541,13 +510,13 @@ FGRenderer::init( void ) stateSet->setUpdateCallback(new FGFogEnableUpdateCallback); // plug in the GUI - osg::Camera* guiCamera = makeSlaveCamera(osg::Camera::POST_RENDER, 100); - guiCamera->setName("GUI"); - osg::Geode* geode = new osg::Geode; - geode->addDrawable(new SGPuDrawable); - geode->addDrawable(new SGHUDAndPanelDrawable); - guiCamera->addChild(geode); - + osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault()); + if (guiCamera) { + osg::Geode* geode = new osg::Geode; + geode->addDrawable(new SGPuDrawable); + geode->addDrawable(new SGHUDAndPanelDrawable); + guiCamera->addChild(geode); + } osg::Switch* sw = new osg::Switch; sw->setUpdateCallback(new FGScenerySwitchCallback); sw->addChild(mRoot.get()); @@ -612,14 +581,14 @@ FGRenderer::update( bool refresh_camera_settings ) { // update view port resize( fgGetInt("/sim/startup/xsize"), fgGetInt("/sim/startup/ysize") ); - +#if 0 SGVec3d position = current__view->getViewPosition(); SGQuatd attitude = current__view->getViewOrientation(); - SGVec3d osgPosition = attitude.transform(-position); FGManipulator *manipulator = globals->get_renderer()->getManipulator(); manipulator->setPosition(position.osg()); manipulator->setAttitude(attitude.osg()); +#endif } osg::Camera *camera = viewer->getCamera(); @@ -743,7 +712,7 @@ FGRenderer::update( bool refresh_camera_settings ) { } // sgEnviro.setLight(l->adj_fog_color()); - +#if 0 double agl = current__view->getAltitudeASL_ft()*SG_FEET_TO_METER - current__view->getSGLocation()->get_cur_elev_m(); @@ -761,7 +730,7 @@ FGRenderer::update( bool refresh_camera_settings ) { setCameraParameters(current__view->get_v_fov(), current__view->get_aspect_ratio(), scene_nearplane, scene_farplane); - +#endif // sgEnviro.startOfFrame(current__view->get_view_pos(), // current__view->get_world_up(), // current__view->getLongitude_deg(), @@ -850,44 +819,40 @@ void FGRenderer::setCameraParameters(float vfov, float aspectRatio, } bool -FGRenderer::pick( unsigned x, unsigned y, - std::vector& pickList, - const osgGA::GUIEventAdapter* ea ) +FGRenderer::pick(std::vector& pickList, + const osgGA::GUIEventAdapter* ea) { - osgViewer::Viewer* viewer = globals->get_renderer()->getViewer(); - // wipe out the return ... - pickList.resize(0); - - if (viewer) { - // just compute intersections in the viewers method ... + osgViewer::Viewer* viewer = globals->get_renderer()->getViewer(); + // wipe out the return ... + pickList.clear(); typedef osgUtil::LineSegmentIntersector::Intersections Intersections; Intersections intersections; - viewer->computeIntersections(ea->getX(), ea->getY(), intersections); - Intersections::iterator hit; - for (hit = intersections.begin(); hit != intersections.end(); ++hit) { - const osg::NodePath& np = hit->nodePath; - osg::NodePath::const_reverse_iterator npi; - for (npi = np.rbegin(); npi != np.rend(); ++npi) { - SGSceneUserData* ud = SGSceneUserData::getSceneUserData(*npi); - if (!ud) - continue; - for (unsigned i = 0; i < ud->getNumPickCallbacks(); ++i) { - SGPickCallback* pickCallback = ud->getPickCallback(i); - if (!pickCallback) - continue; - SGSceneryPick sceneryPick; - sceneryPick.info.local = SGVec3d(hit->getLocalIntersectPoint()); - sceneryPick.info.wgs84 = SGVec3d(hit->getWorldIntersectPoint()); - sceneryPick.callback = pickCallback; - pickList.push_back(sceneryPick); + if (!computeIntersections(CameraGroup::getDefault(), ea, intersections)) + return false; + for (Intersections::iterator hit = intersections.begin(), + e = intersections.end(); + hit != e; + ++hit) { + const osg::NodePath& np = hit->nodePath; + osg::NodePath::const_reverse_iterator npi; + for (npi = np.rbegin(); npi != np.rend(); ++npi) { + SGSceneUserData* ud = SGSceneUserData::getSceneUserData(*npi); + if (!ud) + continue; + for (unsigned i = 0; i < ud->getNumPickCallbacks(); ++i) { + SGPickCallback* pickCallback = ud->getPickCallback(i); + if (!pickCallback) + continue; + SGSceneryPick sceneryPick; + sceneryPick.info.local = SGVec3d(hit->getLocalIntersectPoint()); + sceneryPick.info.wgs84 = SGVec3d(hit->getWorldIntersectPoint()); + sceneryPick.callback = pickCallback; + pickList.push_back(sceneryPick); + } } - } } return !pickList.empty(); - } else { // we can get called early ... - return false; - } } void diff --git a/src/Main/renderer.hxx b/src/Main/renderer.hxx index 918408bcd..f126f2feb 100644 --- a/src/Main/renderer.hxx +++ b/src/Main/renderer.hxx @@ -3,7 +3,6 @@ #define __FG_RENDERER_HXX 1 #include -#include #include #include @@ -46,8 +45,7 @@ public: float zNear, float zFar); /** Just pick into the scene and return the pick callbacks on the way ... */ - static bool pick( unsigned x, unsigned y, - std::vector& pickList, + static bool pick( std::vector& pickList, const osgGA::GUIEventAdapter* ea ); /** Get and set the OSG Viewer object, if any. @@ -57,9 +55,9 @@ public: void setViewer(osgViewer::Viewer* viewer) { this->viewer = viewer; } /** Get and set the manipulator object, if any. */ - FGManipulator* getManipulator() { return manipulator.get(); } - const FGManipulator* getManipulator() const { return manipulator.get(); } - void setManipulator(FGManipulator* manipulator) { + flightgear::FGManipulator* getManipulator() { return manipulator.get(); } + const flightgear::FGManipulator* getManipulator() const { return manipulator.get(); } + void setManipulator(flightgear::FGManipulator* manipulator) { this->manipulator = manipulator; } @@ -69,7 +67,7 @@ public: protected: osg::ref_ptr viewer; - osg::ref_ptr manipulator; + osg::ref_ptr manipulator; }; bool fgDumpSceneGraphToFile(const char* filename); diff --git a/src/Main/viewer.cxx b/src/Main/viewer.cxx index 7c4388b16..c03187619 100644 --- a/src/Main/viewer.cxx +++ b/src/Main/viewer.cxx @@ -50,6 +50,9 @@ #include "viewer.hxx" +#include "CameraGroup.hxx" + +using namespace flightgear; //////////////////////////////////////////////////////////////////////// // Implementation of FGViewer. @@ -81,7 +84,8 @@ FGViewer::FGViewer( fgViewType Type, bool from_model, int from_model_index, _damp_heading(0), _scaling_type(FG_SCALING_MAX), _location(0), - _target_location(0) + _target_location(0), + _cameraGroup(CameraGroup::getDefault()) { _absolute_view_pos = SGVec3d(0, 0, 0); _type = Type; @@ -764,5 +768,7 @@ FGViewer::update (double dt) } } } - + recalc(); + _cameraGroup->update(_absolute_view_pos.osg(), mViewOrientation.osg()); + _cameraGroup->setCameraParameters(get_v_fov(), get_aspect_ratio()); } diff --git a/src/Main/viewer.hxx b/src/Main/viewer.hxx index 808583822..345828846 100644 --- a/src/Main/viewer.hxx +++ b/src/Main/viewer.hxx @@ -32,6 +32,13 @@ # error This library requires C++ #endif +namespace flightgear +{ +class CameraGroup; +} + +#include + #include #include #include @@ -361,6 +368,8 @@ private: // surface at the spot we are directly above SGVec3f _world_up; + // camera group controled by this view + osg::ref_ptr _cameraGroup; ////////////////////////////////////////////////////////////////// // private functions // ////////////////////////////////////////////////////////////////// diff --git a/src/Scenery/tilemgr.hxx b/src/Scenery/tilemgr.hxx index 60752f910..985abb2a5 100644 --- a/src/Scenery/tilemgr.hxx +++ b/src/Scenery/tilemgr.hxx @@ -33,7 +33,11 @@ #include class SGReaderWriterBTGOptions; -class osg::Node; + +namespace osg +{ +class Node; +} class FGTileMgr : public simgear::ModelLoadHelper {