/*
* vtrlt.c Kishan P Thomas 07/09/95
* VisualLab ADE,DRDO
*
* IRIS Performer program 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.
* ( Ref: complex.c)
*
* Text drawing both overlayed on top of the scene and moving
* with the scene, Text string drawn as GL Text.(Ref: text.c)
*
* User Interaction - By Examining the GL - event queue for keyb
* oard and mouse events.(Ref: bench.c)
*
* Visula Database created using: MultiGen Database modeller in
* the Flight database format MultiGen Revision 14.0 March 1994
* Software Systems
*
* Note: Other than the Multigen '.flt' files all popular data
* base file formats whose loaders are available can be loaded
* using this program
*
* Run-time controls: Mouse & Keyboard
*
* ESC-key: exits
* F2-key: profile
* F1-key: Assistance Messages
* F3-key: drive/fly modes
* Left-mouse: advance
* Middle-mouse: stop
* Right-mouse: retreat
* back-space ' ': stop
* 'r': reset position and stop
* 'f': freeze
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <getopt.h> /* for cmdline handler */
#include <gl/device.h>
#include <gl/gl.h>
#include <Performer/pf.h>
#include "pfutil.h"
#include "pfflt.h"
#include "pfsgi.h"
static void CullChannel(pfChannel *chan, void *data);
static void DrawChannel(pfChannel *chan, void *data);
static void DrawChannelOne(pfChannel *chan, void *data);
static void OpenPipeline(pfPipe *p);
static void UpdateView(void);
static void ResetView(void);
static void GetGLInput(void);
static void Usage(void);
static void DrawText(pfChannel *chan);
static void DrawIntro(pfChannel *chan);
static void DrawHud(pfChannel *chan);
static void DrawBorder(pfChannel *chan);
#define Button1Mask 0x01
#define Button2Mask 0x02
#define Button3Mask 0x04
#define HISPEED 0.1f
#define LOSPEED 0.001f
#define ON 1
#define OFF 0
/*
* structure that resides in shared memory so that the
* application, cull, and draw processes can access it.
*/
typedef struct
{
long exitFlag;
long inWindow;
float mouseX;
float mouseY;
float speed;
long mouseButtons;
long winOriginX;
long winOriginY;
long winSizeX;
long winSizeY;
long reset;
long message;
long travmod;
long hudopt;
long chan2opt;
long freeze;
pfCoord view;
pfVec3 orig_xyz;
float sceneSize;
int drawStats;
float xval;
float yval;
float zval;
} SharedData;
static SharedData *Shared;
/* light source created and updated in draw-process */
static pfLight *Sun;
/* for configuring multi-process */
static long ProcSplit = PFMP_APPCULLDRAW;
/* write out scene upon read-in - uses pfDebugPrint */
static long WriteScene = 0;
char ProgName[PF_MAXSTRING];
/* for drawing GL text */
static pfMatrix idmat ={{1., 0., 0., 0.},
{0., 1., 0., 0.},
{0., 0., 1., 0.},
{0., 0., 0., 1.}};
pfSphere *bsphere;
/* global variables for the application program*/
int drawtextfla_g = ON;
pfNode *root;
pfSegSet segset;
int modflag, timer;
/**************************************************************************/
/*
* FUNCTION:
* LoadFile()
*
* DESCRIPTION:
* Load a database file into an IRIS Performer in-memory data
* structure using the filename's extension to intuit the file
* format. The file is sought in the directories named in the
* active performer file search path (see pfFilePath for more
* details). The geostate argument provides a default state
* to those database loaders that accept such an argument. A
* value of NULL is a safe default.
*/
extern pfNode *
LoadFile (char *fileName, pfGeoState *geostate)
{
char *dot;
pfNode *node = NULL;
/* check argument */
if (fileName == NULL || *fileName == '\0')
{
pfNotify(PFNFY_NOTICE, PFNFY_PRINT,
"LoadFile(): NULL filename. loading not possible.");
return NULL;
}
/* look for final "." in filename */
if ((dot = strrchr(fileName, '.')) == NULL)
{
pfNotify(PFNFY_NOTICE, PFNFY_PRINT,
"LoadFile(): No file type extension. loading not possible.");
return NULL;
}
/* use filename extension as file-format identifier */
if (strcmp(dot, ".bin") == 0)
node = LoadBin(fileName, geostate);
else
if (strcmp(dot, ".dwb") == 0)
node = LoadDwb(fileName);
else
if (strcmp(dot, ".dxf") == 0)
node = LoadDxf(fileName, geostate);
else
if (strcmp(dot, ".flt") == 0)
#define USE_SOFTWARE_SYSTEMS_LOADER
#ifdef USE_SOFTWARE_SYSTEMS_LOADER
node = LoadFlt(fileName);
#else
node = LoadFlt11(fileName);
#endif
else
if (strcmp(dot, ".gfo") == 0)
node = LoadGfo(fileName, geostate);
else
if (strcmp(dot, ".irtp") == 0)
node = LoadIrtp(fileName, geostate);
/***************************************************************************
else
if (strcmp(dot, ".iv") == 0)
node = LoadIv(fileName, geostate);
***************************************************************************/
else
if (strcmp(dot, ".lsa") == 0)
node = LoadLsa(fileName, geostate);
else
if (strcmp(dot, ".lsb") == 0)
node = LoadLsb(fileName, geostate);
else
if (strcmp(dot, ".obj") == 0)
node = LoadObj(fileName, geostate);
else
if (strcmp(dot, ".poly") == 0 || strcmp(dot, ".ascii") == 0)
node = LoadPoly(fileName, geostate);
else
if (strcmp(dot, ".ptu") == 0)
node = LoadPtu(fileName, geostate);
else
if (strcmp(dot, ".sgo") == 0)
node = LoadSgo(fileName, geostate);
else
if (strcmp(dot, ".stla") == 0 || strcmp(dot, ".stl") == 0)
node = LoadStla(fileName, geostate);
else
if (strcmp(dot, ".stlb") == 0)
node = LoadStlb(fileName, geostate);
else
if (strcmp(dot, ".sv") == 0)
node = LoadSv(fileName, geostate);
else
pfNotify(PFNFY_NOTICE, PFNFY_PRINT,
"File \"%s\" not loaded -- unknown file type", fileName);
/* return pointer to in-memory hierarchy */
return node;
}
/***************************************************************************/
/*
* Usage() -- print usage advice and exit. This procedure
* is executed in the application process.
*/
static void
Usage (void)
{
fprintf(stderr, "Usage: %s [-p procSplit] [file.ext ...]\n", ProgName);
exit(1);
}
/*
* docmdline() -- use getopt to get command-line arguments,
* executed at the start of the application process.
*/
static long
docmdline(int argc, char *argv[])
{
long opt;
strcpy(ProgName, argv[0]);
/* process command-line arguments */
while ((opt = getopt(argc, argv, "wp:?")) != -1)
{
switch (opt)
{
case 'w':
WriteScene = 1;
break;
case 'p':
ProcSplit = atoi(optarg);
break;
case '?':
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;
int once = 1;
pfChannel *chan;
pfChannel *chan1;
pfChannel *chan2;
pfChannel *chan3;
pfScene *scene;
pfPipe *p;
pfEarthSky *eSky;
pfBox bbox;
float far = 30000.0f;
pfCoord view;
pfVec3 backxyz;
pfVec3 backhpr;
if (argc < 2)
Usage();
arg = docmdline(argc, argv);
pfInit();
/* configure multi-process selection */
pfMultiprocess(ProcSplit);
/* for GL text drawing */
bsphere = (pfSphere*) pfMalloc (sizeof(pfSphere), pfGetSharedArena());
/* allocate shared before fork()'ing parallel processes */
Shared = (SharedData*)pfMalloc(sizeof(SharedData), pfGetSharedArena());
Shared->inWindow = 0;
Shared->exitFlag = 0;
Shared->drawStats = 0;
Shared->speed = 0.0f;
Shared->travmod = 1;
Shared->freeze = 0;
/* initiate multi-processing mode set in pfMultiprocess call */
pfConfig();
scene = pfNewScene();
/* specify directories where geometry and textures exist */
if (!(getenv("PFPATH")))
pfFilePath(
"."
":./data"
":../data"
":../../data"
":/usr/src/Performer/data"
);
fprintf(stderr,"FilePath: %s\n", pfGetFilePath());
/* load files named by command line arguments */
for (found = 0; arg < argc; arg++)
{
if ((root = LoadFile(argv[arg], NULL)) != NULL)
{
pfAddChild(scene, root);
found++;
}
}
/* if no files successfully loaded, terminate program */
if (!found)
Usage();
/* determine extent of scene's geometry */
pfuTravCalcBBox(scene, &bbox);
p = pfGetPipe(0);
pfPhase(PFPHASE_FLOAT);
/* Open and configure full screen GL window. */
pfInitPipe(p, OpenPipeline);
pfFrameRate(20.0f);
/* Create the main channel */
chan = pfNewChan(p);
pfChanCullFunc(chan, CullChannel);
pfChanDrawFunc(chan, DrawChannel);
pfChanScene(chan, scene);
pfChanNearFar(chan, 0.1f, far);
/* vertical FOV is matched to window aspect ratio */
pfChanFOV(chan, 45.0f, -1.0f);
/* Create an earth/sky model that draws sky/ground/horizon */
eSky = pfNewESky();
pfESkyMode(eSky, PFES_BUFFER_CLEAR, PFES_SKY_GRND);
pfESkyAttr(eSky, PFES_GRND_HT, -10.0f);
pfChanESky(chan, eSky);
/* set initial view position
Shared->yval=0.0f;
Shared->zval=0.5f;*/
{
float sceneSize;
/* Set initial view to be "in front" of scene */
/* view point at center of bbox */
pfAddVec3(Shared->view.xyz, bbox.min, bbox.max);
pfScaleVec3(Shared->view.xyz, 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;
pfSetVec3(Shared->view.xyz,0.0f,0.0f,10.0f);
pfSetVec3(Shared->view.hpr, 0.0f, 0.0f, 0.0f);
pfChanView(chan, Shared->view.xyz, Shared->view.hpr);
}
/* set up intersection segment, pointing down
for "terrain" following */
segset.activeMask = 1;
segset.isectMask = 0xFFFF;
segset.mode = PFTRAV_IS_PRIM|PFTRAV_IS_NORM|PFTRAV_IS_CULL_BACK;
pfSetVec3(segset.segs[0].dir, 0.0f, 0.0f, -1.0f);
segset.segs[0].length = 50000.0f;
/* save initial viewpoint for reset */
pfCopyVec3(Shared->orig_xyz, Shared->view.xyz);
/* Clock initialised for timing the intro */
pfInitClock (0.0f);
/* main simulation loop */
while (!Shared->exitFlag)
{
/* TERRAIN FOLLOWING */
pfHit **hits[100];
long isect;
/* wait until next frame boundary */
pfSync();
/* Set view parameters. */
pfChanView(chan, Shared->view.xyz, Shared->view.hpr);
if(Shared->chan2opt)
{
if(once)
{
once = !once;
/* Create 1st channel */
/* PERSPECTIVE BIRD'S EYE VIEW */
chan1 = pfNewChan(p);
pfChanCullFunc(chan1, CullChannel);
pfChanDrawFunc(chan1, DrawChannelOne);
pfChanScene(chan1, scene);
pfChanNearFar(chan1, 0.1f, far);
/* vertical FOV is matched to window aspect ratio */
pfChanFOV(chan1, 60.0f, -1.0f);
pfESkyMode(eSky, PFES_BUFFER_CLEAR, PFES_SKY);
pfESkyAttr(eSky, PFES_GRND_HT, 5.0f);
pfChanESky(chan1, eSky);
pfChanViewport(chan1, 0.03, 0.3, 0.72, 0.97);
/* 'chan1' slaved to masterchan 'chan' */
/*pfAttachChan(chan,chan1);*/
/*ViewState->masterChan = chan;*/
/* Set chan1's viewing offset to top view */
/* viewpoint does not change */
pfSetVec3(backxyz, 0.0f, -200.0f,0.0f);
/* viewdirection changes */
pfSetVec3(backhpr,0.0f, 0.0f, 0.0f);
pfChanViewOffsets(chan1,backxyz,backhpr);
/* Create 2nd channel */
/* VERTICAL AREAL VIEW */
chan2 = pfNewChan(p);
pfChanCullFunc(chan2, CullChannel);
pfChanDrawFunc(chan2, DrawChannelOne);
pfChanScene(chan2, scene);
pfChanNearFar(chan2, 0.1f, far);
/* vertical FOV is matched to window aspect ratio */
pfChanFOV(chan2, 45.0f, -1.0f);
pfESkyMode(eSky, PFES_BUFFER_CLEAR, PFES_SKY_GRND);
pfESkyAttr(eSky, PFES_GRND_HT, -10.0f);
pfChanESky(chan2, eSky);
pfChanViewport(chan2, 0.69, .97, 0.72, 0.97);
/* 'chan2' slaved to masterchan 'chan' */
/*pfAttachChan(chan,chan2);*/
/*ViewState->masterChan = chan;*/
/* Set chan2's viewing offset to getback view */
/* viewpoint does not change */
pfSetVec3(backxyz, 0.0f, 20.0f,500.0f);
/* viewdirection changes */
pfSetVec3(backhpr,0.0f, -90.0f, 0.0f);
pfChanViewOffsets(chan2,backxyz,backhpr);
/* Create 3rd channel */
/* REAR VIEW MIRROR */
chan3 = pfNewChan(p);
pfChanCullFunc(chan3, CullChannel);
pfChanDrawFunc(chan3, DrawChannelOne);
pfChanScene(chan3, scene);
pfChanNearFar(chan3, 0.1f, far);
/* vertical FOV is matched to window aspect ratio */
pfChanFOV(chan3, 45.0f, -1.0f);
pfESkyMode(eSky, PFES_BUFFER_CLEAR, PFES_SKY_GRND);
pfESkyAttr(eSky, PFES_GRND_HT, -10.0f);
pfChanESky(chan3, eSky);
pfChanViewport(chan3, 0.36, 0.63, 0.72, 0.97);
/* 'chan3' slaved to masterchan 'chan' */
/*pfAttachChan(chan,chan3);*/
/*ViewState->masterChan = chan;*/
/* Set chan3's viewing offset to getback view */
/* viewpoint does not change */
pfSetVec3(backxyz, 0.0f, 0.0f,0.0f);
/* viewdirection changes */
pfSetVec3(backhpr,-180.0f, 0.0f, 0.0f);
pfChanViewOffsets(chan3,backxyz,backhpr);
}
/* Set view parameters. */
pfChanView(chan2, Shared->view.xyz, Shared->view.hpr);
pfChanView(chan3, Shared->view.xyz, Shared->view.hpr);
pfChanView(chan1, Shared->view.xyz, Shared->view.hpr);
}
/* Set view parameters. */
pfChanView(chan, Shared->view.xyz, Shared->view.hpr);
/* initiate traversal using current state */
pfFrame();
/* compute new view */
if (Shared->reset)
ResetView();
else
if(!Shared->freeze)
UpdateView();
}
/* terminate cull and draw processes (if they exist) */
pfExit();
/* exit to operating system
exit(0);*/
}
/*
* Resetview() Resetes the viewpoint
*/
static void
ResetView(void)
{
pfCoord *view = &Shared->view;
Shared->reset = 0;
Shared->hudopt = 0.0;
pfCopyVec3(view->xyz, Shared->orig_xyz);
pfSetVec3(view->hpr, 0.0f, 0.0f, 0.0f);
}
/*
* UpdateView() updates the eyepoint based on the information
* placed in Shared memory by GetGLInput().
*/
static void
UpdateView(void)
{
static float speed = 0.0f;
static float speedLimit = 4.0f;
pfCoord *view = &Shared->view;
float cp;
pfHit **hits[100];
long isect;
if (!Shared->inWindow)
{
speed = 0;
return;
}
switch (Shared->mouseButtons)
{
case Button1Mask: /* LEFTMOUSE: faster forward or slower backward*/
if (speed > 0.0f)
speed *= 1.2f;
else
speed /= 1.2f;
if (PF_ABSLT(speed, LOSPEED * Shared->sceneSize))
speed = LOSPEED * Shared->sceneSize;
else if (speed >= HISPEED * Shared->sceneSize)
speed = HISPEED * Shared->sceneSize;
break;
case Button2Mask: /* MIDDLEMOUSE: stop moving and pick */
speed = 0.0f;
break;
case Button3Mask: /* RIGHTMOUSE: faster backward or slower foreward*/
if (speed < 0.0f)
speed *= 1.2f;
else
speed /= 1.2f;
if (PF_ABSLT(speed, LOSPEED * Shared->sceneSize))
speed = -LOSPEED * Shared->sceneSize;
else if (speed <= -HISPEED * Shared->sceneSize)
speed = -HISPEED * Shared->sceneSize;
break;
}
/* update view direction */
view->hpr[PF_H] -= Shared->mouseX * PF_ABS(Shared->mouseX);
view->hpr[PF_P] = Shared->mouseY * PF_ABS(Shared->mouseY) * 90.0f;
cp = cosf(PF_DEG2RAD(view->hpr[PF_P]));
if(!Shared->travmod) view->hpr[PF_P] = 0.0f;
view->hpr[PF_R] = 0.0f;
/*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]));
if(!Shared->travmod)
{
/* update location of intersection segment */
pfSetVec3(segset.segs[0].pos, view->xyz[PF_X], view->xyz[PF_Y], view->xyz[PF_Z]);
/* do an intersection test against the scene graph */
isect = pfSegsIsectNode(root, &segset, hits);
/* if successful, set our height to that of
the point of contact, plus a small offset */
if(isect)
{
pfVec3 pnt;
pfQueryHit(*hits[0], PFQHIT_POINT, &pnt);
view->xyz[PF_Z] = pnt[PF_Z] + 0.5;
}
}
}
/*
* OpenPipeline() -- create a pipeline: setup the window system,
* the IRIS GL, and IRIS Performer. this procedure is executed in
* the draw process (when there is a separate draw process).
*/
static void
OpenPipeline(pfPipe *p)
{
float xSize = 800;
float ySize = 500;
/* negotiate with window-manager */
/* Open,Size and Position graphics window */
foreground();
prefposition(200, 900, 300, 800);
winopen("R.E.A.L.I.T.Y by K + I + S + H + A + N");
winconstraints();
/* register events of note with event-queue manager */
qdevice(ESCKEY);
qdevice(F1KEY);
qdevice(F2KEY);
qdevice(F3KEY);
qdevice(SPACEKEY);
qdevice(KEYBD);
/* negotiate with GL */
/* Configure window with reasonable defaults. */
pfInitGfx(p);
/* create a light source in the "south-west" (QIII) */
Sun = pfNewLight(NULL);
pfLightPos(Sun, -0.3f, -0.3f, 1.0f, 0.0f);
/* create a default texture environment */
pfApplyTEnv(pfNewTEnv(NULL));
/* create a default lighting model */
pfApplyLModel(pfNewLModel(NULL));
pfApplyMtl(pfNewMtl(NULL));
/* enable culling of back-facing polygons */
pfCullFace(PFCF_BACK);
/*
* These enables should be set to reflect the majority of the
* database. If most geometry is not textured, then texture
* should be disabled. However, you then need to change the
* FLIGHT-format file reader. (pfflt.c)
*/
pfEnable(PFEN_TEXTURE);
pfEnable(PFEN_LIGHTING);
pfEnable(PFEN_FOG);
}
/*
* 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 *channel, void *data)
{
/*
* pfDrawGeoSet or other display listable Performer routines
* could be invoked before or after pfCull()
*/
pfCull();
}
/*
* 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 *data)
{
float t;
/* rebind light so it stays fixed in position */
pfLightOn(Sun);
/* erase framebuffer and draw Earth-Sky model */
pfClearChan(channel);
/* invoke Performer draw-processing for this frame */
pfDraw();
/* Draw Introductory Information */
/* Check the time */
t = pfGetTime();
if( (t < 20.0f)&&(!Shared->drawStats) )
DrawIntro(channel);
if(Shared->hudopt)
DrawHud(channel);
/* draw Performer throughput statistics */
if (Shared->drawStats)
pfDrawChanStats(channel);
/* draw text on the scene */
if (Shared->message)
DrawText(channel);
if(modflag)
{
DrawModinfo(channel, 1);
modflag++;
if(modflag >= 99)
modflag = 0;
}
/* read window origin and size (it may have changed) */
pfGetPipeSize(pfGetChanPipe(channel),
&Shared->winSizeX, &Shared->winSizeY);
pfGetPipeOrigin(pfGetChanPipe(channel),
&Shared->winOriginX, &Shared->winOriginY);
GetGLInput();
}
/*
* DrawChannelOne() -- draw a channel and read input queue. this
* procedure is executed in the draw process (when there is a
* separate draw process).For the inset Views
*/
static void
DrawChannelOne (pfChannel *channel, void *data)
{
float t;
/* rebind light so it stays fixed in position */
pfLightOn(Sun);
/* erase framebuffer and draw Earth-Sky model */
pfClearChan(channel);
/* invoke Performer draw-processing for this frame */
pfDraw();
/* Draw Borders */
if (Shared->chan2opt)
DrawBorder(channel);
/* draw Performer throughput statistics */
if (Shared->drawStats)
pfDrawChanStats(channel);
/* read window origin and size (it may have changed) */
pfGetPipeSize(pfGetChanPipe(channel),
&Shared->winSizeX, &Shared->winSizeY);
pfGetPipeOrigin(pfGetChanPipe(channel),
&Shared->winOriginX, &Shared->winOriginY);
}
static void
GetGLInput(void)
{
long x, y;
while (qtest())
{
short value;
long device = qread(&value);
/* only act on key-down transitions */
if (value)
{
switch (device)
{
/* ESC-key signals end of simulation */
case ESCKEY:
Shared->exitFlag = 1;
break;
/* F2-key toggles channel-stats display */
case F2KEY:
Shared->drawStats = !Shared->drawStats;
Shared->message = 0;
Shared->hudopt = 0;
break;
/* F1-key toggles message display */
case F1KEY:
Shared->message = !Shared->message;
break;
/* F3-key toggles drive/fly modes */
case F3KEY:
Shared->travmod = !Shared->travmod;
modflag = 1;
break;
case SPACEKEY:
Shared->speed = 0.0f;
break;
case KEYBD:
switch(value)
{
case 'r':
Shared->speed = 0.0f;
Shared->reset = 1;
break;
case 'h':
Shared->hudopt = !Shared->hudopt;
Shared->drawStats = 0;
Shared->message = 0;
break;
case 'b':
Shared->chan2opt = !Shared->chan2opt;
Shared->hudopt = 0;
Shared->drawStats = 0;
Shared->message = 0;
break;
case 'm':
Shared->message = !Shared->message;
Shared->hudopt = 0;
Shared->drawStats = 0;
break;
case 'f':
Shared->freeze = !Shared->freeze;
break;
}
}
}
}
/* read cursor position (may be outside our window) */
x = getvaluator(MOUSEX);
y = getvaluator(MOUSEY);
Shared->inWindow = 0;
/* update cursor virtual position when cursor inside window */
if (x >= Shared->winOriginX &&
x < (Shared->winOriginX + Shared->winSizeX) &&
y >= Shared->winOriginY &&
y < (Shared->winOriginY + Shared->winSizeY))
{
Shared->inWindow = 1;
Shared->mouseX = 2.0f * ((x - Shared->winOriginX) /
(float)Shared->winSizeX) - 1.0f;
Shared->mouseY = 2.0f * ((y - Shared->winOriginY) /
(float)Shared->winSizeY) - 1.0f;
Shared->mouseButtons = ((getbutton(LEFTMOUSE) ? Button1Mask : 0) |
(getbutton(MIDDLEMOUSE) ? Button2Mask : 0) |
(getbutton(RIGHTMOUSE) ? Button3Mask : 0));
}
}
/*
* Drawtext() -- Draws the text string on the
* scene as GL text
*/
static void
DrawText(pfChannel *channel)
{
static pfMatrix tempmat;
pfClearChan (channel);
pfDraw();
zfunction (ZF_ALWAYS); /* text always drawn */
zwritemask (0x0);
/* text moving with the scene */
pfPushState();
pfBasicState();
cmov (0.5f * bsphere->radius, 0.0f, 0.5f * bsphere->radius);
cpack(0xFFFFFF);
charstr ("VIRTUAL REALITY");
pfPopState();
/* text overlaid on top of scene, like a HUD */
pfPushState();
pfBasicState();
mmode (MPROJECTION);
getmatrix (tempmat);
mmode (MVIEWING);
ortho2 (-10.0, 10.0, -10.0, 10.0);
RGBmode();
gconfig();
RGBcolor(200, 0, 255);
pfPushMatrix();
pfLoadMatrix(idmat);
cmov2 (-9.5, 9.5);
charstr ("EVEN BETTER THAN THE REAL THING + Kishan P.Thomas + 08/09/95");
pfLoadMatrix(idmat);
cmov2 (-9.5, -8.0);
RGBcolor(255, 255, 255);
charstr (">> Left-mouse:Advance, Middle-mouse:Stop, Right-mouse:Retreat *");
pfLoadMatrix(idmat);
cmov2 (-9.5, -8.5);
charstr (">> r -Reset * f -Freeze * h -HUD * b -Other views * Esc -Exit * ");
pfLoadMatrix(idmat);
cmov2 (-9.5, -9.0);
charstr (">> F1-Assistance * F2 -Statistics * F3 -Mod *");
pfPopMatrix();
mmode (MPROJECTION);
loadmatrix (tempmat);
mmode (MVIEWING);
pfPopState();
zfunction (ZF_LEQUAL); /* the default */
zwritemask (0xffffffff);
}
/*
* DrawIntro() -- Draws the introduction
* messages as GL text for 10 seconds
*/
static void
DrawIntro(pfChannel *channel)
{
static pfMatrix tempmat;
long vert1[2] = {-5, -5};
long vert2[2] = {-5, 5};
long vert3[2] = { 5, 5};
long vert4[2] = { 5, -5};
pfClearChan (channel);
pfDraw();
zfunction (ZF_ALWAYS); /* text always drawn */
zwritemask (0x0);
/* text overlaid on top of scene, like a HUD */
pfPushState();
pfBasicState();
mmode (MPROJECTION);
getmatrix (tempmat);
mmode (MVIEWING);