-/* -*- 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-2014 Jamie Zawinski <jwz@jwz.org>
*
- * 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 <tschmidt@micron.com>
- * 1996: "written" by Danny Sung <dannys@ucla.edu>
- * Based on 3-D gear wheels by Brian Paul which is in the public domain.
- */
-
-/*-
- * 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.
+ * Originally written by Brian Paul in 1996 or earlier;
+ * rewritten by jwz in Nov 2007.
*/
-/*-
- * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
- * otherwise caddr_t is not defined correctly
- */
+#define DEFAULTS "*delay: 30000 \n" \
+ "*count: 0 \n" \
+ "*showFPS: False \n" \
+ "*wireframe: False \n" \
-#include <X11/Intrinsic.h>
-
-#ifdef STANDALONE
-# define PROGCLASS "Gears"
-# define HACK_INIT init_gears
-# define HACK_DRAW draw_gears
-# define HACK_RESHAPE reshape_gears
-# define HACK_HANDLE_EVENT gears_handle_event
-# define EVENT_MASK PointerMotionMask
-# define gears_opts xlockmore_opts
-# define DEFAULTS "*count: 1 \n" \
- "*cycles: 2 \n" \
- "*delay: 20000 \n" \
- "*planetary: False \n" \
- "*showFPS: False \n" \
- "*wireframe: False \n"
-# include "xlockmore.h" /* from the xscreensaver distribution */
-#else /* !STANDALONE */
-# include "xlock.h" /* from the xlockmore distribution */
-#endif /* !STANDALONE */
-
-#ifdef USE_GL
+# define refresh_gears 0
+# 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 <ctype.h>
-#undef countof
-#define countof(x) (sizeof((x))/sizeof((*x)))
+#ifdef USE_GL /* whole file */
-#define DEF_PLANETARY "False"
+#undef BELLRAND
+#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
-static int planetary;
+#define DEF_SPIN "True"
+#define DEF_WANDER "True"
+#define DEF_SPEED "1.0"
-static XrmOptionDescRec opts[] = {
- {"-planetary", ".gears.planetary", XrmoptionNoArg, (caddr_t) "true" },
- {"+planetary", ".gears.planetary", XrmoptionNoArg, (caddr_t) "false" },
-};
+typedef struct {
+ GLXContext *glx_context;
+ rotator *rot;
+ trackball_state *trackball;
+ Bool button_down_p;
+ Bool planetary_p;
-static argtype vars[] = {
- {&planetary, "planetary", "Planetary", DEF_PLANETARY, t_Bool},
-};
+ int ngears;
+ gear **gears;
-ModeSpecOpt gears_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ GLuint armature_dlist;
+ int armature_polygons;
-#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};
+ struct { GLfloat x1, y1, x2, y2; } bbox;
-#endif
+} gears_configuration;
-#define SMOOTH_TUBE /* whether to have smooth or faceted tubes */
+static gears_configuration *bps = NULL;
-#ifdef SMOOTH_TUBE
-# define TUBE_FACES 20 /* how densely to render tubes */
-#else
-# define TUBE_FACES 6
-#endif
+static Bool do_spin;
+static GLfloat speed;
+static Bool do_wander;
+static XrmOptionDescRec opts[] = {
+ { "-spin", ".spin", XrmoptionNoArg, "True" },
+ { "+spin", ".spin", XrmoptionNoArg, "False" },
+ { "-speed", ".speed", XrmoptionSepArg, 0 },
+ { "-wander", ".wander", XrmoptionNoArg, "True" },
+ { "+wander", ".wander", XrmoptionNoArg, "False" },
+};
-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;
-} gearsstruct;
+static argtype vars[] = {
+ {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
+ {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
+ {&speed, "speed", "Speed", DEF_SPEED, t_Float},
+};
-static gearsstruct *gears = NULL;
+ENTRYPOINT ModeSpecOpt gears_opts = {countof(opts), opts, countof(vars), vars, 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
+
+/* Window management, etc
*/
-static void
-gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
- GLint teeth, GLfloat tooth_depth, Bool wire, Bool invert)
+ENTRYPOINT void
+reshape_gears (ModeInfo *mi, int width, int height)
{
- GLint i;
- GLfloat r0, r1, r2;
- GLfloat angle, da;
- GLfloat u, v, len;
+ GLfloat h = (GLfloat) height / (GLfloat) width;
- 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);
- }
+ glViewport (0, 0, (GLint) width, (GLint) height);
- 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);
- }
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective (30.0, 1/h, 1.0, 100.0);
- len = sqrt(u * u + v * v);
- u /= len;
- v /= len;
- glNormal3f(v, -u, 0.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( 0.0, 0.0, 30.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
- 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);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
- 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 if (pix <= 25) g->size = INVOLUTE_LARGE;
+ else g->size = INVOLUTE_HUGE;
+ }
- 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;
+ }
- glRotatef (-90, 0, 0, 1);
- glScalef (diameter/length, 1, diameter/length);
+ /* ratios are cumulative for all gears in the train. */
+ g->ratio *= parent->ratio;
+ }
- /* 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 */
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 */
glVertex3f(length/2, width2/2, -height2/2);
glVertex3f(length/2, width2/2, height2/2);
glVertex3f(length/2, -width2/2, height2/2);
+ polys++;
glEnd();
#endif
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 */
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 */
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 */
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 int
+ctube (GLfloat diameter, GLfloat width, Bool wire)
+{
+ tube (0, 0, width/2,
+ 0, 0, -width/2,
+ diameter, 0,
+ 32, True, True, wire);
+ return 0; /* #### */
+}
+
static void
-draw(ModeInfo * mi)
+armature (ModeInfo *mi)
{
- gearsstruct *gp = &gears[MI_SCREEN(mi)];
- int wire = MI_IS_WIREFRAME(mi);
+ gears_configuration *bp = &bps[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);
- }
+ static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
+ GLfloat shiny = 128.0;
+ GLfloat color[4];
- glPushMatrix();
+ 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;
- gltrackball_rotate (gp->trackball);
+ bp->armature_polygons = 0;
+ bp->armature_dlist = glGenLists (1);
+ if (! bp->armature_dlist)
{
- 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);
+ check_gl_error ("glGenLists");
+ abort();
}
- if (!planetary) {
- 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();
-
- 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();
+ glNewList (bp->armature_dlist, GL_COMPILE);
- 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();
+ 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]);
- } else { /* planetary */
+ glPushMatrix();
- glScalef(0.8, 0.8, 0.8);
+ {
+ GLfloat s = bp->gears[0]->r * 2.7;
+ s = s/5.6;
+ glScalef (s, s, s);
+ }
- glPushMatrix();
- glTranslatef(0.0, 4.2, 0.0);
- glRotatef(gp->angle - 7.0, 0.0, 0.0, 1.0);
- glCallList(gp->gear1);
- glPopMatrix();
+ glTranslatef (0, 0, 1.4 + bp->gears[0]->thickness);
+ glRotatef (30, 0, 0, 1);
- 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();
+ bp->armature_polygons += ctube (0.5, 10, wire); /* center axle */
- 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();
-
- glPushMatrix();
- glTranslatef(0.0, 0.0, 0.0);
- glRotatef(-gp->angle, 0.0, 0.0, 1.0);
- glCallList(gp->gear_inner);
- glPopMatrix();
-
- 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 */
-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 GLfloat pos[4] =
- {5.0, 5.0, 10.0, 1.0};
- static GLfloat red[4] =
- {0.8, 0.1, 0.0, 1.0};
- static GLfloat green[4] =
- {0.0, 0.8, 0.2, 1.0};
- static GLfloat blue[4] =
- {0.2, 0.2, 1.0, 1.0};
- static GLfloat gray[4] =
- {0.5, 0.5, 0.5, 1.0};
- static 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;
+
+ /* rotate central gear 1/2 tooth-size if odd number of teeth */
+ if (g4->nteeth & 1)
+ g4->th -= (180.0 / g4->nteeth);
+
+ g0->inverted_p = True;
+ g0->x = 0;
+ g0->y = 0;
+ g0->nteeth = g1->nteeth * 3;
+ g0->r = g1->r * 3.05;
+ g0->inner_r = g0->r * 0.8;
+ g0->inner_r2 = 0;
+ g0->inner_r3 = 0;
+ g0->th = g1->th + (180 / g0->nteeth);
+ g0->ratio = g1->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 (! planetary) {
-
- 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 { /* planetary */
-
- 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();
+ENTRYPOINT void
+init_gears (ModeInfo *mi)
+{
+ gears_configuration *bp;
+ int wire = MI_IS_WIREFRAME(mi);
+ int i;
- 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();
+ 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);
+ }
+ }
- 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);
- }
+ bp = &bps[MI_SCREEN(mi)];
- glTranslatef(0, 0, 1.5);
- ctube(0.5, 10, wire); /* center axle */
+ bp->glx_context = init_GL(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();
+ reshape_gears (mi, MI_WIDTH(mi), MI_HEIGHT(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();
+ 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(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();
+ if (! bp->rot)
+ {
+ 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 (True);
+ }
- glTranslatef(0, 0, 1.5); /* center disk */
- ctube(1.5, 2, wire);
+ if (bp->gears)
+ {
+ for (i = 0; i < bp->ngears; i++)
+ free_gear (bp->gears[i]);
+ free (bp->gears);
+ bp->gears = 0;
+ bp->ngears = 0;
+ }
- 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();
+ if (!(random() % 8))
+ {
+ planetary_gears (mi);
+ }
+ else
+ {
+ gear *g = 0;
+ int total_gears = MI_COUNT (mi);
- 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->planetary_p = False;
- 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);
+ if (total_gears <= 0)
+ total_gears = 3 + abs (BELLRAND (8) - 4); /* 3 - 7, mostly 3. */
+ bp->gears = (gear **) calloc (total_gears+2, sizeof(**bp->gears));
+ bp->ngears = 0;
+
+ for (i = 0; i < total_gears; i++)
+ g = place_new_gear (mi, g);
}
-}
-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 == 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);
}
-void
-init_gears(ModeInfo * mi)
+ENTRYPOINT void
+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];
-
- gp->window = MI_WINDOW(mi);
-
- gp->rot = make_rotator (1, 1, 1, 1, 0, True);
- gp->trackball = gltrackball_init ();
-
- if ((gp->glx_context = init_GL(mi)) != NULL) {
- reshape_gears(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
- pinit(mi);
- } else {
- MI_CLEARWINDOW(mi);
- }
-}
+ gears_configuration *bp = &bps[MI_SCREEN(mi)];
+ Display *dpy = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ int i;
-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;
+ if (!bp->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
+
+ glShadeModel(GL_SMOOTH);
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_NORMALIZE);
+ glEnable(GL_CULL_FACE);
- if (planetary)
- 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;
+
+ 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];
+
+ glPushMatrix();
+
+ glTranslatef (g->x, g->y, g->z);
+ glRotatef (g->th, 0, 0, 1);
+
+ 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);
}
-void
-release_gears(ModeInfo * mi)
+ENTRYPOINT Bool
+gears_handle_event (ModeInfo *mi, XEvent *event)
{
- 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);
-}
+ gears_configuration *bp = &bps[MI_SCREEN(mi)];
+ if (gltrackball_event_handler (event, bp->trackball,
+ MI_WIDTH (mi), MI_HEIGHT (mi),
+ &bp->button_down_p))
+ return True;
+ else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
+ {
+ init_gears (mi);
+ return True;
+ }
-/*********************************************************/
+ return False;
+}
-#endif
+XSCREENSAVER_MODULE ("Gears", gears)
+
+#endif /* USE_GL */