1 /* involute, Copyright (c) 2004-2014 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Utilities for rendering OpenGL gears with involute teeth.
16 #endif /* HAVE_CONFIG_H */
18 #include "screenhackI.h"
23 #endif /* !HAVE_COCOA */
27 #endif /* HAVE_JWZGLES */
33 #define countof(x) (sizeof((x))/sizeof((*x)))
36 /* For debugging: if true then in wireframe, do not abbreviate. */
37 static Bool wire_all_p = False;
38 static Bool show_normals_p = False;
41 /* Draws an uncapped tube of the given radius extending from top to bottom,
42 with faces pointing either in or out.
45 draw_ring (int segments,
46 GLfloat r, GLfloat top, GLfloat bottom, GLfloat slope,
47 Bool in_p, Bool wire_p)
51 GLfloat width = M_PI * 2 / segments;
53 GLfloat s1 = 1 + ((bottom-top) * slope / 2);
54 GLfloat s2 = 1 - ((bottom-top) * slope / 2);
58 glFrontFace (in_p ? GL_CCW : GL_CW);
59 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
60 for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
62 GLfloat th = i * width;
63 GLfloat cth = cos(th);
64 GLfloat sth = sin(th);
66 glNormal3f (-cth, -sth, 0);
68 glNormal3f (cth, sth, 0);
69 glVertex3f (s1 * cth * r, s1 * sth * r, top);
70 glVertex3f (s2 * cth * r, s2 * sth * r, bottom);
78 glBegin (GL_LINE_LOOP);
79 for (i = 0; i < segments; i++)
81 GLfloat th = i * width;
82 glVertex3f (cos(th) * r, sin(th) * r, top);
85 glBegin (GL_LINE_LOOP);
86 for (i = 0; i < segments; i++)
88 GLfloat th = i * width;
89 glVertex3f (cos(th) * r, sin(th) * r, bottom);
98 /* Draws a donut-shaped disc between the given radii,
99 with faces pointing either up or down.
100 The first radius may be 0, in which case, a filled disc is drawn.
103 draw_disc (int segments,
104 GLfloat ra, GLfloat rb, GLfloat z,
105 Bool up_p, Bool wire_p)
109 GLfloat width = M_PI * 2 / segments;
112 if (rb <= 0) abort();
115 glFrontFace (up_p ? GL_CW : GL_CCW);
117 glFrontFace (up_p ? GL_CCW : GL_CW);
120 glBegin (wire_p ? GL_LINES : GL_TRIANGLE_FAN);
122 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
124 glNormal3f (0, 0, (up_p ? -1 : 1));
126 if (ra == 0 && !wire_p)
127 glVertex3f (0, 0, z);
129 for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
131 GLfloat th = i * width;
132 GLfloat cth = cos(th);
133 GLfloat sth = sin(th);
134 if (wire_p || ra != 0)
135 glVertex3f (cth * ra, sth * ra, z);
136 glVertex3f (cth * rb, sth * rb, z);
144 /* Draws N thick radial lines between the given radii,
145 with faces pointing either up or down.
148 draw_spokes (int n, GLfloat thickness, int segments,
149 GLfloat ra, GLfloat rb, GLfloat z1, GLfloat z2, GLfloat slope,
160 GLfloat s1 = 1 + ((z2-z1) * slope / 2);
161 GLfloat s2 = 1 - ((z2-z1) * slope / 2);
163 if (ra <= 0 || rb <= 0) abort();
167 while (segments2 < segments) /* need a multiple of N >= segments */
168 segments2 += n; /* (yes, this is a moronic way to find that) */
170 insegs = ((float) (segments2 / n) + 0.5) / thickness;
171 outsegs = (segments2 / n) - insegs;
172 if (insegs <= 0) insegs = 1;
173 if (outsegs <= 0) outsegs = 1;
175 segments2 = (insegs + outsegs) * n;
176 width = M_PI * 2 / segments2;
180 for (i = 0; i < segments2; i++, tick++)
182 GLfloat th1 = i * width;
183 GLfloat th2 = th1 + width;
184 GLfloat cth1 = cos(th1);
185 GLfloat sth1 = sin(th1);
186 GLfloat cth2 = cos(th2);
187 GLfloat sth2 = sin(th2);
190 int changed = (i == 0);
192 if (state == 0 && tick == insegs)
193 tick = 0, state = 1, changed = 1;
194 else if (state == 1 && tick == outsegs)
195 tick = 0, state = 0, changed = 1;
197 if ((state == 1 || /* in */
198 (state == 0 && changed)) &&
199 (!wire_p || wire_all_p))
202 glFrontFace (GL_CCW);
203 glBegin (wire_p ? GL_LINES : GL_QUADS);
204 glNormal3f (0, 0, -1);
205 glVertex3f (s1 * cth1 * ra, s1 * sth1 * ra, z1);
206 glVertex3f (s1 * cth1 * rb, s1 * sth1 * rb, z1);
207 glVertex3f (s1 * cth2 * rb, s1 * sth2 * rb, z1);
208 glVertex3f (s1 * cth2 * ra, s1 * sth2 * ra, z1);
214 glBegin (wire_p ? GL_LINES : GL_QUADS);
215 glNormal3f (0, 0, 1);
216 glVertex3f (s2 * cth1 * ra, s2 * sth1 * ra, z2);
217 glVertex3f (s2 * cth1 * rb, s2 * sth1 * rb, z2);
218 glVertex3f (s2 * cth2 * rb, s2 * sth2 * rb, z2);
219 glVertex3f (s2 * cth2 * ra, s2 * sth2 * ra, z2);
224 if (state == 1 && changed) /* entering "in" state */
228 glBegin (wire_p ? GL_LINES : GL_QUADS);
229 do_normal (s1 * cth1 * rb, s1 * sth1 * rb, z1,
230 s1 * cth1 * ra, s1 * sth1 * ra, z1,
231 s2 * cth1 * rb, s2 * sth1 * rb, z2);
232 glVertex3f (s1 * cth1 * ra, s1 * sth1 * ra, z1);
233 glVertex3f (s1 * cth1 * rb, s1 * sth1 * rb, z1);
234 glVertex3f (s2 * cth1 * rb, s2 * sth1 * rb, z2);
235 glVertex3f (s2 * cth1 * ra, s2 * sth1 * ra, z2);
240 if (state == 0 && changed) /* entering "out" state */
243 glFrontFace (GL_CCW);
244 glBegin (wire_p ? GL_LINES : GL_QUADS);
245 do_normal (s1 * cth2 * ra, s1 * sth2 * ra, z1,
246 s1 * cth2 * rb, s1 * sth2 * rb, z1,
247 s2 * cth2 * rb, s2 * sth2 * rb, z2);
248 glVertex3f (s1 * cth2 * ra, s1 * sth2 * ra, z1);
249 glVertex3f (s1 * cth2 * rb, s1 * sth2 * rb, z1);
250 glVertex3f (s2 * cth2 * rb, s2 * sth2 * rb, z2);
251 glVertex3f (s2 * cth2 * ra, s2 * sth2 * ra, z2);
262 /* Draws some bumps (embedded cylinders) on the gear.
265 draw_gear_nubs (gear *g, Bool wire_p)
269 int steps = (g->size < INVOLUTE_LARGE ? 5 : 20);
270 double r, size, height;
275 if (! g->nubs) return 0;
277 which = involute_biggest_ring (g, &r, &size, &height);
281 cc = (which == 1 ? g->color : g->color2);
282 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cc);
285 r = g->r + size + g->tooth_h;
287 width = M_PI * 2 / g->nubs;
288 off = M_PI / (g->nteeth * 2); /* align first nub with a tooth */
290 for (i = 0; i < g->nubs; i++)
292 GLfloat th = (i * width) + off;
295 glRotatef (th * 180 / M_PI, 0, 0, 1);
296 glTranslatef (r, 0, 0);
298 if (g->inverted_p) /* nubs go on the outside rim */
300 size = g->thickness / 3;
301 height = (g->r - g->inner_r)/2;
302 glTranslatef (height, 0, 0);
303 glRotatef (90, 0, 1, 0);
306 if (wire_p && !wire_all_p)
307 polys += draw_ring ((g->size >= INVOLUTE_LARGE ? steps/2 : steps),
308 size, 0, 0, 0, False, wire_p);
311 polys += draw_disc (steps, 0, size, -height, True, wire_p);
312 polys += draw_disc (steps, 0, size, height, False, wire_p);
313 polys += draw_ring (steps, size, -height, height, 0, False, wire_p);
322 /* Draws a much simpler representation of a gear.
323 Returns the number of polygons.
326 draw_involute_schematic (gear *g, Bool wire_p)
330 GLfloat width = M_PI * 2 / g->nteeth;
332 if (!wire_p) glDisable(GL_LIGHTING);
333 glColor3f (g->color[0] * 0.6, g->color[1] * 0.6, g->color[2] * 0.6);
336 for (i = 0; i < g->nteeth; i++)
338 GLfloat th = (i * width) + (width/4);
339 glVertex3f (0, 0, -g->thickness/2);
340 glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
345 glBegin (GL_LINE_LOOP);
346 for (i = 0; i < g->nteeth; i++)
348 GLfloat th = (i * width) + (width/4);
349 glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
354 if (!wire_p) glEnable(GL_LIGHTING);
359 /* Renders all the interior (non-toothy) parts of a gear:
360 the discs, axles, etc.
363 draw_gear_interior (gear *g, Bool wire_p)
367 int steps = g->nteeth * 2;
368 if (steps < 10) steps = 10;
369 if ((wire_p && !wire_all_p) || g->size < INVOLUTE_LARGE) steps /= 2;
370 if (g->size < INVOLUTE_LARGE && steps > 16) steps = 16;
372 /* ring 1 (facing in) is done in draw_gear_teeth */
374 /* ring 2 (facing in) and disc 2
378 GLfloat ra = g->inner_r * 1.04; /* slightly larger than inner_r */
379 GLfloat rb = g->inner_r2; /* since the points don't line up */
380 GLfloat za = -g->thickness2/2;
381 GLfloat zb = g->thickness2/2;
382 GLfloat s1 = 1 + (g->thickness2 * g->tooth_slope / 2);
383 GLfloat s2 = 1 - (g->thickness2 * g->tooth_slope / 2);
385 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color2);
387 if ((g->coax_p != 1 && !g->inner_r3) ||
388 (wire_p && wire_all_p))
389 polys += /* ring facing in */
390 draw_ring (steps, rb, za, zb, g->tooth_slope, True, wire_p);
392 if (wire_p && wire_all_p)
393 polys += /* ring facing in */
394 draw_ring (steps, ra, za, zb, g->tooth_slope, True, wire_p);
397 polys += draw_spokes (g->spokes, g->spoke_thickness,
398 steps, ra, rb, za, zb, g->tooth_slope, wire_p);
399 else if (!wire_p || wire_all_p)
401 polys += /* top plate */
402 draw_disc (steps, s1*ra, s1*rb, za, True, wire_p);
403 polys += /* bottom plate */
404 draw_disc (steps, s2*ra, s2*rb, zb, False, wire_p);
408 /* ring 3 (facing in and out) and disc 3
412 GLfloat ra = g->inner_r2;
413 GLfloat rb = g->inner_r3;
414 GLfloat za = -g->thickness3/2;
415 GLfloat zb = g->thickness3/2;
416 GLfloat s1 = 1 + (g->thickness3 * g->tooth_slope / 2);
417 GLfloat s2 = 1 - (g->thickness3 * g->tooth_slope / 2);
419 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
421 polys += /* ring facing out */
422 draw_ring (steps, ra, za, zb, g->tooth_slope, False, wire_p);
424 if (g->coax_p != 1 || (wire_p && wire_all_p))
425 polys += /* ring facing in */
426 draw_ring (steps, rb, za, zb, g->tooth_slope, True, wire_p);
428 if (!wire_p || wire_all_p)
430 polys += /* top plate */
431 draw_disc (steps, s1*ra, s1*rb, za, True, wire_p);
432 polys += /* bottom plate */
433 draw_disc (steps, s2*ra, s2*rb, zb, False, wire_p);
441 GLfloat cap_height = g->coax_thickness/3;
443 GLfloat ra = (g->inner_r3 ? g->inner_r3 :
444 g->inner_r2 ? g->inner_r2 :
446 GLfloat za = -(g->thickness/2 + cap_height);
447 GLfloat zb = g->coax_thickness/2 + g->coax_displacement + cap_height;
449 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
451 if (wire_p && !wire_all_p) steps /= 2;
454 draw_ring (steps, ra, za, zb, g->tooth_slope, False, wire_p);
456 if (!wire_p || wire_all_p)
459 draw_disc (steps, 0, ra, za, True, wire_p); /* top plate */
461 draw_disc (steps, 0, ra, zb, False, wire_p); /* bottom plate */
468 /* gear_teeth_geometry computes the vertices and normals of the teeth
469 of a gear. This is the heavy lifting: there are a ton of polygons
470 around the perimiter of a gear, and the normals are difficult (not
471 radial or right angles.)
473 It would be nice if we could cache this, but the numbers are
474 different for essentially every gear:
476 - Every gear has a different inner_r, so the vertices of the
477 inner ring (and thus, the triangle fans on the top and bottom
478 faces) are different in a non-scalable way.
480 - If the ratio between tooth_w and tooth_h changes, the normals
481 on the outside edges of the teeth are invalid (this can happen
482 every time we start a new train.)
484 So instead, we rely on OpenGL display lists to do the cacheing for
485 us -- we only compute all these normals once per gear, instead of
486 once per gear per frame.
492 XYZ *fnormals; /* face normals */
493 XYZ *pnormals; /* point normals */
498 tooth_normals (tooth_face *f, GLfloat tooth_slope)
502 /* Compute the face normals for each facet. */
503 for (i = 0; i < f->npoints; i++)
507 int b = (i == f->npoints-1 ? 0 : i+1);
511 p3.x -= (p3.x * tooth_slope);
512 p3.y -= (p3.y * tooth_slope);
514 f->fnormals[i] = calc_normal (p1, p2, p3);
517 /* From the face normals, compute the vertex normals
518 (by averaging the normals of adjascent faces.)
520 for (i = 0; i < f->npoints; i++)
522 int a = (i == 0 ? f->npoints-1 : i-1);
524 XYZ n1 = f->fnormals[a]; /* normal of [i-1 - i] face */
525 XYZ n2 = f->fnormals[b]; /* normal of [i - i+1] face */
526 f->pnormals[i].x = (n1.x + n2.x) / 2;
527 f->pnormals[i].y = (n1.y + n2.y) / 2;
528 f->pnormals[i].z = (n1.z + n2.z) / 2;
534 gear_teeth_geometry (gear *g,
535 tooth_face *orim, /* outer rim (the teeth) */
536 tooth_face *irim) /* inner rim (the hole) */
539 int ppt = 20; /* max points per tooth */
540 GLfloat width = M_PI * 2 / g->nteeth;
541 GLfloat rh = g->tooth_h;
544 /* Approximate shape of an "involute" gear tooth.
547 th0 th2 th4 th6 th8 t10 t12 t14 th16 th18 th20
548 : : : : : : : : : : :
549 : : : : : : : : : : :
550 r0 ........:..:..:...___________...:..:..:......:......:..
551 : : : /: : :\ : : : : :
552 : : : / : : : \ : : : : :
553 : : :/ : : : \: : : : :
554 r2 ........:.....@...:....:....:...@..:..:......:......:..
555 : : @: : : : :@ : : : :
556 (R) ...........:...@.:...:....:....:...:.@..........:......:......
557 : :@ : : : : : @: : : :
558 r4 ........:..@..:...:....:....:...:..@:........:......:..
559 : /: : : : : : :\ : : :
560 :/ : : : : : : : \: : : /
561 r6 ......__/..:..:...:....:....:...:..:..\______________/
562 : : : : : : : : : : :
563 | : : : : : : : | : :
564 : : : : : : : : : : :
565 | : : : : : : : | : :
566 r8 ......__:_____________________________:________________
573 r[0] = R + (rh * 0.50);
574 r[1] = R + (rh * 0.40);
575 r[2] = R + (rh * 0.25);
576 r[3] = R + (rh * 0.05);
580 r[7] = r[6]; /* unused */
583 th[0] = -tw * (g->size == INVOLUTE_SMALL ? 0.5 :
584 g->size == INVOLUTE_MEDIUM ? 0.41 : 0.45);
588 th[4] = -tw * (g->nteeth >= 5 ? 0.16 : 0.12);
590 th[6] = -tw * (g->size == INVOLUTE_MEDIUM ? 0.1 : 0.04);
601 th[17] = width * 0.47;
602 th[18] = width * 0.50;
603 th[19] = width * 0.53;
604 th[20] = th[0] + width; /* unused */
606 if (g->inverted_p) /* put the teeth on the inside */
608 for (i = 0; i < countof(th); i++)
610 for (i = 0; i < countof(r); i++)
611 r[i] = R - (r[i] - R);
615 orim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->points));
616 orim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->fnormals));
617 orim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->pnormals));
620 irim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->points));
621 irim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->fnormals));
622 irim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->pnormals));
624 if (!orim->points || !orim->pnormals || !orim->fnormals ||
625 !irim->points || !irim->pnormals || !irim->fnormals)
627 fprintf (stderr, "%s: out of memory\n", progname);
631 /* First, compute the coordinates of every point used for every tooth.
633 for (i = 0; i < g->nteeth; i++)
635 GLfloat TH = (i * width) + (width/4);
636 int oon = orim->npoints;
637 int oin = irim->npoints;
640 # define PUSH(OPR,IPR,PTH) \
641 orim->points[orim->npoints].x = cos(TH+th[(PTH)]) * r[(OPR)]; \
642 orim->points[orim->npoints].y = sin(TH+th[(PTH)]) * r[(OPR)]; \
644 irim->points[irim->npoints].x = cos(TH+th[(PTH)]) * r[(IPR)]; \
645 irim->points[irim->npoints].y = sin(TH+th[(PTH)]) * r[(IPR)]; \
650 PUSH(6, 8, 0); /* tooth left 1 */
651 PUSH(0, 8, 8); /* tooth top middle */
653 case INVOLUTE_MEDIUM:
654 PUSH(6, 8, 0); /* tooth left 1 */
655 PUSH(0, 8, 6); /* tooth top left */
656 PUSH(0, 8, 10); /* tooth top right */
657 PUSH(6, 8, 16); /* tooth right 6 */
660 PUSH(6, 8, 0); /* tooth left 1 */
661 PUSH(4, 8, 2); /* tooth left 3 */
662 PUSH(2, 8, 4); /* tooth left 5 */
663 PUSH(0, 8, 6); /* tooth top left */
664 PUSH(0, 8, 10); /* tooth top right */
665 PUSH(2, 8, 12); /* tooth right 1 */
666 PUSH(4, 8, 14); /* tooth right 3 */
667 PUSH(6, 8, 16); /* tooth right 5 */
668 PUSH(6, 8, 18); /* gap top */
671 PUSH(6, 8, 0); /* tooth left 1 */
672 PUSH(5, 8, 1); /* tooth left 2 */
673 PUSH(4, 8, 2); /* tooth left 3 */
674 PUSH(3, 8, 3); /* tooth left 4 */
675 PUSH(2, 8, 4); /* tooth left 5 */
676 PUSH(1, 8, 5); /* tooth left 6 */
677 PUSH(0, 8, 6); /* tooth top left */
678 PUSH(0, 8, 8); /* tooth top left */
679 PUSH(0, 8, 10); /* tooth top right */
680 PUSH(1, 8, 11); /* tooth top right */
681 PUSH(2, 8, 12); /* tooth right 1 */
682 PUSH(3, 8, 13); /* tooth right 2 */
683 PUSH(4, 8, 14); /* tooth right 3 */
684 PUSH(5, 8, 15); /* tooth right 4 */
685 PUSH(6, 8, 16); /* tooth right 5 */
686 PUSH(6, 8, 17); /* tooth right 6 */
687 PUSH(6, 8, 18); /* gap top */
688 PUSH(6, 8, 19); /* gap top */
695 if (i == 0 && orim->npoints > ppt) abort(); /* go update "ppt"! */
702 for (j = 0; j < (end-start)/2; j++)
704 XYZ swap = orim->points[end-j-1];
705 orim->points[end-j-1] = orim->points[start+j];
706 orim->points[start+j] = swap;
711 for (j = 0; j < (end-start)/2; j++)
713 XYZ swap = irim->points[end-j-1];
714 irim->points[end-j-1] = irim->points[start+j];
715 irim->points[start+j] = swap;
720 tooth_normals (orim, g->tooth_slope);
721 tooth_normals (irim, 0);
723 if (g->inverted_p) /* flip the normals */
725 for (i = 0; i < orim->npoints; i++)
727 orim->fnormals[i].x = -orim->fnormals[i].x;
728 orim->fnormals[i].y = -orim->fnormals[i].y;
729 orim->fnormals[i].z = -orim->fnormals[i].z;
731 orim->pnormals[i].x = -orim->pnormals[i].x;
732 orim->pnormals[i].y = -orim->pnormals[i].y;
733 orim->pnormals[i].z = -orim->pnormals[i].z;
736 for (i = 0; i < irim->npoints; i++)
738 irim->fnormals[i].x = -irim->fnormals[i].x;
739 irim->fnormals[i].y = -irim->fnormals[i].y;
740 irim->fnormals[i].z = -irim->fnormals[i].z;
742 irim->pnormals[i].x = -irim->pnormals[i].x;
743 irim->pnormals[i].y = -irim->pnormals[i].y;
744 irim->pnormals[i].z = -irim->pnormals[i].z;
750 /* Which of the gear's inside rings is the biggest?
753 involute_biggest_ring (gear *g, double *posP, double *sizeP, double *heightP)
755 double r0 = (g->r - g->tooth_h/2);
756 double r1 = g->inner_r;
757 double r2 = g->inner_r2;
758 double r3 = g->inner_r3;
759 double w1 = (r1 ? r0 - r1 : r0);
760 double w2 = (r2 ? r1 - r2 : 0);
761 double w3 = (r3 ? r2 - r3 : 0);
762 double h1 = g->thickness;
763 double h2 = g->thickness2;
764 double h3 = g->thickness3;
766 if (g->spokes) w2 = 0;
768 if (w1 > w2 && w1 > w3)
770 if (posP) *posP = (r0+r1)/2;
771 if (sizeP) *sizeP = w1;
772 if (heightP) *heightP = h1;
775 else if (w2 > w1 && w2 > w3)
777 if (posP) *posP = (r1+r2)/2;
778 if (sizeP) *sizeP = w2;
779 if (heightP) *heightP = h2;
784 if (posP) *posP = (r2+r3)/2;
785 if (sizeP) *sizeP = w3;
786 if (heightP) *heightP = h3;
792 /* Renders all teeth of a gear.
795 draw_gear_teeth (gear *g, Bool wire_p)
800 GLfloat z1 = -g->thickness/2;
801 GLfloat z2 = g->thickness/2;
802 GLfloat s1 = 1 + (g->thickness * g->tooth_slope / 2);
803 GLfloat s2 = 1 - (g->thickness * g->tooth_slope / 2);
805 tooth_face orim, irim;
806 gear_teeth_geometry (g, &orim, &irim);
808 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
810 /* Draw the outer rim (the teeth)
811 (In wire mode, this draws just the upright lines.)
813 glFrontFace (g->inverted_p ? GL_CCW : GL_CW);
814 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
815 for (i = 0; i < orim.npoints; i++)
817 glNormal3f (orim.pnormals[i].x, orim.pnormals[i].y, orim.pnormals[i].z);
818 glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
819 glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
821 /* Show the face normal vectors */
822 if (0&&wire_p && show_normals_p)
824 XYZ n = orim.fnormals[i];
826 int b = (i == orim.npoints-1 ? 0 : i+1);
827 GLfloat x = (orim.points[a].x + orim.points[b].x) / 2;
828 GLfloat y = (orim.points[a].y + orim.points[b].y) / 2;
829 GLfloat z = (z1 + z2) / 2;
830 glVertex3f (x, y, z);
831 glVertex3f (x + n.x, y + n.y, z + n.z);
834 /* Show the vertex normal vectors */
835 if (wire_p && show_normals_p)
837 XYZ n = orim.pnormals[i];
838 GLfloat x = orim.points[i].x;
839 GLfloat y = orim.points[i].y;
840 GLfloat z = (z1 + z2) / 2;
841 glVertex3f (x, y, z);
842 glVertex3f (x + n.x, y + n.y, z + n.z);
846 if (!wire_p) /* close the quad loop */
848 glNormal3f (orim.pnormals[0].x, orim.pnormals[0].y, orim.pnormals[0].z);
849 glVertex3f (s1*orim.points[0].x, s1*orim.points[0].y, z1);
850 glVertex3f (s2*orim.points[0].x, s2*orim.points[0].y, z2);
852 polys += orim.npoints;
855 /* Draw the outer rim circles, in wire mode */
858 glBegin (GL_LINE_LOOP);
859 for (i = 0; i < orim.npoints; i++)
860 glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
862 glBegin (GL_LINE_LOOP);
863 for (i = 0; i < orim.npoints; i++)
864 glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
868 /* Draw the inner rim (the hole)
869 (In wire mode, this draws just the upright lines.)
871 glFrontFace (g->inverted_p ? GL_CW : GL_CCW);
872 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
873 for (i = 0; i < irim.npoints; i++)
875 glNormal3f(-irim.pnormals[i].x, -irim.pnormals[i].y,-irim.pnormals[i].z);
876 glVertex3f (s1*irim.points[i].x, s1*irim.points[i].y, z1);
877 glVertex3f (s2*irim.points[i].x, s2*irim.points[i].y, z2);
879 /* Show the face normal vectors */
880 if (wire_p && show_normals_p)
882 XYZ n = irim.fnormals[i];
884 int b = (i == irim.npoints-1 ? 0 : i+1);
885 GLfloat x = (irim.points[a].x + irim.points[b].x) / 2;
886 GLfloat y = (irim.points[a].y + irim.points[b].y) / 2;
887 GLfloat z = (z1 + z2) / 2;
888 glVertex3f (x, y, z);
889 glVertex3f (x - n.x, y - n.y, z);
892 /* Show the vertex normal vectors */
893 if (wire_p && show_normals_p)
895 XYZ n = irim.pnormals[i];
896 GLfloat x = irim.points[i].x;
897 GLfloat y = irim.points[i].y;
898 GLfloat z = (z1 + z2) / 2;
899 glVertex3f (x, y, z);
900 glVertex3f (x - n.x, y - n.y, z);
904 if (!wire_p) /* close the quad loop */
906 glNormal3f (-irim.pnormals[0].x,-irim.pnormals[0].y,-irim.pnormals[0].z);
907 glVertex3f (s1*irim.points[0].x, s1*irim.points[0].y, z1);
908 glVertex3f (s2*irim.points[0].x, s2*irim.points[0].y, z2);
910 polys += irim.npoints;
913 /* Draw the inner rim circles, in wire mode
917 glBegin (GL_LINE_LOOP);
918 for (i = 0; i < irim.npoints; i++)
919 glVertex3f (irim.points[i].x, irim.points[i].y, z1);
921 glBegin (GL_LINE_LOOP);
922 for (i = 0; i < irim.npoints; i++)
923 glVertex3f (irim.points[i].x, irim.points[i].y, z2);
927 /* Draw the side (the flat bit)
929 if (!wire_p || wire_all_p)
932 if (irim.npoints != orim.npoints) abort();
933 for (z = z1; z <= z2; z += z2-z1)
935 GLfloat s = (z == z1 ? s1 : s2);
936 glFrontFace (((z == z1) ^ g->inverted_p) ? GL_CCW : GL_CW);
937 glNormal3f (0, 0, z);
938 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
939 for (i = 0; i < orim.npoints; i++)
941 glVertex3f (s*orim.points[i].x, s*orim.points[i].y, z);
942 glVertex3f (s*irim.points[i].x, s*irim.points[i].y, z);
944 if (!wire_p) /* close the quad loop */
946 glVertex3f (s*orim.points[0].x, s*orim.points[0].y, z);
947 glVertex3f (s*irim.points[0].x, s*irim.points[0].y, z);
949 polys += orim.npoints;
955 free (irim.fnormals);
956 free (irim.pnormals);
959 free (orim.fnormals);
960 free (orim.pnormals);
966 /* Render one gear, unrotated at 0,0.
967 Returns the number of polygons.
970 draw_involute_gear (gear *g, Bool wire_p)
974 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
975 GLfloat shiny = 128.0;
977 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
978 glMateriali (GL_FRONT, GL_SHININESS, shiny);
979 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
980 glColor3f (g->color[0], g->color[1], g->color[2]);
983 polys += draw_involute_schematic (g, wire_p);
987 glRotatef (g->wobble, 1, 0, 0);
988 polys += draw_gear_teeth (g, wire_p);
989 polys += draw_gear_interior (g, wire_p);
990 polys += draw_gear_nubs (g, wire_p);