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