|
|
|
Conceptual Overview
Immediate Mode GraphicsST3D makes use of the OpenGL application programming interface (API) to render low-level graphic primitives like lines, points and polygons. OpenGL is considered an "immediate mode" interface since it generally doesnt manage or retain any information about the graphic primitives it draws. It simply draws a primitive when instructed to do so. For example, the following OpenGL commands, invoked from within Smalltalk, draw a green triangle on a black background (To try this code out, open the "CC OpenGL Lab" example in submenu/subfolder Tools->ST3D->ST3D Sample Applications).
If the view in which the triangle is rendered were subsequently invalidated for some reason (e.g., covered with another window and then uncovered), the triangle would be erased and it would be necessary for the application to re-execute the OpenGL commands to redraw the triangle. Although OpenGL is a stable, well-documented and relatively easy to use API, building an application like a game or modeler directly on top of an immediate mode interface can be a complex and time consuming activity. To simplify and accelerate the development process, ST3D provides a number of object-oriented services, frameworks and tools that, in many cases, make it unnecessary to learn OpenGL or use it directly. For developers who would still prefer to work at the OpenGL level anyway, ST3D provides "raw" access to dozens of OpenGL functions and related structures. Also, even if not used for application development, ST3Ds higher level services (which are, themselves, built on OpenGL) provide a wealth of OpenGL programming examples. Additional details on ST3Ds OpenGL subsystem can be found in Appendix A. For additional sources of information on OpenGL itself, see the section entitled "OpenGL" in Appendix F .
Retained Mode Graphics and Scene GraphsST3D provides a "retained mode" graphics system built on top of OpenGLs immediate mode graphics. The retained mode layer supports the creation of graphic objects with associated state like position, orientation, color and texture (e.g., a spaceship located at position 500 @ 100 @ 0). These graphic objects are referred to as "nodes" and are held in a structure called a "scene graph". A scene graph represents a complete three-dimensional world. It consists of a hierarchy of CcSceneGraphNode subinstances with an instance of CcRootNode at its root (See Figure 1). A node has exactly one parent (except for the root node, which does not have a parent) and can have one or more children.
Figure 1. Example of an ST3D scene graph Attributes such as color and textures can be assigned to nodes to change their appearance. Each node also holds an instance of a CcTransform that affects its location, size and orientation. A brief overview of the CcSceneGraphNode class hierarchy is presented below. Additional detail can be found in class and method comments.
Shapes and GeometryCcShape is an abstract subclass of CcPart that holds a single subinstance of CcGeometry. An instance of CcGeometry defines the raw, three-dimensional form of a shape (i.e., its appearance as a cube, sphere, or pool table). CcGeometry subclasses CcVertexGeometry and CcQuadricGeometry implement this spatial definition in two distinct ways. CcVertexGeometry uses an explicit list of vertices (CcVertex) and faces (CcFace) to define its form. For example, an instance of CcVertexGeometry representing a cube would contain eight vertices and six faces with each face referencing the four vertices that define it. CcVertexGeometry instances are created, modified and saved to disk as .geo files using the ST3D Geometry Editor. CcQuadricGeometry and its subclasses make use of OpenGL "quadric" objects to define shapes. OpenGL quadric objects automatically create the appropriate set of faces and vertices based on their type. Unlike instances of CcVertexGeometry, the faces and vertices of a CcQuadricGeometry are managed internally by OpenGL and are not accessible from Smalltalk. This means that quadric objects are generally used "as is" and are not defined and saved to disk using an editor. CcQuadricGeometry and its subclasses do allow some degree of configuration, however, related to level of detail (e.g., number of slices and stacks in a sphere). OpenGL provides several types of quadric objects. These types are supported by subclasses of CcQuadricGeometry (e.g., CcSphereGeometry, CcCylinderGeometry).
TransformsCcSceneGraphNodes hold an instance of CcTransform. A transform determines a nodes position, orientation and scale with respect to its parent nodes coordinate system. As a simple example, if the position of node A is 5 @ 0 @ 0 and it has a child, B, with a position of 2 @ 0 @ 0, child Bs "overall" or "world" position is its parents position plus its own position, or 7 @ 0 @ 0. The position, orientation and scale specified by a nodes transform are considered its "local" values (again, with respect to its parent). When a nodes local transform values are translated into the coordinate system of the scene graphs root node, they are referred to as "world" values. Services are available on CcSceneGraphNode for accessing a nodes local position, orientation and scale and for mapping these local values to world values (and visa versa). CcShape uses a second instance of CcTranform to control its dimension and origin. This transform, held in instance variable geometryTransform, does not affect the size or position of a shapes children.
Node AttributesSubinstances of CcAttribute can be assigned to a scene graph node to affect various aspects of the nodes appearance. These aspects include color, shading, shine and face culling. The attributes of a node are inherited by its descendents. If a node is assigned the color "red", for example, all of its descendents will be red. CcShape also allows textures to be associated with faces and colors to be associated with either individual faces or vertices. These component level attributes are not inherited by the shapes children.
Rendering a Scene GraphIt is often necessary to be able to provide several different "on screen" renditions of the same scene graph node. For example, in addition to its "normal" rendition, a node might have a highlighted rendition and a rendition used by the hit detection (picking) process. Because of the variety of ways in which it might be rendered, a node is usually not responsible for rendering itself. Instead, ST3D uses a render manager (CcRenderManager) and a collection of renderers (subinstances of CcNodeRenderer) to render a scene. The render manager maintains a mapping between node classes and instances of node renderers. As the render manager traverses a scene graph, starting with the root node, it delegates the bulk of the rendering work to the appropriate renderer as each node is encountered. A node renderer is responsible for translating the high-level information contained in a scene graph node (e.g., red box at position 2 @ 3 @ 4) into the proper sequence of OpenGL drawing commands.
User Interface SupportServices for rendering scene graphs in a view and receiving mouse and keyboard input are provided by CcOpenglPresenter and CcOpenglView. The model of the presenter/view pair is normally set to the root node of the scene graph that is to be rendered in the view. The presenter also holds a subinstance of CcCamera (See Figure 2). The location and orientation of the camera determines the viewpoint from which the scene is rendered. When asked to render the scene graph, the presenter first sets its camera as the active camera in the scene graphs switch node (See Figure 2). It then triggers an event (#renderRequired) which provides clients of the presenter (e.g., an application shell) an opportunity to render the scene. The application usually delegates the actual rendering work to one or more instances of CcRenderManager. For example, CcEditor contains a single CcOpenglPresenter but uses two render managers: one for default object rendering and one for highlighting selected objects. When the #renderRequired event is triggered, the editors handler for the event tells its default render manager to render the scene graph, starting with the scene graph root. An alternative to registering an event handler is to have the CcOpenglPresenter initialize a default render manager. This is done using CcOpenglPresenter>>initDefaultRenderer. In this case, only the default rendition of the nodes is displayed (For an example of this, see CcSimpleSceneExample). It is important not to modify the scene graph in any way while it is being rendered. For example, it would be unwise to fork a separate process that updates the scene graph independently of the process that renders the scene. Changes to the scene graph and scene rendering should always be serialized (See CcAnimationExample1).
Figure 2. Connection of user interface components to scene graph High-performance Rendering Using Display Lists and Vertex ArraysOpenGL allows a sequence of commands to be compiled and stored in a structure known as a "display list". Once created, a display list can be rendered with a single OpenGL function call. This single call typically takes much less time to execute than the series of discrete calls used to build the list. Any part, shape, or geometry object can be "display listed" by sending the object #enableDisplayList. It should be noted, however, that changes to an objects attributes (e.g., color) will not be reflected in its appearance if its display list has already been initialized. If an attribute is changed, the display list should be reset using #resetDisplayList. Nesting of display lists is not supported. Another OpenGL feature exploited by ST3D is vertex arrays. Vertex
arrays, like display lists, allow an entire shape to be rendered with only a few calls to
OpenGL. Unlike display lists, however, the use of vertex arrays is not enabled by sending
the shape a message. Instead, the node renderer responsible for rendering the
shape examines the shape’s properties to see if vertex arrays can be used.
In the case of a CcMeshShape, the renderer is always able to make use of vertex
arrays since mesh shapes store their vertex and face data in a vertex array
"friendly" format. CcVertexShapes, on the other
hand, must contain either all triangles or all quads, be smoothly shaded
and have no component-level attributes in order for the
renderer to make use of vertex arrays. Part PersistenceInstances of CcPart are saved to disk using the Dolphin binary filer (extension .STB). The instance of CcVertexGeometry that is held by a CcVertexShape is not saved to the .STB file along with the rest of the shape, however. Instead, a reference to the geometry instance (the geometrys filename) is saved. CcVertexGeometry instances are saved to disk in an ST3D specific format using CcGeometryWriter. To allow flexibility in organizing files within the file system, the geometry filename saved with the vertex shape does not include any path information. When materializing a CcVertexShape instance from its STB file, its associated geometry is materialized from the first geometry file found in the ST3D\Geometry folder hierarchy that matches the specified file name. It follows that all geometry files within the Geometry folder hierarchy should have unique names. The above discussion also applies to instances of CcTexture held by CcVertexShape. Instead of saving the texture along with the shape, a reference is saved which points to the filename of the textures .BMP or .TXR file (located in the ST3D "Images" folder hierarchy). Unlike, CcVertexGeometry, an instance of CcMeshGeometry is saved along with its containing CcMeshShape when the mesh shape is saved.
Object CachingST3D caches instances of CcVertexGeometry and CcTexture since they tend to be large and time consuming to load. Instances of these classes are sharable across multiple subinstances of CcShape and are uniquely identified by their filename. Cache entries are lazy initialized when a specified filename is requested. Geometry and texture caches can be cleared by sending the appropriate class #resetCaches.
Modeler SupportA "modeler" is an application that allows a 3D model to be edited in a WYSIWYG environment and saved to disk (See Figure 3). A 3D model is nothing more than a scene graph containing one or more instances of CcVertexShape but it can represent anything from a spaceship to a landscape to the interior of a building. Like other 3D applications, a modeler renders its scene graph in a CcOpenglView with the assistance of a CcOpenglPresenter, a CcRenderManager and various subinstances of CcNodeRenderer. Unlike the average game or simulation, however, a modeler must allow specific shapes and components (e.g., vertices) in the model to be selected, highlighted and manipulated. In some situations, the most natural way of editing the model is via direct manipulation using the mouse (i.e., drag/drop). In other cases, more precise methods, like forms-based data entry, are needed. ST3D provides services for hit detection (a.k.a., "picking"), selection management and state management (i.e., undo/redo support). A framework for the development of interactive modeler tools (e.g., "rotate part" tool or "scale texture" tool) is also available. To further simplify modeler development, a subclass of CompositePresenter, CcEditor, is provided that pre-configures many of these services. See Appendix B, "Inside the Part Editor", for more information on the services and frameworks available for building modelers.
Working With Input DevicesST3D supports two distinct mechanisms for obtaining data from input devices: the standard event-based system provided by Dolphin Smalltalk and a system based on Microsofts DirectX DirectInput. The event-based system allows an application to respond to an event generated by the keyboard or mouse, as the event occurs. This system is familiar to most Smalltalk developers and is somewhat easier to use than the DirectInput approach. The DirectInput system allows the state of the keyboard or joystick to be read (or polled) at a point in time that is convenient for the application. The polling-based approach is preferable for applications that involve animation since use of the event-based mechanism can adversely affect the frame rate of an animated scene. (Note: A given CcOpenglPresenter can only use one of these mechanisms at a time to access keyboard data. For example, if an application initializes the DirectInput-based system to read keyboard data, no keyboard events will be generated or received). See Appendix C for more information on working with input devices.
Sound Effects and MusicST3D allows wave files (.WAV) to be played using DirectXs DirectSound subsystem. It also allows MIDI music files (.MID) and MP3 files to be played via the Windows Multimedia Command Interface (MCI). The "CoolPool" and "Lander" applications demonstrate the use of DirectSound within Smalltalk. Sound effectsThe key DirectSound-related classes are CcDxs and CcDxsSoundBuffer. CcDxs represents the DirectSound subsystem and provides services for playing wave files and for accessing cached instances of CcDxsSoundBuffer. In order for an application to use DirectSound, it must associate its application window with class CcDxs. CcDxs can only be associated with a single application window at a time (See methods #onViewOpened and #onViewClosed in CcGameShell). An instance of CcDxsSoundBuffer is created for each wave file played. CcDxsSoundBuffer provides services for playing, copying and setting the volume and frequency of a sound. Sounds can either be played in "one shot" mode or "looped" so that the sound is played over and over. Multiple sounds can be playing at the same time. Like parts, sound buffers are cached and accessed via a unique file name. Wave files must be located in the ST3D\WAV subdirectory tree. MusicCcMediaPlayer provides services for playing a MIDI or MP3 file. Once the desired selection (i.e., file name) has been specified, a media player can be told to either play the selection once or play it repeatedly in a loop. DirectSound generated sound effects and MIDI/MP3 music can be playing at the same time and each subsystems volume level can be varied independently of the other. It is important, however, that the application initialize the DirectSound subsystem prior to initializing the media player if the systems are to be used concurrently. |