#include #include #include #include #include #include #include #include #include #include #include #include #include "3darith.h" #include "findvis.h" #include "mathutils.h" #define WINX 400 #define WINY 400 #define TEXSIZE 512 extern const char *__progname; typedef enum { MODE_MAIN = 1, MODE_MAP } MODE; typedef struct viewstate VIEWSTATE; typedef struct bm BM; /* * It would be conceptually easier to keep this state as the camera's * unit vectors (eg, forward, up, and right) as measured in world * coordinates. However, if we do that, we have to invert a * transformation matrix at rendering time. To make the render-time * operations easy, we have to instead maintain the converse: the * world basis vectors in eye coordinates. These are what are stored * in this struct. However, in order to make motion feasibly simple, * we also maintain the converse: the eye x, y, and z unit vectors in * world coordinates. * * We could simply maintain them separately. But then numerical * roundoff errors would cause the two sets, theoretically inverses of * one another, to drift out of sync. So, instead, we depend on * another property: rotation and movement are usually well-separated, * meaning it is feasible to maintain only one of them and invert the * matrix when the other is needed. Since we constantly need the one * for rendering, it is eye vectors in world coordinates that we * maintain lazily. These are not kept here; they are kept in * separate variables ex/ey/ez, with evalid a boolean indicating * whether ex/ey/ez are valid. * * This applies only to rotations. The player's location in world * coordinates is stored in p here, and is always valid. * * "eye coordinates" are a coordinate system whose Z axis points * opposite the view direction, whose X axis points in the camera's * "right" direction, and whose Y axis points in the camera's "up" * direction. The Z axis is negated from the obvious convention * because that's required by the combination of two other useful * properties: (1) eye X and Y coordinates are "the same as" (parallel * to and same sign as) screen X and Y coordinates and (2) the eye * coordinate system obeys the same right-hand rule as the world * coordinate system. */ struct viewstate { XYZ x; XYZ y; XYZ z; XYZ p; } ; struct bm { int w; int wb; int h; int xo; int yo; int adv; unsigned char *bits_b; unsigned char *bits_w; } ; 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 Window pwin; static int peq; static XColor bgcolour; static XColor fgcolour; static GC gc; static GC bitgc; static XFontStruct *font; static struct timeval nexttick; static unsigned int frameus; static struct timeval now; static unsigned int tex; static unsigned char texture[TEXSIZE+2][TEXSIZE+2][4]; static BM *digits[10]; static BM *comma; static VIEWSTATE v; static int evalid; static XYZ ex; static XYZ ey; static XYZ ez; static XYZ align_v[3]; static int align_i; static unsigned int kbstate; #define KBS_ML 0x00000001 #define KBS_MR 0x00000002 #define KBS_MU 0x00000004 #define KBS_MD 0x00000008 #define KBS_MF 0x00000010 #define KBS_MB 0x00000020 #define KBS_RL 0x00000040 #define KBS_RR 0x00000080 #define KBS_RU 0x00000100 #define KBS_RD 0x00000200 #define KBS_RCW 0x00000400 #define KBS_RCCW 0x00000800 #define KBS_LSHF 0x00001000 #define KBS_RSHF 0x00002000 #define KBS_LCTL 0x00004000 #define KBS_RCTL 0x00008000 #define KBS_ALIGN 0x00010000 #define KBS_SHIFT (KBS_LSHF|KBS_RSHF) #define KBS_CTRL (KBS_LCTL|KBS_RCTL) static int dbg; static int swapped; static int mzx = 3; static int mzy = 3; static int mzz = 3; static int mzmax; static int mzxy; static int mzwalls; static int mzcells; typedef unsigned short int MZBITS; static MZBITS *maze; #define SUBSCRIPT(x,y,z) ((x)+(mzx*((y)+(mzy*(z))))) #define Maze(x,y,z) maze[SUBSCRIPT(x,y,z)] #define MZ_PX 0x0001 #define MZ_MX 0x0002 #define MZ_PY 0x0004 #define MZ_MY 0x0008 #define MZ_PZ 0x0010 #define MZ_MZ 0x0020 #define MZ_S 0x0040 #define MZ_G 0x0080 #define MZ_P 0x0100 static unsigned int ticks; static GLuint mainlist; static GLuint maplist; static XYZI mzs3; static XYZI mzg3; static XYZI mzs10; static XYZI mzg10; static unsigned long int seed; static MODE mode; static VIEWSTATE vsave_main; static VIEWSTATE vsave_map; 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); p = XCreatePixmap(disp,rootwin,1,1,1); bitgc = XCreateGC(disp,p,0,0); XFreePixmap(disp,p); font = XQueryFont(disp,XGContextFromGC(XDefaultGCOfScreen(scr))); } 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 clip(int, int, int) __attribute__((__const__)); static int clip(int v, int min, int max) { return((vmax)?max:v); } static BM *setup_char_bitmap(char ch) { XCharStruct cs; int xte_dir; int xte_fa; int xte_fd; Pixmap pm; int w; int wb; int h; XImage *img; BM *bm; int x; int y; XTextExtents(font,&ch,1,&xte_dir,&xte_fa,&xte_fd,&cs); w = cs.rbearing - cs.lbearing; h = cs.ascent + cs.descent; pm = XCreatePixmap(disp,rootwin,w,h,1); XSetForeground(disp,bitgc,0); XFillRectangle(disp,pm,bitgc,0,0,w,h); XSetForeground(disp,bitgc,1); XDrawString(disp,pm,bitgc,-cs.lbearing,cs.ascent,&ch,1); img = XGetImage(disp,pm,0,0,w,h,1,XYPixmap); wb = (w + 2 + 7) >> 3; bm = malloc(sizeof(BM)); bm->w = w + 2; bm->wb = wb; bm->h = h + 2; bm->xo = - cs.lbearing; bm->yo = cs.descent; bm->adv = cs.width; bm->bits_b = malloc((h+2)*wb*2); bzero(bm->bits_b,(h+2)*wb*2); bm->bits_w = bm->bits_b + ((h+2) * wb); for (x=w-1;x>=0;x--) for (y=h-1;y>=0;y--) { if (XGetPixel(img,x,h-1-y)) { bm->bits_w[((y+1)*wb)+((x+1)>>3)] |= 0x80 >> ((x+1) & 7); bm->bits_b[((y+0)*wb)+((x+0)>>3)] |= 0x80 >> ((x+0) & 7); bm->bits_b[((y+0)*wb)+((x+1)>>3)] |= 0x80 >> ((x+1) & 7); bm->bits_b[((y+0)*wb)+((x+2)>>3)] |= 0x80 >> ((x+2) & 7); bm->bits_b[((y+1)*wb)+((x+0)>>3)] |= 0x80 >> ((x+0) & 7); bm->bits_b[((y+1)*wb)+((x+1)>>3)] |= 0x80 >> ((x+1) & 7); bm->bits_b[((y+1)*wb)+((x+2)>>3)] |= 0x80 >> ((x+2) & 7); bm->bits_b[((y+2)*wb)+((x+0)>>3)] |= 0x80 >> ((x+0) & 7); bm->bits_b[((y+2)*wb)+((x+1)>>3)] |= 0x80 >> ((x+1) & 7); bm->bits_b[((y+2)*wb)+((x+2)>>3)] |= 0x80 >> ((x+2) & 7); } } for (x=((h+2)*wb)-1;x>=0;x--) bm->bits_b[x] &= ~bm->bits_w[x]; XDestroyImage(img); XFreePixmap(disp,pm); return(bm); } static void setup_bitmaps(void) { int x; int y; double d; for (x=TEXSIZE+2-1;x>=0;x--) { for (y=TEXSIZE+2-1;y>=0;y--) { d = hypot(x-((TEXSIZE+2-1)/2.0),y-((TEXSIZE+2-1)/2.0)); if (d >= TEXSIZE/2.0) { texture[y][x][0] = 127; texture[y][x][1] = 127; texture[y][x][2] = 127; texture[y][x][3] = 255; } else { d = (d * 3) / (TEXSIZE/2.0); texture[y][x][0] = clip((d<1)?d*256:0,0,255); texture[y][x][1] = clip(((d>=1)&&(d<2))?(d-1)*256:0,0,255); texture[y][x][2] = clip((d>=2)?(d-2)*256:0,0,255); texture[y][x][3] = 255; } } } for (x=10-1;x>=0;x--) digits[x] = setup_char_bitmap("0123456789"[x]); comma = setup_char_bitmap(','); } static void setup_windows(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,WINY,0,depth,InputOutput,vi->visual,attrmask,&attr); XMapRaised(disp,win); attrmask = 0; attr.event_mask = PropertyChangeMask; attrmask |= CWEventMask; pwin = XCreateWindow(disp,win,-1,-1,1,1,0,0,InputOnly,vi->visual,attrmask,&attr); peq = 0; } static void setup_gl(void) { 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(); glFrustum(-.25,.25,-.25,.25,.25,(hypot(hypot(mzx,mzy),mzz)+1)*10); glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); glEnable(GL_POLYGON_OFFSET_FILL); glPixelStorei(GL_UNPACK_ALIGNMENT,1); glGenTextures(1,&tex); glBindTexture(GL_TEXTURE_2D,tex); 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,TEXSIZE+2,TEXSIZE+2,1,GL_RGBA,GL_UNSIGNED_BYTE,&texture[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_CW); glEnable(GL_CULL_FACE); kbstate = 0; } static const char *maze_bit_name(MZBITS) __attribute__((__unused__)); static const char *maze_bit_name(MZBITS bit) { switch (bit) { case MZ_PX: return("PX"); break; case MZ_MX: return("MX"); break; case MZ_PY: return("PY"); break; case MZ_MY: return("MY"); break; case MZ_PZ: return("PZ"); break; case MZ_MZ: return("MZ"); break; case MZ_S: return("S"); break; case MZ_G: return("G"); break; case MZ_P: return("P"); break; } return("?"); } static void draw_box(XYZI p0, XYZI p1) { glBegin(GL_QUAD_STRIP); glVertex3d(p0.x,p0.y,p0.z); glVertex3d(p0.x,p0.y,p1.z); glVertex3d(p1.x,p0.y,p0.z); glVertex3d(p1.x,p0.y,p1.z); glVertex3d(p1.x,p1.y,p0.z); glVertex3d(p1.x,p1.y,p1.z); glVertex3d(p0.x,p1.y,p0.z); glVertex3d(p0.x,p1.y,p1.z); glVertex3d(p0.x,p0.y,p0.z); glVertex3d(p0.x,p0.y,p1.z); glEnd(); glBegin(GL_QUADS); glVertex3d(p0.x,p0.y,p0.z); glVertex3d(p1.x,p0.y,p0.z); glVertex3d(p1.x,p1.y,p0.z); glVertex3d(p0.x,p1.y,p0.z); glVertex3d(p0.x,p0.y,p1.z); glVertex3d(p0.x,p1.y,p1.z); glVertex3d(p1.x,p1.y,p1.z); glVertex3d(p1.x,p0.y,p1.z); glEnd(); } /* * The way GL transformations work: * * If the current matrix is C and the coordinates presented to (eg) * glVertex3d are V, then the transformed point P is * * P = C V * * or * * [ P(0) ] = [ C( 0) C( 4) C( 8) C(12) ] [ V(0) ] * [ P(1) ] = [ C( 1) C( 5) C( 9) C(13) ] [ V(1) ] * [ P(2) ] = [ C( 2) C( 6) C(10) C(14) ] [ V(2) ] * [ P(3) ] = [ C( 3) C( 7) C(11) C(15) ] [ V(3) ] * * Calling glMultMatrix(M) does C = C M, resulting in * * [ P(0) ] = [ C( 0) C( 4) C( 8) C(12) ] [ M( 0) M( 4) M( 8) M(12) ] [ V(0) ] * [ P(1) ] = [ C( 1) C( 5) C( 9) C(13) ] [ M( 1) M( 5) M( 9) M(13) ] [ V(1) ] * [ P(2) ] = [ C( 2) C( 6) C(10) C(14) ] [ M( 2) M( 6) M(10) M(14) ] [ V(2) ] * [ P(3) ] = [ C( 3) C( 7) C(11) C(15) ] [ M( 3) M( 7) M(11) M(15) ] [ V(3) ] * * Since we are transorming a world-coordinate V into an * eye-coordinate P, the values we need for M are the ones in v.x, * v.y, and v.z. */ static void drawstuff(void) { GLdouble m[16]; m[0] = v.x.x; m[1] = v.x.y; m[2] = v.x.z; m[3] = 0; m[4] = v.y.x; m[5] = v.y.y; m[6] = v.y.z; m[7] = 0; m[8] = v.z.x; m[9] = v.z.y; m[10] = v.z.z; m[11] = 0; m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; glMultMatrixd(&m[0]); glTranslated(-v.p.x,-v.p.y,-v.p.z); switch (mode) { case MODE_MAIN: glCallList(mainlist); /* goal marker */ if (now.tv_usec >= 500000) { glColor3f(.333,0,.333); } else { glColor3f(1,1,.333); } #if 0 draw_box( (XYZI){mzg10.x+5,mzg10.y+5,mzg10.z+5}, (XYZI){mzg10.x+6,mzg10.y+6,mzg10.z+6} ); #endif draw_box( (XYZI){mzg10.x+7,mzg10.y+7,mzg10.z+7}, (XYZI){mzg10.x+8,mzg10.y+8,mzg10.z+8} ); break; case MODE_MAP: glCallList(maplist); /* goal marker */ if (now.tv_usec >= 500000) { glColor3f(.333,0,.333); } else { glColor3f(1,1,.333); } draw_box( (XYZI){mzg3.x+1,mzg3.y+1,mzg3.z+1}, (XYZI){mzg3.x+2,mzg3.y+2,mzg3.z+2} ); break; } } static void render(void) { glViewport(0,0,WINX,WINY); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glViewport(0,0,WINX,WINY); drawstuff(); glXSwapBuffers(disp,win); glXWaitGL(); glXWaitX(); while (peq > 0) { XEvent e; XWindowEvent(disp,pwin,PropertyChangeMask,&e); peq --; } XChangeProperty(disp,pwin,XA_STRING,XA_STRING,8,PropModeAppend,"",0); peq ++; } static void setup_ticks(void) { gettimeofday(&nexttick,0); frameus = 10000; ticks = 0; } static void rotate_world(double a, XYZ axis) { v.x = xyzunit(xyzrotate(v.x,a,axis)); v.y = xyzunit(subtract_component(xyzrotate(v.y,a,axis),v.x)); v.z = xyzcross(v.x,v.y); evalid = 0; } /* * We basically have to invert the matrix * * [ rx.x ry.x rz.x ] * [ rx.y ry.y rz.y ] * [ rx.z ry.z rz.z ] * * Fortunately, because it's a rotation matrix, it's about as far from * singular as can be; its determinant is, in theory, exactly 1. * * The closed-form inverse of * * [ A B C ] * [ D E F ] * [ G H I ] * * is * * [ EI-FH CH-BI BF-CE ] / * [ FG-DI AI-CG CD-AF ] / AEI+BFG+CDH-AFH-BDI-CEG * [ DH-EG BG-AH AE-BD ] / * * We save some multiplications by writing the determinant as * A(EI-FH)+B(FG-DI)+C(DH-EG), for 9 multiplies and 5 adds, versus the * above, which is 12 multiplies and 5 adds. * * Arguably, we could omit the determinant because it's theoretically * 1. I'm not sure whether I think that's a good idea or not. */ static void invert_rotations(XYZ rx, XYZ ry, XYZ rz, XYZ *ix, XYZ *iy, XYZ *iz) { double det; det = (rx.x * ((ry.y * rz.z) - (rz.y * ry.z))) + (ry.x * ((rz.y * rx.z) - (rx.y * rz.z))) + (rz.x * ((rx.y * ry.z) - (ry.y * rx.z))); *ix = (XYZ) { ((ry.y * rz.z) - (rz.y * ry.z)) / det, ((rz.y * rx.z) - (rx.y * rz.z)) / det, ((rx.y * ry.z) - (ry.y * rx.z)) / det }; *iy = (XYZ) { ((rz.x * ry.z) - (ry.x * rz.z)) / det, ((rx.x * rz.z) - (rz.x * rx.z)) / det, ((ry.x * rx.z) - (rx.x * ry.z)) / det }; *iz = (XYZ) { ((ry.x * rz.y) - (rz.x * ry.y)) / det, ((rz.x * rx.y) - (rx.x * rz.y)) / det, ((rx.x * ry.y) - (ry.x * rx.y)) / det }; } static void ensure_e(void) { if (evalid) return; invert_rotations(v.x,v.y,v.z,&ex,&ey,&ez); evalid = 1; } static void w_from_e(void) { invert_rotations(ex,ey,ez,&v.x,&v.y,&v.z); evalid = 1; } static void moveby(XYZ dir, double s) { XYZ nploc; int x; int y; int z; unsigned char bits; double px; double py; double d; nploc = xyzadd(v.p,xyzscale(dir,s*.1)); switch (mode) { case MODE_MAIN: x = (nploc.x - .5) / 10; y = (nploc.y - .5) / 10; z = (nploc.z - .5) / 10; if (x < 0) x = 0; else if (x >= mzx) x = mzx - 1; if (y < 0) y = 0; else if (y >= mzy) y = mzy - 1; if (z < 0) z = 0; else if (z >= mzz) z = mzz - 1; bits = Maze(x,y,z); x *= 10; y *= 10; z *= 10; if (!(bits & MZ_PX) && (nploc.x > x+9)) nploc.x = x + 9; if (!(bits & MZ_MX) && (nploc.x < x+2)) nploc.x = x + 2; if (!(bits & MZ_PY) && (nploc.y > y+9)) nploc.y = y + 9; if (!(bits & MZ_MY) && (nploc.y < y+2)) nploc.y = y + 2; if (!(bits & MZ_PZ) && (nploc.z > z+9)) nploc.z = z + 9; if (!(bits & MZ_MZ) && (nploc.z < z+2)) nploc.z = z + 2; #if 0 if (nploc.x < x+5) px = x + .5; else px = x + 10.5; if (nploc.y < y+5) py = y + .5; else py = y + 10.5; if (nploc.z < z+5) pz = z + .5; else pz = z + 10.5; if ((fabs(nploc.x-px) <= 1.5) && (fabs(nploc.y-py) <= 1.5)) { if (fabs(nploc.x-px) <= .5) { if ((nploc.y > py) && (nploc.y < py+1.5)) nploc.y = py + 1.5; else if ((nploc.y < py) && (nploc.y > py-1.5)) nploc.y = py - 1.5; } else if (fabs(nploc.y-py) <= .5) { if ((nploc.x > px) && (nploc.x < px+1.5)) nploc.x = px + 1.5; else if ((nploc.x < px) && (nploc.x > px-1.5)) nploc.x = px - 1.5; } else { if (nploc.x < px) px -= .5; else px += .5; if (nploc.y < py) py -= .5; else py += .5; d = hypot(px-nploc.x,py-nploc.y); if (d < 1) { nploc.x = px - ((px - nploc.x) / d); nploc.y = py - ((py - nploc.y) / d); } } } #else d=0;px=0;py=0; #endif break; case MODE_MAP: break; default: abort(); break; } v.p = nploc; } static void do_rotate(double a, XYZ axis) { switch (mode) { case MODE_MAIN: rotate_world(a,axis); break; case MODE_MAP: ensure_e(); axis = xyzMv(ex,ey,ez,axis); v.p = xyzrotate(v.p,-a,axis); ez = xyzunit(xyzrotate(ez,-a,axis)); ey = xyzunit(subtract_component(xyzrotate(ey,-a,axis),ez)); ex = xyzcross(ey,ez); w_from_e(); break; default: abort(); break; } } static void motion(void) { double f; XYZ axis; double angle; if (kbstate & KBS_ALIGN) { XYZ tx; XYZ ty; XYZ tz; if (align_i < 1) { ex = align_v[0]; ey = align_v[1]; ez = align_v[2]; w_from_e(); kbstate &= ~KBS_ALIGN; return; } ensure_e(); tx = xyzblend(ex,1.0/align_i,align_v[0]); ty = xyzblend(ey,1.0/align_i,align_v[1]); tz = xyzblend(ez,1.0/align_i,align_v[2]); ez = xyzunit(tz); ey = xyzunit(subtract_component(ty,ez)); ex = xyzcross(ey,ez); w_from_e(); align_i --; return; } if (kbstate & KBS_SHIFT) f = 25; else if (kbstate & KBS_CTRL) f = 1; else f = 5; f *= .3; switch (kbstate & (KBS_ML|KBS_MR)) { case KBS_ML: ensure_e(); moveby(ex,-f); break; case KBS_MR: ensure_e(); moveby(ex,f); break; } switch (kbstate & (KBS_MU|KBS_MD)) { case KBS_MU: ensure_e(); moveby(ey,f); break; case KBS_MD: ensure_e(); moveby(ey,-f); break; } switch (kbstate & (KBS_MF|KBS_MB)) { case KBS_MF: ensure_e(); moveby(ez,-f); break; case KBS_MB: ensure_e(); moveby(ez,f); break; } switch (kbstate & (KBS_RL|KBS_RR)) { case KBS_RL: do_rotate(-f,(XYZ){0,1,0}); break; case KBS_RR: do_rotate(f,(XYZ){0,1,0}); break; } switch (kbstate & (KBS_RU|KBS_RD)) { case KBS_RU: do_rotate(-f,(XYZ){1,0,0}); break; case KBS_RD: do_rotate(f,(XYZ){1,0,0}); break; } switch (kbstate & (KBS_RCW|KBS_RCCW)) { case KBS_RCW: rotate_world(-f,(XYZ){0,0,1}); break; case KBS_RCCW: rotate_world(f,(XYZ){0,0,1}); break; } switch (mode) { case MODE_MAIN: break; case MODE_MAP: ensure_e(); if (dbg) { printf("v.x = (%g,%g,%g)\n",v.x.x,v.x.y,v.x.z); printf("v.y = (%g,%g,%g)\n",v.y.x,v.y.y,v.y.z); printf("v.z = (%g,%g,%g)\n",v.z.x,v.z.y,v.z.z); printf("v.p = (%g,%g,%g)\n",v.p.x,v.p.y,v.p.z); printf("ex = (%g,%g,%g)\n",ex.x,ex.y,ex.z); printf("ey = (%g,%g,%g)\n",ey.x,ey.y,ey.z); printf("ez = (%g,%g,%g)\n",ez.x,ez.y,ez.z); } axis = xyzcross(v.p,xyzscale(ez,-1)); f = xyzlength(axis) / xyzlength(v.p); if (dbg) printf("axis = (%g,%g,%g) f = %g\n",axis.x,axis.y,axis.z,f); if (f > 1e-3) { angle = asindeg(f); if (dbg) printf("angle = %g\n",angle); axis = xyzunit(axis); ez = xyzunit(xyzrotate(ez,angle,axis)); ey = xyzunit(subtract_component(xyzrotate(ey,angle,axis),ez)); ex = xyzcross(ey,ez); w_from_e(); if (dbg) { printf("ex = (%g,%g,%g)\n",ex.x,ex.y,ex.z); printf("ey = (%g,%g,%g)\n",ey.x,ey.y,ey.z); printf("ez = (%g,%g,%g)\n",ez.x,ez.y,ez.z); printf("v.x = (%g,%g,%g)\n",v.x.x,v.x.y,v.x.z); printf("v.y = (%g,%g,%g)\n",v.y.x,v.y.y,v.y.z); printf("v.z = (%g,%g,%g)\n",v.z.x,v.z.y,v.z.z); } } dbg = 0; break; default: abort(); break; } } static void axis_align(void) { static const XYZ axes[6] = { { 0, 0, 1 }, { 0, 1, 0 }, { 1, 0, 0 }, { 0, 0,-1 }, { 0,-1, 0 }, { -1, 0, 0 } }; int i; int bi; double bestd; double d; XYZ t; double az; double ay; if (mode != MODE_MAIN) return; ensure_e(); bestd = -2; for (i=6-1;i>=0;i--) { d = dot(axes[i],ez); if (d > bestd) { bestd = d; bi = i; } } if (bestd < 0) abort(); align_v[2] = axes[bi]; t = subtract_component(ey,axes[bi]); bestd = -2; for (i=6-1;i>=0;i--) { d = dot(axes[i],t); if (d > bestd) { bestd = d; bi = i; } } if (bestd < 0) abort(); align_v[1] = axes[bi]; align_v[0] = xyzcross(align_v[1],align_v[2]); az = acos(dot(ez,align_v[2])); ay = acos(dot(ey,align_v[1])); d = (az > ay) ? az : ay; align_i = (d / .04) + 1; kbstate |= KBS_ALIGN; } static void switch_mode(void) { MODE m; kbstate &= ~KBS_ALIGN; switch (mode) { case MODE_MAIN: vsave_main = v; m = MODE_MAP; break; case MODE_MAP: vsave_map = v; m = MODE_MAIN; break; default: abort(); break; } mode = m; switch (mode) { case MODE_MAIN: v = vsave_main; break; case MODE_MAP: v = vsave_map; break; default: abort(); break; } evalid = 0; } static void keystroke(XKeyEvent *ev, char updn) { KeySym ks; unsigned int bit; ks = XLookupKeysym(ev,0); bit = 0; switch (ks) { case XK_w: case XK_W: bit = KBS_MU; break; case XK_s: case XK_S: bit = KBS_MD; break; case XK_a: case XK_A: bit = KBS_ML; break; case XK_d: case XK_D: bit = KBS_MR; break; case XK_r: case XK_R: bit = KBS_MF; break; case XK_f: case XK_F: bit = KBS_MB; break; case XK_i: case XK_I: bit = KBS_RU; break; case XK_k: case XK_K: bit = KBS_RD; break; case XK_j: case XK_J: bit = KBS_RL; break; case XK_l: case XK_L: bit = KBS_RR; break; case XK_u: case XK_U: bit = KBS_RCCW; break; case XK_o: case XK_O: bit = KBS_RCW; 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_period: if (updn == 'd') axis_align(); break; case XK_m: case XK_M: if (updn == 'd') switch_mode(); break; case XK_x: case XK_X: if (updn == 'd') swapped = ! swapped; break; case XK_q: case XK_Q: if ((updn == 'u') && (kbstate & KBS_SHIFT)) exit(0); break; case XK_1: if (updn == 'd') dbg = 1; 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; case PropertyNotify: /* XPropertyEvent - xproperty */ if (ev.xproperty.window == pwin) peq --; break; } } } static void await(void) { int ms; nexttick.tv_usec += frameus; if (nexttick.tv_usec >= 1000000) { nexttick.tv_usec -= 1000000; nexttick.tv_sec ++; } gettimeofday(&now,0); if ( (now.tv_sec > nexttick.tv_sec) || ( (now.tv_sec == nexttick.tv_sec) && (now.tv_usec >= nexttick.tv_usec) ) ) { frameus += 10000; nexttick = now; return; } ms = ((nexttick.tv_sec - now.tv_sec) * 1000) + 1 + (nexttick.tv_usec / 1000) - (now.tv_usec / 1000); if (ms < 20) { frameus += 10000; } else { frameus -= 10; } if (ms < 1) ms = 1; poll(0,0,ms); gettimeofday(&now,0); } static void tick(void) { motion(); render(); events(); await(); ticks ++; } static void setup_view(void) { v.p = xyzscale((XYZ){1,1,1},2*mzmax); ez = xyzunit(v.p); ey = xyzunit(subtract_component((XYZ){0,0,1},ez)); ex = xyzcross(ey,ez); w_from_e(); vsave_map = v; v.p = (XYZ){3,3,3}; ez = xyzunit((XYZ){-1,-1,0}); ey = xyzunit(subtract_component((XYZ){0,0,1},ez)); ex = xyzcross(ey,ez); w_from_e(); mode = MODE_MAIN; evalid = 1; swapped = 0; } static void setup_input(void) { XGrabKeyboard(disp,win,False,GrabModeAsync,GrabModeAsync,CurrentTime); } static void draw_wall(XYZI p0, XYZI u, XYZI r) { glTexCoord2d(0,0); glVertex3d(p0.x,p0.y,p0.z); glTexCoord2d(0,1); glVertex3d(p0.x+u.x,p0.y+u.y,p0.z+u.z); glTexCoord2d(1,1); glVertex3d(p0.x+u.x+r.x,p0.y+u.y+r.y,p0.z+u.z+r.z); glTexCoord2d(1,0); glVertex3d(p0.x+r.x,p0.y+r.y,p0.z+r.z); } static void setup_main_list(void) { int x; int y; int z; mainlist = glGenLists(1); glNewList(mainlist,GL_COMPILE); glPolygonOffset(0,1); mzs3.x = -1; mzg3.x = -1; /* walls */ glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,tex); glBegin(GL_QUADS); for (x=mzx-1;x>=0;x--) for (y=mzy-1;y>=0;y--) for (z=mzz-1;z>=0;z--) { MZBITS b; b = Maze(x,y,z); if (! (b & MZ_PX)) { draw_wall( (XYZI){(x+1)*10,(y*10)+1,(z*10)+1}, (XYZI){0,9,0}, (XYZI){0,0,9} ); } if (! (b & MZ_MX)) { draw_wall( (XYZI){(x*10)+1,(y*10)+1,(z*10)+1}, (XYZI){0,0,9}, (XYZI){0,9,0} ); } if (! (b & MZ_PY)) { draw_wall( (XYZI){(x*10)+1,(y+1)*10,(z*10)+1}, (XYZI){0,0,9}, (XYZI){9,0,0} ); } if (! (b & MZ_MY)) { draw_wall( (XYZI){(x*10)+1,(y*10)+1,(z*10)+1}, (XYZI){9,0,0}, (XYZI){0,0,9} ); } if (! (b & MZ_PZ)) { draw_wall( (XYZI){(x*10)+1,(y*10)+1,(z+1)*10}, (XYZI){9,0,0}, (XYZI){0,9,0} ); } if (! (b & MZ_MZ)) { draw_wall( (XYZI){(x*10)+1,(y*10)+1,(z*10)+1}, (XYZI){0,9,0}, (XYZI){9,0,0} ); } if (b & MZ_S) { mzs3 = (XYZI){x*3,y*3,z*3}; mzs10 = (XYZI){x*10,y*10,z*10}; } if (b & MZ_G) { mzg3 = (XYZI){x*3,y*3,z*3}; mzg10 = (XYZI){x*10,y*10,z*10}; } } glEnd(); glDisable(GL_TEXTURE_2D); /* start marker */ if (mzs3.x < 0) abort(); glColor3f(.333,.333,.667); #if 0 draw_box( (XYZI){mzs10.x+5,mzs10.y+5,mzs10.z+5}, (XYZI){mzs10.x+6,mzs10.y+6,mzs10.z+6} ); #else draw_box( (XYZI){mzs10.x+3,mzs10.y+3,mzs10.z+3}, (XYZI){mzs10.x+4,mzs10.y+4,mzs10.z+4} ); #endif /* goal marker is done dynamically, so it can flash */ if (mzg3.x < 0) abort(); /* "structural" skeleton */ for (x=mzx;x>=0;x--) for (y=mzy;y>=0;y--) for (z=mzz;z>=0;z--) { glColor3f(.5,.5,.5); draw_box( (XYZI){x*10,y*10,z*10}, (XYZI){(x*10)+1,(y*10)+1,(z*10)+1} ); if (x < mzx) { glColor3f(.75,.3,.3); draw_box( (XYZI){(x*10)+1,y*10,z*10}, (XYZI){(x+1)*10,(y*10)+1,(z*10)+1} ); } if (y < mzy) { glColor3f(.3,.75,.3); draw_box( (XYZI){x*10,(y*10)+1,z*10}, (XYZI){(x*10)+1,(y+1)*10,(z*10)+1} ); } if (z < mzz) { glColor3f(.3,.3,.75); draw_box( (XYZI){x*10,y*10,(z*10)+1}, (XYZI){(x*10)+1,(y*10)+1,(z+1)*10} ); } } /* cell-centre markers */ /* * glDrawPixels appears to be broken - it ignores alpha values. So, * if we want nonrectangular images we have to use glBitmap. * Multicolour nonrectangular images, multiple glBitmap calls. * * Grrr. * * Furthermore, doing * * glRasterPos3f(...); * glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID,&v); * if (v == GL_TRUE) * { glColor3f(...colour A...); * glBitmap(27,27,13,13,0,0,...bitmap A...); * glColor3f(...colour B...); * glBitmap(27,27,13,13,0,0,...bitmap B...); * } * * draws both bitmaps in colour B! Double grrr. * * Repeating the glRasterPos/glGet "fixes" this last...after a * fashion. Doing just a single * * glRasterPos3f(...); * glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID,&v); * if (v == GL_TRUE) * { glColor3f(...colour X...); * glBitmap(27,27,13,13,0,0,...bitmap X...); * } * * works right, for colour and bitmap either A or B. But doing * * glRasterPos3f(...); * glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID,&v); * if (v == GL_TRUE) * { glColor3f(...colour A...); * glBitmap(27,27,13,13,0,0,...bitmap A...); * } * glRasterPos3f(...); * glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID,&v); * if (v == GL_TRUE) * { glColor3f(...colour B...); * glBitmap(27,27,13,13,0,0,...bitmap B...); * } * * draws bitmap A in colour B and bitmap B in colour A. I don't know * WTF is going on here. Easter-egging, I found the code below, * which seems to work for me. */ { GLboolean v; char *s; int sl; int i; int x; int xmin; int xmax; int ymin; int ymax; BM *bm; BM **bmv; int c; glPixelStorei(GL_UNPACK_ALIGNMENT,1); glPixelStorei(GL_UNPACK_ROW_LENGTH,0); glPixelStorei(GL_UNPACK_LSB_FIRST,0); for (c=1;c>=0;c--) { glColor3f(c,c,c); for (x=mzx-1;x>=0;x--) for (y=mzy-1;y>=0;y--) for (z=mzz-1;z>=0;z--) { glRasterPos3f((x*10)+5.5,(y*10)+5.5,(z*10)+5.5); glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID,&v); if (v == GL_TRUE) { sl = asprintf(&s,"%d,%d,%d",x,y,z); bmv = malloc(sl*sizeof(BM *)); for (i=0;s[i];i++) { switch (s[i]) { case '0': bm = digits[0]; break; case '1': bm = digits[1]; break; case '2': bm = digits[2]; break; case '3': bm = digits[3]; break; case '4': bm = digits[4]; break; case '5': bm = digits[5]; break; case '6': bm = digits[6]; break; case '7': bm = digits[7]; break; case '8': bm = digits[8]; break; case '9': bm = digits[9]; break; case ',': bm = comma; break; default: abort(); break; } if ((i == 0) || (x-bm->xo < xmin)) xmin = x - bm->xo; if ((i == 0) || (x+bm->w-bm->xo > xmax)) xmax = x + bm->w - bm->xo; if ((i == 0) || (-bm->yo < ymin)) ymin = - bm->yo; if ((i == 0) || (bm->h-bm->yo > ymax)) ymax = bm->h - bm->yo; x += bm->adv; bmv[i] = bm; } glBitmap(0,0,0,0,(xmin-xmax)/2,(ymin-ymax)/2,0); x = xmin; for (i=0;s[i];i++) { bm = bmv[i]; glBitmap(bm->w,bm->h,bm->xo,bm->yo,bm->adv,0,c?bm->bits_w:bm->bits_b); } free(s); free(bmv); } } } } glEndList(); } static void draw_textured_box(XYZI p0, XYZI p1) { draw_wall( (XYZI) p0, (XYZI) { p1.x-p0.x, 0, 0 }, (XYZI) { 0, p1.y-p0.y, 0 } ); draw_wall( (XYZI) p0, (XYZI) { 0, p1.y-p0.y, 0 }, (XYZI) { 0, 0, p1.z-p0.z } ); draw_wall( (XYZI) p0, (XYZI) { 0, 0, p1.z-p0.z }, (XYZI) { p1.x-p0.x, 0, 0 } ); draw_wall( (XYZI) p1, (XYZI) { 0, p0.y-p1.y, 0 }, (XYZI) { p0.x-p1.x, 0, 0 } ); draw_wall( (XYZI) p1, (XYZI) { 0, 0, p0.z-p1.z }, (XYZI) { 0, p0.y-p1.y, 0 } ); draw_wall( (XYZI) p1, (XYZI) { p0.x-p1.x, 0, 0 }, (XYZI) { 0, 0, p0.z-p1.z } ); } static void setup_map_list(void) { int x; int y; int z; maplist = glGenLists(1); glNewList(maplist,GL_COMPILE); glPolygonOffset(0,1); glTranslated(-mzx*1.5,-mzy*1.5,-mzz*1.5); /* walls */ glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,tex); for (x=mzx-1;x>=0;x--) for (y=mzy-1;y>=0;y--) for (z=mzz-1;z>=0;z--) { if (! (Maze(x,y,z) & MZ_P)) continue; if (! (Maze(x,y,z) & (MZ_S|MZ_G))) { glBegin(GL_QUADS); draw_textured_box((XYZI){(x*3)+1,(y*3)+1,(z*3)+1},(XYZI){(x*3)+2,(y*3)+2,(z*3)+2}); glEnd(); } glColor3f(.5,.5,.5); if ( (Maze(x,y,z) & MZ_MX) && (Maze(x-1,y,z) & MZ_P) ) draw_box((XYZI){(x*3)+0,(y*3)+1,(z*3)+1},(XYZI){(x*3)+1,(y*3)+2,(z*3)+2}); if ( (Maze(x,y,z) & MZ_PX) && (Maze(x+1,y,z) & MZ_P) ) draw_box((XYZI){(x*3)+2,(y*3)+1,(z*3)+1},(XYZI){(x*3)+3,(y*3)+2,(z*3)+2}); if ( (Maze(x,y,z) & MZ_MY) && (Maze(x,y-1,z) & MZ_P) ) draw_box((XYZI){(x*3)+1,(y*3)+0,(z*3)+1},(XYZI){(x*3)+2,(y*3)+1,(z*3)+2}); if ( (Maze(x,y,z) & MZ_PY) && (Maze(x,y+1,z) & MZ_P) ) draw_box((XYZI){(x*3)+1,(y*3)+2,(z*3)+1},(XYZI){(x*3)+2,(y*3)+3,(z*3)+2}); if ( (Maze(x,y,z) & MZ_MZ) && (Maze(x,y,z-1) & MZ_P) ) draw_box((XYZI){(x*3)+1,(y*3)+1,(z*3)+0},(XYZI){(x*3)+2,(y*3)+2,(z*3)+1}); if ( (Maze(x,y,z) & MZ_PZ) && (Maze(x,y,z+1) & MZ_P) ) draw_box((XYZI){(x*3)+1,(y*3)+1,(z*3)+2},(XYZI){(x*3)+2,(y*3)+2,(z*3)+3}); } glDisable(GL_TEXTURE_2D); /* start marker */ if (mzs3.x < 0) abort(); glColor3f(.333,.333,.667); draw_box( (XYZI){mzs3.x+1,mzs3.y+1,mzs3.z+1}, (XYZI){mzs3.x+2,mzs3.y+2,mzs3.z+2} ); /* goal marker is done dynamically, so it can flash */ if (mzg3.x < 0) abort(); glEndList(); } static void setup_lists(void) { setup_main_list(); setup_map_list(); } static int set_sizes_from_string(const char *s0) { const char *s; char *e; long int x; long int y; long int z; x = strtol(s0,&e,10); if ((e == s0) || (*e != 'x')) return(1); s = e + 1; y = strtol(s,&e,10); if ((e == s) || (*e != 'x')) return(1); s = e + 1; z = strtol(s,&e,10); if ((e == s) || *e) return(1); if ((x < 2) || (y < 2) || (z < 2) || (x > 32) || (y > 32) || (z > 32)) return(1); mzx = x; mzy = y; mzz = z; return(0); } static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: extra argument `%s'\n",__progname,*av); errs = 1; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-size")) { WANTARG(); if (set_sizes_from_string(av[skip])) { fprintf(stderr,"%s: bad size string `%s'\n",__progname,av[skip]); errs = 1; } continue; } if (!strcmp(*av,"-seed")) { WANTARG(); seed = strtoul(av[skip],0,0); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } static void setup_maze(void) { typedef struct { unsigned char x; unsigned char y; unsigned char z; MZBITS bit; } WALL; WALL *walls; int *cells; int x; int y; int z; int i; int nw; int zc; int wi; WALL w; int c; int openit; MZBITS obit; int done(void) { int i; if (zc > 0) return(0); for (i=mzcells-1;i>=0;i--) if (cells[i] != 1) return(0); return(1); } void renumber(int a, int b) { int i; if (b < a) { int t; t = a; a = b; b = t; } for (i=mzcells-1;i>=0;i--) if (cells[i] == b) cells[i] = a; } mzxy = mzx * mzy; mzwalls = (mzx * mzy * mzz * 3) - (mzx * mzy) - (mzy * mzz) - (mzz * mzx); mzcells = mzx * mzy * mzz; mzmax = mzx; if (mzy > mzmax) mzmax = mzy; if (mzz > mzmax) mzmax = mzz; maze = malloc(mzcells*sizeof(*maze)); bzero(maze,mzcells*sizeof(*maze)); if (seed == 0) { seed = time(0); seed = ((seed >> 16) & 0xffff) | ((seed & 0xffff) << 16); seed = ((seed >> 8) & 0xff00ff) | ((seed & 0xff00ff) << 8); seed = ((seed >> 4) & 0xf0f0f0f) | ((seed & 0xf0f0f0f) << 4); seed = ((seed >> 2) & 0x33333333) | ((seed & 0x33333333) << 2); seed = ((seed >> 1) & 0x55555555) | ((seed & 0x55555555) << 1); } printf("seed = %lu\n",seed); srandom(seed); nw = 0; cells = malloc(mzcells*sizeof(*cells)); walls = malloc(mzwalls*sizeof(*walls)); i = 0; for (x=mzx-1;x>=0;x--) for (y=mzy-1;y>=0;y--) for (z=mzz-1;z>=0;z--) { cells[i++] = 0; if (x > 0) walls[nw++] = (WALL){.x=x,.y=y,.z=z,.bit=MZ_MX}; if (y > 0) walls[nw++] = (WALL){.x=x,.y=y,.z=z,.bit=MZ_MY}; if (z > 0) walls[nw++] = (WALL){.x=x,.y=y,.z=z,.bit=MZ_MZ}; } if (nw != mzwalls) abort(); zc = mzcells; c = 1; while (! done()) { wi = random() % nw; w = walls[wi]; nw --; if (wi < nw) walls[wi] = walls[nw]; wi = w.x + (mzx * (w.y + (mzy * w.z))); i = wi; switch (w.bit) { case MZ_MX: x = w.x - 1; y = w.y; z = w.z; obit = MZ_PX; i --; break; case MZ_MY: x = w.x; y = w.y - 1; z = w.z; obit = MZ_PY; i -= mzx; break; case MZ_MZ: x = w.x; y = w.y; z = w.z - 1; obit = MZ_PZ; i -= mzx * mzy; break; default: abort(); break; } openit = 1; if (cells[wi]) { if (cells[i]) { if (cells[i] == cells[wi]) { openit = 0; } else { renumber(cells[wi],cells[i]); } } else { cells[i] = cells[wi]; zc --; } } else { if (cells[i]) { cells[wi] = cells[i]; zc --; } else { cells[i] = c; cells[wi] = c; c ++; zc -= 2; } } if (openit) { maze[wi] |= w.bit; maze[i] |= obit; } } maze[0] |= MZ_S; maze[mzcells-1] |= MZ_G; { int walk(int i) { if (! cells[i]) { cells[i] = 1; if ( (maze[i] & MZ_G) || ((maze[i] & MZ_PX) && walk(i+1)) || ((maze[i] & MZ_MX) && walk(i-1)) || ((maze[i] & MZ_PY) && walk(i+mzx)) || ((maze[i] & MZ_MY) && walk(i-mzx)) || ((maze[i] & MZ_PZ) && walk(i+mzxy)) || ((maze[i] & MZ_MZ) && walk(i-mzxy)) ) { cells[i] = 2; maze[i] |= MZ_P; return(1); } } return(0); } for (i=mzcells-1;i>=0;i--) cells[i] = 0; walk(0); } for (x=mzx-1;x>=0;x--) for (y=mzy-1;y>=0;y--) for (z=mzz-1;z>=0;z--) { if ( ( (x > 0) && ( ( (Maze(x-1,y,z) & MZ_PX) && !(Maze(x,y,z) & MZ_MX) ) || ( !(Maze(x-1,y,z) & MZ_PX) && (Maze(x,y,z) & MZ_MX) ) ) ) || ( (y > 0) && ( ( (Maze(x,y-1,z) & MZ_PY) && !(Maze(x,y,z) & MZ_MY) ) || ( !(Maze(x,y-1,z) & MZ_PY) && (Maze(x,y,z) & MZ_MY) ) ) ) || ( (z > 0) && ( ( (Maze(x,y,z-1) & MZ_PZ) && !(Maze(x,y,z) & MZ_MZ) ) || ( !(Maze(x,y,z-1) & MZ_PZ) && (Maze(x,y,z) & MZ_MZ) ) ) ) || ((x == 0) && (Maze(x,y,z) & MZ_MX)) || ((y == 0) && (Maze(x,y,z) & MZ_MY)) || ((z == 0) && (Maze(x,y,z) & MZ_MZ)) || ((x == mzx-1) && (Maze(x,y,z) & MZ_PX)) || ((y == mzy-1) && (Maze(x,y,z) & MZ_PY)) || ((z == mzz-1) && (Maze(x,y,z) & MZ_PZ)) ) abort(); } for (z=mzmax-1;z>=0;z--) { int sel1(int a, int b __attribute__((__unused__)), int c __attribute__((__unused__))) { return(a); } int sel2(int a __attribute__((__unused__)), int b, int c __attribute__((__unused__))) { return(b); } int sel3(int a __attribute__((__unused__)), int b __attribute__((__unused__)), int c) { return(c); } int sub123(int x, int y, int z) { return(SUBSCRIPT(x,y,z)); } int sub312(int x, int y, int z) { return(SUBSCRIPT(y,z,x)); } int sub231(int x, int y, int z) { return(SUBSCRIPT(z,x,y)); } void call_line(void (*fn)( int (*)(int, int, int), int (*)(int, int, int), int (*)(int, int, int), int (*)(int, int, int) )) { (*fn)(&sel1,&sel2,&sel3,&sub123); printf(" "); (*fn)(&sel3,&sel1,&sel2,&sub312); printf(" "); (*fn)(&sel2,&sel3,&sel1,&sub231); printf("\n"); } #define CLARGS int (*sx)(int, int, int) __attribute__((__unused__)),\ int (*sy)(int, int, int) __attribute__((__unused__)),\ int (*sz)(int, int, int) __attribute__((__unused__)),\ int (*sub)(int, int, int) __attribute__((__unused__)) #define CLMX ((*sx)(mzx,mzy,mzz)) #define CLMY ((*sy)(mzx,mzy,mzz)) #define CLMZ ((*sz)(mzx,mzy,mzz)) #define SUB ((*sub)(x,y,z)) #define MZSEL() Maze((*sx)(x,y,z),(*sy)(x,y,z),(*sz)(x,y,z)) void row1(CLARGS) { int x; if ((z >= CLMZ) || (y >= CLMY)) { printf(" "); for (x=0;x= CLMZ) || (y >= CLMY)) { printf(" "); for (x=0;x= CLMX) ? ' ' : (maze[SUB] & MZ_S) ? 's' : (maze[SUB] & MZ_G) ? 'g' : (cells[SUB] == 2) ? '*' : ' ', ((x < CLMX) && (maze[SUB] & (*sz)(MZ_PX,MZ_PY,MZ_PZ))) ? '^' : ' ', ((x < CLMX) && !(maze[SUB] & (*sx)(MZ_PX,MZ_PY,MZ_PZ))) ? '|' : ' ' ); } } void botline(CLARGS) { int x; if (z < CLMZ) { printf("+"); for (x=0;x=0;y--) { call_line(&row1); call_line(&row2); } call_line(&botline); #undef MZSEL #undef CLARGS #undef CLMX #undef CLMY #undef CLMZ #undef SUB } free(cells); free(walls); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); setup_maze(); open_display(); setup_visual(); setup_X(); setup_context(); setup_bitmaps(); setup_windows(); setup_gl(); setup_input(); setup_view(); setup_ticks(); setup_lists(); while (1) tick(); }