X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fpinion.c;h=aefbe711464f687078b07d793a2bdc465706ae74;hp=413fc1a94db290f295329a09ae61edfc045cc247;hb=6b1c86cf395f59389e4ece4ea8f4bea2c332745b;hpb=bc7b7a8eb122206d239ec0e693676bcce31be1aa diff --git a/hacks/glx/pinion.c b/hacks/glx/pinion.c index 413fc1a9..aefbe711 100644 --- a/hacks/glx/pinion.c +++ b/hacks/glx/pinion.c @@ -1,4 +1,4 @@ -/* pinion, Copyright (c) 2004 Jamie Zawinski +/* pinion, Copyright (c) 2004-2008 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -9,34 +9,15 @@ * implied warranty. */ -#include - -extern XtAppContext app; - -#define PROGCLASS "Pinion" -#define HACK_INIT init_pinion -#define HACK_DRAW draw_pinion -#define HACK_RESHAPE reshape_pinion -#define HACK_HANDLE_EVENT pinion_handle_event -#define EVENT_MASK PointerMotionMask -#define sws_opts xlockmore_opts - -#define DEF_SPIN_SPEED "1.0" -#define DEF_SCROLL_SPEED "1.0" -#define DEF_GEAR_SIZE "1.0" -#define DEF_MAX_RPM "900" - #define DEFAULTS "*delay: 15000 \n" \ "*showFPS: False \n" \ "*wireframe: False \n" \ - "*spinSpeed: " DEF_SPIN_SPEED " \n" \ - "*scrollSpeed:" DEF_SCROLL_SPEED " \n" \ - "*maxRPM: " DEF_MAX_RPM " \n" \ - "*gearSize: " DEF_GEAR_SIZE " \n" \ "*titleFont: -*-times-bold-r-normal-*-180-*\n" \ "*titleFont2: -*-times-bold-r-normal-*-120-*\n" \ "*titleFont3: -*-times-bold-r-normal-*-80-*\n" \ +# define refresh_pinion 0 +# define release_pinion 0 #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) @@ -44,49 +25,18 @@ extern XtAppContext app; #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) #include "xlockmore.h" +#include "normals.h" #include "gltrackball.h" +#include "glxfonts.h" +#include "involute.h" #include #ifdef USE_GL /* whole file */ -#include - -typedef struct { - unsigned long id; /* unique name */ - double x, y, z; /* position */ - double r; /* radius of the gear, at middle of teeth */ - double th; /* rotation (degrees) */ - - GLint nteeth; /* how many teeth */ - double tooth_w, tooth_h; /* size of teeth */ - - double inner_r; /* radius of the (larger) inside hole */ - double inner_r2; /* radius of the (smaller) inside hole, if any */ - double inner_r3; /* yet another */ - - double thickness; /* height of the edge */ - double thickness2; /* height of the (smaller) inside disc if any */ - double thickness3; /* yet another */ - int spokes; /* how many spokes inside, if any */ - int nubs; /* how many little nubbly bits, if any */ - double spoke_thickness; /* spoke versus hole */ - GLfloat wobble; /* factory defect! */ - int motion_blur_p; /* whether it's spinning too fast to draw */ - int polygons; /* how many polys in this gear */ - - double ratio; /* gearing ratio with previous gears */ - double rpm; /* approximate revolutions per minute */ - - Bool base_p; /* whether this gear begins a new train */ - int coax_p; /* whether this is one of a pair of bound gears. - 1 for first, 2 for second. */ - double coax_thickness; /* thickness of the other gear in the pair */ - GLfloat color[4]; - GLfloat color2[4]; - - GLuint dlist; -} gear; - +#define DEF_SPIN_SPEED "1.0" +#define DEF_SCROLL_SPEED "1.0" +#define DEF_GEAR_SIZE "1.0" +#define DEF_MAX_RPM "900" typedef struct { GLXContext *glx_context; @@ -106,24 +56,29 @@ typedef struct { XFontStruct *xfont1, *xfont2, *xfont3; GLuint font1_dlist, font2_dlist, font3_dlist; GLuint title_list; + int draw_tick; + + GLfloat plane_displacement; /* distance between coaxial gears */ + + int debug_size_failures; /* for debugging messages */ + int debug_position_failures; + unsigned long current_length; /* gear count in current train */ + unsigned long current_blur_length; /* how long have we been blurring? */ } pinion_configuration; static pinion_configuration *pps = NULL; -static GLfloat spin_speed, scroll_speed, max_rpm, gear_size; -static GLfloat plane_displacement = 0.1; /* distance between coaxial gears */ + +/* command line arguments */ +static GLfloat spin_speed, scroll_speed, gear_size, max_rpm; static Bool verbose_p = False; /* print progress on stderr */ -static Bool debug_placement_p = False; /* extreme verbosity on stderr */ static Bool debug_p = False; /* render as flat schematic */ -static Bool debug_one_gear_p = False; /* draw one big stationary gear */ -static Bool wire_all_p = False; /* in wireframe, do not abbreviate */ -static int debug_size_failures; /* for debugging messages */ -static int debug_position_failures; -static unsigned long current_length; /* gear count in current train */ -static unsigned long current_blur_length; /* how long have we been blurring? */ +/* internal debugging variables */ +static Bool debug_placement_p = False; /* extreme verbosity on stderr */ +static Bool debug_one_gear_p = False; /* draw one big stationary gear */ static XrmOptionDescRec opts[] = { @@ -144,182 +99,22 @@ static argtype vars[] = { {&verbose_p, "verbose", "Verbose", "False", t_Bool}, }; -ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL}; - - -/* Computing normal vectors - */ - -typedef struct { - double x,y,z; -} XYZ; - -/* Calculate the unit normal at p given two other points p1,p2 on the - surface. The normal points in the direction of p1 crossproduct p2 - */ -static XYZ -calc_normal (XYZ p, XYZ p1, XYZ p2) -{ - XYZ n, pa, pb; - pa.x = p1.x - p.x; - pa.y = p1.y - p.y; - pa.z = p1.z - p.z; - pb.x = p2.x - p.x; - pb.y = p2.y - p.y; - pb.z = p2.z - p.z; - n.x = pa.y * pb.z - pa.z * pb.y; - n.y = pa.z * pb.x - pa.x * pb.z; - n.z = pa.x * pb.y - pa.y * pb.x; - return (n); -} - -static void -do_normal(GLfloat x1, GLfloat y1, GLfloat z1, - GLfloat x2, GLfloat y2, GLfloat z2, - GLfloat x3, GLfloat y3, GLfloat z3) -{ - XYZ p1, p2, p3, p; - p1.x = x1; p1.y = y1; p1.z = z1; - p2.x = x2; p2.y = y2; p2.z = z2; - p3.x = x3; p3.y = y3; p3.z = z3; - p = calc_normal (p1, p2, p3); - glNormal3f (p.x, p.y, p.z); -} +ENTRYPOINT ModeSpecOpt pinion_opts = {countof(opts), opts, countof(vars), vars, NULL}; /* Font stuff */ -static void -load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP) -{ - const char *font = get_string_resource (res, "Font"); - XFontStruct *f; - Font id; - int first, last; - - if (!font) font = "-*-times-bold-r-normal-*-180-*"; - - f = XLoadQueryFont(mi->dpy, font); - if (!f) f = XLoadQueryFont(mi->dpy, "fixed"); - - id = f->fid; - first = f->min_char_or_byte2; - last = f->max_char_or_byte2; - - clear_gl_error (); - *dlistP = glGenLists ((GLuint) last+1); - check_gl_error ("glGenLists"); - glXUseXFont(id, first, last-first+1, *dlistP + first); - check_gl_error ("glXUseXFont"); - - *fontP = f; -} - - static void load_fonts (ModeInfo *mi) { pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - load_font (mi, "titleFont", &pp->xfont1, &pp->font1_dlist); - load_font (mi, "titleFont2", &pp->xfont2, &pp->font2_dlist); - load_font (mi, "titleFont3", &pp->xfont3, &pp->font3_dlist); + load_font (mi->dpy, "titleFont", &pp->xfont1, &pp->font1_dlist); + load_font (mi->dpy, "titleFont2", &pp->xfont2, &pp->font2_dlist); + load_font (mi->dpy, "titleFont3", &pp->xfont3, &pp->font3_dlist); } -static int -string_width (XFontStruct *f, const char *c) -{ - int w = 0; - while (*c) - { - int cc = *((unsigned char *) c); - w += (f->per_char - ? f->per_char[cc-f->min_char_or_byte2].rbearing - : f->min_bounds.rbearing); - c++; - } - return w; -} - -static void -print_title_string (ModeInfo *mi, const char *string, - GLfloat x, GLfloat y, - XFontStruct *font, int font_dlist) -{ - GLfloat line_height = font->ascent + font->descent; - GLfloat sub_shift = (line_height * 0.3); - int cw = string_width (font, "m"); - int tabs = cw * 7; - - y -= line_height; - - glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */ - GL_ENABLE_BIT); /* for various glDisable calls */ - glDisable (GL_LIGHTING); - glDisable (GL_DEPTH_TEST); - { - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - { - glLoadIdentity(); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - { - int i; - int x2 = x; - Bool sub_p = False; - glLoadIdentity(); - - gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height); - - glColor3f (0.8, 0.8, 0); - - glRasterPos2f (x, y); - for (i = 0; i < strlen(string); i++) - { - char c = string[i]; - if (c == '\n') - { - glRasterPos2f (x, (y -= line_height)); - x2 = x; - } - else if (c == '\t') - { - x2 -= x; - x2 = ((x2 + tabs) / tabs) * tabs; /* tab to tab stop */ - x2 += x; - glRasterPos2f (x2, y); - } - else if (c == '[' && (isdigit (string[i+1]))) - { - sub_p = True; - glRasterPos2f (x2, (y -= sub_shift)); - } - else if (c == ']' && sub_p) - { - sub_p = False; - glRasterPos2f (x2, (y += sub_shift)); - } - else - { - glCallList (font_dlist + (int)(c)); - x2 += (font->per_char - ? font->per_char[c - font->min_char_or_byte2].width - : font->min_bounds.width); - } - } - } - glPopMatrix(); - } - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - } - glPopAttrib(); - - glMatrixMode(GL_MODELVIEW); -} static void rpm_string (double rpm, char *s); @@ -343,8 +138,15 @@ new_label (ModeInfo *mi) *label = 0; else { - sprintf (label, "%d teeth\n", g->nteeth); + sprintf (label, "%d teeth\n", (int) g->nteeth); rpm_string (g->rpm, label + strlen(label)); + if (debug_p) + sprintf (label + strlen (label), "\nPolys: %d\nModel: %s (%.2f)\n", + g->polygons, + (g->size == INVOLUTE_SMALL ? "small" : + g->size == INVOLUTE_MEDIUM ? "medium" + : "large"), + g->tooth_h * MI_HEIGHT(mi)); } glNewList (pp->title_list, GL_COMPILE); @@ -359,9 +161,11 @@ new_label (ModeInfo *mi) else f = pp->xfont3, fl = pp->font3_dlist; /* tiny font */ - print_title_string (mi, label, - 10, mi->xgwa.height - 10, - f, fl); + glColor3f (0.8, 0.8, 0); + print_gl_string (mi->dpy, f, fl, + mi->xgwa.width, mi->xgwa.height, + 10, mi->xgwa.height - 10, + label); } glEndList (); } @@ -430,48 +234,6 @@ rpm_string (double rpm, char *s) } - -/* Which of the gear's inside rings is the biggest? - */ -static int -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; - } -} - /* Layout and stuff. */ @@ -483,15 +245,17 @@ biggest_ring (gear *g, double *posP, double *sizeP, double *heightP) static gear * new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p) { - /* pinion_configuration *pp = &pps[MI_SCREEN(mi)]; */ + pinion_configuration *pp = &pps[MI_SCREEN(mi)]; gear *g = (gear *) calloc (1, sizeof (*g)); int loop_count = 0; - static unsigned long id = 0; + static unsigned long id = 0; /* only used in debugging output */ if (!g) return 0; if (coaxial_p && !parent) abort(); g->id = ++id; + g->coax_displacement = pp->plane_displacement; + while (1) { loop_count++; @@ -547,6 +311,8 @@ new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p) if (parent->r < g->r * 0.6) break; /* g much larger than parent */ } + /* g->tooth_slope = (parent ? -parent->tooth_slope : 4); */ + /* Colorize */ g->color[0] = 0.5 + frand(0.5); @@ -628,7 +394,7 @@ new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p) if (g->nteeth > 5) { double size = 0; - biggest_ring (g, 0, &size, 0); + involute_biggest_ring (g, 0, &size, 0); if (size > g->r * 0.2 && (random() % 5) == 0) { g->nubs = 1 + (random() % 16); @@ -640,6 +406,15 @@ new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p) if (g->inner_r2 > g->inner_r) abort(); if (g->inner_r > g->r) abort(); + /* Decide how complex the polygon model should be. + */ + { + double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */ + if (pix <= 2.5) g->size = INVOLUTE_SMALL; + else if (pix <= 3.5) g->size = INVOLUTE_MEDIUM; + else g->size = INVOLUTE_LARGE; + } + g->base_p = !parent; return g; @@ -667,7 +442,7 @@ place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p) progname, (g->r + g->tooth_h), gear_size, pp->vp_width, pp->vp_height); - debug_size_failures++; + pp->debug_size_failures++; return False; } @@ -732,7 +507,7 @@ place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p) } else if (coaxial_p) { - double off = plane_displacement; + double off = pp->plane_displacement; g->x = parent->x; g->y = parent->y; @@ -765,7 +540,7 @@ place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p) if (verbose_p && debug_placement_p) fprintf (stderr, "%s: placement: bad depth: %.2f\n", progname, g->z); - debug_position_failures++; + pp->debug_position_failures++; return False; } } @@ -791,7 +566,7 @@ place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p) if (verbose_p && debug_placement_p) fprintf (stderr, "%s: placement: out of bounds: %s\n", progname, (g->y > pp->vp_top ? "top" : "bottom")); - debug_position_failures++; + pp->debug_position_failures++; return False; } @@ -835,7 +610,7 @@ place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p) { if (verbose_p && debug_placement_p) fprintf (stderr, "%s: placement: out of bounds: left\n", progname); - debug_position_failures++; + pp->debug_position_failures++; return False; } @@ -865,7 +640,7 @@ place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p) if (verbose_p && debug_placement_p) fprintf (stderr, "%s: placement: collision with %lu\n", progname, og->id); - debug_position_failures++; + pp->debug_position_failures++; return False; } } @@ -877,7 +652,7 @@ place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p) /* Make deeper gears be darker. */ { - double depth = g->z / plane_displacement; + double depth = g->z / pp->plane_displacement; double brightness = 1 + (depth / 6); double limit = 0.4; if (brightness < limit) brightness = limit; @@ -992,8 +767,8 @@ push_gear (ModeInfo *mi) Bool last_ditch_coax_p = False; int loop_count = 0; - debug_size_failures = 0; - debug_position_failures = 0; + pp->debug_size_failures = 0; + pp->debug_position_failures = 0; AGAIN: loop_count++; @@ -1030,7 +805,7 @@ push_gear (ModeInfo *mi) it's a safe guess that we've wandered off into the woods and aren't coming back. Bail on this train. */ - if (current_blur_length >= 10) + if (pp->current_blur_length >= 10) { if (verbose_p) fprintf (stderr, "%s: it's a blurpocalypse!\n\n", progname); @@ -1096,7 +871,7 @@ push_gear (ModeInfo *mi) "%s: placement: resetting growth zone! " "failed: %d size, %d pos\n", progname, - debug_size_failures, debug_position_failures); + pp->debug_size_failures, pp->debug_position_failures); for (i = pp->ngears-1; i >= 0; i--) { gear *g = pp->gears[i]; @@ -1128,34 +903,34 @@ push_gear (ModeInfo *mi) fprintf (stderr, " %2d%%", (int) (g->r * 2 * 100 / pp->vp_height)); - fprintf (stderr, " %2d teeth", g->nteeth); + fprintf (stderr, " %2d teeth", (int) g->nteeth); fprintf (stderr, " %3.0f rpm;", g->rpm); { char buf1[50], buf2[50], buf3[100]; *buf1 = 0; *buf2 = 0; *buf3 = 0; - if (debug_size_failures) - sprintf (buf1, "%3d sz", debug_size_failures); - if (debug_position_failures) - sprintf (buf2, "%2d pos", debug_position_failures); + if (pp->debug_size_failures) + sprintf (buf1, "%3d sz", pp->debug_size_failures); + if (pp->debug_position_failures) + sprintf (buf2, "%2d pos", pp->debug_position_failures); if (*buf1 || *buf2) sprintf (buf3, " tries: %-7s%s", buf1, buf2); fprintf (stderr, "%-21s", buf3); } - if (g->base_p) fprintf (stderr, " RESET %lu", current_length); + if (g->base_p) fprintf (stderr, " RESET %lu", pp->current_length); fprintf (stderr, "\n"); } if (g->base_p) - current_length = 1; + pp->current_length = 1; else - current_length++; + pp->current_length++; if (g->motion_blur_p) - current_blur_length++; + pp->current_blur_length++; else - current_blur_length = 0; + pp->current_blur_length = 0; } @@ -1212,6 +987,7 @@ scroll_gears (ModeInfo *mi) else break; i++; + if (debug_one_gear_p) break; } /* @@ -1250,6 +1026,7 @@ static void ffwd (ModeInfo *mi) { pinion_configuration *pp = &pps[MI_SCREEN(mi)]; + if (debug_one_gear_p) return; while (1) { gear *g = farthest_gear (mi, True); @@ -1261,790 +1038,13 @@ ffwd (ModeInfo *mi) -/* Rendering the 3D objects into the scene. - */ - - -/* Draws an uncapped tube of the given radius extending from top to bottom, - with faces pointing either in or out. - */ -static int -draw_ring (ModeInfo *mi, int segments, - GLfloat r, GLfloat top, GLfloat bottom, Bool in_p) -{ - int i; - int polys = 0; - Bool wire_p = MI_IS_WIREFRAME(mi); - GLfloat width = M_PI * 2 / segments; - - 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 (cth * r, sth * r, top); - glVertex3f (cth * r, 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 (ModeInfo *mi, int segments, - GLfloat ra, GLfloat rb, GLfloat z, Bool up_p) -{ - int i; - int polys = 0; - Bool wire_p = MI_IS_WIREFRAME(mi); - 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++; - } - glEnd(); - return polys; -} - - -/* Draws N thick radial lines between the given radii, - with faces pointing either up or down. - */ -static int -draw_spokes (ModeInfo *mi, int n, GLfloat thickness, int segments, - GLfloat ra, GLfloat rb, GLfloat z1, GLfloat z2) -{ - int i; - int polys = 0; - Bool wire_p = MI_IS_WIREFRAME(mi); - GLfloat width; - int segments2 = 0; - int insegs, outsegs; - int tick; - int state; - - 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 (cth1 * ra, sth1 * ra, z1); - glVertex3f (cth1 * rb, sth1 * rb, z1); - glVertex3f (cth2 * rb, sth2 * rb, z1); - glVertex3f (cth2 * ra, sth2 * ra, z1); - polys++; - glEnd(); - - /* bottom */ - glFrontFace (GL_CW); - glBegin (wire_p ? GL_LINES : GL_QUADS); - glNormal3f (0, 0, 1); - glVertex3f (cth1 * ra, sth1 * ra, z2); - glVertex3f (cth1 * rb, sth1 * rb, z2); - glVertex3f (cth2 * rb, sth2 * rb, z2); - glVertex3f (cth2 * ra, 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 (cth1 * rb, sth1 * rb, z1, - cth1 * ra, sth1 * ra, z1, - cth1 * rb, sth1 * rb, z2); - glVertex3f (cth1 * ra, sth1 * ra, z1); - glVertex3f (cth1 * rb, sth1 * rb, z1); - glVertex3f (cth1 * rb, sth1 * rb, z2); - glVertex3f (cth1 * ra, 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 (cth2 * ra, sth2 * ra, z1, - cth2 * rb, sth2 * rb, z1, - cth2 * rb, sth2 * rb, z2); - glVertex3f (cth2 * ra, sth2 * ra, z1); - glVertex3f (cth2 * rb, sth2 * rb, z1); - glVertex3f (cth2 * rb, sth2 * rb, z2); - glVertex3f (cth2 * ra, sth2 * ra, z2); - polys++; - glEnd(); - } - - rb = orb; - } - glEnd(); - return polys; -} - - -/* Draws some bumps (embedded cylinders) on the gear. - */ -static int -draw_gear_nubs (ModeInfo *mi, gear *g) -{ - Bool wire_p = MI_IS_WIREFRAME(mi); - int polys = 0; - int i; - int steps = 20; - double r, size, height; - GLfloat *cc; - int which; - GLfloat width, off; - - if (! g->nubs) return 0; - - which = 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); - - 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(); - glTranslatef (cos(th) * r, sin(th) * r, 0); - - if (wire_p && !wire_all_p) - polys += draw_ring (mi, steps/2, size, 0, 0, False); - else - { - polys += draw_disc (mi, steps, 0, size, -height, True); - polys += draw_disc (mi, steps, 0, size, height, False); - polys += draw_ring (mi, steps, size, -height, height, False); - } - glPopMatrix(); - } - return polys; -} - - - -/* Draws a much simpler representation of a gear. - */ -static int -draw_gear_schematic (ModeInfo *mi, gear *g) -{ - Bool wire_p = MI_IS_WIREFRAME(mi); - 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 (ModeInfo *mi, gear *g) -{ - Bool wire_p = MI_IS_WIREFRAME(mi); - int polys = 0; - - int steps = g->nteeth * 2; - if (steps < 10) steps = 10; - if (wire_p && !wire_all_p) steps /= 2; - - /* 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; - GLfloat rb = g->inner_r2; - GLfloat za = -g->thickness2/2; - GLfloat zb = g->thickness2/2; - - glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color2); - - if ((g->coax_p != 1 && !g->inner_r3) || - (wire_p && wire_all_p)) - polys += draw_ring (mi, steps, rb, za, zb, True); /* ring facing in */ - - if (wire_p && wire_all_p) - polys += draw_ring (mi, steps, ra, za, zb, True); /* ring facing in */ - - if (g->spokes) - polys += draw_spokes (mi, g->spokes, g->spoke_thickness, - steps, ra, rb, za, zb); - else if (!wire_p || wire_all_p) - { - polys += draw_disc (mi, steps, ra, rb, za, True); /* top plate */ - polys += draw_disc (mi, steps, ra, rb, zb, False); /* bottom plate */ - } - } - - /* 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; - - glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color); - - polys += draw_ring (mi, steps, ra, za, zb, False); /* ring facing out */ - - if (g->coax_p != 1 || (wire_p && wire_all_p)) - polys += draw_ring (mi, steps, rb, za, zb, True); /* ring facing in */ - - if (!wire_p || wire_all_p) - { - polys += draw_disc (mi, steps, ra, rb, za, True); /* top plate */ - polys += draw_disc (mi, steps, ra, rb, zb, False); /* bottom plate */ - } - } - - /* 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 + plane_displacement + cap_height; - - glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color); - - if (wire_p && !wire_all_p) steps /= 2; - - polys += draw_ring (mi, steps, ra, za, zb, False); /* ring facing out */ - - if (!wire_p || wire_all_p) - { - polys += draw_disc (mi, steps, 0, ra, za, True); /* top plate */ - polys += draw_disc (mi, steps, 0, ra, zb, False); /* bottom plate */ - } - } - return polys; -} - - -/* 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. - */ -static void -gear_teeth_geometry (ModeInfo *mi, gear *g, - int *points_per_tooth_ret, - XYZ **points_ret, - XYZ **normals_ret) -{ - int i; - - int ppt = 15; /* points per tooth */ - - GLfloat width = M_PI * 2 / g->nteeth; - - GLfloat rh = g->tooth_h; - GLfloat tw = width; - GLfloat fudge = (g->nteeth >= 5 ? 0 : 0.04); /* reshape small ones a bit */ - - XYZ *points = (XYZ *) calloc (ppt * g->nteeth + 1, sizeof(*points)); - XYZ *fnormals = (XYZ *) calloc (ppt * g->nteeth + 1, sizeof(*points)); - XYZ *pnormals = (XYZ *) calloc (ppt * g->nteeth + 1, sizeof(*points)); - int npoints = 0; - - /* Approximate shape of an "involute" gear tooth. - - (TH) - th0 th1 th2 th3 th4 th5 th6 th7 th8 th9 th10 - : : : : : : : : : : : - : : : : : : : : : : : - r0 ........:..:..:...___________...:..:..:......:......:.. - : : : /: : :\ : : : : : - : : : / : : : \ : : : : : - : : :/ : : : \: : : : : - r1 ........:.....@...:....:....:...@..:..:......:......:.. - : : @: : : : :@ : : : : - (R) ...........:...@.:...:....:....:...:.@..........:......:...... - : :@ : : : : : @: : : : - r2 ........:..@..:...:....:....:...:..@:........:......:.. - : /: : : : : : :\ : : : - :/ : : : : : : : \: : : / - r3 ......__/..:..:...:....:....:...:..:..\______________/ - : : : : : : : : : : : - | : : : : : : : | : : - : : : : : : : : : : : - | : : : : : : : | : : - r4 ......__:_____________________________:________________ - */ - - GLfloat R = g->r; - - GLfloat r[20]; - GLfloat th[20]; - - r[0] = R + (rh * 0.5); - r[1] = R + (rh * 0.25); - r[2] = R - (rh * 0.25); - r[3] = R - (rh * 0.5); - r[4] = g->inner_r; - - th[0] = -tw * 0.45; - th[1] = -tw * 0.30; - th[2] = -tw *(0.16 - fudge); - th[3] = -tw * 0.04; - th[4] = 0; - th[5] = tw * 0.04; - th[6] = tw *(0.16 - fudge); - th[7] = tw * 0.30; - th[8] = tw * 0.45; - th[9] = width / 2; - th[10]= th[0] + width; - - if (!points || !fnormals || !pnormals) - { - fprintf (stderr, "%s: out of memory\n", progname); - exit (1); - } - - npoints = 0; - - /* First, compute the coordinates of every point used for every tooth. - */ - for (i = 0; i < g->nteeth; i++) - { - GLfloat TH = (i * width) + (width/4); - -# undef PUSH -# define PUSH(PR,PTH) \ - points[npoints].x = cos(TH+th[(PTH)]) * r[(PR)]; \ - points[npoints].y = sin(TH+th[(PTH)]) * r[(PR)]; \ - npoints++ - - /* start1 = npoints; */ - - PUSH(3, 0); /* tooth left 1 */ - PUSH(2, 1); /* tooth left 2 */ - PUSH(1, 2); /* tooth left 3 */ - PUSH(0, 3); /* tooth top 1 */ - PUSH(0, 5); /* tooth top 2 */ - PUSH(1, 6); /* tooth right 1 */ - PUSH(2, 7); /* tooth right 2 */ - PUSH(3, 8); /* tooth right 3 */ - PUSH(3, 10); /* gap top */ - - /* end1 = npoints; */ - - PUSH(4, 8); /* gap interior */ - - /* start2 = npoints; */ - - PUSH(4, 10); /* tooth interior 1 */ - PUSH(4, 8); /* tooth interior 2 */ - PUSH(4, 4); /* tooth bottom 1 */ - PUSH(4, 0); /* tooth bottom 2 */ - - /* end2 = npoints; */ - - PUSH(3, 4); /* midpoint */ - - /* mid = npoints-1; */ - - if (i == 0 && npoints != ppt) abort(); /* go update "ppt"! */ -# undef PUSH - } - - - /* Now compute the face normals for each facet on the tooth rim. - */ - for (i = 0; i < npoints; i++) - { - XYZ p1, p2, p3; - p1 = points[i]; - p2 = points[i+1]; - p3 = p1; - p3.z++; - 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 < npoints; i++) - { - int a = (i == 0 ? npoints-1 : i-1); - int b = i; - - /* Kludge to fix the normal on the last top point: since the - faces go all the way around, this normal pointed clockwise - instead of radially out. */ - int start1 = (i / ppt) * ppt; - int end1 = start1 + 9; - XYZ n1, n2; - - if (b == end1-1) - b = (start1 + ppt == npoints ? 0 : start1 + ppt); - - n1 = fnormals[a]; /* normal of [i-1 - i] face */ - n2 = fnormals[b]; /* normal of [i - i+1] face */ - pnormals[i].x = (n1.x + n2.x) / 2; - pnormals[i].y = (n1.y + n2.y) / 2; - pnormals[i].z = (n1.z + n2.z) / 2; - } - - free (fnormals); - - if (points_ret) - *points_ret = points; - else - free (points); - - if (normals_ret) - *normals_ret = pnormals; - else - free (pnormals); - - if (points_per_tooth_ret) - *points_per_tooth_ret = ppt; -} - - -/* Renders all teeth of a gear. - */ -static int -draw_gear_teeth (ModeInfo *mi, gear *g) -{ - Bool wire_p = MI_IS_WIREFRAME(mi); - int polys = 0; - int i; - - GLfloat z1 = -g->thickness/2; - GLfloat z2 = g->thickness/2; - - int ppt; - XYZ *points, *pnormals; - - gear_teeth_geometry (mi, g, &ppt, &points, &pnormals); - - glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g->color); - - for (i = 0; i < g->nteeth; i++) - { - int j; - GLfloat z; - - int start1 = i * ppt; - int end1 = start1 + 9; - int start2 = end1 + 1; - int end2 = start2 + 4; - int mid = end2; - - /* Outside rim of the tooth - */ - glFrontFace (GL_CW); - glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP); - for (j = start1; j < end1; j++) - { - glNormal3f (pnormals[j].x, pnormals[j].y, pnormals[j].z); - glVertex3f (points[j].x, points[j].y, z1); - glVertex3f (points[j].x, points[j].y, z2); - polys++; - -# if 0 - /* Show the face normal vectors */ - if (wire_p) - { - XYZ n = fnormals[j]; - GLfloat x = (points[j].x + points[j+1].x) / 2; - GLfloat y = (points[j].y + points[j+1].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) - { - XYZ n = pnormals[j]; - GLfloat x = points[j].x; - GLfloat y = points[j].y; - GLfloat z = (z1 + z2) / 2; - glVertex3f (x, y, z); - glVertex3f (x + n.x, y + n.y, z); - } -# endif /* 0 */ - } - glEnd(); - - /* Some more lines for the outside rim of the tooth... - */ - if (wire_p) - { - glBegin (GL_LINE_STRIP); - for (j = start1; j < end1; j++) - glVertex3f (points[j].x, points[j].y, z1); - glEnd(); - glBegin (GL_LINE_STRIP); - for (j = start1; j < end1; j++) - glVertex3f (points[j].x, points[j].y, z2); - glEnd(); - } - - /* Inside rim behind the tooth - */ - glFrontFace (GL_CW); - glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP); - for (j = start2; j < end2; j++) - { - glNormal3f (-points[j].x, -points[j].y, 0); - glVertex3f ( points[j].x, points[j].y, z1); - glVertex3f ( points[j].x, points[j].y, z2); - polys++; - } - glEnd(); - - /* Some more lines for the inside rim... - */ - if (wire_p) - { - glBegin (GL_LINE_STRIP); - for (j = start2; j < end2; j++) - glVertex3f (points[j].x, points[j].y, z1); - glEnd(); - glBegin (GL_LINE_STRIP); - for (j = start2; j < end2; j++) - glVertex3f (points[j].x, points[j].y, z2); - glEnd(); - } - - /* All top and bottom facets. We can skip all of these in wire mode. - */ - if (!wire_p || wire_all_p) - for (z = z1; z <= z2; z += z2-z1) - { - /* Flat edge of the tooth - */ - glFrontFace (z == z1 ? GL_CW : GL_CCW); - glBegin (wire_p ? GL_LINES : GL_TRIANGLE_FAN); - glNormal3f (0, 0, z); - for (j = start1; j < end2; j++) - { - if (j == end1-1 || j == end1 || j == start2) - continue; /* kludge... skip these... */ - - if (wire_p || j == start1) - glVertex3f (points[mid].x, points[mid].y, z); - glVertex3f (points[j].x, points[j].y, z); - polys++; - } - glVertex3f (points[start1].x, points[start1].y, z); - glEnd(); - - /* Flat edge between teeth - */ - glFrontFace (z == z1 ? GL_CW : GL_CCW); - glBegin (wire_p ? GL_LINES : GL_QUADS); - glNormal3f (0, 0, z); - glVertex3f (points[end1-1 ].x, points[end1-1 ].y, z); - glVertex3f (points[start2 ].x, points[start2 ].y, z); - glVertex3f (points[start2+1].x, points[start2+1].y, z); - glVertex3f (points[end1-2 ].x, points[end1-2 ].y, z); - polys++; - glEnd(); - } - } - - free (points); - free (pnormals); - return polys; -} - - -/* Render one gear, unrotated at 0,0. - */ -static int -draw_gear_1 (ModeInfo *mi, gear *g) -{ - Bool wire_p = MI_IS_WIREFRAME(mi); - int polys = 0; - - static GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0}; - static 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 (debug_p && wire_p) - polys += draw_gear_schematic (mi, g); - else - { - glPushMatrix(); - glRotatef (g->wobble, 1, 0, 0); - polys += draw_gear_teeth (mi, g); - polys += draw_gear_interior (mi, g); - polys += draw_gear_nubs (mi, g); - glPopMatrix(); - } - return polys; -} - - /* Render one gear in the proper position, creating the gear's display list first if necessary. */ static void draw_gear (ModeInfo *mi, int which) { + Bool wire_p = MI_IS_WIREFRAME(mi); pinion_configuration *pp = &pps[MI_SCREEN(mi)]; gear *g = pp->gears[which]; GLfloat th; @@ -2069,7 +1069,7 @@ draw_gear (ModeInfo *mi, int which) } glNewList (g->dlist, GL_COMPILE); - g->polygons = draw_gear_1 (mi, g); + g->polygons = draw_involute_gear (g, (wire_p && debug_p ? 2 : wire_p)); glEndList (); } @@ -2096,7 +1096,7 @@ draw_gear (ModeInfo *mi, int which) glPushName (g->id); if (! visible_p) - mi->polygon_count += draw_gear_schematic (mi, g); + mi->polygon_count += draw_involute_schematic (g, wire_p); else { glCallList (g->dlist); @@ -2127,7 +1127,7 @@ draw_gears (ModeInfo *mi) /* draw a line connecting gears that are, uh, geared. */ if (debug_p) { - static GLfloat color[4] = {1.0, 0.0, 0.0, 1.0}; + static const GLfloat color[4] = {1.0, 0.0, 0.0, 1.0}; GLfloat off = 0.1; GLfloat ox=0, oy=0, oz=0; @@ -2155,7 +1155,7 @@ draw_gears (ModeInfo *mi) /* Mouse hit detection */ -void +static void find_mouse_gear (ModeInfo *mi) { pinion_configuration *pp = &pps[MI_SCREEN(mi)]; @@ -2231,7 +1231,7 @@ find_mouse_gear (ModeInfo *mi) /* Window management, etc */ -void +ENTRYPOINT void reshape_pinion (ModeInfo *mi, int width, int height) { GLfloat h = (GLfloat) height / (GLfloat) width; @@ -2273,13 +1273,13 @@ reshape_pinion (ModeInfo *mi, int width, int height) } -Bool +ENTRYPOINT Bool pinion_handle_event (ModeInfo *mi, XEvent *event) { pinion_configuration *pp = &pps[MI_SCREEN(mi)]; if (event->xany.type == ButtonPress && - event->xbutton.button & Button1) + event->xbutton.button == Button1) { pp->button_down_p = True; gltrackball_start (pp->trackball, @@ -2288,11 +1288,21 @@ pinion_handle_event (ModeInfo *mi, XEvent *event) return True; } else if (event->xany.type == ButtonRelease && - event->xbutton.button & Button1) + event->xbutton.button == Button1) { pp->button_down_p = False; return True; } + else if (event->xany.type == ButtonPress && + (event->xbutton.button == Button4 || + event->xbutton.button == Button5 || + event->xbutton.button == Button6 || + event->xbutton.button == Button7)) + { + gltrackball_mousewheel (pp->trackball, event->xbutton.button, 5, + !!event->xbutton.state); + return True; + } else if (event->xany.type == MotionNotify && pp->button_down_p) { @@ -2317,7 +1327,7 @@ pinion_handle_event (ModeInfo *mi, XEvent *event) } -void +ENTRYPOINT void init_pinion (ModeInfo *mi) { pinion_configuration *pp; @@ -2347,7 +1357,7 @@ init_pinion (ModeInfo *mi) pp->gears_size = 0; pp->gears = 0; - plane_displacement *= gear_size; + pp->plane_displacement = gear_size * 0.1; if (!wire) { @@ -2373,18 +1383,19 @@ init_pinion (ModeInfo *mi) } -void +ENTRYPOINT void draw_pinion (ModeInfo *mi) { pinion_configuration *pp = &pps[MI_SCREEN(mi)]; Display *dpy = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); Bool wire_p = MI_IS_WIREFRAME(mi); - static int tick = 0; if (!pp->glx_context) return; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(pp->glx_context)); + if (!pp->button_down_p) { if (!debug_one_gear_p || pp->ngears == 0) @@ -2455,9 +1466,9 @@ draw_pinion (ModeInfo *mi) if (!wire_p) glEnable(GL_LIGHTING); } - if (tick++ > 10) /* only do this every N frames */ + if (pp->draw_tick++ > 10) /* only do this every N frames */ { - tick = 0; + pp->draw_tick = 0; find_mouse_gear (mi); new_label (mi); } @@ -2472,4 +1483,6 @@ draw_pinion (ModeInfo *mi) glXSwapBuffers(dpy, window); } +XSCREENSAVER_MODULE ("Pinion", pinion) + #endif /* USE_GL */