//to szg-1.1 11jun07 //reversion to Portal started 03may07 by GF added dawn //this revision 21feb06 and compiles and runs in mac szg-1.0 //but win version is seriously compromised by the gluttistry //Image is dated 4oct05 and ran in the DiVE. //29sep05 recompiled with 0.7 on trivial. Not currently CUBEworthy. //5jun04 gesture navigation completed //aff should, some day, become affmat to be less obscure //can't remove the problem with "swap" so I'm changing its name //ditto messages which seems to be somebody's property, is now postit /* 9jun begin fixing up CAVE version of avn */ /* problem with speed*sdt factor in thumb-button */ /* button is too hard to push */ /*tubes 8 tuberadius .02 default CAVEhkey and jkey active */ /* This is AFS: $MATH/sullivan/src */ /* -rw-r--r-- 1 slevy ac 97215 Feb 9 10:37 avn.c */ /* 16jan98 gkf version of slevy 9jan98 */ /****************************************************************/ /**** optiVerse a.k.a. avn, slevy, 31dec 1997 ****/ /**** equiVert a.k.a. nvn, August 1997 ****/ /**** Chris Hartman, merged noosh.c wth vn.c ****/ /**** co-developers: Alex Bourd, Glenn Chappell ****/ /**** John Sullivan, and George Francis ****/ /**** (C) 1997, Board of Trustees, U. Illinois. ****/ /****************************************************************/ //gkf: these really have to be at the tippity top to prevent much hassle #include "arPrecompiled.h" //must be the first line #include "arGraphicsHeader.h" #include "arMasterSlaveFramework.h" #include "dawn.c" string arDataPath; /* used by myfopen() */ #ifdef i386 #define LITTLEENDIAN 1 #endif #if ( defined(_WIN32) || defined(AR_USE_WIN_32) ) && !defined(WIN32) # define WIN32 1 #endif #ifdef WIN32 //#ifndef SOUND //#define SOUND 1 /* If Makefile.win didn't define it, we want SOUND anyway */ //#endif //this does not work on windows #define LITTLEENDIAN 0x0001 /* WIN32 -> little-endian (sometimes) */ #include #pragma warning (disable:4305) /* disable double-to-float warning */ #pragma warning (disable:4101) /* disable "unreferenced local variable" warning */ #pragma warning (disable:4056) /* disable bogus "overflow in floating-point constant arithmetic" warning */ #include #include #include #include #include #include #include #endif /*WIN32*/ #include #include #include #include #ifdef linux #include #endif #include #include #include // #include #ifdef MAC_OS_X #include #endif #include "GL/glut.h" //gkf 11jun07 #include #ifdef sun #define cosf(_) cos(_) #define sinf(_) sin(_) #define ntohl(x) (x) #endif /* sun */ #ifdef unix #include #include #endif /*unix*/ #ifdef MAC_OS_X #define cosf(_) cos(_) #define sinf(_) sin(_) //#include //#include #else // #include // #include #endif #ifdef WIN32 # define cosf(_) cos(_) # define sinf(_) sin(_) # define rint(_) ((int)(_)) # define popen(_,__) _popen(_,__) # define pclose(_) _pclose(_) # define finite(_) _finite(_) # define mkdir(_,__) _mkdir(_) #endif #undef GL_POLYGON_OFFSET_EXT #ifndef M_PI #define M_PI (3.14159265358979) #endif #define PRINT(x,y) fprintf(stderr,"x" , y); #if defined(sgi) || defined(__sgi) # define HAS_SPROC 1 # define HAS_SGINAP 1 # define ntohl(x) (x) #else # define sproc(func, opts) (func(0), 0) #endif #ifdef ZLIB /* built-in compression library, for fast gzipped snapshots */ # include #endif // #include #ifndef alloca #ifdef MAC_OS_X #include #else #include #endif #endif #if SOUND #include "vssClient.h" int Faud = -1; #endif /*SOUND*/ #ifdef CAVE #include #include #endif #define MAXTOPES 1200 /* that fit into the viewer */ #undef CTRL #define CTRL(c) (c & 0x1F) typedef struct { float x[3]; } Point; /*gkf: Why x? Point.x[2] looks like an x coordinate, not a z coordinate.*/ /* and why is a Matrix a simple type but Point a whole structure? */ typedef float Matrix[4*4]; #define NewA(type, count) (type *)alloca((count) * sizeof(type)) #define NewN(type, count) (type *)MALLOC((count) * sizeof(type)) typedef struct LOOP { struct LOOP *next; int closed; int nverts; Point *verts; Point (*xyv)[2]; /* Basis vectors for tube cross section */ } Loop; typedef struct TOPE { char *comment; int nverts; Point *verts; Point min, size; /* Bounding box; "size" guaranteed nonzero */ int nfaces; Point *norm; /* norm[nfaces] : facet normals */ Point *vnorm; /* vnorm[verts] : vertex normals */ int *nfv; /* nfv[nfaces] : number of verts on each face */ int *fv0; /* fv0[nfaces] : starting index in fv[] */ int totfv; /* sum of nfv over all faces */ int *fv; /* fv[totfv] : vertex indices per face */ unsigned char *fromdl; /* fromdl[nfaces] : graph distance from double-locus curve */ int *fadj; /* fadj[totfv] : faceno adjacent on */ /* facevert i..i+1 edge (-1 if none) */ int nedgeverts; /* number of verts on self-intersection locus */ Point *edges; /* edges[nedgeverts]; each from edges[2*i] to edges[2*i+1] */ Loop *loops; } Tope; /* Examples of useful geometric macros, use inliners in C++ */ #define MAX(x,y) (((x)<(y))?(y):(x)) #define MIN(x,y) ((x>y)?y:x) #define ABS(x) (((x)<0)?-(x):(x)) #define FOR(a,b,c) for(a=b;av ? v: x)) /* global variables ... add new ones on top of the stack */ int thick0 ; /* gkf 1jan98 line thickness, 1 for CAVE 3 for console, args only */ Loop circle; int step, movie, swapp,fore; int debug = 0; int quiet = 1; char *audfname = "trance.aud"; int nosproc = 0; float phase0, gap0=.9; /*very bad kludge ... for letting arguments reset default gap value */ int rotn0 = 0; #ifdef WIN32 int showstars0 = 0; #else int showstars0 = 1; #endif int audiohandle; /* for telling vss the name of the .aud file */ Point lux = {1.,2.,3.}; /*light source direction vector */ float mysiz, speed, torq, focal, farclip; /*console navigation variables */ /* not quite, speed also matters in the CAVE thumbbutton sensitivity */ float zoom2; int singlebuffer = 0; float nullwidth = 3; int recmode = 0; int boxinterp = 1; int stopreading = 0; int quadbinoc = 0; static GLint glvp[4]; /* GL viewport (for sizing text): x0,y0,width,height */ /* * Global Constants */ Matrix id = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; float tubecolors[8][4] = { {.2,.1,.5,.5},{1,.65,0.,1},{.2,.8,.3,1},{.7,.2,.5,1}, {1,.2,.2,1}, {.2,.8,.2,1},{.3,.2,1,1}, {.5,.2,.7,1} }; /* { {.4,.1,.25,.5},{1,.65,0.,1},{.2,.8,.3,1},{.7,.2,.5,1}, {1,.2,.2,1}, {.2,.8,.2,1},{.3,.2,1,1}, {.5,.2,.7,1} }; */ /* * Global variables */ /* The paw==5 resets most globals from the wand and all those affecting the graphics processes need to be shared. But speedo (potentially) has to report any of the these hence they should be shared to begin with. There remains the question whether the tymer can be successfully restarted by deFault without being shared */ #define TICKTYME 0 /* tymode values */ #define REALTYME 1 /* swapp and fore are used by the tymer */ char *filename = NULL; char astring[255]; int child=0; int win = 1; /* 640x480, use 0 for demand sized */ int jflag = 0; /* integerflag you set from command line */ int bwflag = 0; /* black&white: flag set from command line */ int ribbonflag = 0; /* integerflag you set from command line */ unsigned int BUT,XX,YY,SHIF; /* used in chaptrack gluttery */ int xt,yt; /* once was xt,yt,xm,ym for viewportery */ int caveyes=0; /* rename? ----------> caveflag */ int mode,morph,msg; /* pretty global */ int loop0 = 1; int binoc; /* flag: 0 for normal, 1 for binoc, 2 for different views */ float nose; /* to eye distance in notCAVE */ //gkf 4jun04 int gesture; float eps; float del; int resethand; int tracing; int tracecount; int tracelimit; FILE *tracefile; struct key { float ptime; float rtope; Point quat, squat, transl; float kboxr; Point kbox0; /* gkf: position of box in "world"=Object coordinates */ Point kCbox; /* gkf: position of box in Camera=Console=Cave coordinates */ }; int playing, playpipe; int playsynched; float playtimebase; FILE *playfile; char *playfname, *tracefname; struct key prevkey, curkey; float curplaytime; int arenasize = 2*1024*1024; /* default shared-mem arena size */ int arenasize0; int arenaused, arenaalloced; void *arena; #define TURNMODE (0) #define FLYMODE (1) //---------------------szg-topmatter-------------------------- //gkf in the time honored method of putting new defines and new functions //at the interface of a .c program, these are here. But cubeinit doesn't //work here. So we'll move cubinit closer to sharevars float sizz=5.; //from siz, and caveyes in CAVEskels enum Eyes {console, cave, cube} eyes; //---------------------end-topmatter-------------------------- void vsub( Point *dst, Point *a, Point *b ); void vadd( Point *dst, Point *a, Point *b ); void vcross( Point *dst, Point *a, Point *b ); float vdot( Point *a, Point *b ); void vscale( Point *dst, float s, Point *src ); void vsadd( Point *dst, Point *a, float sb, Point *b ); void vlerp( Point *dst, float frac, Point *vfrom, Point *vto ); void vcomb( Point *dst, float sa, Point *a, float sb, Point *b ); float vunit( Point *dst, Point *src ); void vproj( Point *along, Point *perp, Point *vec, Point *onto ); float vdist( Point *p1, Point *p2 ); float qdist( Point *q1, Point *q2 ); float tdist( Matrix t1, Matrix t2 ); float vlength( Point *v ); void vuntfmvector( Point *dst, Point *src, Matrix T ); void vtfmvector( Point *dst, Point *src, Matrix T ); void vtfmpoint( Point *dst, Point *src, Matrix T ); void vgettranslation( Point *dst, Matrix T ); void vsettranslation( Matrix T, Point *src ); void vrotxy( Point *dst, Point *src, float cs[2] ); void eucinv( Matrix dst, const Matrix src ); void grotation( Matrix Trot, Point *fromaxis, Point *toaxis ); void mcopy( Matrix dst, Matrix src ); void mmmul( Matrix dst, Matrix a, Matrix b ); float tfm2quat( Point *iquat, Matrix src ); void quat2tfm( Matrix dst, Point *iquat ); void quat_lerp( Point *dquat, float frac, Point *qfrom, Point *qto ); extern void dotrans(void *ignored_sproc_arg); /* file reader */ int readanoff(FILE *inf, Tope *tp, int ascii); void reverts(Tope *tp); void findadj(Tope *tp); void readdl(FILE *inf, Tope *tp); void tubebasis( Loop *l ); void loopdl( Tope *tp ); void findfromdl(Tope *tp); void searchv(Tope *tp, int f0, int f1, int f, int v, int thresh, int next[], int *tail); int paint(int front, float lmb, float dog); void audioclean(); void initstars(); void initcircle(); float speedometer(); void idle(void); float timenow(); void char2wall(GLfloat x,GLfloat y,GLfloat z, char buf[]); void keyboard(unsigned char ch, int x, int y); void drawcons(void); float getnumber(float dflt); void bump(float *valp, float incr); void clock_tick(); int topeof(float rtope, int *lphase); float settope(float rtope); void traceon( char *fname ); void traceoff(); void tracecut(); void playon( char *fname ); void playoff(); float playtime( float tyme ); void do_tri(Point *a,Point *b,Point *c, float ggap, int rgba); void do_tube( Point *p0, Point *p1, int nprofile, float (*profile)[2], Point *light ); void do_tubeloop( Loop *l, int nprofile, float (*profile)[2], Point *light, float radius, float scale, float color[4]); void cavetrack(void); void trace(int ch); void tracewhere(); void traceall(); /* * Shared memory variables */ typedef struct SHARED { float s_siz; Matrix s_aff, s_starmat; Point s_luxx; float s_alfa; float s_gap; int s_wnd; int s_paw; int s_maus[2]; int s_modus,s_phase,s_readytogo,s_locus; int s_tope; int s_newdata; float s_realtope; float s_movierate; float s_tickrate; /* simulated-framerate (frames/sec) if TICKTYME */ float s_tymenow; /* current simulated time */ float s_deltat; /* timestep from previous frame */ int s_tymode; /* Timing mode: 1=real, 0=tymedt per frame */ int s_park; /* park (freeze all activity) */ int s_oneside; /* 0=normal, 1=show only one side (other invisible), 2=show one side with current gap (other gap=1) 3=show both sides as front (for nonorientable surface) */ int s_snap; /* Taking "snap" more image-snapshots */ int s_snapno; /* Snapshot sequence number */ int s_skipsnap; /* Need to skip one snapshot (at beginning of playback) */ int s_thick; /* double-locus pixel thickness */ int s_showstars; /* Show starry background? */ float s_closer; /* push lines closer to viewer (glPolygonOffsetEXT scaling) */ int s_boxclip; /* clip-to-box flag = clipping is enabled*/ int s_boxtrack; /*tie-box-to-camera flag = box0 changes with flying */ int s_boxshow; /*show the box */ float s_boxrange; /* false: place box-origin this far in front of camera, if boxtrack */ /* gkf: except that it is not a fixed distance in front of the * camera, even by default. If you fly the camera to a new * location and then turn the box on, it is located in the old * place relative to the object ... */ Point s_box0; /* box center in object coordinates */ Point s_Cbox; /* box center in camera=console coordinates */ float s_boxr; /* box radius */ int s_dotube; /* Generate tube around double-locus? How many facets? */ int s_docans; /* Use tin-can style? Else connected tube. */ float s_tuberadius; /* tube radius */ float s_circradius; /* circle tube radius */ float s_circscale; /* circle tube scale */ float s_canlength; /* hack: extend each tube seg by this fraction */ float s_tubespecular; float s_tubediffuse; float s_dlradius; int s_tilesnaps; int s_rotn; int s_ntopes; Tope s_topes[MAXTOPES]; } shared_dt; shared_dt *shr; #define luxx (shr->s_luxx) #define locus (shr->s_locus) #define siz (shr->s_siz) #define aff (shr->s_aff) #define starmat (shr->s_starmat) #define gap (shr->s_gap) #define wnd (shr->s_wnd) #define mauspaw (shr->s_paw) #define mausx (shr->s_maus[0]) #define mausy (shr->s_maus[1]) #define alfa (shr->s_alfa) #define modus (shr->s_modus) #define phase (shr->s_phase) #define readytogo (shr->s_readytogo) #define colors (shr->s_colors) #define tope (shr->s_tope) #define realtope (shr->s_realtope) #define movierate (shr->s_movierate) #define tickrate (shr->s_tickrate) #define park (shr->s_park) #define tymenow (shr->s_tymenow) #define deltat (shr->s_deltat) #define newdata (shr->s_newdata) #define rotn (shr->s_rotn) #define oneside (shr->s_oneside) #define snap (shr->s_snap) #define snapno (shr->s_snapno) #define skipsnap (shr->s_skipsnap) #define tymode (shr->s_tymode) #define showstars (shr->s_showstars) #define thick (shr->s_thick) #define boxclip (shr->s_boxclip) #define boxtrack (shr->s_boxtrack) #define boxshow (shr->s_boxshow) #define boxrange (shr->s_boxrange) #define box0 (shr->s_box0) #define Cbox (shr->s_Cbox) #define boxr (shr->s_boxr) #define closer (shr->s_closer) #define dotube (shr->s_dotube) #define docans (shr->s_docans) #define tuberadius (shr->s_tuberadius) #define circradius (shr->s_circradius) #define circscale (shr->s_circscale) #define canlength (shr->s_canlength) #define tubespecular (shr->s_tubespecular) #define tubediffuse (shr->s_tubediffuse) #define dlradius (shr->s_dlradius) #define ntopes (shr->s_ntopes) #define topes (shr->s_topes) #define tilesnaps (shr->s_tilesnaps) //-----------------rest of topmatter------------------------------ bool cubeinit(arMasterSlaveFramework& fw, arSZGClient& ){ ar_setNavMatrix(ar_translationMatrix(0,-5,0)); //initial position // Great-Leap-Forward forbids premature OpenGL calls // glEnable(GL_DEPTH_TEST); // glDisable(GL_CULL_FACE); /*from Ben, what if we don't ? */ //this is how Syzygy does shared memory. fw.addTransferField("aff", aff, AR_FLOAT, 16); fw.addTransferField("starmat", starmat, AR_FLOAT, 16); fw.addTransferField("sizz", (&sizz), AR_FLOAT, 1); fw.addTransferField("del", (&del), AR_FLOAT, 1); fw.addTransferField("eps", (&eps), AR_FLOAT, 1); fw.addTransferField("resethand", (&resethand), AR_INT, 1); fw.addTransferField("movie", (&movie), AR_INT, 1); fw.addTransferField("luxx", &(luxx.x), AR_FLOAT, 3); fw.addTransferField("gap", (&gap), AR_FLOAT, 1); fw.addTransferField("alfa", (&alfa), AR_FLOAT, 1); fw.addTransferField("modus", (&modus), AR_INT, 1); fw.addTransferField("phase", (&phase), AR_INT, 1); fw.addTransferField("readytogo", (&readytogo), AR_INT, 1); fw.addTransferField("locus", (&locus), AR_INT, 1); fw.addTransferField("tope", (&tope), AR_INT, 1); fw.addTransferField("newdata", (&newdata), AR_INT, 1); fw.addTransferField("realtope", (&realtope), AR_FLOAT, 1); fw.addTransferField("movierate", (&movierate), AR_FLOAT, 1); fw.addTransferField("tickrate", (&tickrate), AR_FLOAT, 1); fw.addTransferField("tymenow", (&tymenow), AR_FLOAT, 1); fw.addTransferField("deltat", (&deltat), AR_FLOAT, 1); fw.addTransferField("tymode", (&tymode), AR_INT, 1); fw.addTransferField("park", (&park), AR_INT, 1); fw.addTransferField("oneside", (&oneside), AR_INT, 1); fw.addTransferField("thick", (&thick), AR_INT, 1); fw.addTransferField("showstars", (&showstars), AR_INT, 1); fw.addTransferField("dotube", (&dotube), AR_INT, 1); fw.addTransferField("docans", (&docans), AR_INT, 1); fw.addTransferField("tuberadius", (&tuberadius), AR_FLOAT, 1); fw.addTransferField("canlength", (&canlength), AR_FLOAT, 1); fw.addTransferField("tubespecular", (&tubespecular), AR_FLOAT, 1); fw.addTransferField("tubediffuse", (&tubediffuse), AR_FLOAT, 1); fw.addTransferField("dlradius", (&dlradius), AR_FLOAT, 1); fw.addTransferField("rotn", (&rotn), AR_INT, 1); fw.addTransferField("ntopes", (&ntopes), AR_INT, 1); // fw.addTransferField("th1", (&th1), AR_INT, 1); // fw.addTransferField("ta0", (&ta0), AR_INT, 1); // fw.addTransferField("ta1", (&ta1), AR_INT, 1); // fw.addTransferField("morph", (&morph), AR_INT, 1); arDataPath = fw.getDataPath(); /* Read data file now... */ dotrans( NULL ); return true; } //---------------------end-topmatter-------------------------- #ifdef LITTLEENDIAN static u_long antohl(u_long v) { /* for Intel boxes, anyway. */ return (v&0xFF)<<24 | (v&0xFF00)<<8 | (v>>8)&0xFF00 | (v>>24)&0xFF; } #else #define antohl(x) (x) #endif #ifdef WIN32 /* * Emulate some UNIX functions we need. */ void gettimeofday(struct timeval *tv, void *zone) { struct _timeb tb; _ftime(&tb); tv->tv_sec = tb.time; tv->tv_usec = tb.millitm * 1000; } /* MS seems to define ntohl() in some funny way. * We have our own here. We're protected from the MS definition by * the ``#define ntohl msjunk_ntohl'' near the top of this program. */ #undef ntohl void usleep( int usecs ) { Sleep( usecs / 1000 ); } #endif /* In Syzygy environment, use its ar_fileOpen() to find files on SZG_PATH. * Otherwise just use fopen. */ static char avn_modulename[] = "avn"; FILE *myfopen( char *fname, char *mode ) { #ifdef AR_MASTER_SLAVE_FRAMEWORK if(fname[0] == '/' || fname[1] == ':') { /* Try to detect absolute paths */ return fopen(fname, mode); } /* path? */ return ar_fileOpen( fname, avn_modulename, arDataPath, mode ); #else return fopen(fname, mode); #endif } void autotymer(int); /* to allow deFault to call the autotymer later */ void deFault(void) { int ii; float tmp; static Point cavezero = { 0, 5, -10 }; /*5ft beyond front wall*/ static Point cubezero = { 0, 5, -10 }; /*5ft beyond front wall*/ static Point conszero = { 0, 0, -6 }; /* inside the console */ // eyes=cube; do this from the command line with a -c flag DOES NOT WORK eyes=cube; phase=phase0; gap = gap0; alfa = 1.; movierate = 30; /* 10 topes per second default */ tickrate = 30; /* 10 ticks per second default (in sim-time mode) too */ nullwidth = 3; tymode = caveyes ? TICKTYME : REALTYME; realtope = tope = 0; phase &= 2; fore = 1; //gkf: here's are problem with swapp step=0; movie = 0; swapp = 0; locus=0; /* start by drawing whole surface */ msg=1; mauspaw = 0; wnd = 1; siz = 4; mode=FLYMODE; speed=.8; torq=.015; focal = 2; farclip =20; mysiz=.01; morph=0; /* maybe speed .08 isn't so good for the CAVE */ oneside = 0; binoc=0; nose=.06; thick = thick0; showstars = showstars0; closer = 1; dlradius = 1; snap = 0; snapno = -1; /*skipsnap = 0;*/ boxclip = 0; /* flag for toggling clipping */ boxtrack = 0; /* flag for toggling "dipmode" */ boxshow = 0; /* flag for toggling visible box */ box0.x[0] = box0.x[1] = box0.x[2] = 0; /* object-coordinates of box */ boxrange = 6.0; /* console-coordinate units in front of origin */ boxr = 1.5; /* size of box in console-coordinates */ Cbox.x[0] = Cbox.x[1] = 0; Cbox.x[2]= -boxrange ; /* console-coordinates of box */ dotube = 20; docans = 0; tuberadius = .2; circradius = 0; circscale = 2; canlength = .20; tubespecular = 20.; tubediffuse = 1; mcopy(starmat, id); mcopy(aff, id); vsettranslation( aff, (eyes==cave) ? &cavezero : (eyes==cube) ? &cubezero : &conszero ); tilesnaps = 0; gesture=0; //initially, no gesture navigation eps=0.002; //hand rotation gesture attenuation del=0.02; //hand translation gesture attenuation resethand=0; //declares current wand place as displacement origin autotymer(1); /* reset autotymer to start at the beginning */ vunit( &lux, &lux ); } void calculite() { vuntfmvector( &luxx, &lux, aff ); } int paint(int front, float lmb, float dog) { #define AMB (.15) /* was .1 */ #define PWR (10.) /* was 16. */ int rr,gg,bb, spec; lmb = (lmb<0?-lmb:lmb); lmb = 255*MAX(lmb,AMB); rr = lmb * (bwflag? (front? .7:.2) : (front? .78 : .02+.96*(1-dog))); gg = lmb * (bwflag? (front? .7:.2) : (.02 + .96*dog)); bb = lmb * (bwflag? (front? .7:.2) : (front ? .1 : .78)); spec = 255 - PWR*255 + PWR*lmb; /* Ray Idaszak, 1988 */ if(spec > 225) spec = 225; /* byte-order dependent */ #ifdef LITTLEENDIAN return (MAX(rr,spec)) | (MAX(gg,spec)<<8) | (MAX(bb,spec)<<16) | (int)(alfa*255)<<24; #else /*BIGENDIAN*/ return (MAX(rr,spec)<<24) | (MAX(gg,spec)<<16) | (MAX(bb,spec)<<8) | (int)(alfa*255); #endif } void vrotxy( register Point *dst, register Point *src, float cs[2] ) { dst->x[0] = src->x[0]*cs[0] - src->x[1]*cs[1]; dst->x[1] = src->x[0]*cs[1] + src->x[1]*cs[0]; dst->x[2] = src->x[2]; } #define VROTXY(dst, src, cs) \ (dst)->x[0] = (src)->x[0]*cs[0] - (src)->x[1]*cs[1], \ (dst)->x[1] = (src)->x[0]*cs[1] + (src)->x[1]*cs[0], \ (dst)->x[2] = (src)->x[2] #define VMID(dst, a, b) \ (dst)->x[0] = .5*((a)->x[0] + (b)->x[0]), \ (dst)->x[1] = .5*((a)->x[1] + (b)->x[1]), \ (dst)->x[2] = .5*((a)->x[2] + (b)->x[2]) void vadd( register Point *dst, register Point *a, register Point *b ) { dst->x[0] = a->x[0] + b->x[0]; dst->x[1] = a->x[1] + b->x[1]; dst->x[2] = a->x[2] + b->x[2]; } void vsub( register Point *dst, register Point *a, register Point *b ) { dst->x[0] = a->x[0] - b->x[0]; dst->x[1] = a->x[1] - b->x[1]; dst->x[2] = a->x[2] - b->x[2]; } void vcross( register Point *dst, register Point *a, register Point *b ) { dst->x[0] = a->x[1]*b->x[2] - a->x[2]*b->x[1]; dst->x[1] = a->x[2]*b->x[0] - a->x[0]*b->x[2]; dst->x[2] = a->x[0]*b->x[1] - a->x[1]*b->x[0]; } float vdot( Point *a, Point *b ) { return a->x[0]*b->x[0] + a->x[1]*b->x[1] + a->x[2]*b->x[2]; } void vscale( register Point *dst, float s, register Point *src ) { dst->x[0] = s*src->x[0]; dst->x[1] = s*src->x[1]; dst->x[2] = s*src->x[2]; } void vsadd( Point *dst, Point *a, float sb, Point *b ) { dst->x[0] = a->x[0] + sb * b->x[0]; dst->x[1] = a->x[1] + sb * b->x[1]; dst->x[2] = a->x[2] + sb * b->x[2]; } float vdist( Point *p1, Point *p2 ) { Point d; vsub(&d, p1, p2); return vlength(&d); } float vlength( Point *v ) { return sqrt(DOT(*v, *v)); } float vunit( register Point *dst, register Point *src ) { float s = sqrt(DOT(*src, *src)); float scl = s>0 ? 1/s : 0; vscale( dst, scl, src ); return s; } /* along = onto * (vec . onto / onto . onto) * perp = vec - along */ void vproj( Point *along, Point *perp, Point *vec, Point *onto ) { float mag2 = DOT(*onto, *onto); float dot = DOT(*vec, *onto); float s = (mag2 > 0) ? dot / mag2 : 0; Point talong; if(along == NULL) along = &talong; vscale( along, s, onto ); if(perp != NULL) vsadd( perp, vec, -1, along ); } /* gkf: these matrix multiplications are short one dimension * for exampel vtfm * slevy: yes, that's intentional. * vtfmvector() transforms a vector (in homog coords, [x,y,z,0]) by a matrix. * vtfmpoint() transforms a point [x,y,z,1]. * The difference is that vtfmpoint() includes the matrix's translation part * and vtfmvector() doesn't. */ void vuntfmvector( Point *dst, register Point *src, register Matrix T ) { int i; for(i = 0; i < 3; i++) dst->x[i] = src->x[0]*T[4*i] + src->x[1]*T[4*i+1] + src->x[2]*T[4*i+2]; } void vtfmvector( Point *dst, register Point *src, register Matrix T ) { int i; for(i = 0; i < 3; i++) dst->x[i] = src->x[0]*T[i] + src->x[1]*T[i+4] + src->x[2]*T[i+8]; } void vtfmpoint( Point *dst, register Point *src, register Matrix T ) { int i; for(i = 0; i < 3; i++) dst->x[i] = src->x[0]*T[i] + src->x[1]*T[i+4] + src->x[2]*T[i+8] + T[i+12]; } void vgettranslation( Point *dst, Matrix T ) { memcpy(dst->x, &T[4*3+0], 3*sizeof(float)); } void vsettranslation( Matrix T, Point *src ) { memcpy(&T[4*3+0], src->x, 3*sizeof(float)); } /* Invert a matrix, assuming it's a Euclidean isometry * plus possibly uniform scaling. */ void eucinv( Matrix dst, const Matrix src ) { int i, j; float s = DOT(*(Point *)src, *(Point *)src); Point trans; for(i = 0; i < 3; i++) { for(j = 0; j < 3; j++) dst[i*4+j] = src[j*4+i] / s; dst[i*4+3] = 0; } vtfmvector( &trans, (Point *)&src[4*3+0], dst ); vscale( (Point *)&dst[3*4+0], -1, &trans ); dst[3*4+3] = 1; } void mcopy( Matrix dst, Matrix src ) { memcpy( dst, src, sizeof(Matrix) ); } void mmmul( Matrix dst, Matrix a, Matrix b ) { int i, irow, j; for(i = 0; i < 4; i++) { irow = i*4; for(j = 0; j < 4; j++) dst[irow+j] = a[irow]*b[j] + a[irow+1]*b[1*4+j] + a[irow+2]*b[2*4+j] + a[irow+3]*b[3*4+j]; } } /* Construct matrix for geodesic rotation from "a" to "b". */ void grotation( Matrix Trot, Point *va, Point *vb ) { Point a, b, aperp; float ab, ab_1, apb, aa, bb; int i, j; mcopy( Trot, id ); if(vunit(&a, va) == 0 || vunit(&b, vb) == 0) return; ab_1 = DOT(a,b) - 1; vproj( NULL, &aperp, &b, &a ); if(vunit(&aperp, &aperp) == 0) { float dot; if(ab_1 >= -1) return; /* Vectors are identical: no rotation */ /* Otherwise, vectors are oppositely directed. * Rotate in an arbitrary plane which includes them. */ aperp.x[ fabs(a.x[0]) < .7 ? 0 : 1 ] = 1; vproj( NULL, &aperp, &aperp, &a ); vunit(&aperp, &aperp); } apb = DOT(aperp, b); for(i = 0; i < 3; i++) { float acoef = a.x[i]*ab_1 - aperp.x[i]*apb; float apcoef = aperp.x[i]*ab_1 + a.x[i]*apb; for(j = 0; j < 3; j++) Trot[i*4+j] += a.x[j]*acoef + aperp.x[j]*apcoef; } } /* * Convert the rotation part of a Euclidean isometry+uniform-scaling matrix * into a unit quaternion, with non-negative real part. * Returns the real part of the quaternion, with the three imaginary components * left in iquat->x[0,1,2]. */ float tfm2quat( Point *iquat, Matrix T ) { float mag, sinhalf, trace; float scl = vlength((Point *)T); /* gauge scaling from 1st row */ Point axis; #define Tij(i,j) T[(i)*4+(j)] trace = scl==0 ? 3 : (T[0*4+0] + T[1*4+1] + T[2*4+2])/scl; /*1 + 2*cos(ang)*/ if(trace<-1) trace = -1; else if(trace > 3) trace = 3; sinhalf = sqrt(3 - trace) / 2; /* sin(angle/2) */ axis.x[0] = Tij(1,2) - Tij(2,1); axis.x[1] = Tij(2,0) - Tij(0,2); axis.x[2] = Tij(0,1) - Tij(1,0); if(trace < -.25) { /* Angle near pi; sin(angle) is small, so use cos-related elements */ float c = (trace-1)/2; /* cos(angle) */ float v = 1-c; /* versine(angle) */ if(Tij(0,0) > c+.5) { /* large x component */ axis.x[0] = sqrt((Tij(0,0)-c)/v) * (axis.x[0]<0 ? -1 : 1); axis.x[1] = (Tij(0,1)+Tij(1,0)) / (2*v*axis.x[0]); axis.x[2] = (Tij(0,2)+Tij(2,0)) / (2*v*axis.x[0]); } else if(Tij(1,1) > c+.5) { /* large Y component */ axis.x[1] = sqrt((Tij(1,1)-c)/v) * (axis.x[1]<0 ? -1 : 1); axis.x[0] = (Tij(0,1)+Tij(1,0)) / (2*v*axis.x[1]); axis.x[2] = (Tij(2,1)+Tij(1,2)) / (2*v*axis.x[1]); } else if(Tij(2,2) > c+.5) { /* large Z component */ axis.x[2] = sqrt((Tij(2,2)-c)/v) * (axis.x[2]<0 ? -1 : 1); axis.x[0] = (Tij(0,2)+Tij(2,0)) / (2*v*axis.x[2]); axis.x[1] = (Tij(2,1)+Tij(1,2)) / (2*v*axis.x[2]); } else { int i; fprintf(stderr, "Hey, tfm2quat() got a non-rotation matrix!\n"); fprintf(stderr, "Check this out:\n"); for(i=0;i<4;i++) fprintf(stderr, "%12.8g %12.8g %12.8g %12.8g\n", aff[4*i],aff[4*i+1],aff[4*i+2],aff[4*i+3]); } } mag = vlength(&axis); if(!finite(mag)) { fprintf(stderr, "Yikes, tfm2quat() yields NaN?\n"); } /* The imaginary part is a vector pointing along the axis of rotation, * of magnitude sin(angle/2). So normalize & scale the axis, * but don't fail if its magnitude was zero (i.e. no rotation). */ vscale( iquat, mag==0 ? 0 : sinhalf/mag, &axis ); return sqrt(1 + trace) / 2; } void quat2tfm( Matrix dst, Point *iquat ) { Point axis; float s, c, v, coshalf; int i, j; float sinhalf = vlength(iquat); mcopy( dst, id ); if(sinhalf == 0) return; if(sinhalf > 1) { fprintf(stderr, "quat2tfm: Yikes, clamping quat to length 1 (was 1+%g)\n", sinhalf-1); sinhalf = 1; } vscale(&axis, 1/sinhalf, iquat); coshalf = sqrt(1 - sinhalf*sinhalf); s = 2*sinhalf*coshalf; /* sin(angle) */ v = 2*sinhalf*sinhalf; /* versine: 1 - cos(angle) */ c = 1 - v; /* cos(angle) */ for(i = 0; i < 3; i++) { for(j = 0; j < i; j++) dst[4*i+j] = dst[4*j+i] = axis.x[i]*axis.x[j]*v; dst[4*i+i] = axis.x[i]*axis.x[i]*v + c; } dst[4*0+1] += axis.x[2]*s; dst[4*1+0] -= axis.x[2]*s; dst[4*2+0] += axis.x[1]*s; dst[4*0+2] -= axis.x[1]*s; dst[4*1+2] += axis.x[0]*s; dst[4*2+1] -= axis.x[0]*s; } float qdist( Point *q1, Point *q2 ) { Point nq2; float s, sneg; vscale(&nq2, -1, q2); s = vdist(q1,q2); sneg = vdist(q1, &nq2); return (s < sneg) ? s : sneg; } float tdist( Matrix t1, Matrix t2 ) { float s = 0; int i; for(i=0; i<12; i++) s += (t1[i]-t2[i])*(t1[i]-t2[i]); return sqrt(s); } void quat_lerp( Point *qdst, float frac, Point *qfrom, Point *qto ) { Point dst; Point tto = *qto; float s, rdst; float ifrom2 = DOT(*qfrom,*qfrom); float rfrom = ifrom2>1 ? 0 : sqrt(1 - ifrom2); /* Real part */ float ito2 = DOT(tto,tto); float rto = ito2>1 ? 0 : sqrt(1 - ito2); float dot = DOT(*qfrom,tto); if(dot < 0) { /* quaternions are in opposite hemispheres: negate "tto" */ rto = -rto; vscale( &tto, -1, &tto ); } /* Use linear interpolation between the quaternions. This isn't right, * but shouldn't be far off if they don't differ by much. */ rdst = (1-frac)*rfrom + frac*rto; vlerp( &dst, frac, qfrom, &tto ); s = 1/sqrt(rdst*rdst + DOT(dst,dst)); if(!finite(s)) { fprintf(stderr, "Yeow!\n"); } if(rdst < 0) s = -s; vscale( qdst, s, &dst ); } void vlerp( Point *dst, float frac, Point *vfrom, Point *vto ) { dst->x[0] = (1-frac)*vfrom->x[0] + frac*vto->x[0]; dst->x[1] = (1-frac)*vfrom->x[1] + frac*vto->x[1]; dst->x[2] = (1-frac)*vfrom->x[2] + frac*vto->x[2]; } /* Linear combination: dst = sa*a + sb*b */ void vcomb( Point *dst, float sa, Point *a, float sb, Point *b ) { dst->x[0] = sa*a->x[0] + sb*b->x[0]; dst->x[1] = sa*a->x[1] + sb*b->x[1]; dst->x[2] = sa*a->x[2] + sb*b->x[2]; } void draw_object(float rltope,int lphase) { int ii,jj,kk; int ltope = topeof(rltope, &lphase); Tope *tp = &topes[ltope]; Point *a, *b, *c, *n; Point ra[1], rb[1], rc[1], rn[1]; float lmb, tmp, dog; Point llu; int drawMe = 1; float (*rotcs)[2]; Point *eye, *light; int ndrawn = 0; Matrix w2c, c2w; static Point origin = {0,0,0}; int trotn = rotn; /* "rotn" may change asynchronously while reading file; * ensure we have a consistent copy. */ float symrotn = 0; /* Z-axis rotation (for negative topes) applied * to take advantage of symmetry. */ static Point *vrot; static GLfloat green[] = {0,1,0}, red[] = {1,0,0}; static int vrottope = -1, vrotn, vrotroom; float thickness = zoom2 ? 2*thick : thick; if(trotn < 1) trotn = 1; /* phase rotates object/light 90 degrees and swaps front with back */ if(fabs(rltope) > ntopes) return; #ifdef GL_POLYGON_OFFSET_EXT if(closer != 0) { glEnable(GL_POLYGON_OFFSET_EXT); glPolygonOffsetEXT( 1e-5*thickness*closer, 1e-6*thickness*closer ); } else { glDisable(GL_POLYGON_OFFSET_EXT); } #endif /* gkf the clipbox should not be rotated by the next function */ llu = luxx; //what's this for? it's Stuart code if(lphase&1) { if (!(trotn%2)) { /*trotn is even*/ float cs[2]; cs[0] = cos(M_PI/trotn), cs[1] = -sin(M_PI/trotn); vrotxy( &llu, &luxx, cs ); symrotn = 180./trotn; glRotatef(symrotn, 0,0,1); } } if(!caveyes) if(binoc<=-2){ glRotatef(180., 0,1,0); llu.x[0] *= -1; llu.x[2] *= -1; } /* If binoc mode is 2, one view rotated 180deg; sgn(binoc) indicates which view we are drawing. */ /* Get the Object-to-Camera transformation, so we can gkf: is this the same as w2c = World-to-Camera ? gkf: my World=Camera, my object=World ... so we got that straight * construct the eye-point in object coords. That's just the * translation part of the Camera-to-Object matrix, but we pretend we don't * know that and just transform the origin. gkf: no, it doesn't stay that forever */ eye = NewA(Point, trotn); /* allocates a local variable */ light = NewA(Point, trotn); /* so that there are trotn eyes and lights */ /* gkf: At this moment there is [frustum]:[id][nose][aff][trotn] on the stack * I wonder whether this accounts for the problem with binocular viewing * because [w2c]= [nose][aff], not just [aff] alone * at any event, this produces the glitch at the halfway model, flipping * the box by trot * so w2c should be aff, not the current Modelling matrix, * it remains to be seen what it should be in the CAVE * but I'll leave it for now since it does little damage execept for homotopies */ glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)w2c); /*gkf: I would prefer w2c <- aff here */ eucinv(c2w, w2c); /*overkill, there's no scaling to worry about */ vtfmpoint( &eye[0], &origin, c2w ); /* the eyes are at the same place but rotated by the symmetry */ vtfmvector( &light[0], &lux, c2w ); vunit( &light[0], &light[0] ); if(trotn > 1) { rotcs = (float (*)[2])alloca(2*trotn * sizeof(float)); for(ii = 0; ii < trotn; ii++) { rotcs[ii][0] = cos(2*M_PI*ii / trotn); rotcs[ii][1] = sin(2*M_PI*ii / trotn); } for(ii = 1; ii < trotn; ii++) { vrotxy( &eye[ii], &eye[0], rotcs[trotn-ii] ); vrotxy( &light[ii], &light[0], rotcs[trotn-ii] ); } /* Transform all the vertices, if we haven't already got em cached */ if(vrottope != ltope || vrotn != trotn) { Point *vp, *rp; if(vrotroom < tp->nverts*(trotn-1)) { vrotroom = tp->nverts*trotn; /* Leave some extra space */ if(vrot) free(vrot); vrot = (Point *)malloc(vrotroom*sizeof(Point)); /* non-shared */ } for(rp = vrot, ii = 1; ii < trotn; ii++) { float *cs = rotcs[ii]; for(vp = tp->verts, jj = 0; jj < tp->nverts; jj++, vp++, rp++) { VROTXY(rp, vp, cs); } } vrottope = ltope; vrotn = trotn; } } if(boxtrack) { /* Place clipping box origin 'boxrange' units in front of eye */ /* That is, it's at the point (0,0,-boxrange) in camera coords */ /* gkf: my world-coordinates are not Stuart's world-coordinates*/ /* gkf: camera-coordinates == console/CAVE coordinates */ /* gkf: world-coordinates == object-coordinates */ /* gkf: OK we've got the rotation about the boxorigin and the object * origin fixed now, but this routine does NOT initialize the box * at it's last place, but always puts it to the default position \ * so doit the first time and after that * leave the box where we left it */ vtfmpoint( &box0, &Cbox, c2w ); /* =[c2w~aff] */ trace( 'B' ); } /* gkf: 1mar ... where/when should Cbox be updated? */ if(jflag==1){ /* Monitor the three points */ fprintf(stderr,"aff %0.2f %0.2f %0.2f Cbox %0.3f %0.3f %0.3f box0 %0.3f %0.3f %0.3f boxr %0.3f \n ",\ aff[12],aff[13],aff[14], Cbox.x[0], Cbox.x[1], Cbox.x[2], box0.x[0], box0.x[1], box0.x[2], boxr ); } if(alfa < 1) { glEnable(GL_BLEND); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glDisable(GL_DEPTH_TEST); } else { glDisable(GL_BLEND); } glShadeModel( GL_FLAT ); /* if(boxclip!=0) I may want this back */ { GLdouble plane[4]; static GLfloat cubeframe[][3] = { -1,-1,-1, -1,-1,1, -1,1,1, -1,1,-1, 1,1,-1, 1,1,1, 1,-1,1, 1,-1,-1, -1,-1,-1, }; /* Sigh. We wanted to do this before applying symrotn, * but didn't know w2c then. So undo the symrotn, * install and draw the clipping box, and redo symrotn. */ glRotatef(-symrotn, 0,0,1); /* branch the hierarchy here to draw box */ if(boxshow){ vtfmpoint(&Cbox, &box0, w2c); /* gkf not sure this is a good place */ glPushMatrix(); glTranslatef(box0.x[0], box0.x[1], box0.x[2]); glScalef(boxr, boxr, boxr); glBegin(GL_LINE_STRIP); for(ii=0; ii0) { for(ii=0; ii<6; ii++) { memset(plane, 0, sizeof(plane)); plane[ii/2] = (ii&1) ? -1 : 1; plane[3] = (ii&1) ? box0.x[ii/2]+boxr : -box0.x[ii/2]+boxr; glClipPlane(GL_CLIP_PLANE0+ii, plane); glEnable(GL_CLIP_PLANE0+ii); } /* end clipping planes */ } /* end if boxclip */ glRotatef(symrotn, 0,0,1); } /* end block defining cubeframe */ if(oneside==1) { glEnable(GL_CULL_FACE); glCullFace( ((gap<0)^(lphase&1)) ? GL_BACK : GL_FRONT ); } else { glDisable(GL_CULL_FACE); } // if (gap>0) glBegin(GL_TRIANGLES); for(ii=0; ii < tp->nfaces; ii++) { #if defined(WIN32) && SOUND if (Faud >= 0 && ii % 5000 == 0) usleep(6*1000); /* give vss a time-slice every once in a while */ #endif /* gkf: locus=mode for drawing double locus fromdl=0 if facet intersects another */ drawMe = (tp->fromdl[ii] < dlradius); switch (locus) { case 0: drawMe=1; break; /* draw entire surface */ case 1: break; /* draw double locus */ case 2: drawMe=1-drawMe; break; /* draw its complement */ case 3: drawMe=0; /* draw just double locus */ } if (drawMe) { int fv0 = tp->fv0[ii]; int va = tp->fv[fv0], vb = tp->fv[fv0+1], vc = tp->fv[fv0+2]; int rgba, front; float adotn, ggap; n = &tp->norm[ ii ]; a = &tp->verts[ va ]; adotn = DOT(*a, *n); dog = (n->x[2])/2. + .5; /* use normal, not height */ if (lphase&1 || (oneside==3 && dog<.5)) dog = 1-dog; dog = dog<0? 0:( dog>1? 1:dog); /* To decide whether we're facing the front or back of this, * we want to evaluate (eye - a) dot n. * For its rotated siblings, we'll want (eye - Rot(a)) dot Rot(n) * which is the same as (Rot^-1(eye) - a) dot n. * We save a bit of work by precalculating "a dot n". * And, the lighting computation is light dot n; rotated, it's * light dot Rot(n), which is equal to Rot^-1(light) dot n. */ front = (oneside==3) || ((DOT(eye[0],*n) <= adotn) ^ (lphase&1)); rgba = paint(front, DOT(light[0],*n), dog); ggap = (front & (oneside==2)) ? 1 : gap; do_tri(a, &tp->verts[vb], &tp->verts[vc], ggap, rgba); for(jj = 1; jj < trotn; jj++) { Point *rverts = &vrot[tp->nverts*(jj-1)]; front = (DOT(eye[jj],*n) <= adotn) ^ (lphase&1); rgba = paint(front, DOT(light[jj],*n), dog); ggap = (front & (oneside==2)) ? 1 : gap; do_tri(&rverts[va], &rverts[vb], &rverts[vc], ggap, rgba); } } } //glEnd(); if(thickness > 0) { glLineWidth(thickness); glColor3ub(0,255,0); /* doublelocus color */ #ifdef GL_POLYGON_OFFSET_EXT glPolygonOffsetEXT(0, 0); #endif glBegin(GL_LINES); for(kk=0; kk < tp->nedgeverts; kk += 2) { a = &tp->edges[kk]; b = &tp->edges[kk+1]; glVertex3fv(a->x); glVertex3fv(b->x); for(jj = 1; jj < trotn; jj++) { vrotxy(ra, a, &rotcs[jj][0]); vrotxy(rb, b, &rotcs[jj][0]); glVertex3fv(ra->x); glVertex3fv(rb->x); } } glEnd(); glLineWidth( 1 ); } if(dotube > 0) { float (*profile)[2]; static Point tubelight_cam = {{-.15,.4,1}}; Point tubelight; Loop *l; int i; vtfmvector( &tubelight, &tubelight_cam, c2w ); vunit( &tubelight, &tubelight ); profile = (float (*)[2])alloca(2*(dotube+1) * sizeof(float)); for(jj = 0; jj <= dotube; jj++) { profile[jj][0] = cos(2*M_PI*jj / dotube); profile[jj][1] = sin(2*M_PI*jj / dotube); } glShadeModel( GL_SMOOTH ); if(tp->loops == NULL || docans) { for(kk = 0; kk < tp->nedgeverts; kk += 2) { Point p0, p1; do_tube( &tp->edges[kk], &tp->edges[kk+1], dotube, profile, &tubelight ); for(jj = 1; jj < trotn; jj++) { vrotxy(&p0, &tp->edges[kk], &rotcs[jj][0]); vrotxy(&p1, &tp->edges[kk+1], &rotcs[jj][0]); do_tube( &p0, &p1, dotube, profile, &tubelight ); } } } else { if (circradius>0.) do_tubeloop( &circle, dotube, profile, &tubelight, circradius, circscale, tubecolors[0] ); for(l = tp->loops, i=0; l != NULL; l = l->next) { do_tubeloop( l, dotube, profile, &tubelight, tuberadius, 1., tubecolors[++i%8] ); } } } if(alfa < 1) { glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); } if(oneside==1) { glDisable(GL_CULL_FACE); } if(boxclip > 0) { for(ii=0; ii<6; ii++) glDisable(GL_CLIP_PLANE0+ii); } } /* end of draw_object */ void do_tri(Point *a,Point *b,Point *c, float ggap, int rgba) { float p[3],q[3],r[3]; register float g, midpart; if(ggap == -1 || ggap == 0) return; glColor4ubv( (GLubyte *)&rgba ); if(ggap == 1) { glBegin(GL_TRIANGLES); glVertex3fv((GLfloat *)a); glVertex3fv((GLfloat *)b); glVertex3fv((GLfloat *)c); glEnd(); return; } g = fabs(ggap); #define INTERP(dst, src, i) dst[i] = src->x[i]*g + midpart #define INTERPpqr(i) midpart = (1-g)*(a->x[i] + b->x[i] + c->x[i])/3, \ INTERP(p,a,i),INTERP(q,b,i), INTERP(r,c,i) /* Unroll the loop for speed */ INTERPpqr(0); INTERPpqr(1); INTERPpqr(2); if (ggap>0) { glBegin(GL_TRIANGLES); glVertex3fv(p); glVertex3fv(q); glVertex3fv(r); glEnd(); } else { /* Make a strip from * a -- b -- c -- a * | | | | * p -- q -- r -- p */ glBegin(GL_QUAD_STRIP); glVertex3fv(a->x); glVertex3fv(p); glVertex3fv(b->x); glVertex3fv(q); glVertex3fv(c->x); glVertex3fv(r); glVertex3fv(a->x); glVertex3fv(p); glEnd(); } } void do_tube( Point *p0, Point *p1, int nprofile, float (*profile)[2], Point *light ) { static Point vx = {1,0,0}; Point e0, e1; Point dp; Point end0, end1, out, edge0, edge1; float spec, illum, gray; int i; vsub(&dp, p0, p1); if( vunit(&dp, &dp) == 0) return; vproj( NULL, &e0, &vx, &dp ); if(vunit(&e0, &e0) == 0) { e0.x[ (fabs(dp.x[1])>fabs(dp.x[2])) ? 2 : 1 ] = 1; vproj( NULL, &e0, &e0, &dp ); vunit( &e0, &e0 ); } vcross( &e1, &dp, &e0 ); /* Now we have a basis for the cross-section: {e0, e1} */ vlerp( &end0, -canlength, p0, p1 ); vlerp( &end1, -canlength, p1, p0 ); glBegin(GL_QUAD_STRIP); for(i = 0; i <= nprofile; i++) { vcomb( &out, tuberadius*profile[i][0], &e0, tuberadius*profile[i][1], &e1 ); vadd( &edge0, &end0, &out ); vadd( &edge1, &end1, &out ); illum = fabs( DOT( out, *light ) ) / tuberadius; /* Surface normal */ spec = 1 - tubespecular * (1 - illum); gray = illum * tubediffuse; if(gray < spec) gray = spec; glColor3f( gray, gray, gray ); glVertex3fv( &edge0.x[0] ); glVertex3fv( &edge1.x[0] ); } glEnd(); } void do_tubeloop( Loop *l, int nprofile, float (*profile)[2], Point *light, float radius, float scale, float color[4] ) { int i, k, prevk; float spec, illum, red,green,blue; Point *edge0 = NewA( Point, nprofile+1 ); Point *edge1 = NewA( Point, nprofile+1 ); float *red0 = NewA( float, nprofile+1 ); float *green0 = NewA( float, nprofile+1 ); float *blue0 = NewA( float, nprofile+1 ); float *red1 = NewA( float, nprofile+1 ); float *green1 = NewA( float, nprofile+1 ); float *blue1 = NewA( float, nprofile+1 ); Point *eprev = edge0, *ecur = edge1, *et; float *rprev = red0, *rcur = red1, *rt; float *gprev = green0, *gcur = green1, *gt; float *bprev = blue0, *bcur = blue1, *bt; float diff; if(l->closed) { prevk = l->nverts-1; k = 0; } else { prevk = 0; k = 1; } for(i = 0; i <= nprofile; i++) { vcomb( &eprev[i], profile[i][0], &l->xyv[prevk][0], profile[i][1], &l->xyv[prevk][1] ); illum = DOT( eprev[i], *light ); /* Surface normal */ if (illum<0) illum = -illum/10; spec = color[3] - tubespecular * (1 - illum); diff = illum * tubediffuse + .2; red = diff * color[0]; green = diff * color[1]; blue = diff * color[2]; if(red < spec) red = spec; if(green < spec) green = spec; if(blue < spec) blue = spec; rprev[i] = red; gprev[i] = green; bprev[i] = blue; vcomb( &eprev[i], scale, &l->verts[prevk], radius, &eprev[i] ); } for( ; k < l->nverts; k++) { if(nprofile <= 2) { glBegin( GL_LINES ); vcomb( eprev, scale, &l->verts[k], radius, &l->xyv[k][0] ); glColor3f(1,0,0); glVertex3fv( &eprev->x[0] ); glVertex3fv( &l->verts[k].x[0] ); glColor3f(0,1,0); vcomb( eprev, scale, &l->verts[k], radius, &l->xyv[k][1] ); glVertex3fv( &eprev->x[0] ); glEnd(); } else { glBegin(GL_QUAD_STRIP); for(i = 0; i <= nprofile; i++) { glColor3f( rprev[i], gprev[i], bprev[i] ); glVertex3fv( &eprev[i].x[0] ); vcomb( &ecur[i], profile[i][0], &l->xyv[k][0], profile[i][1], &l->xyv[k][1] ); illum = DOT( ecur[i], *light ); /* Surface normal */ if (illum<0) illum = -illum/10; spec = color[3] - tubespecular * (1 - illum); diff = illum * tubediffuse + .2; red = diff * color[0]; green = diff * color[1]; blue = diff * color[2]; if(red < spec) red = spec; if(green < spec) green = spec; if(blue < spec) blue = spec; rcur[i] = red; gcur[i] = green; bcur[i] = blue; vcomb( &ecur[i], scale, &l->verts[k], radius, &ecur[i] ); glVertex3fv( &ecur[i].x[0] ); } glEnd(); } et = eprev; eprev = ecur; ecur = et; rt = rprev; rprev = rcur; rcur = rt; gt = gprev; gprev = gcur; gcur = gt; bt = bprev; bprev = bcur; bcur = bt; } } void drawall(float rltope, int lphase) /* Draws everything (except stars + messages) */ { static int frameno = 0; if((frameno++ & 127) == 0 && getenv("SPEEDO")) fprintf(stderr, "%.1f fps %d*%d faces\n", speedometer(), topes[(int)rltope].nfaces, rotn); if(readytogo) { static float last_tope = -1; static int last_phase = -1; if(boxclip == 2) { float oalfa = alfa; alfa = 1; draw_object(rltope, lphase); boxclip = 0; alfa = (oalfa == 1) ? .1 : oalfa; draw_object(rltope, lphase); alfa = oalfa; boxclip = 2; } else { draw_object(rltope,lphase); } if(tracing>0 && rltope != last_tope || lphase != last_phase) { trace('T'); last_tope = rltope, last_phase = lphase; } if(tracing>0 && !park) tracewhere(); } } static struct timeval tv0; static float parktime; float timenow() { struct timeval tv; gettimeofday(&tv, NULL); if(tv0.tv_sec == 0) tv0 = tv; return (tv.tv_sec - tv0.tv_sec) + (tv.tv_usec - tv0.tv_usec)*1e-6; } void setpark(int newpark) { int oldpark = park; glutIdleFunc( newpark ? NULL : idle ); park = newpark; if(newpark != oldpark) { if(newpark) parktime = timenow(); else { float dt = timenow() - parktime; tv0.tv_sec += (int) dt; tv0.tv_usec += 1000000 * (dt - (int)dt); } clock_tick(); } } /* clock_tick() gets called once per display cycle, * and computes "deltat" for this cycle. * This is either the real time delta since prev cycle, if tymode is REALTYME, * or is our fixed increment of 1/tymerate if tymode is TICKTYME. */ void clock_tick() { static int lastymode = -1; static float lastrealtime = 0; static int wasparked = 0; float now = timenow(); if(park) { deltat = 0; } else if(tymode == REALTYME) { deltat = (lastymode != tymode) ? 0 : now - lastrealtime; lastrealtime = now; } else { deltat = tickrate ? 1/tickrate : 0; } lastymode = tymode; tymenow += deltat; /*if(wasparked) playsynched = 0;*/ wasparked = park; } int topeof(float rtope, int *lphase) { int t = rint(rtope+.001); /* Round to nearest integer, with bias for exact half-integers */ if(t < 0) { *lphase |= 1; t = -t; } else { *lphase &= ~1; } return t; } float settope(float rtope) { if(movie == 2) { if(rtope < 0) rtope += ntopes; if(rtope > ntopes-1) rtope -= ntopes; } else { while(ntopes > 0) { if(rtope > ntopes-1) { rtope = 2*(ntopes-1) - rtope; fore = -1; } else if(rtope < -(ntopes-1)) { rtope = -2*(ntopes-1) - rtope; fore = 1; } else break; } } tope = topeof(rtope, &phase); return rtope; } void tymer() /* from: main() uses: movie step fore tope realtope phase sets: tope realtope phase */ { if (swapp) return; if (movie || step) { if(movie) realtope += fore * deltat * movierate; if(step) { realtope = rint(realtope + fore*step); /* Force to round number */ step = 0; } realtope = settope(realtope); } } #ifdef CAVE void *aamalloc(int nbytes) { void *p = NULL; int need; if(nbytes == 0) nbytes = 1; if(arena == NULL || (p = amalloc(nbytes, arena)) == NULL) { /* OK, forget that old arena, start a fresh one. * This only works if we never afree() or arealloc()! */ arenaalloced++; arenasize += arenasize/2; need = (nbytes>arenasize ? nbytes+100 : arenasize); arena = CAVEUserSharedMemory(need); if((p = amalloc(nbytes, arena)) == NULL) { fprintf(stderr, "Out of shared memory after using ~%dK; couldn't allocate another %d bytes\n", arenaused>>10, need); fprintf(stderr, "pid %d pausing for debugging...\n", getpid()); pause(); exit(1); } } arenaused += nbytes + 64; return p; } void aafree( void *block ) { if(arena != NULL) afree( block, arena ); } #endif /*CAVE*/ void getmem() { #ifdef CAVE # define MALLOC(x) aamalloc((x)) # define FREE(x) aafree((x)) #else /* notCAVE*/ # define MALLOC(x) malloc((x)) # define FREE(x) free(x) #endif #ifdef CAVE /* How big is our input file? Make our shared-data area that big. */ if(filename != NULL) { struct stat st; if(stat(filename, &st) == 0) arenasize = 2 * st.st_size + 65536; MALLOC(1); /* Start allocation now, before we fork() */ arenasize0 = arenasize; } #endif shr = (shared_dt*)MALLOC(sizeof(shared_dt)); memset(shr, 0, sizeof(*shr)); } void cleanup() { #if SOUND if(!quiet && Faud >= 0) audioclean(); #endif #ifdef unix if(child) kill(child,9); #endif } void dotrans(void *ignored_sproc_arg) { FILE *topefile, *dlfile; char *filename2; int ii,jj,temp; printf("Starting to read %s\n", filename); if(filename == NULL) { fprintf(stderr, "Oh, forget it. Need a datafile.\n"); exit(1); } /* jms 1dec96 allow stdin with filename '-' */ if (!strcmp(filename,"-")) topefile=stdin; else if ((topefile = myfopen(filename,"rb")) == NULL) { /* jms 10mar98 try also adding ".mov" */ char *fn = filename; filename = (char*)malloc(strlen(filename)+5); sprintf(filename, "%s.mov", fn); if ((topefile = myfopen(filename,"rb")) == NULL) { fprintf(stderr, "Can't open %s: ", filename); perror(""); exit(1); } } filename2 = (char*)malloc(strlen(filename) + 4); sprintf(filename2, "%s.dl", filename); dlfile = myfopen(filename2, "rb"); if(dlfile==NULL) printf("Double locus file <%s> not present.\n",filename2); fprintf(stderr,"Getting topes%s: ", dlfile ? " and double-loci" : ""); for(;;) { astring[0] = 0; if(fgets(astring, sizeof(astring), topefile) == NULL) { break; /* We've read all there is... */ } /* Expect something like * (geometry blah { OFF BINARY * ... binary OFF data ... * }) * # comment ... * (geometry blah2 { OFF BINARY * ... more binary OFF data ... * }) * ... * (exit) */ if(strncmp(astring, "(exit)", 6) == 0) /* Done! */ break; if(astring[0] == '#') { char *nlp = strchr(astring, '\n'); if(nlp) *nlp = '\0'; if(ntopes > 0) { topes[ntopes-1].comment = (char*)MALLOC(strlen(astring + 1) + 1); strcpy(topes[ntopes-1].comment, astring+1); } nlp = strstr(astring, "Rotn="); if(nlp && rotn == 0) { sscanf(nlp, "Rotn=%d", &rotn); if(rotn & 1) phase |= 2; } } else if(strstr(astring, "OFF") != NULL) { if( readanoff( topefile, &topes[ntopes], strstr(astring,"BINARY")==NULL ) ) break; /* If readanoff() returns nonzero, we're hosed */ /* otherwise, we've got a new tope */ printf("%3d \b\b\b\b", ntopes); fflush(stdout); if(dlfile != NULL) { readdl( dlfile, &topes[ntopes] ); if(rotn > 0) loopdl( &topes[ntopes] ); if(ntopes == 1 && topes[0].loops == NULL) { /* Now that we know the rotational symmetry ... */ loopdl( &topes[0] ); } } ntopes++; #if HAS_SGINAP && HAS_SPROC newdata = 1; if(!nosproc) { /* If we're reading in parallel with a parent process, * give up now if it's gone. */ if(kill(getppid(), 0) < 0) { /* Mom's gone -- give up! */ perror("parent's gone"); exit(0); } if(stopreading) exit(0); } #endif } } if(topefile != stdin) fclose(topefile); printf("Read %d topes.\n", ntopes); if(arenaused > 0) printf("Used ~%dK memory (estimated %dK)\n", (arenaused>>10) + 1, (arenasize0>>10)+1); if(arenaalloced > 1) { fprintf(stderr, "*** Trouble[%d/%d]: initial estimate of shared-memory size (%dK) wasn't enough;\n\ rerun with -K%d\n", arenaalloced>>10, arenasize>>10, (arenasize0>>10)+1, (arenaused>>10)+1); } readytogo = 2; #if HAS_SPROC if(!nosproc) exit(1); #endif } void swabl(int *dst, int *src, int nwds) { if(1 != antohl(1) || dst != src) { while(--nwds >= 0) { *dst++ = antohl(*src++); } } } int fgetint(FILE *f, int ascii) { int v; if(ascii) { fscanf(f, "%d", &v); return v; } if(fread(&v, sizeof(int), 1, f) <= 0) return -1; return antohl(v); } int fgetfloats(float *fp, int nfloats, FILE *f, int ascii) { int k; if(ascii) { for(k = 0; k < nfloats; k++) if(fscanf(f, "%f", &fp[k]) <= 0) break; return k; } k = fread(fp, sizeof(float), nfloats, f); swabl((int *)fp, (int *)fp, k); return k; } #define FBITS 24 #define FACEOF(fvcode) ((fvcode) & ((1<> FBITS) & ((1 << (32-FBITS))-1)) #define FVCODE(face, fvert) ((fvert << FBITS) | (face)) int nhash; int *ht; float (*rotcs)[2]; int vhash(int *data) { return (((data[0]>>1) | (data[1]>>2) | (data[2]>>3)) & ((1<<30) - 1)) % nhash; } void reverts(register Tope *tp) { int nv = tp->nverts; int *uniqv; int *newv; Point *newverts; int uniqvno, newvno; int i, hash; int maxuniqv; Point min, max; nhash = 2*tp->nverts+1; ht = NewA(int, nhash); memset(ht, -1, nhash*sizeof(int)); min.x[0] = min.x[1] = min.x[2] = 1e30; max.x[0] = max.x[1] = max.x[2] = -1e30; uniqv = NewA(int, tp->nverts); /* Scan the vertices, and find (exact) matches. */ for(i = 0; i < nv; i++) { hash = vhash((int *)&tp->verts[i]); for(;;) { if(ht[hash] == -1) { ht[hash] = i; uniqv[i] = i; break; } if(memcmp(&tp->verts[i], &tp->verts[ht[hash]], sizeof(Point)) == 0) { uniqv[i] = ht[hash]; break; } if(++hash >= nhash) hash = 0; } } /* Now, which of these vertices do we actually need (i.e. to which ones * does some face refer?) */ newv = NewA(int, tp->nverts); memset(newv, -1, tp->nverts*sizeof(int)); newvno = 0; maxuniqv = 0; for(i = 0; i < tp->totfv; i++) { uniqvno = uniqv[tp->fv[i]]; if(newv[uniqvno] == -1) { newv[uniqvno] = newvno; newvno++; } tp->fv[i] = newv[uniqvno]; if(maxuniqv < uniqvno) maxuniqv=uniqvno; } /* Now rebuild the tp->verts table, and compute bounding box. */ newverts = NewN(Point, newvno); for(i = 0; i < tp->nverts; i++) { if(newv[i] != -1) { newverts[ newv[i] ] = tp->verts[i]; if(min.x[0] > tp->verts[i].x[0]) min.x[0] = tp->verts[i].x[0]; if(min.x[1] > tp->verts[i].x[1]) min.x[1] = tp->verts[i].x[1]; if(min.x[2] > tp->verts[i].x[2]) min.x[2] = tp->verts[i].x[2]; if(max.x[0] < tp->verts[i].x[0]) max.x[0] = tp->verts[i].x[0]; if(max.x[1] < tp->verts[i].x[1]) max.x[1] = tp->verts[i].x[1]; if(max.x[2] < tp->verts[i].x[2]) max.x[2] = tp->verts[i].x[2]; } } /* Don't free(tp->verts), just change it; readanoff() got it with alloca() */ tp->nverts = newvno; tp->verts = newverts; tp->min = min; vsub(&tp->size, &max, &min); if(tp->size.x[0] == 0) tp->size.x[0] = .001; if(tp->size.x[1] == 0) tp->size.x[1] = .001; if(tp->size.x[2] == 0) tp->size.x[2] = .001; if(debug) fprintf(stderr, "[%d->%dv; max%d]", nv, newvno, maxuniqv); } int match_edge(register Tope *tp, int fno, int fvno) { int nfv = tp->nfv[fno]; int fv0 = tp->fv0[fno]; int v1 = tp->fv[fv0 + fvno]; int v2 = tp->fv[(fvno+1 == nfv) ? fv0 : fv0+fvno+1]; int hash = ( (v1nfv[othf]; int othfv0 = tp->fv0[othf]; if(v2 == tp->fv[othfv0+othfvno] && v1 == tp->fv[(othfvno+1==othnfv) ? othfv0 : othfv0+othfvno+1]) { /* Found it */ tp->fadj[fv0 + fvno] = othf; tp->fadj[othfv0 + othfvno] = fno; /* Clear this hash entry if it's safe */ if(hash+1= nhash) { hash = 0; if(++oops > 1) { fprintf(stderr, "Yikes, %d-entry edge-hash table filled up!\n", nhash); return -1; } } } /* Not matched; add this face-edge to hash table so our mate will find us later */ ht[hash] = FVCODE(fno, fvno); return 0; } int pointhash(Point *pt, Tope *tp) { int x = 1024*(pt->x[0] - tp->min.x[0]) / tp->size.x[0]; int y = 1024*(pt->x[1] - tp->min.x[1]) / tp->size.x[1]; int z = 1024*(pt->x[2] - tp->min.x[2]) / tp->size.x[2]; #define BITS10(v) ((v) & ((1<<10)-1)) return (BITS10(x) << 20 | BITS10(y) << 10 | BITS10(z)) % nhash; } int match_edge_midpoint(register Tope *tp, int fno, int fvno, int trotn, float (*rotcs)[2]) { int nfv = tp->nfv[fno]; int fv0 = tp->fv0[fno]; int v1 = tp->fv[fv0 + fvno]; int v2 = tp->fv[(fvno+1 == nfv) ? fv0 : fv0+fvno+1]; int i, oops; int hash, hash0; int neighbor; Point midpt, testpt; VMID(&midpt, &tp->verts[v1], &tp->verts[v2]); hash0 = pointhash(&midpt, tp); testpt = midpt; for(i = 0; i < trotn; i++) { if(i == 0) { testpt = midpt; hash = hash0; } else { vrotxy(&testpt, &midpt, rotcs[i]); hash = pointhash(&testpt, tp); } oops = 0; while(ht[hash] != -1) { int othf = FACEOF(ht[hash]); int othfvno = FVERTOF(ht[hash]); int othnfv = tp->nfv[othf]; int othfv0 = tp->fv0[othf]; int othv1 = tp->fv[othfv0 + othfvno]; int othv2 = tp->fv[(othfvno+1 == othnfv) ? othfv0 : othfv0 + othfvno+1]; Point othmid, ds; VMID(&othmid, &tp->verts[othv1], &tp->verts[othv2]); vsub(&ds, &othmid, &testpt); if(fabs(ds.x[0]) < tp->size.x[0]*.001 && fabs(ds.x[1]) < tp->size.x[1]*.001 && fabs(ds.x[2]) < tp->size.x[2]*.001) { /* Eureka */ tp->fadj[fv0 + fvno] = othf; tp->fadj[othfv0 + othfvno] = fno; /* Clear this hash entry if it's safe */ if(hash+1= nhash) { hash = 0; if(oops++ > 0) { fprintf(stderr, "Yikes, %d-entry edge-hash table filled up!\n", nhash); return -1; } } } } /* Failed. Add unrotated midpoint to our hash table for the future. */ for(hash = hash0; ht[hash] != -1; ) if(++hash >= nhash) hash = 0; ht[hash] = FVCODE(fno, fvno); return 0; } int findmoreadj(register Tope *tp) { int i, fno, fvno; int matched = 0; int trotn = rotn; if(trotn < 1) trotn = 1; /* Re-use findadj()'s hash table. Since it was created with * alloca(), we can only do this if findadj() calls us! */ memset(ht, -1, nhash*sizeof(int)); rotcs = (float (*)[2])alloca(2*trotn * sizeof(float)); for(i = 1; i < trotn; i++) { rotcs[i][0] = cos(2*M_PI*i/trotn); rotcs[i][1] = sin(2*M_PI*i/trotn); } for(fno = 0; fno < tp->nfaces; fno++) { int fv0 = tp->fv0[fno]; for(fvno = 0; fvno < tp->nfv[fno]; fvno++) if(tp->fadj[fv0+fvno] == -1) matched += match_edge_midpoint(tp, fno, fvno, trotn, rotcs); } return matched; } void findadj(Tope *tp) { int fno, fvno, fv0; int matched = 0, rematched = 0; if(tp->fadj == NULL) { tp->fadj = NewN(int, tp->totfv); memset(tp->fadj, -1, tp->totfv * sizeof(int)); } /* Hash table for the face-edges. "tp->totfv" is related to the size; * that'll be the number of edges if all faces are disconnected, or * twice the number of edges if the surface is closed. * Let's use a table twice that big to ensure it's sparse. * We use hashing with simple linear probing. */ nhash = 2*tp->totfv + 1; if((nhash&1) == 0) nhash++; ht = (int *)alloca(nhash * sizeof(int)); memset(ht, -1, nhash*sizeof(int)); for(fno = 0; fno < tp->nfaces; fno++) { int fv0 = tp->fv0[fno]; for(fvno = 0; fvno < tp->nfv[fno]; fvno++) matched += match_edge(tp, fno, fvno); } rematched = findmoreadj(tp); if(debug) printf("[%d+%d=%d/%d]\n", matched, rematched, matched+rematched, tp->totfv); fflush(stdout); } int readanoff( FILE *geom, register Tope *tp, int ascii ) { long base = ftell(geom); int fno, ncolor; float acolor[4]; int nfv, k; int *fv, fvroom; int totfv; int optimize = (getenv("NOSIMP") == NULL); memset(tp, 0, sizeof(Tope)); tp->nverts = fgetint(geom, ascii); tp->nfaces = fgetint(geom, ascii); (void) fgetint(geom, ascii); if(tp->nverts < 0 || tp->nverts > 100000000 || tp->nfaces < 0 || tp->nfaces > 100000000) { fprintf(stderr, "Implausible binary-OFF header in tope %d (file offset %d): nverts %d, nfaces %d\n", ntopes, base, tp->nverts, tp->nfaces); return 1; } if(optimize) { tp->verts = NewA(Point, tp->nverts); } else { tp->verts = NewN(Point, tp->nverts); } if(fgetfloats((float *)tp->verts, 3*tp->nverts, geom, ascii) < 3*tp->nverts) { fprintf(stderr, "Couldn't read %d verts in tope %d (offset %ld)\n", tp->verts, ntopes, base); return 1; } tp->norm = (Point *) MALLOC( tp->nfaces * sizeof(Point) ); tp->nfv = (int *) MALLOC( tp->nfaces * sizeof(int) ); tp->fv0 = (int *) MALLOC( tp->nfaces * sizeof(int) ); tp->fromdl = (unsigned char *) MALLOC( tp->nfaces * sizeof(char) ); memset(tp->fromdl, 255, tp->nfaces*sizeof(unsigned char)); fvroom = 4 * tp->nfaces + 1000; fv = (int *)alloca( fvroom*sizeof(int) ); totfv = 0; for(fno = 0; fno < tp->nfaces; fno++) { nfv = fgetint(geom, ascii); if(nfv <= 0 || nfv > 100000) { fprintf(stderr, "Unreasonable number of verts (%d) on face %d of tope %d (file offset %d)\n", nfv, fno, ntopes, base); return 1; } tp->nfv[fno] = nfv; /* Number of verts on this face */ tp->fv0[fno] = totfv; /* Table-index of 1st vert of this face */ if(nfv + totfv > fvroom) { /* need more room in fv[] table? */ int *newfv = (int *)alloca( 2*fvroom*sizeof(int) ); fvroom *= 2; memcpy( newfv, fv, totfv*sizeof(int) ); fv = newfv; } for(k = 0; k < nfv; k++) fv[totfv++] = fgetint(geom, ascii); if(ascii) { while((k = getc(geom)) != '\n' && k != EOF) ; } else { ncolor = fgetint(geom, ascii); /* Read and ignore any colors */ if(ncolor > 4 || ncolor < 0) { fprintf(stderr, "Unreasonable number of face-color components (%d) on face %d of tope %d (file offset %d)\n", ncolor, fno, ntopes, base); return 1; } fread(&acolor,ncolor*sizeof(float),1,geom); } } tp->fv = (int *)MALLOC( totfv * sizeof(int) ); tp->totfv = totfv; memcpy(tp->fv, fv, totfv*sizeof(int)); /* Compute normals */ for(fno = 0; fno < tp->nfaces; fno++) { Point v01, v02; int fv0 = tp->fv0[fno]; vsub(&v01, &tp->verts[tp->fv[fv0]], &tp->verts[tp->fv[fv0+1]]); vsub(&v02, &tp->verts[tp->fv[fv0]], &tp->verts[tp->fv[fv0+2]]); vcross(&tp->norm[fno], &v01, &v02); vunit(&tp->norm[fno], &tp->norm[fno]); } if(optimize) { reverts(&topes[ntopes]); findadj(&topes[ntopes]); /*tp->vnorm = (Point *) MALLOC( tp->nverts * sizeof(Point) );*/ /*memset(tp->vnorm, 0, tp->nverts*sizeof(Point));*/ /*for(vno = 0; vno < nverts; vno++) */ } return 0; } void findfromdl( register Tope *tp ) { int *next = NewA(int, tp->nfaces); int head, tail; int f, e, v; /* Breadth-first search for facets at given distance from the * double locus. Start with distance-0 facets. */ head = tail = -1; for(f = 0; f < tp->nfaces; f++) { if(tp->fromdl[f] == 0) { if(head < 0) head = tail = f; else { next[tail] = f; next[f] = -1; tail = f; } } } /* Now scan the list, adding facet neighbors. */ for(f = head; f >= 0; f = next[f]) { int thresh = tp->fromdl[f] + 1; if(thresh > 255) thresh = 255; if(!ribbonflag) { for(e = tp->nfv[f]; --e >= 0; ) { int adj = tp->fadj[tp->fv0[f] + e]; if(adj >= 0 && adj < tp->nfaces && tp->fromdl[adj] > thresh) { tp->fromdl[adj] = thresh; next[tail] = adj; next[adj] = -1; tail = adj; } } } else { for(v = tp->nfv[f]; --v >= 0; ) searchv(tp,f,f,f,tp->fv[tp->fv0[f]+v],thresh,next,&tail); } } } #define facet_has_vert(f,v) \ ( tp->fv[tp->fv0[f]]==(v) || tp->fv[tp->fv0[f]+1]==(v) || \ tp->fv[tp->fv0[f]+2]==(v) ) void searchv(Tope *tp, int f0, int flast, int f, int v, int thresh, int next[], int *tail) { int e; for(e = tp->nfv[f]; --e >= 0; ) { int adj = tp->fadj[tp->fv0[f] + e]; if(adj >= 0 && adj < tp->nfaces && adj != f0 && adj != flast && facet_has_vert(adj,v) && tp->fromdl[adj] >= thresh) { /* here we need to search even through faces already set = thresh, but we need somehow to avoid infinite loops */ if (tp->fromdl[adj]>thresh) { tp->fromdl[adj] = thresh; next[*tail] = adj; next[adj] = -1; *tail = adj; } searchv(tp,f0,f,adj,v,thresh,next,tail); } } } void readdl( FILE *dlf, Tope *tp ) { int i; int any = 0; int v = -1; char line[256]; tp->fromdl = NewN(unsigned char, tp->nfaces); memset(tp->fromdl, -1, tp->nfaces*sizeof(unsigned char)); /* We expect: * a list of the faces which lie on the double locus, * then -1, * then a vertex count and list of 3-D vertex pairs which trace the locus. */ while(fgets(line, sizeof(line), dlf) != NULL) { if(sscanf(line, "%d", &v) <= 0) /* Ignore blank lines, comments */ continue; if(v < 0) /* Reached "-1" */ break; if(v < tp->nfaces) tp->fromdl[v] = 0; any = 1; } fgets(line, sizeof(line), dlf); if(sscanf(line, "%d", &tp->nedgeverts) > 0 && tp->nedgeverts > 0) { tp->edges = NewN(Point, tp->nedgeverts); memset(tp->edges, 0, tp->nedgeverts*sizeof(Point)); for(i = 0; i < tp->nedgeverts; i++) { line[0] = '\0'; fgets(line, sizeof(line), dlf); if(3 != sscanf(line,"%f %f %f",&tp->edges[i].x[0], &tp->edges[i].x[1], &tp->edges[i].x[2])) { fprintf(stderr, "\nExpected vert %d/%d of double locus for %d, got: %s", i, tp->nedgeverts, ntopes, line); break; } } } findfromdl(tp); } int evhash( Point *p ) { int x = p->x[0] * 10000; int y = p->x[1] * 10000; int z = p->x[2] * 10000; return ((unsigned int)(x + y*11 + z*37)) % nhash; } int evhash2( Point *p, int dp[3] ) { int x = (int)(p->x[0] * 10000) + dp[0]; int y = (int)(p->x[1] * 10000) + dp[1]; int z = (int)(p->x[2] * 10000) + dp[2]; return ((unsigned int)(x + y*11 + z*37)) % nhash; } #define EVTINYSEG .02 #define EVEPS .00002 int evmatch( Point *a, Point *b ) { return (fabs(a->x[0] - b->x[0]) < EVEPS && fabs(a->x[1] - b->x[1]) < EVEPS && fabs(a->x[2] - b->x[2]) < EVEPS); } #define MULTPAIR(mvno) (-3 - (mvno)) #define MVNO(multpair) (-3 - (multpair)) void evcollision( FILE *f, int i, Point segs[] ) { if(i != -1) fprintf(f, /*" %d[%g %g %g]"*/" %d", i, segs[i].x[0], segs[i].x[1], segs[i].x[2]); } #define CIRCLEN 90 void initcircle() { int i; circle.next = NULL; circle.closed = 1; circle.nverts = CIRCLEN; circle.verts = NewN(Point,CIRCLEN); for (i=0; invnos; i++) if(vno == mv->vnos[i]) return; if(mv->nvnos >= mv->vroom) mv->vnos = (int *)realloc(mv->vnos, (mv->vroom *= 2)*sizeof(int)); mv->vnos[mv->nvnos++] = vno; } void evmultclear() { int i; for(i = 0; i < nmultverts; i++) free(multverts[i].vnos); nmultverts = 0; } /* * seg-vertex "i" appears as the end of more than two segments. */ int evmultiple( int i, int hent, Point *segs, int paired[] ) { int mvno; if(ht[hent] < -1) { mvno = MVNO( ht[hent] ); } else { /* It's a new one */ if(nmultverts >= multvertroom) multverts = (struct multvert *) ( multvertroom>0 ? realloc(multverts, (multvertroom *= 2)*sizeof(*multverts)) : malloc((multvertroom = 30)*sizeof(*multverts))); mvno = nmultverts++; multverts[mvno].vnos = (int *)malloc( (multverts[mvno].vroom = 10) * sizeof(int) ); multverts[mvno].nvnos = 0; } if(debug) printf("multvert %d: %d (%d) %d (%d) ((%d)) %g %g %g\n", MULTPAIR(mvno), i, paired[i], ht[hent], paired[ht[hent]], paired[paired[ht[hent]]], segs[i].x[0], segs[i].x[1], segs[i].x[2]); evmultvert( i, mvno ); evmultvert( paired[i], mvno ); if(paired[i] >= 0) { evmultvert( paired[paired[i]], mvno ); paired[paired[i]] = MULTPAIR(mvno); } if(ht[hent] >= 0) { evmultvert( ht[hent], mvno ); evmultvert( paired[ht[hent]], mvno ); if(paired[ht[hent]] >= 0) paired[paired[ht[hent]]] = MULTPAIR(mvno); paired[ht[hent]] = MULTPAIR(mvno); } paired[i] = MULTPAIR(mvno); ht[hent] = MULTPAIR(mvno); return mvno; } int evsearch( int i, Point segs[], int paired[] ) { Point *p = &segs[i]; int h, hv; for(h = evhash(p); ht[h] != -1; ++h >= nhash ? (h=0) : 0) { hv = (ht[h] >= 0) ? ht[h] : multverts[MVNO(ht[h])].vnos[0]; if(evmatch(p, &segs[hv])) { if( ht[h] < 0 || paired[hv] != -1 ) { return evmultiple( i, h, segs, paired ); } paired[i] = ht[h]; paired[ht[h]] = i; return ht[h]; } } ht[h] = i; return -1; } int evsearch2( int i, int dp[], Point segs[], int paired[] ) { Point *p = &segs[i]; int h = evhash2( p, dp ); int hv; for( ; ht[h] != -1; ++h >= nhash ? (h=0) : 0) { hv = (ht[h] >= 0) ? ht[h] : multverts[MVNO(ht[h])].vnos[0]; if(i != ht[h] && evmatch(p, &segs[hv])) { if(paired[hv] != -1) { return evmultiple( i, h, segs, paired ); } paired[i] = ht[h]; paired[ht[h]] = i; return ht[h]; } } return -1; } int evfollow( int start, int paired[], char used[], Point *segs, int *countp, int name ) { int ni, i = start; int count = 0; if(start < 0) return -1; if((name & 0xFE) == 0) name += 2; while((ni = paired[i]) != -1) { if(ni < -1) { /* Struck a higher-order vertex. By now we know everything * attached to this vertex, so figure out what's next. * We want to join it with a segment which: * - is not already used * - is either very short, or nearly parallel to this segment */ int k, bestk = -1, oi; int mvno = MVNO(ni); struct multvert *mv = &multverts[mvno]; Point way, oway; float dot, bestdot = -1.1; vsub( &way, &segs[i], &segs[i^1] ); vunit( &way, &way ); for(k = 0; k < mv->nvnos; k++) { oi = mv->vnos[k]; if(used[oi]) continue; vsub( &oway, &segs[oi], &segs[oi^1] ); vunit( &oway, &oway ); dot = fabs(DOT(way, oway)); if(bestdot <= dot) bestdot = dot, bestk = k; } if(bestk >= 0) { /* Good enough, use it. */ ni = paired[i] = mv->vnos[bestk]; paired[ni] = i; } else { fprintf(stdout, "Tope %d: %d-fold vertex [%d] %d: %g %g %g\n", _tope, mv->nvnos, ni, i, segs[i].x[0], segs[i].x[1], segs[i].x[2]); break; } } if(paired[ni] != i) { fprintf(stderr, "Broken back-link? "); evcollision( stderr, i, segs ); fprintf(stderr, " => "); evcollision( stderr, ni, segs ); fprintf(stderr, " but %d => ", ni); evcollision( stderr, paired[ni], segs ); fprintf(stderr, "\n"); paired[ni] = i; } used[i] = used[i^1] = name; count++; if(used[i] == 0) { used[i] = used[i^1] = name; count++; } if(used[ni] != 0) break; i = ni^1; /* Switch to other vertex of new segment */ } /* One last tweak. Why is this necessary? */ if(paired[i]>=0 && used[paired[i]] == 0) { i = paired[i]^1; used[i] = used[i^1] = name; count++; } if(used[i] == 0) { used[i] = used[i^1] = name; /* Another hack */ count++; } if(countp != NULL) *countp += count; return i; } void tubebasis( Loop *l ) { Point prevtanv, prevsegv, nextsegv; static Point xv0 = {1,0,0}, yv0 = {0,1,0}, zv0 = {0,0,1}; Point xv, yv, tanv, tanv0, t; Matrix Trot; int i, j; float arclen, turn; l->xyv = (Point (*)[2])NewN(Point, l->nverts * 2); /* Two arbitrary orthogonal unit vectors, basis for the cross-section. * They remain orth unit vectors through the following loop. */ xv = xv0; yv = yv0; tanv = zv0; arclen = 0; if(l->closed) { vsub( &nextsegv, &l->verts[0], &l->verts[l->nverts-1] ); } else { VZERO(nextsegv); } VZERO(prevtanv); arclen += vunit( &nextsegv, &nextsegv ); for(i = 0; i < l->nverts; i++) { /* Compute tangent vector to core curve at this point */ prevtanv = tanv; prevsegv = nextsegv; if(i == l->nverts-1) { if(l->closed) { /* If open, leave nextsegv == prevsegv */ vsub( &nextsegv, &l->verts[0], &l->verts[i] ); } } else { vsub( &nextsegv, &l->verts[i+1], &l->verts[i] ); } arclen += vunit( &nextsegv, &nextsegv ); vadd( &tanv, &prevsegv, &nextsegv ); if(fabs(DOT(tanv, tanv)) < .01) { /* sigh */ vsub( &tanv, &prevsegv, &nextsegv ); if(fabs(DOT(tanv, tanv)) < .01) tanv = prevsegv; } grotation( Trot, &prevtanv, &tanv ); vtfmvector( &t, &xv, Trot ); xv = t; vtfmvector( &t, &yv, Trot ); yv = t; vproj( NULL, &xv, &xv, &tanv ); /* Re-orthogonalize. Should we need to do this? */ vunit( &xv, &xv ); vcross( &yv, &tanv, &xv ); vunit(&yv, &yv); if(fabs(DOT(xv,yv)) > .1 || DOT(yv,yv)<.9||DOT(xv,xv)<.9) { i++;i--; /*debug stop*/ } l->xyv[i][0] = xv; l->xyv[i][1] = yv; if(i == 0) tanv0 = tanv; } if(l->closed && arclen > 0) { /* Correct for holonomy. */ float fturn, cs[2]; float arcnow = 0; grotation( Trot, &tanv, &tanv0 ); vtfmvector( &t, &xv, Trot ); xv = t; vtfmvector( &t, &yv, Trot ); yv = t; vtfmvector( &t, &tanv, Trot ); turn = atan2( DOT(l->xyv[0][0], yv), DOT(l->xyv[0][0], xv) ); for(i = 1; i < l->nverts; i++) { arcnow += vdist( &l->verts[i-1], &l->verts[i] ); fturn = turn * arcnow / arclen; cs[0] = cos(fturn); cs[1] = sin(fturn); vcomb( &t, cs[0], &l->xyv[i][0], cs[1], &l->xyv[i][1] ); vcomb( &l->xyv[i][1], -cs[1], &l->xyv[i][0], cs[0], &l->xyv[i][1] ); l->xyv[i][0] = t; } i++; } } float distrap = 1.2; void loopdl( Tope *tp ) { int nv = tp->nedgeverts; int trotn = (rotn > 0) ? rotn : 1; int rnv = trotn * nv; Point *segs = NewA( Point, rnv ); int *paired; char *used; float cs[2]; int i, r, k, m, loopno; Loop *l; FILE *flink; char flname[32]; nhash = rnv * 3 + 1; ht = NewA( int, nhash ); memset(ht, -1, nhash*sizeof(int) ); paired = NewA( int, rnv ); memset( paired, -1, rnv*sizeof(int) ); used = NewA( char, rnv ); memset(used, 0, rnv*sizeof(char) ); _paired = paired; _segs = segs; _tope = tp - topes; /* for debug only */ for(i = 0; i < nv; i++) { segs[i] = tp->edges[i]; if((i&1)==0 && memcmp(&tp->edges[i], &tp->edges[i+1], sizeof(Point))==0) { used[i] = used[i^1] = 1; /* Hack to avoid wasting time on junk */ i++; continue; } evsearch( i, segs, paired ); } k = nv; for(r = 1; r < trotn; r++) { cs[0] = cos(2*M_PI*r/trotn); cs[1] = sin(2*M_PI*r/trotn); for(i = 0; i < nv; i++) { vrotxy( &segs[k+i], &segs[i], cs ); if((i&1)==0 && evmatch( &segs[k+i], &segs[i^1] )) { used[k+i] = used[(k+i)^1] = 1; i++; /* This segment is its own image under rotation! */ continue; /* Skip segment: don't enter duplicate into hash table. */ } evsearch( k+i, segs, paired ); } k += nv; } /* Now paired[] contains all the vert-to-vert matches we could find easily. */ for(i = 0; i < rnv; i++) { if(paired[i] == -1 && used[i] == 0) { int dp[3]; for(dp[0] = -1; dp[0] <= 1; dp[0]++) { for(dp[1] = -1; dp[1] <= 1; dp[1]++) { for(dp[2] = -1; dp[2] <= 1; dp[2]++) { if(evsearch2( i, dp, segs, paired ) >= 0) goto found; } } } if(debug) { printf("Tope %d: Unmatched vertex %d\n", _tope, i); pseg(i); } found: ; } } if(debug) printf("Tope %d loops: ", _tope); for(i = 0, loopno = 1; i < rnv; i += 2) { int end0, end1; int count = 0; if(used[i]) continue; end0 = evfollow( i, paired, used, segs, &count, loopno*2 ); if(paired[i^1] == end0) end1 = i^1; else { count++; end1 = evfollow( i^1, paired, used, segs, &count, loopno*2+1 ); } if(used[i] == 0) { /* Hack to pick up isolated segments */ end0 = i; end1 = i^1; used[i] = used[i^1] = loopno*2; count = 1; } if(count > 0) { int prevk; k = 0; l = NewN( Loop, 1 ); memset(l, 0, sizeof(Loop)); l->closed = ((paired[end0] == end1) || ((end0^end1) == 1)) && (count > 2); l->verts = NewN( Point, count+1 ); for(i = 0, m = 0, k = end0; m < count && k >= 0; m++) { float tdist; l->verts[i] = segs[k]; if(i == 0 || vdist(&l->verts[i], &l->verts[i-1]) > EVTINYSEG) i++; prevk = k; k = paired[k ^ 1]; } if(!l->closed && i>0 && vdist(&l->verts[i-1], &segs[end1]) > .0005) l->verts[i++] = segs[end1]; l->nverts = i; if(i <= 1) { FREE(l->verts); FREE(l); continue; } if(debug) printf(" %d", l->closed ? -i : i); l->next = tp->loops; tp->loops = l; tubebasis( l ); loopno++; } } if(debug) { for(i=0; i < rnv; i += 2) { if(used[i] == 0) { loopno++; /* for debugging */ } } } if(debug>1) { sprintf(flname, "loop%03d.lnk", tp - topes); flink = myfopen(flname, "wb"); fprintf(flink, "LINK\n%d\n", loopno-1); for(l = tp->loops; l != NULL; l = l->next) fprintf(flink, "%d\n", l->nverts); for(l = tp->loops; l != NULL; l = l->next) { fprintf(flink, "\n"); for(k = 0; k < l->nverts; k++) fprintf(flink, "%g %g %g\n", l->verts[k].x[0], l->verts[k].x[1], l->verts[k].x[2]); } fclose(flink); } if(debug) printf("\n"); evmultclear(); } void gotnewtope() { readytogo |= 1; if (ntopes>=MAXTOPES){ printf("oops, too many topes. Need to recompile with larger MAXTOPES (was %d)\n", MAXTOPES);} if (swapp) {realtope = settope(-(ntopes-1));} newdata = 0; } void trynewtope() { if(filename != NULL) {if (newdata) gotnewtope();} } struct starsize { float size; int base; int count; int color; } *starsizes; int nstarsizes = 0; float scolors[][3] = { .5, 1, 1, /* W */ .7, .8, 1, /* O */ .9, .9, 1, /* B */ 1, 1, 1, /* A */ 1, 1, .9, /* F */ 1, 1, .8, /* G */ 1, .8, .7, /* K */ 1, .7, .5, /* M */ }; float *starpos; #define SRADIX 91 #define SZERO '$' #define SSIZE '!' #define SSIZESCL 8 #define SRASCALE (8192 / (2*M_PI)) #define SDECSCALE (8192 / M_PI) #define SDECBIAS 4096 #define SCOLORBASE 0 #define SSIZEBASE 8 /* 1128 real stars, to about visual magnitude 4.7 */ /* from star2illi -m 4.7 -d 1.2 -c 3.5 */ char stardata[] = /* Show Pleiades at upper right, Orion at center of field */ "#@ -.98525 .062202 .1594 0 .16485 .095488 .98169 0 .045842 .99349 -.10433 0 0 0 1\n" "!<'=BH_" "!:(<$6`" "!9'iodI!9&7`Lw!9)Z~2Y7mh&!9*YQZ[" "!8&*.4G!8(@eS\\!8+:7Te" "!7'nLUM!7&Xh2oVCKK!7*5;Y=!7+arCl" "!6'z/B6q[g`!6&S~33J'W$!6*A,_'Z~2Y" "!5'@J`{@J`{FY.1!5&>3BTRa1Mes>N8BT58H_A9&PIw'9S!5+R{4M" "!4'TGm$:OgQkCn" "f-;R-Q}`!4*MQouCJ3:c).PGPLb+z\\h&fH&Xy>o[bv-!4+(Ebo!4%9?P'!4$B[9C" "!3'PBX?S[8S^K^FFo3EV;lPMFm7!3&/ieQ8iPrW;6=[09AZg;y`'EdfJ=Q!3(pIe1$XnZ!3*" "F<;>&Tm>g@ji+hf5c2?r%_;ruTU{!3+y59W!3%B9=%" "!2'dJI2P]kssyp@N1[=bCKfkKB*:Pc]+3[J)Eo0!2&?iBE'VoFzXX\\G15QX99ER$H9QU7_" "]BLA`DG.99?}\\4;LSC.Lec>D!2(8mH-!2)S3E@X4Z7LK8?!2*qwb$JiZy_'T96XaZ?B>N" "[B^VhoB,n2VA!2+zR_)/HS)" "!1'V)>_[cH~7:NOueHz]M.`TSd4Hd0Ok[Wz,-bR!1&L60m^N+K28ktFR6tfx!1)aSokav[ieak3" "%[*FbZ`nTvVQ8SF]tgN9/Vkjy7`/vtPr=;]YH]\\vUzEK!1*fPS?iA;kA}>LE,Ws;jSv.{c;=}OQ1Q\\7;{73e^P|gwp@,;DhqOW!0+,eOSJvejh\\>]" "27+u" "!/'505P=S2'yyI-Q}mUN2XfVyPe4lXz/1On]b:`+2pry.VKWr<3`&=mFL3Sk`NXj_ad3(W;Am6S!/(dT;G=CWNlrRX+*_mJV\\f!/)(09_ACDX3v1jCvoFDyT8]'6{fa^s'*mx]9aay_]A*RI(" "\\Ge7!/*&N`MXwCa]onRkdC4gNL0w6n/JY2Cqqoy(>Kx>EC(NJaW!/+;L\\<\\SDETPRe/XdK" ")T;DnzZidbX7" "!.'HBU|I|YH_EO?w2T.9cI[?GY==mb$E~hZP./^Xlq7_2Xf_LS;[IQ{!.&i6:&IA5fYf9}" ",U7:4/@.c>>$7rMYDT6Vee2`zHf5.]^_2B](&@k|^bB/gy7{Dg@J67Sn2)]+6HR9!.(?S\\$" "f.I@hpuG9UEkGbGfpVS\\P>^$_Wu=Ha:>Ixscd(!.)4lZ[bcdOOAA+NOI\\" "oW/wA']7p1Jg0mUT)eXcHY1i+;76gF_^nfT7!.*k/FNC(U[y]r/1{L0%8L[]]>s*0iBZR`6" "^YBzRC2m);LxA,>&J4JpTv-9cK;of]0_4:XorM3kzrFK{CR_P1hw:Ol2s+f}!.+0OF0!.%" "8{U}" "!-'g}UlaGZZfiREr$LS[?HznU^LV=^LV=K;3^DV9bKq8u_lXq0%BS[7N4nVQS!-)" "tLEmkWF0lCkd?s^wJiZy9|FX59Af|\\h;n=t2;u@>`}13Vd=@b.)VMl3S^PI\\!-**|Kq1@L>" "ASm-hLr3YoshG/qgLK1HZLqb/[R)O4lY$i$[v2%0[" "ieLu3t;rg>c^5HIr8d?==uDw$Z:0I)^%/'LWnlbWzx:GH;PL!-+ynM8.al|n9Z=O8sb!-%" "94Oe" "!,'(=5Gck`PsiS^5=OC@pL9l]H,%^;3DmZ,NiV=rZe[)d8O.ukH.,YazE3C2VClR`0n?`jG=j7->lb:jpr`6y\\J7}}TM5qOGDx_HQUD^a;7y" "k:XVkFN1tugnxfP{6|o:P0T=TK4JgrR*Z,jzZt8>" "[:?K\\n:E&{]1@R^Ncg6J*PjE-|Q46TWj=uJ~C3*UDk;bWrXw]E3[s1HHGw3GM:GrNnH3YSN%t2Zw" "uPnI" "!+'*)ee,r.a/IE69*U__@Z+-}.s.(=+15WPAB9oKf)dP32IR./&^W/qkr>)ku=Cr;CVxG;0.QI6H}IYQCUFzB6]iA8Onf<+uW]pwScu|jSo3yi88;M.DE;S^A`dqvDF|lg5?GwXM/2C>veJDwy*CP22\\~@+`vYUh)Wr?kYAKtjF[>wNmUyLW-" "8bS~AnEXb1f91*n~9?P'K;cEz&@g{[a}?`buz'nKaOsrdeuciaAZ8{bd*:p/Ji" "!*'(?|1(Ch^-592.,n9BR.cC?*d+mWPFxEhs%|1S&I_a,AUL;U?L>%HPJ$V%mjZ&mpYio$?E|B>-" "!)'8jZAUGN:cHV-6@N>{`1Av8GNH`k[`C&ec^+gJ`.zjve{M@g'X\\f/1aEiX^R:TeKNOfgC,}amj$9HC7'?<>x)ii}BBz1XD8KRW8|NZ@}BmI%l)_\\DC`kC(e~9ijy\\CklLN(2q" "UT3([CYQ\\.q~]q=^mWDX{W\\t$BN3%*dE41j3B$GnMa1ll]I'q1b^+-R[8hME@bC[DgMHFh>?OK5s" "TK3K^HK}_Wf:f0L{gVO3k'S.'Un[/7[b/7[b/{do2Itb:h[+AX86MXTbN7\\W_E^(`94.ahLoxrjl" "!&'6z[m??C*?xi]@OBoSR=%^p:a_MD0gUS6g}UFiO1ri\\;qo,^u(\\]@2\\Dd5/XL6LV,:01N=xW[" "@4ATSw4iWb7>W}qF`88Gc0K\\dsc`h_CPzTj((N[T.8^s3$E$=*U|?1Cp?H>^De[hK845Ky3JQ*TA" "SNM%Sa8ZW@lEjenelxQ4x-Qd}t]Y*qKa1,ik:uIUFLpk^h?mgSYGrq@tuIj[" "!%'7(Xe=}FzB,P@H3SCa>)_i$LUm>srq8XCu,G=+5/4+kBE4aXF5NX|ET6_G7BZGNEpSs2SViMu" "gpB8mf`,sJV+u0M,vfOyvf4\\y:GZzuEk" ; static int slow_stars = 0; void initstars() { char *cp; float ra, dec, cd; int maxstars = 5000; int maxsizes = 32; float *stars = NewA(float, maxstars*3); float *starp; int nstars = 0; struct starsize *sizes = NewA(struct starsize, maxsizes); struct starsize *sizep; int i, nsizes = 0; char line[512]; FILE *inf = myfopen("stars.illi", "rb"); float r = caveyes ? 90. : 1.; Point sp; static Matrix startfm = { 0,0,-1,0, -1,0,0,0, 0,1,0,0, 0,0,0,1 }; #if defined(sgi) || defined(__sgi) const GLubyte *rend = glGetString(GL_RENDERER); if(rend==NULL || (rend[0]=='X'||rend[0]=='G')) slow_stars = 1; #endif starp = &stars[0]; sizep = &sizes[0]; sizep->size = 1; sizep->base = 0; sizep->color = 3; nsizes = 0; cp = stardata; do { if(inf) { line[sizeof(line)-1] = '\0'; if(fgets(line, sizeof(line)-1, inf) == NULL) break; cp = line; } for(;;) { if(*cp >= '$') { if(nstars >= maxstars-1) { maxstars *= 3; starp = NewA(float, 3*maxstars); memcpy(starp, stars, nstars*3*sizeof(float)); stars = starp; starp = &stars[nstars*3]; } ra = ((cp[0] - SZERO)*SRADIX + (cp[1] - SZERO)) / SRASCALE; dec = ((cp[2] - SZERO)*SRADIX + (cp[3] - SZERO) - SDECBIAS) / SDECSCALE; cd = cosf(dec); sp.x[0] /*starp[2]*/ = -r * cd * cosf(ra); sp.x[1] /*starp[0]*/ = -r * cd * sinf(ra); sp.x[2] /*starp[1]*/ = r * sinf(dec); vtfmvector( (Point *)starp, &sp, startfm ); cp += 4; starp += 3; nstars++; } else if(*cp == '#') { /* Comments. * Also, "#@" hack allows specifying star transformation. */ if(cp[1] == '@') { cp += 2; for(i = 0; i<16; i++) startfm[i] = strtod(cp, &cp); cp--; } while(*++cp != '\n' && *cp != '\0') ; } else if(*cp == '!') { sizep->count = nstars - sizep->base; if(sizep->count > 0) nsizes++, sizep++; if(nsizes >= maxsizes) { maxsizes *= 3; sizep = NewA(struct starsize, maxsizes); memcpy(sizep, sizes, nsizes*sizeof(struct starsize)); sizes = sizep; sizep = &sizes[nsizes]; } sizep->size = (cp[1] - SZERO) / (float)SSIZESCL; sizep->color = (cp[2] - SZERO); sizep->base = nstars; cp += 3; } else if(*cp == '\0') { break; } else { cp++; } } } while(inf || *cp != '\0'); sizep->count = nstars - sizep->base; nstarsizes = nsizes+1; starsizes = NewN(struct starsize, nstarsizes); memcpy(starsizes, sizes, nstarsizes*sizeof(struct starsize)); starpos = NewN(float, 3*nstars); memcpy(starpos, stars, nstars*3*sizeof(float)); } void drawstars(void) { int s, i; float v, size; struct starsize *sp; float *starp; if(getenv("NOSTARS") || !showstars) return; glMatrixMode(GL_MODELVIEW); glPushMatrix(); if(!slow_stars || zoom2) glEnable(GL_POINT_SMOOTH); glDisable(GL_DEPTH_TEST); glMultMatrixf(starmat); for(i = nstarsizes; --i >= 0; ) { sp = &starsizes[i]; size = (zoom2+1) * sp->size; glPointSize( size ); v = size < 1 ? size : 1 - (ceil(size) - size) * .25; glColor3f( v*scolors[sp->color][0],v*scolors[sp->color][1],v*scolors[sp->color][2] ); glBegin(GL_POINTS); for(s = sp->count, starp = &starpos[3*sp->base]; --s >= 0; starp += 3) glVertex3fv( starp ); glEnd(); } glPopMatrix(); glDisable(GL_POINT_SMOOTH); /*glClear(GL_DEPTH_BUFFER_BIT);*/ glEnable(GL_DEPTH_TEST); } static float last_aud_time = -1000; void audioprep() { #if SOUND if(!quiet && Faud < 0){ int soundserver; soundserver=BeginSoundServer(); if(!soundserver) { fprintf(stderr,"UDP connection to soundserver failed\n"); } else { Faud = AUDinit(audfname); if (Faud < 0) { fprintf(stderr, "Couldn't load .aud file \"%s\", sorry\n", audfname); } } } #endif /*SOUND*/ } void audioclean(){ #if SOUND if(!quiet && Faud >= 0) { AUDterminate(Faud); Faud = -1; EndSoundServer(); } #endif /*SOUND*/ } #define AUDRATE .4 /* rate limit: about 7 aud updates per second */ void audiofunc() { #if SOUND if(!quiet && Faud >= 0){ static float then = -1000; float now = timenow(); if(now - then > AUDRATE) { float topeindex = abs(tope); AUDupdate(Faud, "Opti", 1, &topeindex); then = now; } } #endif /*SOUND*/ } /* end audiofunc */ #ifdef WIN32 /* substitute getopt, sigh */ int optind; char *optarg; static int optpos; int getopt( int argc, char *argv[], char *args ) { int c; if(optind == 0) optpos = 0; if(optpos <= 1 || argv[optind][optpos] == '\0') { optpos = 1; optind++; } if(optind >= argc) return -1; if(argv[optind][0] != '-' || argv[optind][1] == '\0') return -1; char *cp; c = argv[optind][optpos++]; if(c == '\0') { optpos = 0; optind++; } cp = strchr(args, c); if(cp == NULL) { fprintf(stderr, "Unknown option '%c'\n", c); optpos = 0; return '?'; } optarg = NULL; if(cp[1] == ':') { optarg = &argv[optind][optpos+1]; optpos = 0; if(*optarg != '\0') { fprintf(stderr, "-%c%s\n", c, optarg); return c; } optind++; if(optind >= argc) { fprintf(stderr, "Option -%c needs argument\n", c); return -1; } fprintf(stderr, "-%c %s\n", c, optarg); optarg = argv[optind]; } fprintf(stderr, "-%c\n", c); return c; } #endif void arguments(int argc,char **argv) { extern char *optarg; extern int optind; int chi,err=0; char *fn; /* "w:" needs ONE number after -w, "c" means NO number follows*/ while ((chi = getopt(argc,argv,"r:f:i:p:o:stw:ck:bjg:K:dqa:PSp:RlJQ")) != -1) { switch(chi) { case 'b': bwflag = 1; break; case 'k': thick0 = atoi(optarg);break; // case 'c': caveyes=1; break; this switch is easier in the cube case 'c': eyes==console; break; case 'j': jflag=1; break; case 'w': win=atoi(optarg); break; case 'g': gap0 = atof(optarg); break; case 'r': rotn0 = atoi(optarg); phase0 = 2*(rotn0&1); break; /*phase0 and gap0 exist because we cannot put things into shared memory before we allocate it. We can't allocate shared memory until after arguments, because we only learn whether or not we are in the cave in arguments.*/ case 's': swapp = 1; break; /* does not accumulate topes */ case 'S': singlebuffer = 1; break; /* single-buffered graphics, for more bit depth */ case 't': showstars0 = !showstars0; break; case 'f': filename = optarg; break; case 'K': arenasize = (atoi(optarg) + 10) * 1024; break; case 'd': debug++; break; case 'q': quiet = 1; break; case 'a': quiet = 0; audfname = optarg; break; case 'P': nosproc = 1; break; case 'p': playfname = strdup(optarg); printf("Press F3 key to play %s after topes are loaded.\n", playfname); break; case 'R': recmode = 1; win = 4; break; case 'l': loop0 = 2; break; case 'J': ribbonflag = 1; break; /* supposedly less ragged ribbons */ case 'Q': quadbinoc = 1; break; /* quadbuffered stereo mode */ default: fprintf(stderr, "Unrecognized option \"%c\"\n", chi); err = 1; break; } } if(thick0 == 0) thick0 = caveyes ? 1 : 3; if(filename == NULL && optind == argc-1) filename = argv[optind++]; if(filename != NULL && strcmp(filename,"-") != 0 && access(filename, 0) < 0) { char *fn = (char*)malloc(strlen(filename)+5); sprintf(fn, "%s.mov", filename); if(access(fn, 0) == 0) filename = fn; } if (optind != argc || err) { fprintf(stderr,"Usage: %s [options ...] \n\ Play a movie of Evolver Geomview snapshots (containing binary OFF models).\n\ Options:\n\ -c run in CAVE mode (else desktop)\n\ -w N desktop window style: 0: small; 1: 640x480; 2: fullscreen; 3: 1280x960\n\ -k N draw self-intersection curve N pixels wide\n\ -g F initial inter-facet gap (-1..1).\n\ -r N Z-axis rotational symmetry (else look for \"#...Rotn=N...\" comments)\n\ -S draw in single-buffered mode (for better image snapshots on SGIs)\n\ -t [select type of audio accompaniment?]\n\ -K select shared-arena size in Kbytes (default: guess from file size)\n\ -d enable debugging messages\n\ -a file.aud play VSS sound (requires custom vss 3.0 & e.g. \"trance.aud\")\n\ -l loop in movie mode (0..N->0 not 0..N..0..-N..0); like typing \"2m\"\n\ -P no sproc(): load all topes before starting to display\n", argv[0]); } } void dataprep(void){ audioprep(); rotn = rotn0; /* Set rotn just once at startup, not in deFault() */ deFault(); initstars(); initcircle(); } int number, hasnumber, decimal, sign; float getnumber(float dflt) { float v = (sign ? -number : number); if(!hasnumber) return dflt; return decimal>0 ? v/(float)decimal : v; } /* * Increment val by the relative amount 'incr', and round it to some * nice precision based on the amount of the increment * so e.g. 1.4 bumped by .1 yields 1.5, not 1.54. * Positive incr's => multiply by 1+incr; negative => divide by 1+abs(incr) * so e.g. bump(&x, .3) and bump(&x, -.3) are inverses of each other, * except for decimal rounding. * If a numeric prefix was typed, set *val to exactly that instead. */ void bump(float *val, float incr) { float by; char code[32], *fmt; if(hasnumber) { *val = getnumber(0); return; } by = fabs(incr); if(by <= .003) fmt = "%.3e"; else if(by <= .03) fmt = "%.2e"; else fmt = "%.1e"; sprintf(code, fmt, (incr > 0) ? *val * (1+by) : *val / (1+by)); sscanf(code, "%f", val); } #ifdef CAVE void raisetty(int up) { char *wid = getenv("WINDOWID"); int win; if(wid) { win = strtol(wid, NULL, 0); if(win) { Display *dpy = XOpenDisplay(NULL); if(dpy) { if(up) XRaiseWindow(dpy, win); else XLowerWindow(dpy, win); } XCloseDisplay(dpy); } } } #endif static void (*promptfunc)(char *); static char *promptstr; static char promptinp[150]; static int promptnc; void promptdone() { char *p, *q; for(p = promptinp; *p!='\0' && isspace(*p); p++) ; for(q = promptinp+promptnc; --q>=p && isspace(*q); ) ; *(q+1) = '\0'; clock_tick(); /* Time has moved on! */ glutIdleFunc(idle); glutKeyboardFunc(keyboard); glutDisplayFunc(drawcons); if(promptfunc != NULL) (*promptfunc)(p); } void promptkeyboard(unsigned char ch, int x, int y) { glutPostRedisplay(); if(ch == ('U'&0x1F)) { /* ctrl-U : line erase */ promptnc = 0; } else if(ch == 27) { /* ESC : cancel */ promptnc = -1; promptdone(); } else if(ch == '\b'||ch == 0x7F) { /* backspace or DEL */ if(promptnc>0) promptnc--; } else if(ch == '\r' || ch == '\n') { /* CR or NL */ promptdone(); } else if(ch >= ' ') { /* other printing character */ promptinp[promptnc++] = ch; if(promptnc >= sizeof(promptinp)-1) promptnc = sizeof(promptinp)-2; } } void promptidle() { fd_set fds; struct timeval now; FD_ZERO(&fds); FD_SET(fileno(stdin), &fds); now.tv_sec = now.tv_usec = 0; if(select(fileno(stdin)+1, &fds, NULL, NULL, &now) > 0) { fgets(promptinp, sizeof(promptinp), stdin); promptnc = strlen(promptinp); promptdone(); } } void promptdisplay(void) { if (bwflag) glClearColor(1.,1.,1.,0.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glGetIntegerv( GL_VIEWPORT, glvp ); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glOrtho( -1, 1, -1, 1, -1, 1 ); glColor3f(1,1,.5); char2wall(-.95, .9, 0, promptstr); glColor3f(1,1,1); promptinp[promptnc] = '\0'; char2wall(-.95, .75, 0, promptinp); glutSwapBuffers(); } void promptname( char *prompt, void (*func)(char *) ) { setpark(1); fputs("\7\n", stderr); fputs(prompt, stderr); fputc('\n', stderr); #ifdef TCIFLUSH signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); tcflush(fileno(stdin), TCIFLUSH); /* Flush pending input -- presumably junk */ #endif /*TCIFLUSH*/ promptstr = prompt; promptfunc = func; promptnc = 0; glutKeyboardFunc(promptkeyboard); glutDisplayFunc(promptdisplay); glutIdleFunc(promptidle); } void exitus( char *ans ) { if (*ans=='y' || (*ans == '\0' && promptnc<0)) { /* "y" or ESC ESC */ audioclean(); #if unix if(child) kill(child,9); #endif exit(0); } /* ESC exit */ } void traceon( char *fname ) { char hostname[128]; int append; if(fname == NULL || fname[0] == '\0') { traceoff(); return; } setpark(1); mkdir("album", 0777); if(tracefile != NULL) fclose(tracefile); append = access(fname, 1) == 0; tracefile = myfopen(fname, "a+b"); #ifdef WIN32 strcpy(hostname, "Win32 box"); #else gethostname( hostname, sizeof(hostname) ); #endif if(tracefile == NULL) { fprintf(stderr, "\7Not tracing, couldn't open %s: ", fname); perror(""); } else { time_t now = time(NULL); fprintf(tracefile, "# Tracing avn %s by %s@%.127s at %s", filename, getenv("LOGNAME"), hostname, ctime(&now)); fprintf(stderr, "%s to %s; press \"/\" key to stop.\n", append ? "APPENDING trace" : "Tracing", fname); tracing = 1; tracecount = 0; tracecut(); traceall(); tracewhere(); if(tracefname) free(tracefname); tracefname = strdup(fname); } } void traceoff() { if(tracefile != NULL) { fprintf(stderr, "End trace -- recorded %d frames.\n", tracecount); traceall(); tracecut(); fclose(tracefile); tracefile = NULL; } tracing = 0; tracecount = 0; } void tracewhere() { Point quat, squat, trans, tquat, ntquat; static Point oquat; static float otyme = -999; Matrix taff; if(tracefile == NULL) return; tracecount++; tfm2quat( &quat, aff ); tfm2quat( &squat, starmat ); vgettranslation( &trans, aff ); quat2tfm( taff, &quat ); tfm2quat( &tquat, taff ); if(tdist( taff, aff ) > .01 || qdist( &tquat, &quat ) > .001) { int i; fprintf(stderr, "Yeow: tfm2quat( %.7g %.7g %.7g ) = %.7g %.7g %.7g\n", quat.x[0],quat.x[1],quat.x[2], tquat.x[0],tquat.x[1],tquat.x[2]); fprintf(stderr, "For matrix:\n"); for(i=0;i<4;i++) fprintf(stderr, "%12.8g %12.8g %12.8g %12.8g\n", aff[4*i],aff[4*i+1],aff[4*i+2],aff[4*i+3]); } if(tymenow - otyme < 2.0) { if(qdist(&quat, &oquat) > .2 || (quat.x[0]==0&&quat.x[1]==0&&quat.x[2]==0)) { fprintf(stderr, "Hop: %.3f @ %.6f %.6f %.6f\n -> %.3f @ %.6f %.6f %.6f\n", otyme, oquat.x[0],oquat.x[1],oquat.x[2], tymenow, quat.x[0],quat.x[1],quat.x[2]); quat.x[0] = quat.x[0]; /* set breakpoints here */ } } otyme = tymenow; oquat = quat; fprintf(tracefile, "%.3f @ %.7f %.7f %.7f %.7f %.7f %.7f %.7f %.7f %.7f\n", tymenow, quat.x[0], quat.x[1], quat.x[2], trans.x[0], trans.x[1], trans.x[2], squat.x[0], squat.x[1], squat.x[2]); } void traceall() { char *str = "hvwnlaeiopsqgtrkxd[]BRThj"; while(*str) trace(*str++); tracewhere(); } void tracecut() { trace('C'); } void trace(int ch) { int iv; float fv; char buf[128]; #define PUTINT(i) iv = i; goto putint #define PUTFLOAT(f) fv = f; goto putfloat if(tracefile == NULL) return; switch(ch) { case '-': ch = '+'; /* and fall into '+' */ case '+': PUTINT(step); case 'h': PUTINT(dotube); case 'j': PUTFLOAT(tuberadius); case 'v': PUTINT(binoc); case 'w': /*PUTINT(msg);*/ break; case 'n': PUTFLOAT(nose); case 'l': PUTINT(locus); case 'a': PUTINT(alfa*255); case 'i': PUTFLOAT(mysiz); case 'o': PUTFLOAT(focal); case 'p': PUTFLOAT(farclip); case 's': PUTFLOAT(speed); case 'q': PUTFLOAT(torq); case 'g': PUTFLOAT(gap); case 'z': traceall(); break; case 't': PUTINT(swapp); case 'T': PUTFLOAT(realtope); case 'r': PUTFLOAT(dlradius); case 'k': PUTINT(thick); case 'x': PUTFLOAT(boxr); case 'd': PUTFLOAT(boxrange); case '[': PUTINT(boxclip); case ']': PUTINT(boxtrack); case '=': PUTINT(boxshow) ; case 'B': fprintf(tracefile, "%.3f %c %g %g %g\n", tymenow, ch, box0.x[0], box0.x[1], box0.x[2]); break; case 'R': PUTINT(rotn); case 'C': PUTINT(0); } return; putint: fprintf(tracefile, "%.3f %c %d\n", tymenow, ch, iv); return; putfloat: fprintf(tracefile, "%.3f %c %g\n", tymenow, ch, fv); } void playon( char *fname ) { char *tf; playoff(); if(fname == NULL || fname[0] == '\0') return; playpipe = (fname[0] == '|'); playfile = playpipe ? popen(fname+1, "rb") : myfopen(fname, "rb"); if(playfile == NULL) { fprintf(stderr, "Can't open %s: ", fname); perror(""); return; } tf = strdup(fname); if(playfname) free(playfname); playfname = tf; playing = 1; curkey.ptime = prevkey.ptime = -999999; skipsnap = 1; setpark(1); if(!caveyes) idle(); /* Fetch first frame */ } void playoff() { if(playfile != NULL) { if(playpipe) pclose(playfile); else fclose(playfile); if(recmode) exit(0); } playfile = NULL; playing = 0; playsynched = 0; } /* Seek to the corresponding place in the trace-file if any; * set all non-interpolated settings to their values there; * set "prevkey" and "curkey" to the values at the adjacent keypoints. * Returns the interpolation fraction: use (1-frac)*prevkey + frac*curkey. */ #define PTIMEDELTA .003 float playtime( float tyme ) { char line[256]; float ptime, val; char chs[4]; char ch; float rtope = curkey.rtope; if(!playing || playfile == NULL || feof(playfile)) return 0; if(prevkey.ptime >= 0) { if(!playsynched) { playtimebase = prevkey.ptime - tymenow; playsynched = 1; } if(tyme + playtimebase + PTIMEDELTA <= curkey.ptime) { /* OK, it's between prevkey and curkey. */ val = (tyme + playtimebase - prevkey.ptime); return val <= 0 ? 0 : val / (curkey.ptime - prevkey.ptime); } } while(fgets(line, sizeof(line), playfile) != NULL) { if(sscanf(line, "%f %3s %f", &ptime, &chs, &val) != 3) continue; ch = chs[0]; if(ch == '^') ch = CTRL(chs[1]); switch(ch) { case '+': /* ignore step */ break; case 'h': if(val != 1) dotube = val; break; case 'j': tuberadius = val; break; case CTRL('r'): circradius = val; break; case CTRL('s'): circscale = val; break; case CTRL('h'): canlength = val; break; case CTRL('j'): tubespecular = val; break; case CTRL('k'): tubediffuse = val; break; case CTRL('l'): docans = val; break; case 'v': binoc = val; break; case 'w': /*msg = val;*/ break; case 'n': nose = val; break; case 'l': locus = val; break; case 'a': alfa = val/255.; break; case 'i': mysiz = val; break; case 'o': focal = val; break; case 'p': farclip = val; break; case 's': speed = val; break; case 'q': torq = val; break; case 'g': gap = val; break; case 't': swapp = val; break; case 'T': rtope = val; break; case 'r': dlradius = val; break; case 'e': oneside = (int)val; break; case 'k': thick = val; break; case 'x': boxr = val; break; case 'd': boxrange = val; break; case '[': boxclip = val; break; case ']': boxtrack = val; break; case '=': boxshow = val; break; case 'B': sscanf(line, "%*f %*c %f %f %f", &box0.x[0],&box0.x[1],&box0.x[2]); break; case 'C': playsynched = 0; break; case '@': if(prevkey.ptime >= 0) prevkey = curkey; sscanf(line, "%f @ %f %f %f %f %f %f %f %f %f", &curkey.ptime, &curkey.quat.x[0], &curkey.quat.x[1], &curkey.quat.x[2], &curkey.transl.x[0], &curkey.transl.x[1], &curkey.transl.x[2], &curkey.squat.x[0], &curkey.squat.x[1], &curkey.squat.x[2]); curkey.rtope = rtope; curkey.kboxr = boxr; curkey.kbox0 = box0; if(prevkey.ptime < -99999) prevkey = curkey; if(!playsynched) { playtimebase = prevkey.ptime - tymenow; playsynched = 1; } if(tyme + playtimebase + PTIMEDELTA <= curkey.ptime) { /* OK, it's between prevkey and curkey. */ val = (tyme + playtimebase - prevkey.ptime); return val <= 0 ? 0 : val / (curkey.ptime - prevkey.ptime); } } if(tracefile != NULL && (ch != 'T' && ch != '@')) trace(ch); } playoff(); fprintf(stderr, "\7End playback. Paused -- press END key to resume.\n"); setpark(1); return 1; } /*gkf: autotymer is never used in avn */ void autotymer(int reset) { #define TYME(cnt,max,act) {static cnt; if(first)cnt=max; else\ if(cnt?cnt--:0){ act ; goto Break;}} static int first = 1; /* the first time autymer is called */ if(reset)first=1; /* or if it is reset to start over */ /* TYME( shrink , 150,th0++;th1--;ta0++;ta1--) */ /*TYME( pause , 20, ) */ /*TYME( grow , 150,th0--;th1++;ta0--;ta1++) */ /*TYME( dwell , 30, ) */ /*TYME(finish , 1 , first = 1 ) */ first = 0; /*Break: ; /* yes Virginia, C has gotos */ } /*Beware of competing keys in CAVEsimulation and wnd==1 */ /* you may want to remap these keys to avoid the conflict */ /* console-mode keyboard */ void keyboard(unsigned char key, int x, int y) { #define IF(K) if(key==K) #define PRESS(K,A,b) IF(K){b;trace(K);}IF((K-32)){A;trace(K);} #define TOGGLE(K,flg) IF(K){(flg) = getnumber(1-(flg)); trace(K); } #define CYCLE(K,f,m) PRESS((K), (f)=getnumber(((f)+(m)-1)%(m)),(f)=getnumber(++(f)%(m))) #define SLIDI(K,f,m,M) PRESS(K,(--fM?M:f)) #define SLIDF(K,f,m,M,d) PRESS(K,f = ((f-=d)M?M:f)) /* Only ASCII characters can be processed by this GLUT callback function */ IF('+') step = getnumber(1); CYCLE('v',binoc,3); /* cross-eyed */ /*gfk: the front/back view seems to be broken */ TOGGLE('w',msg); /* writing on/off */ PRESS('n', bump(&nose, -.05), bump(&nose, .05) ); /* for binoculars */ TOGGLE(' ', mode); /* fly/turn modes */ CYCLE('l',locus,4); PRESS('k', thick -= (thick>0), thick = getnumber(thick+1)); if(key == 'T' || key == 't') { realtope = settope(getnumber(realtope)); } PRESS('b', bump(&movierate, -.1), bump(&movierate, .1)); PRESS('y', bump(&tickrate, -.1), bump(&tickrate, .1)); IF('m') movie = getnumber(movie ? 0 : loop0); /* toggles homotopy animation */ IF('M') { fore = getnumber(-fore); if(fore==0) fore = 1; } PRESS('a', bump(&alfa, -.1), bump(&alfa, .1)); PRESS('c', bump(&closer, -.2), bump(&closer, .2)); /* push double-locus closer to eye */ CYCLE('e', oneside,4); PRESS('i', bump(&mysiz, -.1), bump(&mysiz, .1)) /* rescale the world */ PRESS('o', bump(&focal, -.1), bump(&focal, .1)) /* telephoto */ PRESS('p', bump(&farclip, .01), bump(&farclip, -.01)) /* rear clipping plane */ PRESS('s', bump(&speed, -.02), bump(&speed, .02)); /* flying speed */ PRESS('q', bump(&torq, -.02), bump(&torq, -.02)); /* turning speed */ PRESS('g', bump(&gap, .02), bump(&gap, -.02)); /* gap parameter */ PRESS('r', bump(&dlradius, -.1), bump(&dlradius, .1)); /* double-locus tube radius */ PRESS('z', deFault(), deFault()); /* zap changes */ IF('h') { dotube = getnumber( dotube ? 0 : 8 ); trace('h'); } IF(CTRL('h')) canlength = getnumber( canlength * 1.1 ); PRESS('j', bump(&tuberadius,-.1), bump(&tuberadius, .1)); /* tube radius */ IF(CTRL('r')) circradius = getnumber(circradius*1.1); IF(CTRL('s')) circscale = getnumber(circscale*1.1); IF(CTRL('j')) tubespecular = getnumber(tubespecular*1.1); IF(CTRL('k')) tubediffuse = getnumber(tubediffuse*1.1); IF(CTRL('l')) docans = getnumber( !docans ); IF('W') bump(&nullwidth, 1); IF(CTRL('w')) { /* ctrl-W */ int wsize = getnumber(0); glutPositionWindow(0, 0); if(wsize) glutReshapeWindow(wsize, wsize); else glutReshapeWindow((zoom2+1)*640, (zoom2+1)*480); } IF(CTRL('y')) { glutReshapeWindow( xt, getnumber(yt) ); } IF(CTRL('p')) { tilesnaps = getnumber(0); } IF(CTRL('s')) { showstars = getnumber(!showstars); } IF(CTRL('q')) stopreading = 1; /* ctrl-Q tells reader-process we've read enough topes */ IF('@') { FILE *otrace = tracefile; tracefile = stdout; tracewhere(); tracecount--; tracefile = otrace; } #if SOUND if (!quiet){ IF('!') { fprintf(stderr, "audioclean called\n"); audioclean(); } IF('$'){ fprintf(stderr, "audioprep called\n"); audioprep(); } } #endif /* gkf: viewbox control starts here */ /* samples PRESS('g', bump(&gap, .02), bump(&gap, -.02)); */ /* samples TOGGLE('m',movie);*/ PRESS('x', bump(&boxr,.02), bump(&boxr,-.02)); IF('[') { boxclip = getnumber(!boxclip); trace('['); } PRESS('d', bump(&boxrange,.02), bump(&boxrange,-.02)); TOGGLE('\\',boxinterp); TOGGLE(']',boxtrack); TOGGLE('=',boxshow); TOGGLE('f',tymode); IF(':') { /* ":" selects continuous trace mode */ if(tracing) tracing = 1; } IF(';') { if(tracing>0) tracing = -1; else if(tracefile != NULL) { trace('T'); tracewhere(); fflush(tracefile); } } IF('>') { tracelimit = getnumber(1<<30); promptname( "trace to file [Return to cancel]: ", traceon ); } IF('/') traceoff(); IF('<') promptname("play back from file [Return to cancel]: ", playon); IF(27) promptname("really exit? [ny]: ", exitus); /* ESC exit */ /* gkf: this is hard to read and what are the semicolons for? */ IF('?') { fprintf(stderr, "Keyboard shortcuts, among others:\n\ [ : Toggle([) boxclip and adjust its radius with the bo(X)key \n\ ] : Toggle(]) boxtrack and adjust its (D)istance before camera \n\ = : Toggle(=) boxshow \n\ ! : Severs the soundserver. Reconnect a healthy vss thu($) \n\ l : show/hide double locus & facets near it\n\ w : show/hide screen Writing r/R : adjust \"near double locus\" tube radius\n\ y/Y : adjust simulated-time rate (f/s) b/B : adjust movie rate (tope/s)\n\ f : toggle simulated/real time m : play/pause tope movie\n\ g : adjust inter-facet gap G : cycle type of gap\n\ PAGEDOWN : save frames to album/NN.NNN.ppm.gz until PAGEUP\n\ PAGEUP : save one frame to album/NN.NNN.ppm.gz (or stop PAGEDOWN)\n\ W : (capital-W) adjust null-zone width (pixels)\n\ F2 : toggle 2x zoom for snaps\n\ F3 : replay last played file\n\ > : save state stream to given file (type name on console) (until \"/\")\n\ / : end writing trace\n\ < : play back trace file\n\ @ : dump current position to stdout\n\ HOME : freeze activity END : thaw activity\n\ h : draw tubes j: set tube width ^L: use tin-cans ^H: excess can length\n\ ^S: toggle stars ^K: tube lightness ^J: tube spec expon\n\ nnn^W : make window nnn pixels wide (default 640x480 or 1280x960 from F2 zoom)\n\ nnn^Y : make window nnn pixels high\n\ nnn^P : render tiled image, nnn times larger than current window\n"); } if(key >= '0' && key <= '9') { hasnumber = 1; number = number*10+key-'0'; decimal *= 10; } else if(key == '.') { decimal = 1; } else if(key == '-') { sign = -1; } else { hasnumber = number = decimal = sign = 0; } glutPostRedisplay(); } /* more console-mode keyboard */ void special_keybo(int key, int x, int y){ /* non-ASCII keypresses go here -- see glut.h for their names */ switch(key) { case GLUT_KEY_HOME: setpark( 1 ); break; case GLUT_KEY_END: setpark( 0 ); break; case GLUT_KEY_PAGE_DOWN: snap = getnumber(9999); snapno = -1; break; case GLUT_KEY_PAGE_UP: if(snap>0) {snap = 0, snapno = -1;} else snap = 1; break; case GLUT_KEY_RIGHT: step = getnumber(1); tymer(); break; case GLUT_KEY_LEFT: step = -getnumber(1); tymer(); break; case GLUT_KEY_UP: bump(&movierate, .1); break; case GLUT_KEY_DOWN: bump(&movierate, -.1); break; case GLUT_KEY_F1: tymode = getnumber(!tymode); break; case GLUT_KEY_F2: zoom2 = getnumber(!zoom2); break; case GLUT_KEY_F3: if(playfname) playon(playfname); break; case GLUT_KEY_F4: if(tracefname) { tracelimit = getnumber(1<<30); traceon(tracefname); } break; default: fprintf(stderr," non-ASCII character was pressed. [%d]\n",key); fprintf(stderr," use special_keybo() to process it\n"); } hasnumber = number = decimal = 0; glutPostRedisplay(); } void char2wall(GLfloat x,GLfloat y, GLfloat z, char buf[]) { char *p, *pbase; static GLuint fontbase[2] = { 0, 0 }; static void *glutfont[2] = { GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_9_BY_15 }; int font = (glvp[3] > 450) ? 1 : 0; int onwall = (z <= -5.0 && z >= -6.0); int n; /* Initialize font if not already done */ if(fontbase[font] == 0) { fontbase[font] = glGenLists( 96 ); for(n = 0; n < 96; n++) { glNewList( fontbase[font] + n, GL_COMPILE ); glutBitmapCharacter( glutfont[font], n + ' ' ); glEndList(); } } /* Fake out 3-D position. * Force the text to lie on the current wall, * and transform it so that existing char2wall() values give * sensible positions. * In particular: * if z = -5, assume caller considers the wall to be -5<=x,y<=5, z=-5. * Otherwise take it literally. */ if(onwall) { glPushMatrix(); glLoadIdentity(); glMatrixMode( GL_PROJECTION ); glPushMatrix(); glLoadIdentity(); glOrtho( -5, 5, 0, 10, -10, 10 ); } glRasterPos3f(x,y,z); glListBase(fontbase[font] - ' '); for(p = pbase = buf; *p; p++) { if(*p < ' ' || *p >= ' ' + 96) { if(p > pbase) glCallLists(p - pbase, GL_UNSIGNED_BYTE, pbase); pbase = p+1; } } if(p > pbase) glCallLists(p - pbase, GL_UNSIGNED_BYTE, pbase); if(onwall) { glPopMatrix(); glMatrixMode( GL_MODELVIEW ); glPopMatrix(); } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */ #ifdef CAVE /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */ void cavekeybo(void){ #define CAVEIF(K) if(CAVEgetbutton(K)) #define CAVESOAK(K) while(CAVEgetbutton(K)) usleep(10000) #define CAVEPRESS(K,A,b) CAVEIF(K){if(CAVEgetbutton(CAVE_LEFTSHIFTKEY)||\ CAVEgetbutton(CAVE_RIGHTSHIFTKEY)){b;}else{A;}} #define CAVESTEP(K,A,b) CAVEPRESS(K,A,b); CAVESOAK(K); #define CAVETOGGLE(K,flg) CAVEIF(K){(flg) = 1-(flg); CAVESOAK(K); } #define CAVECYCLE(K,f,m) CAVEPRESS((K),(f)=(((f)+(m)-1)%(m)),(f)=(++(f)%(m)) ); CAVESOAK(K); #define CAVESLIDI(K,f,m,M) CAVEPRESS(K,(--fM?M:f)) #define CAVESLIDF(K,f,m,M,d) CAVEPRESS(K,((f -= d)M?M:f)) CAVETOGGLE(CAVE_MKEY,movie) /* toggles homotopy animation */ /* I hope this is the way to install the hkey and jkey in the CAVE. */ CAVEIF(CAVE_HKEY){ dotube = getnumber( dotube ? 0 : 8 ); trace('h'); } CAVEPRESS(CAVE_JKEY, bump(&tuberadius,-.1), bump(&tuberadius, .1)); /* tube radius */ CAVEPRESS(CAVE_PKEY, farclip *= 1.01, farclip /= 1.01) /* rear clipping plane*/ CAVEPRESS(CAVE_QKEY, speed /= 1.02, speed *= 1.02);/* flying speed */ CAVEPRESS(CAVE_TKEY, torq /= 1.02, torq *= 1.02); /* turning speed */ CAVEPRESS(CAVE_FKEY, tymode = !tymode, tymode = 1); CAVEPRESS(CAVE_F1KEY, tymode = !tymode, tymode = 1); CAVETOGGLE(CAVE_YKEY,wnd); /* mauspaw vs wandpaw */ CAVESTEP(CAVE_RKEY, dlradius++; locus+=(locus==0), dlradius -= (dlradius>0)); CAVECYCLE(CAVE_LKEY, locus, 4); CAVEIF(CAVE_GKEY) { gap = (gap>0) ? -gap : 1; CAVESOAK(CAVE_GKEY); } CAVEIF(CAVE_F3KEY) { if(playfname) playon(playfname); } /* not yet implemented */ CAVEIF(CAVE_COMMAKEY) { if(CAVEgetbutton(CAVE_LEFTSHIFTKEY)||CAVEgetbutton(CAVE_RIGHTSHIFTKEY)) { raisetty(1); promptname( "play back from file [Return to cancel]: ", playon); raisetty(0); } } CAVEPRESS(CAVE_ZKEY, deFault(), deFault()); /* zap changes */ } void graffiti(void){ /* CAVE messages; used to be called speedo */ char buf[256]; /* messages written into here */ #define LABEL3(x,y,z,W,u){sprintf(buf,(W),(u));char2wall(x,y,z,buf);} #define ZWALL -5 static float last=0.,fps; /*for measuring time */ if(!CAVEMasterDisplay()) return; /* USE CAVEMasterWall in papeBeta */ glGetIntegerv( GL_VIEWPORT, glvp ); if(floor(2.*(*CAVETime))>floor(2.*last)){ /* rationalize this */ last = *CAVETime; fps = *CAVEFramesPerSecond; } if(mode==TURNMODE){ glColor3f(0.,1.,1.); /* cyan is easier to see than magenta gkf1jan98*/ LABEL3(-3.8,1.,ZWALL,\ "%s","MODE = click(M), TRACTOR = hold(M), EVERT = click(L)"); LABEL3(-3.8,.75,ZWALL,\ "%s","RESIZE = hold(M)click(L|R), REGAP = hold(R)click(L|M)"); LABEL3(-3.8,.5,ZWALL, \ "%s","RESET = hold(M)click(L+R) %s"); } else { glColor3f(1.,1.,0.); LABEL3(-3.8,1.0,-5.0, "%s"," push thumb-button to fly"); LABEL3(-3.8,.75,-5.0, \ "%s","FORE = click(L) AFT = click(R) NOTURN = click(L+R) "); } LABEL3(-4.8,8.0,-5.0, "optiVert by Hartman, Bourd, Chappell,\ Francis, Sullivan and Levy (C) 1995..1998 U.Illinois %s","") if(readytogo < 2) glColor3f(1.,.25,.25); sprintf(buf, "%5.1f fps Tope %d/%d", fps, (phase&1)?-tope:tope, ntopes); char2wall(-4.8,1.25,-5.0, buf); if(topes[tope].comment) { glColor3ub(255,255,255); LABEL3(-4.8,1.5,-5.0,"%s",topes[tope].comment); } } void caveframe() { cavetrack(); tymer(); } float clampjoystick( float value, float threshold ) { if(value < -threshold) return (value + threshold) / (1 - threshold); if(value > threshold) return (value - threshold) / (1 - threshold); return 0; } void cavetrack(void) { float azi,ele,rol,hx,hy,hz,wx,wy,wz; static float ohx,ohy,ohz; /*old head position */ static float owx,owy,owz; /*old wand position */ static int opaw = 0,paw, joy = 0, friz; float sdt; /* what is this ? */ float xjoy, yjoy; if(!CAVEMasterDisplay()) return; /* doit once per frame */ clock_tick(); if(playing) { Point quat, squat, transl; float frac = playtime(tymenow); quat_lerp( &quat, frac, &prevkey.quat, &curkey.quat ); quat_lerp( &squat, frac, &prevkey.squat, &curkey.squat ); vlerp( &transl, frac, &prevkey.transl, &curkey.transl ); realtope = settope( (1-frac)*prevkey.rtope + frac*curkey.rtope ); quat2tfm( aff, &quat ); vsettranslation( aff, &transl ); quat2tfm( starmat, &squat ); if(boxinterp) { vlerp( &box0, frac, &prevkey.kbox0, &curkey.kbox0 ); boxr = (1-frac)*prevkey.kboxr + frac*curkey.kboxr; } } else { sdt = 10*deltat; /* why ? */ paw = wnd ? (CAVEBUTTON1*2+CAVEBUTTON2)*2+CAVEBUTTON3 : mauspaw; if(paw) joy = 1; /* activate joystick by clicking any button*/ if(opaw!=2 && paw==2) mode = 1-mode; /* toggle mode */ if(mode==TURNMODE){ /* hold middle button and click right to shrink */ if (opaw == 2 && paw == 3)siz /= 1.5; /*shrink size */ if (opaw == 2 && paw == 6)siz *= 1.5; /* grow size */ if (opaw == 1 && paw == 3)gap *= 1.1; /* shrink gap */ if (opaw == 1 && paw == 5)gap /= 1.1; /* grow gap */ if (opaw != 4 && paw == 4)movie = 1-movie;/* shape changer */ } if(mode==FLYMODE){ /* gkf 1jan98 */ if (opaw != 4 && paw == 4)step = 1; /* one step forward */ if (opaw != 1 && paw == 1)step = -1; /* one step backward */ if (opaw != 5 && paw == 5)friz = 1-friz; /* parking option */ } if(opaw==2 && paw == 7){ /* restart, the most useful feature */ deFault(); ohx=ohy=ohz=0; owx=owy=owz=0; } if(wnd){ CAVEGetWandOrientation(azi,ele,rol); CAVEGetHead(hx,hy,hz); /*position*/ CAVEGetWand(wx,wy,wz); /*position*/ } else { /* fix this for CAVE mouse flying */ azi = .5*512; ele = .5*-512; } glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTranslatef(ohx-hx,ohy-hy,ohz-hz); if(mode==TURNMODE) glTranslatef(aff[12],aff[13],aff[14]); if(!friz){ /* if not parked */ glRotatef(sdt*rol*(mode?torq:-torq),0.,0.,1.); glRotatef(sdt*ele*-torq, 1.,0.,0.); glRotatef(sdt*azi*-torq,0.,1.,0.); } if(joy) glTranslatef( clampjoystick( CAVE_JOYSTICK_X, .12 )*sdt*speed, 0., clampjoystick( CAVE_JOYSTICK_Y, .12 )*sdt*speed); if(opaw==2 && paw==2) glTranslatef(wx-owx, wy-owy, wz-owz); /*wand tractor */ if(mode == TURNMODE)glTranslatef(-aff[12],-aff[13],-aff[14]); glMultMatrixf(aff); glGetFloatv(GL_MODELVIEW_MATRIX,aff); if(mode==FLYMODE && !friz) { /*make the star matrix rotate*/ glLoadIdentity(); glRotatef(sdt*rol*(mode?torq:-torq),0.,0.,1.); glRotatef(sdt*ele*-torq,1.,0.,0.); glRotatef(sdt*azi*-torq,0.,1.,0.); glMultMatrixf(starmat); glGetFloatv(GL_MODELVIEW_MATRIX,starmat); } glPopMatrix(); opaw=paw; ohx=hx; ohy=hy; ohz=hz; owx=wx; owy=wy; owz=wz; if(morph) autotymer(0); /* advance autotymer */ if(ntopes>1 && !park) tymer(); } audiofunc(); calculite(); } void drawcaveinit(void){ /* kludge? needed why ? */ CAVEFar = 1000.; glClearColor(0.,0.,0.,0.); if (bwflag) glClearColor(1.,1.,1.,0.); glClearDepth(1.); glShadeModel(GL_FLAT); glEnable(GL_POLYGON_OFFSET_EXT); } void drawcave(int ltope, int lphase){ float hx,hy,hz; if (bwflag) glClearColor(1.,1.,1.,0.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawstars(); graffiti(); CAVEGetHead(hx,hy,hz); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(hx,hy,hz); glMultMatrixf(aff); glScalef(siz,siz,siz); drawall(((lphase&1)?-ltope:ltope),lphase); glPopMatrix(); } #endif /*CAVE*/ float speedometer(void){ static int ii = 0; static double rate; double dbl; static struct timeval now, then; #ifdef CAVE if(caveyes) return *CAVEFramesPerSecond; #endif if(++ii % 8 == 0){ /* 8 times around measure time */ gettimeofday(&now, NULL); /* elapsed time */ dbl = (double)(now.tv_sec - then.tv_sec) +(double)(now.tv_usec - then.tv_usec)/1000000; then = now; rate = 8/dbl; } return((float)rate); } void postit(void){ /* Console messages */ char buf[256]; /* console messages are done differently from cave */ static GLubyte bullcolor[2][3] = { 0x22,0x88,0xdd, 0xCC,0xCC,0xCC }; static GLubyte textcolor[2][3] = { 0x00,0x80,0xDF, 0xFF,0xFF,0x00 }; int i; GLfloat bull[4][2] = { -1,-1, -1,1, 1,1, 1,-1 }; //gkf: hack to fix (W) problem in the cube if(msg==0)return; // #define LABEL2(x,y,W,u) {sprintf(buf,(W),(u));} // #define LABEL22(x,y,W,u,v) {sprintf(buf,(W),u,v);} // #define LABEL23(x,y,W,u,v,w) {sprintf(buf,(W),u,v,w);} #define LABEL2(x,y,W,u) {sprintf(buf,(W),(u));char2wall(x,y,0.,buf);} #define LABEL22(x,y,W,u,v) {sprintf(buf,(W),u,v);char2wall(x,y,0.,buf);} #define LABEL23(x,y,W,u,v,w) {sprintf(buf,(W),u,v,w);char2wall(x,y,0.,buf);} glGetIntegerv( GL_VIEWPORT, glvp ); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); /* Might as well do all our work on the MODELVIEW matrix */ glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); gluOrtho2D( -glvp[2]*.5, glvp[2]*.5, -glvp[3]*.5, glvp[3]*.5 ); /*bull's eye*/ glColor3ubv( bullcolor[mode] ); glLineWidth(1); glScalef(nullwidth, nullwidth, nullwidth); glBegin(GL_LINE_LOOP); for(i = 0; i < 4; i++) glVertex2fv( &bull[i][0] ); glEnd(); glPopMatrix(); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0,3000,0,3000); /* writings */ glColor3ubv( textcolor[mode] ); LABEL22(80,80,park?"%4.1f fps tyme %.3f PAUSED":"%4.1f fps tyme %.3f", speedometer(),tymenow); if(tracing != 0) LABEL22(80,290, "Tracing to %s (%s)", tracefname, tracing<0 ? "stepwise; \";\" records step, \":\" continuous" : "continuous; \";\" to select stepwise"); if(playing) LABEL22(80,220, "Playing %s time %.3f", playfname, curplaytime); if(topes[tope].comment) LABEL2(80,150, "%s", topes[tope].comment); if(eyes==cube){ LABEL2(80,2840,\ "buttons {A}movie {B}resethand {C}gesture {X}writing %s{Y}mode {Z}ap )", mode?"fly":"turn"); LABEL2(10,10,"%s","optiVerse: 04oct05 Sullivan, Francis, Schaeffer, Levy, Bourd, Hartman (C) 1995..2004, U Illinois."); }else{ //console LABEL2(80,2840,\ "(ESC)ape (V)iew (MAUS2)Fore/Aft (BAR)%s ho(M)otopy (W)riting\ Single (+)(-)One Frame Reset=(Z)ap", mode?"turn":"fly"); LABEL2(10,10,"%s","optiVerse: Sullivan, Francis, Schaeffer, Levy, Bourd, Hartman (C) 1995..2004, U Illinois."); LABEL2(80,2770,"(N)ose %0.3f",nose); LABEL2(80,2700,"(S)peed %0.4f",speed); LABEL2(80,2630," tor(Q) %0.4f",torq); LABEL22(80,2560,"cli(P) %.3g %.2g", mysiz*focal, farclip); LABEL22(80,2490,"f(O)cal %g s(I)ze %.2g",focal,mysiz); LABEL22(80,2420,"(L)oc %d (R)adius %.3g",locus,dlradius); LABEL22(80,2350,"(B) %.3g tope/s %s (M)",movierate, movie?"says":"if"); LABEL22(80,2280,"t(Y)me %.3g f/s %s (f)", tickrate, tymode==TICKTYME ? "says":"if"); LABEL22(80,2210,"(G)ap %.2g (C)lo %.3g",gap, closer); LABEL22(80,2140,"(T)ope %d/%d", (phase&1) ? -tope : tope, ntopes); LABEL2(80,2070,"%s","? for key help"); /* gkf: boxmessage */ LABEL22(80,2000,"tog([)%d bo(X)clip %.2g",boxclip,boxr); LABEL22(80,1930,"boxtrack(])%d show(=)%d",boxtrack,boxshow); LABEL2(80,1860,"boxinterp(\\)%d",boxinterp); LABEL23(80,1790,"tube(h)%d rad(J)%.3f can(^H)%.2f", dotube, tuberadius, canlength); LABEL23(80,1720,"spec(^J)%.3g diff(^K)%.2f cans(^L)%d", tubespecular, tubediffuse, docans); } //endif console glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } void chaptrack(int but,int xx,int yy,int shif) { long dx, dy; float sdx, sdy, sdt; /* displacements scaled by time */ float bx0, bx1, bx2; /* box coordinates in console coordinates */ if(park) return; if(playing) { Point quat, squat, transl; float frac = playtime(tymenow); quat_lerp( &quat, frac, &prevkey.quat, &curkey.quat ); quat_lerp( &squat, frac, &prevkey.squat, &curkey.squat ); vlerp( &transl, frac, &prevkey.transl, &curkey.transl ); realtope = settope( (1-frac)*prevkey.rtope + frac*curkey.rtope ); curplaytime = (1-frac)*prevkey.ptime + frac*curkey.ptime; quat2tfm( aff, &quat ); vsettranslation( aff, &transl ); quat2tfm( starmat, &squat ); if(boxinterp) { vlerp( &box0, frac, &prevkey.kbox0, &curkey.kbox0 ); boxr = (1-frac)*prevkey.kboxr + frac*curkey.kboxr; } } else { dx = xx -.5*xt; dx = abs(dx)>nullwidth?dx:0; dy = yy -.5*yt; dy = abs(dy)>nullwidth?dy:0; sdt = 10*deltat; sdx = sdt*dx; sdy = sdt*dy; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); /* gkf: In order to rotate about the boxorigin and not the objectorigin * we have to conjugate by the box location, regardless how we dipped it * to get it there. */ /* gkf: once working, replace this with Cbox[012] */ {Point Abox ; vtfmpoint(&Abox, &box0, aff); bx0 = Abox.x[0]; bx1 = Abox.x[1]; bx2 = Abox.x[2];} if(mode==TURNMODE && boxclip == 0)glTranslatef(aff[12],aff[13],aff[14]); if(mode==TURNMODE && boxclip == 1)glTranslatef(bx0,bx1,bx2); glRotatef(sdx*torq,0.,1.,0.); glRotatef(sdy*torq,1.,0.,0.); if(but&(1<> 2; q[1] = (p[1] + p[3+1] + p[rowbytes+1] + p[rowbytes+3+1]) >> 2; q[2] = (p[2] + p[3+2] + p[rowbytes+2] + p[rowbytes+3+2]) >> 2; q += 3; p += 3*2; } while(--k > 0); } int snapshot(char *sub) { char fname[256]; char *fnamep = fname; char header[500]; struct viewport { GLint x, y, w, h; } vp; int i, rowlen; int step = (zoom2==1) ? 2 : 1; int owide, ohigh, junk; char *img; #ifdef ZLIB gzFile out; #else FILE *outf; #endif mkdir("album", 0777); if(snapno < 0) { for(snapno = 0; ; snapno += 10000) { sprintf(fname, snapfmt, snapno/10000, 0, ""); /* If neither NN.0000.ppm.gz nor NN.0000.00.00.ppm.gz exist, use it. */ if(access(fname, 0) < 0) { sprintf(fname, snapfmt, snapno/10000, 0, sub); if(access(fname, 0) < 0) break; } } } #ifdef ZLIB sprintf(fname, snapfmt, snapno/10000, snapno%10000, sub); out = gzopen(fname, "wb"); if(out == NULL) { fprintf(stderr, "can't write to %s: ", fname); perror(""); return -1; } #else /* no ZLIB, pipe to gzip */ strcpy(fname, "gzip >"); fnamep = fname + strlen(fname); sprintf(fnamep, snapfmt, snapno/10000, snapno%10000, sub); outf = popen(fname, "w"); if(outf == NULL) { fprintf(stderr, "can't spawn gzip to write to %s: ", fnamep); perror(""); return -1; } #endif glGetIntegerv(GL_VIEWPORT, (GLint *)&vp); rowlen = (vp.w*3 + 4-1) & ~3; img = (char *)alloca(vp.h * rowlen); glReadPixels(0, 0, vp.w, vp.h, GL_RGB, GL_UNSIGNED_BYTE, img); owide = vp.w/step; ohigh = vp.h/step; sprintf(header, "P6\n%d %d # Tope %d of %d in %.450s", owide, ohigh, topeof(realtope, &junk), ntopes, filename); i = strlen(header); while((i&7) != 2) header[i++] = ' '; strcpy(header+i, "\n255\n"); #ifdef ZLIB gzwrite(out, header, strlen(header)); for(i = vp.h; (i -= step) >= 0; ) { char *row = img + i*rowlen; if(zoom2==1) shrink2(row, vp.w, rowlen); if(gzwrite(out, row, owide*3) != owide*3) { fprintf(stderr, "Error writing row %d of %s\n", vp.h - i, fname); break; } } if(gzclose(out) != Z_OK && i < 0) { fprintf(stderr, "Error closing %s\n", fname); return -1; } #else /* no ZLIB, using external gzip */ fwrite(header, strlen(header), 1, outf); for(i = vp.h; --i >= 0; ) { char *row = img + i*rowlen; if(zoom2==1) shrink2(row, vp.w, rowlen); if(fwrite(row, owide*3, 1, outf) != 1) { fprintf(stderr, "Error writing row %d of %s\n", vp.h - i, fnamep); break; } } if(pclose(outf) && i < 0) { fprintf(stderr, "Error closing %s\n", fnamep); return -1; } #endif if(i < 0) { printf("%s\n", fnamep); return 0; } return -1; } void reshaped(int xx, int yy){ xt=xx; yt=yy; } void drawtiles(int nx, int ny) { int x, y; float xf, yf; char sub[16]; int osnapno = snapno; snapno = -1; xf = 2*(mysiz * xt/yt) / nx; yf = 2*mysiz / ny; for(y = 0; y < ny; y++) { for(x = 0; x < nx; x++) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0,0,xt,yt); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(xf*(x - .5*nx), xf*(x+1 - .5*nx), yf*(ny-1-y - .5*ny), yf*(ny-y - .5*ny), mysiz*focal, farclip); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); drawstars(); glMultMatrixf(aff); drawall(realtope,phase); if(msg) postit(); sprintf(sub, ".%02d.%02d", y, x); snapshot(sub); glutSwapBuffers(); } } snapno = osnapno; } void drawcons(void){ if (bwflag) glClearColor(1.,1.,1.,0.); else glClearColor(.06,.12,.20,1.); if(tilesnaps>0) { printf("Tiling %dx%d of %dx%d-pixel images\n", tilesnaps, tilesnaps, xt, yt); drawtiles(tilesnaps, tilesnaps); tilesnaps = 0; } if (bwflag) glClearColor(1.,1.,1.,0.); else glClearColor(.06,.12,.20,1.); glDrawBuffer(GL_BACK_LEFT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0,0,xt,yt); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-mysiz*xt/yt/(binoc?2:1),mysiz*xt/yt/(binoc?2:1), -mysiz,mysiz,mysiz*focal,farclip); if(binoc) glViewport(0,0,xt/2,yt); glMatrixMode(GL_MODELVIEW); glDrawBuffer(GL_BACK_LEFT); glLoadIdentity(); drawstars(); glTranslatef((binoc?-1:(quadbinoc?1:0))*nose,0.0,0.0); glMultMatrixf(aff); drawall(realtope,phase); if(binoc||quadbinoc){ if(binoc) glViewport(xt/2,0,xt/2,yt); if(quadbinoc) { glDrawBuffer(GL_BACK_RIGHT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } glLoadIdentity(); drawstars(); glTranslatef((quadbinoc?-1:1)*nose,0.0,0.0); glMultMatrixf(aff); drawall(realtope,phase); if(binoc) glViewport(0,0,xt,yt); if(quadbinoc) glDrawBuffer(GL_BACK); } glMatrixMode(GL_PROJECTION); if(msg) postit(); else speedometer(); /* Keep the clock up-to-date */ if(!park && --skipsnap < 0 && snap > 0) { int ok = snapshot(""); if(ok < 0) { snap = 0; /* Stop on error */ } else { snap--; snapno++; } } glutSwapBuffers(); } void idle(void){ /*do this when nothing else is happening*/ if(!park && tracing && tracecount > tracelimit) traceoff(); clock_tick(); if(morph) autotymer(0); /* advance autotymer */ if(ntopes>1 && !park) tymer(); /*tyming only makes sense if ntopes>1 */ trynewtope(); glutPostRedisplay(); /*redraw the window*/ chaptrack(BUT,XX,YY,SHIF); audiofunc(); } void recidle(void) { glutPositionWindow(0, 0); glutReshapeWindow(zoom2?1280:640, zoom2?960:480); } void recdspy(void) { if(xt == (zoom2?1280:640)) { glutIdleFunc(idle); glutReshapeFunc(reshaped); glutDisplayFunc(drawcons); setpark(0); } else { usleep(200000); } } void mousepushed(int but,int stat,int x,int y){ if(stat==GLUT_DOWN) BUT |= (1< tr[1]) ii = 0; if(tr[3] > tr[2]) jj = 3; if(tr[jj] > tr[ii]) ii = jj; // printf("\n %i is the largest trace. \n", ii); tmp = 4*sqrt(tr[ii]+1)/2; switch(ii){ case 0: quat[0] = tmp/4; quat[1] = (mat[6] - mat[9]) / tmp; quat[2] = (mat[8] - mat[2]) / tmp; quat[3] = (mat[1] - mat[4]) / tmp; break; case 1: quat[0] = (mat[6] - mat[9]) / tmp; quat[1] = tmp/4; quat[2] = (mat[1] + mat[4]) / tmp; quat[3] = (mat[8] + mat[2]) / tmp; break; case 2: quat[0] = (mat[8] - mat[2]) / tmp; quat[1] = (mat[4] + mat[1]) / tmp; quat[2] = tmp/4; quat[3] = (mat[9] + mat[6]) / tmp; break; case 3: quat[0] = (mat[1] - mat[4]) / tmp; quat[1] = (mat[8] + mat[2]) / tmp; quat[2] = (mat[9] + mat[6]) / tmp; quat[3] = tmp/4; break; } } void loadIdentity(float* mat) {int ii; for(ii=0;ii<16;ii++) mat[ii]=float(ii/4 == ii%4);} void getmat(float *target, float *source){ memcpy(target,source, 16*sizeof(float)); } void multmatinv(float *T){ // of an Euclidean isometry float N[16]; getmat(N,T); N[1] = T[4]; N[2] = T[8]; N[6] = T[9]; N[4] = T[1]; N[8] = T[2]; N[9] = T[6]; N[12]= -T[0]*T[12]-T[1]*T[13]-T[2]*T[14]; N[13]= -T[4]*T[12]-T[5]*T[13]-T[6]*T[14]; N[14]= -T[8]*T[12]-T[9]*T[13]-T[10]*T[14]; glMultMatrixf(N); } void updateaffmat(float *oldhand, float *newhand){ float dx=0, dy=0, dz=0 ; float dhand[16]; float quat[4]; glMatrixMode(GL_TEXTURE); //do some arithmetic on an unused stack glPushMatrix(); glLoadIdentity(); multmatinv(newhand); glMultMatrixf(oldhand); glGetFloatv(GL_TEXTURE_MATRIX, dhand); glPopMatrix(); //dhand= newhand^-1 * oldhand dx=del*dhand[12]; dhand[12]=0; //attenuation is about right dy=del*dhand[13]; dhand[13]=0; dz=del*dhand[14]; dhand[14]=0; //use a bit of the translation in dhand and then strip translation if(gesture){ //shrink dhand m2q(dhand,quat); quat[0]= 1-eps + eps*quat[0]; quat[1] *= eps; quat[2] *= eps; quat[3] *= eps; normalize(quat); q2m(quat,dhand); } if(mode==FLYMODE){ glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMultMatrixf(dhand); //rotate the stars glMultMatrixf(starmat); glGetFloatv(GL_TEXTURE_MATRIX,starmat); } glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); if(mode==TURNMODE) glTranslatef(aff[12],aff[13],aff[14]); glMultMatrixf(dhand); //just the rotation of it glTranslatef(dx,dy,dz); if(mode==TURNMODE) glTranslatef(-aff[12],-aff[13],-aff[14]); glMultMatrixf(aff); glGetFloatv(GL_TEXTURE_MATRIX,aff); } void readbuttons(arMasterSlaveFramework& fw){ const int butn[6]= { fw.getButton(0), fw.getButton(1), fw.getButton(2), fw.getButton(3), fw.getButton(4), fw.getButton(5) }; static int obutn[6]={0,0,0,0,0,0}; if(obutn[0]==0 & butn[0] == 1) movie=1-movie; //{A}toggle if(obutn[1]==0 & butn[1] == 1) resethand=1; //{B}button if(obutn[2]==0 & butn[2] == 1) gesture=1-gesture; //{C}toggle if(obutn[3]==0 & butn[3] == 1) msg=1-msg; //{X}toggle if(obutn[4]==0 & butn[4] == 1) mode=1-mode; //{Y}toggle if(obutn[5]==0 & butn[5] == 1) deFault(); //{Z}button tymer(); clock_tick(); memcpy(obutn,butn,6*sizeof(int)); } void gesturetrack(arMasterSlaveFramework& fw){ //the present logic is legacy stuff from zyspace, rationalize it! if (!fw.getMaster())return; //only the master should listen //this is the gesture navigator static float oldhand[16]; float handmat[16]; arMatrix4 arhand=fw.getMatrix(1); getmat(handmat,arhand.v); //geotrack(arhand.v) gets arhand.v and used as float* handmat static bool reshand=true; if(resethand){reshand=true; resethand=false;} //do we really need two flags? if(gesture){ updateaffmat(oldhand,handmat); } if(reshand){reshand=false; getmat(oldhand, handmat);} //this is the Pape navigator //this should really be done on the arithmetic (Texture) stack glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); const float joy0 = fw.getAxis(0); //joystick right-left const float joy1 = fw.getAxis(1); //joystick fore-aft const float xx = arhand.v[8]; const float yy = arhand.v[9]; const float zz = arhand.v[10]; // printf(" xx %g yy %g zz %g \n",xx,yy,zz); const float da = .5*joy0; const float ds = .1*joy1; //joystick translates in the wand direction glTranslatef(xx*ds, yy*ds, zz*ds); glRotatef(da, 0.,1.,0.); glMultMatrixf(aff); glGetFloatv(GL_MODELVIEW_MATRIX,aff); glPopMatrix(); //this is an illogical place for this but maybe it works again {int ii,jj; for(ii=0;ii<3;ii++){ luxx.x[ii]=0; /* calculite */ for(jj=0;jj<3;jj++) luxx.x[ii] += aff[ii*4+jj]*lux.x[jj];} } readbuttons(fw); } /**********************************************************************/ void cubecleanup(arMasterSlaveFramework&) { //now we want to do any calculations that would rely on the shared stuff // return true; } void cubedraw(arMasterSlaveFramework& fw){ glPushMatrix(); glTranslatef(0,5,0); glScalef(sizz,sizz,sizz); dawn(1); drawstars(); glPopMatrix(); glMultMatrixf(aff); glScalef(sizz,sizz,sizz); drawall(realtope,phase); } int main(int argc, char **argv){ arguments(argc,argv); getmem(); dataprep(); /* dotrans() not called here any more -- see below */ if(eyes==console){ /*console main*/ dotrans(NULL); /* read datafile */ glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH); switch(win) { case 0: break; case 1: glutInitWindowSize(640, 480); glutInitWindowPosition(0,0); break; case 2: glutInitWindowPosition(0,0); break; } glutCreateWindow("<* illiSkel in C/OpenGL/GLUT *>"); if(win==2) glutFullScreen(); glEnable(GL_DEPTH_TEST); glutDisplayFunc(drawcons); glutKeyboardFunc(keyboard); glutSpecialFunc(special_keybo); glutMouseFunc(mousepushed); glutMotionFunc(mousemoved); glutPassiveMotionFunc(mousemoved); glutReshapeFunc(reshaped); glutIdleFunc(idle); glutMainLoop(); } else if(eyes==cube){ //syzygy main /* * in Syzygy case, we don't call dotrans() to read the datafiles here, * but inside of cubeinit(). */ arMasterSlaveFramework* fff = new arMasterSlaveFramework(); if(!fff->init(argc,argv)) return 1; // fff->setInitCallback(cubeinit); fff->setStartCallback(cubeinit); // fff->setPreExchangeCallback(cubetrack); fff->setPreExchangeCallback(gesturetrack); fff->setDrawCallback(cubedraw); fff->setPostExchangeCallback(cubecleanup); fff->setKeyboardCallback(clefs); fff->setOverlayCallback(overlay); // framework.setClipPlanes( .1, 1000 ); // framework.setEyeSpacing(6/(12*2.54)); return fff->start() ? 0:1; } } #if 0 int main(int argc, char **argv) { arguments(argc,argv); #ifdef CAVE if(caveyes) { CAVEConfigure(&argc, argv, NULL); } #endif getmem(); dataprep(); if(caveyes){ #ifdef CAVE CAVESetOption( CAVE_GL_SAMPLES, 8 ); CAVEInit(); /* Each wall is (part of) a forked process from here on.*/ CAVEFrameFunction(caveframe,0); /* is restricted to MasterWall, so once per frame */ CAVEInitApplication(drawcaveinit, 0); CAVEDisplay(drawcave, 2, tope,phase); if (filename != NULL) child = sproc(dotrans,PR_SADDR); else {printf("How did I get here? No socket or file open!!!\n"); exit(1);} while(!CAVEgetbutton(CAVE_ESCKEY)) { cavekeybo(); /* is asynchronous from display processes */ settope(realtope); CAVEDisplay(drawcave, 2, tope,phase); /* if(ntopes>1) tymer(); Moved into caveframe(), once per frame */ trynewtope(); } audioclean(); if(child) kill(child,9); CAVEExit(); #endif ;} else{ /*console main*/ char title[50]; glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH|GLUT_RGB | (quadbinoc ? GLUT_STEREO:0) | (singlebuffer ? 0:GLUT_DOUBLE)); switch(win) { case 0: glutInitWindowSize(400, 400); break; case 1: glutInitWindowSize(640, 480); #if defined(sgi) || defined(__sgi) glutInitWindowPosition(0,1024-480); #else glutInitWindowPosition(0,0); #endif break; case 2: glutInitWindowPosition(0,0); break; case 3: glutInitWindowSize(1024, 768); glutInitWindowPosition(0, 0); break; case 4: glutInitWindowSize(1280, 960); glutInitWindowPosition(0, 0); break; case 5: glutInitWindowSize(800, 400); break; case 6: glutInitWindowSize(800, 800); break; case 7: glutInitWindowSize(2880, 1200); glutInitWindowPosition(0,0); } sprintf(title, "avn %.45s", filename); glutCreateWindow(title); if(win==2) glutFullScreen(); glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT); glutDisplayFunc(drawcons); glutKeyboardFunc(keyboard); glutSpecialFunc(special_keybo); glutMouseFunc(mousepushed); glutMotionFunc(mousemoved); glutPassiveMotionFunc(mousemoved); glutReshapeFunc(reshaped); glutIdleFunc(idle); if(recmode) { setpark(1); snap = 9999; snapno = -1; glutInitWindowSize(1280, 960); glutInitWindowPosition(0, 0); tymode = TICKTYME; tickrate = 30; zoom2 = 1; nosproc = 1; if(playfname) playon(playfname); msg = 0; glutIdleFunc( recidle ); glutDisplayFunc( recdspy ); } if (filename != NULL) { #ifdef HAS_SPROC if(nosproc) dotrans(NULL); else child = sproc(dotrans,PR_SADDR); #else nosproc = 1; dotrans(NULL); #endif /*HAS_SPROC*/ } else { printf("Must specify an input movie file.\n"); exit(1); } glutMainLoop(); } return 0; } #endif