/* * pick.c * * $Revision: 1.12 $ * * Run-time controls: * ESC-key : exits * g-key : profile * Left-mouse: advance * Middle-mouse: stop * Right-mouse: retreat * Space-bar : stop * Alt+Left-mouse: pick * A-key : toggle all/nearest picking * D-key : picking depth: groups, geodes, geosets */ #include #include #include #include #include #include #include "/usr/src/Performer/include/pfflt.h" #include "/usr/src/Performer/include/pfutil.h" static void CullChannel(pfChannel *chan, void *data); static void DrawChannel(pfChannel *chan, void *data); static void OpenPipeline(pfPipe *p); static long CheckPickList(pfuIsect *list, long length, pfVec3 eye); static void usage(void); #define HISPEED 0.1f #define LOSPEED 0.001f /* * structure that resides in shared memory so that the * application, cull, and draw processes can access it. */ typedef struct { long exitFlag; float mouseX; float mouseY; float speed; float sceneSize; long profile; long reset; long pickFlag; long pickDepth; long pickMode; float nmx, nmy; } SharedData; static SharedData *Shared; /* light source created and updated in draw-process */ static pfLight *Sun; /* * main() -- program entry point. this procedure * is executed in the application process. */ int main (int argc, char *argv[]) { int arg; int found; pfGroup *root; pfChannel *chan; pfScene *scene; pfPipe *p; pfEarthSky *eSky; pfBox bbox; pfCoord view; float far = 10000.0f; float slop; pfVec3 orig_xyz; if (argc < 2) usage(); pfInit(); pfNotifyLevel(PFNFY_DEBUG); pfMultiprocess(PFMP_DEFAULT); /* allocate shared before fork()'ing parallel processes */ Shared = (SharedData*)pfMalloc(sizeof(SharedData), pfGetSharedArena()); Shared->speed = 0.0f; Shared->exitFlag = 0; Shared->profile = 0; Shared->pickFlag = 0; Shared->reset = 0; Shared->pickDepth = PFTRAV_IS_PRIM; /* initiate multi-processing mode set in pfMultiprocess call */ pfConfig(); scene = pfNewScene(); /* specify directories where geometry and textures exist */ pfFilePath(".:data:../data:/usr/src/Performer/data"); /* load FLIGHT files named by command line arguments */ for (arg = 1, found = 0; arg < argc; arg++) { if ((root = (pfGroup *)LoadFlt(argv[arg])) != NULL) { pfAddChild(scene, root); pfNodePickSetup(scene); 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); chan = pfNewChan(p); pfChanCullFunc(chan, CullChannel); pfChanDrawFunc(chan, DrawChannel); pfChanScene(chan, scene); pfChanNearFar(chan, 0.1f, far); /* 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); /* vertical FOV is matched to window aspect ratio */ pfChanFOV(chan, 45.0f, -1.0f); { /* Set initial view to be "in front" of scene */ /* view point at center of bbox */ pfAddVec3(view.xyz, bbox.min, bbox.max); pfScaleVec3(view.xyz, 0.5f, view.xyz); /* find max dimension */ Shared->sceneSize = bbox.max[PF_X] - bbox.min[PF_X]; Shared->sceneSize = PF_MAX2(Shared->sceneSize, bbox.max[PF_Y] - bbox.min[PF_Y]); Shared->sceneSize = PF_MAX2(Shared->sceneSize, bbox.max[PF_Z] - bbox.min[PF_Z]); Shared->sceneSize = PF_MIN2(Shared->sceneSize, 0.5 * far); /* offset so all is visible */ view.xyz[PF_Y] -= Shared->sceneSize; view.xyz[PF_Z] += 0.25*Shared->sceneSize; pfSetVec3(view.hpr, 0.0f, 0.0f, 0.0f); pfChanView(chan, view.xyz, view.hpr); /* save initial viewpoint for reset */ pfCopyVec3(orig_xyz, view.xyz); } /* main simulation loop */ while (!Shared->exitFlag) { float cp; /* wait until next frame boundary */ pfSync(); /* Set view parameters. */ pfChanView(chan, view.xyz, view.hpr); /* initiate traversal using current state */ pfFrame(); if (Shared->reset) { Shared->reset = 0; pfCopyVec3(view.xyz, orig_xyz); pfSetVec3(view.hpr, 0.0f, 0.0f, 0.0f); } else { /* update view direction */ view.hpr[PF_H] -= Shared->mouseX; view.hpr[PF_P] = Shared->mouseY*90.0f; view.hpr[PF_R] = 0.0f; /* update view position */ cp = cosf(PF_DEG2RAD(view.hpr[PF_P])); view.xyz[PF_X] += Shared->speed*sinf(-PF_DEG2RAD(view.hpr[PF_H])*cp); view.xyz[PF_Y] += Shared->speed*cosf(-PF_DEG2RAD(view.hpr[PF_H])*cp); view.xyz[PF_Z] += Shared->speed*sinf( PF_DEG2RAD(view.hpr[PF_P])); } if (Shared->pickFlag == 1) { long num, i, j, pathLen; long dohl = 1; pfHit **picklistp; pfNode *node, *pathElt; pfGeoSet *gset; pfPath *path; long flags; Shared->pickFlag = 2; num = pfChanPick(chan, Shared->pickMode | Shared->pickDepth | PFTRAV_IS_PATH, Shared->nmx, Shared->nmy, 0, &picklistp); if (num) { pfNotify(PFNFY_DEBUG, PFNFY_PRINT, "Have %d hits\n", num); for (i = 0 ; i < num ; i++) { pfQueryHit(picklistp[i], PFQHIT_NODE, &node); pfQueryHit(picklistp[i], PFQHIT_GSET, &gset); pfQueryHit(picklistp[i], PFQHIT_FLAGS, &flags); pfQueryHit(picklistp[i], PFQHIT_PATH, &path); if (flags & PFHIT_PRIM) fprintf(stderr, "Prim isect in %dth pfHit: 0x%x gset: 0x%x node: 0x%x\n", i, picklistp[i], gset, node); else { if (Shared->pickDepth == PFTRAV_IS_PRIM) fprintf(stderr, "ERROR: didn't get PRIM in %dth pfHit.\n", i); dohl = 0; } if (path) { pathLen = pfGetNum(path); fprintf(stderr, "Have path for %dth pfHit of %d children\n", i, pathLen); for (j=0; j < pathLen; j++) { pathElt = (pfNode*)pfGet(path, j); fprintf(stderr, "\tchild %d 0x%x of type %s\n", j, pathElt, pfGetTypeName(pathElt)); } } if (dohl) { if (pfGetGSetHlight(gset)) pfuTravNodeHlight(node, PFHL_INHERIT); else pfuTravNodeHlight(node, PFHL_ON); } } } else pfNotify(PFNFY_DEBUG, PFNFY_PRINT, "No successful picking intersections: %f %f.\n", Shared->nmx, Shared->nmy); } } /* terminate cull and draw processes (if they exist) */ pfExit(); /* exit to operating system */ exit(0); } /* * 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) { pfHighlight *hl; /* negotiate with window-manager */ scrnselect(0); foreground(); winopen("IRIS Performer"); winconstraints(); /* negotiate with GL */ pfInitGfx(p); /* register events of note with event-queue manager */ qdevice(ESCKEY); qdevice(F1KEY); qdevice(GKEY); qdevice(AKEY); qdevice(DKEY); qdevice(SPACEKEY); qdevice(LEFTALTKEY); qdevice(RIGHTALTKEY); qdevice(LEFTMOUSE); /* 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)); /* 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); hl = pfNewHlight(pfGetSharedArena()); pfApplyHlight(hl); pfHlightMode(hl, PFHL_FILL); pfHlightColor(hl, PFHL_FGCOLOR, 1.0f, 0.0f, 1.0f); } /* * 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) { long xo, yo, xs, ys; long ch_xo,ch_yo, ch_xs, ch_ys; static float chxoLast=0,chyoLast=0, chxsLast=0, chysLast=0; long buttons; long x; long y; short val; long device; /* 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 Performer throughput statistics */ if (Shared->profile) pfDrawChanStats(channel); /* examine the GL-event queue for keyboard events */ while (qtest()) { device = qread(&val); switch (device) { /* ESC-key signals end of simulation */ case ESCKEY: Shared->exitFlag = 1; break; /* g-key toggles channel-profile display */ case GKEY: if (val) Shared->profile = !Shared->profile; break; case F1KEY: Shared->reset = 1; Shared->speed = 0.0f; break; case SPACEKEY: Shared->speed = 0.0f; break; case LEFTMOUSE: if (val && Shared->pickFlag < 0) Shared->pickFlag = 1; else { if (Shared->pickFlag) Shared->pickFlag = -2; } break; case LEFTALTKEY: case RIGHTALTKEY: if (val) Shared->pickFlag = -1; else Shared->pickFlag = 0; break; case AKEY: if (val) Shared->pickMode = (Shared->pickMode ? 0 : PFPK_M_ALL); break; case DKEY: if (val) switch (Shared->pickDepth) { case PFTRAV_IS_PRIM: Shared->pickDepth = PFTRAV_IS_GSET; fprintf(stderr, "pick gsets\n"); break; case PFTRAV_IS_GSET: Shared->pickDepth = PFTRAV_IS_GEODE; fprintf(stderr, "pick geodes\n"); break; case PFTRAV_IS_GEODE: Shared->pickDepth = PFTRAV_IS_PRIM; fprintf(stderr, "pick prims\n"); break; } break; } } /* read cursor position (may be outside our window) */ x = getvaluator(MOUSEX); y = getvaluator(MOUSEY); /* read window origin and size (it may have changed) */ pfGetPipeOrigin(pfGetChanPipe(channel), &xo, &yo); pfGetPipeSize(pfGetChanPipe(channel), &xs, &ys); pfGetChanOrigin((channel), &ch_xo, &ch_yo); pfGetChanSize((channel), &ch_xs, &ch_ys); if (ch_xs != chxsLast || ch_xo != chxoLast || ch_ys != chysLast || ch_yo != chyoLast) { chxsLast = ch_xs; chxoLast = ch_xo; chysLast = ch_ys; chyoLast = ch_yo; } /* update cursor virtual position when cursor inside window */ if (x >= xo && x < (xo + xs) && y >= yo && y < (yo + ys)) { float mx = 2.0f*(x - xo)/(float)xs - 1.0f; float my = 2.0f*(y - yo)/(float)ys - 1.0f; float speedLimit = 4.0f; Shared->nmx = (float) (x - ch_xo - xo) / (float) ch_xs; Shared->nmy = (float) (y - ch_yo - yo) / (float) ch_ys; /* square cursor position to provide a "dead" center */ Shared->mouseX = mx*mx*((mx < 0.0f) ? -1.0f : 1.0f); Shared->mouseY = my*my*((my < 0.0f) ? -1.0f : 1.0f); buttons = ((getbutton(LEFTMOUSE) ? 0x04 : 0) | (getbutton(MIDDLEMOUSE) ? 0x02 : 0) | (getbutton(RIGHTMOUSE) ? 0x01 : 0)); switch (buttons) { case 0x4: /* LEFTMOUSE */ if (Shared->pickFlag < 0) { /* if alt-key - picking */ Shared->pickFlag = 1; } else if (!Shared->pickFlag) { /* else faster forward or slower backward*/ if (Shared->speed > 0.0f) Shared->speed *= 1.2f; else Shared->speed /= 1.2f; if (PF_ABSLT(Shared->speed, LOSPEED * Shared->sceneSize)) Shared->speed = LOSPEED * Shared->sceneSize; else if (Shared->speed >= HISPEED * Shared->sceneSize) Shared->speed = HISPEED * Shared->sceneSize; } break; case 0x02: /* MIDDLEMOUSE: stop moving and pick */ Shared->speed = 0.0f; break; case 0x01: /* RIGHTMOUSE: faster backward or slower foreward*/ if (Shared->speed < 0.0f) Shared->speed *= 1.2f; else Shared->speed /= 1.2f; if (PF_ABSLT(Shared->speed, LOSPEED * Shared->sceneSize)) Shared->speed = -LOSPEED * Shared->sceneSize; else if (Shared->speed <= -HISPEED * Shared->sceneSize) Shared->speed = -HISPEED * Shared->sceneSize; break; default: break; } } } /* * usage() -- print usage advice and exit. This procedure * is executed in the application process. */ static void usage (void) { fprintf(stderr, "Usage: simple file.flt ...\n"); pfExit(); exit(1); } static long CheckPickList(pfuIsect *list, long length, pfVec3 eye) { long i; float dist, last_dist; if (!list) { pfNotify(PFNFY_WARN, PFNFY_SYSERR, "CheckPickList: Error - nil pick list\n"); return 0; } pfNotify(PFNFY_WARN, PFNFY_PRINT, "CheckPickList: length = %d\n", length); /* walk down the isect list and see if things are kosher */ for (i=0; i < length; i++) { dist = PFDISTANCE_PT3(eye, list[i].point); if (dist < last_dist) pfNotify(PFNFY_WARN, PFNFY_PRINT, "ERROR: bad pick sort:"); pfNotify(PFNFY_WARN, PFNFY_PRINT, "\thit %d: eye dist = %f\n", i, dist); last_dist = dist; } return 1; }