#include #include #include #include #include #include #define WINX 400 #define WINY 400 extern const char *__progname; typedef struct xyz XYZ; struct xyz { double x; double y; double z; } ; static SDL_Surface *s; 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 double xyzlength(XYZ v) { return(sqrt((v.x*v.x)+(v.y*v.y)+(v.z*v.z))); } static double dot(XYZ a, XYZ b) { return((a.x*b.x)+(a.y*b.y)+(a.z*b.z)); } static XYZ xyzscale(XYZ v, double s) { return((XYZ){v.x*s,v.y*s,v.z*s}); } static XYZ xyzsub(XYZ a, XYZ b) { return((XYZ){a.x-b.x,a.y-b.y,a.z-b.z}); } static XYZ xyzadd(XYZ a, XYZ b) { return((XYZ){a.x+b.x,a.y+b.y,a.z+b.z}); } static XYZ xyzunit(XYZ v) { return(xyzscale(v,1/xyzlength(v))); } static XYZ xyzcross(XYZ a, XYZ b) { return((XYZ){ (a.y * b.z) - (a.z * b.y), (a.z * b.x) - (a.x * b.z), (a.x * b.y) - (a.y * b.x) }); } static XYZ xyzrotate(XYZ v, double a, XYZ axis) { double s; double c; s = sin(a); c = cos(a); return((XYZ) { (v.x * ((axis.x * axis.x * (1-c)) + c)) + (v.y * ((axis.x * axis.y * (1-c)) - (axis.z * s))) + (v.z * ((axis.x * axis.z * (1-c)) + (axis.y * s))), (v.x * ((axis.y * axis.x * (1-c)) + (axis.z * s))) + (v.y * ((axis.y * axis.y * (1-c)) + c)) + (v.z * ((axis.y * axis.z * (1-c)) - (axis.x * s))), (v.x * ((axis.z * axis.x * (1-c)) - (axis.y * s))) + (v.y * ((axis.z * axis.y * (1-c)) + (axis.x * s))) + (v.z * ((axis.z * axis.z * (1-c)) + c)) }); } static XYZ subtract_component(XYZ v, XYZ u) { return(xyzsub(v,xyzscale(u,dot(v,u)))); } static void setup_gl(void) { const SDL_VideoInfo *inf; if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) { fprintf(stderr,"%s: SDL_Init: %s\n",__progname,SDL_GetError()); exit(0); } inf = SDL_GetVideoInfo(); if (! inf) { fprintf(stderr,"%s: SDL_GetVideoInfo: %s\n",__progname,SDL_GetError()); SDL_Quit(); exit(0); } SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,1); s = SDL_SetVideoMode(WINX*2,WINY,24,SDL_OPENGL|(inf->hw_available?SDL_HWSURFACE:SDL_SWSURFACE)); if (! s) { fprintf(stderr,"%s: SDL_SetVideoMode: %s\n",__progname,SDL_GetError()); SDL_Quit(); exit(0); } SDL_ShowCursor(SDL_DISABLE); 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); nexttick.tv_usec = (nexttick.tv_usec / 10000) * 10000; } 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(); SDL_GL_SwapBuffers(); glFinish(); dbg = 0; } static void keystroke(char updn, unsigned int keycode) { unsigned int bit; bit = 0; switch (keycode) { case SDLK_LEFT: bit = KBS_L; break; case SDLK_RIGHT: bit = KBS_R; break; case SDLK_UP: bit = KBS_U; break; case SDLK_DOWN: bit = KBS_D; break; case SDLK_END: bit = KBS_CCW; break; case SDLK_PAGEDOWN: bit = KBS_CW; break; case SDLK_LSHIFT: bit = KBS_LSHF; break; case SDLK_RSHIFT: bit = KBS_RSHF; break; case SDLK_LCTRL: bit = KBS_LCTL; break; case SDLK_RCTRL: bit = KBS_RCTL; break; case SDLK_a: bit = KBS_SHP; break; case SDLK_z: bit = KBS_SHM; break; case SDLK_KP1: if (updn == 'd') kbstate ^= KBS_H_R; break; case SDLK_KP2: if (updn == 'd') kbstate ^= KBS_H_G; break; case SDLK_KP3: if (updn == 'd') kbstate ^= KBS_H_B; break; case SDLK_KP4: if (updn == 'd') kbstate ^= KBS_H_C; break; case SDLK_KP5: if (updn == 'd') kbstate ^= KBS_H_M; break; case SDLK_KP6: if (updn == 'd') kbstate ^= KBS_H_Y; break; case SDLK_x: if (updn == 'd') swapped = ! swapped; break; case SDLK_d: if (updn == 'd') dbg = 1; break; case SDLK_q: if ((updn == 'd') && (kbstate & KBS_SHIFT)) { SDL_Quit(); exit(0); } break; } switch (updn) { case 'u': kbstate &= ~bit; break; case 'd': kbstate |= bit; break; default: abort(); break; } } static void events(void) { SDL_Event ev; while (SDL_PollEvent(&ev)) { switch (ev.type) { case SDL_QUIT: SDL_Quit(); exit(0); break; case SDL_KEYDOWN: keystroke('d',ev.key.keysym.sym); break; case SDL_KEYUP: keystroke('u',ev.key.keysym.sym); break; } } } static void await(void) { struct timeval now; int ms; nexttick.tv_usec += 10000; 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 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 tick(void) { motion(); render(); events(); await(); } static void setup_game(void) { mx = (XYZ){1,0,0}; my = (XYZ){0,1,0}; mz = (XYZ){0,0,1}; shrink = 0; swapped = 0; } int main(void); int main(void) { setup_gl(); setup_ticks(); setup_game(); while (1) tick(); }