1 /* polyhedra, Copyright (c) 2004-2014 Jamie Zawinski <jwz@jwz.org>
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
11 * Renders 160 different 3D solids, and displays some information about each.
12 * A new solid is chosen every few seconds.
14 * This file contains the OpenGL side; computation of the polyhedra themselves
15 * is in "polyhedra.c".
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 "*suppressRotationAnimation: True\n" \
27 # define refresh_polyhedra 0
28 # define release_polyhedra 0
30 #define countof(x) (sizeof((x))/sizeof((*x)))
32 #include "xlockmore.h"
37 # include <X11/Xlib.h>
44 #endif /* HAVE_JWZGLES */
46 #define DEF_SPIN "True"
47 #define DEF_WANDER "True"
48 #define DEF_SPEED "1.0"
49 #define DEF_TITLES "True"
50 #define DEF_DURATION "12"
51 #define DEF_WHICH "random"
55 #include "polyhedra.h"
58 #include "gltrackball.h"
62 # define XK_MISCELLANY
63 # include <X11/keysymdef.h>
71 #ifdef USE_GL /* whole file */
74 GLXContext *glx_context;
76 trackball_state *trackball;
80 polyhedron **polyhedra;
86 int mode; /* 0 = normal, 1 = out, 2 = in */
92 texture_font_data *font1_data, *font2_data, *font3_data;
94 time_t last_change_time;
97 } polyhedra_configuration;
99 static polyhedra_configuration *bps = NULL;
102 static GLfloat speed;
103 static Bool do_wander;
104 static Bool do_titles;
107 static char *do_which_str;
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 },
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},
130 ENTRYPOINT ModeSpecOpt polyhedra_opts = {countof(opts), opts, countof(vars), vars, NULL};
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.
139 kludge_normal (int n, const int *indices, const point *points)
141 XYZ normal = { 0, 0, 0 };
145 for (i = 0; i < n; ++i) {
147 int i2 = indices[(i + 1) % n];
148 int i3 = indices[(i + 2) % n];
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;
155 p = calc_normal (p1, p2, p3);
161 /*normalize(&normal);*/
162 if (normal.x == 0 && normal.y == 0 && normal.z == 0) {
163 glNormal3f (p.x, p.y, p.z);
165 glNormal3f (normal.x, normal.y, normal.z);
171 load_fonts (ModeInfo *mi)
173 polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
174 bp->font1_data = load_texture_font (mi->dpy, "titleFont");
175 bp->font2_data = load_texture_font (mi->dpy, "titleFont2");
176 bp->font3_data = load_texture_font (mi->dpy, "titleFont3");
182 startup_blurb (ModeInfo *mi)
184 polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
185 const char *s = "Computing polyhedra...";
186 texture_font_data *f = bp->font1_data;
188 glColor3f (0.8, 0.8, 0);
189 print_texture_label (mi->dpy, f,
190 mi->xgwa.width, mi->xgwa.height,
193 glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
198 /* Window management, etc
201 reshape_polyhedra (ModeInfo *mi, int width, int height)
203 GLfloat h = (GLfloat) height / (GLfloat) width;
205 glViewport (0, 0, (GLint) width, (GLint) height);
207 glMatrixMode(GL_PROJECTION);
209 gluPerspective (30.0, 1/h, 1.0, 100.0);
211 glMatrixMode(GL_MODELVIEW);
213 gluLookAt( 0.0, 0.0, 30.0,
217 glClear(GL_COLOR_BUFFER_BIT);
222 polyhedra_handle_event (ModeInfo *mi, XEvent *event)
224 polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
226 if (gltrackball_event_handler (event, bp->trackball,
227 MI_WIDTH (mi), MI_HEIGHT (mi),
230 else if (event->xany.type == KeyPress)
234 XLookupString (&event->xkey, &c, 1, &keysym, 0);
237 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
238 bp->change_to = random() % bp->npolyhedra;
239 else if (c == '>' || c == '.' || c == '+' || c == '=' ||
240 keysym == XK_Right || keysym == XK_Up || keysym == XK_Next)
241 bp->change_to = (bp->which + 1) % bp->npolyhedra;
242 else if (c == '<' || c == ',' || c == '-' || c == '_' ||
243 c == '\010' || c == '\177' ||
244 keysym == XK_Left || keysym == XK_Down || keysym == XK_Prior)
245 bp->change_to = (bp->which + bp->npolyhedra - 1) % bp->npolyhedra;
246 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
249 if (bp->change_to != -1)
252 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
255 bp->change_to = random() % bp->npolyhedra;
264 draw_label (ModeInfo *mi)
266 polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
267 polyhedron *p = bp->which >= 0 ? bp->polyhedra[bp->which] : 0;
270 GLfloat color[4] = { 0.8, 0.8, 0.8, 1 };
271 texture_font_data *f;
273 if (!p || !do_titles) return;
275 strcpy (name2, p->name);
277 sprintf (name2 + strlen(name2), " (%s)", p->class);
280 "Polyhedron %d: \t%s\n\n"
281 "Wythoff Symbol:\t%s\n"
282 "Vertex Configuration:\t%s\n"
283 "Symmetry Group:\t%s\n"
284 /* "Dual of: \t%s\n" */
291 bp->which, name2, p->wythoff, p->config, p->group,
293 p->logical_faces, p->nedges, p->logical_vertices,
294 p->density, (p->chi < 0 ? "" : " "), p->chi);
296 if (MI_WIDTH(mi) >= 500 && MI_HEIGHT(mi) >= 375)
298 else if (MI_WIDTH(mi) >= 350 && MI_HEIGHT(mi) >= 260)
299 f = bp->font2_data; /* small font */
301 f = bp->font3_data; /* tiny font */
304 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
305 print_texture_label (mi->dpy, f,
306 mi->xgwa.width, mi->xgwa.height,
313 tess_error (GLenum errorCode)
315 fprintf (stderr, "%s: tesselation error: %s\n",
316 progname, gluErrorString(errorCode));
319 #endif /* HAVE_TESS */
323 new_polyhedron (ModeInfo *mi)
325 polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
327 int wire = MI_IS_WIREFRAME(mi);
330 /* Use the GLU polygon tesselator so that nonconvex faces are displayed
331 correctly (e.g., for the "pentagrammic concave deltohedron").
334 GLUtesselator *tobj = gluNewTess();
335 gluTessCallback (tobj, GLU_TESS_BEGIN, (void (*) (void)) &glBegin);
336 gluTessCallback (tobj, GLU_TESS_END, (void (*) (void)) &glEnd);
337 gluTessCallback (tobj, GLU_TESS_VERTEX, (void (*) (void)) &glVertex3dv);
338 gluTessCallback (tobj, GLU_TESS_ERROR, (void (*) (void)) &tess_error);
339 # endif /* HAVE_TESS */
341 mi->polygon_count = 0;
344 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
345 make_random_colormap (0, 0, 0,
346 bp->colors, &bp->ncolors,
347 True, False, 0, False);
349 if (do_which >= bp->npolyhedra)
352 bp->which = (bp->change_to != -1 ? bp->change_to :
353 do_which >= 0 ? do_which :
354 (random() % bp->npolyhedra));
356 p = bp->polyhedra[bp->which];
359 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
361 glNewList (bp->object_list, GL_COMPILE);
362 if (bp->which == bp->npolyhedra-1)
365 bcolor[0] = bp->colors[0].red / 65536.0;
366 bcolor[1] = bp->colors[0].green / 65536.0;
367 bcolor[2] = bp->colors[0].blue / 65536.0;
372 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bcolor);
374 glScalef (0.8, 0.8, 0.8);
375 p->nfaces = unit_teapot (6, wire);
376 p->nedges = p->nfaces * 3 / 2;
377 p->npoints = p->nfaces * 3;
378 p->logical_faces = p->nfaces;
379 p->logical_vertices = p->npoints;
383 glFrontFace (GL_CCW);
384 for (i = 0; i < p->nfaces; i++)
387 face *f = &p->faces[i];
389 if (f->color > 64 || f->color < 0) abort();
395 bcolor[0] = bp->colors[f->color].red / 65536.0;
396 bcolor[1] = bp->colors[f->color].green / 65536.0;
397 bcolor[2] = bp->colors[f->color].blue / 65536.0;
399 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bcolor);
402 kludge_normal (f->npoints, f->points, p->points);
405 gluTessBeginPolygon (tobj, 0);
406 gluTessBeginContour (tobj);
407 for (j = 0; j < f->npoints; j++)
409 point *pp = &p->points[f->points[j]];
410 gluTessVertex (tobj, &pp->x, &pp->x);
412 gluTessEndContour (tobj);
413 gluTessEndPolygon (tobj);
414 # else /* !HAVE_TESS */
415 glBegin (wire ? GL_LINE_LOOP :
416 f->npoints == 3 ? GL_TRIANGLES :
417 f->npoints == 4 ? GL_QUADS :
419 for (j = 0; j < f->npoints; j++)
421 point *pp = &p->points[f->points[j]];
422 glVertex3f (pp->x, pp->y, pp->z);
425 # endif /* !HAVE_TESS */
430 mi->polygon_count += p->nfaces;
432 gluDeleteTess (tobj);
438 construct_teapot (ModeInfo *mi)
440 polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
441 int n = bp->npolyhedra-1;
442 polyhedron *p = (polyhedron *) calloc (1, sizeof(*p));
444 p->wythoff = strdup("X00398|1984");
445 p->name = strdup("Teapot");
446 p->dual = strdup("");
447 p->config = strdup("Melitta");
448 p->group = strdup("Teapotahedral (Newell[1975])");
449 p->class = strdup("Utah Teapotahedron");
450 bp->polyhedra[n] = p;
455 init_polyhedra (ModeInfo *mi)
457 polyhedra_configuration *bp;
458 int wire = MI_IS_WIREFRAME(mi);
460 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
461 MI_IS_WIREFRAME(mi) = 0;
465 MI_INIT (mi, bps, NULL);
467 bp = &bps[MI_SCREEN(mi)];
469 bp->glx_context = init_GL(mi);
477 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
478 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
479 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
480 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
482 glEnable(GL_LIGHTING);
484 glEnable(GL_DEPTH_TEST);
485 /* glEnable(GL_CULL_FACE); */
487 /* We need two-sided lighting for polyhedra where both sides of
488 a face are simultaneously visible (e.g., the "X-hemi-Y-hedrons".)
490 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
492 glLightfv(GL_LIGHT0, GL_POSITION, pos);
493 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
494 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
495 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
499 double spin_speed = 2.0;
500 double wander_speed = 0.05;
501 double spin_accel = 0.2;
503 bp->rot = make_rotator (do_spin ? spin_speed : 0,
504 do_spin ? spin_speed : 0,
505 do_spin ? spin_speed : 0,
507 do_wander ? wander_speed : 0,
509 bp->trackball = gltrackball_init (True);
512 bp->npolyhedra = construct_polyhedra (&bp->polyhedra);
513 construct_teapot (mi);
515 bp->object_list = glGenLists (1);
522 if (!strcasecmp (do_which_str, "random"))
524 else if (1 == sscanf (do_which_str, " %d %c", &x, &c))
526 if (x >= 0 && x < bp->npolyhedra)
530 "%s: polyhedron %d does not exist: there are only %d.\n",
531 progname, x, bp->npolyhedra-1);
533 else if (*do_which_str)
536 for (s = do_which_str; *s; s++)
537 if (*s == '-' || *s == '_') *s = ' ';
539 for (x = 0; x < bp->npolyhedra; x++)
540 if (!strcasecmp (do_which_str, bp->polyhedra[x]->name) ||
541 !strcasecmp (do_which_str, bp->polyhedra[x]->class) ||
542 !strcasecmp (do_which_str, bp->polyhedra[x]->wythoff) ||
543 !strcasecmp (do_which_str, bp->polyhedra[x]->config))
550 fprintf (stderr, "%s: no such polyhedron: \"%s\"\n",
551 progname, do_which_str);
558 reshape_polyhedra (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
559 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
565 draw_polyhedra (ModeInfo *mi)
567 polyhedra_configuration *bp = &bps[MI_SCREEN(mi)];
568 Display *dpy = MI_DISPLAY(mi);
569 Window window = MI_WINDOW(mi);
571 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
572 GLfloat bshiny = 128.0;
574 if (!bp->glx_context)
577 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
579 if (bp->mode == 0 && do_which >= 0 && bp->change_to < 0)
581 else if (bp->mode == 0)
583 if (bp->change_to >= 0)
584 bp->change_tick = 999, bp->last_change_time = 1;
585 if (bp->change_tick++ > 10)
587 time_t now = time((time_t *) 0);
588 if (bp->last_change_time == 0) bp->last_change_time = now;
590 if (!bp->button_down_p && now - bp->last_change_time >= duration)
592 bp->mode = 1; /* go out */
593 bp->mode_tick = 20 / speed;
594 bp->last_change_time = now;
598 else if (bp->mode == 1) /* out */
600 if (--bp->mode_tick <= 0)
603 bp->mode_tick = 20 / speed;
604 bp->mode = 2; /* go in */
607 else if (bp->mode == 2) /* in */
609 if (--bp->mode_tick <= 0)
610 bp->mode = 0; /* normal */
615 glShadeModel(GL_FLAT);
616 glEnable(GL_NORMALIZE);
618 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
622 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
624 GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
625 int o = (int) current_device_rotation();
626 if (o != 0 && o != 180 && o != -180)
627 glScalef (1/h, 1/h, 1/h);
631 glScalef(1.1, 1.1, 1.1);
635 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
636 glTranslatef((x - 0.5) * 8,
640 gltrackball_rotate (bp->trackball);
642 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
643 glRotatef (x * 360, 1.0, 0.0, 0.0);
644 glRotatef (y * 360, 0.0, 1.0, 0.0);
645 glRotatef (z * 360, 0.0, 0.0, 1.0);
648 glScalef (2.0, 2.0, 2.0);
650 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, bspec);
651 glMateriali (GL_FRONT_AND_BACK, GL_SHININESS, bshiny);
655 GLfloat s = (bp->mode == 1
656 ? bp->mode_tick / (20 / speed)
657 : ((20 / speed) - bp->mode_tick + 1) / (20 / speed));
662 glCallList (bp->object_list);
663 if (bp->mode == 0 && !bp->button_down_p)
664 draw_label (mi); /* print_texture_font can't go inside a display list */
668 if (mi->fps_p) do_fps (mi);
671 glXSwapBuffers(dpy, window);
674 XSCREENSAVER_MODULE ("Polyhedra", polyhedra)