VR Using QuickDraw 3D - 2
Volume Number: 14
Issue Number: 8
Column Tag: Power Graphics
Desktop VR using QuickDraw 3D, Part II
by by Tom Djajadiningrat and Maarten Gribnau, Delft University of Technology
Edited by the MacTech Editorial Staff
Using the Pointing device Manager Implementation of a
HeadTracked Display
Summary
Wouldn't it be cool to be able to look around three dimensional objects displayed on
your monitor by moving your head, just as if the objects were standing there? Kind of
like a hologram, but with the flexibility of 3D computer graphics. Futuristic and
expensive? It could be easier and cheaper than you think. In a two part article we
explain how to implement such a system, also known as a head-tracked display, on a
PowerMacintosh. It provides the user with a sense of depth without the use of
stereoscopy. To facilitate implementation we use QuickDraw 3D, Apple's 3D graphics
library. In terms of hardware all you need is a PowerMac with QuickDraw 3D, an
absolute position-measuring device with three degrees of freedom and, preferably, a
QuickDraw 3D accelerator board. This month we discuss the hardware related aspects
of the head-tracked display. The graphics related topics were covered in last month's
issue.
This article is the second and last part of our coverage of a head-tracked display
system. The system has two parts: the viewer application, called MacVRoom and a
driver. In last month's graphics issue we focused on the viewer application and
explained how to control the camera by the user's head position and to show the
corresponding perspective on the monitor. This month, we will explain how to write
the driver and how to use the Pointing Device Manager to handle the communication
between the driver and MacVRoom. We will also discuss a number of calibration
methods that will achieve the maximum accuracy. Illustrated in Figure 1 are the
topics covered in both articles. In last month's issue we suggested which topics to read
or skip depending on your goals. We also addressed the issue of which knowledge and
hardware you require.
Road Map to This Article
Figure 1. Overview of the two-part article. The subjects which are covered this
month are marked with a grey block.
The QuickDraw 3D Pointing Device Manager
Introduction to the Pointing Device Manager
The MacVRoom application needs to read positions from a 3D input device to establish
the position of the head of the user. We could have chosen to use one primary position
sensing device and to include the code for reading the device into MacVRoom, but we
would rather support any 3D input device. This is one of the reasons for using
QuickDraw 3D's Pointing Device Manager (PDM). It enables you to separate the code
for reading device positions from the application itself. Without the PDM driver code
would have to be added to the application for each new device to be supported. Using the
PDM, only the driver of the device needs to be written and every PDM-savvy
application will be able to use it. The next few paragraphs will explain the basic
concepts of the PDM.
The PDM was added to the QuickDraw 3D library to provide a means for 3D
applications to support 3D pointing devices. Most computer systems come equipped
with 2D pointing devices (like a mouse, a trackball or a graphics tablet) that measure
positions in two dimensions. 3D pointing devices come in a lot of flavors but they are
generally able to sense more than two independent variables. Most operating systems
do not provide a way to transport data from 3D pointing devices to 3D applications. The
PDM does this, and in addition, it supplies routines that you can use to determine what
kind of devices are available and to assign them to tasks in your application.
The PDM defines two types of objects: controllers and trackers. Controller objects are
the PDM's abstract representation of 3D pointing devices. They are intended to be
created by device drivers. Trackers are created by a 3D application to track positions,
orientations and button states of a pointing device. The purpose of this is that
controllers and trackers can be in different applications. A driver creates a controller
and updates it regularly with fresh device readings, while applications create a
tracker, connect it to the controller and extract the device readings from it when
needed.
Figure 2 shows the information streams that flow between connected controllers and
trackers. The main objective of the controller object is that pointing devices can send
their position and orientation and the state of their buttons to the tracker. The figure
also shows two other data streams. The controller values are meant to report about
optional device dependent information to the application, like the state of switches.
Controller channels can be used to communicate data from the application back to the
device. Some devices for instance, are equipped with alphanumeric displays that label
the switches. The channels can be used to have the driver update these labels. Although
controller channels and values may be useful in some applications, their use has the
disadvantage that the application will have to contain device dependent code. This
eliminates device independence of the application which is a motivation for using the
PDM.
Figure 2. Information streams between QuickDraw 3D Controller and Tracker
objects.
Positions and orientations can be reported to the tracker in two ways, absolute and
relative. This is because there are two kinds of pointing devices. Absolute pointing
devices measure positions and/or orientations, relative to the origin of their
coordinate system. Relative pointing devices do not have an origin, they report only the
changes in position and/or orientation since the last measurement. This is best
illustrated by comparing two 2D input devices: a drawing tablet and a mouse. The
tablet is an absolute device. When you pick up the stylus and move it to another
location, the cursor will move to another location too because the tablet reports the
new location to the system. A mouse however, does not report the new location to the
system when it is picked up and moved to a new location. While the mouse is in the air
the tracking is lost and it can not report changes in position to the system until it is
put down again. In this project we need to establish the position of the head of the user
relative to the monitor and therefore we need an absolute pointing device.
Using the PDM, you can use several input devices concurrently to control different
application tasks. A useful application area of this feature is two-handed input where
the simultaneous input of two hands is used (Gribnau and Hennessey, 1998). The PDM
also enables you to connect multiple controllers to the same tracker. This means that
several input devices can control one application task at the same time. The reverse
situation is prohibited; it is not allowed to connect one controller to more than one
tracker. In practice, connecting several devices to one task is useful only when, at
most, one of those is absolute. When two or more absolute controllers are connected to
one tracker they will compete for the current position and/or orientation of the
tracker resulting in a jittery appearance. Relative devices do not behave dominantly.
The connection of a few relative devices to one tracker will give predictable results.
The changes in positions and/or orientations they report to the tracker are added to the
current position and/or orientation of the tracker. The behavior of the combination of
an absolute device and one or more relative devices is logical too. The offsets reported
by the relative device(s) are overruled by the measurements of the absolute devices.
To illustrate the use of the PDM we will now describe how to create a QuickDraw 3D
driver for a serial device and how to access it from the MacVRoom application. In the
code presented we have removed all the device related parts and all the code concerning
the user interface. Readers interested in the omitted code can download the source code
of the drivers that we offer on the web site. The first section will deal with the reading
of positions from a device. The second section will explain how to use the PDM
controller object to report the positions to an application and in the last section you
will learn how to use the QuickDraw 3D tracker object in an application to read
positions from a QuickDraw 3D device driver.
Reading data from serial input devices
The main objective of the driver is to execute as many readings from the serial port as
needed for a smooth operation. As the driver is running in the background we cannot
poll the serial port periodically for new data because we are not sure we will get
enough processor cycles. Instead of polling we will therefore use the Device Manager
PBReadAsync routine to read the serial port. This routine can be used to request a
number of bytes from the serial port and it will call a completion routine when they
have arrived in the buffer. Here, we would like to extract the positions from the bytes
in the buffer but regrettably we can't. Calling routines that move memory will crash
the driver and it is not advisable to do lengthy operations that could block other
operations. Hence, we have to find a way to process the data at a later time when it is
safe to call lengthy routines that move memory around. This is exactly the
functionality the Deferred Time Manager offers. You can use its services whenever you
need to install a lengthy interrupt task capable of running with all interrupts enabled.
In the completion routine we install a deferred task that will process the data and
update the controller. At the end of the deferred task we request another reading from
the serial port. This closes the circle of a read chain that will read data from the serial
port and process it indefinitely. The read chain is illustrated in Figure 3 and
demonstrates the sequence of events.
Figure 3. The driver serial port read chain.
Listing 1 contains constants, global variables and the code for the DriverStart routine.
The routine prepares the global variables and the structures for both the completion
routine (fIoParam) and the deferred task (fDefTask). The pointer to our completion
routine is put in the ioCompletion field of the IOParam structure. The rest of the fields
tell the Device Manager how much data is requested, from which device and were it
should be stored. You may notice that the symbol PACK_LEN is not defined in the code.
It is the number of bytes that the device needs to send a new reading and therefore
different for each device.
To set up the DeferredTask structure we need to fill in only the qType, dtAddr, and
dtReserved fields. The dtAddr field specifies the address of the routine whose execution
you want to defer, we set it to our deferred completion routine. The dtParam field
contains an optional parameter that is loaded into register A1 just before the routine
specified by the dtAddr field is executed, we don't use it and clear it. The dtFlags and
dtReserved fields of the deferred task record are reserved and the last one should be set
to 0. At the end of the routine the serial port buffer is cleared, using SerRecvFlush
(not listed), and a request is made for the first package of bytes.
Listing 1: Driver.c
DriverStart
#define BUF_LEN 256
// The state of the driver
static Boolean fRunning = false;
// The serial input port ID
static short fSerInp = 0;
// The buffer that holds the bytes read from the port
static Byte fBuf[BUF_LEN];
// The number of valid data bytes in the buffer
static long fBufCount = 0;
// The completion routine device parameter block
static IOParam fIoParam;
// The deferred task manager structure for our deferred completion
routine
static DeferredTask fDefTask;
// The current position of the device
TQ3Point3D fPos;
OSErr DriverStart()
// Set up the serial parameter block
fIoParam.ioCompletion = NewIOCompletionProc(Completion);