/* 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. ****/ /****************************************************************/ #ifdef i386 #define LITTLEENDIAN 1 #endif #ifdef macintosh # define ntohl(x) (x) #include #include #include #include #include #include #include #include "getopt.h" #include "glut.h" #include //For TickCount #else #ifdef WIN32 #ifndef SOUND #define SOUND 1 /* If Makefile.win didn't define it, we want SOUND anyway */ #endif #define LITTLEENDIAN 0x0001 /* WIN32 -> little-endian (sometimes) */ #define ntohl msjunk_ntohl #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 #define cosf(_) cos(_) #define sinf(_) sin(_) #define rint(_) ((int)(_)) #define popen(_,__) _popen(_,__) #define pclose(_) _pclose(_) #define finite(_) _finite(_) #define mkdir(_,__) _mkdir(_) #include #endif /*WIN32*/ #include #include #include #include #ifdef linux #include #endif #include #include #include #include #include "getopt.h" #include "glut.h" #include #ifdef unix #include #include #endif /*unix*/ #endif /*macintosh*/ #include #include #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 #ifndef alloca #include #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) #ifdef macintosh // Mac toolbox already defines Point #define POINT AVNPOINT #define Point AvnPoint #endif typedef struct POINT { 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 */ int step, movie, swap,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; 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 }; /* * 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 /* swap 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 */ 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) #ifdef macintosh char *strdup(char *s1); #endif 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 ); int readanoff(FILE *inf, Tope *tp, int ascii); void reverts(Tope *tp); void findadj(Tope *tp); void readdl(FILE *inf, Tope *tp); 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(); 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 ); 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_lu; 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; /* show only one side of surface (other invisible) */ 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_canlength; /* hack: extend each tube seg by this fraction */ int s_tubefacets; 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 lu (shr->s_lu) #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 tymedt (shr->s_tymedt) #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 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) #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 #ifdef LITTLEENDIAN static u_long ntohl(u_long v) { /* for Intel boxes, anyway. */ return (v&0xFF)<<24 | (v&0xFF00)<<8 | (v>>8)&0xFF00 | (v>>24)&0xFF; } #else #define ntohl(x) (x) #endif void usleep( int usecs ) { Sleep( usecs / 1000 ); } #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 conszero = { 0, 0, -6 }; /* inside the console */ phase=phase0; gap = gap0; alfa = 1.; movierate = 10; /* 10 topes per second default */ tickrate = 10; /* 10 ticks per second default (in sim-time mode) too */ nullwidth = 3; tymode = caveyes ? TICKTYME : REALTYME; realtope = tope = 0; phase &= 2; fore = 1; step=0; movie = 0; swap = 0; locus=0; /* start by drawing whole surface */ msg=1; mauspaw = 0; wnd = 1; siz = 4; mode=TURNMODE; 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 = 8; docans = 0; tuberadius = .02; canlength = .20; tubespecular = 10.; tubediffuse = .8; mcopy(starmat, id); mcopy(aff, id); vsettranslation( aff, caveyes ? &cavezero : &conszero ); tilesnaps = 0; autotymer(1); /* reset autotymer to start at the beginning */ vunit( &lux, &lux ); } void calculite() { vuntfmvector( &lu, &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. */ /* WHERE */ 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(mag!=mag) { //NaN's never equal themselves 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(s!=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]; } /* THERE */ 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 = lu; 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, &lu, cs ); symrotn = 180./trotn; glRotatef(symrotn, 0,0,1); } } /*gkf this never happens, does it? why minus 2?? is a corruption */ 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) { 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; 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) 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. */ rgba = paint((DOT(eye[0],*n) <= adotn) ^ (lphase&1), DOT(light[0],*n), dog); do_tri(a, &tp->verts[vb], &tp->verts[vc], gap, rgba); for(jj = 1; jj < trotn; jj++) { Point *rverts = &vrot[tp->nverts*(jj-1)]; rgba = paint((DOT(eye[jj],*n) <= adotn) ^ (lphase&1), DOT(light[jj],*n), dog); do_tri(&rverts[va], &rverts[vb], &rverts[vc], gap, 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(); } if(dotube > 0) { float (*profile)[2]; static Point tubelight_cam = {0,0,1}; Point tubelight; Loop *l; 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 { for(l = tp->loops; l != NULL; l = l->next) { do_tubeloop( l, dotube, profile, &tubelight ); } } } if(alfa < 1) { glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); } if(oneside) { 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; if(ggap == 1) { glColor4ubv( (GLubyte *)&rgba ); glVertex3fv((GLfloat *)a); glVertex3fv((GLfloat *)b); glVertex3fv((GLfloat *)c); 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); glColor4ubv( (GLubyte *)&rgba ); if (ggap>0) { glVertex3fv(p); glVertex3fv(q); glVertex3fv(r); } 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 ) { int i, k, prevk; float spec, illum, gray; Point *edge0 = NewA( Point, nprofile+1 ); Point *edge1 = NewA( Point, nprofile+1 ); float *color0 = NewA( float, nprofile+1 ); float *color1 = NewA( float, nprofile+1 ); Point *eprev = edge0, *ecur = edge1, *et; float *cprev = color0, *ccur = color1, *ct; 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 = fabs( DOT( eprev[i], *light ) ); /* Surface normal */ spec = 1 - tubespecular * (1 - illum); gray = illum * tubediffuse; if(gray < spec) gray = spec; cprev[i] = gray; vsadd( &eprev[i], &l->verts[prevk], tuberadius, &eprev[i] ); } for( ; k < l->nverts; k++) { if(nprofile <= 2) { glBegin( GL_LINES ); vsadd( eprev, &l->verts[k], tuberadius, &l->xyv[k][0] ); glColor3f(1,0,0); glVertex3fv( &eprev->x[0] ); glVertex3fv( &l->verts[k].x[0] ); glColor3f(0,1,0); vsadd( eprev, &l->verts[k], tuberadius, &l->xyv[k][1] ); glVertex3fv( &eprev->x[0] ); glEnd(); } else { glBegin(GL_QUAD_STRIP); for(i = 0; i <= nprofile; i++) { glColor3f( cprev[i], cprev[i], cprev[i] ); glVertex3fv( &eprev[i].x[0] ); vcomb( &ecur[i], profile[i][0], &l->xyv[k][0], profile[i][1], &l->xyv[k][1] ); illum = fabs( DOT( ecur[i], *light ) ); /* Surface normal */ spec = 1 - tubespecular * (1 - illum); gray = illum * tubediffuse; if(gray < spec) gray = spec; glColor3f( gray, gray, gray ); ccur[i] = gray; vsadd( &ecur[i], &l->verts[k], tuberadius, &ecur[i] ); glVertex3fv( &ecur[i].x[0] ); } glEnd(); } et = eprev; eprev = ecur; ecur = et; ct = cprev; cprev = ccur; ccur = ct; } } 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 float parktime; #ifdef macintosh static long tc0=0; float timenow(){ long newticks=TickCount(); if (tc0==0) tc0=TickCount(); return (newticks-tc0)/60.0; } #else static struct timeval tv0; 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; } #endif 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; #ifdef macintosh tc0+=(int) (dt*60); #else tv0.tv_sec += (int) dt; tv0.tv_usec += 1000000 * (dt - (int)dt); #endif } 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 (swap) 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 = (struct SHARED *)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); /* jms 1dec96 allow stdin with filename '-' */ if (!strcmp(filename,"-")) topefile=stdin; else if ((topefile = fopen(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 = fopen(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 = fopen(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 != ntohl(1) || dst != src) { while(--nwds >= 0) { *dst++ = ntohl(*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 ntohl(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]); } /* We need another package to keep track of multiply-used vertices */ struct multvert { int nvnos, vroom; int *vnos; }; struct multvert *multverts; int nmultverts, multvertroom; int *_paired, _tope; Point *_segs; void pseg( int i ) { printf("(%4d) %4d %7.5f %7.5f %7.5f : %7.5f %7.5f %7.5f %d (%d)\n", _paired[i], i, _segs[i].x[0], _segs[i].x[1], _segs[i].x[2], _segs[i^1].x[0], _segs[i^1].x[1], _segs[i^1].x[2], i^1, _paired[i^1]); } void evmultvert( int vno, int mvno ) { struct multvert *mv = &multverts[mvno]; int i; if(vno < 0) return; for(i = 0; i < mv->nvnos; 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[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 = fopen(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 (swap) {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 = fopen("stars.illi", "rb"); float r = caveyes ? 90. : 1.; const GLubyte *rend = glGetString(GL_RENDERER); Point sp; static Matrix startfm = { 0,0,-1,0, -1,0,0,0, 0,1,0,0, 0,0,0,1 }; if(rend==NULL || (rend[0]=='X'||rend[0]=='G')) slow_stars = 1; 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 */ 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:RlJ")) != -1) { switch(chi) { case 'b': bwflag = 1; break; case 'k': thick0 = atoi(optarg);break; case 'c': caveyes=1; 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': swap = 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 = (char *)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 */ 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++]; #ifdef macintosh //Mac doesn't really have permissions as such... if(filename != NULL && strcmp(filename,"-") != 0 ) { #else if(filename != NULL && strcmp(filename,"-") != 0 && access(filename, 0) < 0) { #endif char *fn = (char *)malloc(strlen(filename)+5); sprintf(fn, "%s.mov", filename); #ifndef macintosh if(access(fn, 0) == 0) #endif 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(); } 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; } void bump(float *val, float incr) { float by = fabs(incr); char fmt[8], code[32]; int digits = 1; if(hasnumber) { *val = getnumber(0); return; } if(by <= .003) digits = 3; else if(by <= .03) digits = 2; sprintf(fmt, "%%.%de", digits); sprintf(code, fmt, *val * (1 + incr)); sscanf(code, "%f", val); } #ifndef WIN32 #ifndef macintosh 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 #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; } } #ifndef macintosh 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(); } } #endif 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); #ifndef macintosh glutIdleFunc(promptidle); #endif } 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 ) { #ifndef macintosh char hostname[128]; int append; if(fname == NULL || fname[0] == '\0') { traceoff(); return; } setpark(1); mkdir("album", 0777); if(tracefile != NULL) fclose(tracefile); { struct stat buf; append= stat(fname,&buf)!=0; } // append = access(fname, 1) == 0; tracefile = fopen(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 = (char *)strdup(fname); } #else fprintf(stderr,"Tracing not enabled in Mac version.\n"); #endif } void traceoff() { #ifndef macintosh if(tracefile != NULL) { fprintf(stderr, "End trace -- recorded %d frames.\n", tracecount); traceall(); tracecut(); fclose(tracefile); tracefile = NULL; } tracing = 0; tracecount = 0; #endif } void tracewhere() { #ifndef macintosh 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]); #endif } void traceall() { #ifndef macintosh char *str = "hvwnlaeiopsqgtrkxd[]BRThj"; while(*str) trace(*str++); tracewhere(); #endif } void tracecut() { trace('C'); } void trace(int ch) { #ifndef macintosh 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(swap); 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); #endif } void playon( char *fname ) { char *tf; playoff(); if(fname == NULL || fname[0] == '\0') return; playpipe = (fname[0] == '|'); #ifndef macintosh playfile = playpipe ? popen(fname+1, "rb") : fopen(fname, "rb"); #else playfile=fopen(fname,"rb"); #endif if(playfile == NULL) { fprintf(stderr, "Can't open %s: ", fname); perror(""); return; } tf = (char *)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) { #ifndef macintosh if(playpipe) pclose(playfile); else #endif 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('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': swap = val; break; case 'T': rtope = val; break; case 'r': dlradius = 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 */ TOGGLE('e', oneside); 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('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); 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\ F1 : 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_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){ #ifdef CAVE if(caveyes) return *CAVEFramesPerSecond; #endif #ifdef macintosh static int ii=0; static long oldticks=-1; long newticks; static float rate=0; if (++ii %8==0){ newticks=TickCount(); rate=8.0/((float)(newticks-oldticks)/60.0); oldticks=newticks; } return rate; #else // IF not macintosh static int ii = 0; static double rate; double dbl; static struct timeval now, then; 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); #endif } void messages(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 }; #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); 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","optiVert by Hartman, Bourd, Chappell, Francis, Sullivan, and Levy (C) 1995..1998, 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 (F1)", 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); 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) { #ifndef macintosh //Piping doesn't _quite_ work on the mac! char fname[256]; char *fnamep = fname; char header[500]; struct viewport { GLint x, y, w, h; } vp; int i, rowlen; int step = zoom2+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; } #endif //!defined macintosh 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) messages(); sprintf(sub, ".%02d.%02d", y, x); snapshot(sub); glutSwapBuffers(); } } snapno = osnapno; } void drawcons(void){ 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.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if(binoc) glViewport(0,yt/4,xt/2,yt/2); else glViewport(0,0,xt,yt); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-mysiz*xt/yt,mysiz*xt/yt,-mysiz,mysiz,mysiz*focal,farclip); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); drawstars(); glTranslatef(-binoc*nose,0.0,0.0); glMultMatrixf(aff); drawall(realtope,phase); if(binoc){ glViewport(xt/2,yt/4,xt/2,yt/2); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); drawstars(); glTranslatef(binoc*nose,0.0,0.0); glMultMatrixf(aff); drawall(realtope,phase); glViewport(0,0,xt,yt); } if(msg) messages(); 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 { #ifdef macintosh unsigned long tc; Delay(12,&tc); #else usleep(200000); #endif } } void mousepushed(int but,int stat,int x,int y){ if(stat==GLUT_DOWN) BUT |= (1<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(singlebuffer ? GLUT_DEPTH|GLUT_RGB : GLUT_DOUBLE|GLUT_DEPTH|GLUT_RGB); switch(win) { case 0: glutInitWindowSize(400, 400); break; case 1: glutInitWindowSize(640, 480); #if defined(sgi) || defined(__sgi) glutInitWindowPosition(0,1024-480); #else glutInitWindowPosition(10,40); #endif break; case 2: glutInitWindowPosition(10,40); break; case 3: glutInitWindowSize(1024, 768); glutInitWindowPosition(10, 40); break; case 4: glutInitWindowSize(1280, 960); glutInitWindowPosition(10, 40); break; } 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(); } } #ifdef macintosh char *strdup(char *s1){ char *result=(char *)malloc(strlen(s1)+1); strcpy(result,s1); return result; } #endif