e85559dbe6b8ecf0613aa03345073954a0e16cfb
[xscreensaver] / hacks / glx / polyhedra-gl.c
1 /* polyhedra, Copyright (c) 2004 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Renders 160 different 3D solids, and displays some information about each.
12  *  A new solid is chosen every few seconds.
13  *
14  * This file contains the OpenGL side; computation of the polyhedra themselves
15  * is in "polyhedra.c".
16  *
17  * KNOWN BUG:
18  *
19  *   The normals are wrong (inverted) on some faces of some of the duals
20  *   (e.g., "Rhombicosacron".)  I can't figure out how to tell when the
21  *   normal should be pointing the other way.
22  */
23
24 #include <X11/Intrinsic.h>
25
26 extern XtAppContext app;
27
28 #define PROGCLASS       "Polyhedra"
29 #define HACK_INIT       init_polyhedra
30 #define HACK_DRAW       draw_polyhedra
31 #define HACK_RESHAPE    reshape_polyhedra
32 #define HACK_HANDLE_EVENT polyhedra_handle_event
33 #define EVENT_MASK      PointerMotionMask
34 #define sws_opts        xlockmore_opts
35
36 #define DEF_SPIN        "True"
37 #define DEF_WANDER      "True"
38 #define DEF_SPEED       "1.0"
39 #define DEF_TITLES      "True"
40 #define DEF_DURATION    "12"
41 #define DEF_WHICH       "-1"
42
43 #define DEFAULTS        "*delay:        30000         \n" \
44                         "*showFPS:      False         \n" \
45                         "*wireframe:    False         \n" \
46                         "*speed:      " DEF_SPEED    "\n" \
47                         "*spin:       " DEF_SPIN     "\n" \
48                         "*wander:     " DEF_WANDER   "\n" \
49                         "*duration:   " DEF_DURATION "\n" \
50                         "*which:      " DEF_WHICH    "\n" \
51                         "*titleFont:  -*-times-bold-r-normal-*-180-*\n" \
52                         "*titleFont2: -*-times-bold-r-normal-*-120-*\n" \
53                         "*titleFont3: -*-times-bold-r-normal-*-80-*\n"  \
54
55
56 #undef countof
57 #define countof(x) (sizeof((x))/sizeof((*x)))
58
59 #include "xlockmore.h"
60 #include "polyhedra.h"
61 #include "colors.h"
62 #include "rotator.h"
63 #include "gltrackball.h"
64 #include <ctype.h>
65
66 #ifdef USE_GL /* whole file */
67
68 #include <GL/glu.h>
69
70 typedef struct {
71   GLXContext *glx_context;
72   rotator *rot;
73   trackball_state *trackball;
74   Bool button_down_p;
75
76   int npolyhedra;
77   polyhedron **polyhedra;
78
79   int which;
80   int change_to;
81   GLuint object_list;
82   GLuint title_list;
83
84   int mode;  /* 0 = normal, 1 = out, 2 = in */
85   int mode_tick;
86
87   int ncolors;
88   XColor *colors;
89
90   XFontStruct *xfont1, *xfont2, *xfont3;
91   GLuint font1_dlist, font2_dlist, font3_dlist;
92
93 } polyhedra_configuration;
94
95 static polyhedra_configuration *bps = NULL;
96
97 static Bool do_spin;
98 static GLfloat speed;
99 static Bool do_wander;
100 static Bool do_titles;
101 static int duration;
102 static int do_which;
103 static char *do_which_str;
104
105 static XrmOptionDescRec opts[] = {
106   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
107   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
108   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
109   { "-wander", ".wander", XrmoptionNoArg, "True" },
110   { "+wander", ".wander", XrmoptionNoArg, "False" },
111   { "-titles", ".titles", XrmoptionNoArg, "True" },
112   { "+titles", ".titles", XrmoptionNoArg, "False" },
113   { "-duration",".duration",XrmoptionSepArg, 0 },
114   { "-which",  ".which",   XrmoptionSepArg, 0 },
115 };
116
117 static argtype vars[] = {
118   {&do_spin,   "spin",   "Spin",   DEF_SPIN,    t_Bool},
119   {&do_wander, "wander", "Wander", DEF_WANDER,  t_Bool},
120   {&do_titles, "titles", "Titles", DEF_TITLES,  t_Bool},
121   {&speed,     "speed",  "Speed",  DEF_SPEED,   t_Float},
122   {&duration,"duration","Duration",DEF_DURATION,t_Int},
123   {&do_which_str,"which", "Which", DEF_WHICH,   t_String},
124 };
125
126 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
127
128
129 \f
130 typedef struct {
131   double x,y,z;
132 } XYZ;
133
134 static void
135 normalize (XYZ *p)
136 {
137   double length;
138   length = sqrt (p->x * p->x +
139                  p->y * p->y +
140                  p->z * p->z);
141   if (length != 0)
142     {
143       p->x /= length;
144       p->y /= length;
145       p->z /= length;
146     }
147   else
148     {
149       p->x = 0;
150       p->y = 0;
151       p->z = 0;
152     }
153 }
154
155 /* Calculate the unit normal at p given two other points p1,p2 on the
156    surface. The normal points in the direction of p1 crossproduct p2
157  */
158 static XYZ
159 calc_normal (XYZ p, XYZ p1, XYZ p2)
160 {
161   XYZ n, pa, pb;
162   pa.x = p1.x - p.x;
163   pa.y = p1.y - p.y;
164   pa.z = p1.z - p.z;
165   pb.x = p2.x - p.x;
166   pb.y = p2.y - p.y;
167   pb.z = p2.z - p.z;
168   n.x = pa.y * pb.z - pa.z * pb.y;
169   n.y = pa.z * pb.x - pa.x * pb.z;
170   n.z = pa.x * pb.y - pa.y * pb.x;
171   normalize (&n);
172   return (n);
173 }
174
175
176 static void
177 do_normal(GLfloat x1, GLfloat y1, GLfloat z1,
178           GLfloat x2, GLfloat y2, GLfloat z2,
179           GLfloat x3, GLfloat y3, GLfloat z3)
180 {
181   XYZ p1, p2, p3, p;
182   p1.x = x1; p1.y = y1; p1.z = z1;
183   p2.x = x2; p2.y = y2; p2.z = z2;
184   p3.x = x3; p3.y = y3; p3.z = z3;
185
186   p = calc_normal (p1, p2, p3);
187
188   glNormal3f (p.x, p.y, p.z);
189
190 #ifdef DEBUG
191   /* Draw a line in the direction of this face's normal. */
192   {
193     glPushMatrix();
194     glTranslatef ((x1 + x2 + x3) / 3,
195                   (y1 + y2 + y3) / 3,
196                   (z1 + z2 + z3) / 3);
197     glScalef (0.5, 0.5, 0.5);
198     glBegin(GL_LINE_LOOP);
199     glVertex3f(0, 0, 0);
200     glVertex3f(p.x, p.y, p.z);
201     glEnd();
202     glPopMatrix();
203   }
204 #endif /* DEBUG */
205 }
206
207 static void
208 load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP)
209 {
210   const char *font = get_string_resource (res, "Font");
211   XFontStruct *f;
212   Font id;
213   int first, last;
214
215   if (!font) font = "-*-times-bold-r-normal-*-180-*";
216
217   f = XLoadQueryFont(mi->dpy, font);
218   if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
219
220   id = f->fid;
221   first = f->min_char_or_byte2;
222   last = f->max_char_or_byte2;
223   
224   clear_gl_error ();
225   *dlistP = glGenLists ((GLuint) last+1);
226   check_gl_error ("glGenLists");
227   glXUseXFont(id, first, last-first+1, *dlistP + first);
228   check_gl_error ("glXUseXFont");
229
230   *fontP = f;
231 }
232
233
234 static void
235 load_fonts (ModeInfo *mi)
236 {
237   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
238   load_font (mi, "titleFont",  &bp->xfont1, &bp->font1_dlist);
239   load_font (mi, "titleFont2", &bp->xfont2, &bp->font2_dlist);
240   load_font (mi, "titleFont3", &bp->xfont3, &bp->font3_dlist);
241 }
242
243
244 static int
245 string_width (XFontStruct *f, const char *c)
246 {
247   int w = 0;
248   while (*c)
249     {
250       int cc = *((unsigned char *) c);
251       w += (f->per_char
252             ? f->per_char[cc-f->min_char_or_byte2].rbearing
253             : f->min_bounds.rbearing);
254       c++;
255     }
256   return w;
257 }
258
259 static void
260 print_title_string (ModeInfo *mi, const char *string,
261                     GLfloat x, GLfloat y,
262                     XFontStruct *font, int font_dlist)
263 {
264   GLfloat line_height = font->ascent + font->descent;
265   GLfloat sub_shift = (line_height * 0.3);
266   int cw = string_width (font, "m");
267   int tabs = cw * 7;
268
269   y -= line_height;
270
271   glPushAttrib (GL_TRANSFORM_BIT |  /* for matrix contents */
272                 GL_ENABLE_BIT);     /* for various glDisable calls */
273   glDisable (GL_LIGHTING);
274   glDisable (GL_DEPTH_TEST);
275   {
276     glMatrixMode(GL_PROJECTION);
277     glPushMatrix();
278     {
279       glLoadIdentity();
280
281       glMatrixMode(GL_MODELVIEW);
282       glPushMatrix();
283       {
284         int i;
285         int x2 = x;
286         Bool sub_p = False;
287         glLoadIdentity();
288
289         gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height);
290
291         glColor3f (0.8, 0.8, 0);
292
293         glRasterPos2f (x, y);
294         for (i = 0; i < strlen(string); i++)
295           {
296             char c = string[i];
297             if (c == '\n')
298               {
299                 glRasterPos2f (x, (y -= line_height));
300                 x2 = x;
301               }
302             else if (c == '\t')
303               {
304                 x2 -= x;
305                 x2 = ((x2 + tabs) / tabs) * tabs;  /* tab to tab stop */
306                 x2 += x;
307                 glRasterPos2f (x2, y);
308               }
309             else if (c == '[' && (isdigit (string[i+1])))
310               {
311                 sub_p = True;
312                 glRasterPos2f (x2, (y -= sub_shift));
313               }
314             else if (c == ']' && sub_p)
315               {
316                 sub_p = False;
317                 glRasterPos2f (x2, (y += sub_shift));
318               }
319             else
320               {
321                 glCallList (font_dlist + (int)(c));
322                 x2 += (font->per_char
323                        ? font->per_char[c - font->min_char_or_byte2].width
324                        : font->min_bounds.width);
325               }
326           }
327       }
328       glPopMatrix();
329     }
330     glMatrixMode(GL_PROJECTION);
331     glPopMatrix();
332   }
333   glPopAttrib();
334
335   glMatrixMode(GL_MODELVIEW);
336 }
337
338
339 static void
340 startup_blurb (ModeInfo *mi)
341 {
342   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
343   const char *s = "Computing polyhedra...";
344   print_title_string (mi, s,
345                       mi->xgwa.width - (string_width (bp->xfont1, s) + 40),
346                       10 + bp->xfont1->ascent + bp->xfont1->descent,
347                       bp->xfont1, bp->font1_dlist);
348   glFinish();
349   glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
350 }
351
352
353
354 /* Window management, etc
355  */
356 static void new_label (ModeInfo *mi);
357
358 void
359 reshape_polyhedra (ModeInfo *mi, int width, int height)
360 {
361   GLfloat h = (GLfloat) height / (GLfloat) width;
362
363   glViewport (0, 0, (GLint) width, (GLint) height);
364
365   glMatrixMode(GL_PROJECTION);
366   glLoadIdentity();
367   gluPerspective (30.0, 1/h, 1.0, 100.0);
368
369   glMatrixMode(GL_MODELVIEW);
370   glLoadIdentity();
371   gluLookAt( 0.0, 0.0, 30.0,
372              0.0, 0.0, 0.0,
373              0.0, 1.0, 0.0);
374
375   glClear(GL_COLOR_BUFFER_BIT);
376
377   /* need to re-render the text when the window size changes */
378   new_label (mi);
379 }
380
381
382 Bool
383 polyhedra_handle_event (ModeInfo *mi, XEvent *event)
384 {
385   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
386
387   if (event->xany.type == ButtonPress &&
388       event->xbutton.button & Button1)
389     {
390       bp->button_down_p = True;
391       gltrackball_start (bp->trackball,
392                          event->xbutton.x, event->xbutton.y,
393                          MI_WIDTH (mi), MI_HEIGHT (mi));
394       return True;
395     }
396   else if (event->xany.type == ButtonRelease &&
397            event->xbutton.button & Button1)
398     {
399       bp->button_down_p = False;
400       return True;
401     }
402   else if (event->xany.type == KeyPress)
403     {
404       KeySym keysym;
405       char c = 0;
406       XLookupString (&event->xkey, &c, 1, &keysym, 0);
407
408       bp->change_to = -1;
409       if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
410         bp->change_to = random() % bp->npolyhedra;
411       else if (c == '>' || c == '.' || c == '+' || c == '=')
412         bp->change_to = (bp->which + 1) % bp->npolyhedra;
413       else if (c == '<' || c == ',' || c == '-' || c == '_' ||
414                c == '\010' || c == '\177')
415         bp->change_to = (bp->which + bp->npolyhedra - 1) % bp->npolyhedra;
416
417       if (bp->change_to != -1)
418         return True;
419     }
420   else if (event->xany.type == MotionNotify &&
421            bp->button_down_p)
422     {
423       gltrackball_track (bp->trackball,
424                          event->xmotion.x, event->xmotion.y,
425                          MI_WIDTH (mi), MI_HEIGHT (mi));
426       return True;
427     }
428
429   return False;
430 }
431
432
433 static void
434 new_label (ModeInfo *mi)
435 {
436   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
437   polyhedron *p = bp->which >= 0 ? bp->polyhedra[bp->which] : 0;
438
439   glNewList (bp->title_list, GL_COMPILE);
440   if (p && do_titles)
441     {
442       char label[1024];
443       char name2[255];
444       strcpy (name2, p->name);
445       if (*p->class)
446         sprintf (name2 + strlen(name2), "  (%s)", p->class);
447
448       sprintf (label,
449                "Polyhedron %d:   \t%s\n\n"
450                "Wythoff Symbol:\t%s\n"
451                "Vertex Configuration:\t%s\n"
452                "Symmetry Group:\t%s\n"
453             /* "Dual of:              \t%s\n" */
454                "\n"
455                "Faces:\t  %d\n"
456                "Edges:\t  %d\n"
457                "Vertices:\t  %d\n"
458                "Density:\t  %d\n"
459                "Euler:\t%s%d\n",
460                bp->which, name2, p->wythoff, p->config, p->group,
461             /* p->dual, */
462                p->logical_faces, p->nedges, p->logical_vertices,
463                p->density, (p->chi < 0 ? "" : "  "), p->chi);
464
465       {
466         XFontStruct *f;
467         GLuint fl;
468         if (MI_WIDTH(mi) >= 500 && MI_HEIGHT(mi) >= 375)
469           f = bp->xfont1, fl = bp->font1_dlist;                /* big font */
470         else if (MI_WIDTH(mi) >= 350 && MI_HEIGHT(mi) >= 260)
471           f = bp->xfont2, fl = bp->font2_dlist;                /* small font */
472         else
473           f = bp->xfont3, fl = bp->font3_dlist;                /* tiny font */
474
475         print_title_string (mi, label,
476                             10, mi->xgwa.height - 10,
477                             f, fl);
478       }
479     }
480   glEndList ();
481 }
482
483
484 static void
485 new_polyhedron (ModeInfo *mi)
486 {
487   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
488   polyhedron *p;
489   int wire = MI_IS_WIREFRAME(mi);
490   static GLfloat bcolor[4] = {0.0, 0.0, 0.0, 1.0};
491   int i;
492
493   mi->polygon_count = 0;
494
495   bp->ncolors = 128;
496   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
497   make_random_colormap (0, 0, 0,
498                         bp->colors, &bp->ncolors,
499                         True, False, 0, False);
500
501   if (do_which >= bp->npolyhedra)
502     do_which = -1;
503
504   bp->which = (bp->change_to != -1 ? bp->change_to :
505                do_which >= 0 ? do_which :
506                (random() % bp->npolyhedra));
507   bp->change_to = -1;
508   p = bp->polyhedra[bp->which];
509
510   new_label (mi);
511
512   glNewList (bp->object_list, GL_COMPILE);
513   for (i = 0; i < p->nfaces; i++)
514     {
515       int j;
516       face *f = &p->faces[i];
517
518       if (f->color > 64 || f->color < 0) abort();
519       if (wire)
520         glColor3f (0, 1, 0);
521       else
522         {
523           bcolor[0] = bp->colors[f->color].red   / 65536.0;
524           bcolor[1] = bp->colors[f->color].green / 65536.0;
525           bcolor[2] = bp->colors[f->color].blue  / 65536.0;
526           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bcolor);
527         }
528
529       do_normal (p->points[f->points[0]].x,
530                  p->points[f->points[0]].y,
531                  p->points[f->points[0]].z,
532                  p->points[f->points[1]].x,
533                  p->points[f->points[1]].y,
534                  p->points[f->points[1]].z,
535                  p->points[f->points[2]].x,
536                  p->points[f->points[2]].y,
537                  p->points[f->points[2]].z);
538
539       glBegin (wire ? GL_LINE_LOOP :
540                f->npoints == 3 ? GL_TRIANGLES :
541                f->npoints == 4 ? GL_QUADS :
542                GL_POLYGON);
543       for (j = 0; j < f->npoints; j++)
544         {
545           point *pp = &p->points[f->points[j]];
546           glVertex3f (pp->x, pp->y, pp->z);
547         }
548       glEnd();
549     }
550   glEndList ();
551
552   mi->polygon_count += p->nfaces;
553 }
554
555
556 void 
557 init_polyhedra (ModeInfo *mi)
558 {
559   polyhedra_configuration *bp;
560   int wire = MI_IS_WIREFRAME(mi);
561
562   if (!bps) {
563     bps = (polyhedra_configuration *)
564       calloc (MI_NUM_SCREENS(mi), sizeof (polyhedra_configuration));
565     if (!bps) {
566       fprintf(stderr, "%s: out of memory\n", progname);
567       exit(1);
568     }
569
570     bp = &bps[MI_SCREEN(mi)];
571   }
572
573   bp = &bps[MI_SCREEN(mi)];
574
575   bp->glx_context = init_GL(mi);
576
577   bp->which = -1;
578   load_fonts (mi);
579   startup_blurb (mi);
580
581   reshape_polyhedra (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
582
583   if (!wire)
584     {
585       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
586       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
587       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
588       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
589
590       glEnable(GL_LIGHTING);
591       glEnable(GL_LIGHT0);
592       glEnable(GL_DEPTH_TEST);
593       /* glEnable(GL_CULL_FACE); */
594
595       glLightfv(GL_LIGHT0, GL_POSITION, pos);
596       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
597       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
598       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
599     }
600
601   {
602     double spin_speed   = 2.0;
603     double wander_speed = 0.05;
604     double spin_accel   = 0.2;
605
606     bp->rot = make_rotator (do_spin ? spin_speed : 0,
607                             do_spin ? spin_speed : 0,
608                             do_spin ? spin_speed : 0,
609                             spin_accel,
610                             do_wander ? wander_speed : 0,
611                             True);
612     bp->trackball = gltrackball_init ();
613   }
614
615   bp->npolyhedra = construct_polyhedra (&bp->polyhedra);
616
617   bp->object_list = glGenLists (1);
618   bp->title_list  = glGenLists (1);
619   bp->change_to = -1;
620
621   {
622     int x;
623     char c;
624     do_which = -1;
625     if (1 == sscanf (do_which_str, " %d %c", &x, &c))
626       do_which = x;
627     else if (*do_which_str)
628       {
629         char *s;
630         for (s = do_which_str; *s; s++)
631           if (*s == '-' || *s == '_') *s = ' ';
632
633         for (x = 0; x < bp->npolyhedra; x++)
634           if (!strcasecmp (do_which_str, bp->polyhedra[x]->name) ||
635               !strcasecmp (do_which_str, bp->polyhedra[x]->wythoff) ||
636               !strcasecmp (do_which_str, bp->polyhedra[x]->config))
637             {
638               do_which = x;
639               break;
640             }
641         if (do_which < 0)
642           {
643             fprintf (stderr, "%s: no such polyhedron: \"%s\"\n",
644                      progname, do_which_str);
645             exit (1);
646           }
647       }
648   }
649
650   new_polyhedron (mi);
651 }
652
653
654 void
655 draw_polyhedra (ModeInfo *mi)
656 {
657   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
658   Display *dpy = MI_DISPLAY(mi);
659   Window window = MI_WINDOW(mi);
660
661   static time_t last_time = 0;
662
663   static GLfloat bspec[4]  = {1.0, 1.0, 1.0, 1.0};
664   static GLfloat bshiny    = 128.0;
665
666   if (!bp->glx_context)
667     return;
668
669   if (bp->mode == 0 && do_which >= 0 && bp->change_to < 0)
670     ;
671   else if (bp->mode == 0)
672     {
673       static int tick = 0;
674
675       if (bp->change_to >= 0)
676         tick = 999, last_time = 1;
677       if (tick++ > 10)
678         {
679           time_t now = time((time_t *) 0);
680           if (last_time == 0) last_time = now;
681           tick = 0;
682           if (!bp->button_down_p && now - last_time >= duration)
683             {
684               bp->mode = 1;    /* go out */
685               bp->mode_tick = 20 * speed;
686               last_time = now;
687             }
688         }
689     }
690   else if (bp->mode == 1)   /* out */
691     {
692       if (--bp->mode_tick <= 0)
693         {
694           new_polyhedron (mi);
695           bp->mode_tick = 20 * speed;
696           bp->mode = 2;  /* go in */
697         }
698     }
699   else if (bp->mode == 2)   /* in */
700     {
701       if (--bp->mode_tick <= 0)
702         bp->mode = 0;  /* normal */
703     }
704   else
705     abort();
706
707   glShadeModel(GL_FLAT);
708   glEnable(GL_NORMALIZE);
709
710   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
711
712   glPushMatrix ();
713
714   glScalef(1.1, 1.1, 1.1);
715
716   {
717     double x, y, z;
718     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
719     glTranslatef((x - 0.5) * 8,
720                  (y - 0.5) * 8,
721                  (z - 0.5) * 15);
722
723     gltrackball_rotate (bp->trackball);
724
725     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
726     glRotatef (x * 360, 1.0, 0.0, 0.0);
727     glRotatef (y * 360, 0.0, 1.0, 0.0);
728     glRotatef (z * 360, 0.0, 0.0, 1.0);
729   }
730
731   glScalef (2.0, 2.0, 2.0);
732
733   glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,  bspec);
734   glMateriali  (GL_FRONT_AND_BACK, GL_SHININESS, bshiny);
735
736   if (bp->mode != 0)
737     {
738       GLfloat s = (bp->mode == 1
739                    ? bp->mode_tick / (20 * speed)
740                    : ((20 * speed) - bp->mode_tick + 1) / (20 * speed));
741       glScalef (s, s, s);
742     }
743
744   glScalef (2, 2, 2);
745   glCallList (bp->object_list);
746   if (bp->mode == 0 && !bp->button_down_p)
747     glCallList (bp->title_list);
748
749   glPopMatrix ();
750
751   if (mi->fps_p) do_fps (mi);
752   glFinish();
753
754   glXSwapBuffers(dpy, window);
755 }
756
757 #endif /* USE_GL */