/* flyingtoasters, Copyright (c) 2003-2018 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.
*/
#define DEFAULTS "*delay: 30000 \n" \
"*showFPS: False \n" \
"*wireframe: False \n" \
/* #define DEBUG */
# define release_toasters 0
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
#define DEF_SPEED "1.0"
#define DEF_NTOASTERS "20"
#define DEF_NSLICES "25"
#define DEF_TEXTURE "True"
#undef BELLRAND
#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
#include "xlockmore.h"
#include "gltrackball.h"
#include "ximage-loader.h"
#include <ctype.h>
#define HAVE_TEXTURE
#ifdef HAVE_TEXTURE
# include "images/gen/chromesphere_png.h"
# include "images/gen/toast_png.h"
#endif /* HAVE_TEXTURE */
#ifdef USE_GL /* whole file */
#include "gllist.h"
extern const struct gllist
*toaster, *toaster_base, *toaster_handle, *toaster_handle2, *toaster_jet,
*toaster_knob, *toaster_slots, *toaster_wing, *toast, *toast2;
static const struct gllist * const *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 const 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;
int track_tick;
GLuint *dlists;
# ifdef HAVE_TEXTURE
GLuint chrome_texture;
GLuint toast_texture;
# endif
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, "True" },
{"+texture", ".texture", XrmoptionNoArg, "False" },
};
static argtype vars[] = {
{&speed, "speed", "Speed", DEF_SPEED, t_Float},
{&ntoasters, "ntoasters", "Count", DEF_NTOASTERS, t_Int},
{&nslices, "nslices", "Count", DEF_NSLICES, t_Int},
{&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
};
ENTRYPOINT ModeSpecOpt toasters_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)
{
if (++bp->track_tick < 200/speed) return;
bp->track_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
*/
ENTRYPOINT void
reshape_toasters (ModeInfo *mi, int width, int height)
{
GLfloat h = (GLfloat) height / (GLfloat) width;
int y = 0;
if (width > height * 5) { /* tiny window: show middle */
height = width * 9/16;
y = -height/2;
h = height / (GLfloat) width;
}
glViewport (0, y, (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);
# ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
{
int o = (int) current_device_rotation();
if (o != 0 && o != 180 && o != -180)
glScalef (1/h, 1/h, 1/h);
}
# endif
glClear(GL_COLOR_BUFFER_BIT);
}
ENTRYPOINT Bool
toasters_handle_event (ModeInfo *mi, XEvent *event)
{
toaster_configuration *bp = &bps[MI_SCREEN(mi)];
if (gltrackball_event_handler (event, bp->user_trackball,
MI_WIDTH (mi), MI_HEIGHT (mi),
&bp->button_down_p))
return True;
return False;
}
#ifdef HAVE_TEXTURE
static void
load_textures (ModeInfo *mi)
{
toaster_configuration *bp = &bps[MI_SCREEN(mi)];
XImage *xi;
xi = image_data_to_ximage (mi->dpy, mi->xgwa.visual,
chromesphere_png, sizeof(chromesphere_png));
clear_gl_error();
#ifndef HAVE_JWZGLES /* No SPHERE_MAP yet */
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, xi->data);
check_gl_error("texture");
XDestroyImage (xi);
xi = 0;
#endif
xi = image_data_to_ximage (mi->dpy, mi->xgwa.visual,
toast_png, sizeof(toast_png));
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_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
xi->width, xi->height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, xi->data);
check_gl_error("texture");
XDestroyImage (xi);
xi = 0;
}
#endif /* HAVE_TEXTURE */
ENTRYPOINT void
init_toasters (ModeInfo *mi)
{
toaster_configuration *bp;
int wire = MI_IS_WIREFRAME(mi);
int i;
MI_INIT (mi, bps);
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);
}
# ifdef HAVE_TEXTURE
if (!wire && do_texture)
load_textures (mi);
# endif
bp->user_trackball = gltrackball_init (False);
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++)
{
const struct gllist *gll = *all_objs[i];
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);
glDisable (GL_TEXTURE_2D);
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);
#ifdef HAVE_TEXTURE
if (do_texture)
{
#ifndef HAVE_JWZGLES /* No SPHERE_MAP yet */
glEnable (GL_TEXTURE_2D);
glEnable (GL_TEXTURE_GEN_S);
glEnable (GL_TEXTURE_GEN_T);
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);
#endif
}
# endif
}
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);
#ifdef HAVE_TEXTURE
if (do_texture)
{
glEnable (GL_TEXTURE_2D);
glEnable (GL_TEXTURE_GEN_S);
glEnable (GL_TEXTURE_GEN_T);
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);
}
# endif
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, wire);
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();
}
ENTRYPOINT 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;
glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix ();
glRotatef(current_device_rotation(), 0, 0, 1);
glRotatef(bp->view_x, 1, 0, 0);
glRotatef(bp->view_y, 0, 1, 0);
/* Rotate the scene around a point that's a little deeper in. */
glTranslatef (0, 0, -50);
gltrackball_rotate (bp->user_trackball);
glTranslatef (0, 0, 50);
#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);
}
ENTRYPOINT void
free_toasters (ModeInfo *mi)
{
toaster_configuration *bp = &bps[MI_SCREEN(mi)];
int i;
if (!bp->glx_context) return;
glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
if (bp->dlists) free (bp->dlists);
if (bp->floaters) free (bp->floaters);
if (bp->user_trackball) gltrackball_free (bp->user_trackball);
for (i = 0; i < countof(all_objs); i++)
if (glIsList(bp->dlists[i])) glDeleteLists(bp->dlists[i], 1);
if (bp->toast_texture) glDeleteTextures (1, &bp->toast_texture);
# ifndef HAVE_JWZGLES
if (bp->chrome_texture) glDeleteTextures (1, &bp->chrome_texture);
# endif
}
XSCREENSAVER_MODULE_2 ("FlyingToasters", flyingtoasters, toasters)
#endif /* USE_GL */