#include #include #include #include #include #include #include #include #include #include #include #include "3darith.h" #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 struct timeval nexttick; static XYZ mx; static XYZ my; static XYZ mz; static double shrink; 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_SHP 0x00000400 #define KBS_SHM 0x00000800 #define KBS_H_R 0x00001000 #define KBS_H_G 0x00002000 #define KBS_H_B 0x00004000 #define KBS_H_C 0x00008000 #define KBS_H_M 0x00010000 #define KBS_H_Y 0x00020000 #define KBS_SHIFT (KBS_LSHF|KBS_RSHF) #define KBS_CTRL (KBS_LCTL|KBS_RCTL) static int dbg; static int swapped; static void open_display(void) { disp = XOpenDisplay(0); if (disp == 0) { fprintf(stderr,"%s: can't open display\n",__progname); exit(1); } } static void find_visual(void) { XVisualInfo template; int nvi; template.visualid = 35; vi = XGetVisualInfo(disp,VisualIDMask,&template,&nvi); if (nvi < 1) { fprintf(stderr,"%s: can't find visual\n",__progname); exit(1); } if (nvi > 1) { fprintf(stderr,"%s: found multiple visuals\n",__progname); exit(1); } } static void setup_X(void) { 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); } 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 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) { 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(); glFrustum(-.03,.03,-.03,.03,.1,50); glEnable(GL_DEPTH_TEST); glMatrixMode(GL_MODELVIEW); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glEnable(GL_COLOR_MATERIAL); { GLfloat p[4]; glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,1); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,1); p[0] = 1; p[1] = 0; p[2] = 0; p[3] = 1; glLightfv(GL_LIGHT0,GL_DIFFUSE,&p[0]); p[0] = 0; p[1] = 1; glLightfv(GL_LIGHT1,GL_DIFFUSE,&p[0]); p[1] = 0; glLightfv(GL_LIGHT0,GL_AMBIENT,&p[0]); glLightfv(GL_LIGHT1,GL_AMBIENT,&p[0]); p[2] = 1; glLightfv(GL_LIGHT0,GL_SPECULAR,&p[0]); glLightfv(GL_LIGHT1,GL_SPECULAR,&p[0]); } kbstate = 0; } static void setup_ticks(void) { gettimeofday(&nexttick,0); } static void setup_view(void) { mx = (XYZ){1,0,0}; my = (XYZ){0,1,0}; mz = (XYZ){0,0,1}; shrink = 0; swapped = 0; } static void rotate_model(double a, XYZ axis) { mx = xyzunit(xyzrotate(mx,a,axis)); my = xyzrotate(my,a,axis); my = xyzunit(subtract_component(my,mx)); mz = xyzrotate(mz,a,axis); mz = xyzunit(subtract_component(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; switch (kbstate & (KBS_L|KBS_R)) { case KBS_L: rotate_model(-.003*f,(XYZ){0,1,0}); break; case KBS_R: rotate_model(.003*f,(XYZ){0,1,0}); break; } switch (kbstate & (KBS_U|KBS_D)) { case KBS_U: rotate_model(-.003*f,(XYZ){1,0,0}); break; case KBS_D: rotate_model(.003*f,(XYZ){1,0,0}); break; } switch (kbstate & (KBS_CW|KBS_CCW)) { case KBS_CW: rotate_model(-.003*f,(XYZ){0,0,1}); break; case KBS_CCW: rotate_model(.003*f,(XYZ){0,0,1}); break; } switch (kbstate & (KBS_SHP|KBS_SHM)) { case KBS_SHP: shrink += .002*f; if (shrink > .9) shrink = .9; break; case KBS_SHM: shrink -= .002*f; if (shrink < 0) shrink = 0; break; } } static void xyzvertex(XYZ v) { glVertex3f(v.x,v.y,v.z); } static void rect(XYZ c, XYZ s1, XYZ s2) { XYZ s1u; double s1d; XYZ s2u; double s2d; void pt(double f1, double f2) { xyzvertex(xyzadd(c,xyzadd(xyzscale(s1u,f1),xyzscale(s2u,f2)))); } s1d = xyzlength(s1); s1u = xyzscale(s1,1/s1d); s2d = xyzlength(s2); s2u = xyzscale(s2,1/s2d); pt(shrink,shrink); pt(s1d-shrink,shrink); pt(s1d-shrink,s2d-shrink); pt(shrink,s2d-shrink); } static void draw_stuff(void) { GLdouble m[16]; GLfloat p[4]; int i; double a; double a2; double s; double c; double s2; double c2; 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); } p[0] = 30; p[1] = 30; p[2] = 30; p[3] = 1; glLightfv(GL_LIGHT0,GL_POSITION,&p[0]); 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]); p[0] = 30; p[1] = 30; p[2] = 30; p[3] = 1; glLightfv(GL_LIGHT1,GL_POSITION,&p[0]); glColor3f(1,0,0); glBegin(GL_QUADS); for (i=15;i>=0;i--) { a = (i * M_PI) / 8; a2 = ((i+1) * M_PI) / 8; s = sin(a); c = cos(a); s2 = sin(a2); c2 = cos(a2); rect((XYZ){3*c,3*s,-2},(XYZ){3*(c2-c),3*(s2-s),0},(XYZ){0,0,4}); } 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); glTranslatef(0,0,-25); glRotatef(3,0,1,0); draw_stuff(); glLoadIdentity(); glViewport(swapped?0:WINX,0,WINX,WINY); glTranslatef(0,0,-25); glRotatef(-3,0,1,0); draw_stuff(); glXSwapBuffers(disp,win); glXWaitGL(); glXWaitX(); dbg = 0; } 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_a: case XK_A: bit = KBS_SHP; break; case XK_z: case XK_Z: bit = KBS_SHM; break; case XK_m: case XK_M: if (updn == 'd') kbstate ^= ((kbstate & KBS_SHIFT) ? KBS_H_C : KBS_H_R); break; case XK_comma: if (updn == 'd') kbstate ^= ((kbstate & KBS_SHIFT) ? KBS_H_M : KBS_H_G); break; case XK_period: if (updn == 'd') kbstate ^= ((kbstate & KBS_SHIFT) ? KBS_H_Y : KBS_H_B); 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 == 'd') && (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 += 100000; 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(); } int main(void); int main(void) { open_display(); find_visual(); setup_X(); setup_context(); create_window(); setup_gl(); setup_ticks(); setup_view(); while (1) tick(); }