From http://www.jwz.org/xscreensaver/xscreensaver-5.20.tar.gz
[xscreensaver] / hacks / glx / polyhedra-gl.c
1 /* polyhedra, Copyright (c) 2004-2012 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 #define DEFAULTS        "*delay:        30000         \n" \
19                         "*showFPS:      False         \n" \
20                         "*wireframe:    False         \n" \
21                         "*titleFont:  -*-helvetica-medium-r-normal-*-140-*\n" \
22                         "*titleFont2: -*-helvetica-medium-r-normal-*-100-*\n" \
23                         "*titleFont3: -*-helvetica-medium-r-normal-*-80-*\n"  \
24
25
26 # define refresh_polyhedra 0
27 # define release_polyhedra 0
28 #undef countof
29 #define countof(x) (sizeof((x))/sizeof((*x)))
30
31 #include "xlockmore.h"
32
33 #ifdef HAVE_COCOA
34 # include "jwxyz.h"
35 #else
36 # include <X11/Xlib.h>
37 # include <GL/gl.h>
38 # include <GL/glu.h>
39 #endif
40
41 #ifdef HAVE_JWZGLES
42 # include "jwzgles.h"
43 #endif /* HAVE_JWZGLES */
44
45 #define DEF_SPIN        "True"
46 #define DEF_WANDER      "True"
47 #define DEF_SPEED       "1.0"
48 #define DEF_TITLES      "True"
49 #define DEF_DURATION    "12"
50 #define DEF_WHICH       "random"
51
52 #include "glxfonts.h"
53 #include "normals.h"
54 #include "polyhedra.h"
55 #include "colors.h"
56 #include "rotator.h"
57 #include "gltrackball.h"
58 #include "teapot.h"
59
60 #ifndef HAVE_COCOA
61 # define XK_MISCELLANY
62 # include <X11/keysymdef.h>
63 #endif
64
65 #ifndef HAVE_JWZGLES
66 # define HAVE_TESS
67 #endif
68
69
70 #ifdef USE_GL /* whole file */
71
72 typedef struct {
73   GLXContext *glx_context;
74   rotator *rot;
75   trackball_state *trackball;
76   Bool button_down_p;
77
78   int npolyhedra;
79   polyhedron **polyhedra;
80
81   int which;
82   int change_to;
83   GLuint object_list;
84   GLuint title_list;
85
86   int mode;  /* 0 = normal, 1 = out, 2 = in */
87   int mode_tick;
88
89   int ncolors;
90   XColor *colors;
91
92 # ifdef HAVE_GLBITMAP
93   XFontStruct *xfont1, *xfont2, *xfont3;
94   GLuint font1_dlist, font2_dlist, font3_dlist;
95 # else
96   texture_font_data *font1_data, *font2_data, *font3_data;
97 # endif
98
99   time_t last_change_time;
100   int change_tick;
101
102 } polyhedra_configuration;
103
104 static polyhedra_configuration *bps = NULL;
105
106 static Bool do_spin;
107 static GLfloat speed;
108 static Bool do_wander;
109 static Bool do_titles;
110 static int duration;
111 static int do_which;
112 static char *do_which_str;
113
114 static XrmOptionDescRec opts[] = {
115   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
116   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
117   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
118   { "-wander", ".wander", XrmoptionNoArg, "True" },
119   { "+wander", ".wander", XrmoptionNoArg, "False" },
120   { "-titles", ".titles", XrmoptionNoArg, "True" },
121   { "+titles", ".titles", XrmoptionNoArg, "False" },
122   { "-duration",".duration",XrmoptionSepArg, 0 },
123   { "-which",  ".which",   XrmoptionSepArg, 0 },
124 };
125
126 static argtype vars[] = {
127   {&do_spin,   "spin",   "Spin",   DEF_SPIN,    t_Bool},
128   {&do_wander, "wander", "Wander", DEF_WANDER,  t_Bool},
129   {&do_titles, "titles", "Titles", DEF_TITLES,  t_Bool},
130   {&speed,     "speed",  "Speed",  DEF_SPEED,   t_Float},
131   {&duration,"duration","Duration",DEF_DURATION,t_Int},
132   {&do_which_str,"which", "Which", DEF_WHICH,   t_String},
133 };
134
135 ENTRYPOINT ModeSpecOpt polyhedra_opts = {countof(opts), opts, countof(vars), vars, NULL};
136
137
138 \f
139 /* Calculate the normals at each vertex of a face, and use the sum to
140    decide which normal to assign to the entire face.  This also solves
141    problems caused by nonconvex faces, in most (but not all) cases.
142  */
143 static void
144 kludge_normal (int n, const int *indices, const point *points)
145 {
146   XYZ normal = { 0, 0, 0 };
147   XYZ p = { 0, 0, 0 };
148   int i;
149
150   for (i = 0; i < n; ++i) {
151     int i1 = indices[i];
152     int i2 = indices[(i + 1) % n];
153     int i3 = indices[(i + 2) % n];
154     XYZ p1, p2, p3;
155
156     p1.x = points[i1].x; p1.y = points[i1].y; p1.z = points[i1].z;
157     p2.x = points[i2].x; p2.y = points[i2].y; p2.z = points[i2].z;
158     p3.x = points[i3].x; p3.y = points[i3].y; p3.z = points[i3].z;
159
160     p = calc_normal (p1, p2, p3);
161     normal.x += p.x;
162     normal.y += p.y;
163     normal.z += p.z;
164   }
165
166   /*normalize(&normal);*/
167   if (normal.x == 0 && normal.y == 0 && normal.z == 0) {
168     glNormal3f (p.x, p.y, p.z);
169   } else {
170     glNormal3f (normal.x, normal.y, normal.z);
171   }
172 }
173
174
175 static void
176 load_fonts (ModeInfo *mi)
177 {
178   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
179 # ifdef HAVE_GLBITMAP
180   load_font (mi->dpy, "titleFont",  &bp->xfont1, &bp->font1_dlist);
181   load_font (mi->dpy, "titleFont2", &bp->xfont2, &bp->font2_dlist);
182   load_font (mi->dpy, "titleFont3", &bp->xfont3, &bp->font3_dlist);
183 # else /* !HAVE_GLBITMAP */
184   bp->font1_data = load_texture_font (mi->dpy, "titleFont");
185   bp->font2_data = load_texture_font (mi->dpy, "titleFont2");
186   bp->font3_data = load_texture_font (mi->dpy, "titleFont3");
187 # endif /* !HAVE_GLBITMAP */
188 }
189
190
191
192 static void
193 startup_blurb (ModeInfo *mi)
194 {
195   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
196   const char *s = "Computing polyhedra...";
197 # ifdef HAVE_GLBITMAP
198   XFontStruct *f = bp->xfont1;
199 # else /* !HAVE_GLBITMAP */
200   texture_font_data *f = bp->font1_data;
201 # endif /* !HAVE_GLBITMAP */
202
203   glColor3f (0.8, 0.8, 0);
204   print_gl_string (mi->dpy, 
205 # ifdef HAVE_GLBITMAP
206                    bp->xfont1, bp->font1_dlist,
207 # else /* !HAVE_GLBITMAP */
208                    bp->font1_data,
209 # endif /* !HAVE_GLBITMAP */
210                    mi->xgwa.width, mi->xgwa.height,
211                    mi->xgwa.width - (
212 # ifdef HAVE_GLBITMAP
213                                      string_width (f, s, 0)
214 # else /* !HAVE_GLBITMAP */
215                                      texture_string_width (f, s, 0)
216 # endif /* !HAVE_GLBITMAP */
217                                      + 40),
218                    mi->xgwa.height - 10,
219                    s, False);
220   glFinish();
221   glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
222 }
223
224
225
226 /* Window management, etc
227  */
228 static void new_label (ModeInfo *mi);
229
230 ENTRYPOINT void
231 reshape_polyhedra (ModeInfo *mi, int width, int height)
232 {
233   GLfloat h = (GLfloat) height / (GLfloat) width;
234
235   glViewport (0, 0, (GLint) width, (GLint) height);
236
237   glMatrixMode(GL_PROJECTION);
238   glLoadIdentity();
239   gluPerspective (30.0, 1/h, 1.0, 100.0);
240
241   glMatrixMode(GL_MODELVIEW);
242   glLoadIdentity();
243   gluLookAt( 0.0, 0.0, 30.0,
244              0.0, 0.0, 0.0,
245              0.0, 1.0, 0.0);
246
247   glClear(GL_COLOR_BUFFER_BIT);
248
249   /* need to re-render the text when the window size changes */
250   new_label (mi);
251 }
252
253
254 ENTRYPOINT Bool
255 polyhedra_handle_event (ModeInfo *mi, XEvent *event)
256 {
257   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
258
259   if (event->xany.type == ButtonPress &&
260       event->xbutton.button == Button1)
261     {
262       bp->button_down_p = True;
263       gltrackball_start (bp->trackball,
264                          event->xbutton.x, event->xbutton.y,
265                          MI_WIDTH (mi), MI_HEIGHT (mi));
266       return True;
267     }
268   else if (event->xany.type == ButtonRelease &&
269            event->xbutton.button == Button1)
270     {
271       bp->button_down_p = False;
272       return True;
273     }
274   else if (event->xany.type == ButtonPress &&
275            (event->xbutton.button == Button4 ||
276             event->xbutton.button == Button5 ||
277             event->xbutton.button == Button6 ||
278             event->xbutton.button == Button7))
279     {
280       gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
281                               !!event->xbutton.state);
282       return True;
283     }
284   else if (event->xany.type == KeyPress)
285     {
286       KeySym keysym;
287       char c = 0;
288       XLookupString (&event->xkey, &c, 1, &keysym, 0);
289
290       bp->change_to = -1;
291       if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
292         bp->change_to = random() % bp->npolyhedra;
293       else if (c == '>' || c == '.' || c == '+' || c == '=' ||
294                keysym == XK_Right || keysym == XK_Up)
295         bp->change_to = (bp->which + 1) % bp->npolyhedra;
296       else if (c == '<' || c == ',' || c == '-' || c == '_' ||
297                c == '\010' || c == '\177' ||
298                keysym == XK_Left || keysym == XK_Down)
299         bp->change_to = (bp->which + bp->npolyhedra - 1) % bp->npolyhedra;
300
301       if (bp->change_to != -1)
302         return True;
303     }
304   else if (event->xany.type == MotionNotify &&
305            bp->button_down_p)
306     {
307       gltrackball_track (bp->trackball,
308                          event->xmotion.x, event->xmotion.y,
309                          MI_WIDTH (mi), MI_HEIGHT (mi));
310       return True;
311     }
312
313   return False;
314 }
315
316
317 static void
318 new_label (ModeInfo *mi)
319 {
320   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
321   polyhedron *p = bp->which >= 0 ? bp->polyhedra[bp->which] : 0;
322
323   glNewList (bp->title_list, GL_COMPILE);
324   if (p && do_titles)
325     {
326       char label[1024];
327       char name2[255];
328       strcpy (name2, p->name);
329       if (*p->class)
330         sprintf (name2 + strlen(name2), "  (%s)", p->class);
331
332       sprintf (label,
333                "Polyhedron %d:   \t%s\n\n"
334                "Wythoff Symbol:\t%s\n"
335                "Vertex Configuration:\t%s\n"
336                "Symmetry Group:\t%s\n"
337             /* "Dual of:              \t%s\n" */
338                "\n"
339                "Faces:\t  %d\n"
340                "Edges:\t  %d\n"
341                "Vertices:\t  %d\n"
342                "Density:\t  %d\n"
343                "Euler:\t%s%d\n",
344                bp->which, name2, p->wythoff, p->config, p->group,
345             /* p->dual, */
346                p->logical_faces, p->nedges, p->logical_vertices,
347                p->density, (p->chi < 0 ? "" : "  "), p->chi);
348
349       {
350 # ifdef HAVE_GLBITMAP
351         XFontStruct *f;
352         GLuint fl;
353 # else /* !HAVE_GLBITMAP */
354         texture_font_data *f;
355 # endif /* !HAVE_GLBITMAP */
356         if (MI_WIDTH(mi) >= 500 && MI_HEIGHT(mi) >= 375)
357 # ifdef HAVE_GLBITMAP
358           f = bp->xfont1, fl = bp->font1_dlist;                /* big font */
359 # else /* !HAVE_GLBITMAP */
360           f = bp->font1_data;
361 # endif /* !HAVE_GLBITMAP */
362         else if (MI_WIDTH(mi) >= 350 && MI_HEIGHT(mi) >= 260)
363 # ifdef HAVE_GLBITMAP
364           f = bp->xfont2, fl = bp->font2_dlist;                /* small font */
365 # else /* !HAVE_GLBITMAP */
366           f = bp->font2_data;                                  /* small font */
367 # endif /* !HAVE_GLBITMAP */
368         else
369 # ifdef HAVE_GLBITMAP
370           f = bp->xfont3, fl = bp->font3_dlist;                /* tiny font */
371 # else /* !HAVE_GLBITMAP */
372           f = bp->font3_data;                                  /* tiny font */
373 # endif /* !HAVE_GLBITMAP */
374
375         glColor3f (0.8, 0.8, 0);
376         print_gl_string (mi->dpy, f,
377 # ifdef HAVE_GLBITMAP
378                          fl,
379 # endif /* HAVE_GLBITMAP */
380                          mi->xgwa.width, mi->xgwa.height,
381                          10, mi->xgwa.height - 10,
382                          label, False);
383       }
384     }
385   glEndList ();
386 }
387
388
389 #ifdef HAVE_TESS
390 static void
391 tess_error (GLenum errorCode)
392 {
393   fprintf (stderr, "%s: tesselation error: %s\n",
394            progname, gluErrorString(errorCode));
395   abort();
396 }
397 #endif /* HAVE_TESS */
398
399
400 static void
401 new_polyhedron (ModeInfo *mi)
402 {
403   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
404   polyhedron *p;
405   int wire = MI_IS_WIREFRAME(mi);
406   int i;
407
408   /* Use the GLU polygon tesselator so that nonconvex faces are displayed
409      correctly (e.g., for the "pentagrammic concave deltohedron").
410    */
411 # ifdef HAVE_TESS
412   GLUtesselator *tobj = gluNewTess();
413   gluTessCallback (tobj, GLU_TESS_BEGIN,  (void (*) (void)) &glBegin);
414   gluTessCallback (tobj, GLU_TESS_END,    (void (*) (void)) &glEnd);
415   gluTessCallback (tobj, GLU_TESS_VERTEX, (void (*) (void)) &glVertex3dv);
416   gluTessCallback (tobj, GLU_TESS_ERROR,  (void (*) (void)) &tess_error);
417 # endif /* HAVE_TESS */
418
419   mi->polygon_count = 0;
420
421   bp->ncolors = 128;
422   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
423   make_random_colormap (0, 0, 0,
424                         bp->colors, &bp->ncolors,
425                         True, False, 0, False);
426
427   if (do_which >= bp->npolyhedra)
428     do_which = -1;
429
430   bp->which = (bp->change_to != -1 ? bp->change_to :
431                do_which >= 0 ? do_which :
432                (random() % bp->npolyhedra));
433   bp->change_to = -1;
434   p = bp->polyhedra[bp->which];
435
436   new_label (mi);
437
438   if (wire)
439     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
440
441   glNewList (bp->object_list, GL_COMPILE);
442   if (bp->which == bp->npolyhedra-1)
443     {
444       GLfloat bcolor[4];
445       bcolor[0] = bp->colors[0].red   / 65536.0;
446       bcolor[1] = bp->colors[0].green / 65536.0;
447       bcolor[2] = bp->colors[0].blue  / 65536.0;
448       bcolor[3] = 1.0;
449       if (wire)
450         glColor3f (0, 1, 0);
451       else
452         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bcolor);
453
454       glScalef (0.8, 0.8, 0.8);
455       p->nfaces = unit_teapot (6, wire);
456       p->nedges = p->nfaces * 3 / 2;
457       p->npoints = p->nfaces * 3;
458       p->logical_faces = p->nfaces;
459       p->logical_vertices = p->npoints;
460     }
461   else
462     {
463       glFrontFace (GL_CCW);
464       for (i = 0; i < p->nfaces; i++)
465         {
466           int j;
467           face *f = &p->faces[i];
468
469           if (f->color > 64 || f->color < 0) abort();
470           if (wire)
471             glColor3f (0, 1, 0);
472           else
473             {
474               GLfloat bcolor[4];
475               bcolor[0] = bp->colors[f->color].red   / 65536.0;
476               bcolor[1] = bp->colors[f->color].green / 65536.0;
477               bcolor[2] = bp->colors[f->color].blue  / 65536.0;
478               bcolor[3] = 1.0;
479               glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bcolor);
480             }
481
482           kludge_normal (f->npoints, f->points, p->points);
483       
484 # ifdef HAVE_TESS
485           gluTessBeginPolygon (tobj, 0);
486           gluTessBeginContour (tobj);
487           for (j = 0; j < f->npoints; j++)
488             {
489               point *pp = &p->points[f->points[j]];
490               gluTessVertex (tobj, &pp->x, &pp->x);
491             }
492           gluTessEndContour (tobj);
493           gluTessEndPolygon (tobj);
494 # else  /* !HAVE_TESS */
495           glBegin (wire ? GL_LINE_LOOP :
496                    f->npoints == 3 ? GL_TRIANGLES :
497                    f->npoints == 4 ? GL_QUADS :
498                    GL_POLYGON);
499           for (j = 0; j < f->npoints; j++)
500             {
501               point *pp = &p->points[f->points[j]];
502               glVertex3f (pp->x, pp->y, pp->z);
503             }
504           glEnd();
505 # endif /* !HAVE_TESS */
506         }
507     }
508   glEndList ();
509
510   mi->polygon_count += p->nfaces;
511 # ifdef HAVE_TESS
512   gluDeleteTess (tobj);
513 # endif
514 }
515
516
517 static void
518 construct_teapot (ModeInfo *mi)
519 {
520   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
521   int n = bp->npolyhedra-1;
522   polyhedron *p = (polyhedron *) calloc (1, sizeof(*p));
523   p->number = n;
524   p->wythoff = strdup("X00398|1984");
525   p->name = strdup("Teapot");
526   p->dual = strdup("");
527   p->config = strdup("Melitta");
528   p->group = strdup("Teapotahedral (Newell[1975])");
529   p->class = strdup("Utah Teapotahedron");
530   bp->polyhedra[n] = p;
531 }
532
533
534 ENTRYPOINT void 
535 init_polyhedra (ModeInfo *mi)
536 {
537   polyhedra_configuration *bp;
538   int wire = MI_IS_WIREFRAME(mi);
539
540 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
541   MI_IS_WIREFRAME(mi) = 0;
542   wire = 0;
543 # endif
544
545   if (!bps) {
546     bps = (polyhedra_configuration *)
547       calloc (MI_NUM_SCREENS(mi), sizeof (polyhedra_configuration));
548     if (!bps) {
549       fprintf(stderr, "%s: out of memory\n", progname);
550       exit(1);
551     }
552   }
553
554   bp = &bps[MI_SCREEN(mi)];
555
556   bp->glx_context = init_GL(mi);
557
558   bp->which = -1;
559   load_fonts (mi);
560   startup_blurb (mi);
561
562   if (!wire)
563     {
564       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
565       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
566       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
567       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
568
569       glEnable(GL_LIGHTING);
570       glEnable(GL_LIGHT0);
571       glEnable(GL_DEPTH_TEST);
572       /* glEnable(GL_CULL_FACE); */
573
574       /* We need two-sided lighting for polyhedra where both sides of
575          a face are simultaneously visible (e.g., the "X-hemi-Y-hedrons".)
576        */
577       glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
578
579       glLightfv(GL_LIGHT0, GL_POSITION, pos);
580       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
581       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
582       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
583     }
584
585   {
586     double spin_speed   = 2.0;
587     double wander_speed = 0.05;
588     double spin_accel   = 0.2;
589
590     bp->rot = make_rotator (do_spin ? spin_speed : 0,
591                             do_spin ? spin_speed : 0,
592                             do_spin ? spin_speed : 0,
593                             spin_accel,
594                             do_wander ? wander_speed : 0,
595                             True);
596     bp->trackball = gltrackball_init ();
597   }
598
599   bp->npolyhedra = construct_polyhedra (&bp->polyhedra);
600   construct_teapot (mi);
601
602   bp->object_list = glGenLists (1);
603   bp->title_list  = glGenLists (1);
604   bp->change_to = -1;
605
606   {
607     int x;
608     char c;
609     do_which = -1;
610     if (!strcasecmp (do_which_str, "random"))
611       ;
612     else if (1 == sscanf (do_which_str, " %d %c", &x, &c))
613       {
614         if (x >= 0 && x < bp->npolyhedra) 
615           do_which = x;
616         else
617           fprintf (stderr, 
618                    "%s: polyhedron %d does not exist: there are only %d.\n",
619                    progname, x, bp->npolyhedra-1);
620       }
621     else if (*do_which_str)
622       {
623         char *s;
624         for (s = do_which_str; *s; s++)
625           if (*s == '-' || *s == '_') *s = ' ';
626
627         for (x = 0; x < bp->npolyhedra; x++)
628           if (!strcasecmp (do_which_str, bp->polyhedra[x]->name) ||
629               !strcasecmp (do_which_str, bp->polyhedra[x]->class) ||
630               !strcasecmp (do_which_str, bp->polyhedra[x]->wythoff) ||
631               !strcasecmp (do_which_str, bp->polyhedra[x]->config))
632             {
633               do_which = x;
634               break;
635             }
636         if (do_which < 0)
637           {
638             fprintf (stderr, "%s: no such polyhedron: \"%s\"\n",
639                      progname, do_which_str);
640             exit (1);
641           }
642       }
643   }
644
645   new_polyhedron (mi);
646   reshape_polyhedra (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
647   clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
648
649 }
650
651
652 ENTRYPOINT void
653 draw_polyhedra (ModeInfo *mi)
654 {
655   polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
656   Display *dpy = MI_DISPLAY(mi);
657   Window window = MI_WINDOW(mi);
658
659   static const GLfloat bspec[4]  = {1.0, 1.0, 1.0, 1.0};
660   GLfloat bshiny    = 128.0;
661
662   if (!bp->glx_context)
663     return;
664
665   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
666
667   if (bp->mode == 0 && do_which >= 0 && bp->change_to < 0)
668     ;
669   else if (bp->mode == 0)
670     {
671       if (bp->change_to >= 0)
672         bp->change_tick = 999, bp->last_change_time = 1;
673       if (bp->change_tick++ > 10)
674         {
675           time_t now = time((time_t *) 0);
676           if (bp->last_change_time == 0) bp->last_change_time = now;
677           bp->change_tick = 0;
678           if (!bp->button_down_p && now - bp->last_change_time >= duration)
679             {
680               bp->mode = 1;    /* go out */
681               bp->mode_tick = 20 * speed;
682               bp->last_change_time = now;
683             }
684         }
685     }
686   else if (bp->mode == 1)   /* out */
687     {
688       if (--bp->mode_tick <= 0)
689         {
690           new_polyhedron (mi);
691           bp->mode_tick = 20 * speed;
692           bp->mode = 2;  /* go in */
693         }
694     }
695   else if (bp->mode == 2)   /* in */
696     {
697       if (--bp->mode_tick <= 0)
698         bp->mode = 0;  /* normal */
699     }
700   else
701     abort();
702
703   glShadeModel(GL_FLAT);
704   glEnable(GL_NORMALIZE);
705
706   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
707
708   glPushMatrix ();
709
710   glScalef(1.1, 1.1, 1.1);
711
712   {
713     double x, y, z;
714     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
715     glTranslatef((x - 0.5) * 8,
716                  (y - 0.5) * 8,
717                  (z - 0.5) * 15);
718
719     /* Do it twice because we don't track the device's orientation. */
720     glRotatef( current_device_rotation(), 0, 0, 1);
721     gltrackball_rotate (bp->trackball);
722     glRotatef(-current_device_rotation(), 0, 0, 1);
723
724     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
725     glRotatef (x * 360, 1.0, 0.0, 0.0);
726     glRotatef (y * 360, 0.0, 1.0, 0.0);
727     glRotatef (z * 360, 0.0, 0.0, 1.0);
728   }
729
730   glScalef (2.0, 2.0, 2.0);
731
732   glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,  bspec);
733   glMateriali  (GL_FRONT_AND_BACK, GL_SHININESS, bshiny);
734
735   if (bp->mode != 0)
736     {
737       GLfloat s = (bp->mode == 1
738                    ? bp->mode_tick / (20 * speed)
739                    : ((20 * speed) - bp->mode_tick + 1) / (20 * speed));
740       glScalef (s, s, s);
741     }
742
743   glScalef (2, 2, 2);
744   glCallList (bp->object_list);
745   if (bp->mode == 0 && !bp->button_down_p)
746     glCallList (bp->title_list);
747
748   glPopMatrix ();
749
750   if (mi->fps_p) do_fps (mi);
751   glFinish();
752
753   glXSwapBuffers(dpy, window);
754 }
755
756 XSCREENSAVER_MODULE ("Polyhedra", polyhedra)
757
758 #endif /* USE_GL */