X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fgears.c;h=054bc1c0a7b972d224b02ea8dc4dda64de33e1ed;hp=1350f737b4b04e0bca2922489721c7dadc088c89;hb=6b1c86cf395f59389e4ece4ea8f4bea2c332745b;hpb=df7adbee81405e2849728a24b498ad2117784b1f diff --git a/hacks/glx/gears.c b/hacks/glx/gears.c index 1350f737..054bc1c0 100644 --- a/hacks/glx/gears.c +++ b/hacks/glx/gears.c @@ -1,516 +1,945 @@ -/* -*- Mode: C; tab-width: 4 -*- */ -/* gears --- 3D gear wheels */ - -#if !defined( lint ) && !defined( SABER ) -static const char sccsid[] = "@(#)gears.c 4.07 97/11/24 xlockmore"; - -#endif - -/*- - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, - * provided that the above copyright notice appear in all copies and that - * both that copyright notice and this permission notice appear in - * supporting documentation. +/* gears, Copyright (c) 2007-2008 Jamie Zawinski * - * This file is provided AS IS with no warranties of any kind. The author - * shall have no liability with respect to the infringement of copyrights, - * trade secrets or any patents by this file or any part thereof. In no - * event will the author be liable for any lost revenue or profits or - * other special, indirect and consequential damages. + * 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. * - * Revision History: - * 10-May-97: Compatible with xscreensaver - * 22-Mar-97: Added support for -mono mode, and monochrome X servers. - * Ed Mackey, emackey@netaxs.com - * 13-Mar-97: Memory leak fix by Tom Schmidt - * 1996: "written" by Danny Sung - * Based on 3-D gear wheels by Brian Paul which is in the public domain. + * Originally written by Brian Paul in 1996 or earlier; + * rewritten by jwz in Nov 2007. */ -/*- - * PURIFY 3.0a on SunOS4 reports an unitialized memory read on each of - * the glCallList() functions below when using MesaGL 2.1. This has - * been fixed in MesaGL 2.2 and later releases. - */ +#define DEFAULTS "*delay: 30000 \n" \ + "*count: 0 \n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ -/*- - * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock. - * otherwise caddr_t is not defined correctly - */ +# define refresh_gears 0 +# define release_gears 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) -#include - -#ifdef STANDALONE -# define PROGCLASS "Gears" -# define HACK_INIT init_gears -# define HACK_DRAW draw_gears -# define gears_opts xlockmore_opts -# define DEFAULTS "*count: 1 \n" \ - "*cycles: 2 \n" \ - "*delay: 100 \n" \ - "*wireframe: False \n" -# include "xlockmore.h" /* from the xscreensaver distribution */ -#else /* !STANDALONE */ -# include "xlock.h" /* from the xlockmore distribution */ -#endif /* !STANDALONE */ - -#ifdef USE_GL - -ModeSpecOpt gears_opts = -{0, NULL, 0, NULL, NULL}; - -#ifdef USE_MODULES -ModStruct gears_description = -{"gears", "init_gears", "draw_gears", "release_gears", - "draw_gears", "init_gears", NULL, &gears_opts, - 1000, 1, 2, 1, 4, 1.0, "", - "Shows GL's gears", 0, NULL}; +#include "xlockmore.h" +#include "involute.h" +#include "normals.h" +#include "tube.h" +#include "rotator.h" +#include "gltrackball.h" +#include -#endif +#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 { - GLfloat view_rotx, view_roty, view_rotz; - GLuint gear1, gear2, gear3; - GLfloat angle; - GLXContext *glx_context; - Window window; -#if 0 - Window win; -#endif -} gearsstruct; + GLXContext *glx_context; + rotator *rot; + trackball_state *trackball; + Bool button_down_p; + Bool planetary_p; -static gearsstruct *gears = NULL; + int ngears; + gear **gears; -/*- - * Draw a gear wheel. You'll probably want to call this function when - * building a display list since we do a lot of trig here. - * - * Input: inner_radius - radius of hole at center - * outer_radius - radius at center of teeth - * width - width of gear - * teeth - number of teeth - * tooth_depth - depth of tooth - * wire - true for wireframe mode + 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 */ -static void -gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, - GLint teeth, GLfloat tooth_depth, Bool wire) +ENTRYPOINT void +reshape_gears (ModeInfo *mi, int width, int height) { - GLint i; - GLfloat r0, r1, r2; - GLfloat angle, da; - GLfloat u, v, len; - - r0 = inner_radius; - r1 = outer_radius - tooth_depth / 2.0; - r2 = outer_radius + tooth_depth / 2.0; - - da = 2.0 * M_PI / teeth / 4.0; - - glShadeModel(GL_FLAT); - - /* This subroutine got kind of messy when I added all the checks - * for wireframe mode. A much cleaner solution that I sometimes - * use is to have a variable hold the value GL_LINE_LOOP when - * in wireframe mode, or hold the value GL_POLYGON otherwise. - * Then I just call glBegin(that_variable), give my polygon - * coordinates, and glEnd(). Pretty neat eh? Too bad I couldn't - * integrate that trick here. - * --Ed. - */ - - if (!wire) - glNormal3f(0.0, 0.0, 1.0); - - /* draw front face */ - if (!wire) - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= teeth; i++) { - if (wire) - glBegin(GL_LINES); - angle = i * 2.0 * M_PI / teeth; - glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); - if (!wire) { - glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); - } else { - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); - glVertex3f(r1 * cos(angle + 4 * da), r1 * sin(angle + 4 * da), width * 0.5); - glEnd(); - } - } - if (!wire) - glEnd(); - - /* draw front sides of teeth */ - if (!wire) - glBegin(GL_QUADS); - da = 2.0 * M_PI / teeth / 4.0; - for (i = 0; i < teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - - if (wire) - glBegin(GL_LINE_LOOP); - glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); - if (wire) - glEnd(); - } - if (!wire) - glEnd(); - - - if (!wire) - glNormal3f(0.0, 0.0, -1.0); - - /* draw back face */ - if (!wire) - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - if (wire) - glBegin(GL_LINES); - glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); - if (!wire) { - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); - } else { - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); - glVertex3f(r1 * cos(angle + 4 * da), r1 * sin(angle + 4 * da), -width * 0.5); - glEnd(); - } - } - if (!wire) - glEnd(); - - /* draw back sides of teeth */ - if (!wire) - glBegin(GL_QUADS); - da = 2.0 * M_PI / teeth / 4.0; - for (i = 0; i < teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - - if (wire) - glBegin(GL_LINE_LOOP); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); - if (wire) - glEnd(); - } - if (!wire) - glEnd(); - - - /* draw outward faces of teeth */ - if (!wire) - glBegin(GL_QUAD_STRIP); - for (i = 0; i < teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - - if (wire) - glBegin(GL_LINES); - glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); - u = r2 * cos(angle + da) - r1 * cos(angle); - v = r2 * sin(angle + da) - r1 * sin(angle); - len = sqrt(u * u + v * v); - u /= len; - v /= len; - glNormal3f(v, -u, 0.0); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); - glNormal3f(cos(angle), sin(angle), 0.0); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5); - u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); - v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); - glNormal3f(v, -u, 0.0); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); - glNormal3f(cos(angle), sin(angle), 0.0); - if (wire) - glEnd(); - } - - if (!wire) { - glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5); - glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5); - glEnd(); - } - if (!wire) - glShadeModel(GL_SMOOTH); - - /* draw inside radius cylinder */ - if (!wire) - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= teeth; i++) { - angle = i * 2.0 * M_PI / teeth; - if (wire) - glBegin(GL_LINES); - glNormal3f(-cos(angle), -sin(angle), 0.0); - glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); - if (wire) { - glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); - glVertex3f(r0 * cos(angle + 4 * da), r0 * sin(angle + 4 * da), -width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); - glVertex3f(r0 * cos(angle + 4 * da), r0 * sin(angle + 4 * da), width * 0.5); - glEnd(); - } - } - if (!wire) - glEnd(); + GLfloat h = (GLfloat) height / (GLfloat) width; + + glViewport (0, 0, (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); } -static void -draw(ModeInfo * mi) + +ENTRYPOINT Bool +gears_handle_event (ModeInfo *mi, XEvent *event) { - gearsstruct *gp = &gears[MI_SCREEN(mi)]; - int wire = MI_IS_WIREFRAME(mi); - - if (!wire) { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } else { - glClear(GL_COLOR_BUFFER_BIT); - } - - glPushMatrix(); - glRotatef(gp->view_rotx, 1.0, 0.0, 0.0); - glRotatef(gp->view_roty, 0.0, 1.0, 0.0); - glRotatef(gp->view_rotz, 0.0, 0.0, 1.0); - - glPushMatrix(); - glTranslatef(-3.0, -2.0, 0.0); - glRotatef(gp->angle, 0.0, 0.0, 1.0); -/* PURIFY 4.0.1 reports an unitialized memory read on the next line when using - * MesaGL 2.2 and -mono. This has been fixed in MesaGL 2.3 and later. */ - glCallList(gp->gear1); - glPopMatrix(); - - glPushMatrix(); - glTranslatef(3.1, -2.0, 0.0); - glRotatef(-2.0 * gp->angle - 9.0, 0.0, 0.0, 1.0); - glCallList(gp->gear2); - glPopMatrix(); - - glPushMatrix(); - glTranslatef(-3.1, 4.2, 0.0); - glRotatef(-2.0 * gp->angle - 25.0, 0.0, 0.0, 1.0); - glCallList(gp->gear3); - glPopMatrix(); - - glPopMatrix(); + gears_configuration *bp = &bps[MI_SCREEN(mi)]; + + if (event->xany.type == ButtonPress && + event->xbutton.button == Button1) + { + bp->button_down_p = True; + gltrackball_start (bp->trackball, + event->xbutton.x, event->xbutton.y, + MI_WIDTH (mi), MI_HEIGHT (mi)); + return True; + } + else if (event->xany.type == ButtonRelease && + event->xbutton.button == Button1) + { + bp->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 (bp->trackball, event->xbutton.button, 10, + !!event->xbutton.state); + return True; + } + else if (event->xany.type == MotionNotify && + bp->button_down_p) + { + gltrackball_track (bp->trackball, + event->xmotion.x, event->xmotion.y, + MI_WIDTH (mi), MI_HEIGHT (mi)); + return True; + } + + return False; } -/* new window size or exposure */ static void -reshape(int width, int height) +free_gear (gear *g) { - GLfloat h = (GLfloat) height / (GLfloat) width; - - glViewport(0, 0, (GLint) width, (GLint) height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glTranslatef(0.0, 0.0, -40.0); + if (g->dlist) + glDeleteLists (g->dlist, 1); + free (g); +} - /* The depth buffer will be cleared, if needed, before the - * next frame. Right now we just want to black the screen. - */ - glClear(GL_COLOR_BUFFER_BIT); +/* 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 g->size = INVOLUTE_LARGE; + } + + g->base_p = !parent; + + return g; } -static void -pinit(ModeInfo * mi) +/* 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) { - gearsstruct *gp = &gears[MI_SCREEN(mi)]; - static GLfloat pos[4] = - {5.0, 5.0, 10.0, 1.0}; - static GLfloat red[4] = - {0.8, 0.1, 0.0, 1.0}; - static GLfloat green[4] = - {0.0, 0.8, 0.2, 1.0}; - static GLfloat blue[4] = - {0.2, 0.2, 1.0, 1.0}; - static GLfloat gray[4] = - {0.5, 0.5, 0.5, 1.0}; - static GLfloat white[4] = - {1.0, 1.0, 1.0, 1.0}; - int wire = MI_IS_WIREFRAME(mi); - int mono = MI_IS_MONO(mi); - - if (!wire) { - glLightfv(GL_LIGHT0, GL_POSITION, pos); - glEnable(GL_CULL_FACE); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_DEPTH_TEST); - } -#if 0 -/*- - * Messes up on multiscreen Pseudocolor:0 StaticGray(monochrome):1 - * 2nd time mode is run it is Grayscale on PseudoColor. - * The code below forces monochrome on TrueColor. + 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] */ - if (MI_IS_MONO(mi)) { - red[0] = red[1] = red[2] = 1.0; - green[0] = green[1] = green[2] = 1.0; - blue[0] = blue[1] = blue[2] = 1.0; - } +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 - /* make the gears */ - gp->gear1 = glGenLists(1); - glNewList(gp->gear1, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(red); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); - } - gear(1.0, 4.0, 1.0, 20, 0.7, wire); - glEndList(); - - gp->gear2 = glGenLists(1); - glNewList(gp->gear2, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(green); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); - } - gear(0.5, 2.0, 2.0, 10, 0.7, wire); - glEndList(); - - gp->gear3 = glGenLists(1); - glNewList(gp->gear3, GL_COMPILE); - if (wire) { - if (mono) - glColor4fv(white); - else - glColor4fv(blue); - } else { - if (mono) - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray); - else - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); - } - gear(1.3, 2.0, 0.5, 10, 0.7, wire); - glEndList(); - if (!wire) - glEnable(GL_NORMALIZE); + /* 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; } -void -init_gears(ModeInfo * mi) + +static int +ctube (GLfloat diameter, GLfloat width, Bool wire) { - int screen = MI_SCREEN(mi); - - /*Colormap cmap; */ - /* Boolean rgba, doublebuffer, cmap_installed; */ - gearsstruct *gp; - - if (gears == NULL) { - if ((gears = (gearsstruct *) calloc(MI_NUM_SCREENS(mi), - sizeof (gearsstruct))) == NULL) - return; - } - gp = &gears[screen]; - - gp->window = MI_WINDOW(mi); - gp->view_rotx = NRAND(360); - gp->view_roty = NRAND(360); - gp->view_rotz = NRAND(360); - gp->angle = NRAND(360); - - if ((gp->glx_context = init_GL(mi)) != NULL) { - reshape(MI_WIDTH(mi), MI_HEIGHT(mi)); - pinit(mi); - } else { - MI_CLEARWINDOW(mi); - } + tube (0, 0, width/2, + 0, 0, -width/2, + diameter, 0, + 32, True, True, wire); + return 0; /* #### */ } -void -draw_gears(ModeInfo * mi) +static void +armature (ModeInfo *mi) { - gearsstruct *gp = &gears[MI_SCREEN(mi)]; - Display *display = MI_DISPLAY(mi); - Window window = MI_WINDOW(mi); - int angle_incr = MI_CYCLES(mi) ? MI_CYCLES(mi) : 2; - int rot_incr = MI_COUNT(mi) ? MI_COUNT(mi) : 1; + 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 (); +} - if (!gp->glx_context) - return; - glDrawBuffer(GL_BACK); +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; +} - glXMakeCurrent(display, window, *(gp->glx_context)); - draw(mi); - /* let's do something so we don't get bored */ - gp->angle = (int) (gp->angle + angle_incr) % 360; - gp->view_rotx = (int) (gp->view_rotx + rot_incr) % 360; - gp->view_roty = (int) (gp->view_roty + rot_incr) % 360; - gp->view_rotz = (int) (gp->view_rotz + rot_incr) % 360; - glFinish(); - glXSwapBuffers(display, window); -} -void -release_gears(ModeInfo * mi) +ENTRYPOINT void +init_gears (ModeInfo *mi) { - if (gears != NULL) { - int screen; - - for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) { - gearsstruct *gp = &gears[screen]; - - if (gp->glx_context) { - /* Display lists MUST be freed while their glXContext is current. */ - glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context)); - - if (glIsList(gp->gear1)) - glDeleteLists(gp->gear1, 1); - if (glIsList(gp->gear2)) - glDeleteLists(gp->gear2, 1); - if (glIsList(gp->gear3)) - glDeleteLists(gp->gear3, 1); - - } - } - (void) free((void *) gears); - gears = NULL; - } - FreeAllGL(mi); + gears_configuration *bp; + int wire = MI_IS_WIREFRAME(mi); + int i; + + if (!bps) { + bps = (gears_configuration *) + calloc (MI_NUM_SCREENS(mi), sizeof (gears_configuration)); + if (!bps) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(1); + } + + bp = &bps[MI_SCREEN(mi)]; + } + + 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); + } + + { + 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 (); + } + + if (!(random() % 8)) + { + planetary_gears (mi); + } + else + { + gear *g = 0; + int total_gears = MI_COUNT (mi); + int i; + if (total_gears <= 0) + total_gears = 3 + abs (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; -#endif + 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); +} + +XSCREENSAVER_MODULE ("Gears", gears) + +#endif /* USE_GL */