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