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