diff options
Diffstat (limited to 'hacks/glx/pinion.c')
| -rw-r--r-- | hacks/glx/pinion.c | 1486 |
1 files changed, 0 insertions, 1486 deletions
diff --git a/hacks/glx/pinion.c b/hacks/glx/pinion.c deleted file mode 100644 index 343600f..0000000 --- a/hacks/glx/pinion.c +++ /dev/null @@ -1,1486 +0,0 @@ -/* pinion, Copyright (c) 2004-2014 Jamie Zawinski <jwz@jwz.org> - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -#define DEFAULTS "*delay: 15000 \n" \ - "*showFPS: False \n" \ - "*wireframe: False \n" \ - "*titleFont: sans-serif 18\n" \ - "*titleFont2: sans-serif 12\n" \ - "*titleFont3: sans-serif 8\n" \ - -# define release_pinion 0 - -#undef BELLRAND -#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) - -#include "xlockmore.h" -#include "normals.h" -#include "gltrackball.h" -#include "texfont.h" -#include "involute.h" -#include <ctype.h> - -#ifdef USE_GL /* whole file */ - -#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; - GLfloat vp_left, vp_right, vp_top, vp_bottom; /* default visible area */ - GLfloat vp_width, vp_height; - GLfloat render_left, render_right; /* area in which gears are displayed */ - GLfloat layout_left, layout_right; /* layout region, on the right side */ - - int ngears; - int gears_size; - gear **gears; - - trackball_state *trackball; - Bool button_down_p; - unsigned long mouse_gear_id; - - texture_font_data *font1, *font2, *font3; - - 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; - -/* 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_p = False; /* render as flat schematic */ - -/* 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[] = { - { "-spin", ".spinSpeed", XrmoptionSepArg, 0 }, - { "-scroll", ".scrollSpeed", XrmoptionSepArg, 0 }, - { "-size", ".gearSize", XrmoptionSepArg, 0 }, - { "-max-rpm",".maxRPM", XrmoptionSepArg, 0 }, - { "-debug", ".debug", XrmoptionNoArg, "True" }, - { "-verbose",".verbose", XrmoptionNoArg, "True" }, -}; - -static argtype vars[] = { - {&spin_speed, "spinSpeed", "SpinSpeed", DEF_SPIN_SPEED, t_Float}, - {&scroll_speed, "scrollSpeed", "ScrollSpeed", DEF_SCROLL_SPEED, t_Float}, - {&gear_size, "gearSize", "GearSize", DEF_GEAR_SIZE, t_Float}, - {&max_rpm, "maxRPM", "MaxRPM", DEF_MAX_RPM, t_Float}, - {&debug_p, "debug", "Debug", "False", t_Bool}, - {&verbose_p, "verbose", "Verbose", "False", t_Bool}, -}; - -ENTRYPOINT ModeSpecOpt pinion_opts = {countof(opts), opts, countof(vars), vars, NULL}; - - -/* Font stuff - */ - -static void -load_fonts (ModeInfo *mi) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - pp->font1 = load_texture_font (mi->dpy, "titleFont"); - pp->font2 = load_texture_font (mi->dpy, "titleFont2"); - pp->font3 = load_texture_font (mi->dpy, "titleFont3"); -} - - - -static void rpm_string (double rpm, char *s); - -static void -draw_label (ModeInfo *mi) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - char label[1024]; - int i; - gear *g = 0; - - if (pp->mouse_gear_id) - for (i = 0; i < pp->ngears; i++) - if (pp->gears[i]->id == pp->mouse_gear_id) - { - g = pp->gears[i]; - break; - } - - if (!g) - *label = 0; - else - { - 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)); - } - - if (*label) - { - texture_font_data *fd; - if (MI_WIDTH(mi) >= 500 && MI_HEIGHT(mi) >= 375) - fd = pp->font1; - else if (MI_WIDTH(mi) >= 350 && MI_HEIGHT(mi) >= 260) - fd = pp->font2; - else - fd = pp->font3; - - glColor3f (0.8, 0.8, 0); - print_texture_label (mi->dpy, fd, - mi->xgwa.width, mi->xgwa.height, - 1, label); - } -} - - -/* Some utilities - */ - - -/* Find the gear in the scene that is farthest to the right or left. - */ -static gear * -farthest_gear (ModeInfo *mi, Bool left_p) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - int i; - gear *rg = 0; - double x = (left_p ? 999999 : -999999); - for (i = 0; i < pp->ngears; i++) - { - gear *g = pp->gears[i]; - double gx = g->x + ((g->r + g->tooth_h) * (left_p ? -1 : 1)); - if (left_p ? (x > gx) : (x < gx)) - { - rg = g; - x = gx; - } - } - return rg; -} - - -/* Compute the revolutions per minute of a gear. - */ -static void -compute_rpm (ModeInfo *mi, gear *g) -{ - double fps, rpf, rps; - fps = (MI_PAUSE(mi) == 0 ? 999999 : 1000000.0 / MI_PAUSE(mi)); - - if (fps > 150) fps = 150; /* let's be reasonable... */ - if (fps < 10) fps = 10; - - rpf = (g->ratio * spin_speed) / 360.0; /* rotations per frame */ - rps = rpf * fps; /* rotations per second */ - g->rpm = rps * 60; -} - -/* Prints the RPM into a string, doing fancy float formatting. - */ -static void -rpm_string (double rpm, char *s) -{ - char buf[30]; - int L; - if (rpm >= 0.1) sprintf (buf, "%.2f", rpm); - else if (rpm >= 0.001) sprintf (buf, "%.4f", rpm); - else if (rpm >= 0.00001) sprintf (buf, "%.8f", rpm); - else sprintf (buf, "%.16f",rpm); - - L = strlen(buf); - while (buf[L-1] == '0') buf[--L] = 0; - if (buf[L-1] == '.') buf[--L] = 0; - strcpy (s, buf); - strcat (s, " RPM"); -} - - - -/* Layout and stuff. - */ - - -/* Create and return a new gear sized for placement next to or on top of - the given parent gear (if any.) Returns 0 if out of memory. - */ -static gear * -new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - gear *g = (gear *) calloc (1, sizeof (*g)); - int loop_count = 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++; - if (loop_count > 1000) - /* The only time we loop in here is when making a coaxial gear, and - trying to pick a radius that is either significantly larger or - smaller than its parent. That shouldn't be hard, so something - must be really wrong if we can't do that in only a few tries. - */ - abort(); - - /* Pick the size of the teeth. - */ - if (parent && !coaxial_p) /* adjascent gears need matching teeth */ - { - g->tooth_w = parent->tooth_w; - g->tooth_h = parent->tooth_h; - g->thickness = parent->thickness; - g->thickness2 = parent->thickness2; - g->thickness3 = parent->thickness3; - } - else /* gears that begin trains get any size they want */ - { - double scale = (1.0 + BELLRAND(4.0)) * gear_size; - g->tooth_w = 0.007 * scale; - g->tooth_h = 0.005 * scale; - g->thickness = g->tooth_h * (0.1 + BELLRAND(1.5)); - g->thickness2 = g->thickness / 4; - g->thickness3 = g->thickness; - } - - /* Pick the number of teeth, and thus, the radius. - */ - { - double c; - - AGAIN: - g->nteeth = 3 + (random() % 97); /* from 3 to 100 teeth */ - - if (g->nteeth < 7 && (random() % 5) != 0) - goto AGAIN; /* Let's make very small tooth-counts more rare */ - - c = g->nteeth * g->tooth_w * 2; /* circumference = teeth + gaps */ - g->r = c / (M_PI * 2); /* c = 2 pi r */ - } - - - /* Are we done now? - */ - if (! coaxial_p) break; /* yes */ - if (g->nteeth == parent->nteeth) continue; /* ugly */ - if (g->r < parent->r * 0.6) break; /* g much smaller than parent */ - if (parent->r < g->r * 0.6) break; /* g much larger than parent */ - } - - /* g->tooth_slope = (parent ? -parent->tooth_slope : 4); */ - - if (debug_one_gear_p) - g->tooth_slope = frand(20)-10; - - - /* Colorize - */ - g->color[0] = 0.5 + frand(0.5); - g->color[1] = 0.5 + frand(0.5); - g->color[2] = 0.5 + frand(0.5); - g->color[3] = 1.0; - - g->color2[0] = g->color[0] * 0.85; - g->color2[1] = g->color[1] * 0.85; - g->color2[2] = g->color[2] * 0.85; - g->color2[3] = g->color[3]; - - - /* Decide on shape of gear interior: - - just a ring with teeth; - - that, plus a thinner in-set "plate" in the middle; - - that, plus a thin raised "lip" on the inner plate; - - or, a wide lip (really, a thicker third inner plate.) - */ - if ((random() % 10) == 0) - { - /* inner_r can go all the way in; there's no inset disc. */ - g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8); - g->inner_r2 = 0; - g->inner_r3 = 0; - } - else - { - /* inner_r doesn't go in very far; inner_r2 is an inset disc. */ - g->inner_r = (g->r * 0.5) + frand((g->r - g->tooth_h) * 0.4); - g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5); - g->inner_r3 = 0; - - if (g->inner_r2 > (g->r * 0.2)) - { - int nn = (random() % 10); - if (nn <= 2) - g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2); - else if (nn <= 7 && g->inner_r2 >= 0.1) - g->inner_r3 = g->inner_r2 - 0.01; - } - } - - /* Coaxial gears need to have the same innermost hole size (for the axle.) - Use whichever of the two is smaller. (Modifies parent.) - */ - if (coaxial_p) - { - double hole1 = (g->inner_r3 ? g->inner_r3 : - g->inner_r2 ? g->inner_r2 : - g->inner_r); - double hole2 = (parent->inner_r3 ? parent->inner_r3 : - parent->inner_r2 ? parent->inner_r2 : - parent->inner_r); - double hole = (hole1 < hole2 ? hole1 : hole2); - if (hole <= 0) abort(); - - if (g->inner_r3) g->inner_r3 = hole; - else if (g->inner_r2) g->inner_r2 = hole; - else g->inner_r = hole; - - if (parent->inner_r3) parent->inner_r3 = hole; - else if (parent->inner_r2) parent->inner_r2 = hole; - else parent->inner_r = hole; - } - - /* If we have three discs, sometimes make the middle disc be spokes. - */ - if (g->inner_r3 && ((random() % 5) == 0)) - { - g->spokes = 2 + BELLRAND (5); - g->spoke_thickness = 1 + frand(7.0); - if (g->spokes == 2 && g->spoke_thickness < 2) - g->spoke_thickness += 1; - } - - /* Sometimes add little nubbly bits, if there is room. - */ - if (g->nteeth > 5) - { - double size = 0; - involute_biggest_ring (g, 0, &size, 0); - if (size > g->r * 0.2 && (random() % 5) == 0) - { - g->nubs = 1 + (random() % 16); - if (g->nubs > 8) g->nubs = 1; - } - } - - if (g->inner_r3 > g->inner_r2) abort(); - 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 if (pix <= 25) g->size = INVOLUTE_LARGE; - else g->size = INVOLUTE_HUGE; - } - - g->base_p = !parent; - - return g; -} - - -/* Given a newly-created gear, place it next to its parent in the scene, - with its teeth meshed and the proper velocity. Returns False if it - didn't work. (Call this a bunch of times until either it works, or - you decide it's probably not going to.) - */ -static Bool -place_gear (ModeInfo *mi, gear *g, gear *parent, Bool coaxial_p) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - - /* If this gear takes up more than 1/3rd of the screen, it's no good. - */ - if (((g->r + g->tooth_h) * (6 / gear_size)) >= pp->vp_width || - ((g->r + g->tooth_h) * (6 / gear_size)) >= pp->vp_height) - { - if (verbose_p && debug_placement_p && 0) - fprintf (stderr, - "%s: placement: too big: %.2f @ %.2f vs %.2f x %.2f\n", - progname, - (g->r + g->tooth_h), gear_size, - pp->vp_width, pp->vp_height); - pp->debug_size_failures++; - return False; - } - - /* Compute this gear's velocity. - */ - if (! parent) - { - g->ratio = 0.8 + BELLRAND(0.4); /* 0.8-1.2 = 8-12rpm @ 60fps */ - g->th = frand (90) * ((random() & 1) ? 1.0 : -1.0); - } - else if (coaxial_p) - { - g->ratio = parent->ratio; /* bound gears have the same ratio */ - g->th = parent->th; - g->rpm = parent->rpm; - g->wobble = parent->wobble; - } - else - { - /* Gearing ratio is the ratio of the number of teeth to previous gear - (which is also the ratio of the circumferences.) - */ - g->ratio = (double) parent->nteeth / (double) g->nteeth; - - /* Set our initial rotation to match that of the previous gear, - multiplied by the gearing ratio. (This is finessed later, - once we know the exact position of the gear relative to its - parent.) - */ - g->th = -(parent->th * g->ratio); - - if (g->nteeth & 1) /* rotate 1/2 tooth-size if odd number of teeth */ - { - double off = (180.0 / g->nteeth); - if (g->th > 0) - g->th += off; - else - g->th -= off; - } - - /* ratios are cumulative for all gears in the train. */ - g->ratio *= parent->ratio; - } - - - /* Place the gear relative to the parent. - */ - - if (! parent) - { - gear *rg = farthest_gear (mi, False); - double right = (rg ? rg->x + rg->r + rg->tooth_h : 0); - if (right < pp->layout_left) /* place off screen */ - right = pp->layout_left; - - g->x = right + g->r + g->tooth_h + (0.01 / gear_size); - g->y = 0; - g->z = 0; - - if (debug_one_gear_p) - g->x = 0; - } - else if (coaxial_p) - { - double off = pp->plane_displacement; - - g->x = parent->x; - g->y = parent->y; - g->z = parent->z + (g->r > parent->r /* small gear on top */ - ? -off : off); - - if (parent->r > g->r) /* mark which is top and which is bottom */ - { - parent->coax_p = 1; - g->coax_p = 2; - parent->wobble = 0; /* looks bad when axle moves */ - } - else - { - parent->coax_p = 2; - g->coax_p = 1; - g->wobble = 0; - } - - g->coax_thickness = parent->thickness; - parent->coax_thickness = g->thickness; - - /* Don't let the train get too close to or far from the screen. - If we're getting too close, give up on this gear. - (But getting very far away is fine.) - */ - if (g->z >= off * 4 || - g->z <= -off * 4) - { - if (verbose_p && debug_placement_p) - fprintf (stderr, "%s: placement: bad depth: %.2f\n", - progname, g->z); - pp->debug_position_failures++; - return False; - } - } - else /* position it somewhere next to the parent. */ - { - double r_off = parent->r + g->r; - int angle; - - if ((random() % 3) != 0) - angle = (random() % 240) - 120; /* mostly -120 to +120 degrees */ - else - angle = (random() % 360) - 180; /* sometimes -180 to +180 degrees */ - - g->x = parent->x + (cos ((double) angle * (M_PI / 180)) * r_off); - g->y = parent->y + (sin ((double) angle * (M_PI / 180)) * r_off); - g->z = parent->z; - - /* If the angle we picked would have positioned this gear - more than halfway off screen, that's no good. */ - if (g->y > pp->vp_top || - g->y < pp->vp_bottom) - { - if (verbose_p && debug_placement_p) - fprintf (stderr, "%s: placement: out of bounds: %s\n", - progname, (g->y > pp->vp_top ? "top" : "bottom")); - pp->debug_position_failures++; - return False; - } - - /* avoid accidentally changing sign of "th" in the math below. */ - g->th += (g->th > 0 ? 360 : -360); - - /* Adjust the rotation of the gear so that its teeth line up with its - parent, based on the position of the gear and the current rotation - of the parent. - */ - { - double p_c = 2 * M_PI * parent->r; /* circumference of parent */ - double g_c = 2 * M_PI * g->r; /* circumference of g */ - - double p_t = p_c * (angle/360.0); /* distance travelled along - circumference of parent when - moving "angle" degrees along - parent. */ - double g_rat = p_t / g_c; /* if travelling that distance - along circumference of g, - ratio of g's circumference - travelled. */ - double g_th = 360.0 * g_rat; /* that ratio in degrees */ - - g->th += angle + g_th; - } - } - - if (debug_one_gear_p) - { - compute_rpm (mi, g); - return True; - } - - /* If the position we picked for this gear would cause it to already - be visible on the screen, give up. This can happen when the train - is growing backwards, and we don't want to see gears flash into - existence. - */ - if (g->x - g->r - g->tooth_h < pp->render_right) - { - if (verbose_p && debug_placement_p) - fprintf (stderr, "%s: placement: out of bounds: left\n", progname); - pp->debug_position_failures++; - return False; - } - - /* If the position we picked for this gear causes it to overlap - with any earlier gear in the train, give up. - */ - { - int i; - - for (i = pp->ngears-1; i >= 0; i--) - { - gear *og = pp->gears[i]; - - if (og == g) continue; - if (og == parent) continue; - if (g->z != og->z) continue; /* Ignore unless on same layer */ - - /* Collision detection without sqrt: - d = sqrt(a^2 + b^2) d^2 = a^2 + b^2 - d < r1 + r2 d^2 < (r1 + r2)^2 - */ - if (((g->x - og->x) * (g->x - og->x) + - (g->y - og->y) * (g->y - og->y)) < - ((g->r + g->tooth_h + og->r + og->tooth_h) * - (g->r + g->tooth_h + og->r + og->tooth_h))) - { - if (verbose_p && debug_placement_p) - fprintf (stderr, "%s: placement: collision with %lu\n", - progname, og->id); - pp->debug_position_failures++; - return False; - } - } - } - - compute_rpm (mi, g); - - - /* Make deeper gears be darker. - */ - { - double depth = g->z / pp->plane_displacement; - double brightness = 1 + (depth / 6); - double limit = 0.4; - if (brightness < limit) brightness = limit; - if (brightness > 1/limit) brightness = 1/limit; - g->color[0] *= brightness; - g->color[1] *= brightness; - g->color[2] *= brightness; - g->color2[0] *= brightness; - g->color2[1] *= brightness; - g->color2[2] *= brightness; - } - - /* If a single frame of animation would cause the gear to rotate by - more than 1/2 the size of a single tooth, then it won't look right: - the gear will appear to be turning at some lower harmonic of its - actual speed. - */ - { - double ratio = g->ratio * spin_speed; - double blur_limit = 180.0 / g->nteeth; - - if (ratio > blur_limit) - g->motion_blur_p = 1; - - if (!coaxial_p) - { - /* ride until the wheels fall off... */ - if (ratio > blur_limit * 0.7) g->wobble += (random() % 2); - if (ratio > blur_limit * 0.9) g->wobble += (random() % 2); - if (ratio > blur_limit * 1.1) g->wobble += (random() % 2); - if (ratio > blur_limit * 1.3) g->wobble += (random() % 2); - if (ratio > blur_limit * 1.5) g->wobble += (random() % 2); - if (ratio > blur_limit * 1.7) g->wobble += (random() % 2); - } - } - - return True; -} - -static void -free_gear (gear *g) -{ - if (g->dlist) - glDeleteLists (g->dlist, 1); - free (g); -} - - -/* Make a new gear, place it next to its parent in the scene, - with its teeth meshed and the proper velocity. Returns the gear; - or 0 if it didn't work. (Call this a bunch of times until either - it works, or you decide it's probably not going to.) - */ -static gear * -place_new_gear (ModeInfo *mi, gear *parent, Bool coaxial_p) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - int loop_count = 0; - gear *g = 0; - - while (1) - { - loop_count++; - if (loop_count >= 100) - { - if (g) - free_gear (g); - g = 0; - break; - } - - g = new_gear (mi, parent, coaxial_p); - if (!g) return 0; /* out of memory? */ - - if (place_gear (mi, g, parent, coaxial_p)) - break; - } - - if (! g) return 0; - - /* We got a gear, and it is properly positioned. - Insert it in the scene. - */ - if (pp->ngears + 2 >= pp->gears_size) - { - pp->gears_size += 100; - pp->gears = (gear **) realloc (pp->gears, - pp->gears_size * sizeof (*pp->gears)); - if (! pp->gears) - { - fprintf (stderr, "%s: out of memory (%d gears)\n", - progname, pp->gears_size); - } - } - - pp->gears[pp->ngears++] = g; - return g; -} - - -static void delete_gear (ModeInfo *mi, gear *g); - -static void -push_gear (ModeInfo *mi) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - gear *g; - gear *parent = (pp->ngears <= 0 ? 0 : pp->gears[pp->ngears-1]); - - Bool tried_coaxial_p = False; - Bool coaxial_p = False; - Bool last_ditch_coax_p = False; - int loop_count = 0; - - pp->debug_size_failures = 0; - pp->debug_position_failures = 0; - - AGAIN: - loop_count++; - if (loop_count > 100) abort(); /* we're doomed! */ - - g = 0; - - /* If the gears are turning at LUDICROUS SPEED, unhook the train to - reset things to a sane velocity. - - 10,000 RPM at 30 FPS = 5.5 rotations per frame. - 1,000 RPM at 30 FPS = 0.5 rotations per frame. - 600 RPM at 30 FPS = 3 frames per rotation. - 200 RPM at 30 FPS = 9 frames per rotation. - 100 RPM at 30 FPS = 18 frames per rotation. - 50 RPM at 30 FPS = 36 frames per rotation. - 10 RPM at 30 FPS = 3 sec per rotation. - 1 RPM at 30 FPS = 30 sec per rotation. - .5 RPM at 30 FPS = 1 min per rotation. - .1 RPM at 30 FPS = 5 min per rotation. - */ - if (parent && parent->rpm > max_rpm) - { - if (verbose_p) - { - char buf[100]; - rpm_string (parent->rpm, buf); - fprintf (stderr, "%s: ludicrous speed! %s\n\n", progname, buf); - } - parent = 0; - } - - /* If the last N gears we've placed have all been motion-blurred, then - it's a safe guess that we've wandered off into the woods and aren't - coming back. Bail on this train. - */ - if (pp->current_blur_length >= 10) - { - if (verbose_p) - fprintf (stderr, "%s: it's a blurpocalypse!\n\n", progname); - parent = 0; - } - - - - /* Sometimes, try to make a coaxial gear. - */ - if (parent && !parent->coax_p && (random() % 40) == 0) - { - tried_coaxial_p = True; - coaxial_p = True; - g = place_new_gear (mi, parent, coaxial_p); - } - - /* Try to make a regular gear. - */ - if (!g) - { - coaxial_p = False; - g = place_new_gear (mi, parent, coaxial_p); - } - - /* If we couldn't make a regular gear, then try to make a coxial gear - (unless we already tried that.) - */ - if (!g && !tried_coaxial_p && parent && !parent->coax_p) - { - tried_coaxial_p = True; - coaxial_p = True; - g = place_new_gear (mi, parent, coaxial_p); - if (g) - last_ditch_coax_p = True; - } - - /* If we couldn't do that either, then the train has hit a dead end: - start a new train. - */ - if (!g) - { - coaxial_p = False; - if (verbose_p) - fprintf (stderr, "%s: dead end!\n\n", progname); - parent = 0; - g = place_new_gear (mi, parent, coaxial_p); - } - - if (! g) - { - /* Unable to make/place any gears at all! - This can happen if we've backed ourself into a corner very near - the top-right or bottom-right corner of the growth zone. - It's time to add a gear, but there's no room to add one! - In that case, let's just wipe all the gears that are in the - growth zone and try again. - */ - int i; - - if (verbose_p && debug_placement_p) - fprintf (stderr, - "%s: placement: resetting growth zone! " - "failed: %d size, %d pos\n", - progname, - pp->debug_size_failures, pp->debug_position_failures); - for (i = pp->ngears-1; i >= 0; i--) - { - gear *g = pp->gears[i]; - if (g->x - g->r - g->tooth_h < pp->render_left) - delete_gear (mi, g); - } - goto AGAIN; - } - - if (g->coax_p) - { - if (!parent) abort(); - if (g->x != parent->x) abort(); - if (g->y != parent->y) abort(); - if (g->z == parent->z) abort(); - if (g->r == parent->r) abort(); - if (g->th != parent->th) abort(); - if (g->ratio != parent->ratio) abort(); - if (g->rpm != parent->rpm) abort(); - } - - if (verbose_p) - { - fprintf (stderr, "%s: %5lu ", progname, g->id); - - fputc ((g->motion_blur_p ? '*' : ' '), stderr); - fputc (((g->coax_p && last_ditch_coax_p) ? '2' : - g->coax_p ? '1' : ' '), - stderr); - - fprintf (stderr, " %2d%%", - (int) (g->r * 2 * 100 / pp->vp_height)); - fprintf (stderr, " %2d teeth", (int) g->nteeth); - fprintf (stderr, " %3.0f rpm;", g->rpm); - - { - char buf1[50], buf2[50], buf3[200]; - *buf1 = 0; *buf2 = 0; *buf3 = 0; - 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", pp->current_length); - fprintf (stderr, "\n"); - } - - if (g->base_p) - pp->current_length = 1; - else - pp->current_length++; - - if (g->motion_blur_p) - pp->current_blur_length++; - else - pp->current_blur_length = 0; -} - - - -/* Remove the given gear from the scene and free it. - */ -static void -delete_gear (ModeInfo *mi, gear *g) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - int i; - - for (i = 0; i < pp->ngears; i++) /* find this gear in the list */ - if (pp->gears[i] == g) break; - if (pp->gears[i] != g) abort(); - - for (; i < pp->ngears-1; i++) /* pull later entries forward */ - pp->gears[i] = pp->gears[i+1]; - pp->gears[i] = 0; - pp->ngears--; - free_gear (g); -} - - -/* Update the position of each gear in the scene. - */ -static void -scroll_gears (ModeInfo *mi) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - int i; - - for (i = 0; i < pp->ngears; i++) - pp->gears[i]->x -= (scroll_speed * 0.002); - - /* if the right edge of any gear is off screen to the left, delete it. - */ - for (i = pp->ngears-1; i >= 0; i--) - { - gear *g = pp->gears[i]; - if (g->x + g->r + g->tooth_h < pp->render_left) - delete_gear (mi, g); - } - - /* if the right edge of the last-added gear is left of the right edge - of the layout area, add another gear. - */ - i = 0; - while (1) - { - gear *g = (pp->ngears <= 0 ? 0 : pp->gears[pp->ngears-1]); - if (!g || g->x + g->r + g->tooth_h < pp->layout_right) - push_gear (mi); - else - break; - i++; - if (debug_one_gear_p) break; - } - - /* - if (i > 1 && verbose_p) - fprintf (stderr, "%s: pushed %d gears\n", progname, i); - */ -} - - -/* Update the rotation of each gear in the scene. - */ -static void -spin_gears (ModeInfo *mi) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - int i; - - for (i = 0; i < pp->ngears; i++) - { - gear *g = pp->gears[i]; - double off = (g->ratio * spin_speed); - - if (g->th > 0) - g->th += off; - else - g->th -= off; - } -} - - -/* Run the animation fast (without displaying anything) until the first - gear is just about to come on screen. This is to avoid a big delay - with a blank screen when -scroll is low. - */ -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); - if (g && g->x - g->r - g->tooth_h/2 <= pp->vp_right * 0.88) - break; - scroll_gears (mi); - } -} - - - -/* 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; - - Bool visible_p = (g->x + g->r + g->tooth_h >= pp->render_left && - g->x - g->r - g->tooth_h <= pp->render_right); - - if (!visible_p && !debug_p) - return; - - if (! g->dlist) - { - g->dlist = glGenLists (1); - if (! g->dlist) - { - /* I don't know how many display lists a GL implementation - is supposed to provide, but hopefully it's more than - "a few hundred", or we'll be in trouble... - */ - check_gl_error ("glGenLists"); - abort(); - } - - glNewList (g->dlist, GL_COMPILE); - g->polygons = draw_involute_gear (g, (wire_p && debug_p ? 2 : wire_p)); - glEndList (); - } - - glPushMatrix(); - - glTranslatef (g->x, g->y, g->z); - - if (g->motion_blur_p && !pp->button_down_p) - { - /* If we're in motion-blur mode, then draw the gear so that each - frame rotates it by exactly one half tooth-width, so that it - looks flickery around the edges. But, revert to the normal - way when the mouse button is down lest the user see overlapping - polygons. - */ - th = g->motion_blur_p * 180.0 / g->nteeth * (g->th > 0 ? 1 : -1); - g->motion_blur_p++; - } - else - th = g->th; - - glRotatef (th, 0, 0, 1); - - glPushName (g->id); - - if (! visible_p) - mi->polygon_count += draw_involute_schematic (g, wire_p); - else - { - glCallList (g->dlist); - mi->polygon_count += g->polygons; - } - - glPopName(); - glPopMatrix(); -} - - -/* Render all gears. - */ -static void -draw_gears (ModeInfo *mi) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - Bool wire_p = MI_IS_WIREFRAME(mi); - int i; - - glColor4f (1, 1, 0.8, 1); - - glInitNames(); - - for (i = 0; i < pp->ngears; i++) - draw_gear (mi, i); - - /* draw a line connecting gears that are, uh, geared. */ - if (debug_p) - { - static const GLfloat color[4] = {1.0, 0.0, 0.0, 1.0}; - GLfloat off = 0.1; - GLfloat ox=0, oy=0, oz=0; - - if (!wire_p) glDisable(GL_LIGHTING); - glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); - glColor3f (color[0], color[1], color[2]); - - for (i = 0; i < pp->ngears; i++) - { - gear *g = pp->gears[i]; - glBegin(GL_LINE_STRIP); - glVertex3f (g->x, g->y, g->z - off); - glVertex3f (g->x, g->y, g->z + off); - if (i > 0 && !g->base_p) - glVertex3f (ox, oy, oz + off); - glEnd(); - ox = g->x; - oy = g->y; - oz = g->z; - } - if (!wire_p) glEnable(GL_LIGHTING); - } -} - - -/* Mouse hit detection - */ -static void -find_mouse_gear (ModeInfo *mi) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - -# ifndef HAVE_JWZGLES - - int screen_width = MI_WIDTH (mi); - int screen_height = MI_HEIGHT (mi); - GLfloat h = (GLfloat) screen_height / (GLfloat) screen_width; - int x, y; - int hits; - - pp->mouse_gear_id = 0; - - /* Poll mouse position */ - { - Window r, c; - int rx, ry; - unsigned int m; - XQueryPointer (MI_DISPLAY (mi), MI_WINDOW (mi), - &r, &c, &rx, &ry, &x, &y, &m); - } - - if (x < 0 || y < 0 || x > screen_width || y > screen_height) - return; /* out of window */ - - /* Run OpenGL hit detector */ - { - GLint vp[4]; - GLuint selbuf[512]; - - glSelectBuffer (sizeof(selbuf), selbuf); /* set up "select" mode */ - glRenderMode (GL_SELECT); - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glGetIntegerv (GL_VIEWPORT, vp); /* save old vp */ - gluPickMatrix (x, vp[3]-y, 5, 5, vp); - gluPerspective (30.0, 1/h, 1.0, 100.0); /* must match reshape_pinion() */ - glMatrixMode (GL_MODELVIEW); - - draw_gears (mi); /* render into "select" buffer */ - - glMatrixMode (GL_PROJECTION); /* restore old vp */ - glPopMatrix (); - glMatrixMode (GL_MODELVIEW); - glFlush(); - hits = glRenderMode (GL_RENDER); /* done selecting */ - - if (hits > 0) - { - int i; - GLuint name_count = 0; - GLuint *p = (GLuint *) selbuf; - GLuint *pnames = 0; - GLuint min_z = ~0; - - for (i = 0; i < hits; i++) - { - int names = *p++; - if (*p < min_z) /* find match closest to screen */ - { - name_count = names; - min_z = *p; - pnames = p+2; - } - p += names+2; - } - - if (name_count > 0) /* take first hit */ - pp->mouse_gear_id = pnames[0]; - } - } - -#else /* HAVE_JWZGLES */ - /* #### not yet implemented */ - pp->mouse_gear_id = (pp->ngears > 1 ? pp->gears[1]->id : 0); - return; -#endif /* HAVE_JWZGLES */ - - -} - - -/* Window management, etc - */ -ENTRYPOINT void -reshape_pinion (ModeInfo *mi, int width, int height) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - GLfloat h = (GLfloat) height / (GLfloat) width; - int y = 0; - - if (width > height * 5) { /* tiny window: show middle */ - height = width * 9/16; - y = -height/2; - h = height / (GLfloat) width; - } - - glViewport (0, y, (GLint) width, (GLint) height); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective (30.0, 1/h, 1.0, 100.0); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gluLookAt( 0.0, 0.0, 30.0, - 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0); - - glClear(GL_COLOR_BUFFER_BIT); - - { - GLfloat render_width, layout_width; - pp->vp_height = 1.0; - pp->vp_width = 1/h; - - pp->vp_left = -pp->vp_width/2; - pp->vp_right = pp->vp_width/2; - pp->vp_top = pp->vp_height/2; - pp->vp_bottom = -pp->vp_height/2; - - render_width = pp->vp_width * 2; - layout_width = pp->vp_width * 0.8 * gear_size; - - pp->render_left = -render_width/2; - pp->render_right = render_width/2; - - pp->layout_left = pp->render_right; - pp->layout_right = pp->layout_left + layout_width; - } -} - - -ENTRYPOINT Bool -pinion_handle_event (ModeInfo *mi, XEvent *event) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - - if (gltrackball_event_handler (event, pp->trackball, - MI_WIDTH (mi), MI_HEIGHT (mi), - &pp->button_down_p)) - return True; - else if (event->xany.type == KeyPress) - { - KeySym keysym; - char c = 0; - XLookupString (&event->xkey, &c, 1, &keysym, 0); - if (c == ' ' && debug_one_gear_p && pp->ngears) - { - delete_gear (mi, pp->gears[0]); - return True; - } - } - - return False; -} - - -ENTRYPOINT void -init_pinion (ModeInfo *mi) -{ - pinion_configuration *pp; - - MI_INIT (mi, pps); - - pp = &pps[MI_SCREEN(mi)]; - - pp->glx_context = init_GL(mi); - - load_fonts (mi); - reshape_pinion (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); - clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */ - - pp->ngears = 0; - pp->gears_size = 0; - pp->gears = 0; - - pp->plane_displacement = gear_size * 0.1; - - pp->trackball = gltrackball_init (False); - - ffwd (mi); -} - - -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); - - if (!pp->glx_context) - return; - - glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *pp->glx_context); - - glPushMatrix(); - glRotatef(current_device_rotation(), 0, 0, 1); - - if (!wire_p) - { - GLfloat pos[4] = {-3.0, 1.0, 1.0, 0.0}; - GLfloat amb[4] = { 0.0, 0.0, 0.0, 1.0}; - GLfloat dif[4] = { 1.0, 1.0, 1.0, 1.0}; - GLfloat spc[4] = { 1.0, 1.0, 1.0, 1.0}; - - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - - glLightfv(GL_LIGHT0, GL_POSITION, pos); - glLightfv(GL_LIGHT0, GL_AMBIENT, amb); - glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); - glLightfv(GL_LIGHT0, GL_SPECULAR, spc); - } - - if (!pp->button_down_p) - { - if (!debug_one_gear_p || pp->ngears == 0) - scroll_gears (mi); - spin_gears (mi); - } - - glShadeModel(GL_SMOOTH); - - glEnable(GL_DEPTH_TEST); - glEnable(GL_NORMALIZE); - glEnable(GL_CULL_FACE); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glPushMatrix (); - { - gltrackball_rotate (pp->trackball); - mi->polygon_count = 0; - - glScalef (16, 16, 16); /* map vp_width/height to the screen */ - - if (debug_one_gear_p) /* zoom in */ - glScalef (3, 3, 3); - else if (debug_p) /* show the "visible" and "layout" areas */ - { - GLfloat ow = pp->layout_right - pp->render_left; - GLfloat rw = pp->render_right - pp->render_left; - GLfloat s = (pp->vp_width / ow) * 0.85; - glScalef (s, s, s); - glTranslatef (-(ow - rw) / 2, 0, 0); - } - else - { - GLfloat s = 1.2; - glScalef (s, s, s); /* zoom in a little more */ - glRotatef (-35, 1, 0, 0); /* tilt back */ - glRotatef ( 8, 0, 1, 0); /* tilt left */ - glTranslatef (0.02, 0.1, 0); /* pan up */ - } - - draw_gears (mi); - - if (debug_p) - { - if (!wire_p) glDisable(GL_LIGHTING); - glColor3f (0.6, 0, 0); - glBegin(GL_LINE_LOOP); - glVertex3f (pp->render_left, pp->vp_top, 0); - glVertex3f (pp->render_right, pp->vp_top, 0); - glVertex3f (pp->render_right, pp->vp_bottom, 0); - glVertex3f (pp->render_left, pp->vp_bottom, 0); - glEnd(); - glColor3f (0.4, 0, 0); - glBegin(GL_LINES); - glVertex3f (pp->vp_left, pp->vp_top, 0); - glVertex3f (pp->vp_left, pp->vp_bottom, 0); - glVertex3f (pp->vp_right, pp->vp_top, 0); - glVertex3f (pp->vp_right, pp->vp_bottom, 0); - glEnd(); - glColor3f (0, 0.4, 0); - glBegin(GL_LINE_LOOP); - glVertex3f (pp->layout_left, pp->vp_top, 0); - glVertex3f (pp->layout_right, pp->vp_top, 0); - glVertex3f (pp->layout_right, pp->vp_bottom, 0); - glVertex3f (pp->layout_left, pp->vp_bottom, 0); - glEnd(); - if (!wire_p) glEnable(GL_LIGHTING); - } - - if (pp->draw_tick++ > 10) /* only do this every N frames */ - { - pp->draw_tick = 0; - find_mouse_gear (mi); - } - } - glPopMatrix (); - - draw_label (mi); - glPopMatrix (); - - if (mi->fps_p) do_fps (mi); - glFinish(); - - glXSwapBuffers(dpy, window); -} - - -ENTRYPOINT void -free_pinion (ModeInfo *mi) -{ - pinion_configuration *pp = &pps[MI_SCREEN(mi)]; - int i; - - if (!pp->glx_context) return; - glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *pp->glx_context); - - for (i = 0; i < pp->ngears; i++) - if (pp->gears[i]) free_gear (pp->gears[i]); - if (pp->gears) free (pp->gears); - if (pp->trackball) gltrackball_free (pp->trackball); - if (pp->font1) free_texture_font (pp->font1); - if (pp->font2) free_texture_font (pp->font2); - if (pp->font3) free_texture_font (pp->font3); -} - -XSCREENSAVER_MODULE ("Pinion", pinion) - -#endif /* USE_GL */ |
