1 /* bouncingcow, Copyright (c) 2003-2018 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 #define DEFAULTS "*delay: 30000 \n" \
16 "*showFPS: False \n" \
17 "*wireframe: False \n" \
20 # define release_cow 0
21 #define DEF_SPEED "1.0"
22 #define DEF_TEXTURE "(none)"
25 #define countof(x) (sizeof((x))/sizeof((*x)))
28 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
30 #define RANDSIGN() ((random() & 1) ? 1 : -1)
32 #include "xlockmore.h"
34 #include "gltrackball.h"
35 #include "ximage-loader.h"
38 #ifdef USE_GL /* whole file */
43 *cow_face, *cow_hide, *cow_hoofs, *cow_horns, *cow_tail, *cow_udder;
45 static struct gllist **all_objs[] = {
46 &cow_face, &cow_hide, &cow_hoofs, &cow_horns, &cow_tail, &cow_udder
60 GLfloat ddx, ddy, ddz;
66 GLXContext *glx_context;
67 trackball_state *trackball;
78 static cow_configuration *bps = NULL;
81 static const char *do_texture;
83 static XrmOptionDescRec opts[] = {
84 { "-speed", ".speed", XrmoptionSepArg, 0 },
85 {"-texture", ".texture", XrmoptionSepArg, 0 },
86 {"+texture", ".texture", XrmoptionNoArg, "(none)" },
89 static argtype vars[] = {
90 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
91 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_String},
94 ENTRYPOINT ModeSpecOpt cow_opts = {countof(opts), opts, countof(vars), vars, NULL};
100 reset_floater (ModeInfo *mi, floater *f)
102 cow_configuration *bp = &bps[MI_SCREEN(mi)];
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. */
116 /* -0.18 max -0.3 top -0.4 middle -0.6 bottom */
117 f->ddy = speed * (-0.6 + BELLRAND(0.45));
121 f->spinner_p = !(random() % (12 * bp->nfloaters));
123 if (! (random() % (30 * bp->nfloaters)))
125 f->dx = BELLRAND(1.8) * RANDSIGN();
126 f->dz = BELLRAND(1.8) * RANDSIGN();
132 tick_floater (ModeInfo *mi, floater *f)
134 cow_configuration *bp = &bps[MI_SCREEN(mi)];
136 if (bp->button_down_p) return;
142 f->x += f->dx * speed;
143 f->y += f->dy * speed;
144 f->z += f->dz * speed;
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);
153 /* Window management, etc
156 reshape_cow (ModeInfo *mi, int width, int height)
158 GLfloat h = (GLfloat) height / (GLfloat) width;
161 if (width > height * 5) { /* tiny window: show middle */
162 height = width * 9/16;
164 h = height / (GLfloat) width;
167 glViewport (0, y, (GLint) width, (GLint) height);
169 glMatrixMode(GL_PROJECTION);
171 gluPerspective (30.0, 1/h, 1.0, 100);
173 glMatrixMode(GL_MODELVIEW);
175 gluLookAt( 0.0, 0.0, 30.0,
179 glClear(GL_COLOR_BUFFER_BIT);
184 cow_handle_event (ModeInfo *mi, XEvent *event)
186 cow_configuration *bp = &bps[MI_SCREEN(mi)];
188 if (gltrackball_event_handler (event, bp->trackball,
189 MI_WIDTH (mi), MI_HEIGHT (mi),
201 load_texture (ModeInfo *mi, const char *filename)
203 Display *dpy = mi->dpy;
204 Visual *visual = mi->xgwa.visual;
208 if (MI_IS_WIREFRAME(mi))
213 !strcasecmp (filename, "(none)"))
215 glDisable (GL_TEXTURE_2D);
219 image = file_to_ximage (dpy, visual, filename);
220 if (!image) return False;
223 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
224 image->width, image->height, 0,
225 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
226 sprintf (buf, "texture: %.100s (%dx%d)",
227 filename, image->width, image->height);
230 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
231 glPixelStorei (GL_UNPACK_ROW_LENGTH, image->width);
238 init_cow (ModeInfo *mi)
240 cow_configuration *bp;
241 int wire = MI_IS_WIREFRAME(mi);
247 bp = &bps[MI_SCREEN(mi)];
249 bp->glx_context = init_GL(mi);
251 reshape_cow (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
253 glShadeModel(GL_SMOOTH);
255 glEnable(GL_DEPTH_TEST);
256 glEnable(GL_NORMALIZE);
257 glEnable(GL_CULL_FACE);
261 GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
262 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
263 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
264 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
265 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
267 glEnable(GL_LIGHTING);
269 glEnable(GL_DEPTH_TEST);
270 glEnable(GL_CULL_FACE);
272 glLightfv(GL_LIGHT0, GL_POSITION, pos);
273 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
274 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
275 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
278 bp->trackball = gltrackball_init (False);
280 bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
281 for (i = 0; i < countof(all_objs); i++)
282 bp->dlists[i] = glGenLists (1);
284 tex_p = load_texture (mi, do_texture);
286 glBindTexture (GL_TEXTURE_2D, bp->texture);
288 for (i = 0; i < countof(all_objs); i++)
290 GLfloat black[4] = {0, 0, 0, 1};
291 const struct gllist *gll = *all_objs[i];
293 glNewList (bp->dlists[i], GL_COMPILE);
295 glDisable (GL_TEXTURE_2D);
299 GLfloat color[4] = {0.63, 0.43, 0.36, 1.00};
302 /* if we have a texture, make the base color be white. */
303 color[0] = color[1] = color[2] = 1.0;
305 glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
306 glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
307 glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
308 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
309 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
310 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
311 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
312 glEnable(GL_TEXTURE_GEN_S);
313 glEnable(GL_TEXTURE_GEN_T);
314 glEnable(GL_TEXTURE_2D);
316 /* approximately line it up with ../images/earth.png */
317 glMatrixMode (GL_TEXTURE);
319 glTranslatef (0.45, 0.58, 0);
320 glScalef (0.08, 0.16, 1);
321 glRotatef (-5, 0, 0, 1);
322 glMatrixMode (GL_MODELVIEW);
324 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
325 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, black);
326 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 128);
330 GLfloat color[4] = {0.63, 0.43, 0.36, 1.00};
331 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
332 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, black);
333 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 128);
337 GLfloat color[4] = {1.00, 0.53, 0.53, 1.00};
338 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
339 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, black);
340 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 128);
342 else if (i == HOOFS || i == HORNS)
344 GLfloat color[4] = {0.20, 0.20, 0.20, 1.00};
345 GLfloat spec[4] = {0.30, 0.30, 0.30, 1.00};
347 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
348 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
349 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
353 GLfloat color[4] = {0.10, 0.10, 0.10, 1.00};
354 GLfloat spec[4] = {0.10, 0.10, 0.10, 1.00};
356 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
357 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
358 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
362 GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
363 GLfloat spec[4] = {1.00, 1.00, 1.00, 1.00};
364 GLfloat shiny = 128.0;
365 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
366 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
367 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
370 renderList (gll, wire);
375 bp->nfloaters = MI_COUNT (mi);
376 bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
378 for (i = 0; i < bp->nfloaters; i++)
380 floater *f = &bp->floaters[i];
381 f->rot = make_rotator (10.0, 0, 0,
384 if (bp->nfloaters == 2)
390 double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
399 reset_floater (mi, f);
405 draw_floater (ModeInfo *mi, floater *f)
407 cow_configuration *bp = &bps[MI_SCREEN(mi)];
411 get_position (f->rot, &x, &y, &z, !bp->button_down_p);
414 glTranslatef (f->x, f->y, f->z);
416 gltrackball_rotate (bp->trackball);
418 glRotatef (y * 360, 0.0, 1.0, 0.0);
421 glRotatef (x * 360, 1.0, 0.0, 0.0);
422 glRotatef (z * 360, 0.0, 0.0, 1.0);
426 if (bp->nfloaters > 99) n *= 0.05;
427 else if (bp->nfloaters > 25) n *= 0.18;
428 else if (bp->nfloaters > 9) n *= 0.3;
429 else if (bp->nfloaters > 1) n *= 0.7;
432 glCallList (bp->dlists[FACE]);
433 mi->polygon_count += (*all_objs[FACE])->points / 3;
435 glCallList (bp->dlists[HIDE]);
436 mi->polygon_count += (*all_objs[HIDE])->points / 3;
438 glCallList (bp->dlists[HOOFS]);
439 mi->polygon_count += (*all_objs[HOOFS])->points / 3;
441 glCallList (bp->dlists[HORNS]);
442 mi->polygon_count += (*all_objs[HORNS])->points / 3;
444 glCallList (bp->dlists[TAIL]);
445 mi->polygon_count += (*all_objs[TAIL])->points / 3;
447 glCallList (bp->dlists[UDDER]);
448 mi->polygon_count += (*all_objs[UDDER])->points / 3;
456 draw_cow (ModeInfo *mi)
458 cow_configuration *bp = &bps[MI_SCREEN(mi)];
459 Display *dpy = MI_DISPLAY(mi);
460 Window window = MI_WINDOW(mi);
463 if (!bp->glx_context)
466 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
468 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
472 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
474 GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
475 int o = (int) current_device_rotation();
476 if (o != 0 && o != 180 && o != -180)
477 glScalef (1/h, 1/h, 1/h);
478 glRotatef(o, 0, 0, 1);
482 glScalef (0.5, 0.5, 0.5);
484 mi->polygon_count = 0;
490 F.dx = F.dy = F.dz = 0;
491 F.ddx = F.ddy = F.ddz = 0;
492 F.rot = make_rotator (0, 0, 0, 1, 0, False);
494 draw_floater (mi, &F);
497 for (i = 0; i < bp->nfloaters; i++)
499 /* "Don't kid yourself, Jimmy. If a cow ever got the chance,
500 he'd eat you and everyone you care about!"
501 -- Troy McClure in "Meat and You: Partners in Freedom"
503 floater *f = &bp->floaters[i];
504 draw_floater (mi, f);
505 tick_floater (mi, f);
511 if (mi->fps_p) do_fps (mi);
514 glXSwapBuffers(dpy, window);
517 XSCREENSAVER_MODULE_2 ("BouncingCow", bouncingcow, cow)