What is Open Producer?
Sorting Out the Terminology
CameraLearning By Example
Lens
RenderSurface
CameraGroup
CameraConfig
KeyboardMouse
InputArea
Camera
RenderSurface
This section will introduce the concepts and motivations behind Open Producer and introduce the components at a high level. It explains how to use Open Producer in your 3D Graphics, OpenGL applications. Open Producer is built on OpenGL and depends upon it. If you are familiar with OpenGL, many of the concepts will be familiar.
Windowing systems, by design, are user interfaces. The whole concept of a window, a cursor, user generated events by keyboard and mouse, and graphical representations of buttons and sliders, and various other types of "widgets", supports the notion that action begins upon user input. The application waits until the user has indicated some desired action, at which point it reacts and computes, but always returns to a resting state, once again waiting for user input.
Most toolkits provided to 3D graphics programmers are based upon the windowing system paradigm. The common structure is to create a window, a graphics context, callbacks to handle user input and window configuration events, and, then, at some point, the programmer relinquishes control of the main loop to the toolkit. Among the callbacks specified is one that can render the 3D graphics. It may be called after some user input, or continuously if and when the main loop is idle, or based upon some timing event.
Applications residing within this context may be referred to as being interactive. The user expectation is that the application will respond in a timely manner to his input requests, and need not be active when the user is not providing input.
However, another classification exists for 3D graphics applications, which require reliably constant and continuous update rates. These applications strive to emulate the real visual world and must, therefore, update at solid frame rates higher than the human eye can distinguish between discreet frames and continuous motion. These applications can be referred to as real-time graphics applications. Examples of real-time applications are flight (or any other motion) simulators, virutual reality, video games.
The problem that Open Producer attempts to solve then, is the provision of methods for creating graphics rendering contexts for real-time applications. Toolkits that are built in the windowing system paradigm fall short of the needs of real-time graphics programmers, forcing them to write their own methods or causing them to attempt to live within the constraints of the graphical user interface paradigm.
Open Producer borrows concepts from the movie industry, because the mechanics of achieving the goals of a real-time programmer and a movie producer are quite similar. They differ only in the abstract. Open Producer builds upon the analogy of a camera, by providing the components of a camera, and the methods for manipulating a camera.
A movie camera has a constant, solid frame rate. It has a rendering surface where the image from the real world, after having passed through the lens is captured or rendered, one frame at a time. It provides the camera man with the ability to change the field of view. It provides configurability for different qualities of film or rendering surfaces. A movie camera does not have a keyboard or a mouse.
Open Producer takes the concept a bit further in the software abstract than the real camera can, however, providing methods for building assymetrical viewing frustums, orthographic projections and rendering to a projection rectangle within the rendering surface. Further, Open Producer provides the concept of a Camera Group to facilitate the synchronization of multiple cameras together. In the world of real-time 3D graphics this is sometimes referred to as "multi-pipe rendering".
It is important to note here that Open Producer is not implemented to the exclusion of interactive programs. On the contrary, it can reside within the constructs of graphical user interfaces. To this end, Open Producer has some user interface provisions such as a KeyboardMouse class. One desirable feature of KeyboardMouse is that it tracks user input seamlessly across multiple windows, known as an InputArea. It is also important to note that Open Producer is not designed to be a user interface. How this is reconciled will be covered in future sections.
Figure 1 shows a diagram of the abstract Open Producer camera. To explain the components and how they relate to OpenGL, consider the process of rendering a 3D scene in OpenGL. The world is defined in three-dimensional cartesian coordinates. Objects are defined in model space and moved into the scene by transforming them through the modelview matrix. A complete scene is rendered and multiplied against a Projection Matrix, which projects the three dimensional world to a 2 dimensional screen. In the process, it can only render that which resides inside the viewing frustum, which is defined by the internal values of the Projection Matrix.
The OpenProducer Camera's Lens generates the OpenGL Projection Matrix. The scene is projected to a projection rectangle on the Open Producer Camera's RenderSurface. The Camera itself can have the attributes position and attitude, which place it somewhere in the world. However, the Open Producer Camera does not have the ability to define the scene. It provides a Scene Handler, which the programmer can use to define the 3D scene.
Let's review these terms and apply some more formalized definitions of them.
For a complete description of the Camera interface click here.
For a complete description of Producer::RenderSurface interface click here.
The code examples are distributed with the Open Producer source, with cross-platform build environments. The reader is encouraged to compile, modify and recompile these examples for a hands-on training experience.
It is best to take the examples in the order that they are given as often later examples will build upon lessons learned in the earlier examples.
The Camera class' scope of responsibility ends with describing the scene it is capturing. As our real world model of a camera does not actually generate the light or the objects the light is illuminating, neither does the Producer Camera class have methods for drawing a scene. The application must therefore provide an implementation of a SceneHandler class derived from the pure virtual class Producer::Camera::SceneHandler. The Scene Handler is discussed in subsequent paragraphs.
The Camera class has a set of defaults. The first example demonstrates the use of the Camera in its simplest form, but just using the defaults.
// C++ source file - Open Producer - Copyright (C) 2002 Don Burns // Distributed under the terms of the GNU LIBRARY GENERAL PUBLIC LICENSE (LGPL) // as published by the Free Software Foundation. // Simple example of use of Producer::Camera // The MySceneHandler class is a simple sample of a Camera::SceneHandler #include <Producer/Camera> #include "MySceneHandler" int main(int argc, char **argv) { // Declare the camera Producer::ref_ptr<Producer::Camera> camera = new Producer::Camera; // Optional. Configure the size of the camera's render // surface. Without these lines, the RenderSurface would // fill the whole screen and have no border Producer::ref_ptr<Producer::RenderSurface> rs = camera->getRenderSurface(); rs->setWindowRectangle( 100, 100, 640, 480 ); rs->setWindowName( "Producer Example using Camera" ); // Tell the camera about the Scene Handler. See notes in MySceneHandler camera->setSceneHandler( new MySceneHandler ); // Main loop. Note that the while() statement comes after camera->frame() // because the RenderSurface is not realized until the first call to camera->frame(). do { camera->frame(); } while( rs->isRealized() ); return 0; }
The first thing to note is that the code snipet is a bit smaller than the RenderSurface example. In fact, if the user's goal to use the entire screen and not set a window rectantle by accessing the Camera's RenderSurface, then the code in main() would only have four significant lines. The real item to focus on in this example is something called the SceneHandler. We'll go into detail as to what this is and how it works in the succeeding paragraphs, but first lets take a look at the first few lines of code.
int main(int argc, char **argv) { // Declare the camera Producer::ref_ptr<Producer::Camera> camera = new Producer::Camera;
Ok... you must be asking yourself at this moment, "What is a
There is a good article that goes in depth on the use of reference pointers here. For now, and in the interest of readability of the code, simply think of a ref_ptr<> as a normal pointer like this:
But it is important to invest the time to understand how ref_ptr<>'s work. All the examples in this tutorial will use ref_ptr<>s.
When a Camera is declared, it already contains its default components and one of them is a RenderSurface. You may assign a RenderSurface to the camera (with setRenderSurface()) if you wish, but getRenderSurface() will return a pointer to the default RenderSurface. In the code here, the RenderSurface is not yet realized so all configuration methods may be called. The RenderSurface gets automatically realized on the first call to Camera::frame().
Note that Cameras may only have one RenderSurface. However, and contrary to our analogy, multiple Cameras may use one RenderSurface. Each camera must define its ProjectionRectangle within the RenderSurface. For OpenGL programmers, this is similar to using multiple glViewport 1()'s in one Window.
|
|
|
If your defined SceneHandler chooses to override everything the Camera does in Camera::frame(), you may define frame() in your derived SceneHandler, returning true. In your frame() method, you must clear the screen, set up the OpenGL PROJECTION matrix, the OpenGL MODELVIEW matrix, draw your scene and swap buffers. Note that a reference to Camera is passed to frame(), giving you access to its components, including the RenderSurface.
But, it makes better sense to allow Camera to do its job in Camera::frame() and the SceneHandler to do its job in cull() and draw(). The cull() method provides the SceneHandler the opportunity to do rendering optimizations such as sorting by visibility, transparency and optimized rendering order. For large scenes, this step is important, but not trivial to implement. The use of a scene graph will greatly simplify this step. Typical scene graphs will have a cull stage, and use only the PROJECTION matrix as input. The PROJECTION matrix is available from the Camera::Lens reference passed to cull().
If you implement the useAutoView() method in your derived SceneHandler and return false, then the setting up of the PROJECTION and initial MODELVIEW matricies will be left to the SceneHandler. If you simply use the default method, however, the Camera will do this for you by either default values, or values you have previously passed to the Camera class to set this up. See the next example for a more throrough handling of Camera values.
In your derived SceneHandler draw() method, draw your scene. You may want to use draw lists created in your cull() method or some other method of handling the scene optimally. If your scene handler defined useAutoView() and returned false, then you must set up your PROJECTION and MODELVIEW matricies before rendering the scene, but if not, simply use draw() to focus on drawing the scene.
By default, the Camera::frame() method will swap the RenderSurface buffers at
the end of the frame. This may not be desirable if, for example, you
have multiple cameras rendering to the same RenderSurface. In this
case you can override the default boolean value passed in Camera::frame() to
false and buffers will not be swapped until you call Camera::advance().
However, in this case it is probably better to use the CameraGroup() class,
discussed in the next section.