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 {