1 /* bouncingcow, Copyright (c) 2003, 2004 Jamie Zawinski <jwz@jwz.org>
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
11 * Boing, boing, boing. Cow, cow, cow.
14 #include <X11/Intrinsic.h>
16 extern XtAppContext app;
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
26 #define DEF_SPEED "0.7"
27 #define DEF_TEXTURE "(none)"
29 #define DEFAULTS "*delay: 30000 \n" \
31 "*showFPS: False \n" \
32 "*wireframe: False \n" \
33 "*speed: " DEF_SPEED " \n" \
34 "*texture: " DEF_TEXTURE "\n" \
39 #define countof(x) (sizeof((x))/sizeof((*x)))
42 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
44 #define RANDSIGN() ((random() & 1) ? 1 : -1)
46 #include "xlockmore.h"
48 #include "gltrackball.h"
49 #include "xpm-ximage.h"
52 #ifdef USE_GL /* whole file */
60 *cow_face, *cow_hide, *cow_hoofs, *cow_horns, *cow_tail, *cow_udder;
62 struct gllist **all_objs[] = {
63 &cow_face, &cow_hide, &cow_hoofs, &cow_horns, &cow_tail, &cow_udder
77 GLfloat ddx, ddy, ddz;
83 GLXContext *glx_context;
84 trackball_state *trackball;
95 static cow_configuration *bps = NULL;
98 static const char *do_texture;
100 static XrmOptionDescRec opts[] = {
101 { "-speed", ".speed", XrmoptionSepArg, 0 },
102 {"-texture", ".texture", XrmoptionSepArg, 0 },
103 {"+texture", ".texture", XrmoptionNoArg, "(none)" },
106 static argtype vars[] = {
107 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
108 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_String},
111 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
117 reset_floater (ModeInfo *mi, floater *f)
119 cow_configuration *bp = &bps[MI_SCREEN(mi)];
125 /* Yes, I know I'm varying the force of gravity instead of varying the
126 launch velocity. That's intentional: empirical studies indicate
127 that it's way, way funnier that way. */
133 /* -0.18 max -0.3 top -0.4 middle -0.6 bottom */
134 f->ddy = speed * (-0.6 + BELLRAND(0.45));
138 f->spinner_p = !(random() % (12 * bp->nfloaters));
140 if (! (random() % (30 * bp->nfloaters)))
142 f->dx = BELLRAND(1.8) * RANDSIGN();
143 f->dz = BELLRAND(1.8) * RANDSIGN();
149 tick_floater (ModeInfo *mi, floater *f)
151 cow_configuration *bp = &bps[MI_SCREEN(mi)];
153 if (bp->button_down_p) return;
159 f->x += f->dx * speed;
160 f->y += f->dy * speed;
161 f->z += f->dz * speed;
163 if (f->y < -BOTTOM ||
164 f->x < -BOTTOM*8 || f->x > BOTTOM*8 ||
165 f->z < -BOTTOM*8 || f->z > BOTTOM*8)
166 reset_floater (mi, f);
170 /* Window management, etc
173 reshape_cow (ModeInfo *mi, int width, int height)
175 GLfloat h = (GLfloat) height / (GLfloat) width;
177 glViewport (0, 0, (GLint) width, (GLint) height);
179 glMatrixMode(GL_PROJECTION);
181 gluPerspective (30.0, 1/h, 1.0, 100);
183 glMatrixMode(GL_MODELVIEW);
185 gluLookAt( 0.0, 0.0, 30.0,
189 glClear(GL_COLOR_BUFFER_BIT);
194 cow_handle_event (ModeInfo *mi, XEvent *event)
196 cow_configuration *bp = &bps[MI_SCREEN(mi)];
198 if (event->xany.type == ButtonPress &&
199 event->xbutton.button == Button1)
201 bp->button_down_p = True;
202 gltrackball_start (bp->trackball,
203 event->xbutton.x, event->xbutton.y,
204 MI_WIDTH (mi), MI_HEIGHT (mi));
207 else if (event->xany.type == ButtonRelease &&
208 event->xbutton.button == Button1)
210 bp->button_down_p = False;
213 else if (event->xany.type == ButtonPress &&
214 (event->xbutton.button == Button4 ||
215 event->xbutton.button == Button5))
217 gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
218 !event->xbutton.state);
221 else if (event->xany.type == MotionNotify &&
224 gltrackball_track (bp->trackball,
225 event->xmotion.x, event->xmotion.y,
226 MI_WIDTH (mi), MI_HEIGHT (mi));
238 load_texture (ModeInfo *mi, const char *filename)
240 Display *dpy = mi->dpy;
241 Visual *visual = mi->xgwa.visual;
242 Colormap cmap = mi->xgwa.colormap;
246 if (MI_IS_WIREFRAME(mi))
251 !strcasecmp (filename, "(none)"))
253 glDisable (GL_TEXTURE_2D);
257 image = xpm_file_to_ximage (dpy, visual, cmap, filename);
260 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
261 image->width, image->height, 0,
263 /* GL_UNSIGNED_BYTE, */
264 GL_UNSIGNED_INT_8_8_8_8_REV,
266 sprintf (buf, "texture: %.100s (%dx%d)",
267 filename, image->width, image->height);
270 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
271 glPixelStorei (GL_UNPACK_ROW_LENGTH, image->width);
272 glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
273 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
274 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
275 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
276 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
278 glEnable(GL_TEXTURE_GEN_S);
279 glEnable(GL_TEXTURE_GEN_T);
280 glEnable(GL_TEXTURE_2D);
286 init_cow (ModeInfo *mi)
288 cow_configuration *bp;
289 int wire = MI_IS_WIREFRAME(mi);
293 bps = (cow_configuration *)
294 calloc (MI_NUM_SCREENS(mi), sizeof (cow_configuration));
296 fprintf(stderr, "%s: out of memory\n", progname);
300 bp = &bps[MI_SCREEN(mi)];
303 bp = &bps[MI_SCREEN(mi)];
305 bp->glx_context = init_GL(mi);
307 reshape_cow (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
309 glShadeModel(GL_SMOOTH);
311 glEnable(GL_DEPTH_TEST);
312 glEnable(GL_NORMALIZE);
313 glEnable(GL_CULL_FACE);
317 GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
318 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
319 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
320 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
321 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
323 glEnable(GL_LIGHTING);
325 glEnable(GL_DEPTH_TEST);
326 glEnable(GL_CULL_FACE);
328 glLightfv(GL_LIGHT0, GL_POSITION, pos);
329 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
330 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
331 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
334 bp->trackball = gltrackball_init ();
336 bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
337 for (i = 0; i < countof(all_objs); i++)
338 bp->dlists[i] = glGenLists (1);
340 for (i = 0; i < countof(all_objs); i++)
342 GLfloat black[4] = {0, 0, 0, 1};
343 struct gllist *gll = *all_objs[i];
345 gll->primitive = GL_LINE_LOOP;
347 glNewList (bp->dlists[i], GL_COMPILE);
349 glMatrixMode(GL_MODELVIEW);
351 glMatrixMode(GL_TEXTURE);
353 glMatrixMode(GL_MODELVIEW);
355 glBindTexture (GL_TEXTURE_2D, 0);
359 GLfloat color[4] = {0.63, 0.43, 0.36, 1.00};
361 if (load_texture (mi, do_texture))
363 glBindTexture (GL_TEXTURE_2D, bp->texture);
364 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
365 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
366 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
368 /* approximately line it up with ../images/earth.xpm */
369 glMatrixMode(GL_TEXTURE);
370 glTranslatef(0.45, 0.58, 0);
371 glScalef(0.08, 0.16, 1);
372 glRotatef(-5, 0, 0, 1);
373 glMatrixMode(GL_MODELVIEW);
375 /* if we have a texture, make the base color be white. */
376 color[0] = color[1] = color[2] = 1.0;
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);
385 GLfloat color[4] = {0.63, 0.43, 0.36, 1.00};
386 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
387 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, black);
388 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 128);
392 GLfloat color[4] = {1.00, 0.53, 0.53, 1.00};
393 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
394 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, black);
395 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 128);
397 else if (i == HOOFS || i == HORNS)
399 GLfloat color[4] = {0.20, 0.20, 0.20, 1.00};
400 GLfloat spec[4] = {0.30, 0.30, 0.30, 1.00};
402 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
403 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
404 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
408 GLfloat color[4] = {0.10, 0.10, 0.10, 1.00};
409 GLfloat spec[4] = {0.10, 0.10, 0.10, 1.00};
411 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
412 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
413 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
417 GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
418 GLfloat spec[4] = {1.00, 1.00, 1.00, 1.00};
419 GLfloat shiny = 128.0;
420 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
421 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
422 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
427 glMatrixMode(GL_TEXTURE);
429 glMatrixMode(GL_MODELVIEW);
435 bp->nfloaters = MI_COUNT (mi);
436 bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
438 for (i = 0; i < bp->nfloaters; i++)
440 floater *f = &bp->floaters[i];
441 f->rot = make_rotator (10.0, 0, 0,
444 if (bp->nfloaters == 2)
450 double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
459 reset_floater (mi, f);
465 draw_floater (ModeInfo *mi, floater *f)
467 cow_configuration *bp = &bps[MI_SCREEN(mi)];
471 get_position (f->rot, &x, &y, &z, !bp->button_down_p);
474 glTranslatef (f->x, f->y, f->z);
476 glRotatef (y * 360, 0.0, 1.0, 0.0);
479 glRotatef (x * 360, 1.0, 0.0, 0.0);
480 glRotatef (z * 360, 0.0, 0.0, 1.0);
484 if (bp->nfloaters > 99) n *= 0.05;
485 else if (bp->nfloaters > 25) n *= 0.18;
486 else if (bp->nfloaters > 9) n *= 0.3;
487 else if (bp->nfloaters > 1) n *= 0.7;
490 glCallList (bp->dlists[FACE]);
491 mi->polygon_count += (*all_objs[FACE])->points / 3;
493 glCallList (bp->dlists[HIDE]);
494 mi->polygon_count += (*all_objs[HIDE])->points / 3;
496 glCallList (bp->dlists[HOOFS]);
497 mi->polygon_count += (*all_objs[HOOFS])->points / 3;
499 glCallList (bp->dlists[HORNS]);
500 mi->polygon_count += (*all_objs[HORNS])->points / 3;
502 glCallList (bp->dlists[TAIL]);
503 mi->polygon_count += (*all_objs[TAIL])->points / 3;
505 glCallList (bp->dlists[UDDER]);
506 mi->polygon_count += (*all_objs[UDDER])->points / 3;
514 draw_cows (ModeInfo *mi)
516 cow_configuration *bp = &bps[MI_SCREEN(mi)];
517 Display *dpy = MI_DISPLAY(mi);
518 Window window = MI_WINDOW(mi);
521 if (!bp->glx_context)
524 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
527 gltrackball_rotate (bp->trackball);
529 glScalef (0.5, 0.5, 0.5);
531 mi->polygon_count = 0;
537 F.dx = F.dy = F.dz = 0;
538 F.ddx = F.ddy = F.ddz = 0;
539 F.rot = make_rotator (0, 0, 0, 1, 0, False);
541 draw_floater (mi, &F);
544 for (i = 0; i < bp->nfloaters; i++)
546 /* "Don't kid yourself, Jimmy. If a cow ever got the chance,
547 he'd eat you and everyone you care about!"
548 -- Troy McClure in "Meat and You: Partners in Freedom"
550 floater *f = &bp->floaters[i];
551 draw_floater (mi, f);
552 tick_floater (mi, f);
558 if (mi->fps_p) do_fps (mi);
561 glXSwapBuffers(dpy, window);