ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[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
18 #include <X11/Intrinsic.h>
19
20 extern XtAppContext app;
21
22 #define PROGCLASS       "Polyhedra"
23 #define HACK_INIT       init_polyhedra
24 #define HACK_DRAW       draw_polyhedra
25 #define HACK_RESHAPE    reshape_polyhedra
26 #define HACK_HANDLE_EVENT polyhedra_handle_event
27 #define EVENT_MASK      PointerMotionMask
28 #define sws_opts        xlockmore_opts
29
30 #define DEF_SPIN        "True"
31 #define DEF_WANDER      "True"
32 #define DEF_SPEED       "1.0"
33 #define DEF_TITLES      "True"
34 #define DEF_DURATION    "12"
35 #define DEF_WHICH       "-1"
36
37 #define DEFAULTS        "*delay:        30000         \n" \
38                         "*showFPS:      False         \n" \
39                         "*wireframe:    False         \n" \
40                         "*speed:      " DEF_SPEED    "\n" \
41                         "*spin:       " DEF_SPIN     "\n" \
42                         "*wander:     " DEF_WANDER   "\n" \
43                         "*duration:   " DEF_DURATION "\n" \
44                         "*which:      " DEF_WHICH    "\n" \
45                         "*titleFont:  -*-times-bold-r-normal-*-180-*\n" \
46                         "*titleFont2: -*-times-bold-r-normal-*-120-*\n" \
47                         "*titleFont3: -*-times-bold-r-normal-*-80-*\n"  \
48
49
50 #undef countof
51 #define countof(x) (sizeof((x))/sizeof((*x)))
52
53 #include "xlockmore.h"
54 #include <GL/glu.h>
55
56 #include "polyhedra.h"
57 #include "colors.h"
58 #include "rotator.h"
59 #include "gltrackball.h"
60 #include <ctype.h>
61
62 #ifdef USE_GL /* whole file */
63
64 #include <GL/glu.h>
65
66 typedef struct {
67   GLXContext *glx_context;
68   rotator *rot;
69   trackball_state *trackball;
70   Bool button_down_p;
71
72   int npolyhedra;
73   polyhedron **polyhedra;
74
75   int which;
76   int change_to;
77   GLuint object_list;
78   GLuint title_list;
79
80   int mode;  /* 0 = normal, 1 = out, 2 = in */
81   int mode_tick;
82
83   int ncolors;
84   XColor *colors;
85
86   XFontStruct *xfont1, *xfont2, *xfont3;
87   GLuint font1_dlist, font2_dlist, font3_dlist;
88
89 } polyhedra_configuration;
90
91 static polyhedra_configuration *bps = NULL;
92
93 static Bool do_spin;
94 static GLfloat speed;
95 static Bool do_wander;
96 static Bool do_titles;
97 static int duration;
98 static int do_which;
99 static char *do_which_str;
100
101 static XrmOptionDescRec opts[] = {
102   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
103   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
104   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
105   { "-wander", ".wander", XrmoptionNoArg, "True" },
106   { "+wander", ".wander", XrmoptionNoArg, "False" },
107   { "-titles", ".titles", XrmoptionNoArg, "True" },
108   { "+titles", ".titles", XrmoptionNoArg, "False" },
109   { "-duration",".duration",XrmoptionSepArg, 0 },
110   { "-which",  ".which",   XrmoptionSepArg, 0 },
111 };
112
113 static argtype vars[] = {
114   {&do_spin,   "spin",   "Spin",   DEF_SPIN,    t_Bool},
115   {&do_wander, "wander", "Wander", DEF_WANDER,  t_Bool},
116   {&do_titles, "titles", "Titles", DEF_TITLES,  t_Bool},
117   {&speed,     "speed",  "Speed",  DEF_SPEED,   t_Float},
118   {&duration,"duration","Duration",DEF_DURATION,t_Int},
119   {&do_which_str,"which", "Which", DEF_WHICH,   t_String},
120 };
121
122 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
123
124
125 \f
126 typedef struct {
127   double x,y,z;
128 } XYZ;
129
130 static void
131 normalize (XYZ *p)
132 {
133   double length;
134   length = sqrt (p->x * p->x +
135                  p->y * p->y +
136                  p->z * p->z);
137   if (length != 0)
138     {
139       p->x /= length;
140       p->y /= length;
141       p->z /= length;
142     }
143   else
144     {
145       p->x = 0;
146       p->y = 0;
147       p->z = 0;
148     }
149 }
150
151 /* Calculate the unit normal at p given two other points p1,p2 on the
152    surface. The normal points in the direction of p1 crossproduct p2
153  */
154 static XYZ
155 calc_normal (XYZ p, XYZ p1, XYZ p2)
156 {
157   XYZ n, pa, pb;
158   pa.x = p1.x - p.x;
159   pa.y = p1.y - p.y;
160   pa.z = p1.z - p.z;
161   pb.x = p2.x - p.x;
162   pb.y = p2.y - p.y;
163   pb.z = p2.z - p.z;
164   n.x = pa.y * pb.z - pa.z * pb.y;
165   n.y = pa.z * pb.x - pa.x * pb.z;
166   n.z = pa.x * pb.y - pa.y * pb.x;
167   normalize (&n);
168   return (n);
169 }
170
171
172 /* Calculate the normals at each vertex of a face, and use the sum to
173    decide which normal to assign to the entire face.  This also solves
174    problems caused by nonconvex faces, in most (but not all) cases.
175  */
176 static void
177 kludge_normal (int n, const int *indices, const point *points)
178 {
179   XYZ normal = { 0, 0, 0 };
180   XYZ p;
181   int i;
182
183   for (i = 0; i < n; ++i) {
184     int i1 = indices[i];
185     int i2 = indices[(i + 1) % n];
186     int i3 = indices[(i + 2) % n];
187     XYZ p1, p2, p3;
188
189     p1.x = points[i1].x; p1.y = points[i1].y; p1.z = points[i1].z;
190     p2.x = points[i2].x; p2.y = points[i2].y; p2.z = points[i2].z;
191     p3.x = points[i3].x; p3.y = points[i3].y; p3.z = points[i3].z;
192
193     p = calc_normal (p1, p2, p3);
194     normal.x += p.x;
195     normal.y += p.y;
196     normal.z += p.z;
197   }
198
199   normalize(&normal);
200   if (normal.x == 0 && normal.y == 0 && normal.z == 0) {
201     glNormal3f (p.x, p.y, p.z);
202   } else {
203     glNormal3f (normal.x, normal.y, normal.z);
204   }
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 tess_error (GLenum errorCode)
486 {
487   fprintf (stderr, "%s: tesselation error: %s\n",
488            progname, gluErrorString(errorCode));
489   exit (0);
490 }
491
492 static void
493 new_polyhedron (ModeInfo *mi)
494 {
495   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
496   polyhedron *p;
497   int wire = MI_IS_WIREFRAME(mi);
498   static GLfloat bcolor[4] = {0.0, 0.0, 0.0, 1.0};
499   int i;
500
501   /* Use the GLU polygon tesselator so that nonconvex faces are displayed
502      correctly (e.g., for the "pentagrammic concave deltohedron").
503    */
504   GLUtesselator *tobj = gluNewTess();
505   gluTessCallback (tobj, GLU_TESS_BEGIN,  (_GLUfuncptr) &glBegin);
506   gluTessCallback (tobj, GLU_TESS_END,    (_GLUfuncptr) &glEnd);
507   gluTessCallback (tobj, GLU_TESS_VERTEX, (_GLUfuncptr) &glVertex3dv);
508   gluTessCallback (tobj, GLU_TESS_ERROR,  (_GLUfuncptr) &tess_error);
509
510   mi->polygon_count = 0;
511
512   bp->ncolors = 128;
513   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
514   make_random_colormap (0, 0, 0,
515                         bp->colors, &bp->ncolors,
516                         True, False, 0, False);
517
518   if (do_which >= bp->npolyhedra)
519     do_which = -1;
520
521   bp->which = (bp->change_to != -1 ? bp->change_to :
522                do_which >= 0 ? do_which :
523                (random() % bp->npolyhedra));
524   bp->change_to = -1;
525   p = bp->polyhedra[bp->which];
526
527   new_label (mi);
528
529   glNewList (bp->object_list, GL_COMPILE);
530   for (i = 0; i < p->nfaces; i++)
531     {
532       int j;
533       face *f = &p->faces[i];
534
535       if (f->color > 64 || f->color < 0) abort();
536       if (wire)
537         glColor3f (0, 1, 0);
538       else
539         {
540           bcolor[0] = bp->colors[f->color].red   / 65536.0;
541           bcolor[1] = bp->colors[f->color].green / 65536.0;
542           bcolor[2] = bp->colors[f->color].blue  / 65536.0;
543           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bcolor);
544         }
545
546       kludge_normal (f->npoints, f->points, p->points);
547       
548       gluTessBeginPolygon (tobj, 0);
549       gluTessBeginContour (tobj);
550       for (j = 0; j < f->npoints; j++)
551         {
552           point *pp = &p->points[f->points[j]];
553           gluTessVertex (tobj, &pp->x, &pp->x);
554         }
555       gluTessEndContour (tobj);
556       gluTessEndPolygon (tobj);
557     }
558   glEndList ();
559
560   mi->polygon_count += p->nfaces;
561   gluDeleteTess (tobj);
562 }
563
564
565 void 
566 init_polyhedra (ModeInfo *mi)
567 {
568   polyhedra_configuration *bp;
569   int wire = MI_IS_WIREFRAME(mi);
570
571   if (!bps) {
572     bps = (polyhedra_configuration *)
573       calloc (MI_NUM_SCREENS(mi), sizeof (polyhedra_configuration));
574     if (!bps) {
575       fprintf(stderr, "%s: out of memory\n", progname);
576       exit(1);
577     }
578
579     bp = &bps[MI_SCREEN(mi)];
580   }
581
582   bp = &bps[MI_SCREEN(mi)];
583
584   bp->glx_context = init_GL(mi);
585
586   bp->which = -1;
587   load_fonts (mi);
588   startup_blurb (mi);
589
590   reshape_polyhedra (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
591
592   if (!wire)
593     {
594       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
595       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
596       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
597       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
598
599       glEnable(GL_LIGHTING);
600       glEnable(GL_LIGHT0);
601       glEnable(GL_DEPTH_TEST);
602       /* glEnable(GL_CULL_FACE); */
603
604       /* We need two-sided lighting for polyhedra where both sides of
605          a face are simultaneously visible (e.g., the "X-hemi-Y-hedrons".)
606        */
607       glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
608
609       glLightfv(GL_LIGHT0, GL_POSITION, pos);
610       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
611       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
612       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
613     }
614
615   {
616     double spin_speed   = 2.0;
617     double wander_speed = 0.05;
618     double spin_accel   = 0.2;
619
620     bp->rot = make_rotator (do_spin ? spin_speed : 0,
621                             do_spin ? spin_speed : 0,
622                             do_spin ? spin_speed : 0,
623                             spin_accel,
624                             do_wander ? wander_speed : 0,
625                             True);
626     bp->trackball = gltrackball_init ();
627   }
628
629   bp->npolyhedra = construct_polyhedra (&bp->polyhedra);
630
631   bp->object_list = glGenLists (1);
632   bp->title_list  = glGenLists (1);
633   bp->change_to = -1;
634
635   {
636     int x;
637     char c;
638     do_which = -1;
639     if (1 == sscanf (do_which_str, " %d %c", &x, &c))
640       do_which = x;
641     else if (*do_which_str)
642       {
643         char *s;
644         for (s = do_which_str; *s; s++)
645           if (*s == '-' || *s == '_') *s = ' ';
646
647         for (x = 0; x < bp->npolyhedra; x++)
648           if (!strcasecmp (do_which_str, bp->polyhedra[x]->name) ||
649               !strcasecmp (do_which_str, bp->polyhedra[x]->wythoff) ||
650               !strcasecmp (do_which_str, bp->polyhedra[x]->config))
651             {
652               do_which = x;
653               break;
654             }
655         if (do_which < 0)
656           {
657             fprintf (stderr, "%s: no such polyhedron: \"%s\"\n",
658                      progname, do_which_str);
659             exit (1);
660           }
661       }
662   }
663
664   new_polyhedron (mi);
665 }
666
667
668 void
669 draw_polyhedra (ModeInfo *mi)
670 {
671   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
672   Display *dpy = MI_DISPLAY(mi);
673   Window window = MI_WINDOW(mi);
674
675   static time_t last_time = 0;
676
677   static GLfloat bspec[4]  = {1.0, 1.0, 1.0, 1.0};
678   static GLfloat bshiny    = 128.0;
679
680   if (!bp->glx_context)
681     return;
682
683   if (bp->mode == 0 && do_which >= 0 && bp->change_to < 0)
684     ;
685   else if (bp->mode == 0)
686     {
687       static int tick = 0;
688
689       if (bp->change_to >= 0)
690         tick = 999, last_time = 1;
691       if (tick++ > 10)
692         {
693           time_t now = time((time_t *) 0);
694           if (last_time == 0) last_time = now;
695           tick = 0;
696           if (!bp->button_down_p && now - last_time >= duration)
697             {
698               bp->mode = 1;    /* go out */
699               bp->mode_tick = 20 * speed;
700               last_time = now;
701             }
702         }
703     }
704   else if (bp->mode == 1)   /* out */
705     {
706       if (--bp->mode_tick <= 0)
707         {
708           new_polyhedron (mi);
709           bp->mode_tick = 20 * speed;
710           bp->mode = 2;  /* go in */
711         }
712     }
713   else if (bp->mode == 2)   /* in */
714     {
715       if (--bp->mode_tick <= 0)
716         bp->mode = 0;  /* normal */
717     }
718   else
719     abort();
720
721   glShadeModel(GL_FLAT);
722   glEnable(GL_NORMALIZE);
723
724   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
725
726   glPushMatrix ();
727
728   glScalef(1.1, 1.1, 1.1);
729
730   {
731     double x, y, z;
732     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
733     glTranslatef((x - 0.5) * 8,
734                  (y - 0.5) * 8,
735                  (z - 0.5) * 15);
736
737     gltrackball_rotate (bp->trackball);
738
739     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
740     glRotatef (x * 360, 1.0, 0.0, 0.0);
741     glRotatef (y * 360, 0.0, 1.0, 0.0);
742     glRotatef (z * 360, 0.0, 0.0, 1.0);
743   }
744
745   glScalef (2.0, 2.0, 2.0);
746
747   glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,  bspec);
748   glMateriali  (GL_FRONT_AND_BACK, GL_SHININESS, bshiny);
749
750   if (bp->mode != 0)
751     {
752       GLfloat s = (bp->mode == 1
753                    ? bp->mode_tick / (20 * speed)
754                    : ((20 * speed) - bp->mode_tick + 1) / (20 * speed));
755       glScalef (s, s, s);
756     }
757
758   glScalef (2, 2, 2);
759   glCallList (bp->object_list);
760   if (bp->mode == 0 && !bp->button_down_p)
761     glCallList (bp->title_list);
762
763   glPopMatrix ();
764
765   if (mi->fps_p) do_fps (mi);
766   glFinish();
767
768   glXSwapBuffers(dpy, window);
769 }
770
771 #endif /* USE_GL */