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