X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fgears.c;h=added7c65b840fb4fc806e38873ef7307c68dff9;hp=bd0bb4e6092528a04d35a806a56d6a367e28e0fd;hb=488f2fa8fbdbc77e91a70da2962d73af49e6cace;hpb=c8c6deae79b408cffbc88043c766b3bc12cf0f13 diff --git a/hacks/glx/gears.c b/hacks/glx/gears.c index bd0bb4e6..added7c6 100644 --- a/hacks/glx/gears.c +++ b/hacks/glx/gears.c @@ -1,457 +1,454 @@ -/* -*- Mode: C; tab-width: 4 -*- */ -/* gears --- 3D gear wheels */ - -#if 0 -static const char sccsid[] = "@(#)gears.c 4.07 97/11/24 xlockmore"; -#endif - -/*- - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, - * provided that the above copyright notice appear in all copies and that - * both that copyright notice and this permission notice appear in - * supporting documentation. +/* gears, Copyright (c) 2007 Jamie Zawinski * - * This file is provided AS IS with no warranties of any kind. The author - * shall have no liability with respect to the infringement of copyrights, - * trade secrets or any patents by this file or any part thereof. In no - * event will the author be liable for any lost revenue or profits or - * other special, indirect and consequential damages. + * 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. * - * Revision History: - * 09-Feb-01: "Planetary" gear system added by jwz@jwz.org. - * 10-May-97: Compatible with xscreensaver - * 22-Mar-97: Added support for -mono mode, and monochrome X servers. - * Ed Mackey, emackey@netaxs.com - * 13-Mar-97: Memory leak fix by Tom Schmidt - * 1996: "written" by Danny Sung - * Based on 3-D gear wheels by Brian Paul which is in the public domain. + * Originally written by Brian Paul in 1996 or earlier; + * rewritten by jwz in Nov 2007. */ -/*- - * PURIFY 3.0a on SunOS4 reports an unitialized memory read on each of - * the glCallList() functions below when using MesaGL 2.1. This has - * been fixed in MesaGL 2.2 and later releases. - */ +#define DEFAULTS "*delay: 30000 \n" \ + "*count: 0 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ -#ifdef STANDALONE -# define DEFAULTS "*count: 1000000 \n" \ - "*cycles: 2 \n" \ - "*delay: 20000 \n" \ - "*showFPS: False \n" \ - "*wireframe: False \n" -# include "xlockmore.h" /* from the xscreensaver distribution */ # define refresh_gears 0 -#else /* !STANDALONE */ -# include "xlock.h" /* from the xlockmore distribution */ -#endif /* !STANDALONE */ - -#ifdef USE_GL +# define release_gears 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) +#include "xlockmore.h" +#include "involute.h" +#include "normals.h" +#include "tube.h" #include "rotator.h" #include "gltrackball.h" +#include -#undef countof -#define countof(x) (sizeof((x))/sizeof((*x))) +#ifdef USE_GL /* whole file */ + +#undef BELLRAND +#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) + +#define DEF_SPIN "True" +#define DEF_WANDER "True" +#define DEF_SPEED "1.0" + +typedef struct { + GLXContext *glx_context; + rotator *rot; + trackball_state *trackball; + Bool button_down_p; + Bool planetary_p; + + int ngears; + gear **gears; + + GLuint armature_dlist; + int armature_polygons; + + struct { GLfloat x1, y1, x2, y2; } bbox; -#define DEF_MODE "random" -#define DEF_SPIN "True" +} gears_configuration; -static char *mode_str; -static int spin; +static gears_configuration *bps = NULL; + +static Bool do_spin; +static GLfloat speed; +static Bool do_wander; static XrmOptionDescRec opts[] = { - {"-mode", ".gears.mode", XrmoptionSepArg, 0 }, - {"-planetary", ".gears.mode", XrmoptionNoArg, "planetary" }, - {"-simple", ".gears.mode", XrmoptionNoArg, "simple" }, - {"-spin", ".gears.spin", XrmoptionNoArg, "true" }, - {"+spin", ".gears.spin", XrmoptionNoArg, "false" }, + { "-spin", ".spin", XrmoptionNoArg, "True" }, + { "+spin", ".spin", XrmoptionNoArg, "False" }, + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-wander", ".wander", XrmoptionNoArg, "True" }, + { "+wander", ".wander", XrmoptionNoArg, "False" }, }; static argtype vars[] = { - {&mode_str, "mode", "Mode", DEF_MODE, t_String}, - {&spin, "spin", "Spin", DEF_SPIN, t_Bool}, + {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool}, + {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool}, + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, }; ENTRYPOINT ModeSpecOpt gears_opts = {countof(opts), opts, countof(vars), vars, NULL}; -#ifdef USE_MODULES -ModStruct gears_description = -{"gears", "init_gears", "draw_gears", "release_gears", - "draw_gears", "init_gears", NULL, &gears_opts, - 1000, 1, 2, 1, 4, 1.0, "", - "Shows GL's gears", 0, NULL}; -#endif +/* Window management, etc + */ +ENTRYPOINT void +reshape_gears (ModeInfo *mi, int width, int height) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; -#define SMOOTH_TUBE /* whether to have smooth or faceted tubes */ + glViewport (0, 0, (GLint) width, (GLint) height); -#ifdef SMOOTH_TUBE -# define TUBE_FACES 20 /* how densely to render tubes */ -#else -# define TUBE_FACES 6 -#endif + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective (30.0, 1/h, 1.0, 100.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( 0.0, 0.0, 30.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); -typedef struct { - GLuint gear1, gear2, gear3; - GLuint gear_inner, gear_outer; - GLuint armature; - GLfloat angle; - GLXContext *glx_context; - Window window; - rotator *rot; - trackball_state *trackball; - Bool button_down_p; - int planetary_p; -} gearsstruct; + glClear(GL_COLOR_BUFFER_BIT); +} -static gearsstruct *gears = NULL; -/*- - * Draw a gear wheel. You'll probably want to call this function when - * building a display list since we do a lot of trig here. - * - * Input: inner_radius - radius of hole at center - * outer_radius - radius at center of teeth - * width - width of gear - * teeth - number of teeth - * tooth_depth - depth of tooth - * wire - true for wireframe mode - */ -static void -gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, - GLint teeth, GLfloat tooth_depth, Bool wire, Bool invert) +ENTRYPOINT Bool +gears_handle_event (ModeInfo *mi, XEvent *event) { - GLint i; - GLfloat r0, r1, r2; - GLfloat angle, da; - GLfloat u, v, len; + gears_configuration *bp = &bps[MI_SCREEN(mi)]; - if (!invert) - { - r0 = inner_radius; - r1 = outer_radius - tooth_depth / 2.0; - r2 = outer_radius + tooth_depth / 2.0; - glFrontFace(GL_CCW); - } - else - { - r0 = outer_radius; - r2 = inner_radius + tooth_depth / 2.0; - r1 = outer_radius - tooth_depth / 2.0; - glFrontFace(GL_CW); - } - - da = 2.0 * M_PI / teeth / 4.0; - - glShadeModel(GL_FLAT); - - /* This subroutine got kind of messy when I added all the checks - * for wireframe mode. A much cleaner solution that I sometimes - * use is to have a variable hold the value GL_LINE_LOOP when - * in wireframe mode, or hold the value GL_POLYGON otherwise. - * Then I just call glBegin(that_variable), give my polygon - * coordinates, and glEnd(). Pretty neat eh? Too bad I couldn't - * integrate that trick here. - * --Ed. - */ - - if (!wire) - glNormal3f(0.0, 0.0, 1.0); - - /* draw front face */ - if (!wire) - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= teeth; i++) { - if (wire) - glBegin(GL_LINES); - angle = i * 2.0 * M_PI / teeth; - glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); - if (!wire) { - glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); - } else { - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); - glVertex3f(r1 * cos(angle + 4 * da), r1 * sin(angle + 4 * da), width * 0.5); - glEnd(); - } - } - if (!wire) - glEnd(); - - /* draw front sides of teeth */ - if (!wire) - glBegin(GL_QUADS); - da = 2.0 * M_PI / teeth / 4.0; - for (i = 0; i < teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - - if (wire) - glBegin(GL_LINE_LOOP); - glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); - if (wire) - glEnd(); - } - if (!wire) - glEnd(); - - - if (!wire) - glNormal3f(0.0, 0.0, -1.0); - - /* draw back face */ - if (!wire) - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - if (wire) - glBegin(GL_LINES); - glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); - if (!wire) { - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); - } else { - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); - glVertex3f(r1 * cos(angle + 4 * da), r1 * sin(angle + 4 * da), -width * 0.5); - glEnd(); - } - } - if (!wire) - glEnd(); - - /* draw back sides of teeth */ - if (!wire) - glBegin(GL_QUADS); - da = 2.0 * M_PI / teeth / 4.0; - for (i = 0; i < teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - - if (wire) - glBegin(GL_LINE_LOOP); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); - if (wire) - glEnd(); - } - if (!wire) - glEnd(); - - - /* draw outward faces of teeth */ - if (!wire) - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - - if(!invert) { - u = r2 * cos(angle + da) - r1 * cos(angle); - v = r2 * sin(angle + da) - r1 * sin(angle); - } else { - u = r2 * cos(angle + da + M_PI/2) - r1 * cos(angle + M_PI/2); - v = r2 * sin(angle + da + M_PI/2) - r1 * sin(angle + M_PI/2); - } + if (event->xany.type == ButtonPress && + event->xbutton.button == Button1) + { + bp->button_down_p = True; + gltrackball_start (bp->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 == ButtonPress && + (event->xbutton.button == Button4 || + event->xbutton.button == Button5)) + { + gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10, + !!event->xbutton.state); + return True; + } + else if (event->xany.type == MotionNotify && + bp->button_down_p) + { + gltrackball_track (bp->trackball, + event->xmotion.x, event->xmotion.y, + MI_WIDTH (mi), MI_HEIGHT (mi)); + return True; + } - len = sqrt(u * u + v * v); - u /= len; - v /= len; - glNormal3f(v, -u, 0.0); + return False; +} - if (wire) - glBegin(GL_LINES); - glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); - if(!invert) - glNormal3f(cos(angle), sin(angle), 0.0); - else - glNormal3f(cos(angle + M_PI/2), sin(angle + M_PI/2), 0.0); +static void +free_gear (gear *g) +{ + if (g->dlist) + glDeleteLists (g->dlist, 1); + free (g); +} - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5); - if(!invert) { - u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); - v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); - } else { - u = r1 * cos(angle + 3 * da + M_PI/2) - r2 * cos(angle + 2 * da + M_PI/2); - v = r1 * sin(angle + 3 * da + M_PI/2) - r2 * sin(angle + 2 * da + M_PI/2); - } +/* Create and return a new gear sized for placement next to or on top of + the given parent gear (if any.) Returns 0 if out of memory. + [Mostly lifted from pinion.c] + */ +static gear * +new_gear (ModeInfo *mi, gear *parent) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + gear *g = (gear *) calloc (1, sizeof (*g)); + static unsigned long id = 0; /* only used in debugging output */ - glNormal3f(v, -u, 0.0); + if (!g) return 0; + g->id = ++id; - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); + /* Pick the size of the teeth. + */ + if (parent) /* adjascent gears need matching teeth */ + { + g->tooth_w = parent->tooth_w; + g->tooth_h = parent->tooth_h; + g->tooth_slope = -parent->tooth_slope; + } + else /* gears that begin trains get any size they want */ + { + g->tooth_w = 0.007 * (1.0 + BELLRAND(4.0)); + g->tooth_h = 0.005 * (1.0 + BELLRAND(8.0)); +/* + g->tooth_slope = ((random() % 8) + ? 0 + : 0.5 + BELLRAND(1)); + */ + } - if (!invert) - glNormal3f(cos(angle), sin(angle), 0.0); - else - glNormal3f(cos(angle + M_PI/2), sin(angle + M_PI/2), 0.0); - - if (wire) - glEnd(); - } - - if (!wire) { - glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5); - glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5); - glEnd(); - } - if (!wire) - glShadeModel(GL_SMOOTH); - - /* draw inside radius cylinder */ - if (!wire) - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - if (wire) - glBegin(GL_LINES); - - if (!invert) - glNormal3f(-cos(angle), -sin(angle), 0.0); - else - glNormal3f(cos(angle), sin(angle), 0.0); - - glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); - if (wire) { - glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); - glVertex3f(r0 * cos(angle + 4 * da), r0 * sin(angle + 4 * da), -width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); - glVertex3f(r0 * cos(angle + 4 * da), r0 * sin(angle + 4 * da), width * 0.5); - glEnd(); - } - } - if (!wire) - glEnd(); + /* Pick the number of teeth, and thus, the radius. + */ + { + double c; -} + if (!parent || bp->ngears > 4) + g->nteeth = 5 + BELLRAND (20); + else + g->nteeth = parent->nteeth * (0.5 + BELLRAND(2)); + c = g->nteeth * g->tooth_w * 2; /* circumference = teeth + gaps */ + g->r = c / (M_PI * 2); /* c = 2 pi r */ + } -static void -unit_tube (Bool wire) -{ - int i; - int faces = TUBE_FACES; - GLfloat step = M_PI * 2 / faces; - GLfloat th; - int z = 0; + g->thickness = g->tooth_w + frand (g->r); + g->thickness2 = g->thickness * 0.7; + g->thickness3 = g->thickness; - /* side walls + /* Colorize */ - glFrontFace(GL_CCW); + g->color[0] = 0.5 + frand(0.5); + g->color[1] = 0.5 + frand(0.5); + g->color[2] = 0.5 + frand(0.5); + g->color[3] = 1.0; + + g->color2[0] = g->color[0] * 0.85; + g->color2[1] = g->color[1] * 0.85; + g->color2[2] = g->color[2] * 0.85; + g->color2[3] = g->color[3]; + + + /* Decide on shape of gear interior: + - just a ring with teeth; + - that, plus a thinner in-set "plate" in the middle; + - that, plus a thin raised "lip" on the inner plate; + - or, a wide lip (really, a thicker third inner plate.) + */ + if ((random() % 10) == 0) + { + /* inner_r can go all the way in; there's no inset disc. */ + g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8); + g->inner_r2 = 0; + g->inner_r3 = 0; + } + else + { + /* inner_r doesn't go in very far; inner_r2 is an inset disc. */ + g->inner_r = (g->r * 0.5) + frand((g->r - g->tooth_h) * 0.4); + g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5); + g->inner_r3 = 0; -# ifdef SMOOTH_TUBE - glBegin(wire ? GL_LINES : GL_QUAD_STRIP); -# else - glBegin(wire ? GL_LINES : GL_QUADS); -# endif + if (g->inner_r2 > (g->r * 0.2)) + { + int nn = (random() % 10); + if (nn <= 2) + g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2); + else if (nn <= 7 && g->inner_r2 >= 0.1) + g->inner_r3 = g->inner_r2 - 0.01; + } + } - for (i = 0, th = 0; i <= faces; i++) + /* If we have three discs, sometimes make the middle disc be spokes. + */ + if (g->inner_r3 && ((random() % 5) == 0)) { - GLfloat x = cos (th); - GLfloat y = sin (th); - glNormal3f(x, 0, y); - glVertex3f(x, 0.0, y); - glVertex3f(x, 1.0, y); - th += step; - -# ifndef SMOOTH_TUBE - x = cos (th); - y = sin (th); - glVertex3f(x, 1.0, y); - glVertex3f(x, 0.0, y); -# endif + g->spokes = 2 + BELLRAND (5); + g->spoke_thickness = 1 + frand(7.0); + if (g->spokes == 2 && g->spoke_thickness < 2) + g->spoke_thickness += 1; } - glEnd(); - /* End caps + /* Sometimes add little nubbly bits, if there is room. */ - for (z = 0; z <= 1; z++) + if (g->nteeth > 5) { - glFrontFace(z == 0 ? GL_CCW : GL_CW); - glNormal3f(0, (z == 0 ? -1 : 1), 0); - glBegin(wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN); - if (! wire) glVertex3f(0, z, 0); - for (i = 0, th = 0; i <= faces; i++) + double size = 0; + involute_biggest_ring (g, 0, &size, 0); + if (size > g->r * 0.2 && (random() % 5) == 0) { - GLfloat x = cos (th); - GLfloat y = sin (th); - glVertex3f(x, z, y); - th += step; + g->nubs = 1 + (random() % 16); + if (g->nubs > 8) g->nubs = 1; } - glEnd(); } -} + if (g->inner_r3 > g->inner_r2) abort(); + if (g->inner_r2 > g->inner_r) abort(); + if (g->inner_r > g->r) abort(); -static void -tube (GLfloat x1, GLfloat y1, GLfloat z1, - GLfloat x2, GLfloat y2, GLfloat z2, - GLfloat diameter, GLfloat cap_size, - Bool wire) -{ - GLfloat length, angle, a, b, c; + /* Decide how complex the polygon model should be. + */ + { + double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */ + if (pix <= 2.5) g->size = INVOLUTE_SMALL; + else if (pix <= 3.5) g->size = INVOLUTE_MEDIUM; + else g->size = INVOLUTE_LARGE; + } - if (diameter <= 0) abort(); + g->base_p = !parent; - a = (x2 - x1); - b = (y2 - y1); - c = (z2 - z1); + return g; +} - length = sqrt (a*a + b*b + c*c); - angle = acos (a / length); - glPushMatrix(); - glTranslatef(x1, y1, z1); - glScalef (length, length, length); +/* Given a newly-created gear, place it next to its parent in the scene, + with its teeth meshed and the proper velocity. Returns False if it + didn't work. (Call this a bunch of times until either it works, or + you decide it's probably not going to.) + [Mostly lifted from pinion.c] + */ +static Bool +place_gear (ModeInfo *mi, gear *g, gear *parent) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; - if (c == 0 && b == 0) - glRotatef (angle / (M_PI / 180), 0, 1, 0); + /* Compute this gear's velocity. + */ + if (! parent) + { + g->ratio = 0.8 + BELLRAND(0.4); /* 0.8-1.2 = 8-12rpm @ 60fps */ + g->th = 1; /* not 0 */ + } else - glRotatef (angle / (M_PI / 180), 0, -c, b); + { + /* Gearing ratio is the ratio of the number of teeth to previous gear + (which is also the ratio of the circumferences.) + */ + g->ratio = (double) parent->nteeth / (double) g->nteeth; + + /* Set our initial rotation to match that of the previous gear, + multiplied by the gearing ratio. (This is finessed later, + once we know the exact position of the gear relative to its + parent.) + */ + g->th = -(parent->th * g->ratio); + + if (g->nteeth & 1) /* rotate 1/2 tooth-size if odd number of teeth */ + { + double off = (180.0 / g->nteeth); + if (g->th > 0) + g->th += off; + else + g->th -= off; + } + + /* ratios are cumulative for all gears in the train. */ + g->ratio *= parent->ratio; + } - glRotatef (-90, 0, 0, 1); - glScalef (diameter/length, 1, diameter/length); - /* extend the endpoints of the tube by the cap size in both directions */ - if (cap_size != 0) + if (parent) /* Place the gear next to the parent. */ { - GLfloat c = cap_size/length; - glTranslatef (0, -c, 0); - glScalef (1, 1+c+c, 1); + double r_off = parent->r + g->r; + int angle; + + angle = (random() % 360) - 180; /* -180 to +180 degrees */ + + g->x = parent->x + (cos ((double) angle * (M_PI / 180)) * r_off); + g->y = parent->y + (sin ((double) angle * (M_PI / 180)) * r_off); + g->z = parent->z; + + /* avoid accidentally changing sign of "th" in the math below. */ + g->th += (g->th > 0 ? 360 : -360); + + /* Adjust the rotation of the gear so that its teeth line up with its + parent, based on the position of the gear and the current rotation + of the parent. + */ + { + double p_c = 2 * M_PI * parent->r; /* circumference of parent */ + double g_c = 2 * M_PI * g->r; /* circumference of g */ + + double p_t = p_c * (angle/360.0); /* distance travelled along + circumference of parent when + moving "angle" degrees along + parent. */ + double g_rat = p_t / g_c; /* if travelling that distance + along circumference of g, + ratio of g's circumference + travelled. */ + double g_th = 360.0 * g_rat; /* that ratio in degrees */ + + g->th += angle + g_th; + } } - unit_tube (wire); - glPopMatrix(); + /* If the position we picked for this gear causes it to overlap + with any earlier gear in the train, give up. + */ + { + int i; + + for (i = bp->ngears-1; i >= 0; i--) + { + gear *og = bp->gears[i]; + + if (og == g) continue; + if (og == parent) continue; + if (g->z != og->z) continue; /* Ignore unless on same layer */ + + /* Collision detection without sqrt: + d = sqrt(a^2 + b^2) d^2 = a^2 + b^2 + d < r1 + r2 d^2 < (r1 + r2)^2 + */ + if (((g->x - og->x) * (g->x - og->x) + + (g->y - og->y) * (g->y - og->y)) < + ((g->r + g->tooth_h + og->r + og->tooth_h) * + (g->r + g->tooth_h + og->r + og->tooth_h))) + return False; + } + } + + return True; } -static void -ctube (GLfloat diameter, GLfloat width, Bool wire) +/* Make a new gear, place it next to its parent in the scene, + with its teeth meshed and the proper velocity. Returns the gear; + or 0 if it didn't work. (Call this a bunch of times until either + it works, or you decide it's probably not going to.) + [Mostly lifted from pinion.c] + */ +static gear * +place_new_gear (ModeInfo *mi, gear *parent) { - tube (0, 0, width/2, - 0, 0, -width/2, - diameter, 0, wire); + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + int loop_count = 0; + gear *g = 0; + + while (1) + { + loop_count++; + if (loop_count >= 100) + { + if (g) + free_gear (g); + g = 0; + break; + } + + g = new_gear (mi, parent); + if (!g) return 0; /* out of memory? */ + + if (place_gear (mi, g, parent)) + break; + } + + if (! g) return 0; + + /* We got a gear, and it is properly positioned. + Insert it in the scene. + */ + bp->gears[bp->ngears++] = g; + return g; } -static void -arm(GLfloat length, - GLfloat width1, GLfloat height1, - GLfloat width2, GLfloat height2, - Bool wire) + +static int +arm (GLfloat length, + GLfloat width1, GLfloat height1, + GLfloat width2, GLfloat height2, + Bool wire) { + int polys = 0; glShadeModel(GL_FLAT); #if 0 /* don't need these - they're embedded in other objects */ @@ -463,6 +460,7 @@ arm(GLfloat length, glVertex3f(-length/2, width1/2, -height1/2); glVertex3f(-length/2, width1/2, height1/2); glVertex3f(-length/2, -width1/2, height1/2); + polys++; glEnd(); /* draw end 2 */ @@ -473,6 +471,7 @@ arm(GLfloat length, glVertex3f(length/2, width2/2, -height2/2); glVertex3f(length/2, width2/2, height2/2); glVertex3f(length/2, -width2/2, height2/2); + polys++; glEnd(); #endif @@ -484,6 +483,7 @@ arm(GLfloat length, glVertex3f(-length/2, width1/2, -height1/2); glVertex3f( length/2, width2/2, -height2/2); glVertex3f( length/2, -width2/2, -height2/2); + polys++; glEnd(); /* draw bottom */ @@ -494,6 +494,7 @@ arm(GLfloat length, glVertex3f(-length/2, width1/2, height1/2); glVertex3f( length/2, width2/2, height2/2); glVertex3f( length/2, -width2/2, height2/2); + polys++; glEnd(); /* draw left */ @@ -504,6 +505,7 @@ arm(GLfloat length, glVertex3f(-length/2, -width1/2, height1/2); glVertex3f( length/2, -width2/2, height2/2); glVertex3f( length/2, -width2/2, -height2/2); + polys++; glEnd(); /* draw right */ @@ -514,542 +516,424 @@ arm(GLfloat length, glVertex3f(-length/2, width1/2, height1/2); glVertex3f( length/2, width2/2, height2/2); glVertex3f( length/2, width2/2, -height2/2); + polys++; glEnd(); glFrontFace(GL_CCW); + + return polys; } -static void -draw(ModeInfo * mi) +static int +ctube (GLfloat diameter, GLfloat width, Bool wire) { - gearsstruct *gp = &gears[MI_SCREEN(mi)]; - int wire = MI_IS_WIREFRAME(mi); - - if (!wire) { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } else { - glClear(GL_COLOR_BUFFER_BIT); - } - - glPushMatrix(); + tube (0, 0, width/2, + 0, 0, -width/2, + diameter, 0, + 32, True, True, wire); + return 0; /* #### */ +} - gltrackball_rotate (gp->trackball); +static void +armature (ModeInfo *mi) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); - if (spin) - { - double x, y, z; - get_rotation (gp->rot, &x, &y, &z, !gp->button_down_p); - glRotatef (x * 360, 1.0, 0.0, 0.0); - glRotatef (y * 360, 0.0, 1.0, 0.0); - glRotatef (z * 360, 0.0, 0.0, 1.0); - } + static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0}; + GLfloat shiny = 128.0; + GLfloat color[4]; - if (!gp->planetary_p) { - glPushMatrix(); - glTranslatef(-3.0, -2.0, 0.0); - glRotatef(gp->angle, 0.0, 0.0, 1.0); -/* PURIFY 4.0.1 reports an unitialized memory read on the next line when using - * MesaGL 2.2 and -mono. This has been fixed in MesaGL 2.3 and later. */ - glCallList(gp->gear1); - glPopMatrix(); + color[0] = 0.5 + frand(0.5); + color[1] = 0.5 + frand(0.5); + color[2] = 0.5 + frand(0.5); + color[3] = 1.0; - glPushMatrix(); - glTranslatef(3.1, -2.0, 0.0); - glRotatef(-2.0 * gp->angle - 9.0, 0.0, 0.0, 1.0); - glCallList(gp->gear2); - glPopMatrix(); + bp->armature_polygons = 0; - glPushMatrix(); - glTranslatef(-3.1, 4.2, 0.0); - glRotatef(-2.0 * gp->angle - 25.0, 0.0, 0.0, 1.0); - glCallList(gp->gear3); - glPopMatrix(); + bp->armature_dlist = glGenLists (1); + if (! bp->armature_dlist) + { + check_gl_error ("glGenLists"); + abort(); + } - } else { /* gp->planetary_p */ + glNewList (bp->armature_dlist, GL_COMPILE); - glScalef(0.8, 0.8, 0.8); + glMaterialfv (GL_FRONT, GL_SPECULAR, spec); + glMateriali (GL_FRONT, GL_SHININESS, shiny); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); + glColor3f (color[0], color[1], color[2]); - glPushMatrix(); - glTranslatef(0.0, 4.2, 0.0); - glRotatef(gp->angle - 7.0, 0.0, 0.0, 1.0); - glCallList(gp->gear1); - glPopMatrix(); + glPushMatrix(); - glPushMatrix(); - glRotatef(120, 0.0, 0.0, 1.0); - glTranslatef(0.0, 4.2, 0.0); - glRotatef(gp->angle - 7.0, 0.0, 0.0, 1.0); - glCallList(gp->gear2); - glPopMatrix(); + { + GLfloat s = bp->gears[0]->r * 2.7; + s = s/5.6; + glScalef (s, s, s); + } - glPushMatrix(); - glRotatef(240, 0.0, 0.0, 1.0); - glTranslatef(0.0, 4.2, 0.0); - glRotatef(gp->angle - 7.0, 0.0, 0.0, 1.0); - glCallList(gp->gear3); - glPopMatrix(); + glTranslatef (0, 0, 1.4 + bp->gears[0]->thickness); + glRotatef (30, 0, 0, 1); - glPushMatrix(); - glTranslatef(0.0, 0.0, 0.0); - glRotatef(-gp->angle, 0.0, 0.0, 1.0); - glCallList(gp->gear_inner); - glPopMatrix(); + bp->armature_polygons += ctube (0.5, 10, wire); /* center axle */ - glPushMatrix(); - glTranslatef(0.0, 0.0, 0.0); - glRotatef((gp->angle / 3.0) - 7.5, 0.0, 0.0, 1.0); - glCallList(gp->gear_outer); - glPopMatrix(); + glPushMatrix(); + glTranslatef(0.0, 4.2, -1); + bp->armature_polygons += ctube (0.5, 3, wire); /* axle 1 */ + glTranslatef(0, 0, 1.8); + bp->armature_polygons += ctube (0.7, 0.7, wire); + glPopMatrix(); - glPushMatrix(); - glTranslatef(0.0, 0.0, 0.0); - glCallList(gp->armature); - glPopMatrix(); - } + glPushMatrix(); + glRotatef(120, 0.0, 0.0, 1.0); + glTranslatef(0.0, 4.2, -1); + bp->armature_polygons += ctube (0.5, 3, wire); /* axle 2 */ + glTranslatef(0, 0, 1.8); + bp->armature_polygons += ctube (0.7, 0.7, wire); + glPopMatrix(); - glPopMatrix(); -} + glPushMatrix(); + glRotatef(240, 0.0, 0.0, 1.0); + glTranslatef(0.0, 4.2, -1); + bp->armature_polygons += ctube (0.5, 3, wire); /* axle 3 */ + glTranslatef(0, 0, 1.8); + bp->armature_polygons += ctube (0.7, 0.7, wire); + glPopMatrix(); + glTranslatef(0, 0, 1.5); /* center disk */ + bp->armature_polygons += ctube (1.5, 2, wire); + glPushMatrix(); + glRotatef(270, 0, 0, 1); + glRotatef(-10, 0, 1, 0); + glTranslatef(-2.2, 0, 0); + bp->armature_polygons += arm (4.0, 1.0, 0.5, + 2.0, 1.0, wire); /* arm 1 */ + glPopMatrix(); -/* new window size or exposure */ -ENTRYPOINT void -reshape_gears (ModeInfo *mi, int width, int height) -{ - GLfloat h = (GLfloat) height / (GLfloat) width; + glPushMatrix(); + glRotatef(30, 0, 0, 1); + glRotatef(-10, 0, 1, 0); + glTranslatef(-2.2, 0, 0); + bp->armature_polygons += arm (4.0, 1.0, 0.5, + 2.0, 1.0, wire); /* arm 2 */ + glPopMatrix(); - glViewport(0, 0, (GLint) width, (GLint) height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glTranslatef(0.0, 0.0, -40.0); + glPushMatrix(); + glRotatef(150, 0, 0, 1); + glRotatef(-10, 0, 1, 0); + glTranslatef(-2.2, 0, 0); + bp->armature_polygons += arm (4.0, 1.0, 0.5, + 2.0, 1.0, wire); /* arm 3 */ + glPopMatrix(); - /* The depth buffer will be cleared, if needed, before the - * next frame. Right now we just want to black the screen. - */ - glClear(GL_COLOR_BUFFER_BIT); + glPopMatrix(); + glEndList (); } static void -pinit(ModeInfo * mi) +planetary_gears (ModeInfo *mi) { - gearsstruct *gp = &gears[MI_SCREEN(mi)]; - static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0}; - static const GLfloat red[4] = {0.8, 0.1, 0.0, 1.0}; - static const GLfloat green[4] = {0.0, 0.8, 0.2, 1.0}; - static const GLfloat blue[4] = {0.2, 0.2, 1.0, 1.0}; - static const GLfloat gray[4] = {0.5, 0.5, 0.5, 1.0}; - static const GLfloat white[4] = {1.0, 1.0, 1.0, 1.0}; - int wire = MI_IS_WIREFRAME(mi); - int mono = MI_IS_MONO(mi); - - if (!wire) { - glLightfv(GL_LIGHT0, GL_POSITION, pos); - glEnable(GL_CULL_FACE); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_DEPTH_TEST); - } -#if 0 -/*- - * Messes up on multiscreen Pseudocolor:0 StaticGray(monochrome):1 - * 2nd time mode is run it is Grayscale on PseudoColor. - * The code below forces monochrome on TrueColor. - */ - if (MI_IS_MONO(mi)) { - red[0] = red[1] = red[2] = 1.0; - green[0] = green[1] = green[2] = 1.0; - blue[0] = blue[1] = blue[2] = 1.0; - } -#endif + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + gear *g0, *g1, *g2, *g3, *g4; + GLfloat distance = 2.02; + + bp->planetary_p = True; + + g0 = new_gear (mi, 0); + g1 = new_gear (mi, 0); + g2 = new_gear (mi, 0); + g3 = new_gear (mi, 0); + g4 = new_gear (mi, 0); + + if (! place_gear (mi, g0, 0)) abort(); + if (! place_gear (mi, g1, 0)) abort(); + if (! place_gear (mi, g2, 0)) abort(); + if (! place_gear (mi, g3, 0)) abort(); + if (! place_gear (mi, g4, 0)) abort(); + + g0->nteeth = 12 + (3 * (random() % 10)); /* must be multiple of 3 */ + g0->tooth_w = g0->r / g0->nteeth; + g0->tooth_h = g0->tooth_w * 2.8; + +# define COPY(F) g4->F = g3->F = g2->F = g1->F = g0->F + COPY(r); + COPY(th); + COPY(nteeth); + COPY(tooth_w); + COPY(tooth_h); + COPY(tooth_slope); + COPY(inner_r); + COPY(inner_r2); + COPY(inner_r3); + COPY(thickness); + COPY(thickness2); + COPY(thickness3); + COPY(ratio); + COPY(size); +# undef COPY + + g1->x = cos (M_PI * 2 / 3) * g1->r * distance; + g1->y = sin (M_PI * 2 / 3) * g1->r * distance; + + g2->x = cos (M_PI * 4 / 3) * g2->r * distance; + g2->y = sin (M_PI * 4 / 3) * g2->r * distance; + + g3->x = cos (M_PI * 6 / 3) * g3->r * distance; + g3->y = sin (M_PI * 6 / 3) * g3->r * distance; + + g4->x = 0; + g4->y = 0; + g4->th = -g3->th; + + g0->inverted_p = True; + g0->x = 0; + g0->y = 0; + g0->nteeth = g4->nteeth * 3; + g0->r = g4->r * 3.05; + g0->inner_r = g0->r * 0.8; + g0->inner_r2 = 0; + g0->inner_r3 = 0; + g0->th = -(g4->th - (180 / g0->nteeth)); + g0->ratio = g4->ratio / 3; + + g0->tooth_slope = 0; + g0->nubs = 3; + g0->spokes = 0; + g0->size = INVOLUTE_LARGE; + + bp->gears = (gear **) calloc (6, sizeof(**bp->gears)); + bp->ngears = 0; + + bp->gears[bp->ngears++] = g1; + bp->gears[bp->ngears++] = g2; + bp->gears[bp->ngears++] = g3; + bp->gears[bp->ngears++] = g4; + bp->gears[bp->ngears++] = g0; +} - /* make the gears */ - - if (! gp->planetary_p) { - - gp->gear1 = glGenLists(1); - glNewList(gp->gear1, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(red); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); - } - gear(1.0, 4.0, 1.0, 20, 0.7, wire, False); - glEndList(); - - gp->gear2 = glGenLists(1); - glNewList(gp->gear2, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(green); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); - } - gear(0.5, 2.0, 2.0, 10, 0.7, wire, False); - glEndList(); - - gp->gear3 = glGenLists(1); - glNewList(gp->gear3, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(blue); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); - } - gear(1.3, 2.0, 0.5, 10, 0.7, wire, False); - glEndList(); - if (!wire) - glEnable(GL_NORMALIZE); - - } else { /* gp->planetary_p */ - - gp->gear1 = glGenLists(1); - glNewList(gp->gear1, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(red); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); - } - gear(1.3, 2.0, 2.0, 12, 0.7, wire, False); - glEndList(); - - gp->gear2 = glGenLists(1); - glNewList(gp->gear2, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(green); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); - } - gear(1.3, 2.0, 2.0, 12, 0.7, wire, False); - glEndList(); - - gp->gear3 = glGenLists(1); - glNewList(gp->gear3, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(blue); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); - } - gear(1.3, 2.0, 2.0, 12, 0.7, wire, False); - glEndList(); - if (!wire) - glEnable(GL_NORMALIZE); - - - gp->gear_inner = glGenLists(1); - glNewList(gp->gear_inner, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(blue); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); - } - gear(1.0, 2.0, 2.0, 12, 0.7, wire, False); - glEndList(); - if (!wire) - glEnable(GL_NORMALIZE); - - - gp->gear_outer = glGenLists(1); - glNewList(gp->gear_outer, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(blue); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); - } - gear(5.7, 7.0, 2.0, 36, 0.7, wire, True); - /* put some nubs on the outer ring, so we can tell how it's moving */ - glPushMatrix(); - glTranslatef(7.0, 0, 0); - glRotatef(90, 0, 1, 0); - - ctube(0.5, 0.5, wire); /* nub 1 */ - glPopMatrix(); - glPushMatrix(); - glRotatef(120, 0, 0, 1); - glTranslatef(7.0, 0, 0); - glRotatef(90, 0, 1, 0); - ctube(0.5, 0.5, wire); /* nub 2 */ - glPopMatrix(); +ENTRYPOINT void +init_gears (ModeInfo *mi) +{ + gears_configuration *bp; + int wire = MI_IS_WIREFRAME(mi); + int i; - glPushMatrix(); - glRotatef(240, 0, 0, 1); - glTranslatef(7.0, 0, 0); - glRotatef(90, 0, 1, 0); - ctube(0.5, 0.5, wire); /* nub 3 */ - glPopMatrix(); - - - glEndList(); - if (!wire) - glEnable(GL_NORMALIZE); - - gp->armature = glGenLists(1); - glNewList(gp->armature, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(blue); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - } + if (!bps) { + bps = (gears_configuration *) + calloc (MI_NUM_SCREENS(mi), sizeof (gears_configuration)); + if (!bps) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(1); + } - glTranslatef(0, 0, 1.5); - ctube(0.5, 10, wire); /* center axle */ + bp = &bps[MI_SCREEN(mi)]; + } - glPushMatrix(); - glTranslatef(0.0, 4.2, -1); - ctube(0.5, 3, wire); /* axle 1 */ - glTranslatef(0, 0, 1.8); - ctube(0.7, 0.7, wire); - glPopMatrix(); + bp = &bps[MI_SCREEN(mi)]; - glPushMatrix(); - glRotatef(120, 0.0, 0.0, 1.0); - glTranslatef(0.0, 4.2, -1); - ctube(0.5, 3, wire); /* axle 2 */ - glTranslatef(0, 0, 1.8); - ctube(0.7, 0.7, wire); - glPopMatrix(); + bp->glx_context = init_GL(mi); - glPushMatrix(); - glRotatef(240, 0.0, 0.0, 1.0); - glTranslatef(0.0, 4.2, -1); - ctube(0.5, 3, wire); /* axle 3 */ - glTranslatef(0, 0, 1.8); - ctube(0.7, 0.7, wire); - glPopMatrix(); + reshape_gears (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); - glTranslatef(0, 0, 1.5); /* center disk */ - ctube(1.5, 2, wire); + if (!wire) + { + GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0}; + GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0}; + GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0}; + GLfloat spc[4] = {0.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); + } - glPushMatrix(); - glRotatef(270, 0, 0, 1); - glRotatef(-10, 0, 1, 0); - glTranslatef(-2.2, 0, 0); - arm(4.0, 1.0, 0.5, 2.0, 1.0, wire); /* arm 1 */ - glPopMatrix(); + { + double spin_speed = 0.5; + double wander_speed = 0.01; + double spin_accel = 0.25; + + bp->rot = make_rotator (do_spin ? spin_speed : 0, + do_spin ? spin_speed : 0, + do_spin ? spin_speed : 0, + spin_accel, + do_wander ? wander_speed : 0, + True + ); + bp->trackball = gltrackball_init (); + } + + if (!(random() % 8)) + { + planetary_gears (mi); + } + else + { + gear *g = 0; + int total_gears = MI_COUNT (mi); + int i; + if (total_gears <= 0) + total_gears = 3 + abs (BELLRAND (8) - 4); /* 3 - 7, mostly 3. */ - glPushMatrix(); - glRotatef(30, 0, 0, 1); - glRotatef(-10, 0, 1, 0); - glTranslatef(-2.2, 0, 0); - arm(4.0, 1.0, 0.5, 2.0, 1.0, wire); /* arm 2 */ - glPopMatrix(); + bp->gears = (gear **) calloc (total_gears+2, sizeof(**bp->gears)); + bp->ngears = 0; - glPushMatrix(); - glRotatef(150, 0, 0, 1); - glRotatef(-10, 0, 1, 0); - glTranslatef(-2.2, 0, 0); - arm(4.0, 1.0, 0.5, 2.0, 1.0, wire); /* arm 3 */ - glPopMatrix(); - - glEndList(); - if (!wire) - glEnable(GL_NORMALIZE); + for (i = 0; i < total_gears; i++) + g = place_new_gear (mi, g); } -} -ENTRYPOINT Bool -gears_handle_event (ModeInfo *mi, XEvent *event) -{ - gearsstruct *gp = &gears[MI_SCREEN(mi)]; + /* Center gears in scene. */ + { + GLfloat minx=99999, miny=99999, maxx=-99999, maxy=-99999; + int i; + for (i = 0; i < bp->ngears; i++) + { + gear *g = bp->gears[i]; + if (g->x - g->r < minx) minx = g->x - g->r; + if (g->x + g->r > maxx) maxx = g->x + g->r; + if (g->y - g->r < miny) miny = g->y - g->r; + if (g->y + g->r > maxy) maxy = g->y + g->r; + } + bp->bbox.x1 = minx; + bp->bbox.y1 = miny; + bp->bbox.x2 = maxx; + bp->bbox.y2 = maxy; + } - if (event->xany.type == ButtonPress && - event->xbutton.button == Button1) - { - gp->button_down_p = True; - gltrackball_start (gp->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) - { - gp->button_down_p = False; - return True; - } - else if (event->xany.type == ButtonPress && - (event->xbutton.button == Button4 || - event->xbutton.button == Button5)) - { - gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10, - !!event->xbutton.state); - return True; - } - else if (event->xany.type == MotionNotify && - gp->button_down_p) + /* Now render each gear into its display list. + */ + for (i = 0; i < bp->ngears; i++) { - gltrackball_track (gp->trackball, - event->xmotion.x, event->xmotion.y, - MI_WIDTH (mi), MI_HEIGHT (mi)); - return True; - } + gear *g = bp->gears[i]; + g->dlist = glGenLists (1); + if (! g->dlist) + { + check_gl_error ("glGenLists"); + abort(); + } - return False; + glNewList (g->dlist, GL_COMPILE); + g->polygons += draw_involute_gear (g, wire); + glEndList (); + } + if (bp->planetary_p) + armature (mi); } ENTRYPOINT void -init_gears (ModeInfo * mi) +draw_gears (ModeInfo *mi) { - int screen = MI_SCREEN(mi); - - /*Colormap cmap; */ - /* Boolean rgba, doublebuffer, cmap_installed; */ - gearsstruct *gp; - - if (gears == NULL) { - if ((gears = (gearsstruct *) calloc(MI_NUM_SCREENS(mi), - sizeof (gearsstruct))) == NULL) - return; - } - gp = &gears[screen]; - - if (mode_str && !strcasecmp (mode_str, "planetary")) - gp->planetary_p = True; - else if (mode_str && !strcasecmp (mode_str, "simple")) - gp->planetary_p = False; - else if (!mode_str || !*mode_str || !strcasecmp (mode_str, "random")) - gp->planetary_p = !(random() % 2); - else - fprintf (stderr, "%s: mode must be planetary, simple, or random", - progname); + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + int i; - gp->window = MI_WINDOW(mi); + if (!bp->glx_context) + return; - gp->rot = make_rotator (1, 1, 1, 1, 0, True); - gp->trackball = gltrackball_init (); + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context)); - if ((gp->glx_context = init_GL(mi)) != NULL) { - reshape_gears(mi, MI_WIDTH(mi), MI_HEIGHT(mi)); - pinit(mi); - } else { - MI_CLEARWINDOW(mi); - } -} + glShadeModel(GL_SMOOTH); -ENTRYPOINT void -draw_gears (ModeInfo * mi) -{ - gearsstruct *gp = &gears[MI_SCREEN(mi)]; - Display *display = MI_DISPLAY(mi); - Window window = MI_WINDOW(mi); - int angle_incr = MI_CYCLES(mi) ? MI_CYCLES(mi) : 2; + glEnable(GL_DEPTH_TEST); + glEnable(GL_NORMALIZE); + glEnable(GL_CULL_FACE); - if (gp->planetary_p) - angle_incr *= 3; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - if (!gp->glx_context) - return; + glPushMatrix (); - glDrawBuffer(GL_BACK); + { + double x, y, z; + get_position (bp->rot, &x, &y, &z, !bp->button_down_p); + glTranslatef ((x - 0.5) * 4, + (y - 0.5) * 4, + (z - 0.5) * 7); - glXMakeCurrent(display, window, *(gp->glx_context)); - draw(mi); + gltrackball_rotate (bp->trackball); - /* let's do something so we don't get bored */ - gp->angle = (int) (gp->angle + angle_incr) % 360; + get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p); - if (mi->fps_p) do_fps (mi); - glFinish(); - glXSwapBuffers(display, window); -} + /* add a little rotation for -no-spin mode */ + x -= 0.14; + y -= 0.06; -ENTRYPOINT void -release_gears (ModeInfo * mi) -{ - if (gears != NULL) { - int screen; - - for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) { - gearsstruct *gp = &gears[screen]; - - if (gp->glx_context) { - /* Display lists MUST be freed while their glXContext is current. */ - glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context)); - - if (glIsList(gp->gear1)) - glDeleteLists(gp->gear1, 1); - if (glIsList(gp->gear2)) - glDeleteLists(gp->gear2, 1); - if (glIsList(gp->gear3)) - glDeleteLists(gp->gear3, 1); - if (glIsList(gp->gear_inner)) - glDeleteLists(gp->gear_inner, 1); - if (glIsList(gp->gear_outer)) - glDeleteLists(gp->gear_outer, 1); - - } - } - (void) free((void *) gears); - gears = NULL; - } - FreeAllGL(mi); -} + glRotatef (x * 360, 1.0, 0.0, 0.0); + glRotatef (y * 360, 0.0, 1.0, 0.0); + glRotatef (z * 360, 0.0, 0.0, 1.0); + } + /* Center the scene's bounding box in the window, + and scale it to fit. + */ + { + GLfloat w = bp->bbox.x2 - bp->bbox.x1; + GLfloat h = bp->bbox.y2 - bp->bbox.y1; + GLfloat s = 10.0 / (w > h ? w : h); + glScalef (s, s, s); + glTranslatef (-(bp->bbox.x1 + w/2), + -(bp->bbox.y1 + h/2), + 0); + } + + mi->polygon_count = 0; + + for (i = 0; i < bp->ngears; i++) + { + gear *g = bp->gears[i]; -XSCREENSAVER_MODULE ("Gears", gears) + glPushMatrix(); -/*********************************************************/ + glTranslatef (g->x, g->y, g->z); + glRotatef (g->th, 0, 0, 1); -#endif + glCallList (g->dlist); + mi->polygon_count += g->polygons; + + glPopMatrix (); + } + + if (bp->planetary_p) + { + glCallList (bp->armature_dlist); + mi->polygon_count += bp->armature_polygons; + } + + glPopMatrix (); + + /* spin gears */ + if (!bp->button_down_p) + for (i = 0; i < bp->ngears; i++) + { + gear *g = bp->gears[i]; + double off = g->ratio * 5 * speed; + if (g->th > 0) + g->th += off; + else + g->th -= off; + } + + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); +} + +XSCREENSAVER_MODULE ("Gears", gears) + +#endif /* USE_GL */