1 /* boing, Copyright (c) 2005-2012 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 * A clone of the Amiga 1000 "Boing" demo. This was the first graphics demo
12 * for the Amiga, written by Dale Luck and RJ Mical during a break at the 1984
13 * Consumer Electronics Show (or so the legend goes.) The boing ball was
14 * briefly the official logo of Amiga Inc., until they were bought by
15 * Commodore later that year.
17 * With no arguments, this program looks a lot like the original Amiga demo.
18 * With "-smooth -lighting", it looks... less old.
20 * The amiga version made noise when the ball hit the walls. This version
21 * does not, obviously.
24 #define DEFAULTS "*delay: 30000 \n" \
25 "*showFPS: False \n" \
26 "*wireframe: False \n" \
28 # define refresh_boing 0
29 # define release_boing 0
31 #define countof(x) (sizeof((x))/sizeof((*x)))
33 #include "xlockmore.h"
34 #include "gltrackball.h"
37 #ifdef USE_GL /* whole file */
40 #define DEF_SPIN "True"
41 #define DEF_LIGHTING "False"
42 #define DEF_SMOOTH "False"
43 #define DEF_SCANLINES "True"
44 #define DEF_SPEED "1.0"
45 #define DEF_BALL_SIZE "0.5"
46 #define DEF_ANGLE "15"
47 #define DEF_MERIDIANS "16"
48 #define DEF_PARALLELS "8"
49 #define DEF_TILES "12"
50 #define DEF_THICKNESS "0.05"
52 #define DEF_BALL_COLOR1 "#CC1919"
53 #define DEF_BALL_COLOR2 "#F2F2F2"
54 #define DEF_GRID_COLOR "#991999"
55 #define DEF_SHADOW_COLOR "#303030"
56 #define DEF_BACKGROUND "#8C8C8C"
58 typedef struct { GLfloat x, y, z; } XYZ;
61 GLXContext *glx_context;
62 trackball_state *trackball;
66 double ball_x, ball_y, ball_z, ball_th;
67 double ball_dx, ball_dy, ball_dz, ball_dth;
68 double ball_ddx, ball_ddy, ball_ddz;
70 GLfloat ball_color1[4], ball_color2[4], grid_color[4];
71 GLfloat bg_color[4], shadow_color[4];
74 } boing_configuration;
76 static boing_configuration *bps = NULL;
79 static Bool lighting_p;
81 static Bool scanlines_p;
84 static GLfloat ball_size;
85 static unsigned int meridians;
86 static unsigned int parallels;
87 static unsigned int tiles;
88 static GLfloat thickness;
89 static char *ball_color1_str, *ball_color2_str, *grid_color_str,
92 static XrmOptionDescRec opts[] = {
93 { "-spin", ".spin", XrmoptionNoArg, "True" },
94 { "+spin", ".spin", XrmoptionNoArg, "False" },
95 { "-lighting", ".lighting", XrmoptionNoArg, "True" },
96 { "+lighting", ".lighting", XrmoptionNoArg, "False" },
97 { "-smooth", ".smooth", XrmoptionNoArg, "True" },
98 { "+smooth", ".smooth", XrmoptionNoArg, "False" },
99 { "-scanlines", ".scanlines", XrmoptionNoArg, "True" },
100 { "+scanlines", ".scanlines", XrmoptionNoArg, "False" },
101 { "-speed", ".speed", XrmoptionSepArg, 0 },
102 { "-angle", ".angle", XrmoptionSepArg, 0 },
103 { "-size", ".ballSize", XrmoptionSepArg, 0 },
104 { "-meridians", ".meridians", XrmoptionSepArg, 0 },
105 { "-parallels", ".parallels", XrmoptionSepArg, 0 },
106 { "-tiles", ".tiles", XrmoptionSepArg, 0 },
107 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
108 { "-ball-color1",".ballColor1",XrmoptionSepArg, 0 },
109 { "-ball-color2",".ballColor2",XrmoptionSepArg, 0 },
110 { "-grid-color", ".gridColor", XrmoptionSepArg, 0 },
111 { "-shadow-color",".shadowColor",XrmoptionSepArg, 0 },
112 { "-background", ".boingBackground",XrmoptionSepArg, 0 },
113 { "-bg", ".boingBackground",XrmoptionSepArg, 0 },
116 static argtype vars[] = {
117 {&spin, "spin", "Spin", DEF_SPIN, t_Bool},
118 {&lighting_p,"lighting", "Lighting", DEF_LIGHTING, t_Bool},
119 {&smooth_p, "smooth", "Smooth", DEF_SMOOTH, t_Bool},
120 {&scanlines_p,"scanlines","Scanlines", DEF_SCANLINES, t_Bool},
121 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
122 {&angle, "angle", "Angle", DEF_ANGLE, t_Int},
123 {&ball_size, "ballSize", "BallSize", DEF_BALL_SIZE, t_Float},
124 {&meridians, "meridians", "meridians", DEF_MERIDIANS, t_Int},
125 {¶llels, "parallels", "parallels", DEF_PARALLELS, t_Int},
126 {&tiles, "tiles", "Tiles", DEF_TILES, t_Int},
127 {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
128 {&ball_color1_str, "ballColor1", "BallColor1", DEF_BALL_COLOR1, t_String},
129 {&ball_color2_str, "ballColor2", "BallColor2", DEF_BALL_COLOR2, t_String},
130 {&grid_color_str, "gridColor", "GridColor", DEF_GRID_COLOR, t_String},
131 {&shadow_str, "shadowColor","ShadowColor",DEF_SHADOW_COLOR,t_String},
132 /* dammit, -background is too magic... */
133 {&bg_str, "boingBackground", "Background", DEF_BACKGROUND, t_String},
136 ENTRYPOINT ModeSpecOpt boing_opts = {countof(opts), opts, countof(vars), vars, NULL};
139 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
142 a[3] = 1.0; /* alpha */
144 if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
146 fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
149 a[0] = c.red / 65536.0;
150 a[1] = c.green / 65536.0;
151 a[2] = c.blue / 65536.0;
156 draw_grid (ModeInfo *mi)
158 boing_configuration *bp = &bps[MI_SCREEN(mi)];
160 GLfloat t2 = (GLfloat) tiles / 2;
161 GLfloat s = 1.0 / (tiles + thickness);
164 GLfloat lw = MI_HEIGHT(mi) * 0.06 * thickness;
166 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bp->grid_color);
167 glColor3fv (bp->grid_color);
171 glTranslatef (-t2, -t2, 0);
175 for (y = 0; y <= tiles; y++)
177 glVertex3f (0, y, z);
178 glVertex3f (tiles, y, z);
179 /*mi->polygon_count++;*/
181 for (x = 0; x <= tiles; x++)
183 glVertex3f (x, tiles, z);
184 glVertex3f (x, 0, z);
185 /*mi->polygon_count++;*/
194 draw_box (ModeInfo *mi)
196 /* boing_configuration *bp = &bps[MI_SCREEN(mi)]; */
198 glTranslatef (0, 0, -0.5);
199 /* glFrontFace (GL_CCW);*/
204 glRotatef (90, 1, 0, 0);
205 glTranslatef (0, 0, 0.5);
206 /* glFrontFace (GL_CW);*/
213 draw_ball (ModeInfo *mi)
215 boing_configuration *bp = &bps[MI_SCREEN(mi)];
216 int wire = MI_IS_WIREFRAME(mi);
220 int scale = (smooth_p ? 5 : 1);
222 if (lighting_p && !wire)
223 glEnable (GL_LIGHTING);
234 glTranslatef (bp->ball_x, bp->ball_y, bp->ball_z);
235 glScalef (ball_size, ball_size, ball_size);
236 glRotatef (-angle, 0, 0, 1);
237 glRotatef (bp->ball_th, 0, 1, 0);
239 for (y = 0; y < yy; y++)
241 GLfloat thy0 = y * (M_PI * 2) / (yy * 2) + M_PI_2;
242 GLfloat thy1 = (y+1) * (M_PI * 2) / (yy * 2) + M_PI_2;
244 for (x = 0; x < xx; x++)
246 GLfloat thx0 = x * (M_PI * 2) / xx;
247 GLfloat thx1 = (x+1) * (M_PI * 2) / xx;
249 Bool bgp = ((x/scale) & 1) ^ ((y/scale) & 1);
251 if (wire && bgp) continue;
253 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
254 (bgp ? bp->ball_color2 : bp->ball_color1));
255 glColor3fv (bgp ? bp->ball_color2 : bp->ball_color1);
257 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
261 p.x = cos((thy0+thy1)/2) * cos((thx0+thx1)/2);
262 p.y = sin((thy0+thy1)/2);
263 p.z = cos((thy0+thy1)/2) * sin((thx0+thx1)/2);
264 glNormal3f (-p.x, -p.y, -p.z);
267 p.x = cos(thy0) * cos(thx0) / 2;
269 p.z = cos(thy0) * sin(thx0) / 2;
271 glNormal3f (-p.x, -p.y, -p.z);
272 glVertex3f (p.x, p.y, p.z);
274 p.x = cos(thy1) * cos(thx0) / 2;
276 p.z = cos(thy1) * sin(thx0) / 2;
278 glNormal3f (-p.x, -p.y, -p.z);
279 glVertex3f (p.x, p.y, p.z);
281 p.x = cos(thy1) * cos(thx1) / 2;
283 p.z = cos(thy1) * sin(thx1) / 2;
285 glNormal3f (-p.x, -p.y, -p.z);
286 glVertex3f (p.x, p.y, p.z);
288 p.x = cos(thy0) * cos(thx1) / 2;
290 p.z = cos(thy0) * sin(thx1) / 2;
292 glNormal3f (-p.x, -p.y, -p.z);
293 glVertex3f (p.x, p.y, p.z);
302 if (lighting_p && !wire)
303 glDisable(GL_LIGHTING);
308 draw_shadow (ModeInfo *mi)
310 boing_configuration *bp = &bps[MI_SCREEN(mi)];
311 int wire = MI_IS_WIREFRAME(mi);
316 int scale = (smooth_p ? 5 : 1);
318 if (lighting_p && !wire)
327 glTranslatef (bp->ball_x + xoff, bp->ball_y + yoff, -0.49);
328 glScalef (ball_size, ball_size, ball_size);
329 glRotatef (-angle, 0, 0, 1);
331 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bp->shadow_color);
332 glColor4fv (bp->shadow_color);
334 glFrontFace (GL_CCW);
335 glNormal3f (0, 0, 1);
336 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
337 if (!wire) glVertex3f (0, 0, 0);
339 for (y = 0; y < yy*2+1; y++)
341 GLfloat thy0 = y * (M_PI * 2) / (yy * 2) + M_PI_2;
342 glVertex3f (cos(thy0) / 2, sin(thy0) / 2, 0);
350 if (lighting_p && !wire)
351 glDisable (GL_BLEND);
356 draw_scanlines (ModeInfo *mi)
358 /* boing_configuration *bp = &bps[MI_SCREEN(mi)]; */
359 int wire = MI_IS_WIREFRAME(mi);
360 int w = MI_WIDTH(mi);
361 int h = MI_HEIGHT(mi);
363 if (h <= 300) return;
368 glDisable (GL_DEPTH_TEST);
371 glMatrixMode(GL_PROJECTION);
375 glMatrixMode(GL_MODELVIEW);
381 glOrtho (0, w, 0, h, -1, 1);
383 if (h > 500) lh = 4, ls = 4;
384 else if (h > 300) lh = 2, ls = 1;
388 glDisable (GL_BLEND);
391 glColor4f (0, 0, 0, 0.3);
394 for (y = 0; y < h; y += lh + ls)
396 glVertex3f (0, y, 0);
397 glVertex3f (w, y, 0);
403 glMatrixMode(GL_PROJECTION);
405 glMatrixMode(GL_MODELVIEW);
409 glDisable (GL_BLEND);
410 glEnable (GL_DEPTH_TEST);
417 tick_physics (ModeInfo *mi)
419 boing_configuration *bp = &bps[MI_SCREEN(mi)];
420 GLfloat s2 = ball_size / 2;
421 GLfloat max = 0.5 - s2;
424 bp->ball_th += bp->ball_dth;
425 while (bp->ball_th > 360) bp->ball_th -= 360;
426 while (bp->ball_th < 0) bp->ball_th += 360;
428 bp->ball_dx += bp->ball_ddx;
429 bp->ball_x += bp->ball_dx;
430 if (bp->ball_x < min) bp->ball_x = min, bp->ball_dx = -bp->ball_dx,
431 bp->ball_dth = -bp->ball_dth,
432 bp->ball_dx += (frand(speed/2) - speed);
433 else if (bp->ball_x > max) bp->ball_x = max, bp->ball_dx = -bp->ball_dx,
434 bp->ball_dth = -bp->ball_dth,
435 bp->ball_dx += (frand(speed/2) - speed);
437 bp->ball_dy += bp->ball_ddy;
438 bp->ball_y += bp->ball_dy;
439 if (bp->ball_y < min) bp->ball_y = min, bp->ball_dy = -bp->ball_dy;
440 else if (bp->ball_y > max) bp->ball_y = max, bp->ball_dy = -bp->ball_dy;
442 bp->ball_dz += bp->ball_ddz;
443 bp->ball_z += bp->ball_dz;
444 if (bp->ball_z < min) bp->ball_z = min, bp->ball_dz = -bp->ball_dz;
445 else if (bp->ball_z > max) bp->ball_z = max, bp->ball_dz = -bp->ball_dz;
450 /* Window management, etc
453 reshape_boing (ModeInfo *mi, int width, int height)
455 GLfloat h = (GLfloat) height / (GLfloat) width;
457 h *= 4.0 / 3.0; /* Back in the caveman days we couldn't even afford
460 glViewport (0, 0, (GLint) width, (GLint) height);
462 glMatrixMode(GL_PROJECTION);
467 GLfloat s = width / (GLfloat) height;
471 gluPerspective (8.0, 1/h, 1.0, 10.0);
473 glMatrixMode(GL_MODELVIEW);
475 gluLookAt (0.0, 0.0, 8.0,
479 glClear(GL_COLOR_BUFFER_BIT);
484 boing_handle_event (ModeInfo *mi, XEvent *event)
486 boing_configuration *bp = &bps[MI_SCREEN(mi)];
488 if (event->xany.type == ButtonPress &&
489 event->xbutton.button == Button1)
491 bp->button_down_p = True;
492 gltrackball_start (bp->trackball,
493 event->xbutton.x, event->xbutton.y,
494 MI_WIDTH (mi), MI_HEIGHT (mi));
497 else if (event->xany.type == ButtonRelease &&
498 event->xbutton.button == Button1)
500 bp->button_down_p = False;
503 else if (event->xany.type == ButtonPress &&
504 (event->xbutton.button == Button4 ||
505 event->xbutton.button == Button5 ||
506 event->xbutton.button == Button6 ||
507 event->xbutton.button == Button7))
509 gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
510 !!event->xbutton.state);
513 else if (event->xany.type == MotionNotify &&
516 gltrackball_track (bp->trackball,
517 event->xmotion.x, event->xmotion.y,
518 MI_WIDTH (mi), MI_HEIGHT (mi));
527 init_boing (ModeInfo *mi)
529 boing_configuration *bp;
530 int wire = MI_IS_WIREFRAME(mi);
533 bps = (boing_configuration *)
534 calloc (MI_NUM_SCREENS(mi), sizeof (boing_configuration));
536 fprintf(stderr, "%s: out of memory\n", progname);
541 bp = &bps[MI_SCREEN(mi)];
543 bp->glx_context = init_GL(mi);
545 if (tiles < 1) tiles = 1;
549 if (meridians < 1) meridians = 1;
550 if (parallels < 1) parallels = 1;
554 if (meridians < 3) meridians = 3;
555 if (parallels < 2) parallels = 2;
558 if (meridians > 1 && meridians & 1) meridians++; /* odd numbers look bad */
561 if (thickness <= 0) thickness = 0.001;
562 else if (thickness > 1) thickness = 1;
564 reshape_boing (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
566 parse_color (mi, "ballColor1", ball_color1_str, bp->ball_color1);
567 parse_color (mi, "ballColor2", ball_color2_str, bp->ball_color2);
568 parse_color (mi, "gridColor", grid_color_str, bp->grid_color);
569 parse_color (mi, "shadowColor", shadow_str, bp->shadow_color);
570 parse_color (mi, "background", bg_str, bp->bg_color);
572 bp->shadow_color[3] = 0.9;
574 glClearColor (bp->bg_color[0], bp->bg_color[1], bp->bg_color[2], 1);
578 glEnable(GL_DEPTH_TEST);
579 glEnable(GL_CULL_FACE);
582 bp->lightpos[0] = 0.5;
583 bp->lightpos[1] = 0.5;
584 bp->lightpos[2] = -1;
587 if (lighting_p && !wire)
589 GLfloat amb[4] = {0, 0, 0, 1};
590 GLfloat dif[4] = {1, 1, 1, 1};
591 GLfloat spc[4] = {1, 1, 1, 1};
593 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
594 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
595 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
598 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
600 speed = speed / 800.0;
602 bp->ball_dth = (spin ? -speed * 7 * 360 : 0);
604 bp->ball_x = 0.5 - ((ball_size/2) + frand(1-ball_size));
606 bp->ball_dx = speed * 6 + frand(speed);
607 bp->ball_ddy = -speed;
609 bp->ball_dz = speed * 6 + frand(speed);
611 bp->trackball = gltrackball_init ();
616 draw_boing (ModeInfo *mi)
618 boing_configuration *bp = &bps[MI_SCREEN(mi)];
619 Display *dpy = MI_DISPLAY(mi);
620 Window window = MI_WINDOW(mi);
622 if (!bp->glx_context)
625 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
627 mi->polygon_count = 0;
629 glShadeModel(GL_SMOOTH);
631 glEnable(GL_NORMALIZE);
633 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
635 if (! bp->button_down_p)
641 double rot = current_device_rotation();
642 glRotatef(rot, 0, 0, 1);
643 if ((rot > 45 && rot < 135) ||
644 (rot < -45 && rot > -135))
646 GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
647 glScalef (1/s, s, 1);
651 gltrackball_rotate (bp->trackball);
653 glLightfv (GL_LIGHT0, GL_POSITION, bp->lightpos);
655 glDisable (GL_CULL_FACE);
656 glDisable (GL_DEPTH_TEST);
658 glEnable (GL_LINE_SMOOTH);
659 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
660 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
666 glEnable (GL_CULL_FACE);
667 glEnable (GL_DEPTH_TEST);
675 if (mi->fps_p) do_fps (mi);
678 glXSwapBuffers(dpy, window);
681 XSCREENSAVER_MODULE ("Boing", boing)