1 /* involute, Copyright (c) 2004-2007 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"
21 # include <OpenGL/gl.h>
22 # include <OpenGL/glu.h>
23 #else /* !HAVE_COCOA -- real Xlib */
26 #endif /* !HAVE_COCOA */
32 #define countof(x) (sizeof((x))/sizeof((*x)))
35 /* For debugging: if true then in wireframe, do not abbreviate. */
36 static Bool wire_all_p = False+1;
37 static Bool show_normals_p = False+1;
40 /* Draws an uncapped tube of the given radius extending from top to bottom,
41 with faces pointing either in or out.
44 draw_ring (int segments,
45 GLfloat r, GLfloat top, GLfloat bottom,
46 Bool in_p, Bool wire_p)
50 GLfloat width = M_PI * 2 / segments;
54 glFrontFace (in_p ? GL_CCW : GL_CW);
55 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
56 for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
58 GLfloat th = i * width;
59 GLfloat cth = cos(th);
60 GLfloat sth = sin(th);
62 glNormal3f (-cth, -sth, 0);
64 glNormal3f (cth, sth, 0);
65 glVertex3f (cth * r, sth * r, top);
66 glVertex3f (cth * r, sth * r, bottom);
74 glBegin (GL_LINE_LOOP);
75 for (i = 0; i < segments; i++)
77 GLfloat th = i * width;
78 glVertex3f (cos(th) * r, sin(th) * r, top);
81 glBegin (GL_LINE_LOOP);
82 for (i = 0; i < segments; i++)
84 GLfloat th = i * width;
85 glVertex3f (cos(th) * r, sin(th) * r, bottom);
94 /* Draws a donut-shaped disc between the given radii,
95 with faces pointing either up or down.
96 The first radius may be 0, in which case, a filled disc is drawn.
99 draw_disc (int segments,
100 GLfloat ra, GLfloat rb, GLfloat z,
101 Bool up_p, Bool wire_p)
105 GLfloat width = M_PI * 2 / segments;
108 if (rb <= 0) abort();
111 glFrontFace (up_p ? GL_CW : GL_CCW);
113 glFrontFace (up_p ? GL_CCW : GL_CW);
116 glBegin (wire_p ? GL_LINES : GL_TRIANGLE_FAN);
118 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
120 glNormal3f (0, 0, (up_p ? -1 : 1));
122 if (ra == 0 && !wire_p)
123 glVertex3f (0, 0, z);
125 for (i = 0; i < segments + (wire_p ? 0 : 1); i++)
127 GLfloat th = i * width;
128 GLfloat cth = cos(th);
129 GLfloat sth = sin(th);
130 if (wire_p || ra != 0)
131 glVertex3f (cth * ra, sth * ra, z);
132 glVertex3f (cth * rb, sth * rb, z);
140 /* Draws N thick radial lines between the given radii,
141 with faces pointing either up or down.
144 draw_spokes (int n, GLfloat thickness, int segments,
145 GLfloat ra, GLfloat rb, GLfloat z1, GLfloat z2,
156 if (ra <= 0 || rb <= 0) abort();
160 while (segments2 < segments) /* need a multiple of N >= segments */
161 segments2 += n; /* (yes, this is a moronic way to find that) */
163 insegs = ((float) (segments2 / n) + 0.5) / thickness;
164 outsegs = (segments2 / n) - insegs;
165 if (insegs <= 0) insegs = 1;
166 if (outsegs <= 0) outsegs = 1;
168 segments2 = (insegs + outsegs) * n;
169 width = M_PI * 2 / segments2;
173 for (i = 0; i < segments2; i++, tick++)
175 GLfloat th1 = i * width;
176 GLfloat th2 = th1 + width;
177 GLfloat cth1 = cos(th1);
178 GLfloat sth1 = sin(th1);
179 GLfloat cth2 = cos(th2);
180 GLfloat sth2 = sin(th2);
183 int changed = (i == 0);
185 if (state == 0 && tick == insegs)
186 tick = 0, state = 1, changed = 1;
187 else if (state == 1 && tick == outsegs)
188 tick = 0, state = 0, changed = 1;
190 if ((state == 1 || /* in */
191 (state == 0 && changed)) &&
192 (!wire_p || wire_all_p))
195 glFrontFace (GL_CCW);
196 glBegin (wire_p ? GL_LINES : GL_QUADS);
197 glNormal3f (0, 0, -1);
198 glVertex3f (cth1 * ra, sth1 * ra, z1);
199 glVertex3f (cth1 * rb, sth1 * rb, z1);
200 glVertex3f (cth2 * rb, sth2 * rb, z1);
201 glVertex3f (cth2 * ra, sth2 * ra, z1);
207 glBegin (wire_p ? GL_LINES : GL_QUADS);
208 glNormal3f (0, 0, 1);
209 glVertex3f (cth1 * ra, sth1 * ra, z2);
210 glVertex3f (cth1 * rb, sth1 * rb, z2);
211 glVertex3f (cth2 * rb, sth2 * rb, z2);
212 glVertex3f (cth2 * ra, sth2 * ra, z2);
217 if (state == 1 && changed) /* entering "in" state */
221 glBegin (wire_p ? GL_LINES : GL_QUADS);
222 do_normal (cth1 * rb, sth1 * rb, z1,
223 cth1 * ra, sth1 * ra, z1,
224 cth1 * rb, sth1 * rb, z2);
225 glVertex3f (cth1 * ra, sth1 * ra, z1);
226 glVertex3f (cth1 * rb, sth1 * rb, z1);
227 glVertex3f (cth1 * rb, sth1 * rb, z2);
228 glVertex3f (cth1 * ra, sth1 * ra, z2);
233 if (state == 0 && changed) /* entering "out" state */
236 glFrontFace (GL_CCW);
237 glBegin (wire_p ? GL_LINES : GL_QUADS);
238 do_normal (cth2 * ra, sth2 * ra, z1,
239 cth2 * rb, sth2 * rb, z1,
240 cth2 * rb, sth2 * rb, z2);
241 glVertex3f (cth2 * ra, sth2 * ra, z1);
242 glVertex3f (cth2 * rb, sth2 * rb, z1);
243 glVertex3f (cth2 * rb, sth2 * rb, z2);
244 glVertex3f (cth2 * ra, sth2 * ra, z2);
256 /* Draws some bumps (embedded cylinders) on the gear.
259 draw_gear_nubs (gear *g, Bool wire_p)
263 int steps = (g->size != INVOLUTE_LARGE ? 5 : 20);
264 double r, size, height;
269 if (! g->nubs) return 0;
271 which = involute_biggest_ring (g, &r, &size, &height);
275 cc = (which == 1 ? g->color : g->color2);
276 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cc);
279 r = g->r + size + g->tooth_h;
281 width = M_PI * 2 / g->nubs;
282 off = M_PI / (g->nteeth * 2); /* align first nub with a tooth */
284 for (i = 0; i < g->nubs; i++)
286 GLfloat th = (i * width) + off;
289 glRotatef (th * 180 / M_PI, 0, 0, 1);
290 glTranslatef (r, 0, 0);
292 if (g->inverted_p) /* nubs go on the outside rim */
294 size = g->thickness / 3;
295 height = (g->r - g->inner_r)/2;
296 glTranslatef (height, 0, 0);
297 glRotatef (90, 0, 1, 0);
300 if (wire_p && !wire_all_p)
301 polys += draw_ring ((g->size == INVOLUTE_LARGE ? steps/2 : steps),
302 size, 0, 0, False, wire_p);
305 polys += draw_disc (steps, 0, size, -height, True, wire_p);
306 polys += draw_disc (steps, 0, size, height, False, wire_p);
307 polys += draw_ring (steps, size, -height, height, False, wire_p);
316 /* Draws a much simpler representation of a gear.
317 Returns the number of polygons.
320 draw_involute_schematic (gear *g, Bool wire_p)
324 GLfloat width = M_PI * 2 / g->nteeth;
326 if (!wire_p) glDisable(GL_LIGHTING);
327 glColor3f (g->color[0] * 0.6, g->color[1] * 0.6, g->color[2] * 0.6);
330 for (i = 0; i < g->nteeth; i++)
332 GLfloat th = (i * width) + (width/4);
333 glVertex3f (0, 0, -g->thickness/2);
334 glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
339 glBegin (GL_LINE_LOOP);
340 for (i = 0; i < g->nteeth; i++)
342 GLfloat th = (i * width) + (width/4);
343 glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
348 if (!wire_p) glEnable(GL_LIGHTING);
353 /* Renders all the interior (non-toothy) parts of a gear:
354 the discs, axles, etc.
357 draw_gear_interior (gear *g, Bool wire_p)
361 int steps = g->nteeth * 2;
362 if (steps < 10) steps = 10;
363 if ((wire_p && !wire_all_p) || g->size != INVOLUTE_LARGE) steps /= 2;
364 if (g->size != INVOLUTE_LARGE && steps > 16) steps = 16;
366 /* ring 1 (facing in) is done in draw_gear_teeth */
368 /* ring 2 (facing in) and disc 2
372 GLfloat ra = g->inner_r * 1.04; /* slightly larger than inner_r */
373 GLfloat rb = g->inner_r2; /* since the points don't line up */
374 GLfloat za = -g->thickness2/2;
375 GLfloat zb = g->thickness2/2;
377 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color2);
379 if ((g->coax_p != 1 && !g->inner_r3) ||
380 (wire_p && wire_all_p))
382 draw_ring (steps, rb, za, zb, True, wire_p); /* ring facing in*/
384 if (wire_p && wire_all_p)
386 draw_ring (steps, ra, za, zb, True, wire_p); /* ring facing in*/
389 polys += draw_spokes (g->spokes, g->spoke_thickness,
390 steps, ra, rb, za, zb, wire_p);
391 else if (!wire_p || wire_all_p)
394 draw_disc (steps, ra, rb, za, True, wire_p); /* top plate */
396 draw_disc (steps, ra, rb, zb, False, wire_p); /* bottom plate*/
400 /* ring 3 (facing in and out) and disc 3
404 GLfloat ra = g->inner_r2;
405 GLfloat rb = g->inner_r3;
406 GLfloat za = -g->thickness3/2;
407 GLfloat zb = g->thickness3/2;
409 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
412 draw_ring (steps, ra, za, zb, False, wire_p); /* ring facing out */
414 if (g->coax_p != 1 || (wire_p && wire_all_p))
416 draw_ring (steps, rb, za, zb, True, wire_p); /* ring facing in */
418 if (!wire_p || wire_all_p)
421 draw_disc (steps, ra, rb, za, True, wire_p); /* top plate */
423 draw_disc (steps, ra, rb, zb, False, wire_p); /* bottom plate */
431 GLfloat cap_height = g->coax_thickness/3;
433 GLfloat ra = (g->inner_r3 ? g->inner_r3 :
434 g->inner_r2 ? g->inner_r2 :
436 GLfloat za = -(g->thickness/2 + cap_height);
437 GLfloat zb = g->coax_thickness/2 + g->coax_displacement + cap_height;
439 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
441 if (wire_p && !wire_all_p) steps /= 2;
444 draw_ring (steps, ra, za, zb, False, wire_p); /* ring facing out */
446 if (!wire_p || wire_all_p)
449 draw_disc (steps, 0, ra, za, True, wire_p); /* top plate */
451 += draw_disc (steps, 0, ra, zb, False, wire_p); /* bottom plate */
458 /* gear_teeth_geometry computes the vertices and normals of the teeth
459 of a gear. This is the heavy lifting: there are a ton of polygons
460 around the perimiter of a gear, and the normals are difficult (not
461 radial or right angles.)
463 It would be nice if we could cache this, but the numbers are
464 different for essentially every gear:
466 - Every gear has a different inner_r, so the vertices of the
467 inner ring (and thus, the triangle fans on the top and bottom
468 faces) are different in a non-scalable way.
470 - If the ratio between tooth_w and tooth_h changes, the normals
471 on the outside edges of the teeth are invalid (this can happen
472 every time we start a new train.)
474 So instead, we rely on OpenGL display lists to do the cacheing for
475 us -- we only compute all these normals once per gear, instead of
476 once per gear per frame.
482 XYZ *fnormals; /* face normals */
483 XYZ *pnormals; /* point normals */
488 tooth_normals (tooth_face *f, GLfloat tooth_slope)
492 /* Compute the face normals for each facet. */
493 for (i = 0; i < f->npoints; i++)
497 int b = (i == f->npoints-1 ? 0 : i+1);
501 p3.x -= (p3.x * tooth_slope);
502 p3.y -= (p3.y * tooth_slope);
504 f->fnormals[i] = calc_normal (p1, p2, p3);
507 /* From the face normals, compute the vertex normals
508 (by averaging the normals of adjascent faces.)
510 for (i = 0; i < f->npoints; i++)
512 int a = (i == 0 ? f->npoints-1 : i-1);
514 XYZ n1 = f->fnormals[a]; /* normal of [i-1 - i] face */
515 XYZ n2 = f->fnormals[b]; /* normal of [i - i+1] face */
516 f->pnormals[i].x = (n1.x + n2.x) / 2;
517 f->pnormals[i].y = (n1.y + n2.y) / 2;
518 f->pnormals[i].z = (n1.z + n2.z) / 2;
524 gear_teeth_geometry (gear *g,
525 tooth_face *orim, /* outer rim (the teeth) */
526 tooth_face *irim) /* inner rim (the hole) */
529 int ppt = 9; /* max points per tooth */
530 GLfloat width = M_PI * 2 / g->nteeth;
531 GLfloat rh = g->tooth_h;
534 /* Approximate shape of an "involute" gear tooth.
537 th0 th1 th2 th3 th4 th5 th6 th7 th8 th9 th10
538 : : : : : : : : : : :
539 : : : : : : : : : : :
540 r0 ........:..:..:...___________...:..:..:......:......:..
541 : : : /: : :\ : : : : :
542 : : : / : : : \ : : : : :
543 : : :/ : : : \: : : : :
544 r1 ........:.....@...:....:....:...@..:..:......:......:..
545 : : @: : : : :@ : : : :
546 (R) ...........:...@.:...:....:....:...:.@..........:......:......
547 : :@ : : : : : @: : : :
548 r2 ........:..@..:...:....:....:...:..@:........:......:..
549 : /: : : : : : :\ : : :
550 :/ : : : : : : : \: : : /
551 r3 ......__/..:..:...:....:....:...:..:..\______________/
552 : : : : : : : : : : :
553 | : : : : : : : | : :
554 : : : : : : : : : : :
555 | : : : : : : : | : :
556 r4 ......__:_____________________________:________________
563 r[0] = R + (rh * 0.5);
564 r[1] = R + (rh * 0.25);
569 th[0] = -tw * (g->size == INVOLUTE_SMALL ? 0.5 :
570 g->size == INVOLUTE_MEDIUM ? 0.41 : 0.45);
572 th[2] = -tw * (g->nteeth >= 5 ? 0.16 : 0.12);
573 th[3] = -tw * (g->size == INVOLUTE_MEDIUM ? 0.1 : 0.04);
580 th[10]= th[0] + width;
582 if (g->inverted_p) /* put the teeth on the inside */
584 for (i = 0; i < countof(th); i++)
586 for (i = 0; i < countof(r); i++)
587 r[i] = R - (r[i] - R);
591 if (g->inverted_p) /* put the teeth on the inside */
593 GLfloat swap[countof(th)];
595 for (i = 0; i < maxr; i++)
596 swap[maxr-i-1] = R - (r[i] - R);
597 for (i = 0; i < maxr; i++)
600 for (i = 0; i < maxth; i++)
601 swap[maxth-i-1] = -th[i];
602 for (i = 0; i < maxth; i++)
608 orim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->points));
609 orim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->fnormals));
610 orim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->pnormals));
613 irim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->points));
614 irim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->fnormals));
615 irim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->pnormals));
617 if (!orim->points || !orim->pnormals || !orim->fnormals ||
618 !irim->points || !irim->pnormals || !irim->fnormals)
620 fprintf (stderr, "%s: out of memory\n", progname);
624 /* First, compute the coordinates of every point used for every tooth.
626 for (i = 0; i < g->nteeth; i++)
628 GLfloat TH = (i * width) + (width/4);
629 int oon = orim->npoints;
630 int oin = irim->npoints;
633 # define PUSH(OPR,IPR,PTH) \
634 orim->points[orim->npoints].x = cos(TH+th[(PTH)]) * r[(OPR)]; \
635 orim->points[orim->npoints].y = sin(TH+th[(PTH)]) * r[(OPR)]; \
637 irim->points[irim->npoints].x = cos(TH+th[(PTH)]) * r[(IPR)]; \
638 irim->points[irim->npoints].y = sin(TH+th[(PTH)]) * r[(IPR)]; \
641 if (g->size == INVOLUTE_SMALL)
643 PUSH(3, 4, 0); /* tooth left 1 */
644 PUSH(0, 4, 4); /* tooth top middle */
646 else if (g->size == INVOLUTE_MEDIUM)
648 PUSH(3, 4, 0); /* tooth left 1 */
649 PUSH(0, 4, 3); /* tooth top left */
650 PUSH(0, 4, 5); /* tooth top right */
651 PUSH(3, 4, 8); /* tooth right 3 */
653 else if (g->size == INVOLUTE_LARGE)
655 PUSH(3, 4, 0); /* tooth left 1 */
656 PUSH(2, 4, 1); /* tooth left 2 */
657 PUSH(1, 4, 2); /* tooth left 3 */
658 PUSH(0, 4, 3); /* tooth top left */
659 PUSH(0, 4, 5); /* tooth top right */
660 PUSH(1, 4, 6); /* tooth right 1 */
661 PUSH(2, 4, 7); /* tooth right 2 */
662 PUSH(3, 4, 8); /* tooth right 3 */
663 PUSH(3, 4, 9); /* gap top */
669 if (i == 0 && orim->npoints > ppt) abort(); /* go update "ppt"! */
676 for (j = 0; j < (end-start)/2; j++)
678 XYZ swap = orim->points[end-j-1];
679 orim->points[end-j-1] = orim->points[start+j];
680 orim->points[start+j] = swap;
685 for (j = 0; j < (end-start)/2; j++)
687 XYZ swap = irim->points[end-j-1];
688 irim->points[end-j-1] = irim->points[start+j];
689 irim->points[start+j] = swap;
694 tooth_normals (orim, g->tooth_slope);
695 tooth_normals (irim, 0);
697 if (g->inverted_p) /* flip the normals */
699 for (i = 0; i < orim->npoints; i++)
701 orim->fnormals[i].x = -orim->fnormals[i].x;
702 orim->fnormals[i].y = -orim->fnormals[i].y;
703 orim->fnormals[i].z = -orim->fnormals[i].z;
705 orim->pnormals[i].x = -orim->pnormals[i].x;
706 orim->pnormals[i].y = -orim->pnormals[i].y;
707 orim->pnormals[i].z = -orim->pnormals[i].z;
710 for (i = 0; i < irim->npoints; i++)
712 irim->fnormals[i].x = -irim->fnormals[i].x;
713 irim->fnormals[i].y = -irim->fnormals[i].y;
714 irim->fnormals[i].z = -irim->fnormals[i].z;
716 irim->pnormals[i].x = -irim->pnormals[i].x;
717 irim->pnormals[i].y = -irim->pnormals[i].y;
718 irim->pnormals[i].z = -irim->pnormals[i].z;
724 /* Which of the gear's inside rings is the biggest?
727 involute_biggest_ring (gear *g, double *posP, double *sizeP, double *heightP)
729 double r0 = (g->r - g->tooth_h/2);
730 double r1 = g->inner_r;
731 double r2 = g->inner_r2;
732 double r3 = g->inner_r3;
733 double w1 = (r1 ? r0 - r1 : r0);
734 double w2 = (r2 ? r1 - r2 : 0);
735 double w3 = (r3 ? r2 - r3 : 0);
736 double h1 = g->thickness;
737 double h2 = g->thickness2;
738 double h3 = g->thickness3;
740 if (g->spokes) w2 = 0;
742 if (w1 > w2 && w1 > w3)
744 if (posP) *posP = (r0+r1)/2;
745 if (sizeP) *sizeP = w1;
746 if (heightP) *heightP = h1;
749 else if (w2 > w1 && w2 > w3)
751 if (posP) *posP = (r1+r2)/2;
752 if (sizeP) *sizeP = w2;
753 if (heightP) *heightP = h2;
758 if (posP) *posP = (r2+r3)/2;
759 if (sizeP) *sizeP = w3;
760 if (heightP) *heightP = h3;
766 /* Renders all teeth of a gear.
769 draw_gear_teeth (gear *g, Bool wire_p)
774 GLfloat z1 = -g->thickness/2;
775 GLfloat z2 = g->thickness/2;
776 GLfloat s1 = 1 + (g->thickness * g->tooth_slope / 2);
777 GLfloat s2 = 1 - (g->thickness * g->tooth_slope / 2);
779 tooth_face orim, irim;
780 gear_teeth_geometry (g, &orim, &irim);
782 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
784 /* Draw the outer rim (the teeth)
785 (In wire mode, this draws just the upright lines.)
787 glFrontFace (g->inverted_p ? GL_CCW : GL_CW);
788 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
789 for (i = 0; i < orim.npoints; i++)
791 glNormal3f (orim.pnormals[i].x, orim.pnormals[i].y, orim.pnormals[i].z);
792 glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
793 glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
795 /* Show the face normal vectors */
796 if (wire_p && show_normals_p)
798 XYZ n = orim.fnormals[i];
800 int b = (i == orim.npoints-1 ? 0 : i+1);
801 GLfloat x = (orim.points[a].x + orim.points[b].x) / 2;
802 GLfloat y = (orim.points[a].y + orim.points[b].y) / 2;
803 GLfloat z = (z1 + z2) / 2;
804 glVertex3f (x, y, z);
805 glVertex3f (x + n.x, y + n.y, z + n.z);
808 /* Show the vertex normal vectors */
809 if (wire_p && show_normals_p)
811 XYZ n = orim.pnormals[i];
812 GLfloat x = orim.points[i].x;
813 GLfloat y = orim.points[i].y;
814 GLfloat z = (z1 + z2) / 2;
815 glVertex3f (x, y, z);
816 glVertex3f (x + n.x, y + n.y, z + n.z);
820 if (!wire_p) /* close the quad loop */
822 glNormal3f (orim.pnormals[0].x, orim.pnormals[0].y, orim.pnormals[0].z);
823 glVertex3f (s1*orim.points[0].x, s1*orim.points[0].y, z1);
824 glVertex3f (s2*orim.points[0].x, s2*orim.points[0].y, z2);
826 polys += orim.npoints;
829 /* Draw the outer rim circles, in wire mode */
832 glBegin (GL_LINE_LOOP);
833 for (i = 0; i < orim.npoints; i++)
834 glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
836 glBegin (GL_LINE_LOOP);
837 for (i = 0; i < orim.npoints; i++)
838 glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
842 /* Draw the inner rim (the hole)
843 (In wire mode, this draws just the upright lines.)
845 glFrontFace (g->inverted_p ? GL_CW : GL_CCW);
846 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
847 for (i = 0; i < irim.npoints; i++)
849 glNormal3f(-irim.pnormals[i].x, -irim.pnormals[i].y,-irim.pnormals[i].z);
850 glVertex3f (irim.points[i].x, irim.points[i].y, z1);
851 glVertex3f (irim.points[i].x, irim.points[i].y, z2);
853 /* Show the face normal vectors */
854 if (wire_p && show_normals_p)
856 XYZ n = irim.fnormals[i];
858 int b = (i == irim.npoints-1 ? 0 : i+1);
859 GLfloat x = (irim.points[a].x + irim.points[b].x) / 2;
860 GLfloat y = (irim.points[a].y + irim.points[b].y) / 2;
861 GLfloat z = (z1 + z2) / 2;
862 glVertex3f (x, y, z);
863 glVertex3f (x - n.x, y - n.y, z);
866 /* Show the vertex normal vectors */
867 if (wire_p && show_normals_p)
869 XYZ n = irim.pnormals[i];
870 GLfloat x = irim.points[i].x;
871 GLfloat y = irim.points[i].y;
872 GLfloat z = (z1 + z2) / 2;
873 glVertex3f (x, y, z);
874 glVertex3f (x - n.x, y - n.y, z);
878 if (!wire_p) /* close the quad loop */
880 glNormal3f (-irim.pnormals[0].x,-irim.pnormals[0].y,-irim.pnormals[0].z);
881 glVertex3f (irim.points[0].x, irim.points[0].y, z1);
882 glVertex3f (irim.points[0].x, irim.points[0].y, z2);
884 polys += irim.npoints;
887 /* Draw the inner rim circles, in wire mode
891 glBegin (GL_LINE_LOOP);
892 for (i = 0; i < irim.npoints; i++)
893 glVertex3f (irim.points[i].x, irim.points[i].y, z1);
895 glBegin (GL_LINE_LOOP);
896 for (i = 0; i < irim.npoints; i++)
897 glVertex3f (irim.points[i].x, irim.points[i].y, z2);
901 /* Draw the side (the flat bit)
903 if (!wire_p || wire_all_p)
906 if (irim.npoints != orim.npoints) abort();
907 for (z = z1; z <= z2; z += z2-z1)
909 GLfloat s = (z == z1 ? s1 : s2);
910 glFrontFace (((z == z1) ^ g->inverted_p) ? GL_CCW : GL_CW);
911 glNormal3f (0, 0, z);
912 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
913 for (i = 0; i < orim.npoints; i++)
915 glVertex3f (s*orim.points[i].x, s*orim.points[i].y, z);
916 glVertex3f ( irim.points[i].x, irim.points[i].y, z);
918 if (!wire_p) /* close the quad loop */
920 glVertex3f (s*orim.points[0].x, s*orim.points[0].y, z);
921 glVertex3f ( irim.points[0].x, irim.points[0].y, z);
923 polys += orim.npoints;
929 free (irim.fnormals);
930 free (irim.pnormals);
933 free (orim.fnormals);
934 free (orim.pnormals);
940 /* Render one gear, unrotated at 0,0.
941 Returns the number of polygons.
944 draw_involute_gear (gear *g, Bool wire_p)
948 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
949 GLfloat shiny = 128.0;
951 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
952 glMateriali (GL_FRONT, GL_SHININESS, shiny);
953 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
954 glColor3f (g->color[0], g->color[1], g->color[2]);
957 polys += draw_involute_schematic (g, wire_p);
961 glRotatef (g->wobble, 1, 0, 0);
962 polys += draw_gear_teeth (g, wire_p);
963 polys += draw_gear_interior (g, wire_p);
964 polys += draw_gear_nubs (g, wire_p);