http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / glx / flyingtoasters.c
1 /* flyingtoasters, Copyright (c) 2003, 2004 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 #include <X11/Intrinsic.h>
30
31 extern XtAppContext app;
32
33 #define PROGCLASS       "FlyingToasters"
34 #define HACK_INIT       init_toasters
35 #define HACK_DRAW       draw_toasters
36 #define HACK_RESHAPE    reshape_toasters
37 #define HACK_HANDLE_EVENT toaster_handle_event
38 #define EVENT_MASK      PointerMotionMask
39 #define sws_opts        xlockmore_opts
40
41 #define DEF_SPEED       "1.0"
42 #define DEF_NTOASTERS   "20"
43 #define DEF_NSLICES     "25"
44 #define DEF_TEXTURE     "True"
45
46 #define DEFAULTS        "*delay:        30000       \n" \
47                         "*showFPS:      False       \n" \
48                         "*wireframe:    False       \n" \
49                         "*speed:      " DEF_SPEED " \n" \
50                         "*ntoasters:  " DEF_NTOASTERS "\n" \
51                         "*nslices:    " DEF_NSLICES   "\n" \
52                         "*texture:    " DEF_TEXTURE   "\n" \
53
54 /* #define DEBUG */
55
56 #undef countof
57 #define countof(x) (sizeof((x))/sizeof((*x)))
58
59 #undef BELLRAND
60 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
61
62 #include "xlockmore.h"
63 #include "gltrackball.h"
64 #include "xpm-ximage.h"
65 #include <ctype.h>
66
67 #include "../images/chromesphere.xpm"
68 #include "../images/toast.xpm"
69
70 #ifdef USE_GL /* whole file */
71
72 #include <GL/glu.h>
73
74
75 #include "gllist.h"
76
77 extern struct gllist
78   *toaster, *toaster_base, *toaster_handle, *toaster_handle2, *toaster_jet,
79   *toaster_knob, *toaster_slots, *toaster_wing, *toast, *toast2;
80
81 struct gllist **all_objs[] = {
82   &toaster, &toaster_base, &toaster_handle, &toaster_handle2, &toaster_jet,
83   &toaster_knob, &toaster_slots, &toaster_wing, &toast, &toast2
84 };
85
86 #define BASE_TOASTER    0
87 #define BASE            1
88 #define HANDLE          2
89 #define HANDLE_SLOT     3
90 #define JET             4
91 #define KNOB            5
92 #define SLOTS           6
93 #define JET_WING        7
94 #define TOAST           8
95 #define TOAST_BITTEN    9
96
97 #define GRID_SIZE   60
98 #define GRID_DEPTH 500
99
100
101 static struct { GLfloat x, y; } nice_views[] = {
102   {  0,  120 },
103   {  0, -120 },
104   { 12,   28 },     /* this is a list of viewer rotations that look nice. */
105   { 12,  -28 },     /* every now and then we switch to a new one.         */
106   {-10,  -28 },     /* (but we only use the first two at start-up.)       */
107   { 40,  -60 },
108   {-40,  -60 },
109   { 40,   60 },
110   {-40,   60 },
111   { 30,    0 },
112   {-30,    0 },
113 };
114
115
116 typedef struct {
117   GLfloat x, y, z;
118   GLfloat dx, dy, dz;
119   Bool toaster_p;
120   int toast_type;      /* 0, 1 */
121   GLfloat handle_pos;  /* 0.0 - 1.0 */
122   GLfloat knob_pos;    /* degrees */
123   int loaded;          /* 2 bits */
124 } floater;
125
126 typedef struct {
127   GLXContext *glx_context;
128   trackball_state *user_trackball;
129   Bool button_down_p;
130
131   int last_view, target_view;
132   GLfloat view_x, view_y;
133   int view_steps, view_tick;
134   Bool auto_tracking_p;
135
136   GLuint *dlists;
137   GLuint chrome_texture;
138   GLuint toast_texture;
139
140   int nfloaters;
141   floater *floaters;
142
143 } toaster_configuration;
144
145 static toaster_configuration *bps = NULL;
146
147 static GLfloat speed;
148 static int ntoasters;
149 static int nslices;
150 static int do_texture;
151
152 static XrmOptionDescRec opts[] = {
153   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
154   { "-ntoasters",  ".ntoasters", XrmoptionSepArg, 0 },
155   { "-nslices",    ".nslices",   XrmoptionSepArg, 0 },
156   {"-texture",     ".texture",   XrmoptionNoArg, "True" },
157   {"+texture",     ".texture",   XrmoptionNoArg, "False" },
158 };
159
160 static argtype vars[] = {
161   {&speed,      "speed",      "Speed",   DEF_SPEED,     t_Float},
162   {&ntoasters,  "ntoasters",  "Count",   DEF_NTOASTERS, t_Int},
163   {&nslices,    "nslices",    "Count",   DEF_NSLICES,   t_Int},
164   {&do_texture, "texture",    "Texture", DEF_TEXTURE,   t_Bool},
165 };
166
167 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
168
169
170 static void
171 reset_floater (ModeInfo *mi, floater *f)
172 {
173 /*  toaster_configuration *bp = &bps[MI_SCREEN(mi)]; */
174
175   GLfloat n = GRID_SIZE/2.0;
176   GLfloat n2 = GRID_DEPTH/2.0;
177   GLfloat delta = GRID_SIZE * speed / 200.0;
178
179   f->dx = 0;
180   f->dy = 0;
181   f->dz = delta;
182
183   f->dz += BELLRAND(delta) - delta/3;
184
185   if (! (random() % 5)) {
186     f->dx += (BELLRAND(delta*2) - delta);
187     f->dy += (BELLRAND(delta*2) - delta);
188   }
189
190   if (! (random() % 40)) f->dz *= 10;    /* occasional speedy one */
191
192   f->x = frand(n) - n/2;
193   f->y = frand(n) - n/2;
194   f->z = -n2 - frand(delta * 4);
195
196   if (f->toaster_p)
197     {
198       f->loaded = 0;
199       f->knob_pos = frand(180) - 90;
200       f->handle_pos = ((random() & 1) ? 0.0 : 1.0);
201
202       if (f->handle_pos > 0.8 && (! (random() % 5)))
203         f->loaded = (random() & 3);  /* let's toast! */
204     }
205   else
206     {
207       if (! (random() % 10))
208         f->toast_type = 1;      /* toast_bitten */
209     }
210 }
211
212
213 static void
214 tick_floater (ModeInfo *mi, floater *f)
215 {
216   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
217
218   GLfloat n1 = GRID_DEPTH/2.0;
219   GLfloat n2 = GRID_SIZE*4;
220
221   if (bp->button_down_p) return;
222
223   f->x += f->dx;
224   f->y += f->dy;
225   f->z += f->dz;
226
227   if (! (random() % 50000))  /* sudden gust of gravity */
228     f->dy -= 2.8;
229
230   if (f->x < -n2 || f->x > n2 ||
231       f->y < -n2 || f->y > n2 ||
232       f->z > n1)
233     reset_floater (mi, f);
234 }
235
236
237 static void
238 auto_track_init (ModeInfo *mi)
239 {
240   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
241   bp->last_view = (random() % 2);
242   bp->target_view = bp->last_view + 2;
243   bp->view_x = nice_views[bp->last_view].x;
244   bp->view_y = nice_views[bp->last_view].y;
245   bp->view_steps = 100;
246   bp->view_tick = 0;
247   bp->auto_tracking_p = True;
248 }
249
250
251 static void
252 auto_track (ModeInfo *mi)
253 {
254   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
255
256   if (bp->button_down_p)
257     return;
258
259   /* if we're not moving, maybe start moving.  Otherwise, do nothing. */
260   if (! bp->auto_tracking_p)
261     {
262       static int tick = 0;
263       if (++tick < 200/speed) return;
264       tick = 0;
265       if (! (random() % 5))
266         bp->auto_tracking_p = True;
267       else
268         return;
269     }
270
271
272   {
273     GLfloat ox = nice_views[bp->last_view].x;
274     GLfloat oy = nice_views[bp->last_view].y;
275     GLfloat tx = nice_views[bp->target_view].x;
276     GLfloat ty = nice_views[bp->target_view].y;
277
278     /* move from A to B with sinusoidal deltas, so that it doesn't jerk
279        to a stop. */
280     GLfloat th = sin ((M_PI / 2) * (double) bp->view_tick / bp->view_steps);
281
282     bp->view_x = (ox + ((tx - ox) * th));
283     bp->view_y = (oy + ((ty - oy) * th));
284     bp->view_tick++;
285
286   if (bp->view_tick >= bp->view_steps)
287     {
288       bp->view_tick = 0;
289       bp->view_steps = (350.0 / speed);
290       bp->last_view = bp->target_view;
291       bp->target_view = (random() % (countof(nice_views) - 2)) + 2;
292       bp->auto_tracking_p = False;
293     }
294   }
295 }
296
297
298 /* Window management, etc
299  */
300 void
301 reshape_toasters (ModeInfo *mi, int width, int height)
302 {
303   GLfloat h = (GLfloat) height / (GLfloat) width;
304
305   glViewport (0, 0, (GLint) width, (GLint) height);
306
307   glMatrixMode(GL_PROJECTION);
308   glLoadIdentity();
309   gluPerspective (40.0, 1/h, 1.0, 250);
310
311   glMatrixMode(GL_MODELVIEW);
312   glLoadIdentity();
313   gluLookAt( 0.0, 2.0, 30.0,
314              0.0, 0.0, 0.0,
315              0.0, 1.0, 0.0);
316
317   glClear(GL_COLOR_BUFFER_BIT);
318 }
319
320
321 Bool
322 toaster_handle_event (ModeInfo *mi, XEvent *event)
323 {
324   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
325
326   if (event->xany.type == ButtonPress &&
327       event->xbutton.button == Button1)
328     {
329       bp->button_down_p = True;
330       gltrackball_start (bp->user_trackball,
331                          event->xbutton.x, event->xbutton.y,
332                          MI_WIDTH (mi), MI_HEIGHT (mi));
333       return True;
334     }
335   else if (event->xany.type == ButtonRelease &&
336            event->xbutton.button == Button1)
337     {
338       bp->button_down_p = False;
339       return True;
340     }
341   else if (event->xany.type == ButtonPress &&
342            (event->xbutton.button == Button4 ||
343             event->xbutton.button == Button5))
344     {
345       gltrackball_mousewheel (bp->user_trackball, event->xbutton.button, 5,
346                               !event->xbutton.state);
347       return True;
348     }
349   else if (event->xany.type == MotionNotify &&
350            bp->button_down_p)
351     {
352       gltrackball_track (bp->user_trackball,
353                          event->xmotion.x, event->xmotion.y,
354                          MI_WIDTH (mi), MI_HEIGHT (mi));
355       return True;
356     }
357
358   return False;
359 }
360
361
362 static void
363 load_textures (ModeInfo *mi)
364 {
365   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
366   XImage *xi;
367
368   xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
369                       chromesphere_xpm);
370   clear_gl_error();
371
372   glGenTextures (1, &bp->chrome_texture);
373   glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
374   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
375   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
376   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
377   glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
378                 xi->width, xi->height, 0,
379                 GL_RGBA,
380                 /* GL_UNSIGNED_BYTE, */
381                 GL_UNSIGNED_INT_8_8_8_8_REV,
382                 xi->data);
383   check_gl_error("texture");
384
385   xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
386                       toast_xpm);
387
388   glGenTextures (1, &bp->toast_texture);
389   glBindTexture (GL_TEXTURE_2D, bp->toast_texture);
390   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
391   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
392   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
393   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
394   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
395   glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
396                 xi->width, xi->height, 0,
397                 GL_RGBA,
398                 /* GL_UNSIGNED_BYTE, */
399                 GL_UNSIGNED_INT_8_8_8_8_REV,
400                 xi->data);
401   check_gl_error("texture");
402
403   glEnable(GL_TEXTURE_GEN_S);
404   glEnable(GL_TEXTURE_GEN_T);
405   glEnable(GL_TEXTURE_2D);
406 }
407
408
409 void 
410 init_toasters (ModeInfo *mi)
411 {
412   toaster_configuration *bp;
413   int wire = MI_IS_WIREFRAME(mi);
414   int i;
415
416   if (!bps) {
417     bps = (toaster_configuration *)
418       calloc (MI_NUM_SCREENS(mi), sizeof (toaster_configuration));
419     if (!bps) {
420       fprintf(stderr, "%s: out of memory\n", progname);
421       exit(1);
422     }
423
424     bp = &bps[MI_SCREEN(mi)];
425   }
426
427   bp = &bps[MI_SCREEN(mi)];
428
429   bp->glx_context = init_GL(mi);
430
431   reshape_toasters (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
432
433   glShadeModel(GL_SMOOTH);
434
435   glEnable(GL_DEPTH_TEST);
436   glEnable(GL_NORMALIZE);
437   glEnable(GL_CULL_FACE);
438
439   if (!wire)
440     {
441       GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
442 /*      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
443       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
444       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
445       GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
446
447       glEnable(GL_LIGHTING);
448       glEnable(GL_LIGHT0);
449       glEnable(GL_DEPTH_TEST);
450       glEnable(GL_CULL_FACE);
451
452       glLightfv(GL_LIGHT0, GL_POSITION, pos);
453       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
454       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
455       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
456     }
457
458   if (!wire && do_texture)
459     load_textures (mi);
460
461   bp->user_trackball = gltrackball_init ();
462   auto_track_init (mi);
463
464   bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
465   for (i = 0; i < countof(all_objs); i++)
466     bp->dlists[i] = glGenLists (1);
467
468   for (i = 0; i < countof(all_objs); i++)
469     {
470       struct gllist *gll = *all_objs[i];
471       if (wire)
472         gll->primitive = GL_LINE_LOOP;
473
474       glNewList (bp->dlists[i], GL_COMPILE);
475
476       glMatrixMode(GL_MODELVIEW);
477       glPushMatrix();
478       glMatrixMode(GL_TEXTURE);
479       glPushMatrix();
480       glMatrixMode(GL_MODELVIEW);
481
482       glRotatef (-90, 1, 0, 0);
483       glRotatef (180, 0, 0, 1);
484       glScalef (6, 6, 6);
485
486       glBindTexture (GL_TEXTURE_2D, 0);
487
488       if (i == BASE_TOASTER)
489         {
490           GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
491           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
492           GLfloat shiny    = 20.0;
493           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
494           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
495           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
496           if (do_texture)
497             glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
498           glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
499           glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
500         }
501       else if (i == TOAST || i == TOAST_BITTEN)
502         {
503           GLfloat color[4] = {0.80, 0.80, 0.00, 1.0};
504           GLfloat spec[4]  = {0.00, 0.00, 0.00, 1.0};
505           GLfloat shiny    = 0.0;
506           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
507           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
508           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
509           if (do_texture)
510             glBindTexture (GL_TEXTURE_2D, bp->toast_texture);
511           glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
512           glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
513
514           glMatrixMode(GL_TEXTURE);
515           glTranslatef(0.5, 0.5, 0);
516           glMatrixMode(GL_MODELVIEW);
517         }
518       else if (i == SLOTS || i == HANDLE_SLOT)
519         {
520           GLfloat color[4] = {0.30, 0.30, 0.40, 1.0};
521           GLfloat spec[4]  = {0.40, 0.40, 0.70, 1.0};
522           GLfloat shiny    = 128.0;
523           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
524           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
525           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
526         }
527       else if (i == HANDLE)
528         {
529           GLfloat color[4] = {0.80, 0.10, 0.10, 1.0};
530           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
531           GLfloat shiny    = 20.0;
532           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
533           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
534           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
535         }
536       else if (i == KNOB)
537         {
538           GLfloat color[4] = {0.80, 0.10, 0.10, 1.0};
539           GLfloat spec[4]  = {0.00, 0.00, 0.00, 1.0};
540           GLfloat shiny    = 0.0;
541           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
542           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
543           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
544         }
545       else if (i == JET || i == JET_WING)
546         {
547           GLfloat color[4] = {0.70, 0.70, 0.70, 1.0};
548           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
549           GLfloat shiny    = 20.0;
550           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
551           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
552           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
553         }
554       else if (i == BASE)
555         {
556           GLfloat color[4] = {0.50, 0.50, 0.50, 1.0};
557           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
558           GLfloat shiny    = 20.0;
559           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
560           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
561           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
562         }
563       else
564         {
565           GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
566           GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
567           GLfloat shiny    = 128.0;
568           glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
569           glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
570           glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
571         }
572
573       renderList (gll);
574
575       glMatrixMode(GL_TEXTURE);
576       glPopMatrix();
577       glMatrixMode(GL_MODELVIEW);
578       glPopMatrix();
579
580       glEndList ();
581     }
582
583   bp->nfloaters = ntoasters + nslices;
584   bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
585
586   for (i = 0; i < bp->nfloaters; i++)
587     {
588       floater *f = &bp->floaters[i];
589       /* arrange the list so that half the toasters are in front of bread,
590          and half are behind. */
591       f->toaster_p = ((i < ntoasters / 2) ||
592                       (i >= (nslices + (ntoasters / 2))));
593       reset_floater (mi, f);
594
595       /* Position the first generation randomly, but make sure they aren't
596          on screen yet (until we rotate the view into position.)
597        */
598       {
599         GLfloat min = -GRID_DEPTH/2;
600         GLfloat max =  GRID_DEPTH/3.5;
601         f->z = frand (max - min) + min;
602       }
603     }
604 }
605
606
607 static void
608 draw_origin (ModeInfo *mi)
609 {
610 # ifdef DEBUG
611 /*  toaster_configuration *bp = &bps[MI_SCREEN(mi)];*/
612
613   if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
614   if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
615
616   glPushMatrix();
617   glScalef (5, 5, 5);
618   glBegin(GL_LINES);
619   glVertex3f(-1, 0, 0); glVertex3f(1, 0, 0);
620   glVertex3f(0, -1, 0); glVertex3f(0, 1, 0);
621   glVertex3f(0, 0, -1); glVertex3f(0, 0, 1);
622   glEnd();
623   glPopMatrix();
624
625   if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
626   if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
627 # endif /* DEBUG */
628 }
629
630
631 static void
632 draw_grid (ModeInfo *mi)
633 {
634 # ifdef DEBUG
635 /*  toaster_configuration *bp = &bps[MI_SCREEN(mi)];*/
636
637   if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
638   if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
639   glPushMatrix();
640   glBegin(GL_LINE_LOOP);
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   glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
645   glEnd();
646   glBegin(GL_LINE_LOOP);
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   glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
651   glEnd();
652   glBegin(GL_LINE_LOOP);
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   glEnd();
658   glBegin(GL_LINES);
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   glVertex3f( GRID_SIZE/2,  GRID_SIZE/2,  GRID_DEPTH/2);
667   glEnd();
668   glPopMatrix();
669
670   if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
671   if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
672 # endif /* DEBUG */
673 }
674
675
676 static void
677 draw_floater (ModeInfo *mi, floater *f)
678 {
679   toaster_configuration *bp = &bps[MI_SCREEN(mi)];
680   GLfloat n;
681
682   glFrontFace(GL_CCW);
683
684   glPushMatrix();
685   glTranslatef (f->x, f->y, f->z);
686   if (f->toaster_p)
687     {
688       glPushMatrix();
689       glRotatef (180, 0, 1, 0);
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 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   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
812
813   glPushMatrix ();
814   glRotatef(bp->view_x, 1, 0, 0);
815   glRotatef(bp->view_y, 0, 1, 0);
816   gltrackball_rotate (bp->user_trackball);
817
818
819 #if 0
820   {
821     floater F;
822     F.toaster_p = 0;
823     F.toast_type = 1;
824     F.handle_pos = 0;
825     F.knob_pos = -90;
826     F.loaded = 3;
827     F.x = F.y = F.z = 0;
828     F.dx = F.dy = F.dz = 0;
829
830     glScalef(2,2,2);
831     if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
832     if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
833     glBegin(GL_LINES);
834     glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
835     glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
836     glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
837     glEnd();
838     if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
839     if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
840
841     draw_floater (mi, &F);
842     glPopMatrix ();
843     if (mi->fps_p) do_fps (mi);
844     glFinish();
845     glXSwapBuffers(dpy, window);
846     return;
847   }
848 #endif
849
850   glScalef (0.5, 0.5, 0.5);
851   draw_origin (mi);
852   glTranslatef (0, 0, -GRID_DEPTH/2.5);
853   draw_grid (mi);
854
855   mi->polygon_count = 0;
856   for (i = 0; i < bp->nfloaters; i++)
857     {
858       floater *f = &bp->floaters[i];
859       draw_floater (mi, f);
860       tick_floater (mi, f);
861     }
862   auto_track (mi);
863
864   glPopMatrix ();
865
866   if (mi->fps_p) do_fps (mi);
867   glFinish();
868
869   glXSwapBuffers(dpy, window);
870 }
871
872 #endif /* USE_GL */