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);
170 bp = &bps[MI_SCREEN(mi)];
173 bp = &bps[MI_SCREEN(mi)];
175 bp->glx_context = init_GL(mi);
177 reshape_mgears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
181 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
182 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
183 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
184 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
186 glEnable(GL_LIGHTING);
188 glEnable(GL_DEPTH_TEST);
189 glEnable(GL_CULL_FACE);
191 glLightfv(GL_LIGHT0, GL_POSITION, pos);
192 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
193 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
194 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
198 double spin_speed = 0.5;
199 double wander_speed = 0.01;
200 double spin_accel = 2.0;
202 bp->rot = make_rotator (do_spin ? spin_speed : 0,
203 do_spin ? spin_speed : 0,
204 do_spin ? spin_speed : 0,
206 do_wander ? wander_speed : 0,
207 False /* don't randomize */
209 bp->trackball = gltrackball_init ();
213 int total_gears = MI_COUNT(mi);
214 double gears_per_turn;
215 double gear_r, tw, th, thick, slope;
218 if (! (total_gears & 1))
219 total_gears++; /* must be odd or gears intersect */
221 /* Number of teeth must be odd if number of gears is odd, or teeth don't
222 mesh when the loop closes. And since number of gears must be odd...
224 if (! (teeth_arg & 1)) teeth_arg++;
225 if (teeth_arg < 7) teeth_arg = 7;
227 if (total_gears < 13) /* gear mesh angle is too steep with less */
231 nubs = (random() & 3) ? 0 : (random() % teeth_arg) / 2;
235 /* Sloping gears are incompatible with "-roll" ... */
236 /* slope= -M_PI * 2 / total_gears; */
238 gears_per_turn = total_gears / 2.0;
241 gear_r = M_PI * bp->ring_r / gears_per_turn;
243 th = gear_r * 2.5 / teeth_arg;
245 /* If the gears are small, use a lower density mesh. */
246 size = (gear_r > 0.32 ? INVOLUTE_LARGE :
247 gear_r > 0.13 ? INVOLUTE_MEDIUM :
250 /* If there are lots of teeth, use a lower density mesh. */
252 size = INVOLUTE_SMALL;
253 if (teeth_arg > 45 && size == INVOLUTE_LARGE)
254 size = INVOLUTE_MEDIUM;
256 bp->ngears = total_gears;
257 bp->gears = (mogear *) calloc (bp->ngears, sizeof(*bp->gears));
258 for (i = 0; i < bp->ngears; i++)
260 mogear *mg = &bp->gears[i];
265 g->nteeth = teeth_arg;
268 g->tooth_slope = slope;
269 g->thickness = g->r * thick;
270 g->thickness2 = g->thickness * 0.1;
271 g->thickness3 = g->thickness;
272 g->inner_r = g->r * 0.80;
273 g->inner_r2 = g->r * 0.60;
274 g->inner_r3 = g->r * 0.55;
276 mg->pos_th = (M_PI * 2 / gears_per_turn) * i;
277 mg->pos_thz = (M_PI / 2 / gears_per_turn) * i;
280 ? (M_PI * 2 / g->nteeth)
285 g->color[0] = 0.7 + frand(0.3);
286 g->color[1] = 0.7 + frand(0.3);
287 g->color[2] = 0.7 + frand(0.3);
290 g->color2[0] = g->color[0] * 0.85;
291 g->color2[1] = g->color[1] * 0.85;
292 g->color2[2] = g->color[2] * 0.85;
293 g->color2[3] = g->color[3];
295 /* Now render the gear into its display list.
297 g->dlist = glGenLists (1);
300 check_gl_error ("glGenLists");
304 glNewList (g->dlist, GL_COMPILE);
305 g->polygons += draw_involute_gear (g, wire);
313 draw_mgears (ModeInfo *mi)
315 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
316 Display *dpy = MI_DISPLAY(mi);
317 Window window = MI_WINDOW(mi);
320 if (!bp->glx_context)
323 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
325 glShadeModel(GL_SMOOTH);
327 glEnable(GL_DEPTH_TEST);
328 glEnable(GL_NORMALIZE);
329 glEnable(GL_CULL_FACE);
331 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
335 glScalef(1.1, 1.1, 1.1);
339 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
340 glTranslatef ((x - 0.5) * 4,
344 gltrackball_rotate (bp->trackball);
346 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
348 /* add a little rotation for -no-spin mode */
352 glRotatef (x * 360, 1.0, 0.0, 0.0);
353 glRotatef (y * 360, 0.0, 1.0, 0.0);
354 glRotatef (z * 360, 0.0, 0.0, 1.0);
357 mi->polygon_count = 0;
359 glScalef (1.5, 1.5, 1.5);
364 glScalef (.5, .5, .5);
365 glTranslatef (0, -bp->gears[0].g.r * bp->ngears, 0);
368 for (i = 0; i < bp->ngears; i++)
370 mogear *mg = &bp->gears[i];
375 glRotatef (mg->pos_th * 180 / M_PI, 0, 0, 1); /* rotation on ring */
376 glTranslatef (bp->ring_r, 0, 0); /* position on ring */
377 glRotatef (mg->pos_thz * 180 / M_PI, 0, 1, 0); /* twist a bit */
381 glRotatef (bp->roll_th * 180 / M_PI, 0, 1, 0);
382 bp->roll_th += speed * 0.0005;
385 glTranslatef (0, i * 2 * g->r, 0);
387 glRotatef (g->th * 180 / M_PI, 0, 0, 1);
389 glCallList (g->dlist);
390 mi->polygon_count += g->polygons;
398 for (i = 0; i < bp->ngears; i++)
400 mogear *mg = &bp->gears[i];
401 mg->g.th += speed * (M_PI / 100) * (i & 1 ? 1 : -1);
405 if (mi->fps_p) do_fps (mi);
408 glXSwapBuffers(dpy, window);
411 XSCREENSAVER_MODULE_2 ("MoebiusGears", moebiusgears, mgears)