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