From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / companion.c
1 /* companioncube, Copyright (c) 2011-2014 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 (gltrackball_event_handler (event, bp->trackball,
374                                  MI_WIDTH (mi), MI_HEIGHT (mi),
375                                  &bp->button_down_p))
376     return True;
377
378   return False;
379 }
380
381
382 ENTRYPOINT void
383 init_cube (ModeInfo *mi)
384 {
385   cube_configuration *bp;
386   int wire = MI_IS_WIREFRAME(mi);
387   int i;
388
389   if (!bps) {
390     bps = (cube_configuration *)
391       calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration));
392     if (!bps) {
393       fprintf(stderr, "%s: out of memory\n", progname);
394       exit(1);
395     }
396   }
397
398   bp = &bps[MI_SCREEN(mi)];
399
400   bp->glx_context = init_GL(mi);
401
402   reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
403
404   glShadeModel(GL_SMOOTH);
405
406   glEnable(GL_DEPTH_TEST);
407   glEnable(GL_NORMALIZE);
408   glEnable(GL_CULL_FACE);
409
410   if (!wire)
411     {
412       GLfloat pos[4] = {0.7, 0.2, 0.4, 0.0};
413 /*      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
414       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
415       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
416       GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
417
418       glEnable(GL_LIGHTING);
419       glEnable(GL_LIGHT0);
420       glEnable(GL_DEPTH_TEST);
421       glEnable(GL_CULL_FACE);
422
423       glLightfv(GL_LIGHT0, GL_POSITION, pos);
424       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
425       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
426       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
427     }
428
429   bp->trackball = gltrackball_init (False);
430
431   bp->dlists = (GLuint *) calloc (countof(all_objs)+2, sizeof(GLuint));
432   for (i = 0; i < countof(all_objs)+1; i++)
433     bp->dlists[i] = glGenLists (1);
434
435   for (i = 0; i < countof(all_objs); i++)
436     {
437       const struct gllist *gll = *all_objs[i];
438       glNewList (bp->dlists[i], GL_COMPILE);
439       renderList (gll, wire);
440       glEndList ();
441     }
442
443   glNewList (bp->dlists[i], GL_COMPILE);
444   bp->cube_polys = build_cube (mi);
445   glEndList ();
446
447
448   bp->nfloaters = MI_COUNT (mi);
449   bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
450
451   for (i = 0; i < bp->nfloaters; i++)
452     {
453       floater *f = &bp->floaters[i];
454       double spin_speed   = do_spin ? 0.7 : 10;
455       double wander_speed = do_wander ? 0.02 : 0.05 * speed * SPEED_SCALE;
456       double spin_accel   = 0.5;
457       f->rot = make_rotator (spin_speed, spin_speed, spin_speed,
458                              spin_accel,
459                              wander_speed,
460                              True);
461       if (bp->nfloaters == 2)
462         {
463           f->x = (i ? 2 : -2);
464         }
465       else if (i != 0)
466         {
467           double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
468           double r = 3;
469           f->x = r * cos(th);
470           f->z = r * sin(th);
471         }
472
473       f->ix = f->x;
474       f->iy = f->y;
475       f->iz = f->z;
476       reset_floater (mi, f);
477     }
478 }
479
480
481 static void
482 draw_floater (ModeInfo *mi, floater *f)
483 {
484   cube_configuration *bp = &bps[MI_SCREEN(mi)];
485   GLfloat n;
486   double x, y, z;
487
488   get_position (f->rot, &x, &y, &z, !bp->button_down_p);
489
490   glPushMatrix();
491   glTranslatef (f->x, f->y, f->z);
492
493   if (do_wander)
494     glTranslatef (x, y, z);
495
496   if (do_spin)
497     get_rotation (f->rot, &x, &y, &z, !bp->button_down_p);
498
499   if (do_spin || f->spinner_p)
500     {
501       glRotatef (x * 360, 1, 0, 0);
502       glRotatef (y * 360, 0, 1, 0);
503       glRotatef (z * 360, 0, 0, 1);
504     }
505   else
506     {
507       glRotatef (f->zr * 360, 0, 1, 0);
508     }
509
510   n = 1.5;
511   if      (bp->nfloaters > 99) n *= 0.05;
512   else if (bp->nfloaters > 25) n *= 0.18;
513   else if (bp->nfloaters > 9)  n *= 0.3;
514   else if (bp->nfloaters > 1)  n *= 0.7;
515
516   n *= 2;
517
518   if ((do_spin || do_wander) && bp->nfloaters > 1)
519     n *= 0.7;
520
521   glScalef(n, n, n);
522
523   glCallList (bp->dlists[FULL_CUBE]);
524   mi->polygon_count += bp->cube_polys;
525 /*  build_cube (mi);*/
526
527   glPopMatrix();
528 }
529
530
531
532 ENTRYPOINT void
533 draw_cube (ModeInfo *mi)
534 {
535   cube_configuration *bp = &bps[MI_SCREEN(mi)];
536   Display *dpy = MI_DISPLAY(mi);
537   Window window = MI_WINDOW(mi);
538   int i;
539
540   if (!bp->glx_context)
541     return;
542
543   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
544
545   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
546
547   glPushMatrix ();
548   glRotatef(current_device_rotation(), 0, 0, 1);
549   gltrackball_rotate (bp->trackball);
550
551   glScalef (2, 2, 2);
552
553   mi->polygon_count = 0;
554
555 # if 0
556   {
557     floater F;
558     F.x = F.y = F.z = 0;
559     F.dx = F.dy = F.dz = 0;
560     F.ddx = F.ddy = F.ddz = 0;
561     F.rot = make_rotator (0, 0, 0, 1, 0, False);
562     glRotatef (45, 0, 1, 0);
563     draw_floater (mi, &F);
564   }
565 # else
566   for (i = 0; i < bp->nfloaters; i++)
567     {
568       floater *f = &bp->floaters[i];
569       draw_floater (mi, f);
570       tick_floater (mi, f);
571     }
572 # endif
573
574   glPopMatrix ();
575
576   if (mi->fps_p) do_fps (mi);
577   glFinish();
578
579   glXSwapBuffers(dpy, window);
580 }
581
582 XSCREENSAVER_MODULE_2 ("CompanionCube", companioncube, cube)
583
584 #endif /* USE_GL */