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