Hugin's OpenGL preview system overview
This page describes features in an experimental branch of Hugin, it is not in the stable versions, or the main trunk (yet). The information on this page may be outdated quickly. This page gives an overview of how the SoC 2008 Project OpenGL Preview works, for interested developers.
The OpenGL preview's window is a GLPreviewFrame class, similar to the PreviewFrame that the accurate preview uses. This class contains all the GUI controls in the window. The preview itself is a GLViewer widget, which is derived from wxWidget's wxGLCanvas.
There is a class called ViewState that is handles finding what has changed in the panorama, so that it can perform the necessary steps to update the preview. It updates automatically when the panorama changes, but changes to the preview can be made directly using the ViewState object, so that the preview is updated while the panorama is not. This is used for interactive updates, such as when adjusting the field of view using the sliders.
Displaying remapped images
The images in the preview are drawn using two components: the image, with or without photometric correction; and a set of faces that approximate the remapping performed.
The actual image content is managed by a TextureManager. It creates an OpenGL texture containing a scaled down version of the original image, in 8 bits per colour channel per pixel. The texture is then either a 24 bit RGB, or a 32 bit RGBA texture depending on the presence of a mask. With real time photometric correction, the original image has the same colours as the texture (unless it is an HDR image). With full photometric correction, photometric correction is performed on the texture before it is passed to OpenGL.
The remapping is approximated by a set of quadrilaterals. Each vertex of each quadrilateral has vertex coordinates (where it appears in the output) and texture coordinates (where in the texture that represents the image it should be map to). Using this information, the OpenGL renderer can display faces that show parts if the input image. The mapping of remapped images to image numbers is handled by a MeshManager object, but the transformations are performed by child classes of MeshRemapper. The MeshManager creates an OpenGL display list that contains the instructions for drawing the faces.
The MeshManager only uses ChoosyRemapper, a child of MeshRemapper. This picks between the other two MeshRemappers: VertexCoordRemapper and TexCoordRemapper. The VertexCoordRemapper transforms texture coordinates to the corresponding vertex coordinates. It uses an adaptive subdivision method: first the outer corners are transformed, then the single face that is produced is divided into four smaller faces, by splitting each edge exactly in half. The extra coordinates are then mapped to their correct positions. The process repeats on these smaller faces until the geometric difference created when splitting the face becomes negligible, or the face is too small to be worth doing anything with, or the face is significantly outside the panorama. To avoid certain situations where subdividing a face produces little extra detail but subdividing it more shows some, there is a minimum amount of subdivisions that occur. There is also a maximum subdivision level to limit memory usage. With the cylindrical projection types, an image that crosses the +/- 180° seam is not approximated well by this remapping. There will be faces that stretch across the width of the panorama, where the image actually goes off one end and comes on the other. To correct for this discontinuity in the projection, the remapper tries to find the stretched faces, and changes them into two faces that go off the edges instead of one that goes across the middle.
The TexCoordRemapper works the other way around, it specifies vertex coordinates in a grid over the panorama, and then projects these into the image coordinates which it uses for the texture coordinates. The faces are then clipped so they only contain the desired part of the image, and nothing outside of it. This projection is preferable where a point is stretched into a line or disk, such as the poles of an equirectangular projection, or the outside ring of a disk-like projection. In these circumstances, the VertexCoordRemapper would only see one point of the ring or line, so it does not perform so well. However the TexCoordRemapper does not in general handle curved images as neatly as the VertexCoordRemapper, and the TexCoordRemapper samples much of the panorama outside of the image, so the TexCoordRemapper sees limited use.
The ChoosyRemapper gets VertexCoordRemapper or a TexCoordRemapper to do all the remapping work, but it decides which one to use by detecting when an image crosses a pole or similar.
The textures and images are brought together to draw the preview. The actual drawing is managed by a GLRenderer object.
The interactive tools in the preview derive from the PreviewTool class. PreviewTools can change how the preview is drawn and respond to the user. There is a PreviewHelper object that each tool has access to, which provides common information to the tools and manages the events they use. An instance of each tool is created by the GLPreviewPanel, after GLViewer has created an OpenGL context. This is important as tools may wish to create OpenGL textures when they are created. The GLPreviewPanel panel then uses the PreviewToolHelper to "Activate" a tool when the user hits the button. The helper calls the Activate function of the tool, the tool requests any events it needs notification of, and the helper disables any other tool that needs notification only one tool should have (such as mouse clicks) and returns the list of disabled tools back to the GLPreviewPanel, which updates the preview window's buttons to reflect this. The GLViewer tells the helper when some wxWidgets events occur, it processes these as necessary (for example, converting mouse coordinates to panorama coordinates, finding if the images under the mouse have changed) and tells any tool that has registered for notification of any occurring events by calling the corresponding virtual function of PreviewTool.