From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / hacks / glx / flyingtoasters.c
1 /* flyingtoasters, 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  * 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!
14  *
15  * Code by jwz; object models by Baconmonkey.
16  *
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.)
22  *
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.
27  */
28
29 #define DEFAULTS        "*delay:        30000       \n" \
30                         "*showFPS:      False       \n" \
31                         "*wireframe:    False       \n" \
32
33 /* #define DEBUG */
34
35 # define refresh_toasters 0
36 # define release_toasters 0
37 #undef countof
38 #define countof(x) (sizeof((x))/sizeof((*x)))
39
40 #define DEF_SPEED       "1.0"
41 #define DEF_NTOASTERS   "20"
42 #define DEF_NSLICES     "25"
43 #define DEF_TEXTURE     "True"
44
45 #undef BELLRAND
46 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
47
48 #include "xlockmore.h"
49 #include "gltrackball.h"
50 #include "xpm-ximage.h"
51 #include <ctype.h>
52
53 #include "../images/chromesphere.xpm"
54 #include "../images/toast.xpm"
55
56 #ifdef USE_GL /* whole file */
57
58 #include "gllist.h"
59
60 extern const struct gllist
61   *toaster, *toaster_base, *toaster_handle, *toaster_handle2, *toaster_jet,
62   *toaster_knob, *toaster_slots, *toaster_wing, *toast, *toast2;
63
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
67 };
68
69 #define BASE_TOASTER    0
70 #define BASE            1
71 #define HANDLE          2
72 #define HANDLE_SLOT     3
73 #define JET             4
74 #define KNOB            5
75 #define SLOTS           6
76 #define JET_WING        7
77 #define TOAST           8
78 #define TOAST_BITTEN    9
79
80 #define GRID_SIZE   60
81 #define GRID_DEPTH 500
82
83
84 static const struct { GLfloat x, y; } nice_views[] = {
85   {  0,  120 },
86   {  0, -120 },
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.)       */
90   { 40,  -60 },
91   {-40,  -60 },
92   { 40,   60 },
93   {-40,   60 },
94   { 30,    0 },
95   {-30,    0 },
96 };
97
98
99 typedef struct {
100   GLfloat x, y, z;
101   GLfloat dx, dy, dz;
102   Bool toaster_p;
103   int toast_type;      /* 0, 1 */
104   GLfloat handle_pos;  /* 0.0 - 1.0 */
105   GLfloat knob_pos;    /* degrees */
106   int loaded;          /* 2 bits */
107 } floater;
108
109 typedef struct {
110   GLXContext *glx_context;
111   trackball_state *user_trackball;
112   Bool button_down_p;
113
114   int last_view, target_view;
115   GLfloat view_x, view_y;
116   int view_steps, view_tick;
117   Bool auto_tracking_p;
118   int track_tick;
119
120   GLuint *dlists;
121   GLuint chrome_texture;
122   GLuint toast_texture;
123
124   int nfloaters;
125   floater *floaters;
126
127 } toaster_configuration;
128
129 static toaster_configuration *bps = NULL;
130
131 static GLfloat speed;
132 static int ntoasters;
133 static int nslices;
134 static int do_texture;
135
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" },
142 };
143
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},
149 };
150
151 ENTRYPOINT ModeSpecOpt toasters_opts = {countof(opts), opts, countof(vars), vars, NULL};
152
153
154 static void
155 reset_floater (ModeInfo *mi, floater *f)
156 {
157 /*  toaster_configuration *bp = &bps[MI_SCREEN(mi)]; */
158
159   GLfloat n = GRID_SIZE/2.0;
160   GLfloat n2 = GRID_DEPTH/2.0;
161   GLfloat delta = GRID_SIZE * speed / 200.0;
162
163   f->dx = 0;
164   f->dy = 0;
165   f->dz = delta;
166
167   f->dz += BELLRAND(delta) - delta/3;
168
169   if (! (random() % 5)) {
170     f->dx += (BELLRAND(delta*2) - delta);
171     f->dy += (BELLRAND(delta*2) - delta);
172   }
173
174   if (! (random() % 40)) f->dz *= 10;    /* occasional speedy one */
175
176   f->x = frand(n) - n/2;
177   f->y = frand(n) - n/2;
178   f->z = -n2 - frand(delta * 4);
179
180   if (f->toaster_p)
181     {
182       f->loaded = 0;
183       f->knob_pos = frand(180) - 90;
184       f->handle_pos = ((random() & 1) ? 0.0 : 1.0);
185
186       if (f->handle_pos > 0.8 && (! (random() % 5)))
187         f->loaded = (random() & 3);  /* let's toast! */
188     }
189   else
190     {
191       if (! (random() % 10))
192         f->toast_type = 1;      /* toast_bitten */
193     }
194 }
195
196
197 static void
198 tick_floater (ModeInfo *mi, floater *f)
199 {
200   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
201
202   GLfloat n1 = GRID_DEPTH/2.0;
203   GLfloat n2 = GRID_SIZE*4;
204
205   if (bp->button_down_p) return;
206
207   f->x += f->dx;
208   f->y += f->dy;
209   f->z += f->dz;
210
211   if (! (random() % 50000))  /* sudden gust of gravity */
212     f->dy -= 2.8;
213
214   if (f->x < -n2 || f->x > n2 ||
215       f->y < -n2 || f->y > n2 ||
216       f->z > n1)
217     reset_floater (mi, f);
218 }
219
220
221 static void
222 auto_track_init (ModeInfo *mi)
223 {
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;
230   bp->view_tick = 0;
231   bp->auto_tracking_p = True;
232 }
233
234
235 static void
236 auto_track (ModeInfo *mi)
237 {
238   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
239
240   if (bp->button_down_p)
241     return;
242
243   /* if we're not moving, maybe start moving.  Otherwise, do nothing. */
244   if (! bp->auto_tracking_p)
245     {
246       if (++bp->track_tick < 200/speed) return;
247       bp->track_tick = 0;
248       if (! (random() % 5))
249         bp->auto_tracking_p = True;
250       else
251         return;
252     }
253
254
255   {
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;
260
261     /* move from A to B with sinusoidal deltas, so that it doesn't jerk
262        to a stop. */
263     GLfloat th = sin ((M_PI / 2) * (double) bp->view_tick / bp->view_steps);
264
265     bp->view_x = (ox + ((tx - ox) * th));
266     bp->view_y = (oy + ((ty - oy) * th));
267     bp->view_tick++;
268
269   if (bp->view_tick >= bp->view_steps)
270     {
271       bp->view_tick = 0;
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;
276     }
277   }
278 }
279
280
281 /* Window management, etc
282  */
283 ENTRYPOINT void
284 reshape_toasters (ModeInfo *mi, int width, int height)
285 {
286   GLfloat h = (GLfloat) height / (GLfloat) width;
287
288   glViewport (0, 0, (GLint) width, (GLint) height);
289
290   glMatrixMode(GL_PROJECTION);
291   glLoadIdentity();
292   gluPerspective (40.0, 1/h, 1.0, 250);
293
294   glMatrixMode(GL_MODELVIEW);
295   glLoadIdentity();
296   gluLookAt( 0.0, 2.0, 30.0,
297              0.0, 0.0, 0.0,
298              0.0, 1.0, 0.0);
299
300   glClear(GL_COLOR_BUFFER_BIT);
301 }
302
303
304 ENTRYPOINT Bool
305 toasters_handle_event (ModeInfo *mi, XEvent *event)
306 {
307   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
308
309   if (event->xany.type == ButtonPress &&
310       event->xbutton.button == Button1)
311     {
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));
316       return True;
317     }
318   else if (event->xany.type == ButtonRelease &&
319            event->xbutton.button == Button1)
320     {
321       bp->button_down_p = False;
322       return True;
323     }
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))
329     {
330       gltrackball_mousewheel (bp->user_trackball, event->xbutton.button, 5,
331                               !event->xbutton.state);
332       return True;
333     }
334   else if (event->xany.type == MotionNotify &&
335            bp->button_down_p)
336     {
337       gltrackball_track (bp->user_trackball,
338                          event->xmotion.x, event->xmotion.y,
339                          MI_WIDTH (mi), MI_HEIGHT (mi));
340       return True;
341     }
342
343   return False;
344 }
345
346
347 static void
348 load_textures (ModeInfo *mi)
349 {
350   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
351   XImage *xi;
352
353   xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
354                       chromesphere_xpm);
355   clear_gl_error();
356
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,
364                 GL_RGBA,
365 # ifndef USE_IPHONE
366                 GL_UNSIGNED_INT_8_8_8_8_REV,
367 # else
368                 GL_UNSIGNED_BYTE,
369 # endif
370                 xi->data);
371   check_gl_error("texture");
372
373   xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
374                       toast_xpm);
375
376   glGenTextures (1, &bp->toast_texture);
377   glBindTexture (GL_TEXTURE_2D, bp->toast_texture);
378   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
379   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
380 # ifndef HAVE_JWZGLES
381   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
382   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
383 # endif
384   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
385   glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
386                 xi->width, xi->height, 0,
387                 GL_RGBA,
388 # ifndef USE_IPHONE
389                 GL_UNSIGNED_INT_8_8_8_8_REV,
390 # else
391                 GL_UNSIGNED_BYTE,
392 # endif
393                 xi->data);
394   check_gl_error("texture");
395
396   glEnable(GL_TEXTURE_GEN_S);
397   glEnable(GL_TEXTURE_GEN_T);
398   glEnable(GL_TEXTURE_2D);
399 }
400
401
402 ENTRYPOINT void 
403 init_toasters (ModeInfo *mi)
404 {
405   toaster_configuration *bp;
406   int wire = MI_IS_WIREFRAME(mi);
407   int i;
408
409   if (!bps) {
410     bps = (toaster_configuration *)
411       calloc (MI_NUM_SCREENS(mi), sizeof (toaster_configuration));
412     if (!bps) {
413       fprintf(stderr, "%s: out of memory\n", progname);
414       exit(1);
415     }
416   }
417
418   bp = &bps[MI_SCREEN(mi)];
419
420   bp->glx_context = init_GL(mi);
421
422   reshape_toasters (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
423
424   glShadeModel(GL_SMOOTH);
425
426   glEnable(GL_DEPTH_TEST);
427   glEnable(GL_NORMALIZE);
428   glEnable(GL_CULL_FACE);
429
430   if (!wire)
431     {
432       GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
433 /*      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
434       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
435       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
436       GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
437
438       glEnable(GL_LIGHTING);
439       glEnable(GL_LIGHT0);
440       glEnable(GL_DEPTH_TEST);
441       glEnable(GL_CULL_FACE);
442
443       glLightfv(GL_LIGHT0, GL_POSITION, pos);
444       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
445       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
446       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
447     }
448
449   if (!wire && do_texture)
450     load_textures (mi);
451
452   bp->user_trackball = gltrackball_init ();
453   auto_track_init (mi);
454
455   bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
456   for (i = 0; i < countof(all_objs); i++)
457     bp->dlists[i] = glGenLists (1);
458
459   for (i = 0; i < countof(all_objs); i++)
460     {
461       const struct gllist *gll = *all_objs[i];
462
463       glNewList (bp->dlists[i], GL_COMPILE);
464
465       glMatrixMode(GL_MODELVIEW);
466       glPushMatrix();
467       glMatrixMode(GL_TEXTURE);
468       glPushMatrix();
469       glMatrixMode(GL_MODELVIEW);
470
471       glRotatef (-90, 1, 0, 0);
472       glRotatef (180, 0, 0, 1);
473       glScalef (6, 6, 6);
474
475       glBindTexture (GL_TEXTURE_2D, 0);
476
477       if (i == BASE_TOASTER)
478         {
479           GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
480           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
481           GLfloat shiny    = 20.0;
482           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
483           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
484           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
485           if (do_texture)
486             glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
487 # ifndef HAVE_JWZGLES
488           glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
489           glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
490 # endif
491         }
492       else if (i == TOAST || i == TOAST_BITTEN)
493         {
494           GLfloat color[4] = {0.80, 0.80, 0.00, 1.0};
495           GLfloat spec[4]  = {0.00, 0.00, 0.00, 1.0};
496           GLfloat shiny    = 0.0;
497           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
498           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
499           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
500           if (do_texture)
501             glBindTexture (GL_TEXTURE_2D, bp->toast_texture);
502 # ifndef HAVE_JWZGLES
503           glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
504           glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
505 # endif
506
507           glMatrixMode(GL_TEXTURE);
508           glTranslatef(0.5, 0.5, 0);
509           glMatrixMode(GL_MODELVIEW);
510         }
511       else if (i == SLOTS || i == HANDLE_SLOT)
512         {
513           GLfloat color[4] = {0.30, 0.30, 0.40, 1.0};
514           GLfloat spec[4]  = {0.40, 0.40, 0.70, 1.0};
515           GLfloat shiny    = 128.0;
516           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
517           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
518           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
519         }
520       else if (i == HANDLE)
521         {
522           GLfloat color[4] = {0.80, 0.10, 0.10, 1.0};
523           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
524           GLfloat shiny    = 20.0;
525           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
526           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
527           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
528         }
529       else if (i == KNOB)
530         {
531           GLfloat color[4] = {0.80, 0.10, 0.10, 1.0};
532           GLfloat spec[4]  = {0.00, 0.00, 0.00, 1.0};
533           GLfloat shiny    = 0.0;
534           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
535           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
536           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
537         }
538       else if (i == JET || i == JET_WING)
539         {
540           GLfloat color[4] = {0.70, 0.70, 0.70, 1.0};
541           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
542           GLfloat shiny    = 20.0;
543           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
544           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
545           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
546         }
547       else if (i == BASE)
548         {
549           GLfloat color[4] = {0.50, 0.50, 0.50, 1.0};
550           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
551           GLfloat shiny    = 20.0;
552           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
553           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
554           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
555         }
556       else
557         {
558           GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
559           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
560           GLfloat shiny    = 128.0;
561           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
562           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
563           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
564         }
565
566       renderList (gll, wire);
567
568       glMatrixMode(GL_TEXTURE);
569       glPopMatrix();
570       glMatrixMode(GL_MODELVIEW);
571       glPopMatrix();
572
573       glEndList ();
574     }
575
576   bp->nfloaters = ntoasters + nslices;
577   bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
578
579   for (i = 0; i < bp->nfloaters; i++)
580     {
581       floater *f = &bp->floaters[i];
582       /* arrange the list so that half the toasters are in front of bread,
583          and half are behind. */
584       f->toaster_p = ((i < ntoasters / 2) ||
585                       (i >= (nslices + (ntoasters / 2))));
586       reset_floater (mi, f);
587
588       /* Position the first generation randomly, but make sure they aren't
589          on screen yet (until we rotate the view into position.)
590        */
591       {
592         GLfloat min = -GRID_DEPTH/2;
593         GLfloat max =  GRID_DEPTH/3.5;
594         f->z = frand (max - min) + min;
595       }
596     }
597 }
598
599
600 static void
601 draw_origin (ModeInfo *mi)
602 {
603 # ifdef DEBUG
604 /*  toaster_configuration *bp = &bps[MI_SCREEN(mi)];*/
605
606   if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
607   if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
608
609   glPushMatrix();
610   glScalef (5, 5, 5);
611   glBegin(GL_LINES);
612   glVertex3f(-1, 0, 0); glVertex3f(1, 0, 0);
613   glVertex3f(0, -1, 0); glVertex3f(0, 1, 0);
614   glVertex3f(0, 0, -1); glVertex3f(0, 0, 1);
615   glEnd();
616   glPopMatrix();
617
618   if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
619   if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
620 # endif /* DEBUG */
621 }
622
623
624 static void
625 draw_grid (ModeInfo *mi)
626 {
627 # ifdef DEBUG
628 /*  toaster_configuration *bp = &bps[MI_SCREEN(mi)];*/
629
630   if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
631   if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
632   glPushMatrix();
633   glBegin(GL_LINE_LOOP);
634   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
635   glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2, 0);
636   glVertex3f( GRID_SIZE/2,  GRID_SIZE/2, 0);
637   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
638   glEnd();
639   glBegin(GL_LINE_LOOP);
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   glEnd();
645   glBegin(GL_LINE_LOOP);
646   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
647   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
648   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
649   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
650   glEnd();
651   glBegin(GL_LINES);
652   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
653   glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2, -GRID_DEPTH/2);
654   glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
655   glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2,  GRID_DEPTH/2);
656   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
657   glVertex3f( GRID_SIZE/2,  GRID_SIZE/2, -GRID_DEPTH/2);
658   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
659   glVertex3f( GRID_SIZE/2,  GRID_SIZE/2,  GRID_DEPTH/2);
660   glEnd();
661   glPopMatrix();
662
663   if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
664   if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
665 # endif /* DEBUG */
666 }
667
668
669 static void
670 draw_floater (ModeInfo *mi, floater *f)
671 {
672   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
673   GLfloat n;
674
675   glFrontFace(GL_CCW);
676
677   glPushMatrix();
678   glTranslatef (f->x, f->y, f->z);
679   if (f->toaster_p)
680     {
681       glPushMatrix();
682       glRotatef (180, 0, 1, 0);
683
684       glCallList (bp->dlists[BASE_TOASTER]);
685       mi->polygon_count += (*all_objs[BASE_TOASTER])->points / 3;
686       glPopMatrix();
687
688       glPushMatrix();
689       glTranslatef(0, 1.01, 0);
690       n = 0.91; glScalef(n,n,n);
691       glCallList (bp->dlists[SLOTS]);
692       mi->polygon_count += (*all_objs[SLOTS])->points / 3;
693       glPopMatrix();
694
695       glPushMatrix();
696       glRotatef (180, 0, 1, 0);
697       glTranslatef(0, -0.4, -2.38);
698       n = 0.33; glScalef(n,n,n);
699       glCallList (bp->dlists[HANDLE_SLOT]);
700       mi->polygon_count += (*all_objs[HANDLE_SLOT])->points / 3;
701       glPopMatrix();
702
703       glPushMatrix();
704       glTranslatef(0, -1.1, 3);
705       n = 0.3; glScalef (n,n,n);
706       glTranslatef(0, f->handle_pos * 4.8, 0);
707       glCallList (bp->dlists[HANDLE]);
708       mi->polygon_count += (*all_objs[HANDLE])->points / 3;
709       glPopMatrix();
710
711       glPushMatrix();
712       glRotatef (180, 0, 1, 0);
713       glTranslatef(0, -1.1, -3);     /* where the handle is */
714       glTranslatef (1, -0.4, 0);     /* down and to the left */
715       n = 0.08; glScalef (n,n,n);
716       glRotatef (f->knob_pos, 0, 0, 1);
717       glCallList (bp->dlists[KNOB]);
718       mi->polygon_count += (*all_objs[KNOB])->points / 3;
719       glPopMatrix();
720
721       glPushMatrix();
722       glRotatef (180, 0, 1, 0);
723       glTranslatef (0, -2.3, 0);
724       glCallList (bp->dlists[BASE]);
725       mi->polygon_count += (*all_objs[BASE])->points / 3;
726       glPopMatrix();
727
728       glPushMatrix();
729       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;
736       glPopMatrix();
737
738       glPushMatrix();
739       glTranslatef(4.8, 0, 0);
740       glScalef(-1, 1, 1);
741       glFrontFace(GL_CW);
742       glCallList (bp->dlists[JET_WING]);
743       mi->polygon_count += (*all_objs[JET_WING])->points / 3;
744       glScalef (0.5, 0.5, 0.5);
745       glTranslatef (-2, -1, 0);
746       glCallList (bp->dlists[JET]);
747       mi->polygon_count += (*all_objs[JET])->points / 3;
748       glFrontFace(GL_CCW);
749       glPopMatrix();
750
751       if (f->loaded)
752         {
753           glPushMatrix();
754           glTranslatef(0, 1.01, 0);
755           n = 0.91; glScalef(n,n,n);
756           glRotatef (90, 0, 0, 1);
757           glRotatef (90, 0, 1, 0);
758           glTranslatef(0, 0, -0.95);
759           glTranslatef(0, 0.72, 0);
760           if (f->loaded & 1)
761             {
762               glCallList (bp->dlists[TOAST]);
763               mi->polygon_count += (*all_objs[TOAST])->points / 3;
764             }
765           glTranslatef(0, -1.46, 0);
766           if (f->loaded & 2)
767             {
768               glCallList (bp->dlists[TOAST]);
769               mi->polygon_count += (*all_objs[TOAST])->points / 3;
770             }
771           glPopMatrix();
772         }
773     }
774   else
775     {
776       glScalef (0.7, 0.7, 0.7);
777       if (f->toast_type == 0)
778         {
779           glCallList (bp->dlists[TOAST]);
780           mi->polygon_count += (*all_objs[TOAST])->points / 3;
781         }
782       else
783         {
784           glCallList (bp->dlists[TOAST_BITTEN]);
785           mi->polygon_count += (*all_objs[TOAST_BITTEN])->points / 3;
786         }
787     }
788
789   glPopMatrix();
790 }
791
792
793
794 ENTRYPOINT void
795 draw_toasters (ModeInfo *mi)
796 {
797   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
798   Display *dpy = MI_DISPLAY(mi);
799   Window window = MI_WINDOW(mi);
800   int i;
801
802   if (!bp->glx_context)
803     return;
804
805   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
806
807   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
808
809   glPushMatrix ();
810   glRotatef(current_device_rotation(), 0, 0, 1);
811   glRotatef(bp->view_x, 1, 0, 0);
812   glRotatef(bp->view_y, 0, 1, 0);
813   glRotatef(-current_device_rotation(), 0, 0, 1);
814   gltrackball_rotate (bp->user_trackball);
815   glRotatef(current_device_rotation(), 0, 0, 1);
816
817
818 #if 0
819   {
820     floater F;
821     F.toaster_p = 0;
822     F.toast_type = 1;
823     F.handle_pos = 0;
824     F.knob_pos = -90;
825     F.loaded = 3;
826     F.x = F.y = F.z = 0;
827     F.dx = F.dy = F.dz = 0;
828
829     glScalef(2,2,2);
830     if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
831     if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
832     glBegin(GL_LINES);
833     glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
834     glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
835     glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
836     glEnd();
837     if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
838     if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
839
840     draw_floater (mi, &F);
841     glPopMatrix ();
842     if (mi->fps_p) do_fps (mi);
843     glFinish();
844     glXSwapBuffers(dpy, window);
845     return;
846   }
847 #endif
848
849   glScalef (0.5, 0.5, 0.5);
850   draw_origin (mi);
851   glTranslatef (0, 0, -GRID_DEPTH/2.5);
852   draw_grid (mi);
853
854   mi->polygon_count = 0;
855   for (i = 0; i < bp->nfloaters; i++)
856     {
857       floater *f = &bp->floaters[i];
858       draw_floater (mi, f);
859       tick_floater (mi, f);
860     }
861   auto_track (mi);
862
863   glPopMatrix ();
864
865   if (mi->fps_p) do_fps (mi);
866   glFinish();
867
868   glXSwapBuffers(dpy, window);
869 }
870
871 XSCREENSAVER_MODULE_2 ("FlyingToasters", flyingtoasters, toasters)
872
873 #endif /* USE_GL */