diff options
author | Simon Rettberg | 2018-10-16 10:08:48 +0200 |
---|---|---|
committer | Simon Rettberg | 2018-10-16 10:08:48 +0200 |
commit | d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 (patch) | |
tree | cbddf8e50f35a9c6e878a5bfe3c6d625d99e12ba /hacks/glx/gears.c | |
download | xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.gz xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.xz xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.zip |
Original 5.40
Diffstat (limited to 'hacks/glx/gears.c')
-rw-r--r-- | hacks/glx/gears.c | 938 |
1 files changed, 938 insertions, 0 deletions
diff --git a/hacks/glx/gears.c b/hacks/glx/gears.c new file mode 100644 index 0000000..3976f2f --- /dev/null +++ b/hacks/glx/gears.c @@ -0,0 +1,938 @@ +/* gears, Copyright (c) 2007-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. + * + * Originally written by Brian Paul in 1996 or earlier; + * rewritten by jwz in Nov 2007. + */ + +#define DEFAULTS "*delay: 30000 \n" \ + "*count: 0 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*suppressRotationAnimation: True\n" \ + +# define free_gears 0 +# define release_gears 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#include "xlockmore.h" +#include "involute.h" +#include "normals.h" +#include "tube.h" +#include "rotator.h" +#include "gltrackball.h" +#include <ctype.h> + +#ifdef USE_GL /* whole file */ + +#undef BELLRAND +#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) + +#define DEF_SPIN "True" +#define DEF_WANDER "True" +#define DEF_SPEED "1.0" + +typedef struct { + GLXContext *glx_context; + rotator *rot; + trackball_state *trackball; + Bool button_down_p; + Bool planetary_p; + + int ngears; + gear **gears; + + GLuint armature_dlist; + int armature_polygons; + + struct { GLfloat x1, y1, x2, y2; } bbox; + +} gears_configuration; + +static gears_configuration *bps = NULL; + +static Bool do_spin; +static GLfloat speed; +static Bool do_wander; + +static XrmOptionDescRec opts[] = { + { "-spin", ".spin", XrmoptionNoArg, "True" }, + { "+spin", ".spin", XrmoptionNoArg, "False" }, + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-wander", ".wander", XrmoptionNoArg, "True" }, + { "+wander", ".wander", XrmoptionNoArg, "False" }, +}; + +static argtype vars[] = { + {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool}, + {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool}, + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, +}; + +ENTRYPOINT ModeSpecOpt gears_opts = {countof(opts), opts, countof(vars), vars, NULL}; + + +/* Window management, etc + */ +ENTRYPOINT void +reshape_gears (ModeInfo *mi, int width, int height) +{ + 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); + +# ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */ + { + int o = (int) current_device_rotation(); + if (o != 0 && o != 180 && o != -180) + glScalef (1/h, 1/h, 1/h); + } +# endif + + glClear(GL_COLOR_BUFFER_BIT); +} + + +static void +free_gear (gear *g) +{ + if (g->dlist) + glDeleteLists (g->dlist, 1); + free (g); +} + + +/* 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. + [Mostly lifted from pinion.c] + */ +static gear * +new_gear (ModeInfo *mi, gear *parent) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + gear *g = (gear *) calloc (1, sizeof (*g)); + static unsigned long id = 0; /* only used in debugging output */ + + if (!g) return 0; + g->id = ++id; + + /* Pick the size of the teeth. + */ + if (parent) /* adjascent gears need matching teeth */ + { + g->tooth_w = parent->tooth_w; + g->tooth_h = parent->tooth_h; + g->tooth_slope = -parent->tooth_slope; + } + else /* gears that begin trains get any size they want */ + { + g->tooth_w = 0.007 * (1.0 + BELLRAND(4.0)); + g->tooth_h = 0.005 * (1.0 + BELLRAND(8.0)); +/* + g->tooth_slope = ((random() % 8) + ? 0 + : 0.5 + BELLRAND(1)); + */ + } + + /* Pick the number of teeth, and thus, the radius. + */ + { + double c; + + if (!parent || bp->ngears > 4) + g->nteeth = 5 + BELLRAND (20); + else + g->nteeth = parent->nteeth * (0.5 + BELLRAND(2)); + + c = g->nteeth * g->tooth_w * 2; /* circumference = teeth + gaps */ + g->r = c / (M_PI * 2); /* c = 2 pi r */ + } + + g->thickness = g->tooth_w + frand (g->r); + g->thickness2 = g->thickness * 0.7; + g->thickness3 = g->thickness; + + /* 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; + } + } + + /* 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.) + [Mostly lifted from pinion.c] + */ +static Bool +place_gear (ModeInfo *mi, gear *g, gear *parent) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + + /* Compute this gear's velocity. + */ + if (! parent) + { + g->ratio = 0.8 + BELLRAND(0.4); /* 0.8-1.2 = 8-12rpm @ 60fps */ + g->th = 1; /* not 0 */ + } + 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; + } + + + if (parent) /* Place the gear next to the parent. */ + { + double r_off = parent->r + g->r; + int angle; + + angle = (random() % 360) - 180; /* -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; + + /* 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 the position we picked for this gear causes it to overlap + with any earlier gear in the train, give up. + */ + { + int i; + + for (i = bp->ngears-1; i >= 0; i--) + { + gear *og = bp->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))) + return False; + } + } + + return True; +} + + +/* 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.) + [Mostly lifted from pinion.c] + */ +static gear * +place_new_gear (ModeInfo *mi, gear *parent) +{ + gears_configuration *bp = &bps[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); + if (!g) return 0; /* out of memory? */ + + if (place_gear (mi, g, parent)) + break; + } + + if (! g) return 0; + + /* We got a gear, and it is properly positioned. + Insert it in the scene. + */ + bp->gears[bp->ngears++] = g; + return g; +} + + +static int +arm (GLfloat length, + GLfloat width1, GLfloat height1, + GLfloat width2, GLfloat height2, + Bool wire) +{ + int polys = 0; + glShadeModel(GL_FLAT); + +#if 0 /* don't need these - they're embedded in other objects */ + /* draw end 1 */ + glFrontFace(GL_CW); + glNormal3f(-1, 0, 0); + glBegin(wire ? GL_LINE_LOOP : GL_QUADS); + glVertex3f(-length/2, -width1/2, -height1/2); + glVertex3f(-length/2, width1/2, -height1/2); + glVertex3f(-length/2, width1/2, height1/2); + glVertex3f(-length/2, -width1/2, height1/2); + polys++; + glEnd(); + + /* draw end 2 */ + glFrontFace(GL_CCW); + glNormal3f(1, 0, 0); + glBegin(wire ? GL_LINE_LOOP : GL_QUADS); + glVertex3f(length/2, -width2/2, -height2/2); + glVertex3f(length/2, width2/2, -height2/2); + glVertex3f(length/2, width2/2, height2/2); + glVertex3f(length/2, -width2/2, height2/2); + polys++; + glEnd(); +#endif + + /* draw top */ + glFrontFace(GL_CCW); + glNormal3f(0, 0, -1); + glBegin(wire ? GL_LINE_LOOP : GL_QUADS); + glVertex3f(-length/2, -width1/2, -height1/2); + glVertex3f(-length/2, width1/2, -height1/2); + glVertex3f( length/2, width2/2, -height2/2); + glVertex3f( length/2, -width2/2, -height2/2); + polys++; + glEnd(); + + /* draw bottom */ + glFrontFace(GL_CW); + glNormal3f(0, 0, 1); + glBegin(wire ? GL_LINE_LOOP : GL_QUADS); + glVertex3f(-length/2, -width1/2, height1/2); + glVertex3f(-length/2, width1/2, height1/2); + glVertex3f( length/2, width2/2, height2/2); + glVertex3f( length/2, -width2/2, height2/2); + polys++; + glEnd(); + + /* draw left */ + glFrontFace(GL_CW); + glNormal3f(0, -1, 0); + glBegin(wire ? GL_LINE_LOOP : GL_QUADS); + glVertex3f(-length/2, -width1/2, -height1/2); + glVertex3f(-length/2, -width1/2, height1/2); + glVertex3f( length/2, -width2/2, height2/2); + glVertex3f( length/2, -width2/2, -height2/2); + polys++; + glEnd(); + + /* draw right */ + glFrontFace(GL_CCW); + glNormal3f(0, 1, 0); + glBegin(wire ? GL_LINE_LOOP : GL_QUADS); + glVertex3f(-length/2, width1/2, -height1/2); + glVertex3f(-length/2, width1/2, height1/2); + glVertex3f( length/2, width2/2, height2/2); + glVertex3f( length/2, width2/2, -height2/2); + polys++; + glEnd(); + + glFrontFace(GL_CCW); + + return polys; +} + + +static int +ctube (GLfloat diameter, GLfloat width, Bool wire) +{ + tube (0, 0, width/2, + 0, 0, -width/2, + diameter, 0, + 32, True, True, wire); + return 0; /* #### */ +} + +static void +armature (ModeInfo *mi) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + + static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0}; + GLfloat shiny = 128.0; + GLfloat color[4]; + + color[0] = 0.5 + frand(0.5); + color[1] = 0.5 + frand(0.5); + color[2] = 0.5 + frand(0.5); + color[3] = 1.0; + + bp->armature_polygons = 0; + + bp->armature_dlist = glGenLists (1); + if (! bp->armature_dlist) + { + check_gl_error ("glGenLists"); + abort(); + } + + glNewList (bp->armature_dlist, GL_COMPILE); + + glMaterialfv (GL_FRONT, GL_SPECULAR, spec); + glMateriali (GL_FRONT, GL_SHININESS, shiny); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); + glColor3f (color[0], color[1], color[2]); + + glPushMatrix(); + + { + GLfloat s = bp->gears[0]->r * 2.7; + s = s/5.6; + glScalef (s, s, s); + } + + glTranslatef (0, 0, 1.4 + bp->gears[0]->thickness); + glRotatef (30, 0, 0, 1); + + bp->armature_polygons += ctube (0.5, 10, wire); /* center axle */ + + glPushMatrix(); + glTranslatef(0.0, 4.2, -1); + bp->armature_polygons += ctube (0.5, 3, wire); /* axle 1 */ + glTranslatef(0, 0, 1.8); + bp->armature_polygons += ctube (0.7, 0.7, wire); + glPopMatrix(); + + glPushMatrix(); + glRotatef(120, 0.0, 0.0, 1.0); + glTranslatef(0.0, 4.2, -1); + bp->armature_polygons += ctube (0.5, 3, wire); /* axle 2 */ + glTranslatef(0, 0, 1.8); + bp->armature_polygons += ctube (0.7, 0.7, wire); + glPopMatrix(); + + glPushMatrix(); + glRotatef(240, 0.0, 0.0, 1.0); + glTranslatef(0.0, 4.2, -1); + bp->armature_polygons += ctube (0.5, 3, wire); /* axle 3 */ + glTranslatef(0, 0, 1.8); + bp->armature_polygons += ctube (0.7, 0.7, wire); + glPopMatrix(); + + glTranslatef(0, 0, 1.5); /* center disk */ + bp->armature_polygons += ctube (1.5, 2, wire); + + glPushMatrix(); + glRotatef(270, 0, 0, 1); + glRotatef(-10, 0, 1, 0); + glTranslatef(-2.2, 0, 0); + bp->armature_polygons += arm (4.0, 1.0, 0.5, + 2.0, 1.0, wire); /* arm 1 */ + glPopMatrix(); + + glPushMatrix(); + glRotatef(30, 0, 0, 1); + glRotatef(-10, 0, 1, 0); + glTranslatef(-2.2, 0, 0); + bp->armature_polygons += arm (4.0, 1.0, 0.5, + 2.0, 1.0, wire); /* arm 2 */ + glPopMatrix(); + + glPushMatrix(); + glRotatef(150, 0, 0, 1); + glRotatef(-10, 0, 1, 0); + glTranslatef(-2.2, 0, 0); + bp->armature_polygons += arm (4.0, 1.0, 0.5, + 2.0, 1.0, wire); /* arm 3 */ + glPopMatrix(); + + glPopMatrix(); + + glEndList (); +} + + +static void +planetary_gears (ModeInfo *mi) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + gear *g0, *g1, *g2, *g3, *g4; + GLfloat distance = 2.02; + + bp->planetary_p = True; + + g0 = new_gear (mi, 0); + g1 = new_gear (mi, 0); + g2 = new_gear (mi, 0); + g3 = new_gear (mi, 0); + g4 = new_gear (mi, 0); + + if (! place_gear (mi, g0, 0)) abort(); + if (! place_gear (mi, g1, 0)) abort(); + if (! place_gear (mi, g2, 0)) abort(); + if (! place_gear (mi, g3, 0)) abort(); + if (! place_gear (mi, g4, 0)) abort(); + + g0->nteeth = 12 + (3 * (random() % 10)); /* must be multiple of 3 */ + g0->tooth_w = g0->r / g0->nteeth; + g0->tooth_h = g0->tooth_w * 2.8; + +# define COPY(F) g4->F = g3->F = g2->F = g1->F = g0->F + COPY(r); + COPY(th); + COPY(nteeth); + COPY(tooth_w); + COPY(tooth_h); + COPY(tooth_slope); + COPY(inner_r); + COPY(inner_r2); + COPY(inner_r3); + COPY(thickness); + COPY(thickness2); + COPY(thickness3); + COPY(ratio); + COPY(size); +# undef COPY + + g1->x = cos (M_PI * 2 / 3) * g1->r * distance; + g1->y = sin (M_PI * 2 / 3) * g1->r * distance; + + g2->x = cos (M_PI * 4 / 3) * g2->r * distance; + g2->y = sin (M_PI * 4 / 3) * g2->r * distance; + + g3->x = cos (M_PI * 6 / 3) * g3->r * distance; + g3->y = sin (M_PI * 6 / 3) * g3->r * distance; + + g4->x = 0; + g4->y = 0; + g4->th = -g3->th; + + /* rotate central gear 1/2 tooth-size if odd number of teeth */ + if (g4->nteeth & 1) + g4->th -= (180.0 / g4->nteeth); + + g0->inverted_p = True; + g0->x = 0; + g0->y = 0; + g0->nteeth = g1->nteeth * 3; + g0->r = g1->r * 3.05; + g0->inner_r = g0->r * 0.8; + g0->inner_r2 = 0; + g0->inner_r3 = 0; + g0->th = g1->th + (180 / g0->nteeth); + g0->ratio = g1->ratio / 3; + + g0->tooth_slope = 0; + g0->nubs = 3; + g0->spokes = 0; + g0->size = INVOLUTE_LARGE; + + bp->gears = (gear **) calloc (6, sizeof(**bp->gears)); + bp->ngears = 0; + + bp->gears[bp->ngears++] = g1; + bp->gears[bp->ngears++] = g2; + bp->gears[bp->ngears++] = g3; + bp->gears[bp->ngears++] = g4; + bp->gears[bp->ngears++] = g0; +} + + + + +ENTRYPOINT void +init_gears (ModeInfo *mi) +{ + gears_configuration *bp; + int wire = MI_IS_WIREFRAME(mi); + int i; + + MI_INIT (mi, bps); + + bp = &bps[MI_SCREEN(mi)]; + + bp->glx_context = init_GL(mi); + + reshape_gears (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + + if (!wire) + { + GLfloat pos[4] = {1.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] = {0.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 (! bp->rot) + { + double spin_speed = 0.5; + double wander_speed = 0.01; + double spin_accel = 0.25; + + bp->rot = make_rotator (do_spin ? spin_speed : 0, + do_spin ? spin_speed : 0, + do_spin ? spin_speed : 0, + spin_accel, + do_wander ? wander_speed : 0, + True + ); + bp->trackball = gltrackball_init (True); + } + + if (bp->gears) + { + for (i = 0; i < bp->ngears; i++) + free_gear (bp->gears[i]); + free (bp->gears); + bp->gears = 0; + bp->ngears = 0; + } + + if (!(random() % 8)) + { + planetary_gears (mi); + } + else + { + gear *g = 0; + int total_gears = MI_COUNT (mi); + + bp->planetary_p = False; + + if (total_gears <= 0) + total_gears = 3 + fabs (BELLRAND (8) - 4); /* 3 - 7, mostly 3. */ + bp->gears = (gear **) calloc (total_gears+2, sizeof(**bp->gears)); + bp->ngears = 0; + + for (i = 0; i < total_gears; i++) + g = place_new_gear (mi, g); + } + + + /* Center gears in scene. */ + { + GLfloat minx=99999, miny=99999, maxx=-99999, maxy=-99999; + int i; + for (i = 0; i < bp->ngears; i++) + { + gear *g = bp->gears[i]; + if (g->x - g->r < minx) minx = g->x - g->r; + if (g->x + g->r > maxx) maxx = g->x + g->r; + if (g->y - g->r < miny) miny = g->y - g->r; + if (g->y + g->r > maxy) maxy = g->y + g->r; + } + bp->bbox.x1 = minx; + bp->bbox.y1 = miny; + bp->bbox.x2 = maxx; + bp->bbox.y2 = maxy; + } + + /* Now render each gear into its display list. + */ + for (i = 0; i < bp->ngears; i++) + { + gear *g = bp->gears[i]; + g->dlist = glGenLists (1); + if (! g->dlist) + { + check_gl_error ("glGenLists"); + abort(); + } + + glNewList (g->dlist, GL_COMPILE); + g->polygons += draw_involute_gear (g, wire); + glEndList (); + } + if (bp->planetary_p) + armature (mi); +} + + +ENTRYPOINT void +draw_gears (ModeInfo *mi) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + int i; + + if (!bp->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context)); + + glShadeModel(GL_SMOOTH); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_NORMALIZE); + glEnable(GL_CULL_FACE); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix (); + + { + double x, y, z; + get_position (bp->rot, &x, &y, &z, !bp->button_down_p); + glTranslatef ((x - 0.5) * 4, + (y - 0.5) * 4, + (z - 0.5) * 7); + + gltrackball_rotate (bp->trackball); + + get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p); + + /* add a little rotation for -no-spin mode */ + x -= 0.14; + y -= 0.06; + + glRotatef (x * 360, 1.0, 0.0, 0.0); + glRotatef (y * 360, 0.0, 1.0, 0.0); + glRotatef (z * 360, 0.0, 0.0, 1.0); + } + + /* Center the scene's bounding box in the window, + and scale it to fit. + */ + { + GLfloat w = bp->bbox.x2 - bp->bbox.x1; + GLfloat h = bp->bbox.y2 - bp->bbox.y1; + GLfloat s = 10.0 / (w > h ? w : h); + glScalef (s, s, s); + glTranslatef (-(bp->bbox.x1 + w/2), + -(bp->bbox.y1 + h/2), + 0); + } + + mi->polygon_count = 0; + + for (i = 0; i < bp->ngears; i++) + { + gear *g = bp->gears[i]; + + glPushMatrix(); + + glTranslatef (g->x, g->y, g->z); + glRotatef (g->th, 0, 0, 1); + + glCallList (g->dlist); + mi->polygon_count += g->polygons; + + glPopMatrix (); + } + + if (bp->planetary_p) + { + glCallList (bp->armature_dlist); + mi->polygon_count += bp->armature_polygons; + } + + glPopMatrix (); + + /* spin gears */ + if (!bp->button_down_p) + for (i = 0; i < bp->ngears; i++) + { + gear *g = bp->gears[i]; + double off = g->ratio * 5 * speed; + if (g->th > 0) + g->th += off; + else + g->th -= off; + } + + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); +} + +ENTRYPOINT Bool +gears_handle_event (ModeInfo *mi, XEvent *event) +{ + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + + if (gltrackball_event_handler (event, bp->trackball, + MI_WIDTH (mi), MI_HEIGHT (mi), + &bp->button_down_p)) + return True; + else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event)) + { + init_gears (mi); + return True; + } + + return False; +} + +XSCREENSAVER_MODULE ("Gears", gears) + +#endif /* USE_GL */ |