#include #include #include #include #include #include #include #include #include #include #include #include #include "3darith.h" #include "findvis.h" #include "mathutils.h" #define WINX 800 #define WINY 400 extern const char *__progname; #define UNUSED __attribute__((__unused__)) #define TEXSHIFT 6 #define CORNERCOUNT 6 #define CORNERSIZE .1 #define SPOTMAG 40 #define SPOTFUDGE 0 #define SPOTSIZE 12 #define SPOTLOC 12 #define DIERECTS (6+(CORNERCOUNT*12)) #define DIETRIS (CORNERCOUNT*CORNERCOUNT*8) #define SCENERECTS 5 typedef unsigned char TEXEL; #define TEXSIZE (1<>1)+2)*((TEXSIZE>>1)+2)]; static RENDER_R dierects[DIERECTS]; static RENDER_T dietris[DIETRIS]; static RENDER_R scenerects[SCENERECTS]; 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; 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 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,WINY,0,depth,InputOutput,vi->visual,attrmask,&attr); XMapRaised(disp,win); } static void call_eevf( void (*fn)(GLenum, GLenum, const GLfloat *), GLenum a1, GLenum a2, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 ) { GLfloat v[4]; v[0] = v0; v[1] = v1; v[2] = v2; v[3] = v3; (*fn)(a1,a2,&v[0]); } static unsigned char *gen_spot(void) { unsigned char *v; int sx; int sy; int px; int py; int n; unsigned char *vp; double h; v = malloc(SPOTSIZE*SPOTSIZE); vp = v; for (sy=SPOTSIZE-1;sy>=0;sy--) { for (sx=SPOTSIZE-1;sx>=0;sx--) { h = hypot(sx-((SPOTSIZE-1)*.5),sy-((SPOTSIZE-1)*.5)); if (h+.71 < (SPOTSIZE+1)*.5) { *vp = 0; } else if (h-.71 > (SPOTSIZE+1)*.5) { *vp = 255; } else { n = 0; for (px=SPOTMAG-1;px>=0;px--) { for (py=SPOTMAG-1;py>=0;py--) { if ( hypot( (sx * SPOTMAG) + px - (((SPOTSIZE * SPOTMAG) + 1) * .5), (sy * SPOTMAG) + py - (((SPOTSIZE * SPOTMAG) + 1) * .5) ) >= (SPOTSIZE * SPOTMAG * .5) - SPOTFUDGE ) n ++; } } *vp = (n * 256) / ((SPOTMAG*SPOTMAG)+1); } vp ++; } } return(v); } static int scloc_to_coord(int scv) { switch (scv) { case 0: return(SPOTLOC); break; case 1: return(TEXSIZE/2); break; case 2: return(TEXSIZE-SPOTLOC); break; } abort(); } static void spot_at(int scx, int scy) { int x0; int y0; int x; int y; static unsigned char *spot = 0; if (! spot) spot = gen_spot(); x0 = scloc_to_coord(scx) - (SPOTSIZE/2); y0 = scloc_to_coord(scy) - (SPOTSIZE/2); for (y=SPOTSIZE-1;y>=0;y--) { for (x=SPOTSIZE-1;x>=0;x--) { texture[y0+y][x0+x] = spot[(y*SPOTSIZE)+x]; } } } static void gen_texture_image(int n) { memset(&texture,255,sizeof(texture)); switch (n) { case 1: spot_at(1,1); break; case 2: spot_at(0,0); spot_at(2,2); break; case 3: spot_at(2,0); spot_at(1,1); spot_at(0,2); break; case 4: spot_at(0,0); spot_at(0,2); spot_at(2,0); spot_at(2,2); break; case 5: spot_at(0,0); spot_at(0,2); spot_at(1,1); spot_at(2,0); spot_at(2,2); break; case 6: spot_at(0,0); spot_at(0,1); spot_at(0,2); spot_at(2,0); spot_at(2,1); spot_at(2,2); break; default: abort(); break; } } static void set_mean(TEXEL *in, TEXEL *out, int w, int xstride, int h, int ystride) { unsigned int sum; int x; int y; unsigned int n; sum = 0; for (y=w;y>0;y--) { for (x=h;x>0;x--) { sum += *in; in += xstride; } in += ystride - (h * xstride); } n = w * h; *out = (sum + (n/2)) / n; } static void gen_texture_reduction(int s) { int sz; int i; int j; sz = TEXSIZE >> s; textemp[0] = texture[0][0]; textemp[sz+2-1] = texture[0][TEXSIZE+2-1]; textemp[(sz+2-1)*(sz+2)] = texture[TEXSIZE+2-1][0]; textemp[(sz+2-1)*(sz+2+1)] = texture[TEXSIZE+2-1][TEXSIZE+2-1]; for (i=sz-1;i>=0;i--) { set_mean(&texture[0][(i<=0;i--) { for (j=sz-1;j>=0;j--) { set_mean(&texture[(i<=0;i--) { glBindTexture(GL_TEXTURE_2D,tex[i]); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); gen_texture_image(i+1); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,TEXSIZE+2,TEXSIZE+2,1,GL_LUMINANCE,GL_UNSIGNED_BYTE,&texture[0][0]); for (j=1;TEXSIZE>>j;j++) { gen_texture_reduction(j); glTexImage2D(GL_TEXTURE_2D,j,GL_RGBA,(TEXSIZE>>j)+2,(TEXSIZE>>j)+2,1,GL_LUMINANCE,GL_UNSIGNED_BYTE,&textemp[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); /* glLightModel all defaults OK */ glEnable(GL_LIGHT0); use_light_default(); /* GL_POSITION at rendering time */ /* GL_SPOT_DIRECTION, GLSPOT_EXPONENT, GL_SPOT_CUTOFF, GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION */ kbstate = 0; } #if CORNERCOUNT > 0 static void gen_edges(void) { double s1; double c1; double s2; double c2; int i; int ri; void gen_rect(XYZ p0, XYZ n0, XYZ p1, XYZ n1, XYZ p2) { dierects[ri].pt[0] = p0; dierects[ri].norm[0] = n0; dierects[ri].pt[1] = p1; dierects[ri].norm[1] = n1; dierects[ri].pt[2] = p2; dierects[ri].norm[2] = n1; dierects[ri].pt[3] = xyzadd(p0,xyzsub(p2,p1)); dierects[ri].norm[3] = n0; dierects[ri].texture = -1; ri ++; } void gen_rects(XYZ p0, XYZ n0, XYZ p1, XYZ n1, XYZ p2) { void rotate(void) { p0 = xyzrotate90(p0,(XYZ){1,0,0}); n0 = xyzrotate90(n0,(XYZ){1,0,0}); p1 = xyzrotate90(p1,(XYZ){1,0,0}); n1 = xyzrotate90(n1,(XYZ){1,0,0}); p2 = xyzrotate90(p2,(XYZ){1,0,0}); } XYZ r31(XYZ v) { return((XYZ){v.y,v.z,v.x}); } XYZ r32(XYZ v) { return((XYZ){v.z,v.x,v.y}); } void gen3(void) { gen_rect(p0,n0,p1,n1,p2); gen_rect(r31(p0),r31(n0),r31(p1),r31(n1),r31(p2)); gen_rect(r32(p0),r32(n0),r32(p1),r32(n1),r32(p2)); } gen3(); rotate(); gen3(); rotate(); gen3(); rotate(); gen3(); } c2 = 0; s2 = 1; ri = 6; for (i=CORNERCOUNT-1;i>=0;i--) { s1 = s2; c1 = c2; s2 = sindeg((90.0*i)/CORNERCOUNT); c2 = cosdeg((90.0*i)/CORNERCOUNT); gen_rects( (XYZ) { CORNERSIZE-.5, ((1-s2)*CORNERSIZE)-.5, ((1-c2)*CORNERSIZE)-.5 }, (XYZ) { 0, -s2, -c2 }, (XYZ) { CORNERSIZE-.5, ((1-s1)*CORNERSIZE)-.5, ((1-c1)*CORNERSIZE)-.5 }, (XYZ) { 0, -s1, -c1 }, (XYZ) { .5-CORNERSIZE, ((1-s1)*CORNERSIZE)-.5, ((1-c1)*CORNERSIZE)-.5 } ); } if (ri != DIERECTS) abort(); } static void gen_corners(void) { typedef struct pt PT; struct pt { XYZ p; int x; int nl; int edge; #define MAXLINKS 6 PT *links[MAXLINKS]; } ; #define N CORNERCOUNT #define NPT (((N+1)*(N+2))/2) PT p[NPT]; int nt; PT *tp[N*N][3]; void find_p(void) { double arcc[N+1]; double arcs[N+1]; PT *v[N+2]; int i; int j; int ptx; PT *pp; void link_between(PT *a, PT *b) { if (!a || !b) return; if (a->nl >= MAXLINKS) abort(); a->links[a->nl++] = b; if (b->nl >= MAXLINKS) abort(); b->links[b->nl++] = a; } int adjust(void) { PT *ap; int x; XYZ s; int i; double d; double maxd; int rv; PT *lp; rv = 0; for (x=NPT-1;x>=0;x--) { ap = &p[x]; if (ap->edge) continue; s = (XYZ){0,0,0}; maxd = 0; for (i=ap->nl-1;i>=0;i--) { lp = ap->links[i]; s = xyzadd(s,lp->p); d = xyzlength(xyzsub(lp->p,ap->p)); if (d > maxd) maxd = d; } s = xyzunit(xyzscale(s,1./ap->nl)); d = xyzlength(xyzsub(ap->p,s)); if (d > maxd/100) rv = 1; ap->p = s; } return(rv); } void addtri(PT *p1, PT *p2, PT *p3) { if (nt >= N*N) abort(); tp[nt][0] = p1; tp[nt][1] = p2; tp[nt][2] = p3; nt ++; } for (i=N;i>=0;i--) { arcc[i] = cos(i*M_PI/(N*2)); arcs[i] = sin(i*M_PI/(N*2)); } for (i=NPT-1;i>=0;i--) p[i].x = i; ptx = 0; for (i=N;i>=0;i--) { pp = &p[ptx++]; pp->p.x = arcc[i]; pp->p.y = 0; pp->p.z = arcs[i]; pp->nl = 0; pp->edge = 1; v[i] = pp; if (i < N) link_between(pp,v[i+1]); } v[N+1] = 0; nt = 0; for (j=N-1;j>=0;j--) { for (i=j;i>=0;i--) { pp = &p[ptx++]; if (i == 0) { pp->p.x = arcc[N-j]; pp->p.y = arcs[N-j]; pp->p.z = 0; pp->edge = 1; } else if (i == j) { pp->p.x = 0; pp->p.y = arcs[N-j]; pp->p.z = arcc[N-j]; pp->edge = 1; } else { pp->p = (XYZ){1,1,1}; pp->edge = 0; } pp->nl = 0; link_between(pp,v[i+2]); link_between(pp,v[i+1]); link_between(pp,v[i]); if (v[i+2]) addtri(pp,v[i+2],v[i+1]); addtri(pp,v[i+1],v[i]); v[i+1] = pp; } bcopy(&v[1],&v[0],(j+1)*sizeof(PT *)); v[j+1] = 0; } if (ptx != NPT) abort(); if (nt != N*N) abort(); while (adjust()); } void gen_tris(XYZ p1, XYZ p2, XYZ p3) { XYZ n1; XYZ n2; XYZ n3; void record(void) { if (nt >= DIETRIS) abort(); dietris[nt].pt[0] = p1; dietris[nt].norm[0] = n1; dietris[nt].pt[1] = p2; dietris[nt].norm[1] = n2; dietris[nt].pt[2] = p3; dietris[nt].norm[2] = n3; nt ++; } void rot(XYZ axis) { n1 = xyzrotate90(n1,axis); n2 = xyzrotate90(n2,axis); n3 = xyzrotate90(n3,axis); p1 = xyzrotate90(p1,axis); p2 = xyzrotate90(p2,axis); p3 = xyzrotate90(p3,axis); } n1 = p1; n2 = p2; n3 = p3; p1 = xyzadd((XYZ){.5-CORNERSIZE,.5-CORNERSIZE,.5-CORNERSIZE},xyzscale(p1,CORNERSIZE)); p2 = xyzadd((XYZ){.5-CORNERSIZE,.5-CORNERSIZE,.5-CORNERSIZE},xyzscale(p2,CORNERSIZE)); p3 = xyzadd((XYZ){.5-CORNERSIZE,.5-CORNERSIZE,.5-CORNERSIZE},xyzscale(p3,CORNERSIZE)); record(); rot((XYZ){1,0,0}); record(); rot((XYZ){1,0,0}); record(); rot((XYZ){1,0,0}); record(); rot((XYZ){0,0,1}); record(); rot((XYZ){1,0,0}); record(); rot((XYZ){1,0,0}); record(); rot((XYZ){1,0,0}); record(); } void generate(void) { int i; nt = 0; for (i=(N*N)-1;i>=0;i--) { gen_tris(tp[i][0]->p,tp[i][1]->p,tp[i][2]->p); } } find_p(); generate(); if (nt != DIETRIS) abort(); #undef MAXLINKS #undef NPT #undef N } #endif static void setup_model(void) { dierects[0].pt[0] = (XYZ){CORNERSIZE-.5,CORNERSIZE-.5,-.5}; dierects[0].norm[0] = (XYZ){0,0,-1}; dierects[0].pt[1] = (XYZ){1-CORNERSIZE-.5,CORNERSIZE-.5,-.5}; dierects[0].norm[1] = (XYZ){0,0,-1}; dierects[0].pt[2] = (XYZ){1-CORNERSIZE-.5,1-CORNERSIZE-.5,-.5}; dierects[0].norm[2] = (XYZ){0,0,-1}; dierects[0].pt[3] = (XYZ){CORNERSIZE-.5,1-CORNERSIZE-.5,-.5}; dierects[0].norm[3] = (XYZ){0,0,-1}; dierects[0].texture = 0; dierects[1].pt[0] = (XYZ){-.5,CORNERSIZE-.5,CORNERSIZE-.5}; dierects[1].norm[0] = (XYZ){-1,0,0}; dierects[1].pt[1] = (XYZ){-.5,1-CORNERSIZE-.5,CORNERSIZE-.5}; dierects[1].norm[1] = (XYZ){-1,0,0}; dierects[1].pt[2] = (XYZ){-.5,1-CORNERSIZE-.5,1-CORNERSIZE-.5}; dierects[1].norm[2] = (XYZ){-1,0,0}; dierects[1].pt[3] = (XYZ){-.5,CORNERSIZE-.5,1-CORNERSIZE-.5}; dierects[1].norm[3] = (XYZ){-1,0,0}; dierects[1].texture = 1; dierects[2].pt[0] = (XYZ){CORNERSIZE-.5,-.5,CORNERSIZE-.5}; dierects[2].norm[0] = (XYZ){0,-1,0}; dierects[2].pt[1] = (XYZ){CORNERSIZE-.5,-.5,1-CORNERSIZE-.5}; dierects[2].norm[1] = (XYZ){0,-1,0}; dierects[2].pt[2] = (XYZ){1-CORNERSIZE-.5,-.5,1-CORNERSIZE-.5}; dierects[2].norm[2] = (XYZ){0,-1,0}; dierects[2].pt[3] = (XYZ){1-CORNERSIZE-.5,-.5,CORNERSIZE-.5}; dierects[2].norm[3] = (XYZ){0,-1,0}; dierects[2].texture = 2; dierects[3].pt[0] = (XYZ){1-CORNERSIZE-.5,1-CORNERSIZE-.5,.5}; dierects[3].norm[0] = (XYZ){0,0,1}; dierects[3].pt[1] = (XYZ){1-CORNERSIZE-.5,CORNERSIZE-.5,.5}; dierects[3].norm[1] = (XYZ){0,0,1}; dierects[3].pt[2] = (XYZ){CORNERSIZE-.5,CORNERSIZE-.5,.5}; dierects[3].norm[2] = (XYZ){0,0,1}; dierects[3].pt[3] = (XYZ){CORNERSIZE-.5,1-CORNERSIZE-.5,.5}; dierects[3].norm[3] = (XYZ){0,0,1}; dierects[3].texture = 5; dierects[4].pt[0] = (XYZ){.5,1-CORNERSIZE-.5,1-CORNERSIZE-.5}; dierects[4].norm[0] = (XYZ){1,0,0}; dierects[4].pt[1] = (XYZ){.5,1-CORNERSIZE-.5,CORNERSIZE-.5}; dierects[4].norm[1] = (XYZ){1,0,0}; dierects[4].pt[2] = (XYZ){.5,CORNERSIZE-.5,CORNERSIZE-.5}; dierects[4].norm[2] = (XYZ){1,0,0}; dierects[4].pt[3] = (XYZ){.5,CORNERSIZE-.5,1-CORNERSIZE-.5}; dierects[4].norm[3] = (XYZ){1,0,0}; dierects[4].texture = 4; dierects[5].pt[0] = (XYZ){1-CORNERSIZE-.5,.5,1-CORNERSIZE-.5}; dierects[5].norm[0] = (XYZ){0,1,0}; dierects[5].pt[1] = (XYZ){CORNERSIZE-.5,.5,1-CORNERSIZE-.5}; dierects[5].norm[1] = (XYZ){0,1,0}; dierects[5].pt[2] = (XYZ){CORNERSIZE-.5,.5,CORNERSIZE-.5}; dierects[5].norm[2] = (XYZ){0,1,0}; dierects[5].pt[3] = (XYZ){1-CORNERSIZE-.5,.5,CORNERSIZE-.5}; dierects[5].norm[3] = (XYZ){0,1,0}; dierects[5].texture = 3; #if CORNERCOUNT > 0 gen_edges(); gen_corners(); #endif scenerects[0].pt[0] = (XYZ){0,0,0}; scenerects[0].norm[0] = (XYZ){0,0,1}; scenerects[0].pt[1] = (XYZ){0,4,0}; scenerects[0].norm[1] = (XYZ){0,0,1}; scenerects[0].pt[2] = (XYZ){9,4,0}; scenerects[0].norm[2] = (XYZ){0,0,1}; scenerects[0].pt[3] = (XYZ){9,0,0}; scenerects[0].norm[3] = (XYZ){0,0,1}; scenerects[0].texture = -1; scenerects[1].pt[0] = (XYZ){0,0,0}; scenerects[1].norm[0] = (XYZ){0,1,0}; scenerects[1].pt[1] = (XYZ){9,0,0}; scenerects[1].norm[1] = (XYZ){0,1,0}; scenerects[1].pt[2] = (XYZ){9,0,2}; scenerects[1].norm[2] = (XYZ){0,1,0}; scenerects[1].pt[3] = (XYZ){0,0,2}; scenerects[1].norm[3] = (XYZ){0,1,0}; scenerects[1].texture = -1; scenerects[2].pt[0] = (XYZ){9,0,0}; scenerects[2].norm[0] = (XYZ){-1,0,0}; scenerects[2].pt[1] = (XYZ){9,4,0}; scenerects[2].norm[1] = (XYZ){-1,0,0}; scenerects[2].pt[2] = (XYZ){9,4,2}; scenerects[2].norm[2] = (XYZ){-1,0,0}; scenerects[2].pt[3] = (XYZ){9,0,2}; scenerects[2].norm[3] = (XYZ){-1,0,0}; scenerects[2].texture = -1; scenerects[3].pt[0] = (XYZ){9,4,0}; scenerects[3].norm[0] = (XYZ){0,-1,0}; scenerects[3].pt[1] = (XYZ){0,4,0}; scenerects[3].norm[1] = (XYZ){0,-1,0}; scenerects[3].pt[2] = (XYZ){0,4,2}; scenerects[3].norm[2] = (XYZ){0,-1,0}; scenerects[3].pt[3] = (XYZ){9,4,2}; scenerects[3].norm[3] = (XYZ){0,-1,0}; scenerects[3].texture = -1; scenerects[4].pt[0] = (XYZ){0,0,0}; scenerects[4].norm[0] = (XYZ){1,0,0}; scenerects[4].pt[1] = (XYZ){0,0,2}; scenerects[4].norm[1] = (XYZ){1,0,0}; scenerects[4].pt[2] = (XYZ){0,4,2}; scenerects[4].norm[2] = (XYZ){1,0,0}; scenerects[4].pt[3] = (XYZ){0,4,0}; scenerects[4].norm[3] = (XYZ){1,0,0}; scenerects[4].texture = -1; } 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 render_rect(RENDER_R *r) { if (r->texture >= 0) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,tex[r->texture]); glBegin(GL_QUADS); xyzcall(r->norm[0],&glNormal3d); glTexCoord2d(0,0); xyzvertex(r->pt[0]); xyzcall(r->norm[1],&glNormal3d); glTexCoord2d(0,1); xyzvertex(r->pt[1]); xyzcall(r->norm[2],&glNormal3d); glTexCoord2d(1,1); xyzvertex(r->pt[2]); xyzcall(r->norm[3],&glNormal3d); glTexCoord2d(1,0); xyzvertex(r->pt[3]); glEnd(); } else { glDisable(GL_TEXTURE_2D); glBegin(GL_QUADS); xyzcall(r->norm[0],&glNormal3d); xyzvertex(r->pt[0]); xyzcall(r->norm[1],&glNormal3d); xyzvertex(r->pt[1]); xyzcall(r->norm[2],&glNormal3d); xyzvertex(r->pt[2]); xyzcall(r->norm[3],&glNormal3d); xyzvertex(r->pt[3]); glEnd(); } } static void render_tri(RENDER_T *t) { glDisable(GL_TEXTURE_2D); glBegin(GL_TRIANGLES); xyzcall(t->norm[0],&glNormal3d); xyzvertex(t->pt[0]); xyzcall(t->norm[2],&glNormal3d); xyzvertex(t->pt[2]); xyzcall(t->norm[1],&glNormal3d); xyzvertex(t->pt[1]); glEnd(); } static void drawstuff(void) { int i; GLdouble m[16]; static double rx = 0; static double ry = 0; static double rz = 0; 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); dbg = 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]); call_eevf(&glLightfv,GL_LIGHT0,GL_POSITION,2,0,4,1); glEnable(GL_LIGHTING); glTranslated(-4.5,-2,-1); call_eevf(&glMaterialfv,GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,0,.5,0,0); call_eevf(&glMaterialfv,GL_FRONT_AND_BACK,GL_SPECULAR,0,0,0,0); call_eevf(&glMaterialfv,GL_FRONT_AND_BACK,GL_EMISSION,0,0,0,1); glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,0); for (i=SCENERECTS-1;i>=0;i--) render_rect(&scenerects[i]); glTranslated(4.5,2,2); rx += 1.2345; if (rx > 360) rx -= 360; ry += 2.3456; if (ry > 360) ry -= 360; rz += 3.4567; if (rz > 360) rz -= 360; glRotated(rx,1,0,0); glRotated(ry,0,1,0); glRotated(rz,0,0,1); glScaled(.5,.5,.5); glEnable(GL_NORMALIZE); call_eevf(&glMaterialfv,GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,.3,.3,.3,0); call_eevf(&glMaterialfv,GL_FRONT_AND_BACK,GL_SPECULAR,.8,.8,.8,0); call_eevf(&glMaterialfv,GL_FRONT_AND_BACK,GL_EMISSION,0,0,0,1); glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,10); for (i=DIERECTS-1;i>=0;i--) render_rect(&dierects[i]); for (i=DIETRIS-1;i>=0;i--) render_tri(&dietris[i]); } 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(); } 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(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; 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 light_set(int inx) { switch (inx) { case 1: printf("Ambient\n"); use_light_ambient(); break; case 2: printf("Diffuse\n"); use_light_diffuse(); break; case 3: printf("Specular\n"); use_light_specular(); break; case 4: printf("Coloured\n"); use_light_coloured(); break; case 0: printf("Default\n"); use_light_default(); break; } } static void keystroke(XKeyEvent *ev, char updn) { KeySym ks; unsigned int bit; ks = XLookupKeysym(ev,0); 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_1: if (updn == 'd') light_set(1); break; case XK_2: if (updn == 'd') light_set(2); break; case XK_3: if (updn == 'd') light_set(3); break; case XK_4: if (updn == 'd') light_set(4); break; case XK_5: if (updn == 'd') light_set(5); break; case XK_6: if (updn == 'd') light_set(6); break; case XK_7: if (updn == 'd') light_set(7); break; case XK_8: if (updn == 'd') light_set(8); break; case XK_9: if (updn == 'd') light_set(9); break; case XK_0: if (updn == 'd') light_set(0); 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(); create_window(); setup_gl(); setup_input(); setup_model(); setup_view(); setup_ticks(); while (1) tick(); }