/* moebiusgears, Copyright (c) 2007-2014 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * 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: 30000 \n" \ "*count: 17 \n" \ "*showFPS: False \n" \ "*wireframe: False \n" \ "*suppressRotationAnimation: True\n" \ # define release_mgears 0 #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) #include "xlockmore.h" #include "involute.h" #include "normals.h" #include "rotator.h" #include "gltrackball.h" #include #ifdef USE_GL /* whole file */ #define DEF_SPIN "True" #define DEF_WANDER "True" #define DEF_ROLL "True" #define DEF_SPEED "1.0" #define DEF_TEETH "15" typedef struct { gear g; GLfloat pos_th; /* position on ring of gear system */ GLfloat pos_thz; /* rotation out of plane of gear system */ } mogear; typedef struct { GLXContext *glx_context; rotator *rot; trackball_state *trackball; Bool button_down_p; int ngears; mogear *gears; GLfloat ring_r; /* radius of gear system */ GLfloat roll_th; } mgears_configuration; static mgears_configuration *bps = NULL; static Bool do_spin; static GLfloat speed; static Bool do_wander; static Bool do_roll; static int teeth_arg; static XrmOptionDescRec opts[] = { { "-spin", ".spin", XrmoptionNoArg, "True" }, { "+spin", ".spin", XrmoptionNoArg, "False" }, { "-speed", ".speed", XrmoptionSepArg, 0 }, { "-wander", ".wander", XrmoptionNoArg, "True" }, { "+wander", ".wander", XrmoptionNoArg, "False" }, { "-roll", ".roll", XrmoptionNoArg, "True" }, { "+roll", ".roll", XrmoptionNoArg, "False" }, { "-teeth", ".teeth", XrmoptionSepArg, 0 }, }; static argtype vars[] = { {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool}, {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool}, {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool}, {&speed, "speed", "Speed", DEF_SPEED, t_Float}, {&teeth_arg, "teeth", "Teeth", DEF_TEETH, t_Int}, }; ENTRYPOINT ModeSpecOpt mgears_opts = {countof(opts), opts, countof(vars), vars, NULL}; /* Window management, etc */ ENTRYPOINT void reshape_mgears (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 reset_mgears (ModeInfo *mi) { mgears_configuration *bp = &bps[MI_SCREEN(mi)]; int wire = MI_IS_WIREFRAME(mi); int total_gears = MI_COUNT(mi); double gears_per_turn; double gear_r, tw, th, thick, slope; int i, nubs, size; if (! (total_gears & 1)) total_gears++; /* must be odd or gears intersect */ /* Number of teeth must be odd if number of gears is odd, or teeth don't mesh when the loop closes. And since number of gears must be odd... */ if (! (teeth_arg & 1)) teeth_arg++; if (teeth_arg < 7) teeth_arg = 7; if (total_gears < 13) /* gear mesh angle is too steep with less */ total_gears = 13; thick = 0.2; nubs = (random() & 3) ? 0 : (random() % teeth_arg) / 2; slope = 0; /* Sloping gears are incompatible with "-roll" ... */ /* slope= -M_PI * 2 / total_gears; */ gears_per_turn = total_gears / 2.0; bp->ring_r = 3; gear_r = M_PI * bp->ring_r / gears_per_turn; tw = 0; th = gear_r * 2.5 / teeth_arg; /* If the gears are small, use a lower density mesh. */ size = (gear_r > 0.60 ? INVOLUTE_HUGE : gear_r > 0.32 ? INVOLUTE_LARGE : gear_r > 0.13 ? INVOLUTE_MEDIUM : INVOLUTE_SMALL); /* If there are lots of teeth, use a lower density mesh. */ if (teeth_arg > 77) size = INVOLUTE_SMALL; if (teeth_arg > 45 && size >= INVOLUTE_HUGE) size = INVOLUTE_MEDIUM; if (bp->gears) { for (i = 0; i < bp->ngears; i++) glDeleteLists (bp->gears[i].g.dlist, 1); free (bp->gears); bp->gears = 0; } bp->ngears = total_gears; bp->gears = (mogear *) calloc (bp->ngears, sizeof(*bp->gears)); for (i = 0; i < bp->ngears; i++) { mogear *mg = &bp->gears[i]; gear *g = &mg->g; g->r = gear_r; g->size = size; g->nteeth = teeth_arg; g->tooth_w = tw; g->tooth_h = th; g->tooth_slope = slope; g->thickness = g->r * thick; g->thickness2 = g->thickness * 0.1; g->thickness3 = g->thickness; g->inner_r = g->r * 0.80; g->inner_r2 = g->r * 0.60; g->inner_r3 = g->r * 0.55; g->nubs = nubs; mg->pos_th = (M_PI * 2 / gears_per_turn) * i; mg->pos_thz = (M_PI / 2 / gears_per_turn) * i; g->th = ((i & 1) ? (M_PI * 2 / g->nteeth) : 0); /* Colorize */ g->color[0] = 0.7 + frand(0.3); g->color[1] = 0.7 + frand(0.3); g->color[2] = 0.7 + frand(0.3); 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]; /* Now render the gear into its display list. */ 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 (); } } ENTRYPOINT void init_mgears (ModeInfo *mi) { mgears_configuration *bp; int wire = MI_IS_WIREFRAME(mi); MI_INIT (mi, bps); bp = &bps[MI_SCREEN(mi)]; bp->glx_context = init_GL(mi); reshape_mgears (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 = 2.0; 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, False /* don't randomize */ ); bp->trackball = gltrackball_init (True); } reset_mgears (mi); } ENTRYPOINT Bool mgears_handle_event (ModeInfo *mi, XEvent *event) { mgears_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 (event->xany.type == KeyPress) { KeySym keysym; char c = 0; XLookupString (&event->xkey, &c, 1, &keysym, 0); if (c == '+' || c == '=' || keysym == XK_Up || keysym == XK_Right || keysym == XK_Next) { MI_COUNT(mi) += 2; reset_mgears (mi); return True; } else if (c == '-' || c == '_' || keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior) { if (MI_COUNT(mi) <= 13) return False; MI_COUNT(mi) -= 2; reset_mgears (mi); return True; } else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event)) goto DEF; } else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event)) { DEF: MI_COUNT(mi) = 13 + (2 * (random() % 10)); reset_mgears (mi); return True; } return False; } ENTRYPOINT void draw_mgears (ModeInfo *mi) { mgears_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 (); glScalef(1.1, 1.1, 1.1); { 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); } mi->polygon_count = 0; glScalef (1.5, 1.5, 1.5); /*#define DEBUG*/ #ifdef DEBUG glScalef (.5, .5, .5); glTranslatef (0, -bp->gears[0].g.r * bp->ngears, 0); #endif for (i = 0; i < bp->ngears; i++) { mogear *mg = &bp->gears[i]; gear *g = &mg->g; glPushMatrix(); #ifndef DEBUG glRotatef (mg->pos_th * 180 / M_PI, 0, 0, 1); /* rotation on ring */ glTranslatef (bp->ring_r, 0, 0); /* position on ring */ glRotatef (mg->pos_thz * 180 / M_PI, 0, 1, 0); /* twist a bit */ if (do_roll) { glRotatef (bp->roll_th * 180 / M_PI, 0, 1, 0); bp->roll_th += speed * 0.0005; } #else glTranslatef (0, i * 2 * g->r, 0); #endif glRotatef (g->th * 180 / M_PI, 0, 0, 1); glCallList (g->dlist); mi->polygon_count += g->polygons; glPopMatrix (); } glPopMatrix (); #ifndef DEBUG /* spin gears */ for (i = 0; i < bp->ngears; i++) { mogear *mg = &bp->gears[i]; mg->g.th += speed * (M_PI / 100) * (i & 1 ? 1 : -1); } #endif if (mi->fps_p) do_fps (mi); glFinish(); glXSwapBuffers(dpy, window); } ENTRYPOINT void free_mgears (ModeInfo *mi) { mgears_configuration *bp = &bps[MI_SCREEN(mi)]; if (!bp->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); if (bp->gears) free (bp->gears); if (bp->rot) free_rotator (bp->rot); if (bp->trackball) gltrackball_free (bp->trackball); } XSCREENSAVER_MODULE_2 ("MoebiusGears", moebiusgears, mgears) #endif /* USE_GL */