http://www.mirrorservice.org/sites/master.us.finkmirrors.net/distfiles/md5/fa43fdd68d...
[xscreensaver] / hacks / glx / flyingtoasters.c
diff --git a/hacks/glx/flyingtoasters.c b/hacks/glx/flyingtoasters.c
new file mode 100644 (file)
index 0000000..5efc529
--- /dev/null
@@ -0,0 +1,864 @@
+/* flyingtoasters, Copyright (c) 2003 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ *
+ * Draws 3D flying toasters, and toast.  Inspired by the ancient 
+ * Berkeley Systems / After Dark hack, but now updated to the wide
+ * wonderful workd of OpenGL and 3D!
+ *
+ * Code by jwz; object models by Baconmonkey.
+ *
+ * The original After Dark flying toasters, with the fluffy white wings,
+ * were a trademark of Berkeley Systems.  Berkeley Systems ceased to exist
+ * some time in 1998, having been gobbled up by Sierra Online, who were
+ * subsequently gobbled up by Flipside and/or Vivendi (it's hard to tell
+ * exactly what happened when.)
+ *
+ * I doubt anyone even cares any more, but if they do, hopefully this homage,
+ * with the space-age 3D jet-plane toasters, will be considered different
+ * enough that whoever still owns the trademark to the fluffy-winged 2D
+ * bitmapped toasters won't get all huffy at us.
+ */
+
+#include <X11/Intrinsic.h>
+
+extern XtAppContext app;
+
+#define PROGCLASS      "FlyingToasters"
+#define HACK_INIT      init_toasters
+#define HACK_DRAW      draw_toasters
+#define HACK_RESHAPE   reshape_toasters
+#define HACK_HANDLE_EVENT toaster_handle_event
+#define EVENT_MASK      PointerMotionMask
+#define sws_opts       xlockmore_opts
+
+#define DEF_SPEED       "1.0"
+#define DEF_NTOASTERS   "20"
+#define DEF_NSLICES     "25"
+#define DEF_TEXTURE     "True"
+
+#define DEFAULTS       "*delay:        30000       \n" \
+                       "*showFPS:      False       \n" \
+                       "*wireframe:    False       \n" \
+                       "*speed:      " DEF_SPEED " \n" \
+                       "*ntoasters:  " DEF_NTOASTERS "\n" \
+                       "*nslices:    " DEF_NSLICES   "\n" \
+                       "*texture:    " DEF_TEXTURE   "\n" \
+
+/* #define DEBUG */
+
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#undef BELLRAND
+#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
+
+#include "xlockmore.h"
+#include "gltrackball.h"
+#include "xpm-ximage.h"
+#include <ctype.h>
+
+#include "../images/chromesphere.xpm"
+#include "../images/toast.xpm"
+
+#ifdef USE_GL /* whole file */
+
+#include <GL/glu.h>
+
+
+#include "gllist.h"
+
+extern struct gllist
+  *toaster, *toaster_base, *toaster_handle, *toaster_handle2, *toaster_jet,
+  *toaster_knob, *toaster_slots, *toaster_wing, *toast, *toast2;
+
+struct gllist **all_objs[] = {
+  &toaster, &toaster_base, &toaster_handle, &toaster_handle2, &toaster_jet,
+  &toaster_knob, &toaster_slots, &toaster_wing, &toast, &toast2
+};
+
+#define BASE_TOASTER   0
+#define BASE           1
+#define HANDLE         2
+#define HANDLE_SLOT    3
+#define JET            4
+#define KNOB           5
+#define SLOTS          6
+#define JET_WING       7
+#define TOAST          8
+#define TOAST_BITTEN   9
+
+#define GRID_SIZE   60
+#define GRID_DEPTH 500
+
+
+static struct { GLfloat x, y; } nice_views[] = {
+  {  0,  120 },
+  {  0, -120 },
+  { 12,   28 },     /* this is a list of viewer rotations that look nice. */
+  { 12,  -28 },     /* every now and then we switch to a new one.         */
+  {-10,  -28 },     /* (but we only use the first two at start-up.)       */
+  { 40,  -60 },
+  {-40,  -60 },
+  { 40,   60 },
+  {-40,   60 },
+  { 30,    0 },
+  {-30,    0 },
+};
+
+
+typedef struct {
+  GLfloat x, y, z;
+  GLfloat dx, dy, dz;
+  Bool toaster_p;
+  int toast_type;      /* 0, 1 */
+  GLfloat handle_pos;  /* 0.0 - 1.0 */
+  GLfloat knob_pos;    /* degrees */
+  int loaded;          /* 2 bits */
+} floater;
+
+typedef struct {
+  GLXContext *glx_context;
+  trackball_state *user_trackball;
+  Bool button_down_p;
+
+  int last_view, target_view;
+  GLfloat view_x, view_y;
+  int view_steps, view_tick;
+  Bool auto_tracking_p;
+
+  GLuint *dlists;
+  GLuint chrome_texture;
+  GLuint toast_texture;
+
+  int nfloaters;
+  floater *floaters;
+
+} toaster_configuration;
+
+static toaster_configuration *bps = NULL;
+
+static GLfloat speed;
+static int ntoasters;
+static int nslices;
+static int do_texture;
+
+static XrmOptionDescRec opts[] = {
+  { "-speed",  ".speed",  XrmoptionSepArg, 0 },
+  { "-ntoasters",  ".ntoasters", XrmoptionSepArg, 0 },
+  { "-nslices",    ".nslices",   XrmoptionSepArg, 0 },
+  {"-texture",     ".texture",   XrmoptionNoArg, (caddr_t) "True" },
+  {"+texture",     ".texture",   XrmoptionNoArg, (caddr_t) "False" },
+};
+
+static argtype vars[] = {
+  {(caddr_t *) &speed,      "speed",      "Speed",   DEF_SPEED,     t_Float},
+  {(caddr_t *) &ntoasters,  "ntoasters",  "Count",   DEF_NTOASTERS, t_Int},
+  {(caddr_t *) &nslices,    "nslices",    "Count",   DEF_NSLICES,   t_Int},
+  {(caddr_t *) &do_texture, "texture",    "Texture", DEF_TEXTURE,   t_Bool},
+};
+
+ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
+
+
+static void
+reset_floater (ModeInfo *mi, floater *f)
+{
+/*  toaster_configuration *bp = &bps[MI_SCREEN(mi)]; */
+
+  GLfloat n = GRID_SIZE/2.0;
+  GLfloat n2 = GRID_DEPTH/2.0;
+  GLfloat delta = GRID_SIZE * speed / 200.0;
+
+  f->dx = 0;
+  f->dy = 0;
+  f->dz = delta;
+
+  f->dz += BELLRAND(delta) - delta/3;
+
+  if (! (random() % 5)) {
+    f->dx += (BELLRAND(delta*2) - delta);
+    f->dy += (BELLRAND(delta*2) - delta);
+  }
+
+  if (! (random() % 40)) f->dz *= 10;    /* occasional speedy one */
+
+  f->x = frand(n) - n/2;
+  f->y = frand(n) - n/2;
+  f->z = -n2 - frand(delta * 4);
+
+  if (f->toaster_p)
+    {
+      f->loaded = 0;
+      f->knob_pos = frand(180) - 90;
+      f->handle_pos = ((random() & 1) ? 0.0 : 1.0);
+
+      if (f->handle_pos > 0.8 && (! (random() % 5)))
+        f->loaded = (random() & 3);  /* let's toast! */
+    }
+  else
+    {
+      if (! (random() % 10))
+        f->toast_type = 1;     /* toast_bitten */
+    }
+}
+
+
+static void
+tick_floater (ModeInfo *mi, floater *f)
+{
+  toaster_configuration *bp = &bps[MI_SCREEN(mi)];
+
+  GLfloat n1 = GRID_DEPTH/2.0;
+  GLfloat n2 = GRID_SIZE*4;
+
+  if (bp->button_down_p) return;
+
+  f->x += f->dx;
+  f->y += f->dy;
+  f->z += f->dz;
+
+  if (! (random() % 50000))  /* sudden gust of gravity */
+    f->dy -= 2.8;
+
+  if (f->x < -n2 || f->x > n2 ||
+      f->y < -n2 || f->y > n2 ||
+      f->z > n1)
+    reset_floater (mi, f);
+}
+
+
+static void
+auto_track_init (ModeInfo *mi)
+{
+  toaster_configuration *bp = &bps[MI_SCREEN(mi)];
+  bp->last_view = (random() % 2);
+  bp->target_view = bp->last_view + 2;
+  bp->view_x = nice_views[bp->last_view].x;
+  bp->view_y = nice_views[bp->last_view].y;
+  bp->view_steps = 100;
+  bp->view_tick = 0;
+  bp->auto_tracking_p = True;
+}
+
+
+static void
+auto_track (ModeInfo *mi)
+{
+  toaster_configuration *bp = &bps[MI_SCREEN(mi)];
+
+  if (bp->button_down_p)
+    return;
+
+  /* if we're not moving, maybe start moving.  Otherwise, do nothing. */
+  if (! bp->auto_tracking_p)
+    {
+      static int tick = 0;
+      if (++tick < 200/speed) return;
+      tick = 0;
+      if (! (random() % 5))
+        bp->auto_tracking_p = True;
+      else
+        return;
+    }
+
+
+  {
+    GLfloat ox = nice_views[bp->last_view].x;
+    GLfloat oy = nice_views[bp->last_view].y;
+    GLfloat tx = nice_views[bp->target_view].x;
+    GLfloat ty = nice_views[bp->target_view].y;
+
+    /* move from A to B with sinusoidal deltas, so that it doesn't jerk
+       to a stop. */
+    GLfloat th = sin ((M_PI / 2) * (double) bp->view_tick / bp->view_steps);
+
+    bp->view_x = (ox + ((tx - ox) * th));
+    bp->view_y = (oy + ((ty - oy) * th));
+    bp->view_tick++;
+
+  if (bp->view_tick >= bp->view_steps)
+    {
+      bp->view_tick = 0;
+      bp->view_steps = (350.0 / speed);
+      bp->last_view = bp->target_view;
+      bp->target_view = (random() % (countof(nice_views) - 2)) + 2;
+      bp->auto_tracking_p = False;
+    }
+  }
+}
+
+
+/* Window management, etc
+ */
+void
+reshape_toasters (ModeInfo *mi, int width, int height)
+{
+  GLfloat h = (GLfloat) height / (GLfloat) width;
+
+  glViewport (0, 0, (GLint) width, (GLint) height);
+
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  gluPerspective (40.0, 1/h, 1.0, 250);
+
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  gluLookAt( 0.0, 2.0, 30.0,
+             0.0, 0.0, 0.0,
+             0.0, 1.0, 0.0);
+
+  glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+Bool
+toaster_handle_event (ModeInfo *mi, XEvent *event)
+{
+  toaster_configuration *bp = &bps[MI_SCREEN(mi)];
+
+  if (event->xany.type == ButtonPress &&
+      event->xbutton.button & Button1)
+    {
+      bp->button_down_p = True;
+      gltrackball_start (bp->user_trackball,
+                         event->xbutton.x, event->xbutton.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+  else if (event->xany.type == ButtonRelease &&
+           event->xbutton.button & Button1)
+    {
+      bp->button_down_p = False;
+      return True;
+    }
+  else if (event->xany.type == MotionNotify &&
+           bp->button_down_p)
+    {
+      gltrackball_track (bp->user_trackball,
+                         event->xmotion.x, event->xmotion.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+
+  return False;
+}
+
+
+static void
+load_textures (ModeInfo *mi)
+{
+  toaster_configuration *bp = &bps[MI_SCREEN(mi)];
+  XImage *xi;
+
+  xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
+                      chromesphere_xpm);
+  clear_gl_error();
+
+  glGenTextures (1, &bp->chrome_texture);
+  glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
+                xi->width, xi->height, 0,
+                GL_RGBA,
+                /* GL_UNSIGNED_BYTE, */
+                GL_UNSIGNED_INT_8_8_8_8_REV,
+                xi->data);
+  check_gl_error("texture");
+
+  xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
+                      toast_xpm);
+
+  glGenTextures (1, &bp->toast_texture);
+  glBindTexture (GL_TEXTURE_2D, bp->toast_texture);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+  glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
+                xi->width, xi->height, 0,
+                GL_RGBA,
+                /* GL_UNSIGNED_BYTE, */
+                GL_UNSIGNED_INT_8_8_8_8_REV,
+                xi->data);
+  check_gl_error("texture");
+
+  glEnable(GL_TEXTURE_GEN_S);
+  glEnable(GL_TEXTURE_GEN_T);
+  glEnable(GL_TEXTURE_2D);
+}
+
+
+void 
+init_toasters (ModeInfo *mi)
+{
+  toaster_configuration *bp;
+  int wire = MI_IS_WIREFRAME(mi);
+  int i;
+
+  if (!bps) {
+    bps = (toaster_configuration *)
+      calloc (MI_NUM_SCREENS(mi), sizeof (toaster_configuration));
+    if (!bps) {
+      fprintf(stderr, "%s: out of memory\n", progname);
+      exit(1);
+    }
+
+    bp = &bps[MI_SCREEN(mi)];
+  }
+
+  bp = &bps[MI_SCREEN(mi)];
+
+  bp->glx_context = init_GL(mi);
+
+  reshape_toasters (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+  glShadeModel(GL_SMOOTH);
+
+  glEnable(GL_DEPTH_TEST);
+  glEnable(GL_NORMALIZE);
+  glEnable(GL_CULL_FACE);
+
+  if (!wire)
+    {
+      GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
+/*      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
+      GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
+      GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
+      GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
+
+      glEnable(GL_LIGHTING);
+      glEnable(GL_LIGHT0);
+      glEnable(GL_DEPTH_TEST);
+      glEnable(GL_CULL_FACE);
+
+      glLightfv(GL_LIGHT0, GL_POSITION, pos);
+      glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
+      glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
+      glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
+    }
+
+  if (!wire && do_texture)
+    load_textures (mi);
+
+  bp->user_trackball = gltrackball_init ();
+  auto_track_init (mi);
+
+  bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
+  for (i = 0; i < countof(all_objs); i++)
+    bp->dlists[i] = glGenLists (1);
+
+  for (i = 0; i < countof(all_objs); i++)
+    {
+      struct gllist *gll = *all_objs[i];
+      if (wire)
+        gll->primitive = GL_LINE_LOOP;
+
+      glNewList (bp->dlists[i], GL_COMPILE);
+
+      glMatrixMode(GL_MODELVIEW);
+      glPushMatrix();
+      glMatrixMode(GL_TEXTURE);
+      glPushMatrix();
+      glMatrixMode(GL_MODELVIEW);
+
+      glRotatef (-90, 1, 0, 0);
+      glRotatef (180, 0, 0, 1);
+      glScalef (6, 6, 6);
+
+      glBindTexture (GL_TEXTURE_2D, 0);
+
+      if (i == BASE_TOASTER)
+        {
+          GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
+          GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
+          GLfloat shiny    = 20.0;
+          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+          glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
+          glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
+          if (do_texture)
+            glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
+          glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+          glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+        }
+      else if (i == TOAST || i == TOAST_BITTEN)
+        {
+          GLfloat color[4] = {0.80, 0.80, 0.00, 1.0};
+          GLfloat spec[4]  = {0.00, 0.00, 0.00, 1.0};
+          GLfloat shiny    = 0.0;
+          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+          glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
+          glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
+          if (do_texture)
+            glBindTexture (GL_TEXTURE_2D, bp->toast_texture);
+          glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+          glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+          glMatrixMode(GL_TEXTURE);
+          glTranslatef(0.5, 0.5, 0);
+          glMatrixMode(GL_MODELVIEW);
+        }
+      else if (i == SLOTS || i == HANDLE_SLOT)
+        {
+          GLfloat color[4] = {0.30, 0.30, 0.40, 1.0};
+          GLfloat spec[4]  = {0.40, 0.40, 0.70, 1.0};
+          GLfloat shiny    = 128.0;
+          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+          glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
+          glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
+        }
+      else if (i == HANDLE)
+        {
+          GLfloat color[4] = {0.80, 0.10, 0.10, 1.0};
+          GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
+          GLfloat shiny    = 20.0;
+          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+          glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
+          glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
+        }
+      else if (i == KNOB)
+        {
+          GLfloat color[4] = {0.80, 0.10, 0.10, 1.0};
+          GLfloat spec[4]  = {0.00, 0.00, 0.00, 1.0};
+          GLfloat shiny    = 0.0;
+          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+          glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
+          glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
+        }
+      else if (i == JET || i == JET_WING)
+        {
+          GLfloat color[4] = {0.70, 0.70, 0.70, 1.0};
+          GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
+          GLfloat shiny    = 20.0;
+          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+          glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
+          glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
+        }
+      else if (i == BASE)
+        {
+          GLfloat color[4] = {0.50, 0.50, 0.50, 1.0};
+          GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
+          GLfloat shiny    = 20.0;
+          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+          glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
+          glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
+        }
+      else
+        {
+          GLfloat color[4] = {1.00, 1.00, 1.00, 1.00};
+          GLfloat spec[4]  = {1.00, 1.00, 1.00, 1.0};
+          GLfloat shiny    = 128.0;
+          glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+          glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,            spec);
+          glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS,           shiny);
+        }
+
+      renderList (gll);
+
+      glMatrixMode(GL_TEXTURE);
+      glPopMatrix();
+      glMatrixMode(GL_MODELVIEW);
+      glPopMatrix();
+
+      glEndList ();
+    }
+
+  bp->nfloaters = ntoasters + nslices;
+  bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
+
+  for (i = 0; i < bp->nfloaters; i++)
+    {
+      floater *f = &bp->floaters[i];
+      /* arrange the list so that half the toasters are in front of bread,
+         and half are behind. */
+      f->toaster_p = ((i < ntoasters / 2) ||
+                      (i >= (nslices + (ntoasters / 2))));
+      reset_floater (mi, f);
+
+      /* Position the first generation randomly, but make sure they aren't
+         on screen yet (until we rotate the view into position.)
+       */
+      {
+        GLfloat min = -GRID_DEPTH/2;
+        GLfloat max =  GRID_DEPTH/3.5;
+        f->z = frand (max - min) + min;
+      }
+    }
+}
+
+
+static void
+draw_origin (ModeInfo *mi)
+{
+# ifdef DEBUG
+/*  toaster_configuration *bp = &bps[MI_SCREEN(mi)];*/
+
+  if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
+  if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
+
+  glPushMatrix();
+  glScalef (5, 5, 5);
+  glBegin(GL_LINES);
+  glVertex3f(-1, 0, 0); glVertex3f(1, 0, 0);
+  glVertex3f(0, -1, 0); glVertex3f(0, 1, 0);
+  glVertex3f(0, 0, -1); glVertex3f(0, 0, 1);
+  glEnd();
+  glPopMatrix();
+
+  if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
+  if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
+# endif /* DEBUG */
+}
+
+
+static void
+draw_grid (ModeInfo *mi)
+{
+# ifdef DEBUG
+/*  toaster_configuration *bp = &bps[MI_SCREEN(mi)];*/
+
+  if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
+  if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
+  glPushMatrix();
+  glBegin(GL_LINE_LOOP);
+  glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, 0);
+  glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2, 0);
+  glVertex3f( GRID_SIZE/2,  GRID_SIZE/2, 0);
+  glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, 0);
+  glEnd();
+  glBegin(GL_LINE_LOOP);
+  glVertex3f(-GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
+  glVertex3f(-GRID_SIZE/2, GRID_SIZE/2,  GRID_DEPTH/2);
+  glVertex3f( GRID_SIZE/2, GRID_SIZE/2,  GRID_DEPTH/2);
+  glVertex3f( GRID_SIZE/2, GRID_SIZE/2, -GRID_DEPTH/2);
+  glEnd();
+  glBegin(GL_LINE_LOOP);
+  glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
+  glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
+  glVertex3f( GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
+  glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
+  glEnd();
+  glBegin(GL_LINES);
+  glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
+  glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2, -GRID_DEPTH/2);
+  glVertex3f(-GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
+  glVertex3f(-GRID_SIZE/2,  GRID_SIZE/2,  GRID_DEPTH/2);
+  glVertex3f( GRID_SIZE/2, -GRID_SIZE/2, -GRID_DEPTH/2);
+  glVertex3f( GRID_SIZE/2,  GRID_SIZE/2, -GRID_DEPTH/2);
+  glVertex3f( GRID_SIZE/2, -GRID_SIZE/2,  GRID_DEPTH/2);
+  glVertex3f( GRID_SIZE/2,  GRID_SIZE/2,  GRID_DEPTH/2);
+  glEnd();
+  glPopMatrix();
+
+  if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
+  if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
+# endif /* DEBUG */
+}
+
+
+static void
+draw_floater (ModeInfo *mi, floater *f)
+{
+  toaster_configuration *bp = &bps[MI_SCREEN(mi)];
+  GLfloat n;
+
+  glFrontFace(GL_CCW);
+
+  glPushMatrix();
+  glTranslatef (f->x, f->y, f->z);
+  if (f->toaster_p)
+    {
+      glPushMatrix();
+      glRotatef (180, 0, 1, 0);
+      glCallList (bp->dlists[BASE_TOASTER]);
+      mi->polygon_count += (*all_objs[BASE_TOASTER])->points / 3;
+      glPopMatrix();
+
+      glPushMatrix();
+      glTranslatef(0, 1.01, 0);
+      n = 0.91; glScalef(n,n,n);
+      glCallList (bp->dlists[SLOTS]);
+      mi->polygon_count += (*all_objs[SLOTS])->points / 3;
+      glPopMatrix();
+
+      glPushMatrix();
+      glRotatef (180, 0, 1, 0);
+      glTranslatef(0, -0.4, -2.38);
+      n = 0.33; glScalef(n,n,n);
+      glCallList (bp->dlists[HANDLE_SLOT]);
+      mi->polygon_count += (*all_objs[HANDLE_SLOT])->points / 3;
+      glPopMatrix();
+
+      glPushMatrix();
+      glTranslatef(0, -1.1, 3);
+      n = 0.3; glScalef (n,n,n);
+      glTranslatef(0, f->handle_pos * 4.8, 0);
+      glCallList (bp->dlists[HANDLE]);
+      mi->polygon_count += (*all_objs[HANDLE])->points / 3;
+      glPopMatrix();
+
+      glPushMatrix();
+      glRotatef (180, 0, 1, 0);
+      glTranslatef(0, -1.1, -3);     /* where the handle is */
+      glTranslatef (1, -0.4, 0);     /* down and to the left */
+      n = 0.08; glScalef (n,n,n);
+      glRotatef (f->knob_pos, 0, 0, 1);
+      glCallList (bp->dlists[KNOB]);
+      mi->polygon_count += (*all_objs[KNOB])->points / 3;
+      glPopMatrix();
+
+      glPushMatrix();
+      glRotatef (180, 0, 1, 0);
+      glTranslatef (0, -2.3, 0);
+      glCallList (bp->dlists[BASE]);
+      mi->polygon_count += (*all_objs[BASE])->points / 3;
+      glPopMatrix();
+
+      glPushMatrix();
+      glTranslatef(-4.8, 0, 0);
+      glCallList (bp->dlists[JET_WING]);
+      mi->polygon_count += (*all_objs[JET_WING])->points / 3;
+      glScalef (0.5, 0.5, 0.5);
+      glTranslatef (-2, -1, 0);
+      glCallList (bp->dlists[JET]);
+      mi->polygon_count += (*all_objs[JET])->points / 3;
+      glPopMatrix();
+
+      glPushMatrix();
+      glTranslatef(4.8, 0, 0);
+      glScalef(-1, 1, 1);
+      glFrontFace(GL_CW);
+      glCallList (bp->dlists[JET_WING]);
+      mi->polygon_count += (*all_objs[JET_WING])->points / 3;
+      glScalef (0.5, 0.5, 0.5);
+      glTranslatef (-2, -1, 0);
+      glCallList (bp->dlists[JET]);
+      mi->polygon_count += (*all_objs[JET])->points / 3;
+      glFrontFace(GL_CCW);
+      glPopMatrix();
+
+      if (f->loaded)
+        {
+          glPushMatrix();
+          glTranslatef(0, 1.01, 0);
+          n = 0.91; glScalef(n,n,n);
+          glRotatef (90, 0, 0, 1);
+          glRotatef (90, 0, 1, 0);
+          glTranslatef(0, 0, -0.95);
+          glTranslatef(0, 0.72, 0);
+          if (f->loaded & 1)
+            {
+              glCallList (bp->dlists[TOAST]);
+              mi->polygon_count += (*all_objs[TOAST])->points / 3;
+            }
+          glTranslatef(0, -1.46, 0);
+          if (f->loaded & 2)
+            {
+              glCallList (bp->dlists[TOAST]);
+              mi->polygon_count += (*all_objs[TOAST])->points / 3;
+            }
+          glPopMatrix();
+        }
+    }
+  else
+    {
+      glScalef (0.7, 0.7, 0.7);
+      if (f->toast_type == 0)
+        {
+          glCallList (bp->dlists[TOAST]);
+          mi->polygon_count += (*all_objs[TOAST])->points / 3;
+        }
+      else
+        {
+          glCallList (bp->dlists[TOAST_BITTEN]);
+          mi->polygon_count += (*all_objs[TOAST_BITTEN])->points / 3;
+        }
+    }
+
+  glPopMatrix();
+}
+
+
+
+void
+draw_toasters (ModeInfo *mi)
+{
+  toaster_configuration *bp = &bps[MI_SCREEN(mi)];
+  Display *dpy = MI_DISPLAY(mi);
+  Window window = MI_WINDOW(mi);
+  int i;
+
+  if (!bp->glx_context)
+    return;
+
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glPushMatrix ();
+  glRotatef(bp->view_x, 1, 0, 0);
+  glRotatef(bp->view_y, 0, 1, 0);
+  gltrackball_rotate (bp->user_trackball);
+
+
+#if 0
+  {
+    floater F;
+    F.toaster_p = 0;
+    F.toast_type = 1;
+    F.handle_pos = 0;
+    F.knob_pos = -90;
+    F.loaded = 3;
+    F.x = F.y = F.z = 0;
+    F.dx = F.dy = F.dz = 0;
+
+    glScalef(2,2,2);
+    if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
+    if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
+    glBegin(GL_LINES);
+    glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
+    glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
+    glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
+    glEnd();
+    if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
+    if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
+
+    draw_floater (mi, &F);
+    glPopMatrix ();
+    if (mi->fps_p) do_fps (mi);
+    glFinish();
+    glXSwapBuffers(dpy, window);
+    return;
+  }
+#endif
+
+  glScalef (0.5, 0.5, 0.5);
+  draw_origin (mi);
+  glTranslatef (0, 0, -GRID_DEPTH/2.5);
+  draw_grid (mi);
+
+  mi->polygon_count = 0;
+  for (i = 0; i < bp->nfloaters; i++)
+    {
+      floater *f = &bp->floaters[i];
+      draw_floater (mi, f);
+      tick_floater (mi, f);
+    }
+  auto_track (mi);
+
+  glPopMatrix ();
+
+  if (mi->fps_p) do_fps (mi);
+  glFinish();
+
+  glXSwapBuffers(dpy, window);
+}
+
+#endif /* USE_GL */