Windows are the general connection between OpenSG and the windowing system used by the application/platform. OpenSG itself does not open its own windows, that has to be done by the application. Using GLUT it's pretty trivial, take a look at the tutorial examples on how to do that. For other window systems its a little more work, but the goal is to have wrapper classes for the usual GUI toolkits like QT, Motif etc. that simplify the task. We have one for QT, OSGQGLManagedWidget, and are interested in similar ones for other Window systems.
It doesn't do any input event handling or similar things, it's only for output and keeping the osg::Viewport instances that fill the window and keep all the rendering parameters. See SimpleSceneManager for an easy-to-use wrapper for setting these up.
There should be a 1:1 relation between an osg::Window and a window-system window. As such the osg::Window is responsible for handlng the OpenGL related tasks such as OpenGL object and extensions management (see OpenGL Objects & Extension Handling for details on that).
The size of the viewport is defined by its left, right, bottom and top coordinates, given in OpenGL conventions, i.e. the bottom of the screen has the vertical coordinate 0. If the value is bigger than 1, it's a position in pixel. That's independent of the window size, if the window is smaller, parts of the viewport will be cut, if it's bigger parts of the window will not be covered. If they are between 0 and 1 (inclusively) they are relative to the window and are rescaled when the window is resized. If they are -1 they use the extreme applicable value, i.e. 0 for left and bottom, 1 for right and top. For relative sizes the actual value used for right and top is value * size - 1. This allows abutting viewports by using the same relative values for right and left of the viewports that should fit. See the image for an example.
Viewports in differently sized windows
An exception to this size definition is the osg::PassiveViewport. It is meant to be used for integrating OpenSG into other applications and takes the size from the currently active OpenGL viewport and thus ignores its settings. This is pretty extreme, in most cases it will probably be enough to use a osg::PassiveWindow and set the Viewport parameters to not interfere with whatever other OpenGL rendering is taking place.
To define what is being rendered a viewport stores the root osg::Node of the scene graph to be displayed, the osg::Camera, the osg::Background and osg::Foreground instances to use. Of these, only the osg::Foreground is optional, all others are needed to draw or render the osg::Viewport.
There are some special kinds of viewports for specific purposes. The default osg::Viewport is for single buffered rendering, the osg::StereoBufferViewport is used for quad-buffer stereo rendering and the osg::ColorBufferViewport allows rendering to specific color channels only.
Hint!
The osg::StereoBufferViewport only works if the selected window actually has the four buffers. So make sure that your graphics card supports quad-buffer rendering (i.e. has Visuals that are stereo) and that the selected Visual is one of those.
The osg::PassiveViewport is totally passive. It ignores its set size and position and instead takes the current OpenGL settings. This allows pretty simple integratino into other OpenGL programs, but the OpenGL context must be active and the viewport set correctly for it to work.
Position and orientation of the camera are defined by a node in the scene graph, a beacon, similarly to the definition used by light sources. The camera uses the OpenGL defaults for specifying the used coordinate system, i.e. the camera looks along the negative Z coordinate, X points to the right and Y is up. Thus, to use a camera you need a beacon node in the scene to define its position. This can be an object you want to attach the camera to, but in general you'll probably have a Transform node somewhere close to the root to handle it.
This gives full flexibility to use a simple matrix to define camera position and orientation, but can be tedious to specify. Many systems use a from-at-up convention to define camera parameters, i.e. you specify a viewer position, a point that should be in the center of the screen and the direction that should be up on the screen. The osg::MatrixLookAt functions from OSGMatrixUtility.h can convert these settings into a matrix that can directly be used to specify the camera.
The internal parameters of the camera can vary between different kinds of cameras. The only constant thing that a camera for OpenGL needs are the near and far clip distances, which are defined in the general Camera class. The others are defined in the specific camera classes.
The camera parameters needed for rendering are split into three matrices. The first is for viewing, representing teh viewing part of the OpenGL GL_MODELVIEW matrix. The GL_PROJECTION matrix is split into two. The first is the real projection matrix, the second is a projection transformation matrix. The projection transformation is used to add a coordinate system for non-coplanar multi-screen projection setups like CAVEs. See "High-Quality High-Performance Rendering for Multi-Screen Projection Systems" by Dirk Reiners, in Proceedings of the 3 rd International Immersive Projection Technology Workshop for a motivation of this additional coordinate system.
The camera has a bunch of functions to access its parameters and derived values like its viewing frustum, and also to calculate rays through viewport pixel for picking etc.
Ext:
To add a new camera it is necessary to derive from Camera (or one of its descendents) and to override the getProjection() method. It might make sense to also override the getViewing() and/or getProjectionTranslation() methods, in many cases that won't be necessary.
Dev:
It is a little lenient in that it interprets angles > Pi as degrees. Dont know if we should make that official, though. We also need some provisions for non-square aspect ratios.
The main idea is to allow enhancing/manipulating arbitrary cameras for new features with minimal impact on applications. The osg::ShearedStereoDecorator for example allows using any camera, whether it's a perspective, orthographic or whatever camera, to control a stereo projection. Todo that it is only necessary to create two viewports, one for the left and right eye, and use two decorators to decorate the single camera for the left and right eye. Changes to the camera's parameters can be done just like in a single view application and automatically influence all affected viewports, and the difference neccessary to create the different images for the left and right eye are managed by the decorators.
Ext:
To create a new osg::CameraDecorator it is just necessary to derive from osg::CameraDecorator and override the function that needs to change. To access the parameters of the decorated object the osg::CameraDecorator::_sfDecoratee field can be used. See osg::ShearedStereoCameraDecorator for an example.
The part of the image displayed is defined by its left/right/bottom top coordinates and the width/height of the full image.
Hint!
Pixel-based osg::Background instances like Gradient Background will not work with the osg::TileCameraDecorator, as they directly access the physical viewport right now.
The main use is to manipulate the viewing matrix to allow a single camera to look into different directions in different viewports. Note that if you want to use a head-tracked environment, the PageSystemWindowCameraDecoratorsStereoProjection makes that a lot easier.
The osg::StereoCameraDecorator is the base class for all the Decorators that are able to do this. It keeps the generic parameters that are needed for every stereo image: the eye separation and the left/right eye distinction.
The eye separation defines the distance between the two eyes, given in the units used in the models, not some global unit like centimeters.
The left/right eye distinction is handled by a simple bool, which is true for the left eye decorators, and false for the right eye's.
The actual algorithm to generate the images is defined by the specific decorator.
The only parameters it needs is the zero parallax plane distance, which defines the distance to a plane where left and right eye images perfectly overlap (i.e. they have zero parallax). This is the plane that the eye will associate with the projection screen or monitor, and the distance should be accurate, as far as possible, to create the most realistic result. It is defined in model units, just like the eye separation.
The osg::ProjectionStereoCameraDecorator is used to calculate the viewing and projection matrices needed for head-tracked stereo setups like some power-walls or, mostly, CAVEs.
The main difference to standard stereo stems from the fact that the user can now move in front of the screen. Thus the projection matrix needs to be created so that the center of projection is at the user's eye position and the frustum encloses the projection screen. As OpenGL viewports can only be rectangular, this only works right for rectangular projection screens conceptually. This rectangular projection screen is defined by its four corners, which have to be given to the osg::ProjectionStereoCameraDecorator. The order for the four corners is lower-left, lower-right, upper-right and finally upper-left.
Dev:
Internally the corners are used to calculate a local coordinate system of the screen, defined by a left, bottom and normal vector. In addition the width and height of the projection screen are calculated. All of these are used to quickly calculate the projection matrix. When looking at the parameters for glWindow that is actually pretty simple and left as an exercise for the reader. ;)
The other information needed to calculate the matrices is the position of the user's head. This is usually delivered by a mechanical/magnetic/optical tracking system, which has its own coordinate system. This is coordinate system to be used for the user's head position as well as the corners of the projection screen.
The is an additional coordinate system that needs to be taken into account, that's the relation between the projection system and the world. As most projection system setups are smaller than the scenes they depict there is a way to move the whole system in the world, in addition to the user moving inside the projection system. This is conceptually equivalent to the standard user navigation in a scene. To hide the specifics of the osg::ProjectionStereoCameraDecorator from an application that should also be used for standard screens, those two coordinate systems are defined by the coordinate systems of two osg::Node instances.
One of them is the osg::Camera's beacon Node. This is used to move the projection system in the scene, just as if it was a simple user.
The other is the osg::ProjectionStereoCameraDecorator's user Node, which should define the world position of the user's head. As the tracker generates relative coordinates, this node should be a descendant of the beacon node.
Thus the sub-graph for a head-tracked projected stereo should best look like this:
beacon->user (need a real picture here)
This allows simple switching between standard and head-tracked mode, in which the eye position in standard mode is the origin of the coordinate system in head-tracked mode.
In general it has a set of increasing sky angles and a set of sky colors (one more than angles, for the apex). It has a similar set of parameters for the ground. The sky box is defined by 6 (optional) textures, one for each side of the cube.
Images have to be loaded as osg::Image instances, their position has to be defined as a 2D position in the [0,1]x[0,1] range.
The polygon is specified using a set of 3D texture coordinates and a set of 2D positions. Both have to contain the same number of entries. The positions can be interpreted in different ways. Values larger or equal to 0 are interpreted starting from the left/bottom border of the viewport, values less or equal to -1 are interpreted starting from the right/top border of the viewport, which -1 being the right/topmost pixel of the viewport. Whether these values are relative to the viewport size or represent absolute pixel values is determined by the normalizedX/normalizedY fields. If set to true, the calculated position is relative, with a 0 to 1 (or -2 to -1) range covering the whole viewport. Otherwise they are direct pixel positions.
The size of the osg::Image is used to define the size of the grabbed area. If the image is set, but its size is 1 pixel in one dimension it is resized to the size of the viewport (The 1 is needed as it's impossible to create 0x0 pixel images).
It can be activated/deactivated using a flag, per default it is active. There's no need to set the image on the osg::FileGrabForeground, it is created automatically on first use.
To actually write the image the target file name needs to be set. To write a sequence of frames it also keeps a frame counter, which can be automatically incremented after each grabbed image. The name is used for a printf-style command, thus "%d" in the name is replaced by the frame number.
Hint!
Use "%04d" to create file names with leading zeros.
The osg::StatisticsCollector that is used to collect the elements needs to be set in the foreground, as well as the list of osg::StatElemDesc IDs that should be displayed.
osg::SimpleStatisticsForeground displays the statistics info as simple text lines. They are displayed using a compiled-in font that can use an arbitrary color and that can be arbitrarily resized, with the size per line given in pixel.
The format of every element is given by a format string for every element that is directly passed to osg::StatElem::putToString(), so go there to see the possible options.
If no elementIDs are given all elements in the osg::StatCollector are display, using the default format.
osg::GraphicStatisticsForeground displays the statistics info as one of a set of graphical elements. The possible elements are:
Every display can be put at an arbitrary position on screen, defined by a position in X-Window style, i.e. negative positive are taken to be relative to the right edge of the screen. The size of every element can be given either in pixel or relative to the viewport size, similar to the viewport itself, i.e. sizes <=1 are taken to be relative. Every display can have up to three independent colors. The min and max displayable values can either be set directly, or they can be adapted dynamically. To control that adaption there are two flags (Overflow Resize and Underflow Resize), which can be set for every display.
Other flags allow the display the min and max values as text, the display of the reciprocal value instead of the actual value, the display of dots at the positions of data points and the smoothing of the display. The smoothing is done using a running average filter of selectable length. If filtering is used the displays show two indicators, one for the current, one for the filtered value.
Some general attributes for all displays can be set: the line width used, the background color used and if the displays should have a drawn background and/or drawn border at all.
As a consequence the osg::Navigator is the most useful of the navigators and should be used in applications.
The default active navigator is the osg::TrackballNavigator. For this navigator the osg::Navigator will map a left mouse click with motion to a rotation, a left mouse click without motion will set the center of rotation to the hit point. A middle mouse click or move will translate in screen x/y, while keeping the hit point under the mouse. A right mouse click with vertical motion as well as using a mouse wheel (if configured to generate button 4 & 5 events) will move the viewer in the z direction.
The osg::FlyNavigator uses fly forward on left mouse button, backwards on right mouse button and stay still on middle mouse button. In all modes mouse motion is mapped to rotation.
Rotation is done by dragging a point on the sphere, translation by focussing the sphere on a different position in space or by dragging the whole sphere. The size of the sphere can be changed, by default it is 80% of the screen size. It is also possible to click and drag outside the sphere. A useful special case is clicking and dragging at the extreme left and right edges of the screen, which can be used to rotate the model around the screen z-axis.
The distance between the center of rotation and the viewer can also be changed by dragging, resulting in a changing zoom.
The primary parametrisation is a from/at/up point/vector triple, which can be used to initialize and read from the navigator.
The main application area are architecture walkthroughs, where the user walks on a ground through one or more buildings.
It does not open a window itself, that is left to the user to keep the SSM useful for arbitrary window systems. The window has to be passed to the SSM by using setWindow(). That's one half of the necessary initialization. It can't handle input itself, the application has to pass it user input events. It's a lot simpler than it sounds, take a look at the tutorials to see how it works.
The other half of the necessary initialization is telling SSM what to draw by calling setRoot(). That's it. It might be useful to call showAll() to position the camera at a reasonable position, but that's not mandatory.
The SSM can be used in conjunction with any window system, it has been integrated into an easy-to-use QT widget called OSGQGLWidget. See testManagedWindowQT_qt.cpp for an example on how to use it.
As a little bonus, the SSM can display the "Powered by OpenSG" logo. Just call useOpenSGLogo() and you're done. ;)
1.4.3