This is event.c in view mode; [Download] [Up]
/* Copyright (c) 1992 The Geometry Center; University of Minnesota 1300 South Second Street; Minneapolis, MN 55454, USA; This file is part of geomview/OOGL. geomview/OOGL is free software; you can redistribute it and/or modify it only under the terms given in the file COPYING, which you should have received along with this file. This and other related software may be obtained via anonymous ftp from geom.umn.edu; email: software@geom.umn.edu. */ /* Authors: Stuart Levy, Tamara Munzner, Mark Phillips */ #include <ctype.h> #include <sys/types.h> #include <sys/time.h> #include <string.h> #include "mg.h" #include "drawer.h" #include "event.h" #include "lang.h" #include "ui.h" #include "comm.h" #include "pickP.h" #include "transform.h" #include "streampool.h" #include "lights.h" #include "rman.h" #define DOUBLECLICKTIME 333 /* millisecs between double clicks */ EventState estate; /* External motion state */ struct button button; /* Shift, etc. button state */ void emit_pick(int pickedid, Pick *pick); static int view_pick( DView *dv, int x, int y, Pick *pick ); #define ESC '\033' static int hasnumber = 0; static int number; static int expon; static float getreal(float); static int toggle(int val); static void tog_ap_flag(int id, int flagbit); static int retarget(int defaultid); #define SEQ(prefix, ch) ((prefix)<<8 | (ch)) static enum { KEYGEOM=0, KEYCAM=1, KEYNONE=2 } keymode = KEYNONE; static int prefix = 0; static char Help[] = "\ Keyboard commands apply while cursor is in any graphics window and most \n\ control panels. Most commands allow one of the following selection prefixes \n\ (if none is provided the command applies to the current object): \n\ g world geom g# #'th geom g* All geoms\n\ c current camera c# #'th camera c* All cameras\n\ Many allow a numeric prefix: if none they toggle or reset current value.\n\ Appearance:\n\ Draw: Shading: Other:\n\ af Faces 0as Constant av eVert normals: always face viewer\n\ ae Edges 1as Flat #aw Line Width (pixels)\n\ an Normals 2as Smooth #ac edges Closer than faces(try 5-100)\n\ ab Bounding Boxes 3as Smooth, non-lighted\n\ aV Vectors aT allow transparency\n\ Color:\n\ Cf Ce Cn Cb CB face/edge/normal/bbox/backgnd\n\ Motions: Viewing:\n\ r rotate [ Leftmouse=X-Y plane, 0vp Orthographic view\n" #ifdef NeXT " t translate Alt-Left=Z axis, 1vp Perspective view\n" #else " t translate Middle=Z axis, 1vp Perspective view\n" #endif " z zoom FOV Shift=slow motion, vd Draw other views' cameras\n\ f fly in r/t modes. ] #vv field of View\n\ o orbit [Left=steer, Middle=speed ] #vn near clip distance\n\ s scale #vf far clip distance\n\ w/W recenter/all v+ add new camera\n\ h/H halt/halt all vx cursor on/off\n\ @ select center of motion (e.g. g3@) vb backfacing poly cull on/off\n\ #vl focal length\n\ L Look At object v~ Software shading on/off\n\ show Panel: Pm Pa Pl Po main/appearance/lighting/obscure\n\ Pt Pc PC Pf tools/cameras/Commands/file-browser\n\ Ps P- saving/read commands from tty\n\ Lights: ls le Show lights / Edit Lights\n\ Metric: me mh ms Euclidean Hyperbolic Spherical\n\ Model: mv mp mc Virtual Projective Conformal\n\ Other:\n\ N normalization < Pf load geom/command file\n\ 0N none > Ps save something to file ui motion has inertia\n\ 1N each TV NTSC mode toggle uc constrained (X/Y) motion\n\ 2N all uo motion in Own coord system\n\ Rightmouse-doubleclick pick as current target object\n\ Shift-Rightmouse pick interest (center) point\n" #ifdef NeXT " Alt-Leftmouse is synonym for Rightmouse.\n" #endif ; void print_help() { printf("\n%s", Help); rman_do('?', 0,0); fflush(stdout); } void event_init() { estate.motionproc = NULL; } LDEFINE(event_mode, LVOID, "(event-mode MODESTRING)\n\ Set the mouse event (motion) mode; MODESTRING should be one of\n\ the strings that appears in the motion mode browser (including\n\ the keyboard shortcut, e.g. \"[r] Rotate\").") { static Event ev_enter = { EMODEENTER, 0, 0, 0, 0 }; static Event ev_exit = { EMODEEXIT, 0, 0, 0, 0 }; char *modename; LDECLARE(("event-mode", LBEGIN, LSTRING, &modename, LEND)); if ( estate.motionproc != NULL ) { estate.motionproc(&ev_exit); } estate.motionproc = uistate.modes[uistate.mode_current = ui_mode_index(modename) ]; ui_event_mode( modename ); if ( estate.motionproc != NULL ) { estate.motionproc(&ev_enter); } return Lt; } /* * Report time elapsed since the epoch (or since the program began if * since == NULL). Possibly remember the current time in "nextepoch". * Time is measured in floating-point seconds. */ float elapsed(float *since, float *nextepoch) { static struct timeval t0 = { 0, 0 }; struct timeval tnow; float now = 0; float sincetime = 0; gettimeofday(&tnow, NULL); if(t0.tv_sec == 0) { t0 = tnow; tnow.tv_usec++; } now = tnow.tv_sec - t0.tv_sec + 1e-6*(tnow.tv_usec - t0.tv_usec); if(since) { if((sincetime = *since) == 0) sincetime = *since = now; } if(nextepoch) *nextepoch = now; return now - sincetime; } LDEFINE(rawevent, LVOID, "(rawevent dev val x y t)\n\ Enter the specified raw event into the event queue. The\n\ arguments directly specify the members of the event structure\n\ used internally by geomview. This is the lowest level event\n\ handler and is not intended for general use.") /* This used to be dispatch_event(). */ { int id; int k; int err = 0; float v; Appearance *ap; DView *dv; DGeom *dg; char *s; Event event; LDECLARE(("rawevent", LBEGIN, LINT, &event.dev, LINT, &event.val, LINT, &event.x, LINT, &event.y, LINT, &event.t, LEND)); /* * Call the current motion proc, if any. This proc returns 1 if it * used the event, in which case we don't do any further processing. */ if ( estate.motionproc != NULL ) { if ( estate.motionproc(&event) ) return Lt; } /* The rightmouse and doubleclick is now hardcoded but should be bindable through lang later, through a control mechanism similar to current motionproc stuff */ if (event.dev == ERIGHTMOUSE) { if (event.val > 0) { static unsigned long int lastt = 0; Pick *pick = PickSet(NULL, PA_WANT, PW_EDGE|PW_VERT|PW_FACE, PA_END); int pickedid = view_pick( (DView *)drawer_get_object(FOCUSID), event.x, event.y, pick ); if(button.shift) { /* Could change FOCUSID here to ALLCAMS, * to force setting everyone's focal length to * their distance from the pick. */ if(pickedid != NOID) make_center_from_pick("CENTER", pick, FOCUSID); else gv_ui_center(TARGETID); } else { if(pickedid != NOID) emit_pick(pickedid, pick); if (event.t - lastt < DOUBLECLICKTIME) { lastt = 0; gv_ui_target( pickedid!=NOID ? pickedid : WORLDGEOM, IMMEDIATE ); } } PickDelete(pick); lastt = event.t; } } if(!isascii(event.dev)) return Lt; /* Only keyboard events from here on down */ ui_keyboard(event.dev); if(event.dev >= '0' && event.dev <= '9') { if(!hasnumber) { number = 0; hasnumber = 1; } number = 10*number + (event.dev - '0'); if(expon) expon++; } else { id = GEOMID(uistate.targetgeom); rescan: switch(SEQ(prefix, event.dev)) { case '-': case '*': hasnumber = -1; expon = 0; number = 0; goto keepmode; case '.': expon = 1; goto keepmode; case ESC: gv_exit(); /*NOTREACHED*/ case 'g': keymode = KEYGEOM; goto gotmode; /* Select geom: 'g' prefix */ case 'c': keymode = KEYCAM; goto gotmode; /* Select camera: 'c' prefix */ gotmode: if(hasnumber) gv_ui_target(retarget(NOID), IMMEDIATE); goto keepmode; case 'p': { int id; if (keymode == KEYNONE) { id = gv_rawpick(FOCUSID, event.x, event.y); if (id == NOID) id = WORLDGEOM; } else { id = retarget(NOID); } gv_ui_target(id, IMMEDIATE); } case '@': gv_ui_center(retarget(uistate.centerid)); break; case 'N': id = retarget(GEOMID(uistate.targetgeom)); if(!hasnumber) { dg = (DGeom *)drawer_get_object(id); if(dg) number = dg->normalization == NONE ? EACH : NONE; } drawer_int(id, DRAWER_NORMALIZATION, number); break; case '<': s = "Load"; number = 1; goto pickpanel; /* Load file */ case '>': s = "Save"; number = 1; goto pickpanel; /* Save State */ /* Halt current object */ case 'h': drawer_stop(retarget(uistate.targetid)); break; case 'H': /* Halt Everything */ drawer_stop(NOID); break; case 'w': /* Recenter current thing */ drawer_center(retarget(uistate.targetid)); break; case 'W': /* Recenter (and halt) Everything */ drawer_center(NOID); break; case 'L': gv_look(retarget(uistate.targetid),FOCUSID); break; /* * r/t/z/f/o apply to the currently selected object unless target specified. */ case 'f': s = OBJFLY; goto mote; case 'o': s = OBJORBIT; goto mote; case 'r': s = OBJROTATE; goto mote; case 't': s = OBJTRANSLATE; goto mote; case 'z': s = OBJZOOM; goto mote; case 's': s = OBJSCALE; goto mote; mote: k = retarget(NOID); if (k) gv_ui_target( k, IMMEDIATE); gv_event_mode( s ); break; case '?': print_help(); break; #ifdef sgi case 'T': /* NTSC */ #endif case 'v': /* view-related prefix */ case 'a': /* appearance-related prefix */ case 'm': /* metric (euclidean/hyperbolic/spherical) */ case 'l': /* light-related prefix */ case 'd': /* delete */ case 'R': /* renderman */ case 'C': /* color-pick */ case 'P': /* panel show */ case 'u': /* motion style */ if(keymode != KEYNONE) gv_ui_target(retarget(NOID), IMMEDIATE); prefix = event.dev; goto keepnumber; case SEQ('P','m'): case SEQ('P','g'): s = "main"; goto pickpanel; case SEQ('P','a'): s = "Appearance"; goto pickpanel; case SEQ('P','o'): s = "Obscure"; goto pickpanel; case SEQ('P','l'): s = "Lighting"; goto pickpanel; case SEQ('P','C'): s = "Command"; goto pickpanel; case SEQ('P','c'): s = "Camera"; goto pickpanel; case SEQ('P','t'): s = "Tools"; goto pickpanel; case SEQ('P','f'): s = "Files"; goto pickpanel; case SEQ('P','s'): s = "Save"; goto pickpanel; case SEQ('P','M'): s = "Materials"; goto pickpanel; pickpanel: if (!hasnumber) number = -1; ui_showpanel(ui_name2panel(s), number); break; case SEQ('P','-'): comm_object("(read command < -)", &CommandOps, NULL, NULL, COMM_LATER); break; case SEQ('C','f'): k = DRAWER_DIFFUSE; goto pickcolor; case SEQ('C','e'): k = DRAWER_EDGECOLOR; goto pickcolor; case SEQ('C','n'): k = DRAWER_NORMALCOLOR; goto pickcolor; case SEQ('C','b'): k = DRAWER_BBOXCOLOR; goto pickcolor; case SEQ('C','v'): case SEQ('C','B'): k = DRAWER_BACKCOLOR; goto pickcolor; pickcolor: ui_pickcolor( k ); break; case SEQ('u','i'): k = DRAWER_INERTIA; goto motstyle; case SEQ('u','c'): k = DRAWER_CONSTRAIN; goto motstyle; case SEQ('u','o'): k = DRAWER_OWNMOTION; motstyle: drawer_int( WORLDGEOM, k, hasnumber ? number : -1 ); break; case SEQ('v','+'): /* Add camera */ { CameraStruct cs; id = retarget(FOCUSID); dv = ISCAM(id) ? (DView *)drawer_get_object(id) : NULL; cs.h = NULL; cs.cam = dv && dv->cam ? CamCopy(dv->cam, NULL) : NULL; gv_new_camera(NULL, &cs); } break; case SEQ('v','p'): /* Projection: orthographic or perspective */ id = retarget(FOCUSID); if(!hasnumber) { dv = (DView *)drawer_get_object(id); if(dv) CamGet(dv->cam, CAM_PERSPECTIVE, &number); number = !number; } drawer_int( FOCUSID, DRAWER_PROJECTION, number ); break; case SEQ('v','d'): /* Draw other cameras */ id = retarget(FOCUSID); if(!hasnumber) { dv = (DView *)drawer_get_object(id); number = dv ? !dv->cameradraw : 1; } drawer_int( id, DRAWER_CAMERADRAW, number ); break; case SEQ('v','x'): /* Toggle/enable/disable cursor */ ui_curson( hasnumber ? number : -1 ); break; case SEQ('v','b'): gv_cull_backface( uistate.backface = toggle(uistate.backface) ); break; case SEQ('v','s'): id = retarget(FOCUSID); drawer_int( id, DRAWER_DOUBLEBUFFER, hasnumber ? number : -1 ); break; /* For testing software shading */ case SEQ('v','~'): id = retarget(FOCUSID); gv_soft_shader(id, hasnumber ? (number?ON_KEYWORD:OFF_KEYWORD) : TOGGLE_KEYWORD); break; /* Viewing options */ case SEQ('a','c'): case SEQ('v','c'): k = DRAWER_LINE_ZNUDGE; v = 10.; goto setcam; case SEQ('v','v'): k = DRAWER_FOV; v = 45.; goto setcam; case SEQ('v','n'): k = DRAWER_NEAR; v = .1; goto setcam; case SEQ('v','f'): k = DRAWER_FAR; v = 100.; goto setcam; case SEQ('v','l'): k = DRAWER_FOCALLENGTH; v = 3.; goto setcam; setcam: drawer_float( retarget(FOCUSID), k, getreal(v) ); break; /* Might add others here, e.g. a viewfinder mode. */ /* Metrics / Models */ case SEQ('m','e'): gv_space(EUCLIDEAN_KEYWORD); break; case SEQ('m','h'): gv_space(HYPERBOLIC_KEYWORD); break; case SEQ('m','s'): gv_space(SPHERICAL_KEYWORD); break; case SEQ('m','v'): gv_hmodel(CAMID(uistate.targetcam), VIRTUAL_KEYWORD); break; case SEQ('m','p'): gv_hmodel(CAMID(uistate.targetcam), PROJECTIVE_KEYWORD); break; case SEQ('m','c'): gv_hmodel(CAMID(uistate.targetcam), CONFORMALBALL_KEYWORD); break; /* Appearance settings */ case SEQ('a','f'): tog_ap_flag( id, APF_FACEDRAW ); break; case SEQ('a','e'): tog_ap_flag( id, APF_EDGEDRAW ); break; case SEQ('a','n'): tog_ap_flag( id, APF_NORMALDRAW ); break; case SEQ('a','v'): tog_ap_flag( id, APF_EVERT ); break; case SEQ('a','T'): tog_ap_flag( id, APF_TRANSP ); break; case SEQ('a','V'): tog_ap_flag( id, APF_VECTDRAW ); break; case SEQ('a','x'): drawer_set_ap( id, NULL, NULL ); break; case SEQ('a','o'): gv_ap_override( hasnumber ? number : !uistate.apoverride ); break; case SEQ('a','b'): /* Bounding box drawing */ if(!hasnumber) { DGeom *dg = (DGeom *)drawer_get_object( id ); if(dg) number = !dg->bboxdraw; } drawer_int(id, DRAWER_BBOXDRAW, number); break; case SEQ('a','s'): /* Shading */ if(!hasnumber) { ap = drawer_get_ap(id); ApGet(ap, AP_SHADING, &number); ApDelete(ap); number++; } drawer_int( id, DRAWER_SHADING, number&3); break; case SEQ('a','w'): /* line width */ if(!hasnumber) { ap = drawer_get_ap(id); ApGet(ap, AP_LINEWIDTH, &number); ApDelete(ap); number = (number > 1) ? 1 : 2; } drawer_int( id, DRAWER_LINEWIDTH, number); break; /* Scale normals */ case SEQ('a','h'): drawer_float(id, DRAWER_NORMSCALE, getreal(1.0)); break; /* Patch dicing */ case SEQ('a','d'): drawer_int( id, DRAWER_BEZDICE, number ); break; /* hyperbolic sphere at infinity */ case SEQ('a', 'i'): drawer_int( TARGETCAMID, DRAWER_HSPHERE, -1 ); break; /* Delete */ case SEQ('d','d'): gv_delete(uistate.targetid); break; /* NTSC */ #ifdef sgi case SEQ('T','V'): if (!hasnumber) number = -1; ntsc(number); break; #endif /* Timing -- ctrl-T * ^T : print accumulated timing status now * <nnn>^T : print timing status now and every <nnn> main-loop cycles * -^T : quit timing */ case 'T'&0x1f: timing( hasnumber<0 ? 0 : hasnumber ? number : 9999999 ); break; /* Edit Lights */ case SEQ('l','e'): if (!(uistate.lights_shown)) light_edit_mode(1); else gv_event_mode( LIGHTEDIT ); break; /* Toggle Show Lights */ case SEQ('l','s'): light_edit_mode(2); break; /* * All R* commands moved to rman.c - slevy. */ default: err = EOF; if(prefix == 'R') { rman_do(event.dev,hasnumber,number); } else if(prefix != 0) { /* No such command? */ prefix = 0; goto rescan; /* Try same char without prefix */ } keymode = KEYNONE; } hasnumber = expon = 0; prefix = 0; ui_keyboard(err); /* 0 = OK, EOF = -1 = error */ keepnumber: keymode = KEYNONE; keepmode: ; } return Lt; } /* * Interpret a g[N] or c[N] prefix; return the id. */ static int retarget(int defindex) { int t; static int allid[2] = { ALLGEOMS, ALLCAMS }; static char ch[2] = { 'g', 'c' }; char code[12]; if(keymode == KEYNONE) { if(expon && !hasnumber) { /* a "." prefix, sans number */ expon = 0; return TARGETID; } return defindex; /* No prefix, or just numeric */ } sprintf(code, "%c%d", ch[keymode], number); if(hasnumber > 0) t = drawer_idbyname(code); else if(hasnumber < 0) t = allid[keymode]; else t = (keymode == KEYGEOM) ? WORLDGEOM : FOCUSID; hasnumber = expon = 0; keymode = KEYNONE; return t; } static float getreal(float defval) { float v = hasnumber * number; if(!hasnumber) return defval; while(--expon > 0) v *= 0.1; return v; } static int toggle(int val) { return hasnumber ? (hasnumber = expon = 0, number) : !val; } static void tog_ap_flag( int id, int flagbit ) { ApStruct as; int val; if(hasnumber) { val = number; } else { as.ap = drawer_get_ap(id); val = as.ap ? !(as.ap->flag & flagbit) : 1; } as.ap = ApCreate(val ? AP_DO : AP_DONT, flagbit, AP_OVERRIDE, uistate.apoverride & flagbit, AP_END); gv_merge_ap(id, &as); ApDelete(as.ap); } /* NB - I've put in a total hack to avoid calling gvpick more than * once - I think it's pretty stable (so when it stays in for years * and years it might(?) keep working!) -cf 10/29/92 */ static int view_pick( DView *dv, int x, int y, Pick *pick ) { Transform V, T, Tnet, Tt, Tmodel, Tnorm, oldTw, Tworld; register int i; int chosen = NOID; float xpick, ypick; DGeom *dg; Appearance *ap; Point3 got,v,e[2],wgot,wv,we[2], owgot, owv, owe[2]; if(dv == NULL) return NOID; if(dv->stereo == NO_KEYWORD) { mousemap(x, y, &xpick, &ypick, &drawerstate.winpos); } else { /* Map screen -> view position in a stereo window. */ for(i = 0; i < 2; i++) { WnPosition wp; wp.xmin = drawerstate.winpos.xmin + dv->vp[i].xmin; wp.xmax = wp.xmin + dv->vp[i].xmax - dv->vp[i].xmin; wp.ymin = drawerstate.winpos.ymin + dv->vp[i].ymin; wp.ymax = wp.ymin + dv->vp[i].ymax - dv->vp[i].ymin; mousemap(x, y, &xpick, &ypick, &wp); if(fabs(xpick) <= 1 && fabs(ypick) <= 1) break; } } CamView( dv->cam, V ); /* V = camera-to-screen matrix */ if(dv->Item != drawerstate.universe) { /* Picking in a window with a dedicated Scene */ /* We yield results in the Scene's coordinate system */ GeomPosition( dv->Item, T ); TmConcat(T,V, T); /* T = Scene to screen projection */ if(GeomMousePick( dv->Item, pick, (Appearance *)NULL, T, xpick, ypick )) { chosen = dv->id; } return chosen; } /* Picking in the real world */ GeomPosition( drawerstate.world, Tworld ); TmConcat( Tworld,V, T ); /* * We now assume the complete screen -> DGeom transform is in T. * This is true only if we have just a single level of DGeom's in the world. */ LOOPSOMEGEOMS(i,dg,ORDINARY) { if (dg->pickable) { Geom *g = NULL; if (dg->Lgeom) { GeomPosition( dg->Item, Tmodel ); GeomPosition( dg->Inorm, Tnorm ); TmConcat( Tnorm,Tmodel, Tt ); TmConcat( Tt,T, Tnet ); /* Now Tnet = complete geom-to-screen proj'n */ GeomGet( dg->Lgeom, CR_GEOM, &g ); ap = drawer_get_ap(i); if(GeomMousePick( g, pick, ap, Tnet, xpick, ypick )) { chosen = GEOMID(i); /* We remember oldTw to print out info below for debugging only */ TmCopy(pick->Tw, oldTw); /* This is necessary! Arranges for things to be in world coords not Dgeom coords. Tt is the world-to-dgeom transform (or vice versa?) */ TmConcat(pick->Tw, Tt, pick->Tw); drawer_get_transform(WORLDGEOM, pick->Tself, GEOMID(i)); TmConcat(pick->Tw, pick->Tself, pick->Tself); } ApDelete(ap); } } } if (chosen == NOID) { /* printf("Picked nothing.\n"); */ } else { /* printf("Picked dgeom #%d\n", INDEXOF(chosen)); */ /* pick->got is in mouse coords. wgot is world coords. old world is really dgeom coords. (maybe...) got is raw object coords. (the kind of numbers in geom data file!) */ if (pick && getenv("VERBOSE_PICK")) { Pt3Transform(pick->Tmirp, &(pick->got), &got); if (pick->found&PW_VERT) Pt3Transform(pick->Tmirp, &(pick->v), &v); if (pick->found&PW_EDGE) { Pt3Transform(pick->Tmirp, &(pick->e[0]), &(e[0])); Pt3Transform(pick->Tmirp, &(pick->e[1]), &(e[1])); } Pt3Transform(pick->Tw, &(pick->got), &wgot); if (pick->found&PW_VERT) Pt3Transform(pick->Tw, &(pick->v), &wv); if (pick->found&PW_EDGE) { Pt3Transform(pick->Tw, &(pick->e[0]), &(we[0])); Pt3Transform(pick->Tw, &(pick->e[1]), &(we[1])); } Pt3Transform(oldTw, &(pick->got), &owgot); if (pick->found&PW_VERT) Pt3Transform(oldTw, &(pick->v), &owv); if (pick->found&PW_EDGE) { Pt3Transform(oldTw, &(pick->e[0]), &(owe[0])); Pt3Transform(oldTw, &(pick->e[1]), &(owe[1])); } printf("pick->\n"); printf(" got = (%f %f %f)\n", pick->got.x, pick->got.y, pick->got.z); if (pick->found&PW_VERT) printf(" v = (%f %f %f)\n", pick->v.x, pick->v.y, pick->v.z); if (pick->found&PW_EDGE) { printf(" e[0] = (%f %f %f)\n", pick->e[0].x, pick->e[0].y, pick->e[0].z); printf(" e[1] = (%f %f %f)\n", pick->e[1].x, pick->e[1].y, pick->e[1].z); } printf("Transformed pick [raw]->\n"); printf(" got = (%f %f %f)\n", got.x, got.y, got.z); if (pick->found&PW_VERT) printf(" v = (%f %f %f)\n", v.x, v.y, v.z); if (pick->found&PW_EDGE) { printf(" e[0] = (%f %f %f)\n", e[0].x, e[0].y, e[0].z); printf(" e[1] = (%f %f %f)\n", e[1].x, e[1].y, e[1].z); } printf("Transformed pick [old world]->\n"); printf(" got = (%f %f %f)\n", owgot.x, owgot.y, owgot.z); if (pick->found&PW_VERT) printf(" v = (%f %f %f)\n", owv.x, owv.y, owv.z); if (pick->found&PW_EDGE) { printf(" e[0] = (%f %f %f)\n", owe[0].x, owe[0].y, owe[0].z); printf(" e[1] = (%f %f %f)\n", owe[1].x, owe[1].y, owe[1].z); printf("Transformed pick [world]->\n"); printf(" got = (%f %f %f)\n", wgot.x, wgot.y, wgot.z); if (pick->found&PW_VERT) printf(" v = (%f %f %f)\n", wv.x, wv.y, wv.z); if (pick->found&PW_EDGE) { printf(" e[0] = (%f %f %f)\n", we[0].x, we[0].y, we[0].z); printf(" e[1] = (%f %f %f)\n", we[1].x, we[1].y, we[1].z); } } } } return chosen; } LDEFINE(rawpick, LINT, "(rawpick CAMID X Y)\n\ Process a pick event in camera CAMID at location (X,Y) given in\n\ integer pixel coordinates. This is a low-level procedure not\n\ intended for external use.") { int pickedid, id, x, y; Pick *pick; LDECLARE(("rawpick", LBEGIN, LID, &id, LINT, &x, LINT, &y, LEND)); if (TYPEOF(id) != T_CAM) { fprintf(stderr, "rawpick: first arg must be a camera id\n"); return Lnil; } pick = PickSet(NULL, PA_WANT, PW_EDGE|PW_VERT|PW_FACE, PA_END); pickedid= view_pick( (DView *)drawer_get_object(id), x, y, pick ); emit_pick(pickedid, pick); PickDelete(pick); return LNew(LINT, &pickedid); } void emit_pick(int pickedid, Pick *pick) { /* Variables for total hack */ vvec done; char donebits[512]; LInterest *interest = LInterestList("pick"); VVINIT(done, char, 128); if(interest) { vvuse(&done, donebits, COUNT(donebits)); vvzero(&done); } #define DONEID(id) *VVINDEX(done, char, id-CAMID(-20)) for ( ; interest != NULL; interest = interest->next) { Transform T; float got[4], v[4], e[8]; HPoint3 pgot, *f; int gn, vn, vi, en, ei[2], ein, coordsysid, fn, fi; /* extract the coord system to use from the interest filter; if none given, use world */ if ( interest->filter && interest->filter->car && (LFILTERVAL(interest->filter->car)->flag == VAL)) { if (!LFROMOBJ(LID)(LFILTERVAL(interest->filter->car)->value, &coordsysid)) { OOGLError(0,"rawpick: bad coord sys filter type"); continue; } } else { coordsysid = WORLDGEOM; } /* T = transform converting to the coord system of coordsysid */ /* This section does the setup for the total hack */ /* Total hack gigantic if statement */ if (!DONEID(coordsysid)) { DONEID(coordsysid) = 1; switch(coordsysid) { case WORLDGEOM: TmCopy(pick->Tw, T); break; case PRIMITIVE: TmCopy(pick->Tmirp, T); break; case SELF: TmCopy(pick->Tself, T); break; default: drawer_get_transform(WORLDGEOM, T, coordsysid); TmConcat(pick->Tw, T, T); break; } if (pickedid != NOID) { pgot.x = pick->got.x; pgot.y = pick->got.y; pgot.z = pick->got.z; pgot.w = 1; HPt3Transform(T, &pgot, got); gn = 4; } else gn = 0; if (pick->found&PW_VERT) { HPt3Transform(T, &(pick->v), v); vn = 4; vi = pick->vi; } else { vn = 0; vi = -1; } if (pick->found&PW_EDGE) { HPt3TransformN(T, pick->e, e, 2); en = 8; ei[0] = pick->ei[0]; ei[1] = pick->ei[1]; ein = 2; } else { en = 0; ein = 0; } if (pick->found & PW_FACE) { f = OOGLNewNE(HPoint3, pick->fn, "rawpick"); HPt3TransformN(T, pick->f, f, pick->fn); fi = pick->fi; fn = pick->fn * 4; } else { f = NULL; fn = 0; fi = -1; } /* Cause of total hack. * This CANNOT be called once for every interested party - otherwise * every interested party will hear about it numerous times. */ gv_pick(coordsysid, pickedid, got, gn, v, vn, e, en, (float *)f, fn, /* f, fn, */ VVEC(pick->gpath, int), VVCOUNT(pick->gpath), vi, ei, ein, fi ); if (f != NULL) OOGLFree(f); } /* End of total hack if statement */ } vvfree(&done); } LDEFINE(pick, LVOID, "(pick COORDSYS GEOMID G V E F P VI EI FI)\n\ The pick command is executed internally in response to pick\n\ events (right mouse double click).\n\ \n\ COORDSYS = coordinate system in which coordinates of the following\n\ arguments are specified. This can be:\n\ world: world coord sys\n\ self: coord sys of the picked geom (GEOMID)\n\ primitive: coord sys of the actual primitive within\n\ the picked geom where the pick occurred.\n\ GEOMID = id of picked geom\n\ G = picked point (actual intersection of pick ray with object)\n\ V = picked vertex, if any\n\ E = picked edge, if any\n\ F = picked face\n\ P = path to picked primitive [0 or more]\n\ VI = index of picked vertex in primitive\n\ EI = list of indices of endpoints of picked edge, if any\n\ FI = index of picked face\n\ \n\ External modules can find out about pick events by registering\n\ interest in calls to \"pick\" via the \"interest\" command.") { float got[4], v[4], e[8], f[40]; int vi, ei[8], fi, p[40]; int gn=4, vn=4, en=8, fn=40, pn = 40; int ein=8; int id, coordsys; /* NOTE: If you change the lisp syntax of this function (which you shouldn't do), you must also update the DEFPICKFUNC macro in the file "pickfunc.h", which external modules use. */ LDECLARE(("pick", LBEGIN, LID, &coordsys, LID, &id, LHOLD, LARRAY, LFLOAT, got, &gn, LHOLD, LARRAY, LFLOAT, v, &vn, LHOLD, LARRAY, LFLOAT, e, &en, LHOLD, LARRAY, LFLOAT, f, &fn, LHOLD, LARRAY, LINT, p, &pn, LINT, &vi, LHOLD, LARRAY, LINT, ei, &ein, LINT, &fi, LEND)); return Lt; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.