1 /* moebiusgears, Copyright (c) 2007-2008 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 #define DEFAULTS "*delay: 30000 \n" \
14 "*showFPS: False \n" \
15 "*wireframe: False \n" \
17 # define refresh_mgears 0
18 # define release_mgears 0
20 #define countof(x) (sizeof((x))/sizeof((*x)))
22 #include "xlockmore.h"
26 #include "gltrackball.h"
29 #ifdef USE_GL /* whole file */
32 #define DEF_SPIN "True"
33 #define DEF_WANDER "True"
34 #define DEF_ROLL "True"
35 #define DEF_SPEED "1.0"
36 #define DEF_TEETH "15"
41 GLfloat pos_th; /* position on ring of gear system */
42 GLfloat pos_thz; /* rotation out of plane of gear system */
46 GLXContext *glx_context;
48 trackball_state *trackball;
53 GLfloat ring_r; /* radius of gear system */
56 } mgears_configuration;
58 static mgears_configuration *bps = NULL;
62 static Bool do_wander;
66 static XrmOptionDescRec opts[] = {
67 { "-spin", ".spin", XrmoptionNoArg, "True" },
68 { "+spin", ".spin", XrmoptionNoArg, "False" },
69 { "-speed", ".speed", XrmoptionSepArg, 0 },
70 { "-wander", ".wander", XrmoptionNoArg, "True" },
71 { "+wander", ".wander", XrmoptionNoArg, "False" },
72 { "-roll", ".roll", XrmoptionNoArg, "True" },
73 { "+roll", ".roll", XrmoptionNoArg, "False" },
74 { "-teeth", ".teeth", XrmoptionSepArg, 0 },
77 static argtype vars[] = {
78 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
79 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
80 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
81 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
82 {&teeth_arg, "teeth", "Teeth", DEF_TEETH, t_Int},
85 ENTRYPOINT ModeSpecOpt mgears_opts = {countof(opts), opts, countof(vars), vars, NULL};
88 /* Window management, etc
91 reshape_mgears (ModeInfo *mi, int width, int height)
93 GLfloat h = (GLfloat) height / (GLfloat) width;
95 glViewport (0, 0, (GLint) width, (GLint) height);
97 glMatrixMode(GL_PROJECTION);
99 gluPerspective (30.0, 1/h, 1.0, 100.0);
101 glMatrixMode(GL_MODELVIEW);
103 gluLookAt( 0.0, 0.0, 30.0,
107 glClear(GL_COLOR_BUFFER_BIT);
112 mgears_handle_event (ModeInfo *mi, XEvent *event)
114 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
116 if (event->xany.type == ButtonPress &&
117 event->xbutton.button == Button1)
119 bp->button_down_p = True;
120 gltrackball_start (bp->trackball,
121 event->xbutton.x, event->xbutton.y,
122 MI_WIDTH (mi), MI_HEIGHT (mi));
125 else if (event->xany.type == ButtonRelease &&
126 event->xbutton.button == Button1)
128 bp->button_down_p = False;
131 else if (event->xany.type == ButtonPress &&
132 (event->xbutton.button == Button4 ||
133 event->xbutton.button == Button5 ||
134 event->xbutton.button == Button6 ||
135 event->xbutton.button == Button7))
137 gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
138 !!event->xbutton.state);
141 else if (event->xany.type == MotionNotify &&
144 gltrackball_track (bp->trackball,
145 event->xmotion.x, event->xmotion.y,
146 MI_WIDTH (mi), MI_HEIGHT (mi));
156 init_mgears (ModeInfo *mi)
158 mgears_configuration *bp;
159 int wire = MI_IS_WIREFRAME(mi);
163 bps = (mgears_configuration *)
164 calloc (MI_NUM_SCREENS(mi), sizeof (mgears_configuration));
166 fprintf(stderr, "%s: out of memory\n", progname);
171 bp = &bps[MI_SCREEN(mi)];
173 bp->glx_context = init_GL(mi);
175 reshape_mgears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
179 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
180 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
181 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
182 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
184 glEnable(GL_LIGHTING);
186 glEnable(GL_DEPTH_TEST);
187 glEnable(GL_CULL_FACE);
189 glLightfv(GL_LIGHT0, GL_POSITION, pos);
190 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
191 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
192 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
196 double spin_speed = 0.5;
197 double wander_speed = 0.01;
198 double spin_accel = 2.0;
200 bp->rot = make_rotator (do_spin ? spin_speed : 0,
201 do_spin ? spin_speed : 0,
202 do_spin ? spin_speed : 0,
204 do_wander ? wander_speed : 0,
205 False /* don't randomize */
207 bp->trackball = gltrackball_init ();
211 int total_gears = MI_COUNT(mi);
212 double gears_per_turn;
213 double gear_r, tw, th, thick, slope;
216 if (! (total_gears & 1))
217 total_gears++; /* must be odd or gears intersect */
219 /* Number of teeth must be odd if number of gears is odd, or teeth don't
220 mesh when the loop closes. And since number of gears must be odd...
222 if (! (teeth_arg & 1)) teeth_arg++;
223 if (teeth_arg < 7) teeth_arg = 7;
225 if (total_gears < 13) /* gear mesh angle is too steep with less */
229 nubs = (random() & 3) ? 0 : (random() % teeth_arg) / 2;
233 /* Sloping gears are incompatible with "-roll" ... */
234 /* slope= -M_PI * 2 / total_gears; */
236 gears_per_turn = total_gears / 2.0;
239 gear_r = M_PI * bp->ring_r / gears_per_turn;
241 th = gear_r * 2.5 / teeth_arg;
243 /* If the gears are small, use a lower density mesh. */
244 size = (gear_r > 0.32 ? INVOLUTE_LARGE :
245 gear_r > 0.13 ? INVOLUTE_MEDIUM :
248 /* If there are lots of teeth, use a lower density mesh. */
250 size = INVOLUTE_SMALL;
251 if (teeth_arg > 45 && size == INVOLUTE_LARGE)
252 size = INVOLUTE_MEDIUM;
254 bp->ngears = total_gears;
255 bp->gears = (mogear *) calloc (bp->ngears, sizeof(*bp->gears));
256 for (i = 0; i < bp->ngears; i++)
258 mogear *mg = &bp->gears[i];
263 g->nteeth = teeth_arg;
266 g->tooth_slope = slope;
267 g->thickness = g->r * thick;
268 g->thickness2 = g->thickness * 0.1;
269 g->thickness3 = g->thickness;
270 g->inner_r = g->r * 0.80;
271 g->inner_r2 = g->r * 0.60;
272 g->inner_r3 = g->r * 0.55;
274 mg->pos_th = (M_PI * 2 / gears_per_turn) * i;
275 mg->pos_thz = (M_PI / 2 / gears_per_turn) * i;
278 ? (M_PI * 2 / g->nteeth)
283 g->color[0] = 0.7 + frand(0.3);
284 g->color[1] = 0.7 + frand(0.3);
285 g->color[2] = 0.7 + frand(0.3);
288 g->color2[0] = g->color[0] * 0.85;
289 g->color2[1] = g->color[1] * 0.85;
290 g->color2[2] = g->color[2] * 0.85;
291 g->color2[3] = g->color[3];
293 /* Now render the gear into its display list.
295 g->dlist = glGenLists (1);
298 check_gl_error ("glGenLists");
302 glNewList (g->dlist, GL_COMPILE);
303 g->polygons += draw_involute_gear (g, wire);
311 draw_mgears (ModeInfo *mi)
313 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
314 Display *dpy = MI_DISPLAY(mi);
315 Window window = MI_WINDOW(mi);
318 if (!bp->glx_context)
321 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
323 glShadeModel(GL_SMOOTH);
325 glEnable(GL_DEPTH_TEST);
326 glEnable(GL_NORMALIZE);
327 glEnable(GL_CULL_FACE);
329 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
333 glScalef(1.1, 1.1, 1.1);
337 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
338 glTranslatef ((x - 0.5) * 4,
342 /* Do it twice because we don't track the device's orientation. */
343 glRotatef( current_device_rotation(), 0, 0, 1);
344 gltrackball_rotate (bp->trackball);
345 glRotatef(-current_device_rotation(), 0, 0, 1);
347 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
349 /* add a little rotation for -no-spin mode */
353 glRotatef (x * 360, 1.0, 0.0, 0.0);
354 glRotatef (y * 360, 0.0, 1.0, 0.0);
355 glRotatef (z * 360, 0.0, 0.0, 1.0);
358 mi->polygon_count = 0;
360 glScalef (1.5, 1.5, 1.5);
365 glScalef (.5, .5, .5);
366 glTranslatef (0, -bp->gears[0].g.r * bp->ngears, 0);
369 for (i = 0; i < bp->ngears; i++)
371 mogear *mg = &bp->gears[i];
376 glRotatef (mg->pos_th * 180 / M_PI, 0, 0, 1); /* rotation on ring */
377 glTranslatef (bp->ring_r, 0, 0); /* position on ring */
378 glRotatef (mg->pos_thz * 180 / M_PI, 0, 1, 0); /* twist a bit */
382 glRotatef (bp->roll_th * 180 / M_PI, 0, 1, 0);
383 bp->roll_th += speed * 0.0005;
386 glTranslatef (0, i * 2 * g->r, 0);
388 glRotatef (g->th * 180 / M_PI, 0, 0, 1);
390 glCallList (g->dlist);
391 mi->polygon_count += g->polygons;
399 for (i = 0; i < bp->ngears; i++)
401 mogear *mg = &bp->gears[i];
402 mg->g.th += speed * (M_PI / 100) * (i & 1 ? 1 : -1);
406 if (mi->fps_p) do_fps (mi);
409 glXSwapBuffers(dpy, window);
412 XSCREENSAVER_MODULE_2 ("MoebiusGears", moebiusgears, mgears)