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);
255 /* Draws some bumps (embedded cylinders) on the gear.
258 draw_gear_nubs (gear *g, Bool wire_p)
262 int steps = (g->size != INVOLUTE_LARGE ? 5 : 20);
263 double r, size, height;
268 if (! g->nubs) return 0;
270 which = involute_biggest_ring (g, &r, &size, &height);
274 cc = (which == 1 ? g->color : g->color2);
275 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cc);
278 r = g->r + size + g->tooth_h;
280 width = M_PI * 2 / g->nubs;
281 off = M_PI / (g->nteeth * 2); /* align first nub with a tooth */
283 for (i = 0; i < g->nubs; i++)
285 GLfloat th = (i * width) + off;
288 glRotatef (th * 180 / M_PI, 0, 0, 1);
289 glTranslatef (r, 0, 0);
291 if (g->inverted_p) /* nubs go on the outside rim */
293 size = g->thickness / 3;
294 height = (g->r - g->inner_r)/2;
295 glTranslatef (height, 0, 0);
296 glRotatef (90, 0, 1, 0);
299 if (wire_p && !wire_all_p)
300 polys += draw_ring ((g->size == INVOLUTE_LARGE ? steps/2 : steps),
301 size, 0, 0, False, wire_p);
304 polys += draw_disc (steps, 0, size, -height, True, wire_p);
305 polys += draw_disc (steps, 0, size, height, False, wire_p);
306 polys += draw_ring (steps, size, -height, height, False, wire_p);
315 /* Draws a much simpler representation of a gear.
316 Returns the number of polygons.
319 draw_involute_schematic (gear *g, Bool wire_p)
323 GLfloat width = M_PI * 2 / g->nteeth;
325 if (!wire_p) glDisable(GL_LIGHTING);
326 glColor3f (g->color[0] * 0.6, g->color[1] * 0.6, g->color[2] * 0.6);
329 for (i = 0; i < g->nteeth; i++)
331 GLfloat th = (i * width) + (width/4);
332 glVertex3f (0, 0, -g->thickness/2);
333 glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
338 glBegin (GL_LINE_LOOP);
339 for (i = 0; i < g->nteeth; i++)
341 GLfloat th = (i * width) + (width/4);
342 glVertex3f (cos(th) * g->r, sin(th) * g->r, -g->thickness/2);
347 if (!wire_p) glEnable(GL_LIGHTING);
352 /* Renders all the interior (non-toothy) parts of a gear:
353 the discs, axles, etc.
356 draw_gear_interior (gear *g, Bool wire_p)
360 int steps = g->nteeth * 2;
361 if (steps < 10) steps = 10;
362 if ((wire_p && !wire_all_p) || g->size != INVOLUTE_LARGE) steps /= 2;
363 if (g->size != INVOLUTE_LARGE && steps > 16) steps = 16;
365 /* ring 1 (facing in) is done in draw_gear_teeth */
367 /* ring 2 (facing in) and disc 2
371 GLfloat ra = g->inner_r * 1.04; /* slightly larger than inner_r */
372 GLfloat rb = g->inner_r2; /* since the points don't line up */
373 GLfloat za = -g->thickness2/2;
374 GLfloat zb = g->thickness2/2;
376 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color2);
378 if ((g->coax_p != 1 && !g->inner_r3) ||
379 (wire_p && wire_all_p))
381 draw_ring (steps, rb, za, zb, True, wire_p); /* ring facing in*/
383 if (wire_p && wire_all_p)
385 draw_ring (steps, ra, za, zb, True, wire_p); /* ring facing in*/
388 polys += draw_spokes (g->spokes, g->spoke_thickness,
389 steps, ra, rb, za, zb, wire_p);
390 else if (!wire_p || wire_all_p)
393 draw_disc (steps, ra, rb, za, True, wire_p); /* top plate */
395 draw_disc (steps, ra, rb, zb, False, wire_p); /* bottom plate*/
399 /* ring 3 (facing in and out) and disc 3
403 GLfloat ra = g->inner_r2;
404 GLfloat rb = g->inner_r3;
405 GLfloat za = -g->thickness3/2;
406 GLfloat zb = g->thickness3/2;
408 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
411 draw_ring (steps, ra, za, zb, False, wire_p); /* ring facing out */
413 if (g->coax_p != 1 || (wire_p && wire_all_p))
415 draw_ring (steps, rb, za, zb, True, wire_p); /* ring facing in */
417 if (!wire_p || wire_all_p)
420 draw_disc (steps, ra, rb, za, True, wire_p); /* top plate */
422 draw_disc (steps, ra, rb, zb, False, wire_p); /* bottom plate */
430 GLfloat cap_height = g->coax_thickness/3;
432 GLfloat ra = (g->inner_r3 ? g->inner_r3 :
433 g->inner_r2 ? g->inner_r2 :
435 GLfloat za = -(g->thickness/2 + cap_height);
436 GLfloat zb = g->coax_thickness/2 + g->coax_displacement + cap_height;
438 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
440 if (wire_p && !wire_all_p) steps /= 2;
443 draw_ring (steps, ra, za, zb, False, wire_p); /* ring facing out */
445 if (!wire_p || wire_all_p)
448 draw_disc (steps, 0, ra, za, True, wire_p); /* top plate */
450 += draw_disc (steps, 0, ra, zb, False, wire_p); /* bottom plate */
457 /* gear_teeth_geometry computes the vertices and normals of the teeth
458 of a gear. This is the heavy lifting: there are a ton of polygons
459 around the perimiter of a gear, and the normals are difficult (not
460 radial or right angles.)
462 It would be nice if we could cache this, but the numbers are
463 different for essentially every gear:
465 - Every gear has a different inner_r, so the vertices of the
466 inner ring (and thus, the triangle fans on the top and bottom
467 faces) are different in a non-scalable way.
469 - If the ratio between tooth_w and tooth_h changes, the normals
470 on the outside edges of the teeth are invalid (this can happen
471 every time we start a new train.)
473 So instead, we rely on OpenGL display lists to do the cacheing for
474 us -- we only compute all these normals once per gear, instead of
475 once per gear per frame.
481 XYZ *fnormals; /* face normals */
482 XYZ *pnormals; /* point normals */
487 tooth_normals (tooth_face *f, GLfloat tooth_slope)
491 /* Compute the face normals for each facet. */
492 for (i = 0; i < f->npoints; i++)
496 int b = (i == f->npoints-1 ? 0 : i+1);
500 p3.x -= (p3.x * tooth_slope);
501 p3.y -= (p3.y * tooth_slope);
503 f->fnormals[i] = calc_normal (p1, p2, p3);
506 /* From the face normals, compute the vertex normals
507 (by averaging the normals of adjascent faces.)
509 for (i = 0; i < f->npoints; i++)
511 int a = (i == 0 ? f->npoints-1 : i-1);
513 XYZ n1 = f->fnormals[a]; /* normal of [i-1 - i] face */
514 XYZ n2 = f->fnormals[b]; /* normal of [i - i+1] face */
515 f->pnormals[i].x = (n1.x + n2.x) / 2;
516 f->pnormals[i].y = (n1.y + n2.y) / 2;
517 f->pnormals[i].z = (n1.z + n2.z) / 2;
523 gear_teeth_geometry (gear *g,
524 tooth_face *orim, /* outer rim (the teeth) */
525 tooth_face *irim) /* inner rim (the hole) */
528 int ppt = 9; /* max points per tooth */
529 GLfloat width = M_PI * 2 / g->nteeth;
530 GLfloat rh = g->tooth_h;
533 /* Approximate shape of an "involute" gear tooth.
536 th0 th1 th2 th3 th4 th5 th6 th7 th8 th9 th10
537 : : : : : : : : : : :
538 : : : : : : : : : : :
539 r0 ........:..:..:...___________...:..:..:......:......:..
540 : : : /: : :\ : : : : :
541 : : : / : : : \ : : : : :
542 : : :/ : : : \: : : : :
543 r1 ........:.....@...:....:....:...@..:..:......:......:..
544 : : @: : : : :@ : : : :
545 (R) ...........:...@.:...:....:....:...:.@..........:......:......
546 : :@ : : : : : @: : : :
547 r2 ........:..@..:...:....:....:...:..@:........:......:..
548 : /: : : : : : :\ : : :
549 :/ : : : : : : : \: : : /
550 r3 ......__/..:..:...:....:....:...:..:..\______________/
551 : : : : : : : : : : :
552 | : : : : : : : | : :
553 : : : : : : : : : : :
554 | : : : : : : : | : :
555 r4 ......__:_____________________________:________________
562 r[0] = R + (rh * 0.5);
563 r[1] = R + (rh * 0.25);
568 th[0] = -tw * (g->size == INVOLUTE_SMALL ? 0.5 :
569 g->size == INVOLUTE_MEDIUM ? 0.41 : 0.45);
571 th[2] = -tw * (g->nteeth >= 5 ? 0.16 : 0.12);
572 th[3] = -tw * (g->size == INVOLUTE_MEDIUM ? 0.1 : 0.04);
579 th[10]= th[0] + width;
581 if (g->inverted_p) /* put the teeth on the inside */
583 for (i = 0; i < countof(th); i++)
585 for (i = 0; i < countof(r); i++)
586 r[i] = R - (r[i] - R);
590 if (g->inverted_p) /* put the teeth on the inside */
592 GLfloat swap[countof(th)];
594 for (i = 0; i < maxr; i++)
595 swap[maxr-i-1] = R - (r[i] - R);
596 for (i = 0; i < maxr; i++)
599 for (i = 0; i < maxth; i++)
600 swap[maxth-i-1] = -th[i];
601 for (i = 0; i < maxth; i++)
607 orim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->points));
608 orim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->fnormals));
609 orim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*orim->pnormals));
612 irim->points = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->points));
613 irim->fnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->fnormals));
614 irim->pnormals = (XYZ *) calloc(ppt * g->nteeth+1, sizeof(*irim->pnormals));
616 if (!orim->points || !orim->pnormals || !orim->fnormals ||
617 !irim->points || !irim->pnormals || !irim->fnormals)
619 fprintf (stderr, "%s: out of memory\n", progname);
623 /* First, compute the coordinates of every point used for every tooth.
625 for (i = 0; i < g->nteeth; i++)
627 GLfloat TH = (i * width) + (width/4);
628 int oon = orim->npoints;
629 int oin = irim->npoints;
632 # define PUSH(OPR,IPR,PTH) \
633 orim->points[orim->npoints].x = cos(TH+th[(PTH)]) * r[(OPR)]; \
634 orim->points[orim->npoints].y = sin(TH+th[(PTH)]) * r[(OPR)]; \
636 irim->points[irim->npoints].x = cos(TH+th[(PTH)]) * r[(IPR)]; \
637 irim->points[irim->npoints].y = sin(TH+th[(PTH)]) * r[(IPR)]; \
640 if (g->size == INVOLUTE_SMALL)
642 PUSH(3, 4, 0); /* tooth left 1 */
643 PUSH(0, 4, 4); /* tooth top middle */
645 else if (g->size == INVOLUTE_MEDIUM)
647 PUSH(3, 4, 0); /* tooth left 1 */
648 PUSH(0, 4, 3); /* tooth top left */
649 PUSH(0, 4, 5); /* tooth top right */
650 PUSH(3, 4, 8); /* tooth right 3 */
652 else if (g->size == INVOLUTE_LARGE)
654 PUSH(3, 4, 0); /* tooth left 1 */
655 PUSH(2, 4, 1); /* tooth left 2 */
656 PUSH(1, 4, 2); /* tooth left 3 */
657 PUSH(0, 4, 3); /* tooth top left */
658 PUSH(0, 4, 5); /* tooth top right */
659 PUSH(1, 4, 6); /* tooth right 1 */
660 PUSH(2, 4, 7); /* tooth right 2 */
661 PUSH(3, 4, 8); /* tooth right 3 */
662 PUSH(3, 4, 9); /* gap top */
668 if (i == 0 && orim->npoints > ppt) abort(); /* go update "ppt"! */
675 for (j = 0; j < (end-start)/2; j++)
677 XYZ swap = orim->points[end-j-1];
678 orim->points[end-j-1] = orim->points[start+j];
679 orim->points[start+j] = swap;
684 for (j = 0; j < (end-start)/2; j++)
686 XYZ swap = irim->points[end-j-1];
687 irim->points[end-j-1] = irim->points[start+j];
688 irim->points[start+j] = swap;
693 tooth_normals (orim, g->tooth_slope);
694 tooth_normals (irim, 0);
696 if (g->inverted_p) /* flip the normals */
698 for (i = 0; i < orim->npoints; i++)
700 orim->fnormals[i].x = -orim->fnormals[i].x;
701 orim->fnormals[i].y = -orim->fnormals[i].y;
702 orim->fnormals[i].z = -orim->fnormals[i].z;
704 orim->pnormals[i].x = -orim->pnormals[i].x;
705 orim->pnormals[i].y = -orim->pnormals[i].y;
706 orim->pnormals[i].z = -orim->pnormals[i].z;
709 for (i = 0; i < irim->npoints; i++)
711 irim->fnormals[i].x = -irim->fnormals[i].x;
712 irim->fnormals[i].y = -irim->fnormals[i].y;
713 irim->fnormals[i].z = -irim->fnormals[i].z;
715 irim->pnormals[i].x = -irim->pnormals[i].x;
716 irim->pnormals[i].y = -irim->pnormals[i].y;
717 irim->pnormals[i].z = -irim->pnormals[i].z;
723 /* Which of the gear's inside rings is the biggest?
726 involute_biggest_ring (gear *g, double *posP, double *sizeP, double *heightP)
728 double r0 = (g->r - g->tooth_h/2);
729 double r1 = g->inner_r;
730 double r2 = g->inner_r2;
731 double r3 = g->inner_r3;
732 double w1 = (r1 ? r0 - r1 : r0);
733 double w2 = (r2 ? r1 - r2 : 0);
734 double w3 = (r3 ? r2 - r3 : 0);
735 double h1 = g->thickness;
736 double h2 = g->thickness2;
737 double h3 = g->thickness3;
739 if (g->spokes) w2 = 0;
741 if (w1 > w2 && w1 > w3)
743 if (posP) *posP = (r0+r1)/2;
744 if (sizeP) *sizeP = w1;
745 if (heightP) *heightP = h1;
748 else if (w2 > w1 && w2 > w3)
750 if (posP) *posP = (r1+r2)/2;
751 if (sizeP) *sizeP = w2;
752 if (heightP) *heightP = h2;
757 if (posP) *posP = (r2+r3)/2;
758 if (sizeP) *sizeP = w3;
759 if (heightP) *heightP = h3;
765 /* Renders all teeth of a gear.
768 draw_gear_teeth (gear *g, Bool wire_p)
773 GLfloat z1 = -g->thickness/2;
774 GLfloat z2 = g->thickness/2;
775 GLfloat s1 = 1 + (g->thickness * g->tooth_slope / 2);
776 GLfloat s2 = 1 - (g->thickness * g->tooth_slope / 2);
778 tooth_face orim, irim;
779 gear_teeth_geometry (g, &orim, &irim);
781 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
783 /* Draw the outer rim (the teeth)
784 (In wire mode, this draws just the upright lines.)
786 glFrontFace (g->inverted_p ? GL_CCW : GL_CW);
787 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
788 for (i = 0; i < orim.npoints; i++)
790 glNormal3f (orim.pnormals[i].x, orim.pnormals[i].y, orim.pnormals[i].z);
791 glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
792 glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
794 /* Show the face normal vectors */
795 if (wire_p && show_normals_p)
797 XYZ n = orim.fnormals[i];
799 int b = (i == orim.npoints-1 ? 0 : i+1);
800 GLfloat x = (orim.points[a].x + orim.points[b].x) / 2;
801 GLfloat y = (orim.points[a].y + orim.points[b].y) / 2;
802 GLfloat z = (z1 + z2) / 2;
803 glVertex3f (x, y, z);
804 glVertex3f (x + n.x, y + n.y, z + n.z);
807 /* Show the vertex normal vectors */
808 if (wire_p && show_normals_p)
810 XYZ n = orim.pnormals[i];
811 GLfloat x = orim.points[i].x;
812 GLfloat y = orim.points[i].y;
813 GLfloat z = (z1 + z2) / 2;
814 glVertex3f (x, y, z);
815 glVertex3f (x + n.x, y + n.y, z + n.z);
819 if (!wire_p) /* close the quad loop */
821 glNormal3f (orim.pnormals[0].x, orim.pnormals[0].y, orim.pnormals[0].z);
822 glVertex3f (s1*orim.points[0].x, s1*orim.points[0].y, z1);
823 glVertex3f (s2*orim.points[0].x, s2*orim.points[0].y, z2);
825 polys += orim.npoints;
828 /* Draw the outer rim circles, in wire mode */
831 glBegin (GL_LINE_LOOP);
832 for (i = 0; i < orim.npoints; i++)
833 glVertex3f (s1*orim.points[i].x, s1*orim.points[i].y, z1);
835 glBegin (GL_LINE_LOOP);
836 for (i = 0; i < orim.npoints; i++)
837 glVertex3f (s2*orim.points[i].x, s2*orim.points[i].y, z2);
841 /* Draw the inner rim (the hole)
842 (In wire mode, this draws just the upright lines.)
844 glFrontFace (g->inverted_p ? GL_CW : GL_CCW);
845 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
846 for (i = 0; i < irim.npoints; i++)
848 glNormal3f(-irim.pnormals[i].x, -irim.pnormals[i].y,-irim.pnormals[i].z);
849 glVertex3f (irim.points[i].x, irim.points[i].y, z1);
850 glVertex3f (irim.points[i].x, irim.points[i].y, z2);
852 /* Show the face normal vectors */
853 if (wire_p && show_normals_p)
855 XYZ n = irim.fnormals[i];
857 int b = (i == irim.npoints-1 ? 0 : i+1);
858 GLfloat x = (irim.points[a].x + irim.points[b].x) / 2;
859 GLfloat y = (irim.points[a].y + irim.points[b].y) / 2;
860 GLfloat z = (z1 + z2) / 2;
861 glVertex3f (x, y, z);
862 glVertex3f (x - n.x, y - n.y, z);
865 /* Show the vertex normal vectors */
866 if (wire_p && show_normals_p)
868 XYZ n = irim.pnormals[i];
869 GLfloat x = irim.points[i].x;
870 GLfloat y = irim.points[i].y;
871 GLfloat z = (z1 + z2) / 2;
872 glVertex3f (x, y, z);
873 glVertex3f (x - n.x, y - n.y, z);
877 if (!wire_p) /* close the quad loop */
879 glNormal3f (-irim.pnormals[0].x,-irim.pnormals[0].y,-irim.pnormals[0].z);
880 glVertex3f (irim.points[0].x, irim.points[0].y, z1);
881 glVertex3f (irim.points[0].x, irim.points[0].y, z2);
883 polys += irim.npoints;
886 /* Draw the inner rim circles, in wire mode
890 glBegin (GL_LINE_LOOP);
891 for (i = 0; i < irim.npoints; i++)
892 glVertex3f (irim.points[i].x, irim.points[i].y, z1);
894 glBegin (GL_LINE_LOOP);
895 for (i = 0; i < irim.npoints; i++)
896 glVertex3f (irim.points[i].x, irim.points[i].y, z2);
900 /* Draw the side (the flat bit)
902 if (!wire_p || wire_all_p)
905 if (irim.npoints != orim.npoints) abort();
906 for (z = z1; z <= z2; z += z2-z1)
908 GLfloat s = (z == z1 ? s1 : s2);
909 glFrontFace (((z == z1) ^ g->inverted_p) ? GL_CCW : GL_CW);
910 glNormal3f (0, 0, z);
911 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
912 for (i = 0; i < orim.npoints; i++)
914 glVertex3f (s*orim.points[i].x, s*orim.points[i].y, z);
915 glVertex3f ( irim.points[i].x, irim.points[i].y, z);
917 if (!wire_p) /* close the quad loop */
919 glVertex3f (s*orim.points[0].x, s*orim.points[0].y, z);
920 glVertex3f ( irim.points[0].x, irim.points[0].y, z);
922 polys += orim.npoints;
928 free (irim.fnormals);
929 free (irim.pnormals);
932 free (orim.fnormals);
933 free (orim.pnormals);
939 /* Render one gear, unrotated at 0,0.
940 Returns the number of polygons.
943 draw_involute_gear (gear *g, Bool wire_p)
947 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
948 GLfloat shiny = 128.0;
950 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
951 glMateriali (GL_FRONT, GL_SHININESS, shiny);
952 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color);
953 glColor3f (g->color[0], g->color[1], g->color[2]);
956 polys += draw_involute_schematic (g, wire_p);
960 glRotatef (g->wobble, 1, 0, 0);
961 polys += draw_gear_teeth (g, wire_p);
962 polys += draw_gear_interior (g, wire_p);
963 polys += draw_gear_nubs (g, wire_p);