summaryrefslogtreecommitdiffstats
path: root/hacks/glx/involute.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/involute.c')
-rw-r--r--hacks/glx/involute.c998
1 files changed, 998 insertions, 0 deletions
diff --git a/hacks/glx/involute.c b/hacks/glx/involute.c
new file mode 100644
index 0000000..5b49bf0
--- /dev/null
+++ b/hacks/glx/involute.c
@@ -0,0 +1,998 @@
+/* involute, Copyright (c) 2004-2014 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.
+ *
+ * Utilities for rendering OpenGL gears with involute teeth.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "screenhackI.h"
+
+#ifndef HAVE_JWXYZ
+# include <GL/glx.h>
+# include <GL/glu.h>
+#endif
+
+#ifdef HAVE_ANDROID
+# include <GLES/gl.h>
+#endif
+
+#ifdef HAVE_JWZGLES
+# include "jwzgles.h"
+#endif /* HAVE_JWZGLES */
+
+#include "involute.h"
+#include "normals.h"
+
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+
+/* For debugging: if true then in wireframe, do not abbreviate. */
+static Bool wire_all_p = False;
+static Bool show_normals_p = False;
+
+
+/* Draws an uncapped tube of the given radius extending from top to bottom,
+ with faces pointing either in or out.
+ */
+static int
+draw_ring (int segments,
+ GLfloat r, GLfloat top, GLfloat bottom, GLfloat slope,
+ Bool in_p, Bool wire_p)
+{
+ int i;
+ int polys = 0;
+ GLfloat width = M_PI * 2 / segments;
+
+ GLfloat s1 = 1 + ((bottom-top) * slope / 2);
+ GLfloat s2 = 1 - ((bottom-top) * slope / 2);
+
+ if (top != bottom)
+ {
+ glFrontFace (in_p ? GL_CCW : GL_CW);
+ glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
+ for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
+ {
+ GLfloat th = i * width;
+ GLfloat cth = cos(th);
+ GLfloat sth = sin(th);
+ if (in_p)
+ glNormal3f (-cth, -sth, 0);
+ else
+ glNormal3f (cth, sth, 0);
+ glVertex3f (s1 * cth * r, s1 * sth * r, top);
+ glVertex3f (s2 * cth * r, s2 * sth * r, bottom);
+ }
+ polys += segments;
+ glEnd();
+ }
+
+ if (wire_p)
+ {
+ glBegin (GL_LINE_LOOP);
+ for (i = 0; i < segments; i++)
+ {
+ GLfloat th = i * width;
+ glVertex3f (cos(th) * r, sin(th) * r, top);
+ }
+ glEnd();
+ glBegin (GL_LINE_LOOP);
+ for (i = 0; i < segments; i++)
+ {
+ GLfloat th = i * width;
+ glVertex3f (cos(th) * r, sin(th) * r, bottom);
+ }
+ glEnd();
+ }
+
+ return polys;
+}
+
+
+/* Draws a donut-shaped disc between the given radii,
+ with faces pointing either up or down.
+ The first radius may be 0, in which case, a filled disc is drawn.
+ */
+static int
+draw_disc (int segments,
+ GLfloat ra, GLfloat rb, GLfloat z,
+ Bool up_p, Bool wire_p)
+{
+ int i;
+ int polys = 0;
+ GLfloat width = M_PI * 2 / segments;
+
+ if (ra < 0) abort();
+ if (rb <= 0) abort();
+
+ if (ra == 0)
+ glFrontFace (up_p ? GL_CW : GL_CCW);
+ else
+ glFrontFace (up_p ? GL_CCW : GL_CW);
+
+ if (ra == 0)
+ glBegin (wire_p ? GL_LINES : GL_TRIANGLE_FAN);
+ else
+ glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
+
+ glNormal3f (0, 0, (up_p ? -1 : 1));
+
+ if (ra == 0 && !wire_p)
+ glVertex3f (0, 0, z);
+
+ for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
+ {
+ GLfloat th = i * width;
+ GLfloat cth = cos(th);
+ GLfloat sth = sin(th);
+ if (wire_p || ra != 0)
+ glVertex3f (cth * ra, sth * ra, z);
+ glVertex3f (cth * rb, sth * rb, z);
+ }
+ polys += segments;
+ glEnd();
+ return polys;
+}
+
+
+/* Draws N thick radial lines between the given radii,
+ with faces pointing either up or down.
+ */
+static int
+draw_spokes (int n, GLfloat thickness, int segments,
+ GLfloat ra, GLfloat rb, GLfloat z1, GLfloat z2, GLfloat slope,
+ Bool wire_p)
+{
+ int i;
+ int polys = 0;
+ GLfloat width;
+ int segments2 = 0;
+ int insegs, outsegs;
+ int tick;
+ int state;
+
+ GLfloat s1 = 1 + ((z2-z1) * slope / 2);
+ GLfloat s2 = 1 - ((z2-z1) * slope / 2);
+
+ if (ra <= 0 || rb <= 0) abort();
+
+ segments *= 3;
+
+ while (segments2 < segments) /* need a multiple of N >= segments */
+ segments2 += n; /* (yes, this is a moronic way to find that) */
+
+ insegs = ((float) (segments2 / n) + 0.5) / thickness;
+ outsegs = (segments2 / n) - insegs;
+ if (insegs <= 0) insegs = 1;
+ if (outsegs <= 0) outsegs = 1;
+
+ segments2 = (insegs + outsegs) * n;
+ width = M_PI * 2 / segments2;
+
+ tick = 0;
+ state = 0;
+ for (i = 0; i < segments2; i++, tick++)
+ {
+ GLfloat th1 = i * width;
+ GLfloat th2 = th1 + width;
+ GLfloat cth1 = cos(th1);
+ GLfloat sth1 = sin(th1);
+ GLfloat cth2 = cos(th2);
+ GLfloat sth2 = sin(th2);
+ GLfloat orb = rb;
+
+ int changed = (i == 0);
+
+ if (state == 0 && tick == insegs)
+ tick = 0, state = 1, changed = 1;
+ else if (state == 1 && tick == outsegs)
+ tick = 0, state = 0, changed = 1;
+
+ if ((state == 1 || /* in */
+ (state == 0 && changed)) &&
+ (!wire_p || wire_all_p))
+ {
+ /* top */
+ glFrontFace (GL_CCW);
+ glBegin (wire_p ? GL_LINES : GL_QUADS);
+ glNormal3f (0, 0, -1);
+ glVertex3f (s1 * cth1 * ra, s1 * sth1 * ra, z1);
+ glVertex3f (s1 * cth1 * rb, s1 * sth1 * rb, z1);
+ glVertex3f (s1 * cth2 * rb, s1 * sth2 * rb, z1);
+ glVertex3f (s1 * cth2 * ra, s1 * sth2 * ra, z1);
+ polys++;
+ glEnd();
+
+ /* bottom */
+ glFrontFace (GL_CW);
+ glBegin (wire_p ? GL_LINES : GL_QUADS);
+ glNormal3f (0, 0, 1);
+ glVertex3f (s2 * cth1 * ra, s2 * sth1 * ra, z2);
+ glVertex3f (s2 * cth1 * rb, s2 * sth1 * rb, z2);
+ glVertex3f (s2 * cth2 * rb, s2 * sth2 * rb, z2);
+ glVertex3f (s2 * cth2 * ra, s2 * sth2 * ra, z2);
+ polys++;
+ glEnd();
+ }
+
+ if (state == 1 && changed) /* entering "in" state */
+ {
+ /* left */
+ glFrontFace (GL_CW);
+ glBegin (wire_p ? GL_LINES : GL_QUADS);
+ do_normal (s1 * cth1 * rb, s1 * sth1 * rb, z1,
+ s1 * cth1 * ra, s1 * sth1 * ra, z1,
+ s2 * cth1 * rb, s2 * sth1 * rb, z2);
+ glVertex3f (s1 * cth1 * ra, s1 * sth1 * ra, z1);
+ glVertex3f (s1 * cth1 * rb, s1 * sth1 * rb, z1);
+ glVertex3f (s2 * cth1 * rb, s2 * sth1 * rb, z2);
+ glVertex3f (s2 * cth1 * ra, s2 * sth1 * ra, z2);
+ polys++;
+ glEnd();
+ }
+
+ if (state == 0 && changed) /* entering "out" state */
+ {
+ /* right */
+ glFrontFace (GL_CCW);
+ glBegin (wire_p ? GL_LINES : GL_QUADS);
+ do_normal (s1 * cth2 * ra, s1 * sth2 * ra, z1,
+ s1 * cth2 * rb, s1 * sth2 * rb, z1,
+ s2 * cth2 * rb, s2 * sth2 * rb, z2);
+ glVertex3f (s1 * cth2 * ra, s1 * sth2 * ra, z1);
+ glVertex3f (s1 * cth2 * rb, s1 * sth2 * rb, z1);
+ glVertex3f (s2 * cth2 * rb, s2 * sth2 * rb, z2);
+ glVertex3f (s2 * cth2 * ra, s2 * sth2 * ra, z2);
+ polys++;
+ glEnd();
+ }
+
+ rb = orb;
+ }
+ return polys;
+}
+
+
+/* Draws some bumps (embedded cylinders) on the gear.
+ */
+static int
+draw_gear_nubs (gear *g, Bool wire_p)
+{
+ int polys = 0;
+ int i;
+ int steps = (g->size < INVOLUTE_LARGE ? 5 : 20);
+ double r, size, height;
+ GLfloat *cc;
+ int which;
+ GLfloat width, off;
+
+ if (! g->nubs) return 0;
+
+ which = involute_biggest_ring (g, &r, &size, &height);
+ size /= 5;
+ height *= 0.7;
+
+ cc = (which == 1 ? g->color : g->color2);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cc);
+
+ if (g->inverted_p)
+ r = g->r + size + g->tooth_h;
+
+ width = M_PI * 2 / g->nubs;
+ off = M_PI / (g->nteeth * 2); /* align first nub with a tooth */
+
+ for (i = 0; i < g->nubs; i++)
+ {
+ GLfloat th = (i * width) + off;
+ glPushMatrix();
+
+ glRotatef (th * 180 / M_PI, 0, 0, 1);
+ glTranslatef (r, 0, 0);
+
+ if (g->inverted_p) /* nubs go on the outside rim */
+ {
+ size = g->thickness / 3;
+ height = (g->r - g->inner_r)/2;
+ glTranslatef (height, 0, 0);
+ glRotatef (90, 0, 1, 0);
+ }
+
+ if (wire_p && !wire_all_p)
+ polys += draw_ring ((g->size >= INVOLUTE_LARGE ? steps/2 : steps),
+ size, 0, 0, 0, False, wire_p);
+ else
+ {
+ polys += draw_disc (steps, 0, size, -height, True, wire_p);
+ polys += draw_disc (steps, 0, size, height, False, wire_p);
+ polys += draw_ring (steps, size, -height, height, 0, False, wire_p);
+ }
+ glPopMatrix();
+ }
+ return polys;
+}
+
+
+
+/* Draws a much simpler representation of a gear.
+ Returns the number of polygons.
+ */
+int
+draw_involute_schematic (gear *g, Bool wire_p)
+{
+ int polys = 0;
+ int i;
+ GLfloat width = M_PI * 2 / g->nteeth;
+
+ if (!wire_p) glDisable(GL_LIGHTING);
+ glColor3f (g->color[0] * 0.6, g->color[1] * 0.6, g->color[2] * 0.6);
+
+ glBegin (GL_LINES);
+ for (i = 0; i < g->nteeth; i++)
+ {
+ GLfloat th = (i * width) + (width/4);
+ glVertex3f (0, 0, -g->thickness/2);
+ glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
+ }
+ polys += g->nteeth;
+ glEnd();
+
+ glBegin (GL_LINE_LOOP);
+ for (i = 0; i < g->nteeth; i++)
+ {
+ GLfloat th = (i * width) + (width/4);
+ glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
+ }
+ polys += g->nteeth;
+ glEnd();
+
+ if (!wire_p) glEnable(GL_LIGHTING);
+ return polys;
+}
+
+
+/* Renders all the interior (non-toothy) parts of a gear:
+ the discs, axles, etc.
+ */
+static int
+draw_gear_interior (gear *g, Bool wire_p)
+{
+ int polys = 0;
+
+ int steps = g->nteeth * 2;
+ if (steps < 10) steps = 10;
+ if ((wire_p && !wire_all_p) || g->size < INVOLUTE_LARGE) steps /= 2;
+ if (g->size < INVOLUTE_LARGE && steps > 16) steps = 16;
+
+ /* ring 1 (facing in) is done in draw_gear_teeth */
+
+ /* ring 2 (facing in) and disc 2
+ */
+ if (g->inner_r2)
+ {
+ GLfloat ra = g->inner_r * 1.04; /* slightly larger than inner_r */
+ GLfloat rb = g->inner_r2; /* since the points don't line up */
+ GLfloat za = -g->thickness2/2;
+ GLfloat zb = g->thickness2/2;
+ GLfloat s1 = 1 + (g->thickness2 * g->tooth_slope / 2);
+ GLfloat s2 = 1 - (g->thickness2 * g->tooth_slope / 2);
+
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color2);
+
+ if ((g->coax_p != 1 && !g->inner_r3) ||
+ (wire_p && wire_all_p))
+ polys += /* ring facing in */
+ draw_ring (steps, rb, za, zb, g->tooth_slope, True, wire_p);
+
+ if (wire_p && wire_all_p)
+ polys += /* ring facing in */
+ draw_ring (steps, ra, za, zb, g->tooth_slope, True, wire_p);
+
+ if (g->spokes)
+ polys += draw_spokes (g->spokes, g->spoke_thickness,
+ steps, ra, rb, za, zb, g->tooth_slope, wire_p);
+ else if (!wire_p || wire_all_p)
+ {
+ polys += /* top plate */
+ draw_disc (steps, s1*ra, s1*rb, za, True, wire_p);
+ polys += /* bottom plate */
+ draw_disc (steps, s2*ra, s2*rb, zb, False, wire_p);
+ }
+ }
+
+ /* ring 3 (facing in and out) and disc 3
+ */
+ if (g->inner_r3)
+ {
+ GLfloat ra = g->inner_r2;
+ GLfloat rb = g->inner_r3;
+ GLfloat za = -g->thickness3/2;
+ GLfloat zb = g->thickness3/2;
+ GLfloat s1 = 1 + (g->thickness3 * g->tooth_slope / 2);
+ GLfloat s2 = 1 - (g->thickness3 * g->tooth_slope / 2);
+
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
+
+ polys += /* ring facing out */
+ draw_ring (steps, ra, za, zb, g->tooth_slope, False, wire_p);
+
+ if (g->coax_p != 1 || (wire_p && wire_all_p))
+ polys += /* ring facing in */
+ draw_ring (steps, rb, za, zb, g->tooth_slope, True, wire_p);
+
+ if (!wire_p || wire_all_p)
+ {
+ polys += /* top plate */
+ draw_disc (steps, s1*ra, s1*rb, za, True, wire_p);
+ polys += /* bottom plate */
+ draw_disc (steps, s2*ra, s2*rb, zb, False, wire_p);
+ }
+ }
+
+ /* axle tube
+ */
+ if (g->coax_p == 1)
+ {
+ GLfloat cap_height = g->coax_thickness/3;
+
+ GLfloat ra = (g->inner_r3 ? g->inner_r3 :
+ g->inner_r2 ? g->inner_r2 :
+ g->inner_r);
+ GLfloat za = -(g->thickness/2 + cap_height);
+ GLfloat zb = g->coax_thickness/2 + g->coax_displacement + cap_height;
+
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
+
+ if (wire_p && !wire_all_p) steps /= 2;
+
+ polys +=
+ draw_ring (steps, ra, za, zb, g->tooth_slope, False, wire_p);
+
+ if (!wire_p || wire_all_p)
+ {
+ polys +=
+ draw_disc (steps, 0, ra, za, True, wire_p); /* top plate */
+ polys +=
+ draw_disc (steps, 0, ra, zb, False, wire_p); /* bottom plate */
+ }
+ }
+ return polys;
+}
+
+
+/* gear_teeth_geometry computes the vertices and normals of the teeth
+ of a gear. This is the heavy lifting: there are a ton of polygons
+ around the perimiter of a gear, and the normals are difficult (not
+ radial or right angles.)
+
+ It would be nice if we could cache this, but the numbers are
+ different for essentially every gear:
+
+ - Every gear has a different inner_r, so the vertices of the
+ inner ring (and thus, the triangle fans on the top and bottom
+ faces) are different in a non-scalable way.
+
+ - If the ratio between tooth_w and tooth_h changes, the normals
+ on the outside edges of the teeth are invalid (this can happen
+ every time we start a new train.)
+
+ So instead, we rely on OpenGL display lists to do the cacheing for
+ us -- we only compute all these normals once per gear, instead of
+ once per gear per frame.
+ */
+
+typedef struct {
+ int npoints;
+ XYZ *points;
+ XYZ *fnormals; /* face normals */
+ XYZ *pnormals; /* point normals */
+} tooth_face;
+
+
+static void
+tooth_normals (tooth_face *f, GLfloat tooth_slope)
+{
+ int i;
+
+ /* Compute the face normals for each facet. */
+ for (i = 0; i < f->npoints; i++)
+ {
+ XYZ p1, p2, p3;
+ int a = i;
+ int b = (i == f->npoints-1 ? 0 : i+1);
+ p1 = f->points[a];
+ p2 = f->points[b];
+ p3 = p1;
+ p3.x -= (p3.x * tooth_slope);
+ p3.y -= (p3.y * tooth_slope);
+ p3.z++;
+ f->fnormals[i] = calc_normal (p1, p2, p3);
+ }
+
+ /* From the face normals, compute the vertex normals
+ (by averaging the normals of adjascent faces.)
+ */
+ for (i = 0; i < f->npoints; i++)
+ {
+ int a = (i == 0 ? f->npoints-1 : i-1);
+ int b = i;
+ XYZ n1 = f->fnormals[a]; /* normal of [i-1 - i] face */
+ XYZ n2 = f->fnormals[b]; /* normal of [i - i+1] face */
+ f->pnormals[i].x = (n1.x + n2.x) / 2;
+ f->pnormals[i].y = (n1.y + n2.y) / 2;
+ f->pnormals[i].z = (n1.z + n2.z) / 2;
+ }
+}
+
+
+static void
+gear_teeth_geometry (gear *g,
+ tooth_face *orim, /* outer rim (the teeth) */
+ tooth_face *irim) /* inner rim (the hole) */
+{
+ int i;
+ int ppt = 20; /* max points per tooth */
+ GLfloat width = M_PI * 2 / g->nteeth;
+ GLfloat rh = g->tooth_h;
+ GLfloat tw = width;
+
+ /* Approximate shape of an "involute" gear tooth.
+
+ (TH)
+ th0 th2 th4 th6 th8 t10 t12 t14 th16 th18 th20
+ : : : : : : : : : : :
+ : : : : : : : : : : :
+ r0 ........:..:..:...___________...:..:..:......:......:..
+ : : : /: : :\ : : : : :
+ : : : / : : : \ : : : : :
+ : : :/ : : : \: : : : :
+ r2 ........:.....@...:....:....:...@..:..:......:......:..
+ : : @: : : : :@ : : : :
+ (R) ...........:...@.:...:....:....:...:.@..........:......:......
+ : :@ : : : : : @: : : :
+ r4 ........:..@..:...:....:....:...:..@:........:......:..
+ : /: : : : : : :\ : : :
+ :/ : : : : : : : \: : : /
+ r6 ......__/..:..:...:....:....:...:..:..\______________/
+ : : : : : : : : : : :
+ | : : : : : : : | : :
+ : : : : : : : : : : :
+ | : : : : : : : | : :
+ r8 ......__:_____________________________:________________
+ */
+
+ GLfloat r[30];
+ GLfloat th[30];
+ GLfloat R = g->r;
+
+ r[0] = R + (rh * 0.50);
+ r[1] = R + (rh * 0.40);
+ r[2] = R + (rh * 0.25);
+ r[3] = R + (rh * 0.05);
+ r[4] = R - (r[2]-R);
+ r[5] = R - (r[1]-R);
+ r[6] = R - (r[0]-R);
+ r[7] = r[6]; /* unused */
+ r[8] = g->inner_r;
+
+ th[0] = -tw * (g->size == INVOLUTE_SMALL ? 0.5 :
+ g->size == INVOLUTE_MEDIUM ? 0.41 : 0.45);
+ th[1] = -tw * 0.375;
+ th[2] = -tw * 0.300;
+ th[3] = -tw * 0.230;
+ th[4] = -tw * (g->nteeth >= 5 ? 0.16 : 0.12);
+ th[5] = -tw * 0.100;
+ th[6] = -tw * (g->size == INVOLUTE_MEDIUM ? 0.1 : 0.04);
+ th[7] = -tw * 0.020;
+ th[8] = 0;
+ th[9] = -th[7];
+ th[10] = -th[6];
+ th[11] = -th[5];
+ th[12] = -th[4];
+ th[13] = -th[3];
+ th[14] = -th[2];
+ th[15] = -th[1];
+ th[16] = -th[0];
+ th[17] = width * 0.47;
+ th[18] = width * 0.50;
+ th[19] = width * 0.53;
+ th[20] = th[0] + width; /* unused */
+
+ if (g->inverted_p) /* put the teeth on the inside */
+ {
+ for (i = 0; i < countof(th); i++)
+ th[i] = -th[i];
+ for (i = 0; i < countof(r); i++)
+ r[i] = R - (r[i] - R);
+ }
+
+ orim->npoints = 0;
+ orim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->points));
+ orim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->fnormals));
+ orim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->pnormals));
+
+ irim->npoints = 0;
+ irim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->points));
+ irim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->fnormals));
+ irim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->pnormals));
+
+ if (!orim->points || !orim->pnormals || !orim->fnormals ||
+ !irim->points || !irim->pnormals || !irim->fnormals)
+ {
+ fprintf (stderr, "%s: out of memory\n", progname);
+ exit (1);
+ }
+
+ /* First, compute the coordinates of every point used for every tooth.
+ */
+ for (i = 0; i < g->nteeth; i++)
+ {
+ GLfloat TH = (i * width) + (width/4);
+ int oon = orim->npoints;
+ int oin = irim->npoints;
+
+# undef PUSH
+# define PUSH(OPR,IPR,PTH) \
+ orim->points[orim->npoints].x = cos(TH+th[(PTH)]) * r[(OPR)]; \
+ orim->points[orim->npoints].y = sin(TH+th[(PTH)]) * r[(OPR)]; \
+ orim->npoints++; \
+ irim->points[irim->npoints].x = cos(TH+th[(PTH)]) * r[(IPR)]; \
+ irim->points[irim->npoints].y = sin(TH+th[(PTH)]) * r[(IPR)]; \
+ irim->npoints++
+
+ switch (g->size) {
+ case INVOLUTE_SMALL:
+ PUSH(6, 8, 0); /* tooth left 1 */
+ PUSH(0, 8, 8); /* tooth top middle */
+ break;
+ case INVOLUTE_MEDIUM:
+ PUSH(6, 8, 0); /* tooth left 1 */
+ PUSH(0, 8, 6); /* tooth top left */
+ PUSH(0, 8, 10); /* tooth top right */
+ PUSH(6, 8, 16); /* tooth right 6 */
+ break;
+ case INVOLUTE_LARGE:
+ PUSH(6, 8, 0); /* tooth left 1 */
+ PUSH(4, 8, 2); /* tooth left 3 */
+ PUSH(2, 8, 4); /* tooth left 5 */
+ PUSH(0, 8, 6); /* tooth top left */
+ PUSH(0, 8, 10); /* tooth top right */
+ PUSH(2, 8, 12); /* tooth right 1 */
+ PUSH(4, 8, 14); /* tooth right 3 */
+ PUSH(6, 8, 16); /* tooth right 5 */
+ PUSH(6, 8, 18); /* gap top */
+ break;
+ case INVOLUTE_HUGE:
+ PUSH(6, 8, 0); /* tooth left 1 */
+ PUSH(5, 8, 1); /* tooth left 2 */
+ PUSH(4, 8, 2); /* tooth left 3 */
+ PUSH(3, 8, 3); /* tooth left 4 */
+ PUSH(2, 8, 4); /* tooth left 5 */
+ PUSH(1, 8, 5); /* tooth left 6 */
+ PUSH(0, 8, 6); /* tooth top left */
+ PUSH(0, 8, 8); /* tooth top left */
+ PUSH(0, 8, 10); /* tooth top right */
+ PUSH(1, 8, 11); /* tooth top right */
+ PUSH(2, 8, 12); /* tooth right 1 */
+ PUSH(3, 8, 13); /* tooth right 2 */
+ PUSH(4, 8, 14); /* tooth right 3 */
+ PUSH(5, 8, 15); /* tooth right 4 */
+ PUSH(6, 8, 16); /* tooth right 5 */
+ PUSH(6, 8, 17); /* tooth right 6 */
+ PUSH(6, 8, 18); /* gap top */
+ PUSH(6, 8, 19); /* gap top */
+ break;
+ default:
+ abort();
+ }
+# undef PUSH
+
+ if (i == 0 && orim->npoints > ppt) abort(); /* go update "ppt"! */
+
+ if (g->inverted_p)
+ {
+ int start, end, j;
+ start = oon;
+ end = orim->npoints;
+ for (j = 0; j < (end-start)/2; j++)
+ {
+ XYZ swap = orim->points[end-j-1];
+ orim->points[end-j-1] = orim->points[start+j];
+ orim->points[start+j] = swap;
+ }
+
+ start = oin;
+ end = irim->npoints;
+ for (j = 0; j < (end-start)/2; j++)
+ {
+ XYZ swap = irim->points[end-j-1];
+ irim->points[end-j-1] = irim->points[start+j];
+ irim->points[start+j] = swap;
+ }
+ }
+ }
+
+ tooth_normals (orim, g->tooth_slope);
+ tooth_normals (irim, 0);
+
+ if (g->inverted_p) /* flip the normals */
+ {
+ for (i = 0; i < orim->npoints; i++)
+ {
+ orim->fnormals[i].x = -orim->fnormals[i].x;
+ orim->fnormals[i].y = -orim->fnormals[i].y;
+ orim->fnormals[i].z = -orim->fnormals[i].z;
+
+ orim->pnormals[i].x = -orim->pnormals[i].x;
+ orim->pnormals[i].y = -orim->pnormals[i].y;
+ orim->pnormals[i].z = -orim->pnormals[i].z;
+ }
+
+ for (i = 0; i < irim->npoints; i++)
+ {
+ irim->fnormals[i].x = -irim->fnormals[i].x;
+ irim->fnormals[i].y = -irim->fnormals[i].y;
+ irim->fnormals[i].z = -irim->fnormals[i].z;
+
+ irim->pnormals[i].x = -irim->pnormals[i].x;
+ irim->pnormals[i].y = -irim->pnormals[i].y;
+ irim->pnormals[i].z = -irim->pnormals[i].z;
+ }
+ }
+}
+
+
+/* Which of the gear's inside rings is the biggest?
+ */
+int
+involute_biggest_ring (gear *g, double *posP, double *sizeP, double *heightP)
+{
+ double r0 = (g->r - g->tooth_h/2);
+ double r1 = g->inner_r;
+ double r2 = g->inner_r2;
+ double r3 = g->inner_r3;
+ double w1 = (r1 ? r0 - r1 : r0);
+ double w2 = (r2 ? r1 - r2 : 0);
+ double w3 = (r3 ? r2 - r3 : 0);
+ double h1 = g->thickness;
+ double h2 = g->thickness2;
+ double h3 = g->thickness3;
+
+ if (g->spokes) w2 = 0;
+
+ if (w1 > w2 && w1 > w3)
+ {
+ if (posP) *posP = (r0+r1)/2;
+ if (sizeP) *sizeP = w1;
+ if (heightP) *heightP = h1;
+ return 0;
+ }
+ else if (w2 > w1 && w2 > w3)
+ {
+ if (posP) *posP = (r1+r2)/2;
+ if (sizeP) *sizeP = w2;
+ if (heightP) *heightP = h2;
+ return 1;
+ }
+ else
+ {
+ if (posP) *posP = (r2+r3)/2;
+ if (sizeP) *sizeP = w3;
+ if (heightP) *heightP = h3;
+ return 1;
+ }
+}
+
+
+/* Renders all teeth of a gear.
+ */
+static int
+draw_gear_teeth (gear *g, Bool wire_p)
+{
+ int polys = 0;
+ int i;
+
+ GLfloat z1 = -g->thickness/2;
+ GLfloat z2 = g->thickness/2;
+ GLfloat s1 = 1 + (g->thickness * g->tooth_slope / 2);
+ GLfloat s2 = 1 - (g->thickness * g->tooth_slope / 2);
+
+ tooth_face orim, irim;
+ gear_teeth_geometry (g, &orim, &irim);
+
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
+
+ /* Draw the outer rim (the teeth)
+ (In wire mode, this draws just the upright lines.)
+ */
+ glFrontFace (g->inverted_p ? GL_CCW : GL_CW);
+ glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
+ for (i = 0; i < orim.npoints; i++)
+ {
+ glNormal3f (orim.pnormals[i].x, orim.pnormals[i].y, orim.pnormals[i].z);
+ glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
+ glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
+
+ /* Show the face normal vectors */
+ if (0&&wire_p && show_normals_p)
+ {
+ XYZ n = orim.fnormals[i];
+ int a = i;
+ int b = (i == orim.npoints-1 ? 0 : i+1);
+ GLfloat x = (orim.points[a].x + orim.points[b].x) / 2;
+ GLfloat y = (orim.points[a].y + orim.points[b].y) / 2;
+ GLfloat z = (z1 + z2) / 2;
+ glVertex3f (x, y, z);
+ glVertex3f (x + n.x, y + n.y, z + n.z);
+ }
+
+ /* Show the vertex normal vectors */
+ if (wire_p && show_normals_p)
+ {
+ XYZ n = orim.pnormals[i];
+ GLfloat x = orim.points[i].x;
+ GLfloat y = orim.points[i].y;
+ GLfloat z = (z1 + z2) / 2;
+ glVertex3f (x, y, z);
+ glVertex3f (x + n.x, y + n.y, z + n.z);
+ }
+ }
+
+ if (!wire_p) /* close the quad loop */
+ {
+ glNormal3f (orim.pnormals[0].x, orim.pnormals[0].y, orim.pnormals[0].z);
+ glVertex3f (s1*orim.points[0].x, s1*orim.points[0].y, z1);
+ glVertex3f (s2*orim.points[0].x, s2*orim.points[0].y, z2);
+ }
+ polys += orim.npoints;
+ glEnd();
+
+ /* Draw the outer rim circles, in wire mode */
+ if (wire_p)
+ {
+ glBegin (GL_LINE_LOOP);
+ for (i = 0; i < orim.npoints; i++)
+ glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
+ glEnd();
+ glBegin (GL_LINE_LOOP);
+ for (i = 0; i < orim.npoints; i++)
+ glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
+ glEnd();
+ }
+
+ /* Draw the inner rim (the hole)
+ (In wire mode, this draws just the upright lines.)
+ */
+ glFrontFace (g->inverted_p ? GL_CW : GL_CCW);
+ glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
+ for (i = 0; i < irim.npoints; i++)
+ {
+ glNormal3f(-irim.pnormals[i].x, -irim.pnormals[i].y,-irim.pnormals[i].z);
+ glVertex3f (s1*irim.points[i].x, s1*irim.points[i].y, z1);
+ glVertex3f (s2*irim.points[i].x, s2*irim.points[i].y, z2);
+
+ /* Show the face normal vectors */
+ if (wire_p && show_normals_p)
+ {
+ XYZ n = irim.fnormals[i];
+ int a = i;
+ int b = (i == irim.npoints-1 ? 0 : i+1);
+ GLfloat x = (irim.points[a].x + irim.points[b].x) / 2;
+ GLfloat y = (irim.points[a].y + irim.points[b].y) / 2;
+ GLfloat z = (z1 + z2) / 2;
+ glVertex3f (x, y, z);
+ glVertex3f (x - n.x, y - n.y, z);
+ }
+
+ /* Show the vertex normal vectors */
+ if (wire_p && show_normals_p)
+ {
+ XYZ n = irim.pnormals[i];
+ GLfloat x = irim.points[i].x;
+ GLfloat y = irim.points[i].y;
+ GLfloat z = (z1 + z2) / 2;
+ glVertex3f (x, y, z);
+ glVertex3f (x - n.x, y - n.y, z);
+ }
+ }
+
+ if (!wire_p) /* close the quad loop */
+ {
+ glNormal3f (-irim.pnormals[0].x,-irim.pnormals[0].y,-irim.pnormals[0].z);
+ glVertex3f (s1*irim.points[0].x, s1*irim.points[0].y, z1);
+ glVertex3f (s2*irim.points[0].x, s2*irim.points[0].y, z2);
+ }
+ polys += irim.npoints;
+ glEnd();
+
+ /* Draw the inner rim circles, in wire mode
+ */
+ if (wire_p)
+ {
+ glBegin (GL_LINE_LOOP);
+ for (i = 0; i < irim.npoints; i++)
+ glVertex3f (irim.points[i].x, irim.points[i].y, z1);
+ glEnd();
+ glBegin (GL_LINE_LOOP);
+ for (i = 0; i < irim.npoints; i++)
+ glVertex3f (irim.points[i].x, irim.points[i].y, z2);
+ glEnd();
+ }
+
+ /* Draw the side (the flat bit)
+ */
+ if (!wire_p || wire_all_p)
+ {
+ GLfloat z;
+ if (irim.npoints != orim.npoints) abort();
+ for (z = z1; z <= z2; z += z2-z1)
+ {
+ GLfloat s = (z == z1 ? s1 : s2);
+ glFrontFace (((z == z1) ^ g->inverted_p) ? GL_CCW : GL_CW);
+ glNormal3f (0, 0, z);
+ glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
+ for (i = 0; i < orim.npoints; i++)
+ {
+ glVertex3f (s*orim.points[i].x, s*orim.points[i].y, z);
+ glVertex3f (s*irim.points[i].x, s*irim.points[i].y, z);
+ }
+ if (!wire_p) /* close the quad loop */
+ {
+ glVertex3f (s*orim.points[0].x, s*orim.points[0].y, z);
+ glVertex3f (s*irim.points[0].x, s*irim.points[0].y, z);
+ }
+ polys += orim.npoints;
+ glEnd();
+ }
+ }
+
+ free (irim.points);
+ free (irim.fnormals);
+ free (irim.pnormals);
+
+ free (orim.points);
+ free (orim.fnormals);
+ free (orim.pnormals);
+
+ return polys;
+}
+
+
+/* Render one gear, unrotated at 0,0.
+ Returns the number of polygons.
+ */
+int
+draw_involute_gear (gear *g, Bool wire_p)
+{
+ int polys = 0;
+
+ static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
+ GLfloat shiny = 128.0;
+
+ glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
+ glMateriali (GL_FRONT, GL_SHININESS, shiny);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
+ glColor3f (g->color[0], g->color[1], g->color[2]);
+
+ if (wire_p > 1)
+ polys += draw_involute_schematic (g, wire_p);
+ else
+ {
+ glPushMatrix();
+ glRotatef (g->wobble, 1, 0, 0);
+ polys += draw_gear_teeth (g, wire_p);
+ polys += draw_gear_interior (g, wire_p);
+ polys += draw_gear_nubs (g, wire_p);
+ glPopMatrix();
+ }
+ return polys;
+}