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"
31 #endif /* HAVE_JWZGLES */
37 #define countof(x) (sizeof((x))/sizeof((*x)))
40 /* For debugging: if true then in wireframe, do not abbreviate. */
41 static Bool wire_all_p = False;
42 static Bool show_normals_p = False;
45 /* Draws an uncapped tube of the given radius extending from top to bottom,
46 with faces pointing either in or out.
49 draw_ring (int segments,
50 GLfloat r, GLfloat top, GLfloat bottom, GLfloat slope,
51 Bool in_p, Bool wire_p)
55 GLfloat width = M_PI * 2 / segments;
57 GLfloat s1 = 1 + ((bottom-top) * slope / 2);
58 GLfloat s2 = 1 - ((bottom-top) * slope / 2);
62 glFrontFace (in_p ? GL_CCW : GL_CW);
63 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
64 for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
66 GLfloat th = i * width;
67 GLfloat cth = cos(th);
68 GLfloat sth = sin(th);
70 glNormal3f (-cth, -sth, 0);
72 glNormal3f (cth, sth, 0);
73 glVertex3f (s1 * cth * r, s1 * sth * r, top);
74 glVertex3f (s2 * cth * r, s2 * sth * r, bottom);
82 glBegin (GL_LINE_LOOP);
83 for (i = 0; i < segments; i++)
85 GLfloat th = i * width;
86 glVertex3f (cos(th) * r, sin(th) * r, top);
89 glBegin (GL_LINE_LOOP);
90 for (i = 0; i < segments; i++)
92 GLfloat th = i * width;
93 glVertex3f (cos(th) * r, sin(th) * r, bottom);
102 /* Draws a donut-shaped disc between the given radii,
103 with faces pointing either up or down.
104 The first radius may be 0, in which case, a filled disc is drawn.
107 draw_disc (int segments,
108 GLfloat ra, GLfloat rb, GLfloat z,
109 Bool up_p, Bool wire_p)
113 GLfloat width = M_PI * 2 / segments;
116 if (rb <= 0) abort();
119 glFrontFace (up_p ? GL_CW : GL_CCW);
121 glFrontFace (up_p ? GL_CCW : GL_CW);
124 glBegin (wire_p ? GL_LINES : GL_TRIANGLE_FAN);
126 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
128 glNormal3f (0, 0, (up_p ? -1 : 1));
130 if (ra == 0 && !wire_p)
131 glVertex3f (0, 0, z);
133 for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
135 GLfloat th = i * width;
136 GLfloat cth = cos(th);
137 GLfloat sth = sin(th);
138 if (wire_p || ra != 0)
139 glVertex3f (cth * ra, sth * ra, z);
140 glVertex3f (cth * rb, sth * rb, z);
148 /* Draws N thick radial lines between the given radii,
149 with faces pointing either up or down.
152 draw_spokes (int n, GLfloat thickness, int segments,
153 GLfloat ra, GLfloat rb, GLfloat z1, GLfloat z2, GLfloat slope,
164 GLfloat s1 = 1 + ((z2-z1) * slope / 2);
165 GLfloat s2 = 1 - ((z2-z1) * slope / 2);
167 if (ra <= 0 || rb <= 0) abort();
171 while (segments2 < segments) /* need a multiple of N >= segments */
172 segments2 += n; /* (yes, this is a moronic way to find that) */
174 insegs = ((float) (segments2 / n) + 0.5) / thickness;
175 outsegs = (segments2 / n) - insegs;
176 if (insegs <= 0) insegs = 1;
177 if (outsegs <= 0) outsegs = 1;
179 segments2 = (insegs + outsegs) * n;
180 width = M_PI * 2 / segments2;
184 for (i = 0; i < segments2; i++, tick++)
186 GLfloat th1 = i * width;
187 GLfloat th2 = th1 + width;
188 GLfloat cth1 = cos(th1);
189 GLfloat sth1 = sin(th1);
190 GLfloat cth2 = cos(th2);
191 GLfloat sth2 = sin(th2);
194 int changed = (i == 0);
196 if (state == 0 && tick == insegs)
197 tick = 0, state = 1, changed = 1;
198 else if (state == 1 && tick == outsegs)
199 tick = 0, state = 0, changed = 1;
201 if ((state == 1 || /* in */
202 (state == 0 && changed)) &&
203 (!wire_p || wire_all_p))
206 glFrontFace (GL_CCW);
207 glBegin (wire_p ? GL_LINES : GL_QUADS);
208 glNormal3f (0, 0, -1);
209 glVertex3f (s1 * cth1 * ra, s1 * sth1 * ra, z1);
210 glVertex3f (s1 * cth1 * rb, s1 * sth1 * rb, z1);
211 glVertex3f (s1 * cth2 * rb, s1 * sth2 * rb, z1);
212 glVertex3f (s1 * cth2 * ra, s1 * sth2 * ra, z1);
218 glBegin (wire_p ? GL_LINES : GL_QUADS);
219 glNormal3f (0, 0, 1);
220 glVertex3f (s2 * cth1 * ra, s2 * sth1 * ra, z2);
221 glVertex3f (s2 * cth1 * rb, s2 * sth1 * rb, z2);
222 glVertex3f (s2 * cth2 * rb, s2 * sth2 * rb, z2);
223 glVertex3f (s2 * cth2 * ra, s2 * sth2 * ra, z2);
228 if (state == 1 && changed) /* entering "in" state */
232 glBegin (wire_p ? GL_LINES : GL_QUADS);
233 do_normal (s1 * cth1 * rb, s1 * sth1 * rb, z1,
234 s1 * cth1 * ra, s1 * sth1 * ra, z1,
235 s2 * cth1 * rb, s2 * sth1 * rb, z2);
236 glVertex3f (s1 * cth1 * ra, s1 * sth1 * ra, z1);
237 glVertex3f (s1 * cth1 * rb, s1 * sth1 * rb, z1);
238 glVertex3f (s2 * cth1 * rb, s2 * sth1 * rb, z2);
239 glVertex3f (s2 * cth1 * ra, s2 * sth1 * ra, z2);
244 if (state == 0 && changed) /* entering "out" state */
247 glFrontFace (GL_CCW);
248 glBegin (wire_p ? GL_LINES : GL_QUADS);
249 do_normal (s1 * cth2 * ra, s1 * sth2 * ra, z1,
250 s1 * cth2 * rb, s1 * sth2 * rb, z1,
251 s2 * cth2 * rb, s2 * sth2 * rb, z2);
252 glVertex3f (s1 * cth2 * ra, s1 * sth2 * ra, z1);
253 glVertex3f (s1 * cth2 * rb, s1 * sth2 * rb, z1);
254 glVertex3f (s2 * cth2 * rb, s2 * sth2 * rb, z2);
255 glVertex3f (s2 * cth2 * ra, s2 * sth2 * ra, z2);
266 /* Draws some bumps (embedded cylinders) on the gear.
269 draw_gear_nubs (gear *g, Bool wire_p)
273 int steps = (g->size < INVOLUTE_LARGE ? 5 : 20);
274 double r, size, height;
279 if (! g->nubs) return 0;
281 which = involute_biggest_ring (g, &r, &size, &height);
285 cc = (which == 1 ? g->color : g->color2);
286 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cc);
289 r = g->r + size + g->tooth_h;
291 width = M_PI * 2 / g->nubs;
292 off = M_PI / (g->nteeth * 2); /* align first nub with a tooth */
294 for (i = 0; i < g->nubs; i++)
296 GLfloat th = (i * width) + off;
299 glRotatef (th * 180 / M_PI, 0, 0, 1);
300 glTranslatef (r, 0, 0);
302 if (g->inverted_p) /* nubs go on the outside rim */
304 size = g->thickness / 3;
305 height = (g->r - g->inner_r)/2;
306 glTranslatef (height, 0, 0);
307 glRotatef (90, 0, 1, 0);
310 if (wire_p && !wire_all_p)
311 polys += draw_ring ((g->size >= INVOLUTE_LARGE ? steps/2 : steps),
312 size, 0, 0, 0, False, wire_p);
315 polys += draw_disc (steps, 0, size, -height, True, wire_p);
316 polys += draw_disc (steps, 0, size, height, False, wire_p);
317 polys += draw_ring (steps, size, -height, height, 0, False, wire_p);
326 /* Draws a much simpler representation of a gear.
327 Returns the number of polygons.
330 draw_involute_schematic (gear *g, Bool wire_p)
334 GLfloat width = M_PI * 2 / g->nteeth;
336 if (!wire_p) glDisable(GL_LIGHTING);
337 glColor3f (g->color[0] * 0.6, g->color[1] * 0.6, g->color[2] * 0.6);
340 for (i = 0; i < g->nteeth; i++)
342 GLfloat th = (i * width) + (width/4);
343 glVertex3f (0, 0, -g->thickness/2);
344 glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
349 glBegin (GL_LINE_LOOP);
350 for (i = 0; i < g->nteeth; i++)
352 GLfloat th = (i * width) + (width/4);
353 glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
358 if (!wire_p) glEnable(GL_LIGHTING);
363 /* Renders all the interior (non-toothy) parts of a gear:
364 the discs, axles, etc.
367 draw_gear_interior (gear *g, Bool wire_p)
371 int steps = g->nteeth * 2;
372 if (steps < 10) steps = 10;
373 if ((wire_p && !wire_all_p) || g->size < INVOLUTE_LARGE) steps /= 2;
374 if (g->size < INVOLUTE_LARGE && steps > 16) steps = 16;
376 /* ring 1 (facing in) is done in draw_gear_teeth */
378 /* ring 2 (facing in) and disc 2
382 GLfloat ra = g->inner_r * 1.04; /* slightly larger than inner_r */
383 GLfloat rb = g->inner_r2; /* since the points don't line up */
384 GLfloat za = -g->thickness2/2;
385 GLfloat zb = g->thickness2/2;
386 GLfloat s1 = 1 + (g->thickness2 * g->tooth_slope / 2);
387 GLfloat s2 = 1 - (g->thickness2 * g->tooth_slope / 2);
389 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color2);
391 if ((g->coax_p != 1 && !g->inner_r3) ||
392 (wire_p && wire_all_p))
393 polys += /* ring facing in */
394 draw_ring (steps, rb, za, zb, g->tooth_slope, True, wire_p);
396 if (wire_p && wire_all_p)
397 polys += /* ring facing in */
398 draw_ring (steps, ra, za, zb, g->tooth_slope, True, wire_p);
401 polys += draw_spokes (g->spokes, g->spoke_thickness,
402 steps, ra, rb, za, zb, g->tooth_slope, wire_p);
403 else if (!wire_p || wire_all_p)
405 polys += /* top plate */
406 draw_disc (steps, s1*ra, s1*rb, za, True, wire_p);
407 polys += /* bottom plate */
408 draw_disc (steps, s2*ra, s2*rb, zb, False, wire_p);
412 /* ring 3 (facing in and out) and disc 3
416 GLfloat ra = g->inner_r2;
417 GLfloat rb = g->inner_r3;
418 GLfloat za = -g->thickness3/2;
419 GLfloat zb = g->thickness3/2;
420 GLfloat s1 = 1 + (g->thickness3 * g->tooth_slope / 2);
421 GLfloat s2 = 1 - (g->thickness3 * g->tooth_slope / 2);
423 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
425 polys += /* ring facing out */
426 draw_ring (steps, ra, za, zb, g->tooth_slope, False, wire_p);
428 if (g->coax_p != 1 || (wire_p && wire_all_p))
429 polys += /* ring facing in */
430 draw_ring (steps, rb, za, zb, g->tooth_slope, True, wire_p);
432 if (!wire_p || wire_all_p)
434 polys += /* top plate */
435 draw_disc (steps, s1*ra, s1*rb, za, True, wire_p);
436 polys += /* bottom plate */
437 draw_disc (steps, s2*ra, s2*rb, zb, False, wire_p);
445 GLfloat cap_height = g->coax_thickness/3;
447 GLfloat ra = (g->inner_r3 ? g->inner_r3 :
448 g->inner_r2 ? g->inner_r2 :
450 GLfloat za = -(g->thickness/2 + cap_height);
451 GLfloat zb = g->coax_thickness/2 + g->coax_displacement + cap_height;
453 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
455 if (wire_p && !wire_all_p) steps /= 2;
458 draw_ring (steps, ra, za, zb, g->tooth_slope, False, wire_p);
460 if (!wire_p || wire_all_p)
463 draw_disc (steps, 0, ra, za, True, wire_p); /* top plate */
465 draw_disc (steps, 0, ra, zb, False, wire_p); /* bottom plate */
472 /* gear_teeth_geometry computes the vertices and normals of the teeth
473 of a gear. This is the heavy lifting: there are a ton of polygons
474 around the perimiter of a gear, and the normals are difficult (not
475 radial or right angles.)
477 It would be nice if we could cache this, but the numbers are
478 different for essentially every gear:
480 - Every gear has a different inner_r, so the vertices of the
481 inner ring (and thus, the triangle fans on the top and bottom
482 faces) are different in a non-scalable way.
484 - If the ratio between tooth_w and tooth_h changes, the normals
485 on the outside edges of the teeth are invalid (this can happen
486 every time we start a new train.)
488 So instead, we rely on OpenGL display lists to do the cacheing for
489 us -- we only compute all these normals once per gear, instead of
490 once per gear per frame.
496 XYZ *fnormals; /* face normals */
497 XYZ *pnormals; /* point normals */
502 tooth_normals (tooth_face *f, GLfloat tooth_slope)
506 /* Compute the face normals for each facet. */
507 for (i = 0; i < f->npoints; i++)
511 int b = (i == f->npoints-1 ? 0 : i+1);
515 p3.x -= (p3.x * tooth_slope);
516 p3.y -= (p3.y * tooth_slope);
518 f->fnormals[i] = calc_normal (p1, p2, p3);
521 /* From the face normals, compute the vertex normals
522 (by averaging the normals of adjascent faces.)
524 for (i = 0; i < f->npoints; i++)
526 int a = (i == 0 ? f->npoints-1 : i-1);
528 XYZ n1 = f->fnormals[a]; /* normal of [i-1 - i] face */
529 XYZ n2 = f->fnormals[b]; /* normal of [i - i+1] face */
530 f->pnormals[i].x = (n1.x + n2.x) / 2;
531 f->pnormals[i].y = (n1.y + n2.y) / 2;
532 f->pnormals[i].z = (n1.z + n2.z) / 2;
538 gear_teeth_geometry (gear *g,
539 tooth_face *orim, /* outer rim (the teeth) */
540 tooth_face *irim) /* inner rim (the hole) */
543 int ppt = 20; /* max points per tooth */
544 GLfloat width = M_PI * 2 / g->nteeth;
545 GLfloat rh = g->tooth_h;
548 /* Approximate shape of an "involute" gear tooth.
551 th0 th2 th4 th6 th8 t10 t12 t14 th16 th18 th20
552 : : : : : : : : : : :
553 : : : : : : : : : : :
554 r0 ........:..:..:...___________...:..:..:......:......:..
555 : : : /: : :\ : : : : :
556 : : : / : : : \ : : : : :
557 : : :/ : : : \: : : : :
558 r2 ........:.....@...:....:....:...@..:..:......:......:..
559 : : @: : : : :@ : : : :
560 (R) ...........:...@.:...:....:....:...:.@..........:......:......
561 : :@ : : : : : @: : : :
562 r4 ........:..@..:...:....:....:...:..@:........:......:..
563 : /: : : : : : :\ : : :
564 :/ : : : : : : : \: : : /
565 r6 ......__/..:..:...:....:....:...:..:..\______________/
566 : : : : : : : : : : :
567 | : : : : : : : | : :
568 : : : : : : : : : : :
569 | : : : : : : : | : :
570 r8 ......__:_____________________________:________________
577 r[0] = R + (rh * 0.50);
578 r[1] = R + (rh * 0.40);
579 r[2] = R + (rh * 0.25);
580 r[3] = R + (rh * 0.05);
584 r[7] = r[6]; /* unused */
587 th[0] = -tw * (g->size == INVOLUTE_SMALL ? 0.5 :
588 g->size == INVOLUTE_MEDIUM ? 0.41 : 0.45);
592 th[4] = -tw * (g->nteeth >= 5 ? 0.16 : 0.12);
594 th[6] = -tw * (g->size == INVOLUTE_MEDIUM ? 0.1 : 0.04);
605 th[17] = width * 0.47;
606 th[18] = width * 0.50;
607 th[19] = width * 0.53;
608 th[20] = th[0] + width; /* unused */
610 if (g->inverted_p) /* put the teeth on the inside */
612 for (i = 0; i < countof(th); i++)
614 for (i = 0; i < countof(r); i++)
615 r[i] = R - (r[i] - R);
619 orim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->points));
620 orim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->fnormals));
621 orim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->pnormals));
624 irim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->points));
625 irim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->fnormals));
626 irim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->pnormals));
628 if (!orim->points || !orim->pnormals || !orim->fnormals ||
629 !irim->points || !irim->pnormals || !irim->fnormals)
631 fprintf (stderr, "%s: out of memory\n", progname);
635 /* First, compute the coordinates of every point used for every tooth.
637 for (i = 0; i < g->nteeth; i++)
639 GLfloat TH = (i * width) + (width/4);
640 int oon = orim->npoints;
641 int oin = irim->npoints;
644 # define PUSH(OPR,IPR,PTH) \
645 orim->points[orim->npoints].x = cos(TH+th[(PTH)]) * r[(OPR)]; \
646 orim->points[orim->npoints].y = sin(TH+th[(PTH)]) * r[(OPR)]; \
648 irim->points[irim->npoints].x = cos(TH+th[(PTH)]) * r[(IPR)]; \
649 irim->points[irim->npoints].y = sin(TH+th[(PTH)]) * r[(IPR)]; \
654 PUSH(6, 8, 0); /* tooth left 1 */
655 PUSH(0, 8, 8); /* tooth top middle */
657 case INVOLUTE_MEDIUM:
658 PUSH(6, 8, 0); /* tooth left 1 */
659 PUSH(0, 8, 6); /* tooth top left */
660 PUSH(0, 8, 10); /* tooth top right */
661 PUSH(6, 8, 16); /* tooth right 6 */
664 PUSH(6, 8, 0); /* tooth left 1 */
665 PUSH(4, 8, 2); /* tooth left 3 */
666 PUSH(2, 8, 4); /* tooth left 5 */
667 PUSH(0, 8, 6); /* tooth top left */
668 PUSH(0, 8, 10); /* tooth top right */
669 PUSH(2, 8, 12); /* tooth right 1 */
670 PUSH(4, 8, 14); /* tooth right 3 */
671 PUSH(6, 8, 16); /* tooth right 5 */
672 PUSH(6, 8, 18); /* gap top */
675 PUSH(6, 8, 0); /* tooth left 1 */
676 PUSH(5, 8, 1); /* tooth left 2 */
677 PUSH(4, 8, 2); /* tooth left 3 */
678 PUSH(3, 8, 3); /* tooth left 4 */
679 PUSH(2, 8, 4); /* tooth left 5 */
680 PUSH(1, 8, 5); /* tooth left 6 */
681 PUSH(0, 8, 6); /* tooth top left */
682 PUSH(0, 8, 8); /* tooth top left */
683 PUSH(0, 8, 10); /* tooth top right */
684 PUSH(1, 8, 11); /* tooth top right */
685 PUSH(2, 8, 12); /* tooth right 1 */
686 PUSH(3, 8, 13); /* tooth right 2 */
687 PUSH(4, 8, 14); /* tooth right 3 */
688 PUSH(5, 8, 15); /* tooth right 4 */
689 PUSH(6, 8, 16); /* tooth right 5 */
690 PUSH(6, 8, 17); /* tooth right 6 */
691 PUSH(6, 8, 18); /* gap top */
692 PUSH(6, 8, 19); /* gap top */
699 if (i == 0 && orim->npoints > ppt) abort(); /* go update "ppt"! */
706 for (j = 0; j < (end-start)/2; j++)
708 XYZ swap = orim->points[end-j-1];
709 orim->points[end-j-1] = orim->points[start+j];
710 orim->points[start+j] = swap;
715 for (j = 0; j < (end-start)/2; j++)
717 XYZ swap = irim->points[end-j-1];
718 irim->points[end-j-1] = irim->points[start+j];
719 irim->points[start+j] = swap;
724 tooth_normals (orim, g->tooth_slope);
725 tooth_normals (irim, 0);
727 if (g->inverted_p) /* flip the normals */
729 for (i = 0; i < orim->npoints; i++)
731 orim->fnormals[i].x = -orim->fnormals[i].x;
732 orim->fnormals[i].y = -orim->fnormals[i].y;
733 orim->fnormals[i].z = -orim->fnormals[i].z;
735 orim->pnormals[i].x = -orim->pnormals[i].x;
736 orim->pnormals[i].y = -orim->pnormals[i].y;
737 orim->pnormals[i].z = -orim->pnormals[i].z;
740 for (i = 0; i < irim->npoints; i++)
742 irim->fnormals[i].x = -irim->fnormals[i].x;
743 irim->fnormals[i].y = -irim->fnormals[i].y;
744 irim->fnormals[i].z = -irim->fnormals[i].z;
746 irim->pnormals[i].x = -irim->pnormals[i].x;
747 irim->pnormals[i].y = -irim->pnormals[i].y;
748 irim->pnormals[i].z = -irim->pnormals[i].z;
754 /* Which of the gear's inside rings is the biggest?
757 involute_biggest_ring (gear *g, double *posP, double *sizeP, double *heightP)
759 double r0 = (g->r - g->tooth_h/2);
760 double r1 = g->inner_r;
761 double r2 = g->inner_r2;
762 double r3 = g->inner_r3;
763 double w1 = (r1 ? r0 - r1 : r0);
764 double w2 = (r2 ? r1 - r2 : 0);
765 double w3 = (r3 ? r2 - r3 : 0);
766 double h1 = g->thickness;
767 double h2 = g->thickness2;
768 double h3 = g->thickness3;
770 if (g->spokes) w2 = 0;
772 if (w1 > w2 && w1 > w3)
774 if (posP) *posP = (r0+r1)/2;
775 if (sizeP) *sizeP = w1;
776 if (heightP) *heightP = h1;
779 else if (w2 > w1 && w2 > w3)
781 if (posP) *posP = (r1+r2)/2;
782 if (sizeP) *sizeP = w2;
783 if (heightP) *heightP = h2;
788 if (posP) *posP = (r2+r3)/2;
789 if (sizeP) *sizeP = w3;
790 if (heightP) *heightP = h3;
796 /* Renders all teeth of a gear.
799 draw_gear_teeth (gear *g, Bool wire_p)
804 GLfloat z1 = -g->thickness/2;
805 GLfloat z2 = g->thickness/2;
806 GLfloat s1 = 1 + (g->thickness * g->tooth_slope / 2);
807 GLfloat s2 = 1 - (g->thickness * g->tooth_slope / 2);
809 tooth_face orim, irim;
810 gear_teeth_geometry (g, &orim, &irim);
812 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
814 /* Draw the outer rim (the teeth)
815 (In wire mode, this draws just the upright lines.)
817 glFrontFace (g->inverted_p ? GL_CCW : GL_CW);
818 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
819 for (i = 0; i < orim.npoints; i++)
821 glNormal3f (orim.pnormals[i].x, orim.pnormals[i].y, orim.pnormals[i].z);
822 glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
823 glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
825 /* Show the face normal vectors */
826 if (0&&wire_p && show_normals_p)
828 XYZ n = orim.fnormals[i];
830 int b = (i == orim.npoints-1 ? 0 : i+1);
831 GLfloat x = (orim.points[a].x + orim.points[b].x) / 2;
832 GLfloat y = (orim.points[a].y + orim.points[b].y) / 2;
833 GLfloat z = (z1 + z2) / 2;
834 glVertex3f (x, y, z);
835 glVertex3f (x + n.x, y + n.y, z + n.z);
838 /* Show the vertex normal vectors */
839 if (wire_p && show_normals_p)
841 XYZ n = orim.pnormals[i];
842 GLfloat x = orim.points[i].x;
843 GLfloat y = orim.points[i].y;
844 GLfloat z = (z1 + z2) / 2;
845 glVertex3f (x, y, z);
846 glVertex3f (x + n.x, y + n.y, z + n.z);
850 if (!wire_p) /* close the quad loop */
852 glNormal3f (orim.pnormals[0].x, orim.pnormals[0].y, orim.pnormals[0].z);
853 glVertex3f (s1*orim.points[0].x, s1*orim.points[0].y, z1);
854 glVertex3f (s2*orim.points[0].x, s2*orim.points[0].y, z2);
856 polys += orim.npoints;
859 /* Draw the outer rim circles, in wire mode */
862 glBegin (GL_LINE_LOOP);
863 for (i = 0; i < orim.npoints; i++)
864 glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
866 glBegin (GL_LINE_LOOP);
867 for (i = 0; i < orim.npoints; i++)
868 glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
872 /* Draw the inner rim (the hole)
873 (In wire mode, this draws just the upright lines.)
875 glFrontFace (g->inverted_p ? GL_CW : GL_CCW);
876 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
877 for (i = 0; i < irim.npoints; i++)
879 glNormal3f(-irim.pnormals[i].x, -irim.pnormals[i].y,-irim.pnormals[i].z);
880 glVertex3f (s1*irim.points[i].x, s1*irim.points[i].y, z1);
881 glVertex3f (s2*irim.points[i].x, s2*irim.points[i].y, z2);
883 /* Show the face normal vectors */
884 if (wire_p && show_normals_p)
886 XYZ n = irim.fnormals[i];
888 int b = (i == irim.npoints-1 ? 0 : i+1);
889 GLfloat x = (irim.points[a].x + irim.points[b].x) / 2;
890 GLfloat y = (irim.points[a].y + irim.points[b].y) / 2;
891 GLfloat z = (z1 + z2) / 2;
892 glVertex3f (x, y, z);
893 glVertex3f (x - n.x, y - n.y, z);
896 /* Show the vertex normal vectors */
897 if (wire_p && show_normals_p)
899 XYZ n = irim.pnormals[i];
900 GLfloat x = irim.points[i].x;
901 GLfloat y = irim.points[i].y;
902 GLfloat z = (z1 + z2) / 2;
903 glVertex3f (x, y, z);
904 glVertex3f (x - n.x, y - n.y, z);
908 if (!wire_p) /* close the quad loop */
910 glNormal3f (-irim.pnormals[0].x,-irim.pnormals[0].y,-irim.pnormals[0].z);
911 glVertex3f (s1*irim.points[0].x, s1*irim.points[0].y, z1);
912 glVertex3f (s2*irim.points[0].x, s2*irim.points[0].y, z2);
914 polys += irim.npoints;
917 /* Draw the inner rim circles, in wire mode
921 glBegin (GL_LINE_LOOP);
922 for (i = 0; i < irim.npoints; i++)
923 glVertex3f (irim.points[i].x, irim.points[i].y, z1);
925 glBegin (GL_LINE_LOOP);
926 for (i = 0; i < irim.npoints; i++)
927 glVertex3f (irim.points[i].x, irim.points[i].y, z2);
931 /* Draw the side (the flat bit)
933 if (!wire_p || wire_all_p)
936 if (irim.npoints != orim.npoints) abort();
937 for (z = z1; z <= z2; z += z2-z1)
939 GLfloat s = (z == z1 ? s1 : s2);
940 glFrontFace (((z == z1) ^ g->inverted_p) ? GL_CCW : GL_CW);
941 glNormal3f (0, 0, z);
942 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
943 for (i = 0; i < orim.npoints; i++)
945 glVertex3f (s*orim.points[i].x, s*orim.points[i].y, z);
946 glVertex3f (s*irim.points[i].x, s*irim.points[i].y, z);
948 if (!wire_p) /* close the quad loop */
950 glVertex3f (s*orim.points[0].x, s*orim.points[0].y, z);
951 glVertex3f (s*irim.points[0].x, s*irim.points[0].y, z);
953 polys += orim.npoints;
959 free (irim.fnormals);
960 free (irim.pnormals);
963 free (orim.fnormals);
964 free (orim.pnormals);
970 /* Render one gear, unrotated at 0,0.
971 Returns the number of polygons.
974 draw_involute_gear (gear *g, Bool wire_p)
978 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
979 GLfloat shiny = 128.0;
981 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
982 glMateriali (GL_FRONT, GL_SHININESS, shiny);
983 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
984 glColor3f (g->color[0], g->color[1], g->color[2]);
987 polys += draw_involute_schematic (g, wire_p);
991 glRotatef (g->wobble, 1, 0, 0);
992 polys += draw_gear_teeth (g, wire_p);
993 polys += draw_gear_interior (g, wire_p);
994 polys += draw_gear_nubs (g, wire_p);