#include #include #include #include #include #include #include #include #include #include #include #include "3darith.h" #include "findvis.h" #include "mathutils.h" #define TSIZE 3 #define TX .4 #define TY -.4 #define WINX 400 #define WINY 400 extern const char *__progname; #define UNUSED __attribute__((__unused__)) static Display *disp; static XVisualInfo *vi; static GLXContext ctx; static Screen *scr; static int scrwidth; static int scrheight; static int depth; static Window rootwin; static Colormap cmap; static Window win; static XColor bgcolour; static XColor fgcolour; static GC gc; static struct timeval nexttick; static XYZ pts[20]; static int lines[30][2]; static int nlines; static int tris[20][3]; static double tricolours[20][3]; static int ntris; static unsigned int tex[20]; static unsigned char bitmaps[20][18][18][4]; static XYZ mx; static XYZ my; static XYZ mz; static unsigned int kbstate; #define KBS_L 0x00000001 #define KBS_R 0x00000002 #define KBS_U 0x00000004 #define KBS_D 0x00000008 #define KBS_CW 0x00000010 #define KBS_CCW 0x00000020 #define KBS_LSHF 0x00000040 #define KBS_RSHF 0x00000080 #define KBS_LCTL 0x00000100 #define KBS_RCTL 0x00000200 #define KBS_SHIFT (KBS_LSHF|KBS_RSHF) #define KBS_CTRL (KBS_LCTL|KBS_RCTL) static int dbg; static int swapped; #define SQRT_3_4 .86602540378443864676 /* sqrt(3/4) */ static void open_display(void) { disp = XOpenDisplay(0); if (disp == 0) { fprintf(stderr,"%s: can't open display\n",__progname); exit(1); } } static void setup_visual(void) { vi = find_visual(disp); } static void setup_X(void) { Pixmap p; scr = XScreenOfDisplay(disp,vi->screen); scrwidth = XWidthOfScreen(scr); scrheight = XHeightOfScreen(scr); depth = vi->depth; rootwin = XRootWindowOfScreen(scr); cmap = XCreateColormap(disp,rootwin,vi->visual,AllocNone); XParseColor(disp,cmap,"#646",&bgcolour); XAllocColor(disp,cmap,&bgcolour); XParseColor(disp,cmap,"#fff",&fgcolour); XAllocColor(disp,cmap,&fgcolour); p = XCreatePixmap(disp,rootwin,1,1,depth); gc = XCreateGC(disp,p,0,0); XFreePixmap(disp,p); } static void setup_context(void) { ctx = glXCreateContext(disp,vi,0,True); if (! ctx) { fprintf(stderr,"%s: can't create GL context\n",__progname); exit(1); } } static int getpixel(XImage *i, int x, int y) { if ((x < 0) || (y < 0) || (x >= 12) || (y >= 13)) abort(); return(XGetPixel(i,x,y)); } static void setup_bitmaps(void) { XFontStruct *fs; Pixmap pm; GC gc; int i; char s[3]; int sl; XImage *img; int x; int y; pm = XCreatePixmap(disp,rootwin,12,13,1); gc = XCreateGC(disp,pm,0,0); fs = XQueryFont(disp,XGContextFromGC(gc)); for (i=20-1;i>=0;i--) { sl = sprintf(&s[0],"%d",i+1); XSetForeground(disp,gc,0); XFillRectangle(disp,pm,gc,0,0,12,13); XSetForeground(disp,gc,1); XDrawString(disp,pm,gc,(12-(6*sl))/2,fs->ascent,&s[0],sl); img = XGetImage(disp,pm,0,0,12,13,1,XYPixmap); for (y=18-1;y>=0;y--) for (x=18-1;x>=0;x--) { bitmaps[i][y][x][0] = 255; bitmaps[i][y][x][1] = 255; bitmaps[i][y][x][2] = 255; if ( (x < 2) || (y < 2) || (x > 12) || (y > 14) || !getpixel(img,x-2,14-y) ) { bitmaps[i][y][x][3] = 0; } else { bitmaps[i][y][x][3] = 255; } } XDestroyImage(img); } } static void create_window(void) { XSetWindowAttributes attr; unsigned long int attrmask; attrmask = 0; attr.background_pixel = bgcolour.pixel; attrmask |= CWBackPixel; attr.event_mask = StructureNotifyMask | VisibilityChangeMask | KeyPressMask | KeyReleaseMask; attrmask |= CWEventMask; attr.colormap = cmap; attrmask |= CWColormap; win = XCreateWindow(disp,rootwin,0,0,WINX*2,WINY,0,depth,InputOutput,vi->visual,attrmask,&attr); XMapRaised(disp,win); } static void setup_gl(void) { int i; double ps[4] = { .1, 0, 0, 0 }; double pt[4] = { 0, .1, 0, 0 }; if (glXMakeCurrent(disp,(GLXDrawable)win,ctx) != True) { fprintf(stderr,"%s: can't make context current\n",__progname); exit(1); } glShadeModel(GL_SMOOTH); glClearColor(0,0,0,0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.5,1.5,-1.5,1.5,-100,100); glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); glEnable(GL_POLYGON_OFFSET_FILL); glPixelStorei(GL_UNPACK_ALIGNMENT,1); glGenTextures(20,&tex[0]); for (i=20-1;i>=0;i--) { glBindTexture(GL_TEXTURE_2D,tex[i]); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,18,18,1,GL_RGBA,GL_UNSIGNED_BYTE,&bitmaps[i][0][0][0]); glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGendv(GL_S,GL_EYE_PLANE,&ps[0]); glTexGendv(GL_T,GL_EYE_PLANE,&pt[0]); } glCullFace(GL_BACK); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); kbstate = 0; } static void setup_model(void) { double k; double h1; double r1; double h2; double r2; double s72; double c72; double s144; double c144; void addline(int x1, int x2) { if (nlines >= sizeof(lines)/sizeof(lines[0])) abort(); lines[nlines][0] = x1; lines[nlines][1] = x2; nlines ++; } void addtri(int x1, int x2, int x3, double cr, double cg, double cb) { if (ntris >= sizeof(tris)/sizeof(tris[0])) abort(); tris[ntris][0] = x1; tris[ntris][1] = x2; tris[ntris][2] = x3; tricolours[ntris][0] = cr; tricolours[ntris][1] = cg; tricolours[ntris][2] = cb; ntris ++; } void addtetra(int x1, int x2, int x3, int x4, double cr, double cg, double cb) { addtri(x1,x2,x4,cr,cg,cb); addtri(x1,x3,x2,cr,cg,cb); addtri(x1,x4,x3,cr,cg,cb); addtri(x2,x3,x4,cr,cg,cb); } /* obtained analytically: s=sin, c=cos, number is in degrees */ k = sqrt(5); s72 = sqrt((5+k)/8); c72 = (k-1) / 4; s144 = sqrt((5-k)/8); c144 = - (k+1) / 4; /* obtained by numerical solution of relevant equations */ /* h1, h2 = z distance of rings of vertices */ /* r1, r2 = corresponding radii */ h1 = .7946544722917661; r1 = .6070619982066862; h2 = .1875924740850799; r2 = .9822469463768460; pts[ 0] = (XYZ){r1,0,h1}; pts[ 1] = (XYZ){r1*c72,r1*s72,h1}; pts[ 2] = (XYZ){r1*c144,r1*s144,h1}; pts[ 3] = (XYZ){r1*c144,-r1*s144,h1}; pts[ 4] = (XYZ){r1*c72,-r1*s72,h1}; pts[ 5] = (XYZ){r2,0,h2}; pts[ 6] = (XYZ){r2*c72,r2*s72,h2}; pts[ 7] = (XYZ){r2*c144,r2*s144,h2}; pts[ 8] = (XYZ){r2*c144,-r2*s144,h2}; pts[ 9] = (XYZ){r2*c72,-r2*s72,h2}; pts[10] = (XYZ){-r2,0,-h2}; pts[11] = (XYZ){-r2*c72,-r2*s72,-h2}; pts[12] = (XYZ){-r2*c144,-r2*s144,-h2}; pts[13] = (XYZ){-r2*c144,r2*s144,-h2}; pts[14] = (XYZ){-r2*c72,r2*s72,-h2}; pts[15] = (XYZ){-r1,0,-h1}; pts[16] = (XYZ){-r1*c72,-r1*s72,-h1}; pts[17] = (XYZ){-r1*c144,-r1*s144,-h1}; pts[18] = (XYZ){-r1*c144,r1*s144,-h1}; pts[19] = (XYZ){-r1*c72,r1*s72,-h1}; nlines = 0; ntris = 0; #if 1 /* dodecahedron */ addline( 0, 4); addline( 4, 3); addline( 3, 2); addline( 2, 1); addline( 1, 0); /* 0, 1 */ addline( 1, 6); addline( 6,13); addline(13, 5); addline( 5, 0); /* 1, 2 */ addline( 2, 7); addline( 7,14); addline(14, 6); /* 6, 1 */ /* 2, 3 */ addline( 3, 8); addline( 8,10); addline(10, 7); /* 7, 2 */ /* 3, 4 */ addline( 4, 9); addline( 9,11); addline(11, 8); /* 8, 3 */ /* 4, 0 */ /* 0, 5 */ addline( 5,12); addline(12, 9); /* 9, 4 */ /* 5,13 */ addline(13,18); addline(18,17); addline(17,12); /* 12, 5 */ /* 6,14 */ addline(14,19); addline(19,18); /* 18,13 */ /* 13, 6 */ /* 7,10 */ addline(10,15); addline(15,19); /* 19,14 */ /* 14, 7 */ /* 8,11 */ addline(11,16); addline(16,15); /* 15,10 */ /* 10, 8 */ /* 9,12 */ /* 12,17 */ addline(17,16); /* 16,11 */ /* 11, 9 */ /* 15,16 */ /* 16,17 */ /* 17,18 */ /* 18,19 */ /* 19,15 */ #endif #if 1 /* 5 tetrahedra */ addtetra( 0,17, 8,14,.8,.8,.8); addtetra( 1,18, 9,10,1 ,.2,.2); addtetra( 2,19, 5,11,.2,1 ,.2); addtetra( 3,15, 6,12,.2,.2,1 ); addtetra( 4,16, 7,13,.9,.9,.2); #endif #if 0 for (i=0;i<4;i++) { addtri(0,2+i+i,4+i+i); addtri(1,5+i+i,3+i+i); addtri(2+i+i,3+i+i,4+i+i); addtri(4+i+i,3+i+i,5+i+i); } addtri(0,10,2); addtri(1,3,11); addtri(10,11,2); addtri(2,11,3); #endif } static void xyzcall(XYZ p, void (*fn)(double, double, double)) { (*fn)(p.x,p.y,p.z); } static void xyzvertex(XYZ p) { xyzcall(p,&glVertex3d); } static void drawstuff(void) { int i; GLdouble m[16]; if (dbg) { printf("mx = (%g,%g,%g)\n",mx.x,mx.y,mx.z); printf("my = (%g,%g,%g)\n",my.x,my.y,my.z); printf("mz = (%g,%g,%g)\n",mz.x,mz.y,mz.z); } m[0] = mx.x; m[1] = mx.y; m[2] = mx.z; m[3] = 0; m[4] = my.x; m[5] = my.y; m[6] = my.z; m[7] = 0; m[8] = mz.x; m[9] = mz.y; m[10] = mz.z; m[11] = 0; m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; glMultMatrixd(&m[0]); glPolygonOffset(0,1); // glEnable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D); glColor3f(1,0,0); for (i=ntris-1;i>=0;i--) { // glBindTexture(GL_TEXTURE_2D,tex[i]); glBegin(GL_TRIANGLES); glColor3f(tricolours[i][0],tricolours[i][1],tricolours[i][2]); // glColor3f(1,0,0); glTexCoord2d(TX-(TSIZE*.5),TY); xyzvertex(pts[tris[i][0]]); // glColor3f(0,1,0); glTexCoord2d(TX+(TSIZE*.5),TY); xyzvertex(pts[tris[i][1]]); // glColor3f(0,0,1); glTexCoord2d(TX,(TSIZE*SQRT_3_4)+TY); xyzvertex(pts[tris[i][2]]); glEnd(); } glPolygonOffset(0,0); glDisable(GL_TEXTURE_2D); glBegin(GL_LINES); glColor3f(1,1,1); for (i=nlines-1;i>=0;i--) { xyzvertex(pts[lines[i][0]]); xyzvertex(pts[lines[i][1]]); } glVertex3d(0,0,-1.5); glVertex3d(0,0,1.5); glEnd(); } static void render(void) { glViewport(0,0,WINX*2,WINY); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glViewport(swapped?WINX:0,0,WINX,WINY); glRotatef(-5,0,1,0); glPushMatrix(); drawstuff(); glPopMatrix(); glViewport(swapped?0:WINX,0,WINX,WINY); glRotatef(5,0,1,0); glPushMatrix(); drawstuff(); glPopMatrix(); glXSwapBuffers(disp,win); glXWaitGL(); glXWaitX(); } static void setup_ticks(void) { gettimeofday(&nexttick,0); } static void rotate_model(double a, XYZ axis) { mx = xyzunit(xyzrotate(mx,a,axis)); my = xyzrotate(my,a,axis); my = xyzunit(xyz_subtract_component(my,mx)); mz = xyzrotate(mz,a,axis); mz = xyzunit(xyz_subtract_component(xyz_subtract_component(mz,mx),my)); } static void motion(void) { double f; if (kbstate & KBS_SHIFT) f = 25; else if (kbstate & KBS_CTRL) f = 1; else f = 5; f *= .3; switch (kbstate & (KBS_L|KBS_R)) { case KBS_L: rotate_model(-f,(XYZ){0,1,0}); break; case KBS_R: rotate_model(f,(XYZ){0,1,0}); break; } switch (kbstate & (KBS_U|KBS_D)) { case KBS_U: rotate_model(-f,(XYZ){1,0,0}); break; case KBS_D: rotate_model(f,(XYZ){1,0,0}); break; } switch (kbstate & (KBS_CW|KBS_CCW)) { case KBS_CW: rotate_model(-f,(XYZ){0,0,1}); break; case KBS_CCW: rotate_model(f,(XYZ){0,0,1}); break; } } static void keystroke(XKeyEvent *ev, char updn) { KeySym ks; unsigned int bit; printf("keystroke, kc=%d ud=%c t=%lu\n",ev->keycode,updn,(unsigned long int)ev->time); ks = XLookupKeysym(ev,0); printf(" ks=%s\n",XKeysymToString(ks)); bit = 0; switch (ks) { case XK_h: case XK_H: bit = KBS_L; break; case XK_l: case XK_L: bit = KBS_R; break; case XK_k: case XK_K: bit = KBS_U; break; case XK_j: case XK_J: bit = KBS_D; break; case XK_y: case XK_Y: bit = KBS_CCW; break; case XK_u: case XK_U: bit = KBS_CW; break; case XK_Shift_L: bit = KBS_LSHF; break; case XK_Shift_R: bit = KBS_RSHF; break; case XK_Control_L: bit = KBS_LCTL; break; case XK_Control_R: bit = KBS_RCTL; break; case XK_x: case XK_X: if (updn == 'd') swapped = ! swapped; break; case XK_d: case XK_D: if (updn == 'd') dbg = 1; break; case XK_q: case XK_Q: if ((updn == 'u') && (kbstate & KBS_SHIFT)) exit(0); break; } switch (updn) { case 'u': kbstate &= ~bit; break; case 'd': kbstate |= bit; break; default: abort(); break; } } static void events(void) { XEvent ev; while (XPending(disp)) { XNextEvent(disp,&ev); switch (ev.type) { default: /* XXX ignore */ break; case KeyPress: /* XKeyPressedEvent - XKeyEvent - xkey */ keystroke(&ev.xkey,'d'); break; case KeyRelease: /* XKeyReleasedEvent - XKeyEvent - xkey */ keystroke(&ev.xkey,'u'); break; case MappingNotify: /* XMappingEvent - xmapping */ XRefreshKeyboardMapping(&ev.xmapping); break; } } } static void await(void) { struct timeval now; int ms; nexttick.tv_usec += 25000; if (nexttick.tv_usec >= 1000000) { nexttick.tv_usec -= 1000000; nexttick.tv_sec ++; } while (1) { gettimeofday(&now,0); if ( (now.tv_sec > nexttick.tv_sec) || ( (now.tv_sec == nexttick.tv_sec) && (now.tv_usec >= nexttick.tv_usec) ) ) return; ms = ((nexttick.tv_sec - now.tv_sec) * 1000) + 1 + (nexttick.tv_usec / 1000) - (now.tv_usec / 1000); if (ms < 1) ms = 1; poll(0,0,ms); } } static void tick(void) { motion(); render(); events(); await(); } static void setup_view(void) { mx = (XYZ){1,0,0}; my = (XYZ){0,1,0}; mz = (XYZ){0,0,1}; swapped = 0; } static void setup_input(void) { XGrabKeyboard(disp,win,False,GrabModeAsync,GrabModeAsync,CurrentTime); } int main(void); int main(void) { open_display(); setup_visual(); setup_X(); setup_context(); setup_bitmaps(); create_window(); setup_gl(); setup_input(); setup_model(); setup_view(); setup_ticks(); while (1) tick(); }