From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / glx / companion.c
1 /* companioncube, Copyright (c) 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
12 /* The symptoms most commonly produced by Enrichment Center testing are
13    superstition, perceiving inanimate objects as alive, and hallucinations.
14    The Enrichment Center reminds you that the weighted companion cube will
15    never threaten to stab you and, in fact, cannot speak.  In the event that
16    the Weighted Companion Cube does speak, the Enrichment Center urges you to
17    disregard its advice.
18  */
19
20
21 #define DEFAULTS        "*delay:        30000       \n" \
22                         "*showFPS:      False       \n" \
23                         "*count:        3           \n" \
24                         "*wireframe:    False       \n" \
25
26 /* #define DEBUG */
27
28
29 # define refresh_cube 0
30 # define release_cube 0
31 #define DEF_SPEED  "1.0"
32 #define DEF_SPIN   "False"
33 #define DEF_WANDER "False"
34
35 #undef countof
36 #define countof(x) (sizeof((x))/sizeof((*x)))
37
38 #undef BELLRAND
39 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
40 #undef RANDSIGN
41 #define RANDSIGN() ((random() & 1) ? 1 : -1)
42
43 #include "xlockmore.h"
44 #include "rotator.h"
45 #include "gltrackball.h"
46 #include "xpm-ximage.h"
47 #include <ctype.h>
48
49 #ifdef USE_GL /* whole file */
50
51 #include "gllist.h"
52
53 extern const struct gllist *companion_quad, *companion_disc, *companion_heart;
54 static const struct gllist * const *all_objs[] = {
55   &companion_quad, &companion_disc, &companion_heart
56 };
57 #define BASE_QUAD  0
58 #define BASE_DISC  1
59 #define BASE_HEART 2
60 #define FULL_CUBE  3
61
62 #define SPEED_SCALE 0.2
63
64 typedef struct {
65   GLfloat x, y, z;
66   GLfloat ix, iy, iz;
67   GLfloat dx, dy, dz;
68   GLfloat ddx, ddy, ddz;
69   GLfloat zr;
70   rotator *rot;
71   Bool spinner_p;
72 } floater;
73
74 typedef struct {
75   GLXContext *glx_context;
76   trackball_state *trackball;
77   Bool button_down_p;
78
79   GLuint *dlists;
80   int cube_polys;
81
82   int nfloaters;
83   floater *floaters;
84
85 } cube_configuration;
86
87 static cube_configuration *bps = NULL;
88
89 static GLfloat speed;
90 static Bool do_spin;
91 static Bool do_wander;
92
93 static XrmOptionDescRec opts[] = {
94   { "-speed",      ".speed",    XrmoptionSepArg, 0       },
95   { "-spin",       ".spin",     XrmoptionNoArg,  "True"  },
96   { "+spin",       ".spin",     XrmoptionNoArg,  "False" },
97   { "-wander",     ".wander",   XrmoptionNoArg,  "True"  },
98   { "+wander",     ".wander",   XrmoptionNoArg,  "False" },
99 };
100
101 static argtype vars[] = {
102   {&speed,     "speed",    "Speed",   DEF_SPEED,    t_Float},
103   {&do_spin,   "spin",     "Spin",    DEF_SPIN,     t_Bool},
104   {&do_wander, "wander",   "Wander",  DEF_WANDER,   t_Bool},
105 };
106
107 ENTRYPOINT ModeSpecOpt cube_opts = {countof(opts), opts, countof(vars), vars, NULL};
108
109
110 #define BOTTOM 28.0
111
112 static void
113 reset_floater (ModeInfo *mi, floater *f)
114 {
115   cube_configuration *bp = &bps[MI_SCREEN(mi)];
116
117   f->y = -BOTTOM;
118   f->x = f->ix;
119   f->z = f->iz;
120
121   /* Yes, I know I'm varying the force of gravity instead of varying the
122      launch velocity.  That's intentional: empirical studies indicate
123      that it's way, way funnier that way. */
124
125   f->dy = 5.0;
126   f->dx = 0;
127   f->dz = 0;
128
129   /* -0.18 max  -0.3 top -0.4 middle  -0.6 bottom */
130   f->ddy = speed * SPEED_SCALE * (-0.6 + BELLRAND(0.45));
131   f->ddx = 0;
132   f->ddz = 0;
133
134   if (do_spin || do_wander)
135     f->spinner_p = 0;
136   else
137     f->spinner_p = !(random() % (3 * bp->nfloaters));
138
139   if (! (random() % (30 * bp->nfloaters)))
140     {
141       f->dx = BELLRAND(1.8) * RANDSIGN();
142       f->dz = BELLRAND(1.8) * RANDSIGN();
143     }
144
145   f->zr = frand(180);
146   if (do_spin || do_wander)
147     {
148       f->y = 0;
149       if (bp->nfloaters > 2)
150         f->y += frand(3.0) * RANDSIGN();
151     }
152 }
153
154
155 static void
156 tick_floater (ModeInfo *mi, floater *f)
157 {
158   cube_configuration *bp = &bps[MI_SCREEN(mi)];
159
160   if (bp->button_down_p) return;
161
162   if (do_spin || do_wander) return;
163
164   f->dx += f->ddx;
165   f->dy += f->ddy;
166   f->dz += f->ddz;
167
168   f->x += f->dx * speed * SPEED_SCALE;
169   f->y += f->dy * speed * SPEED_SCALE;
170   f->z += f->dz * speed * SPEED_SCALE;
171
172   if (f->y < -BOTTOM ||
173       f->x < -BOTTOM*8 || f->x > BOTTOM*8 ||
174       f->z < -BOTTOM*8 || f->z > BOTTOM*8)
175     reset_floater (mi, f);
176 }
177
178
179
180
181
182 static int
183 build_corner (ModeInfo *mi)
184 {
185   cube_configuration *bp = &bps[MI_SCREEN(mi)];
186   GLfloat s;
187   const struct gllist *gll = *all_objs[BASE_QUAD];
188
189   glPushMatrix();
190   glTranslatef (-0.5, -0.5, -0.5);
191   s = 0.659;
192   glScalef (s, s, s);
193
194   glRotatef (180, 0, 1, 0);
195   glRotatef (180, 0, 0, 1);
196   glTranslatef (-0.12, -1.64, 0.12);
197   glCallList (bp->dlists[BASE_QUAD]);
198   glPopMatrix();
199
200   return gll->points / 3;
201 }
202
203
204 static int
205 build_face (ModeInfo *mi)
206 {
207   int polys = 0;
208   cube_configuration *bp = &bps[MI_SCREEN(mi)];
209   int wire = MI_IS_WIREFRAME(mi);
210   GLfloat s;
211   const struct gllist *gll;
212
213   GLfloat base_color[4]   = {0.53, 0.60, 0.66, 1.00};
214   GLfloat heart_color[4]  = {0.92, 0.67, 1.00, 1.00};
215   GLfloat disc_color[4]   = {0.75, 0.92, 1.00, 1.00};
216   GLfloat corner_color[4] = {0.75, 0.92, 1.00, 1.00};
217
218   if (!wire) 
219     {
220       GLfloat w = 0.010;
221       glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, base_color);
222       glPushMatrix();
223       glNormal3f (0, 0, -1);
224       glTranslatef (-0.5, -0.5, -0.5);
225
226       glBegin(GL_QUADS);
227       glVertex3f (0,     0,     0);
228       glVertex3f (0,     0.5-w, 0);
229       glVertex3f (0.5-w, 0.5-w, 0);
230       glVertex3f (0.5-w, 0,     0);
231
232       glVertex3f (0.5+w, 0,     0);
233       glVertex3f (0.5+w, 0.5-w, 0);
234       glVertex3f (1,     0.5-w, 0);
235       glVertex3f (1,     0,     0);
236
237       glVertex3f (0,     0.5+w, 0);
238       glVertex3f (0,     1,     0);
239       glVertex3f (0.5-w, 1,     0);
240       glVertex3f (0.5-w, 0.5+w, 0);
241
242       glVertex3f (0.5+w, 0.5+w, 0);
243       glVertex3f (0.5+w, 1,     0);
244       glVertex3f (1,     1,     0);
245       glVertex3f (1,     0.5+w, 0);
246       glEnd();
247
248       glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
249
250       glNormal3f (0, -1, 0);
251       glBegin(GL_QUADS);
252       glVertex3f (0, 0.5+w, 0);
253       glVertex3f (1, 0.5+w, 0);
254       glVertex3f (1, 0.5+w, w);
255       glVertex3f (0, 0.5+w, w);
256       glEnd();
257
258       glNormal3f (0, 1, 0);
259       glBegin(GL_QUADS);
260       glVertex3f (0, 0.5-w, w);
261       glVertex3f (1, 0.5-w, w);
262       glVertex3f (1, 0.5-w, 0);
263       glVertex3f (0, 0.5-w, 0);
264       glEnd();
265
266       glNormal3f (-1, 0, 0);
267       glBegin(GL_QUADS);
268       glVertex3f (0.5+w, 0, w);
269       glVertex3f (0.5+w, 1, w);
270       glVertex3f (0.5+w, 1, 0);
271       glVertex3f (0.5+w, 0, 0);
272       glEnd();
273
274       glNormal3f (1, 0, 0);
275       glBegin(GL_QUADS);
276       glVertex3f (0.5-w, 0, 0);
277       glVertex3f (0.5-w, 1, 0);
278       glVertex3f (0.5-w, 1, w);
279       glVertex3f (0.5-w, 0, w);
280       glEnd();
281
282       glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
283
284       glNormal3f (0, 0, -1);
285       glTranslatef (0, 0, w);
286       glBegin(GL_QUADS);
287       glVertex3f (0, 0, 0);
288       glVertex3f (0, 1, 0);
289       glVertex3f (1, 1, 0);
290       glVertex3f (1, 0, 0);
291       glEnd();
292
293       glPopMatrix();
294     }
295
296   glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, corner_color);
297
298   glPushMatrix();
299   polys += build_corner (mi); glRotatef (90, 0, 0, 1);
300   polys += build_corner (mi); glRotatef (90, 0, 0, 1);
301   polys += build_corner (mi); glRotatef (90, 0, 0, 1);
302   polys += build_corner (mi);
303
304   glRotatef (90, 0, 0, 1);
305   glTranslatef (0.585, -0.585, -0.5655);
306
307   s = 10.5;
308   glScalef (s, s, s);
309   glRotatef (180, 0, 1, 0);
310
311   if (! wire)
312     {
313       gll = *all_objs[BASE_HEART];
314       glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
315       glCallList (bp->dlists[BASE_HEART]);
316       polys += gll->points / 3;
317     }
318
319   gll = *all_objs[BASE_DISC];
320   glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, disc_color);
321   glCallList (bp->dlists[BASE_DISC]);
322   polys += gll->points / 3;
323
324   glPopMatrix();
325   return polys;
326 }
327
328
329 static int
330 build_cube (ModeInfo *mi)
331 {
332   int polys = 0;
333   glPushMatrix();
334   polys += build_face (mi); glRotatef (90, 0, 1, 0);
335   polys += build_face (mi); glRotatef (90, 0, 1, 0);
336   polys += build_face (mi); glRotatef (90, 0, 1, 0);
337   polys += build_face (mi); glRotatef (90, 1, 0, 0);
338   polys += build_face (mi); glRotatef (180,1, 0, 0);
339   polys += build_face (mi);
340   glPopMatrix();
341   return polys;
342 }
343
344
345 /* Window management, etc
346  */
347 ENTRYPOINT void
348 reshape_cube (ModeInfo *mi, int width, int height)
349 {
350   GLfloat h = (GLfloat) height / (GLfloat) width;
351
352   glViewport (0, 0, (GLint) width, (GLint) height);
353
354   glMatrixMode(GL_PROJECTION);
355   glLoadIdentity();
356   gluPerspective (30.0, 1/h, 1.0, 100);
357
358   glMatrixMode(GL_MODELVIEW);
359   glLoadIdentity();
360   gluLookAt( 0.0, 0.0, 30.0,
361              0.0, 0.0, 0.0,
362              0.0, 1.0, 0.0);
363
364   glClear(GL_COLOR_BUFFER_BIT);
365 }
366
367
368 ENTRYPOINT Bool
369 cube_handle_event (ModeInfo *mi, XEvent *event)
370 {
371   cube_configuration *bp = &bps[MI_SCREEN(mi)];
372
373   if (event->xany.type == ButtonPress &&
374       event->xbutton.button == Button1)
375     {
376       bp->button_down_p = True;
377       gltrackball_start (bp->trackball,
378                          event->xbutton.x, event->xbutton.y,
379                          MI_WIDTH (mi), MI_HEIGHT (mi));
380       return True;
381     }
382   else if (event->xany.type == ButtonRelease &&
383            event->xbutton.button == Button1)
384     {
385       bp->button_down_p = False;
386       return True;
387     }
388   else if (event->xany.type == ButtonPress &&
389            (event->xbutton.button == Button4 ||
390             event->xbutton.button == Button5 ||
391             event->xbutton.button == Button6 ||
392             event->xbutton.button == Button7))
393     {
394       gltrackball_mousewheel (bp->trackball, event->xbutton.button, 3,
395                               !event->xbutton.state);
396       return True;
397     }
398   else if (event->xany.type == MotionNotify &&
399            bp->button_down_p)
400     {
401       gltrackball_track (bp->trackball,
402                          event->xmotion.x, event->xmotion.y,
403                          MI_WIDTH (mi), MI_HEIGHT (mi));
404       return True;
405     }
406
407   return False;
408 }
409
410
411 ENTRYPOINT void
412 init_cube (ModeInfo *mi)
413 {
414   cube_configuration *bp;
415   int wire = MI_IS_WIREFRAME(mi);
416   int i;
417
418   if (!bps) {
419     bps = (cube_configuration *)
420       calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration));
421     if (!bps) {
422       fprintf(stderr, "%s: out of memory\n", progname);
423       exit(1);
424     }
425   }
426
427   bp = &bps[MI_SCREEN(mi)];
428
429   bp->glx_context = init_GL(mi);
430
431   reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
432
433   glShadeModel(GL_SMOOTH);
434
435   glEnable(GL_DEPTH_TEST);
436   glEnable(GL_NORMALIZE);
437   glEnable(GL_CULL_FACE);
438
439   if (!wire)
440     {
441       GLfloat pos[4] = {0.7, 0.2, 0.4, 0.0};
442 /*      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
443       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
444       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
445       GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
446
447       glEnable(GL_LIGHTING);
448       glEnable(GL_LIGHT0);
449       glEnable(GL_DEPTH_TEST);
450       glEnable(GL_CULL_FACE);
451
452       glLightfv(GL_LIGHT0, GL_POSITION, pos);
453       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
454       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
455       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
456     }
457
458   bp->trackball = gltrackball_init ();
459
460   bp->dlists = (GLuint *) calloc (countof(all_objs)+2, sizeof(GLuint));
461   for (i = 0; i < countof(all_objs)+1; i++)
462     bp->dlists[i] = glGenLists (1);
463
464   for (i = 0; i < countof(all_objs); i++)
465     {
466       const struct gllist *gll = *all_objs[i];
467       glNewList (bp->dlists[i], GL_COMPILE);
468       renderList (gll, wire);
469       glEndList ();
470     }
471
472   glNewList (bp->dlists[i], GL_COMPILE);
473   bp->cube_polys = build_cube (mi);
474   glEndList ();
475
476
477   bp->nfloaters = MI_COUNT (mi);
478   bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
479
480   for (i = 0; i < bp->nfloaters; i++)
481     {
482       floater *f = &bp->floaters[i];
483       double spin_speed   = do_spin ? 0.7 : 10;
484       double wander_speed = do_wander ? 0.02 : 0.05 * speed * SPEED_SCALE;
485       double spin_accel   = 0.5;
486       f->rot = make_rotator (spin_speed, spin_speed, spin_speed,
487                              spin_accel,
488                              wander_speed,
489                              True);
490       if (bp->nfloaters == 2)
491         {
492           f->x = (i ? 2 : -2);
493         }
494       else if (i != 0)
495         {
496           double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
497           double r = 3;
498           f->x = r * cos(th);
499           f->z = r * sin(th);
500         }
501
502       f->ix = f->x;
503       f->iy = f->y;
504       f->iz = f->z;
505       reset_floater (mi, f);
506     }
507 }
508
509
510 static void
511 draw_floater (ModeInfo *mi, floater *f)
512 {
513   cube_configuration *bp = &bps[MI_SCREEN(mi)];
514   GLfloat n;
515   double x, y, z;
516
517   get_position (f->rot, &x, &y, &z, !bp->button_down_p);
518
519   glPushMatrix();
520   glTranslatef (f->x, f->y, f->z);
521
522   if (do_wander)
523     glTranslatef (x, y, z);
524
525   if (do_spin)
526     get_rotation (f->rot, &x, &y, &z, !bp->button_down_p);
527
528   if (do_spin || f->spinner_p)
529     {
530       glRotatef (x * 360, 1, 0, 0);
531       glRotatef (y * 360, 0, 1, 0);
532       glRotatef (z * 360, 0, 0, 1);
533     }
534   else
535     {
536       glRotatef (f->zr * 360, 0, 1, 0);
537     }
538
539   n = 1.5;
540   if      (bp->nfloaters > 99) n *= 0.05;
541   else if (bp->nfloaters > 25) n *= 0.18;
542   else if (bp->nfloaters > 9)  n *= 0.3;
543   else if (bp->nfloaters > 1)  n *= 0.7;
544
545   n *= 2;
546
547   if ((do_spin || do_wander) && bp->nfloaters > 1)
548     n *= 0.7;
549
550   glScalef(n, n, n);
551
552   glCallList (bp->dlists[FULL_CUBE]);
553   mi->polygon_count += bp->cube_polys;
554 /*  build_cube (mi);*/
555
556   glPopMatrix();
557 }
558
559
560
561 ENTRYPOINT void
562 draw_cube (ModeInfo *mi)
563 {
564   cube_configuration *bp = &bps[MI_SCREEN(mi)];
565   Display *dpy = MI_DISPLAY(mi);
566   Window window = MI_WINDOW(mi);
567   int i;
568
569   if (!bp->glx_context)
570     return;
571
572   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
573
574   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
575
576   glPushMatrix ();
577   glRotatef(current_device_rotation(), 0, 0, 1);
578   gltrackball_rotate (bp->trackball);
579
580   glScalef (2, 2, 2);
581
582   mi->polygon_count = 0;
583
584 # if 0
585   {
586     floater F;
587     F.x = F.y = F.z = 0;
588     F.dx = F.dy = F.dz = 0;
589     F.ddx = F.ddy = F.ddz = 0;
590     F.rot = make_rotator (0, 0, 0, 1, 0, False);
591     glRotatef (45, 0, 1, 0);
592     draw_floater (mi, &F);
593   }
594 # else
595   for (i = 0; i < bp->nfloaters; i++)
596     {
597       floater *f = &bp->floaters[i];
598       draw_floater (mi, f);
599       tick_floater (mi, f);
600     }
601 # endif
602
603   glPopMatrix ();
604
605   if (mi->fps_p) do_fps (mi);
606   glFinish();
607
608   glXSwapBuffers(dpy, window);
609 }
610
611 XSCREENSAVER_MODULE_2 ("CompanionCube", companioncube, cube)
612
613 #endif /* USE_GL */