Paul S. Cutt (cutt++at++netcom.com)
Fri, 19 Sep 1997 09:25:54 -0700 (PDT)
> Yes. I have implemented code for Spaceball 3003,
> but 2003 should be similar.
>
> There are at least 3 ways to get data from the Sball.
>
Hi Performers,
Just to let you know, you may find my company's
XVS-Link software a helpful solution. Below is
a description.
Regards,
Paul Cutt
VP Engineering
Xtensory Inc
140 Sunridge Drive
Scotts Valley, CA 95066
USA
Tel: 408/439-0600
Fax: 408/439-8845
Email: cutt++at++netcom.com
URL: www.xtensory.com
...opening the doors of perception (TM)
-------------------------------------------
XVS-Link (TM)
A Standard Device Interface for
Virtual Environments and Applications
(The software formerly known as SyncLink)
Benefits
XVS-Link (TM) is a C++ class library that simplifies adding and
maintaining virtual reality (VR) sensor support in existing
applications. It provides a standard device interface for
including virtual environment devices into applications.
Virtual environments have led to the proliferation of 3D devices
with multiple degrees of freedom x, y, z, roll, pitch, yaw). Each
device has its own strengths and weaknesses, and the creation
of new devices brings with it a constant improvement in the
capabilities available. However, using these devices in
applications is not easy.
Many of these devices are mutually incompatible. They have
different command sets; they use different command syntax for
the same commands; the output they give to the computer follows
different binary formats; and the same type of data is presented
using different coordinate systems. Until now, the near-total
lack of standardization of even the simplest virtual reality
functions has discouraged developers from supporting multiple
devices, or adding any VR support to their applications at all.
XVS-Link takes the load off the application developer by providing
a standardized and portable object-oriented set of VR device
drivers. This lets the developer concentrate on the applications,
rather than the idiosyncracies of each VR device.
Object Oriented
XVS-Link provides an object-oriented interface to VR devices.
There is a single C++ class hierarchy for VR sensors. This
hierarchy currently handles 6-D sensors such as the Polhemus
Fastrak, Logitech 3D Mouse, Ascension Flock of Birds, Spaceball
2003, and the VPL Research DataGlove Model 2. A second C++ class
for coordinate systems allows the application programmer to
translate automatically between the differing coordinate systems
used by VR devices, 3D rendering systems, and existing applications
and data sets. In addition, XVS-Link also provides a C interface
to the C++ libraries. This enables programming entirely in C and
calling all the devices from C.
Common Interface
All devices which provide 6-degree of freedom position and
orientation data are handled similarly. XVS-Link's base sensor
class provides standard operations for opening devices, closing
devices, and reading position, Euler angles, toggles, and other
device data. Common filtering operations are also avaiable in
the base class, including origin offset, setting tolerance levels,
clipping, modulo, and scaling. Each device may report data in
either absolute or relative values, whether it is an isometric
device like the Spaceball, or an isotonic device like the Fastrak.
While the XVS-Link base class provides a common interface to
common VR functions across different devices, it does not limit
the application programmer to the lowest common denominator.
Device-specific functions are also provided. For example, Fastrak
and Flock of Birds users can make use of the multistation
capabilities of these devices, allowing multiple receivers to be
read from a single serial port. DataGlove Model 2 programmers
can calibrate the glove and read and write ASCII-formatted
calibration tables. Logitech users may access the fringe and out
of range settings which warn when the receiver is approaching
its line-of-sight limits.
Sample application code provided with XVS-Link demonstrates how the
same source code can be used to control any of the supported sensors.
Switching Sensors
XVS-Link's common interface makes it easy to switch between sensors
from within an application. Simply close and delete the old sensor
object, create and open a new sensor object, and reapply the
application's sensor filters. Even this level of detail can be
hidden from the user by the application. No longer do the
software incompatibilities between sensors inhibit switching between
them from within an application.
Customized Support
Xtensory provides the services for adding customized device support
at minimal costs. We will add your device to use the same interface.
Custom devices can then use the same standard object-oriented interface
as commercial devices, without losing access to the functionality
that makes the device unique. This enables all devices to share
the same API.
Maintenance
Xtensory provides support for upgrades and maintains the drivers as
new devices become available.
Portability
XVS-Link provides portability between different UNIX and POSIX
platforms. The same C++ class library is available for Silicon
Graphics, Kubota Pacific, and Digital Equipment systems.
Devices Supported
Ascension Flock of Birds
Ascension Extended Range Flock of Birds
Ascension MotionStar
BG Systems BeeBox, FlyBox, CerealBox
General Reality CyberEye
General Reality DataGlove
Immersion Probe and Personal Digitizer
Immersion Impulse Engine 2000
Logitech 3D Mouse, Cyberman and Space Control Mouse/Magellan
Origin Instruments DynaSight
Polhemus Fastrak, Insidetrak, Isotrak, and 3Ball
Precision Navigation Wayfinder
Spacetec Spaceball 2003 and 3003
Virtual I/O i-glasses!
Virtual Technologies CyberGlove
VPL DataGlove Model 2
5DT 5-Glove
Extended Serial Port Capability
XVS-Link supports extended serial port additions via the SCSI bus.
By attaching a serial-to-SCSI interface box to your workstation
or PC you can add more serial ports to your system. This allows
you to run multiple VR devices simultaneously and gets you around
the common problem of running out of serial ports. Since workstations
and PCs commonly have only two serial ports, users who want to run
more than two VR devices at a time have been restricted (for example,
running six SpaceBalls at once). Similarly, users who have a variety
of VR devices and frequently switch between them have been frustrated
with continual plugging and unplugging the device of the moment. With
the extended serial port interface box and XVS-Link you can plug in all
your VR devices once and leave them plugged in, and just use them as
needed.
The serial-to-SCSI interface box attaches externally to the SCSI bus
(it doesn't occupy any internal system card slots) and occupies
only one SCSI address, allowing it to share the bus with other
SCSI peripherals. Different models of interface units provide for
2, 4, 8, 16 or even 32 serial ports, all running on the same SCSI
address. Xtensory can supply the right interface box to meet
your requirements.
System Requirements
XVS-Link includes a C wrapper library, C++ object library, C++
header files, and sample C++ test software. XVS-Link requires
one of the following operating systems:
SGI IRIX 5.2 or later (Irix 6.3 and 6.4 now supported)
Digital OSF/1 1.3 or later
Microsoft Windows NT 3.51 (or later)/Windows 95
HP-UX 9.0 or later
Sun
XVS-Link also requires the appropriate C/C++ compiler for
the platform:
SGI C++ 3.0 or later
Digital C++ 1.3 or later
Microsoft Visual C++ 4.0
HP
Sun
Contact Xtensory regarding support for VR devices or UNIX/POSIX
operating systems not listed above. Xtensory will add in specific
device support for custom devices so that they use the same
interface API at minimal cost.
Contact
Xtensory Inc
140 Sunridge Drive
Scotts Valley, CA 95066
USA
Tel 408/439-0600
Fax 408/439-8845
cutt++at++netcom.com
www.xtensory.com
...opening the doors of perception (TM)
//
// spacemouse.C
// ------------
//
// is based on the Performer example complex.C and includes support
// for the spacemouse device.
//
// new command line options are
//
// -s translation_speed; > 1.0 more translation speed, 0..1 less speed
//
// For further information on spacemouse
// devices watch out for
//
// http://dv.op.dlr.de/FF-DR-RS/SC
//
// If the compiler directives SPACEMOUSE and SPACEMOUSE_DEBUG are
// omitted the program behaves like 'complex'.
//
// spacemouse code was added by Andreas Loesch <andreas++at++munich.sgi.com>
// and Joerg Wallmersperger <joerg++at++munich.sgi.com>
//
// complex.C documentation:
// ------------------------
// IRIS Performer example using cull and draw process callbacks.
// Mouse and keyboard go through GL which is simpler than mixed
// model (GLX), but does incur some overhead in the draw process.
// X input handling is done in a forked event handling process.
//
// $Revision: 1.18 $
// $Date: 1996/12/18 03:49:02 $
//
// Command-line options:
// -b : norborder window
// -f : full screen
// -F : put X input handling in a forked process
// -m procsplit : multiprocess mode
// -w : write scene to file
//
// Run-time controls:
// ESC-key: exits
// F1-key: profile
// Left-mouse: advance
// Middle-mouse: stop
// Right-mouse: retreat
#define SPACEMOUSE
#define STEREO
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <signal.h> // for sigset for forked X event handler process
#include <getopt.h> // for cmdline handler
#include <X11/keysym.h>
#include <Performer/pf/pfNode.h>
#include <Performer/pf/pfPipe.h>
#include <Performer/pf/pfChannel.h>
#include <Performer/pr/pfLight.h>
#include <Performer/pfutil.h>
#include <Performer/pfdu.h>
#ifdef SPACEMOUSE
#include <X11/extensions/XInput.h>
#endif
//
// structure that resides in shared memory so that the
// application, cull, and draw processes can access it.
typedef struct
{
pfPipeWindow *pw;
int exitFlag;
int inWindow, reset;
float mouseX, mouseY;
int winSizeX, winSizeY;
int mouseButtons;
pfCoord view, viewOrig;
float accelRate;
float sceneSize;
int drawStats;
int XInputInited;
#ifdef SPACEMOUSE
int spacemouse;
pfCoord spacecoord;
float smousefactor;
#endif
#ifdef STEREO
int stereo;
#endif
} SharedData;
static SharedData *Shared;
#ifdef SPACEMOUSE
int MagellanMotionEventType,
MagellanButtonPressEventType,
MagellanButtonReleaseEventType,
MagellanMotionEventClass,
MagellanButtonPressEventClass,
MagellanButtonReleaseEventClass;
XEventClass ListOfEventClass[3];
float transSpeed = 1.0;
#endif
#ifdef STEREO
static float Iod = .2f;
static float Converge = .02f;
static float Fov = 45.f;
static int FBAttrs[] = {
PFFB_RGBA,
PFFB_DOUBLEBUFFER,
PFFB_STEREO,
PFFB_DEPTH_SIZE, 23,
PFFB_RED_SIZE, 1,
PFFB_STENCIL_SIZE, 1,
None,
};
#endif
//
// APP process variables
// for configuring multi-process
static int ProcSplit = PFMP_DEFAULT;
// write out scene upon read-in - uses pfDebugPrint
static int WriteScene = 0;
static int FullScreen = 0;
static int WinType = PFPWIN_TYPE_X;
static int NoBorder = 0;
static int ForkedXInput = 0;
char ProgName[PF_MAXSTRING];
// light source created and updated in DRAW-process
static pfLight *Sun;
static void CullChannel(pfChannel *chan, void *data);
static void DrawChannel(pfChannel *chan, void *data);
static void OpenPipeWin(pfPipeWindow *pw);
static void UpdateView(void);
static void GetGLInput(void);
static void InitXInput(pfWSConnection dsp);
static void DoXInput(void);
static void GetXInput(Display *dsp);
static void Usage(void);
#ifdef SPACEMOUSE
static void initSpaceMouse(void);
#endif
//
// Usage() -- print usage advice and exit. This procedure
// is executed in the application process.
static void
Usage (void)
{
pfNotify(PFNFY_FATAL, PFNFY_USAGE,
"\
Usage: %s [-s translation_speed] [-m procSplit] [-f] [-F] [-b]\
[-w] [file.ext ...]\n", ProgName);
exit(1);
}
//
// docmdline() -- use getopt to get command-line arguments,
// executed at the start of the application process.
static int
docmdline(int argc, char *argv[])
{
int opt;
strcpy(ProgName, argv[0]);
// process command-line arguments
while ((opt = getopt(argc, argv, "s:fFbm:wxp:?")) != -1)
{
switch (opt)
{
case 'f':
FullScreen = 1;
break;
case 'F':
ForkedXInput = 1;
break;
case 'm':
case 'p':
ProcSplit = atoi(optarg);
break;
case 'w':
WriteScene = 1;
break;
case 'x':
WinType &= ~(PFPWIN_TYPE_X);
break;
case 'b':
NoBorder ^= 1;
break;
#ifdef SPACEMOUSE
case 's':
transSpeed = atof(optarg);
break;
#endif
#ifdef STEREO
case 'i': /* get interocular distance */
Iod = atof(optarg);
break;
case 'c': /* set convergence ratio */
Converge = atof(optarg);
break;
case 'v': /* get field of view */
Fov = atof(optarg);
break;
#endif
case '?':
case 'h':
Usage();
}
}
return optind;
}
//
// main() -- program entry point. this procedure
// is executed in the application process.
int
main (int argc, char *argv[])
{
int arg;
int found;
pfPipe *p;
pfBox bbox;
float far = 10000.0f;
float near = .1f;
pfWSConnection dsp=NULL;
#ifdef SPACEMOUSE
pfMatrix mat1,mat2;
static double thisTime = -1.0f;
double prevTime;
float deltaTime;
#endif
#ifdef STEREO
unsigned int mask;
int *leftArg, *rightArg;
float halfNearWidth;
float eyeAngle;
pfVec3 xyzOffsets;
pfVec3 hprOffsets;
#endif
arg = docmdline(argc, argv);
pfInit();
// configure multi-process selection
pfMultiprocess(ProcSplit);
// allocate shared before fork()'ing parallel processes
Shared = (SharedData*)pfMalloc(sizeof(SharedData), pfGetSharedArena());
Shared->inWindow = 0;
Shared->reset = 0;
Shared->exitFlag = 0;
Shared->drawStats = 1;
Shared->XInputInited = 0;
// Load all loader DSO's before pfConfig() forks
for (found = arg; found < argc; found++)
pfdInitConverter(argv[found]);
// initiate multi-processing mode set in pfMultiprocess call
// FORKs for Performer processes, CULL and DRAW, etc. happen here.
pfConfig();
// configure pipes and windows
p = pfGetPipe(0);
Shared->pw = new pfPipeWindow(p);
Shared->pw->setName("IRIS Performer");
Shared->pw->setWinType(WinType);
if (NoBorder)
Shared->pw->setMode(PFWIN_NOBORDER, 1);
// Open and configure the GL window.
Shared->pw->setConfigFunc(OpenPipeWin);
Shared->pw->config();
if (FullScreen)
Shared->pw->setFullScreen();
else
Shared->pw->setOriginSize(0, 0, 300, 300);
// set off the draw process to open windows and call init callbacks
pfFrame();
// create forked XInput handling process
// since the Shared pointer has already been initialized, that structure
// will be visible to the XInput process. Nothing else created in the
// application after this fork whose handles are not put in shared memory
// (such as the database and channels) will be visible to the
// XInput process.
if (WinType & PFPWIN_TYPE_X)
{
pid_t fpid = 0;
if (ForkedXInput)
{
if ((fpid = fork()) < 0)
pfNotify(PFNFY_FATAL, PFNFY_SYSERR, "Fork of XInput process failed.");
else if (fpid)
pfNotify(PFNFY_NOTICE,PFNFY_PRINT,"XInput running in forked process %d",
fpid);
else if (!fpid)
DoXInput();
}
else
{
dsp = pfGetCurWSConnection();
}
}
// specify directories where geometry and textures exist
if (!(getenv("PFPATH")))
pfFilePath(
"."
":./data"
":../data"
":../../data"
":/usr/share/Performer/data"
);
pfNotify(PFNFY_INFO, PFNFY_PRINT,"FilePath: %s\n", pfGetFilePath());
// load files named by command line arguments
pfScene *scene = new pfScene();
for (found = 0; arg < argc; arg++)
{
pfNode *root;
if ((root = pfdLoadFile(argv[arg])) != NULL)
{
scene->addChild(root);
found++;
}
}
// if no files successfully loaded, terminate program
#if 0
if (!found)
Usage();
#endif
// Write out nodes in scene (for debugging)
if (WriteScene)
{
FILE *fp;
if (fp = fopen("scene.out", "w"))
{
pfPrint(scene, PFTRAV_SELF|PFTRAV_DESCEND, PFPRINT_VB_DEBUG, fp);
fclose(fp);
}
else
pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
"Could not open scene.out for debug printing.");
}
// determine extent of scene's geometry
pfuTravCalcBBox(scene, &bbox);
pfFrameRate(30.0f);
pfPhase(PFPHASE_FREE_RUN);
#ifdef STEREO
/* create a channel for each eye */
pfChannel *left = new pfChannel(p);
pfChannel *chan = left;
pfChannel *right = new pfChannel(p);
left->attach(right);
mask = left->getShare();
/* same viewport */
mask |= PFCHAN_VIEWPORT;
left->setShare(mask);
left->setTravFunc(PFTRAV_CULL, CullChannel);
left->setTravFunc(PFTRAV_DRAW, DrawChannel);
left->setScene(scene);
left->setNearFar(0.1f, far);
/* set up data to distinguish between left and right eye */
leftArg = (int *)left->allocChanData(sizeof(int));
rightArg = (int *)right->allocChanData(sizeof(int));
*leftArg = 1;
*rightArg = 0;
/* data never changes, so we only need to pass it once */
left->passChanData();
right->passChanData();
left->setFOV(45.f, -1.f);
/* set up offsets for left and right channels for stereo viewing */
/* both eyes look at same spot 1/2 way between eyes at fusion distance */
eyeAngle = PF_RAD2DEG(
atanf(Iod *.5f /(Converge * (far - near) + near)));
/* left eye */
hprOffsets.set(-eyeAngle, 0.f, 0.f);
xyzOffsets.set(-Iod/2.f, 0.f, 0.f);
left->setViewOffsets(xyzOffsets, hprOffsets);
/* right eye */
hprOffsets.set(eyeAngle, 0.f, 0.f);
xyzOffsets.set(Iod/2.f, 0.f, 0.f);
right->setViewOffsets(xyzOffsets, hprOffsets);
#else
pfChannel *chan = new pfChannel(p);
Shared->pw->addChan(chan);
chan->setTravFunc(PFTRAV_CULL, CullChannel);
chan->setTravFunc(PFTRAV_DRAW, DrawChannel);
chan->setScene(scene);
chan->setNearFar(near, far);
// vertical FOV is matched to window aspect ratio
chan->setFOV(45.0f, -1.0f);
#endif
// Create an earth/sky model that draws sky/ground/horizon
// pfEarthSky *eSky = new pfEarthSky();
// eSky->setMode(PFES_BUFFER_CLEAR, PFES_SKY_GRND);
// eSky->setAttr(PFES_GRND_HT, -10.0f);
// chan->setESky(eSky);
chan->setTravMode(PFTRAV_CULL, PFCULL_VIEW|PFCULL_GSET);
if (found)
{
float sceneSize;
// Set initial view to be "in front" of scene
// view point at center of bbox
Shared->view.xyz.add(bbox.min, bbox.max);
Shared->view.xyz.scale(0.5f, Shared->view.xyz);
// find max dimension
sceneSize = bbox.max[PF_X] - bbox.min[PF_X];
sceneSize = PF_MAX2(sceneSize, bbox.max[PF_Y] - bbox.min[PF_Y]);
sceneSize = PF_MAX2(sceneSize, bbox.max[PF_Z] - bbox.min[PF_Z]);
sceneSize = PF_MIN2(sceneSize, 0.5f * far);
Shared->sceneSize = sceneSize;
// offset so all is visible
Shared->view.xyz[PF_Y] -= sceneSize;
Shared->view.xyz[PF_Z] += 0.25f*sceneSize;
} else
{
Shared->view.xyz.set(0.0f, 0.0f, 100.0f);
PFSET_VEC3(bbox.min, -5000.0f, -5000.0f, -1000000.0f);
PFSET_VEC3(bbox.max, 5000.0f, 5000.0f, 10000000.0f);
Shared->sceneSize = 10000.0f;
}
Shared->view.hpr.set(0.0f, 0.0f, 0.0f);
chan->setView(Shared->view.xyz, Shared->view.hpr);
PFCOPY_VEC3(Shared->viewOrig.xyz, Shared->view.xyz);
PFCOPY_VEC3(Shared->viewOrig.hpr, Shared->view.hpr);
#ifdef SPACEMOUSE
Shared->smousefactor = transSpeed * Shared->sceneSize;
#endif
// main simulation loop
while (!Shared->exitFlag)
{
// wait until next frame boundary
pfSync();
pfFrame();
// Set view parameters for next frame
UpdateView();
#ifndef SPACEMOUSE
chan->setView(Shared->view.xyz, Shared->view.hpr);
#else
prevTime = thisTime;
thisTime = pfGetTime();
if (prevTime > 0){
deltaTime = thisTime-prevTime;
Shared->spacecoord.xyz.scale(deltaTime,Shared->spacecoord.xyz);
Shared->spacecoord.hpr.scale(deltaTime,Shared->spacecoord.hpr);
}
mat1.makeCoord(&Shared->view);
mat2.makeCoord(&Shared->spacecoord);
mat1.preMult(mat2);
chan->setViewMat(mat1);
mat1.getOrthoCoord(&Shared->view);
#endif
// initiate traversal using current state
if (!ForkedXInput)
{
if (!Shared->XInputInited)
InitXInput(dsp);
if (Shared->XInputInited)
GetXInput(dsp);
}
}
// terminate cull and draw processes (if they exist)
pfExit();
// exit to operating system
return 0;
}
static void
InitXInput(pfWSConnection dsp)
{
Window w;
#ifdef SPACEMOUSE
initSpaceMouse();
#endif
/* wait for X Window to exist in Performer shared memory */
if (w = Shared->pw->getWSWindow())
{
XSelectInput(dsp, w, PointerMotionMask |
ButtonPressMask | ButtonReleaseMask |
KeyPressMask | KeyReleaseMask);
XMapWindow(dsp, w);
XFlush(dsp);
Shared->XInputInited = 1;
}
}
//
// DoXInput() runs an asychronous forked even handling process.
// Shared memory structures can be read from this process
// but NO performer calls that set any structures should be
// issues by routines in this process.
void
DoXInput(void)
{
// windows from draw should now exist so can attach X input handling
// to the X window
Display *dsp = pfGetCurWSConnection();
prctl(PR_TERMCHILD); // Exit when parent does
sigset(SIGHUP, SIG_DFL); // Exit when sent SIGHUP by TERMCHILD
InitXInput(dsp);
while (1)
{
XEvent event;
if (!Shared->XInputInited)
InitXInput(dsp);
if (Shared->XInputInited)
{
XPeekEvent(dsp, &event);
GetXInput(dsp);
}
}
}
//
// UpdateView() updates the eyepoint based on the information
// placed in shared memory by GetInput().
static void
UpdateView(void)
{
static float speed = 0.0f;
pfCoord *view = &Shared->view;
float cp;
float mx, my;
static double thisTime = -1.0f;
double prevTime;
float deltaTime;
prevTime = thisTime;
thisTime = pfGetTime();
if (prevTime < 0.0f)
return;
if (!Shared->inWindow || Shared->reset)
{
speed = 0;
Shared->reset = 0;
Shared->accelRate = 0.1f * Shared->sceneSize;
return;
}
deltaTime = thisTime - prevTime;
switch (Shared->mouseButtons)
{
case Button1Mask: /* LEFTMOUSE: faster forward or slower backward*/
case Button1Mask|Button2Mask:
speed += Shared->accelRate * deltaTime;
if (speed > Shared->sceneSize)
speed = Shared->sceneSize;
break;
case Button3Mask: /* RIGHTMOUSE: faster backward or slower foreward*/
case Button3Mask|Button2Mask:
speed -= Shared->accelRate * deltaTime;
if (speed < -Shared->sceneSize)
speed = -Shared->sceneSize;
break;
}
if (Shared->mouseButtons)
{
mx = 2.0f * (Shared->mouseX / (float)Shared->winSizeX) - 1.0f;
my = 2.0f * (Shared->mouseY / (float)Shared->winSizeY) - 1.0f;
/* update view direction */
view->hpr[PF_H] -= mx * PF_ABS(mx) * 30.0f * deltaTime;
view->hpr[PF_P] += my * PF_ABS(my) * 30.0f * deltaTime;
#ifndef SPACEMOUSE
view->hpr[PF_R] = 0.0f;
#endif
/* update view position */
cp = cosf(PF_DEG2RAD(view->hpr[PF_P]));
view->xyz[PF_X] += speed*sinf(-PF_DEG2RAD(view->hpr[PF_H]))*cp;
view->xyz[PF_Y] += speed*cosf(-PF_DEG2RAD(view->hpr[PF_H]))*cp;
view->xyz[PF_Z] += speed*sinf( PF_DEG2RAD(view->hpr[PF_P]));
}
else
{
speed = 0.0f;
Shared->accelRate = 0.1f * Shared->sceneSize;
}
}
//
// CullChannel() -- traverse the scene graph and generate a
// display list for the draw process. This procedure is
// executed in the CULL process.
static void
CullChannel(pfChannel *, void *)
{
//
// pfDrawGeoSet or other display listable Performer routines
// could be invoked before or after pfCull()
pfCull();
}
//
// OpenPipeWin() -- create a win: setup the GL and IRIS Performer.
// This procedure is executed in the DRAW process
// (when there is a separate draw process).
static void
OpenPipeWin(pfPipeWindow *pw)
{
#ifdef STEREO
pw->setFBConfigAttrs(FBAttrs);
#endif
pw->open();
#ifdef STEREO
pw->query(PFQWIN_STEREO, &Shared->stereo);
if(Shared->stereo == PFQFTR_FALSE)
pfNotify(PFNFY_NOTICE, PFNFY_RESOURCE,
"Couldn't get a stereo window; using mono mode");
#endif
// create a light source in the "south-west" (QIII)
Sun = new pfLight();
Sun->setPos(-0.3f, -0.3f, 1.0f, 0.0f);
}
//
// DrawChannel() -- draw a channel and read input queue. this
// procedure is executed in the draw process (when there is a
// separate draw process).
static void
DrawChannel (pfChannel *channel, void *left)
{
// rebind light so it stays fixed in position
Sun->on();
#ifdef STEREO
/* which buffer to draw into ? */
if(Shared->stereo) { /* if not stereo, draw to left all the time */
if(*(int*)left) {
glDrawBuffer(GL_BACK_LEFT);
} else {
glDrawBuffer(GL_BACK_RIGHT);
}
}
#endif /*STEREO*/
// erase framebuffer and draw Earth-Sky model
channel->clear();
// invoke Performer draw-processing for this frame
pfDraw();
// draw Performer throughput statistics
if (Shared->drawStats)
channel->drawStats();
// read window origin and size (it may have changed)
channel->getPWin()->getSize(&Shared->winSizeX, &Shared->winSizeY);
#ifdef STEREO
if(!Shared->stereo) /* mono mode */
if(*(int*)left) /* add extra swap to draw both buffers */
(channel->getPWin())->swapBuffers();
#endif
}
static void
GetXInput(pfWSConnection dsp)
{
static int x=0, y=0;
if (XEventsQueued(dsp, QueuedAfterFlush))
while (XEventsQueued(dsp, QueuedAlready))
{
XEvent event;
#ifdef SPACEMOUSE
XDeviceButtonEvent *ButtonPtr;
XDeviceMotionEvent *MotionPtr;
#endif
XNextEvent(dsp, &event);
switch (event.type)
{
case ConfigureNotify:
break;
case FocusIn:
Shared->inWindow = 1;
break;
case FocusOut:
Shared->inWindow = 0;
break;
case MotionNotify:
{
XMotionEvent *motion_event = (XMotionEvent *) &event;
x = motion_event->x;
y = Shared->winSizeY - motion_event->y;
}
break;
case ButtonPress:
{
XButtonEvent *button_event = (XButtonEvent *) &event;
x = event.xbutton.x;
y = Shared->winSizeY - event.xbutton.y;
Shared->inWindow = 1;
switch (button_event->button) {
case Button1:
Shared->mouseButtons |= Button1Mask;
break;
case Button2:
Shared->mouseButtons |= Button2Mask;
break;
case Button3:
Shared->mouseButtons |= Button3Mask;
break;
}
}
break;
case ButtonRelease:
{
XButtonEvent *button_event = (XButtonEvent *) &event;
switch (button_event->button) {
case Button1:
Shared->mouseButtons &= ~Button1Mask;
break;
case Button2:
Shared->mouseButtons &= ~Button2Mask;
break;
case Button3:
Shared->mouseButtons &= ~Button3Mask;
break;
}
}
break;
case KeyPress:
{
char buf[100];
int rv;
KeySym ks;
rv = XLookupString(&event.xkey, buf, sizeof(buf), &ks, 0);
switch(ks) {
case XK_Escape:
Shared->exitFlag = 1;
exit(0);
break;
case XK_space:
Shared->reset = 1;
PFCOPY_VEC3(Shared->view.xyz, Shared->viewOrig.xyz);
PFCOPY_VEC3(Shared->view.hpr, Shared->viewOrig.hpr);
pfNotify(PFNFY_NOTICE, PFNFY_PRINT, "Reset");
break;
case XK_g:
Shared->drawStats = !Shared->drawStats;
break;
default:
break;
}
}
break;
default:
#ifdef SPACEMOUSE
if (event.type == MagellanMotionEventType){
MotionPtr = (XDeviceMotionEvent *) &(event);
if ( (MotionPtr->axes_count == 6) &&
(MotionPtr->first_axis == 0)){
Shared->spacecoord.xyz[0]=MotionPtr->axis_data[ 0 ]
*(.001*Shared->smousefactor);
Shared->spacecoord.xyz[1]=MotionPtr->axis_data[ 2 ]
*(.001*Shared->smousefactor);
Shared->spacecoord.xyz[2]=MotionPtr->axis_data[ 1 ]
*(.001*Shared->smousefactor);
Shared->spacecoord.hpr[0]=.1*MotionPtr->axis_data[ 4 ];
Shared->spacecoord.hpr[1]=.1*MotionPtr->axis_data[ 3 ];
Shared->spacecoord.hpr[2]=.1*MotionPtr->axis_data[ 5 ];
Shared->spacemouse=TRUE;
#ifdef SPACEMOUSE_DEBUG
printf("factor: %f spaceball motion: xyz: %f %f %f hpr: %f %f %f\n",
Shared->smousefactor,
Shared->spacecoord.xyz[0],
Shared->spacecoord.xyz[1],
Shared->spacecoord.xyz[2],
Shared->spacecoord.hpr[0],
Shared->spacecoord.hpr[1],
Shared->spacecoord.hpr[2]);
#endif
}
}
if (event.type == MagellanButtonPressEventType){
ButtonPtr = (XDeviceButtonEvent *) &(event);
}
if (event.type == MagellanButtonReleaseEventType){
ButtonPtr = (XDeviceButtonEvent *) &(event);
}
#endif
break;
}// switch
}
Shared->mouseX = x;
Shared->mouseY = y;
}
#ifdef SPACEMOUSE
void
initSpaceMouse()
{
int MagellanID, DeviceNumber, loop, c_class;
XDeviceInfo *DeviceInfo;
XDevice *Device;
Window w;
Display *dsp;
XAxisInfoPtr XAxisPtr;
XAnyClassPtr XClassPtr;
int axis, axes;
XExtensionVersion *ExtVersion;
XFeedbackState *MagellanFeedback;
int FeedbackNumber;
w = Shared->pw->getWSWindow();
dsp = pfGetCurWSConnection();
ExtVersion = XGetExtensionVersion( dsp, "XInputExtension" );
if ( (ExtVersion == NULL) || ((int)ExtVersion == NoSuchExtension) )
{
fprintf( stderr, "Cannot access X Input Extension. Exit ... \n");
exit( -1 );
};
printf("X Input Extension Version %d.%d \n", ExtVersion->major_version, ExtVersion->minor_version );
XFree( ExtVersion );
DeviceInfo = XListInputDevices( dsp, &DeviceNumber );
MagellanID = -1;
for ( loop=0; loop<DeviceNumber; ++loop )
{
printf("------------------------------------------------------------------------\n");
printf(" Type = %d \n", DeviceInfo[loop].type );
printf("Device Name = %s Type = %d Atom = %s \n", DeviceInfo[loop].name,
DeviceInfo[loop].type, XGetAtomName(dsp,DeviceInfo[loop].type) );
printf(" Device Class(es) = %d Use = %s \n", DeviceInfo[loop].num_classes,
DeviceInfo[loop].use ? "TRUE" : "FALSE" );
XClassPtr = (XAnyClassPtr) DeviceInfo[loop].inputclassinfo;
for ( c_class=0; c_class<DeviceInfo[loop].num_classes; ++c_class )
{
switch( XClassPtr->c_class )
{
case 0:
printf(" Keyboard Keycode Min = %d Max = %d Number of Keys = %d \n",
((XKeyInfo *)XClassPtr)->min_keycode,
((XKeyInfo *)XClassPtr)->max_keycode,
((XKeyInfo *)XClassPtr)->num_keys );
break;
case 1:
printf(" Button(s) Number of Buttons = %d \n",
((XButtonInfo *)XClassPtr)->num_buttons );
break;
case 2:
printf(" Valuator(s) Axes = %d Mode = %d Motion Buffer = %d \n",
((XValuatorInfo *)XClassPtr)->num_axes,
((XValuatorInfo *)XClassPtr)->mode,
((XValuatorInfo *)XClassPtr)->motion_buffer );
XAxisPtr = ((XValuatorInfo *)XClassPtr)->axes;
axes = ((XValuatorInfo *)XClassPtr)->num_axes;
for ( axis=0; axis<axes; ++axis )
{
printf(" Valuator = %d Min = %d Max = %d Resolution = %d \n", axis+1,
XAxisPtr->min_value, XAxisPtr->max_value, XAxisPtr->resolution );
++XAxisPtr;
};
break;
default:
printf(" Class ID = %d Length = %d \n", XClassPtr->c_class, XClassPtr->length );
break;
};
XClassPtr = (XAnyClassPtr) ((char *)XClassPtr+XClassPtr->length);
};
};
for ( loop=0; loop<DeviceNumber; ++loop )
{
if ( strcmp( DeviceInfo[loop].name, "MAGELLAN" ) == NULL ||
strcmp( DeviceInfo[loop].name, "magellan" ) == NULL )
{
MagellanID = DeviceInfo[loop].id;
break;
};
};
if ( MagellanID == -1 )
{
for ( loop=0; loop<DeviceNumber; ++loop )
{
if ( strcmp( DeviceInfo[loop].name, "SPACEBALL" ) == NULL ||
strcmp( DeviceInfo[loop].name, "spaceball" ) == NULL )
MagellanID = DeviceInfo[loop].id;
};
};
XFreeDeviceList( DeviceInfo );
printf("------------------------------------------------------------------------\n");
printf("\n\n");
if ( MagellanID == -1 )
{
printf("Magellan X Input Extension. \nCan't find Magellan. Exit ... \n" );
exit( -1 );
}
else
printf("Magellan X Input Extension ID = %d \n", MagellanID );
Device = XOpenDevice( dsp, MagellanID );
if ( Device == 0 )
{
printf("Magellan X Input Extension. \nCan't open Magellan. Exit ... \n" );
exit( -1 );
};
DeviceMotionNotify( Device, MagellanMotionEventType, MagellanMotionEventClass );
DeviceButtonPress( Device, MagellanButtonPressEventType, MagellanButtonPressEventClass );
DeviceButtonRelease( Device, MagellanButtonReleaseEventType, MagellanButtonReleaseEventClass );
printf("MotionNotify Type=%d Class=%d \n", MagellanMotionEventType, MagellanMotionEventClass );
printf("ButtonPress Type=%d Class=%d \n", MagellanButtonPressEventType, MagellanButtonPressEventClass );
printf("ButtonRelease Type=%d Class=%d \n", MagellanButtonReleaseEventType, MagellanButtonReleaseEventClass );
ListOfEventClass[0] = MagellanMotionEventClass;
ListOfEventClass[1] = MagellanButtonPressEventClass;
ListOfEventClass[2] = MagellanButtonReleaseEventClass;
XSelectExtensionEvent( dsp, w, ListOfEventClass, 3 );
MagellanFeedback = XGetFeedbackControl( dsp, Device, &FeedbackNumber );
printf("Magellan Feedback Number = %d \n", FeedbackNumber );
for ( loop=0; loop<FeedbackNumber; ++loop )
{
printf("Feedback = %d Class = %d Length = %d ID = %d \n", loop+1, MagellanFeedback->c_class,
MagellanFeedback->length, MagellanFeedback->id );
switch( MagellanFeedback->c_class )
{
case KbdFeedbackClass:
printf(" KbdFeedbackClass Bell Volume = %d Pitch = %d Duration = %d \n",
((XKbdFeedbackState *)MagellanFeedback)->percent,
((XKbdFeedbackState *)MagellanFeedback)->pitch,
((XKbdFeedbackState *)MagellanFeedback)->duration );
break;
case BellFeedbackClass:
printf(" BellFeedbackClass Bell Volume = %d Pitch = %d Duration = %d \n",
((XBellFeedbackState *)MagellanFeedback)->percent,
((XBellFeedbackState *)MagellanFeedback)->pitch,
((XBellFeedbackState *)MagellanFeedback)->duration );
break;
case PtrFeedbackClass:
printf(" PtrFeedbackClass \n");
break;
case IntegerFeedbackClass:
printf(" IntegerFeedbackClass \n");
break;
case StringFeedbackClass:
printf(" StringFeedbackClass \n");
break;
case LedFeedbackClass:
printf(" LedFeedbackClass \n");
break;
default:
printf(" Unkonw FeedbackClass \n");
}
MagellanFeedback = (XFeedbackState *)((char *)MagellanFeedback+MagellanFeedback->length);
}
Shared->spacemouse=FALSE;
}
#endif
This archive was generated by hypermail 2.0b2 on Mon Aug 10 1998 - 17:55:58 PDT