1 /* flyingtoasters, Copyright (c) 2003-2006 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 * Draws 3D flying toasters, and toast. Inspired by the ancient
12 * Berkeley Systems / After Dark hack, but now updated to the wide
13 * wonderful workd of OpenGL and 3D!
15 * Code by jwz; object models by Baconmonkey.
17 * The original After Dark flying toasters, with the fluffy white wings,
18 * were a trademark of Berkeley Systems. Berkeley Systems ceased to exist
19 * some time in 1998, having been gobbled up by Sierra Online, who were
20 * subsequently gobbled up by Flipside and/or Vivendi (it's hard to tell
21 * exactly what happened when.)
23 * I doubt anyone even cares any more, but if they do, hopefully this homage,
24 * with the space-age 3D jet-plane toasters, will be considered different
25 * enough that whoever still owns the trademark to the fluffy-winged 2D
26 * bitmapped toasters won't get all huffy at us.
29 #define DEFAULTS "*delay: 30000 \n" \
30 "*showFPS: False \n" \
31 "*wireframe: False \n" \
35 # define refresh_toasters 0
36 # define release_toasters 0
38 #define countof(x) (sizeof((x))/sizeof((*x)))
40 #define DEF_SPEED "1.0"
41 #define DEF_NTOASTERS "20"
42 #define DEF_NSLICES "25"
43 #define DEF_TEXTURE "True"
46 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
48 #include "xlockmore.h"
49 #include "gltrackball.h"
50 #include "xpm-ximage.h"
53 #include "../images/chromesphere.xpm"
54 #include "../images/toast.xpm"
56 #ifdef USE_GL /* whole file */
60 extern const struct gllist
61 *toaster, *toaster_base, *toaster_handle, *toaster_handle2, *toaster_jet,
62 *toaster_knob, *toaster_slots, *toaster_wing, *toast, *toast2;
64 static const struct gllist * const *all_objs[] = {
65 &toaster, &toaster_base, &toaster_handle, &toaster_handle2, &toaster_jet,
66 &toaster_knob, &toaster_slots, &toaster_wing, &toast, &toast2
69 #define BASE_TOASTER 0
78 #define TOAST_BITTEN 9
81 #define GRID_DEPTH 500
84 static const struct { GLfloat x, y; } nice_views[] = {
87 { 12, 28 }, /* this is a list of viewer rotations that look nice. */
88 { 12, -28 }, /* every now and then we switch to a new one. */
89 {-10, -28 }, /* (but we only use the first two at start-up.) */
103 int toast_type; /* 0, 1 */
104 GLfloat handle_pos; /* 0.0 - 1.0 */
105 GLfloat knob_pos; /* degrees */
106 int loaded; /* 2 bits */
110 GLXContext *glx_context;
111 trackball_state *user_trackball;
114 int last_view, target_view;
115 GLfloat view_x, view_y;
116 int view_steps, view_tick;
117 Bool auto_tracking_p;
121 GLuint chrome_texture;
122 GLuint toast_texture;
127 } toaster_configuration;
129 static toaster_configuration *bps = NULL;
131 static GLfloat speed;
132 static int ntoasters;
134 static int do_texture;
136 static XrmOptionDescRec opts[] = {
137 { "-speed", ".speed", XrmoptionSepArg, 0 },
138 { "-ntoasters", ".ntoasters", XrmoptionSepArg, 0 },
139 { "-nslices", ".nslices", XrmoptionSepArg, 0 },
140 {"-texture", ".texture", XrmoptionNoArg, "True" },
141 {"+texture", ".texture", XrmoptionNoArg, "False" },
144 static argtype vars[] = {
145 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
146 {&ntoasters, "ntoasters", "Count", DEF_NTOASTERS, t_Int},
147 {&nslices, "nslices", "Count", DEF_NSLICES, t_Int},
148 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
151 ENTRYPOINT ModeSpecOpt toasters_opts = {countof(opts), opts, countof(vars), vars, NULL};
155 reset_floater (ModeInfo *mi, floater *f)
157 /* toaster_configuration *bp = &bps[MI_SCREEN(mi)]; */
159 GLfloat n = GRID_SIZE/2.0;
160 GLfloat n2 = GRID_DEPTH/2.0;
161 GLfloat delta = GRID_SIZE * speed / 200.0;
167 f->dz += BELLRAND(delta) - delta/3;
169 if (! (random() % 5)) {
170 f->dx += (BELLRAND(delta*2) - delta);
171 f->dy += (BELLRAND(delta*2) - delta);
174 if (! (random() % 40)) f->dz *= 10; /* occasional speedy one */
176 f->x = frand(n) - n/2;
177 f->y = frand(n) - n/2;
178 f->z = -n2 - frand(delta * 4);
183 f->knob_pos = frand(180) - 90;
184 f->handle_pos = ((random() & 1) ? 0.0 : 1.0);
186 if (f->handle_pos > 0.8 && (! (random() % 5)))
187 f->loaded = (random() & 3); /* let's toast! */
191 if (! (random() % 10))
192 f->toast_type = 1; /* toast_bitten */
198 tick_floater (ModeInfo *mi, floater *f)
200 toaster_configuration *bp = &bps[MI_SCREEN(mi)];
202 GLfloat n1 = GRID_DEPTH/2.0;
203 GLfloat n2 = GRID_SIZE*4;
205 if (bp->button_down_p) return;
211 if (! (random() % 50000)) /* sudden gust of gravity */
214 if (f->x < -n2 || f->x > n2 ||
215 f->y < -n2 || f->y > n2 ||
217 reset_floater (mi, f);
222 auto_track_init (ModeInfo *mi)
224 toaster_configuration *bp = &bps[MI_SCREEN(mi)];
225 bp->last_view = (random() % 2);
226 bp->target_view = bp->last_view + 2;
227 bp->view_x = nice_views[bp->last_view].x;
228 bp->view_y = nice_views[bp->last_view].y;
229 bp->view_steps = 100;
231 bp->auto_tracking_p = True;
236 auto_track (ModeInfo *mi)
238 toaster_configuration *bp = &bps[MI_SCREEN(mi)];
240 if (bp->button_down_p)
243 /* if we're not moving, maybe start moving. Otherwise, do nothing. */
244 if (! bp->auto_tracking_p)
246 if (++bp->track_tick < 200/speed) return;
248 if (! (random() % 5))
249 bp->auto_tracking_p = True;
256 GLfloat ox = nice_views[bp->last_view].x;
257 GLfloat oy = nice_views[bp->last_view].y;
258 GLfloat tx = nice_views[bp->target_view].x;
259 GLfloat ty = nice_views[bp->target_view].y;
261 /* move from A to B with sinusoidal deltas, so that it doesn't jerk
263 GLfloat th = sin ((M_PI / 2) * (double) bp->view_tick / bp->view_steps);
265 bp->view_x = (ox + ((tx - ox) * th));
266 bp->view_y = (oy + ((ty - oy) * th));
269 if (bp->view_tick >= bp->view_steps)
272 bp->view_steps = (350.0 / speed);
273 bp->last_view = bp->target_view;
274 bp->target_view = (random() % (countof(nice_views) - 2)) + 2;
275 bp->auto_tracking_p = False;
281 /* Window management, etc
284 reshape_toasters (ModeInfo *mi, int width, int height)
286 GLfloat h = (GLfloat) height / (GLfloat) width;
288 glViewport (0, 0, (GLint) width, (GLint) height);
290 glMatrixMode(GL_PROJECTION);
292 gluPerspective (40.0, 1/h, 1.0, 250);
294 glMatrixMode(GL_MODELVIEW);
296 gluLookAt( 0.0, 2.0, 30.0,
300 glClear(GL_COLOR_BUFFER_BIT);
305 toasters_handle_event (ModeInfo *mi, XEvent *event)
307 toaster_configuration *bp = &bps[MI_SCREEN(mi)];
309 if (event->xany.type == ButtonPress &&
310 event->xbutton.button == Button1)
312 bp->button_down_p = True;
313 gltrackball_start (bp->user_trackball,
314 event->xbutton.x, event->xbutton.y,
315 MI_WIDTH (mi), MI_HEIGHT (mi));
318 else if (event->xany.type == ButtonRelease &&
319 event->xbutton.button == Button1)
321 bp->button_down_p = False;
324 else if (event->xany.type == ButtonPress &&
325 (event->xbutton.button == Button4 ||
326 event->xbutton.button == Button5 ||
327 event->xbutton.button == Button6 ||
328 event->xbutton.button == Button7))
330 gltrackball_mousewheel (bp->user_trackball, event->xbutton.button, 5,
331 !event->xbutton.state);
334 else if (event->xany.type == MotionNotify &&
337 gltrackball_track (bp->user_trackball,
338 event->xmotion.x, event->xmotion.y,
339 MI_WIDTH (mi), MI_HEIGHT (mi));
348 load_textures (ModeInfo *mi)
350 toaster_configuration *bp = &bps[MI_SCREEN(mi)];
353 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
357 glGenTextures (1, &bp->chrome_texture);
358 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
359 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
360 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
361 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
362 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
363 xi->width, xi->height, 0,
365 /* GL_UNSIGNED_BYTE, */
366 GL_UNSIGNED_INT_8_8_8_8_REV,
368 check_gl_error("texture");
370 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
373 glGenTextures (1, &bp->toast_texture);
374 glBindTexture (GL_TEXTURE_2D, bp->toast_texture);
375 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
376 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
377 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
378 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
379 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
380 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
381 xi->width, xi->height, 0,
383 /* GL_UNSIGNED_BYTE, */
384 GL_UNSIGNED_INT_8_8_8_8_REV,
386 check_gl_error("texture");
388 glEnable(GL_TEXTURE_GEN_S);
389 glEnable(GL_TEXTURE_GEN_T);
390 glEnable(GL_TEXTURE_2D);
395 init_toasters (ModeInfo *mi)
397 toaster_configuration *bp;
398 int wire = MI_IS_WIREFRAME(mi);
402 bps = (toaster_configuration *)
403 calloc (MI_NUM_SCREENS(mi), sizeof (toaster_configuration));
405 fprintf(stderr, "%s: out of memory\n", progname);
410 bp = &bps[MI_SCREEN(mi)];
412 bp->glx_context = init_GL(mi);
414 reshape_toasters (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
416 glShadeModel(GL_SMOOTH);
418 glEnable(GL_DEPTH_TEST);
419 glEnable(GL_NORMALIZE);
420 glEnable(GL_CULL_FACE);
424 GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
425 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
426 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
427 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
428 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
430 glEnable(GL_LIGHTING);
432 glEnable(GL_DEPTH_TEST);
433 glEnable(GL_CULL_FACE);
435 glLightfv(GL_LIGHT0, GL_POSITION, pos);
436 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
437 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
438 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
441 if (!wire && do_texture)
444 bp->user_trackball = gltrackball_init ();
445 auto_track_init (mi);
447 bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
448 for (i = 0; i < countof(all_objs); i++)
449 bp->dlists[i] = glGenLists (1);
451 for (i = 0; i < countof(all_objs); i++)
453 const struct gllist *gll = *all_objs[i];
455 glNewList (bp->dlists[i], GL_COMPILE);
457 glMatrixMode(GL_MODELVIEW);
459 glMatrixMode(GL_TEXTURE);
461 glMatrixMode(GL_MODELVIEW);
463 glRotatef (-90, 1, 0, 0);
464 glRotatef (180, 0, 0, 1);
467 glBindTexture (GL_TEXTURE_2D, 0);
469 if (i == BASE_TOASTER)
471 GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
472 GLfloat spec[4] = {1.00, 1.00, 1.00, 1.0};
473 GLfloat shiny = 20.0;
474 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
475 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
476 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
478 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
479 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
480 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
482 else if (i == TOAST || i == TOAST_BITTEN)
484 GLfloat color[4] = {0.80, 0.80, 0.00, 1.0};
485 GLfloat spec[4] = {0.00, 0.00, 0.00, 1.0};
487 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
488 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
489 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
491 glBindTexture (GL_TEXTURE_2D, bp->toast_texture);
492 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
493 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
495 glMatrixMode(GL_TEXTURE);
496 glTranslatef(0.5, 0.5, 0);
497 glMatrixMode(GL_MODELVIEW);
499 else if (i == SLOTS || i == HANDLE_SLOT)
501 GLfloat color[4] = {0.30, 0.30, 0.40, 1.0};
502 GLfloat spec[4] = {0.40, 0.40, 0.70, 1.0};
503 GLfloat shiny = 128.0;
504 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
505 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
506 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
508 else if (i == HANDLE)
510 GLfloat color[4] = {0.80, 0.10, 0.10, 1.0};
511 GLfloat spec[4] = {1.00, 1.00, 1.00, 1.0};
512 GLfloat shiny = 20.0;
513 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
514 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
515 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
519 GLfloat color[4] = {0.80, 0.10, 0.10, 1.0};
520 GLfloat spec[4] = {0.00, 0.00, 0.00, 1.0};
522 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
523 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
524 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
526 else if (i == JET || i == JET_WING)
528 GLfloat color[4] = {0.70, 0.70, 0.70, 1.0};
529 GLfloat spec[4] = {1.00, 1.00, 1.00, 1.0};
530 GLfloat shiny = 20.0;
531 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
532 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
533 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
537 GLfloat color[4] = {0.50, 0.50, 0.50, 1.0};
538 GLfloat spec[4] = {1.00, 1.00, 1.00, 1.0};
539 GLfloat shiny = 20.0;
540 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
541 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
542 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
546 GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
547 GLfloat spec[4] = {1.00, 1.00, 1.00, 1.0};
548 GLfloat shiny = 128.0;
549 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
550 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
551 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
554 renderList (gll, wire);
556 glMatrixMode(GL_TEXTURE);
558 glMatrixMode(GL_MODELVIEW);
564 bp->nfloaters = ntoasters + nslices;
565 bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
567 for (i = 0; i < bp->nfloaters; i++)
569 floater *f = &bp->floaters[i];
570 /* arrange the list so that half the toasters are in front of bread,
571 and half are behind. */
572 f->toaster_p = ((i < ntoasters / 2) ||
573 (i >= (nslices + (ntoasters / 2))));
574 reset_floater (mi, f);
576 /* Position the first generation randomly, but make sure they aren't
577 on screen yet (until we rotate the view into position.)
580 GLfloat min = -GRID_DEPTH/2;
581 GLfloat max = GRID_DEPTH/3.5;
582 f->z = frand (max - min) + min;
589 draw_origin (ModeInfo *mi)
592 /* toaster_configuration *bp = &bps[MI_SCREEN(mi)];*/
594 if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
595 if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
600 glVertex3f(-1, 0, 0); glVertex3f(1, 0, 0);
601 glVertex3f(0, -1, 0); glVertex3f(0, 1, 0);
602 glVertex3f(0, 0, -1); glVertex3f(0, 0, 1);
606 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
607 if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
613 draw_grid (ModeInfo *mi)
616 /* toaster_configuration *bp = &bps[MI_SCREEN(mi)];*/
618 if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
619 if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
621 glBegin(GL_LINE_LOOP);
622 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
623 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, 0);
624 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, 0);
625 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
627 glBegin(GL_LINE_LOOP);
628 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
629 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
630 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
631 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
633 glBegin(GL_LINE_LOOP);
634 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
635 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
636 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
637 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
640 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
641 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
642 glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
643 glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
644 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
645 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
646 glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, GRID_DEPTH/2);
647 glVertex3f( GRID_SIZE/2, GRID_SIZE/2, GRID_DEPTH/2);
651 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
652 if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
658 draw_floater (ModeInfo *mi, floater *f)
660 toaster_configuration *bp = &bps[MI_SCREEN(mi)];
666 glTranslatef (f->x, f->y, f->z);
670 glRotatef (180, 0, 1, 0);
672 glCallList (bp->dlists[BASE_TOASTER]);
673 mi->polygon_count += (*all_objs[BASE_TOASTER])->points / 3;
677 glTranslatef(0, 1.01, 0);
678 n = 0.91; glScalef(n,n,n);
679 glCallList (bp->dlists[SLOTS]);
680 mi->polygon_count += (*all_objs[SLOTS])->points / 3;
684 glRotatef (180, 0, 1, 0);
685 glTranslatef(0, -0.4, -2.38);
686 n = 0.33; glScalef(n,n,n);
687 glCallList (bp->dlists[HANDLE_SLOT]);
688 mi->polygon_count += (*all_objs[HANDLE_SLOT])->points / 3;
692 glTranslatef(0, -1.1, 3);
693 n = 0.3; glScalef (n,n,n);
694 glTranslatef(0, f->handle_pos * 4.8, 0);
695 glCallList (bp->dlists[HANDLE]);
696 mi->polygon_count += (*all_objs[HANDLE])->points / 3;
700 glRotatef (180, 0, 1, 0);
701 glTranslatef(0, -1.1, -3); /* where the handle is */
702 glTranslatef (1, -0.4, 0); /* down and to the left */
703 n = 0.08; glScalef (n,n,n);
704 glRotatef (f->knob_pos, 0, 0, 1);
705 glCallList (bp->dlists[KNOB]);
706 mi->polygon_count += (*all_objs[KNOB])->points / 3;
710 glRotatef (180, 0, 1, 0);
711 glTranslatef (0, -2.3, 0);
712 glCallList (bp->dlists[BASE]);
713 mi->polygon_count += (*all_objs[BASE])->points / 3;
717 glTranslatef(-4.8, 0, 0);
718 glCallList (bp->dlists[JET_WING]);
719 mi->polygon_count += (*all_objs[JET_WING])->points / 3;
720 glScalef (0.5, 0.5, 0.5);
721 glTranslatef (-2, -1, 0);
722 glCallList (bp->dlists[JET]);
723 mi->polygon_count += (*all_objs[JET])->points / 3;
727 glTranslatef(4.8, 0, 0);
730 glCallList (bp->dlists[JET_WING]);
731 mi->polygon_count += (*all_objs[JET_WING])->points / 3;
732 glScalef (0.5, 0.5, 0.5);
733 glTranslatef (-2, -1, 0);
734 glCallList (bp->dlists[JET]);
735 mi->polygon_count += (*all_objs[JET])->points / 3;
742 glTranslatef(0, 1.01, 0);
743 n = 0.91; glScalef(n,n,n);
744 glRotatef (90, 0, 0, 1);
745 glRotatef (90, 0, 1, 0);
746 glTranslatef(0, 0, -0.95);
747 glTranslatef(0, 0.72, 0);
750 glCallList (bp->dlists[TOAST]);
751 mi->polygon_count += (*all_objs[TOAST])->points / 3;
753 glTranslatef(0, -1.46, 0);
756 glCallList (bp->dlists[TOAST]);
757 mi->polygon_count += (*all_objs[TOAST])->points / 3;
764 glScalef (0.7, 0.7, 0.7);
765 if (f->toast_type == 0)
767 glCallList (bp->dlists[TOAST]);
768 mi->polygon_count += (*all_objs[TOAST])->points / 3;
772 glCallList (bp->dlists[TOAST_BITTEN]);
773 mi->polygon_count += (*all_objs[TOAST_BITTEN])->points / 3;
783 draw_toasters (ModeInfo *mi)
785 toaster_configuration *bp = &bps[MI_SCREEN(mi)];
786 Display *dpy = MI_DISPLAY(mi);
787 Window window = MI_WINDOW(mi);
790 if (!bp->glx_context)
793 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
795 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
798 glRotatef(bp->view_x, 1, 0, 0);
799 glRotatef(bp->view_y, 0, 1, 0);
800 gltrackball_rotate (bp->user_trackball);
812 F.dx = F.dy = F.dz = 0;
815 if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
816 if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
818 glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
819 glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
820 glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
822 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
823 if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
825 draw_floater (mi, &F);
827 if (mi->fps_p) do_fps (mi);
829 glXSwapBuffers(dpy, window);
834 glScalef (0.5, 0.5, 0.5);
836 glTranslatef (0, 0, -GRID_DEPTH/2.5);
839 mi->polygon_count = 0;
840 for (i = 0; i < bp->nfloaters; i++)
842 floater *f = &bp->floaters[i];
843 draw_floater (mi, f);
844 tick_floater (mi, f);
850 if (mi->fps_p) do_fps (mi);
853 glXSwapBuffers(dpy, window);
856 XSCREENSAVER_MODULE_2 ("FlyingToasters", flyingtoasters, toasters)