http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / hacks / glx / bouncingcow.c
1 /* bouncingcow, Copyright (c) 2003-2006 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  * Boing, boing, boing.  Cow, cow, cow.
12  */
13
14 #define DEFAULTS        "*delay:        30000       \n" \
15                         "*count:        1           \n" \
16                         "*showFPS:      False       \n" \
17                         "*wireframe:    False       \n" \
18
19 /* #define DEBUG */
20
21
22 # define refresh_cow 0
23 # define release_cow 0
24 #define DEF_SPEED       "1.0"
25 #define DEF_TEXTURE     "(none)"
26
27 #undef countof
28 #define countof(x) (sizeof((x))/sizeof((*x)))
29
30 #undef BELLRAND
31 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
32 #undef RANDSIGN
33 #define RANDSIGN() ((random() & 1) ? 1 : -1)
34
35 #include "xlockmore.h"
36 #include "rotator.h"
37 #include "gltrackball.h"
38 #include "xpm-ximage.h"
39 #include <ctype.h>
40
41 #ifdef USE_GL /* whole file */
42
43 #include "gllist.h"
44
45 extern struct gllist
46  *cow_face, *cow_hide, *cow_hoofs, *cow_horns, *cow_tail, *cow_udder;
47
48 static struct gllist **all_objs[] = {
49  &cow_face, &cow_hide, &cow_hoofs, &cow_horns, &cow_tail, &cow_udder
50 };
51
52 #define FACE    0
53 #define HIDE    1
54 #define HOOFS   2
55 #define HORNS   3
56 #define TAIL    4
57 #define UDDER   5
58
59 typedef struct {
60   GLfloat x, y, z;
61   GLfloat ix, iy, iz;
62   GLfloat dx, dy, dz;
63   GLfloat ddx, ddy, ddz;
64   rotator *rot;
65   Bool spinner_p;
66 } floater;
67
68 typedef struct {
69   GLXContext *glx_context;
70   trackball_state *trackball;
71   Bool button_down_p;
72
73   GLuint *dlists;
74   GLuint texture;
75
76   int nfloaters;
77   floater *floaters;
78
79 } cow_configuration;
80
81 static cow_configuration *bps = NULL;
82
83 static GLfloat speed;
84 static const char *do_texture;
85
86 static XrmOptionDescRec opts[] = {
87   { "-speed",      ".speed",     XrmoptionSepArg, 0 },
88   {"-texture",     ".texture",   XrmoptionSepArg, 0 },
89   {"+texture",     ".texture",   XrmoptionNoArg, "(none)" },
90 };
91
92 static argtype vars[] = {
93   {&speed,      "speed",      "Speed",   DEF_SPEED,     t_Float},
94   {&do_texture, "texture",    "Texture", DEF_TEXTURE,   t_String},
95 };
96
97 ENTRYPOINT ModeSpecOpt cow_opts = {countof(opts), opts, countof(vars), vars, NULL};
98
99
100 #define BOTTOM 28.0
101
102 static void
103 reset_floater (ModeInfo *mi, floater *f)
104 {
105   cow_configuration *bp = &bps[MI_SCREEN(mi)];
106
107   f->y = -BOTTOM;
108   f->x = f->ix;
109   f->z = f->iz;
110
111   /* Yes, I know I'm varying the force of gravity instead of varying the
112      launch velocity.  That's intentional: empirical studies indicate
113      that it's way, way funnier that way. */
114
115   f->dy = 5.0;
116   f->dx = 0;
117   f->dz = 0;
118
119   /* -0.18 max  -0.3 top -0.4 middle  -0.6 bottom */
120   f->ddy = speed * (-0.6 + BELLRAND(0.45));
121   f->ddx = 0;
122   f->ddz = 0;
123
124   f->spinner_p = !(random() % (12 * bp->nfloaters));
125
126   if (! (random() % (30 * bp->nfloaters)))
127     {
128       f->dx = BELLRAND(1.8) * RANDSIGN();
129       f->dz = BELLRAND(1.8) * RANDSIGN();
130     }
131 }
132
133
134 static void
135 tick_floater (ModeInfo *mi, floater *f)
136 {
137   cow_configuration *bp = &bps[MI_SCREEN(mi)];
138
139   if (bp->button_down_p) return;
140
141   f->dx += f->ddx;
142   f->dy += f->ddy;
143   f->dz += f->ddz;
144
145   f->x += f->dx * speed;
146   f->y += f->dy * speed;
147   f->z += f->dz * speed;
148
149   if (f->y < -BOTTOM ||
150       f->x < -BOTTOM*8 || f->x > BOTTOM*8 ||
151       f->z < -BOTTOM*8 || f->z > BOTTOM*8)
152     reset_floater (mi, f);
153 }
154
155
156 /* Window management, etc
157  */
158 ENTRYPOINT void
159 reshape_cow (ModeInfo *mi, int width, int height)
160 {
161   GLfloat h = (GLfloat) height / (GLfloat) width;
162
163   glViewport (0, 0, (GLint) width, (GLint) height);
164
165   glMatrixMode(GL_PROJECTION);
166   glLoadIdentity();
167   gluPerspective (30.0, 1/h, 1.0, 100);
168
169   glMatrixMode(GL_MODELVIEW);
170   glLoadIdentity();
171   gluLookAt( 0.0, 0.0, 30.0,
172              0.0, 0.0, 0.0,
173              0.0, 1.0, 0.0);
174
175   glClear(GL_COLOR_BUFFER_BIT);
176 }
177
178
179 ENTRYPOINT Bool
180 cow_handle_event (ModeInfo *mi, XEvent *event)
181 {
182   cow_configuration *bp = &bps[MI_SCREEN(mi)];
183
184   if (event->xany.type == ButtonPress &&
185       event->xbutton.button == Button1)
186     {
187       bp->button_down_p = True;
188       gltrackball_start (bp->trackball,
189                          event->xbutton.x, event->xbutton.y,
190                          MI_WIDTH (mi), MI_HEIGHT (mi));
191       return True;
192     }
193   else if (event->xany.type == ButtonRelease &&
194            event->xbutton.button == Button1)
195     {
196       bp->button_down_p = False;
197       return True;
198     }
199   else if (event->xany.type == ButtonPress &&
200            (event->xbutton.button == Button4 ||
201             event->xbutton.button == Button5 ||
202             event->xbutton.button == Button6 ||
203             event->xbutton.button == Button7))
204     {
205       gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
206                               !event->xbutton.state);
207       return True;
208     }
209   else if (event->xany.type == MotionNotify &&
210            bp->button_down_p)
211     {
212       gltrackball_track (bp->trackball,
213                          event->xmotion.x, event->xmotion.y,
214                          MI_WIDTH (mi), MI_HEIGHT (mi));
215       return True;
216     }
217
218   return False;
219 }
220
221
222 /* Textures
223  */
224
225 static Bool
226 load_texture (ModeInfo *mi, const char *filename)
227 {
228   Display *dpy = mi->dpy;
229   Visual *visual = mi->xgwa.visual;
230   Colormap cmap = mi->xgwa.colormap;
231   char buf[1024];
232   XImage *image;
233
234   if (MI_IS_WIREFRAME(mi))
235     return False;
236
237   if (!filename ||
238       !*filename ||
239       !strcasecmp (filename, "(none)"))
240     {
241       glDisable (GL_TEXTURE_2D);
242       return False;
243     }
244
245   image = xpm_file_to_ximage (dpy, visual, cmap, filename);
246
247   clear_gl_error();
248   glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
249                 image->width, image->height, 0,
250                 GL_RGBA,
251                 /* GL_UNSIGNED_BYTE, */
252                 GL_UNSIGNED_INT_8_8_8_8_REV,
253                 image->data);
254   sprintf (buf, "texture: %.100s (%dx%d)",
255            filename, image->width, image->height);
256   check_gl_error(buf);
257
258   glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
259   glPixelStorei (GL_UNPACK_ROW_LENGTH, image->width);
260   glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
261   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
262   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
263   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
264   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
265
266   glEnable(GL_TEXTURE_GEN_S);
267   glEnable(GL_TEXTURE_GEN_T);
268   glEnable(GL_TEXTURE_2D);
269   return True;
270 }
271
272
273 ENTRYPOINT void
274 init_cow (ModeInfo *mi)
275 {
276   cow_configuration *bp;
277   int wire = MI_IS_WIREFRAME(mi);
278   int i;
279
280   if (!bps) {
281     bps = (cow_configuration *)
282       calloc (MI_NUM_SCREENS(mi), sizeof (cow_configuration));
283     if (!bps) {
284       fprintf(stderr, "%s: out of memory\n", progname);
285       exit(1);
286     }
287
288     bp = &bps[MI_SCREEN(mi)];
289   }
290
291   bp = &bps[MI_SCREEN(mi)];
292
293   bp->glx_context = init_GL(mi);
294
295   reshape_cow (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
296
297   glShadeModel(GL_SMOOTH);
298
299   glEnable(GL_DEPTH_TEST);
300   glEnable(GL_NORMALIZE);
301   glEnable(GL_CULL_FACE);
302
303   if (!wire)
304     {
305       GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
306 /*      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
307       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
308       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
309       GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
310
311       glEnable(GL_LIGHTING);
312       glEnable(GL_LIGHT0);
313       glEnable(GL_DEPTH_TEST);
314       glEnable(GL_CULL_FACE);
315
316       glLightfv(GL_LIGHT0, GL_POSITION, pos);
317       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
318       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
319       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
320     }
321
322   bp->trackball = gltrackball_init ();
323
324   bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
325   for (i = 0; i < countof(all_objs); i++)
326     bp->dlists[i] = glGenLists (1);
327
328   for (i = 0; i < countof(all_objs); i++)
329     {
330       GLfloat black[4] = {0, 0, 0, 1};
331       const struct gllist *gll = *all_objs[i];
332
333       glNewList (bp->dlists[i], GL_COMPILE);
334
335       glMatrixMode(GL_MODELVIEW);
336       glPushMatrix();
337       glMatrixMode(GL_TEXTURE);
338       glPushMatrix();
339       glMatrixMode(GL_MODELVIEW);
340
341       glBindTexture (GL_TEXTURE_2D, 0);
342
343       if (i == HIDE)
344         {
345           GLfloat color[4] = {0.63, 0.43, 0.36, 1.00};
346
347           if (load_texture (mi, do_texture))
348             {
349               glBindTexture (GL_TEXTURE_2D, bp->texture);
350               glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
351               glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
352               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
353
354               /* approximately line it up with ../images/earth.xpm */
355               glMatrixMode(GL_TEXTURE);
356               glTranslatef(0.45, 0.58, 0);
357               glScalef(0.08, 0.16, 1);
358               glRotatef(-5, 0, 0, 1);
359               glMatrixMode(GL_MODELVIEW);
360
361               /* if we have a texture, make the base color be white. */
362               color[0] = color[1] = color[2] = 1.0;
363             }
364
365           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
366           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            black);
367           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           128);
368         }
369       else if (i == TAIL)
370         {
371           GLfloat color[4] = {0.63, 0.43, 0.36, 1.00};
372           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
373           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            black);
374           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           128);
375         }
376       else if (i == UDDER)
377         {
378           GLfloat color[4] = {1.00, 0.53, 0.53, 1.00};
379           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
380           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            black);
381           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           128);
382         }
383       else if (i == HOOFS || i == HORNS)
384         {
385           GLfloat color[4] = {0.20, 0.20, 0.20, 1.00};
386           GLfloat spec[4]  = {0.30, 0.30, 0.30, 1.00};
387           GLfloat shiny    = 8.0;
388           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
389           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
390           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
391         }
392       else if (i == FACE)
393         {
394           GLfloat color[4] = {0.10, 0.10, 0.10, 1.00};
395           GLfloat spec[4]  = {0.10, 0.10, 0.10, 1.00};
396           GLfloat shiny    = 8.0;
397           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
398           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
399           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
400         }
401       else
402         {
403           GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
404           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.00};
405           GLfloat shiny    = 128.0;
406           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
407           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
408           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
409         }
410
411       renderList (gll, wire);
412
413       glMatrixMode(GL_TEXTURE);
414       glPopMatrix();
415       glMatrixMode(GL_MODELVIEW);
416       glPopMatrix();
417
418       glEndList ();
419     }
420
421   bp->nfloaters = MI_COUNT (mi);
422   bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
423
424   for (i = 0; i < bp->nfloaters; i++)
425     {
426       floater *f = &bp->floaters[i];
427       f->rot = make_rotator (10.0, 0, 0,
428                              4, 0.05 * speed,
429                              True);
430       if (bp->nfloaters == 2)
431         {
432           f->x = (i ? 6 : -6);
433         }
434       else if (i != 0)
435         {
436           double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
437           double r = 10;
438           f->x = r * cos(th);
439           f->z = r * sin(th);
440         }
441
442       f->ix = f->x;
443       f->iy = f->y;
444       f->iz = f->z;
445       reset_floater (mi, f);
446     }
447 }
448
449
450 static void
451 draw_floater (ModeInfo *mi, floater *f)
452 {
453   cow_configuration *bp = &bps[MI_SCREEN(mi)];
454   GLfloat n;
455   double x, y, z;
456
457   get_position (f->rot, &x, &y, &z, !bp->button_down_p);
458
459   glPushMatrix();
460   glTranslatef (f->x, f->y, f->z);
461
462   glRotatef (y * 360, 0.0, 1.0, 0.0);
463   if (f->spinner_p)
464     {
465       glRotatef (x * 360, 1.0, 0.0, 0.0);
466       glRotatef (z * 360, 0.0, 0.0, 1.0);
467     }
468
469   n = 1.5;
470   if      (bp->nfloaters > 99) n *= 0.05;
471   else if (bp->nfloaters > 25) n *= 0.18;
472   else if (bp->nfloaters > 9)  n *= 0.3;
473   else if (bp->nfloaters > 1)  n *= 0.7;
474   glScalef(n, n, n);
475
476   glCallList (bp->dlists[FACE]);
477   mi->polygon_count += (*all_objs[FACE])->points / 3;
478
479   glCallList (bp->dlists[HIDE]);
480   mi->polygon_count += (*all_objs[HIDE])->points / 3;
481
482   glCallList (bp->dlists[HOOFS]);
483   mi->polygon_count += (*all_objs[HOOFS])->points / 3;
484
485   glCallList (bp->dlists[HORNS]);
486   mi->polygon_count += (*all_objs[HORNS])->points / 3;
487
488   glCallList (bp->dlists[TAIL]);
489   mi->polygon_count += (*all_objs[TAIL])->points / 3;
490
491   glCallList (bp->dlists[UDDER]);
492   mi->polygon_count += (*all_objs[UDDER])->points / 3;
493
494   glPopMatrix();
495 }
496
497
498
499 ENTRYPOINT void
500 draw_cow (ModeInfo *mi)
501 {
502   cow_configuration *bp = &bps[MI_SCREEN(mi)];
503   Display *dpy = MI_DISPLAY(mi);
504   Window window = MI_WINDOW(mi);
505   int i;
506
507   if (!bp->glx_context)
508     return;
509
510   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
511
512   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
513
514   glPushMatrix ();
515   gltrackball_rotate (bp->trackball);
516
517   glScalef (0.5, 0.5, 0.5);
518
519   mi->polygon_count = 0;
520
521 # if 0
522   {
523     floater F;
524     F.x = F.y = F.z = 0;
525     F.dx = F.dy = F.dz = 0;
526     F.ddx = F.ddy = F.ddz = 0;
527     F.rot = make_rotator (0, 0, 0, 1, 0, False);
528     glScalef(2,2,2);
529     draw_floater (mi, &F);
530   }
531 # else
532   for (i = 0; i < bp->nfloaters; i++)
533     {
534       /* "Don't kid yourself, Jimmy.  If a cow ever got the chance,
535          he'd eat you and everyone you care about!"
536              -- Troy McClure in "Meat and You: Partners in Freedom"
537        */
538       floater *f = &bp->floaters[i];
539       draw_floater (mi, f);
540       tick_floater (mi, f);
541     }
542 # endif
543
544   glPopMatrix ();
545
546   if (mi->fps_p) do_fps (mi);
547   glFinish();
548
549   glXSwapBuffers(dpy, window);
550 }
551
552 XSCREENSAVER_MODULE_2 ("BouncingCow", bouncingcow, cow)
553
554 #endif /* USE_GL */