Contents |
|
3D Graphics
When developing a 3D engine for both Windows and Macintosh, one has little choice but to use OpenGL, primarily because it is the only 3D API supported on the Macintosh platform. OpenGL carries the tremendous advantage that it is a cross-platform standard. Almost all of a game’s 3D code can be written once and be guaranteed to work on both platforms. The only piece that is dependent on the operating system is the initial context setup. Both the Windows and Macintosh operating systems supply a small API to link OpenGL to their internal windowing systems. On Windows, it is called WGL, and on the Macintosh, it is known as AGL. These libraries enable an application to query the pixel formats that the 3D hardware supports and to bind an OpenGL context to a window.
Listing
1 shows the Windows and Macintosh versions of a class named
GraphicsContext
which encapsulates an OpenGL context. The
constructor of this class creates an OpenGL context and associates it with
a window. The first step towards accomplishing this is to tell the
operating system what pixel format and context attributes are desired.
Both WGL and AGL supply a function which takes this information and
returns the most appropriate pixel format identifier. With this identifier
in hand, an OpenGL context can be created and bound to a window. It is
important to note that on both platforms, each thread of execution may
have only one active OpenGL context at any time, as specified with the
wglMakeCurrent
and aglSetCurrentContext
functions.
The
SetPixelFormat
function on the Windows platform carries with
it the limitation that it may be called only once for any given device
context. This becomes problematic if you would like to change resolutions
or pixel depths and thus need to create a new OpenGL context for a window
which previously had one bound to it. Destroying and recreating the main
window can just make the problem worse since other game systems which are
also associated with that window would have to be restarted as well. A
nice workaround is to create a full-size child window of the main window
and use it as the window to which the OpenGL context is attached. This
way, only the child window needs to be destroyed and recreated allowing
OpenGL context replacement without disturbing other systems.
Once a
context has been created and rendering has occurred, the image is
displayed by calling a single WGL or AGL function which swaps the OpenGL
drawing buffers. This is demonstrated in the UpdateContext
function shown in Listing 1.
Accessing
OpenGL extensions is the only remaining area in which Windows and
Macintosh methods differ. Once you have determined that an extension is
available (by calling glGetString
with the
GL_EXTENSIONS
selector), Windows implementations must call
wglGetProcAddress
to retrieve a pointer to any function
defined by the extension. Extensions currently supported on the Macintosh
already have entry points for their functions defined in the OpenGL
library, so there is no need to locate the function and call it through a
pointer. As an example, suppose that the GL_ARB_multitexture
extension is available and you want to call the function
glActiveTextureARB
. On the Macintosh, this function is
already defined and may be called directly. On Windows, a pointer to this
function can be retrieved by using the following code.
typedef void (*ATProc)(GLenum target);
ATProc
activeTextureProc =
(ATProc
)
wglGetProcAddress("glActiveTextureARB");
Once this function pointer has been acquired, a small inline function can be defined to emulate a direct function call as follows.
inline
void glActiveTextureARB(Glenum target)
{
(*activeTextureProc)(target);
}
This method
provides a consistent interface to the glActiveTextureARB
function on both platforms. The predefined extension functions will
exist only on Macintosh operating systems predating MacOS X. On MacOS X,
the OpenGl library will provide a counterpart to the
wglGetProcAddress
function, and it will be necessary to
retrieve function pointers in the same way that it is currrently done on
Windows.
Sound
Sound programming differs significantly between the Windows and Macintosh operating systems. On Windows, DirectSound provides the functionality for playing basic sounds and for implementing more advanced effects such as 3D positional audio. On the Macintosh, one uses the Sound Manager to handle their audio needs. The Macintosh Sound Manager by itself does not provide any 3D support, however. Instead, 3D sound is provided by the supplemental use of the SoundSprocket library. Listing 2 shows how to play a buffer of 16-bit, 22.050 kHz, stereo sound on both Windows and Macintosh. Adding 3D effects to this code is beyond the scope of this article, but is not difficult once the level of functionality presented here has been implemented.
The
SoundMgr
class shown in Listing 2 demonstrates the
initialization of DirectSound. The Macintosh Sound Manager requires no
initialization, and thus there is no Macintosh counterpart to this class.
DirectSound requires that you create a primary play buffer and specify
what sound format the hardware should be prepared to play. Sounds are
actually played by allocating secondary play buffers which are then mixed
into the primary buffer by DirectSound. This is demonstrated in the
Sound
class shown in Listing 2. The constructor for the
Sound
class creates a secondary sound buffer and copies the
sound information into this buffer (which may actually be on the sound
hardware). The sound is then played by calling the Play
function. Sounds may be looped by specifying the
DSBPLAY_LOOPING
flag as the last parameter to the
IDirectSoundBuffer::Play
function.
Sound is
played on the Macintosh by allocating sound channels and sending commands
to them. The creation of a sound channel is demonstrated in the
Sound
constructor shown in Listing 2. Once a sound channel
exists, a sound buffer can be played through it by issuing a
bufferCmd
command. This command carries with it a pointer to
an extended sound header, which was filled out by the Sound
constructor. This header contains all of the format information necessary
for the Sound Manager to correctly play the sound.
Playing
looping sounds on the Macintosh is not as simple as on Windows. If you
want a sound to play a finite number of times, you can simply issue
several bufferCmd
’s to the sound channel. (Sound commands are
queued and executed only after any previous command has completed.) If you
want the sound to loop indefinitely, however, you will have to issue a
callBackCmd
command which will notify you when a sound has
finished playing. When a sound channel is created, an optional callback
function may be specified, and this function is invoked whenever the sound
channel encounters a callBackCmd
. A callback function on the
Macintosh has to be specified as a universal procedure pointer, as done in
the Sound
constructor. (Universal procedure pointers are
function pointer abstraction mechanisms left over from the 680x0 to
PowerPC transition.) The callback function itself simply issues another
bufferCmd
and callBackCmd
which continue the
looping process.
An important
issue to keep in mind when playing 16-bit sound is byte order. If you are
playing sound from a *.WAV
file for instance, you will have
to reverse the two bytes in each audio sample before playing them on the
Macintosh since these samples are stored in little endian byte order. A
single audio sample can have its byte order reversed with the following
code.
unsigned short *samp; // Pointer to sample
samp =
__lhbrx(0, samp);
This code
uses the handy PowerPC instruction lhbrx
(Load Halfword Byte
Reversed Indexed), which is accessible from C through the
__lhbrx
intrinsic function. This function loads a 16-bit
value and swaps the low and high order bytes.
__________________________________________________