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