1 /* moebiusgears, Copyright (c) 2007-2014 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" \
16 "*suppressRotationAnimation: True\n" \
18 # define free_mgears 0
19 # define release_mgears 0
21 #define countof(x) (sizeof((x))/sizeof((*x)))
23 #include "xlockmore.h"
27 #include "gltrackball.h"
30 #ifdef USE_GL /* whole file */
33 #define DEF_SPIN "True"
34 #define DEF_WANDER "True"
35 #define DEF_ROLL "True"
36 #define DEF_SPEED "1.0"
37 #define DEF_TEETH "15"
42 GLfloat pos_th; /* position on ring of gear system */
43 GLfloat pos_thz; /* rotation out of plane of gear system */
47 GLXContext *glx_context;
49 trackball_state *trackball;
54 GLfloat ring_r; /* radius of gear system */
57 } mgears_configuration;
59 static mgears_configuration *bps = NULL;
63 static Bool do_wander;
67 static XrmOptionDescRec opts[] = {
68 { "-spin", ".spin", XrmoptionNoArg, "True" },
69 { "+spin", ".spin", XrmoptionNoArg, "False" },
70 { "-speed", ".speed", XrmoptionSepArg, 0 },
71 { "-wander", ".wander", XrmoptionNoArg, "True" },
72 { "+wander", ".wander", XrmoptionNoArg, "False" },
73 { "-roll", ".roll", XrmoptionNoArg, "True" },
74 { "+roll", ".roll", XrmoptionNoArg, "False" },
75 { "-teeth", ".teeth", XrmoptionSepArg, 0 },
78 static argtype vars[] = {
79 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
80 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
81 {&do_roll, "roll", "Roll", DEF_ROLL, t_Bool},
82 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
83 {&teeth_arg, "teeth", "Teeth", DEF_TEETH, t_Int},
86 ENTRYPOINT ModeSpecOpt mgears_opts = {countof(opts), opts, countof(vars), vars, NULL};
89 /* Window management, etc
92 reshape_mgears (ModeInfo *mi, int width, int height)
94 GLfloat h = (GLfloat) height / (GLfloat) width;
97 if (width > height * 5) { /* tiny window: show middle */
98 height = width * 9/16;
100 h = height / (GLfloat) width;
103 glViewport (0, y, (GLint) width, (GLint) height);
105 glMatrixMode(GL_PROJECTION);
107 gluPerspective (30.0, 1/h, 1.0, 100.0);
109 glMatrixMode(GL_MODELVIEW);
111 gluLookAt( 0.0, 0.0, 30.0,
115 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
117 int o = (int) current_device_rotation();
118 if (o != 0 && o != 180 && o != -180)
119 glScalef (1/h, 1/h, 1/h);
123 glClear(GL_COLOR_BUFFER_BIT);
128 reset_mgears (ModeInfo *mi)
130 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
131 int wire = MI_IS_WIREFRAME(mi);
132 int total_gears = MI_COUNT(mi);
133 double gears_per_turn;
134 double gear_r, tw, th, thick, slope;
137 if (! (total_gears & 1))
138 total_gears++; /* must be odd or gears intersect */
140 /* Number of teeth must be odd if number of gears is odd, or teeth don't
141 mesh when the loop closes. And since number of gears must be odd...
143 if (! (teeth_arg & 1)) teeth_arg++;
144 if (teeth_arg < 7) teeth_arg = 7;
146 if (total_gears < 13) /* gear mesh angle is too steep with less */
150 nubs = (random() & 3) ? 0 : (random() % teeth_arg) / 2;
154 /* Sloping gears are incompatible with "-roll" ... */
155 /* slope= -M_PI * 2 / total_gears; */
157 gears_per_turn = total_gears / 2.0;
160 gear_r = M_PI * bp->ring_r / gears_per_turn;
162 th = gear_r * 2.5 / teeth_arg;
164 /* If the gears are small, use a lower density mesh. */
165 size = (gear_r > 0.60 ? INVOLUTE_HUGE :
166 gear_r > 0.32 ? INVOLUTE_LARGE :
167 gear_r > 0.13 ? INVOLUTE_MEDIUM :
170 /* If there are lots of teeth, use a lower density mesh. */
172 size = INVOLUTE_SMALL;
173 if (teeth_arg > 45 && size >= INVOLUTE_HUGE)
174 size = INVOLUTE_MEDIUM;
178 for (i = 0; i < bp->ngears; i++)
179 glDeleteLists (bp->gears[i].g.dlist, 1);
184 bp->ngears = total_gears;
186 bp->gears = (mogear *) calloc (bp->ngears, sizeof(*bp->gears));
187 for (i = 0; i < bp->ngears; i++)
189 mogear *mg = &bp->gears[i];
194 g->nteeth = teeth_arg;
197 g->tooth_slope = slope;
198 g->thickness = g->r * thick;
199 g->thickness2 = g->thickness * 0.1;
200 g->thickness3 = g->thickness;
201 g->inner_r = g->r * 0.80;
202 g->inner_r2 = g->r * 0.60;
203 g->inner_r3 = g->r * 0.55;
205 mg->pos_th = (M_PI * 2 / gears_per_turn) * i;
206 mg->pos_thz = (M_PI / 2 / gears_per_turn) * i;
209 ? (M_PI * 2 / g->nteeth)
214 g->color[0] = 0.7 + frand(0.3);
215 g->color[1] = 0.7 + frand(0.3);
216 g->color[2] = 0.7 + frand(0.3);
219 g->color2[0] = g->color[0] * 0.85;
220 g->color2[1] = g->color[1] * 0.85;
221 g->color2[2] = g->color[2] * 0.85;
222 g->color2[3] = g->color[3];
224 /* Now render the gear into its display list.
226 g->dlist = glGenLists (1);
229 check_gl_error ("glGenLists");
233 glNewList (g->dlist, GL_COMPILE);
234 g->polygons += draw_involute_gear (g, wire);
241 init_mgears (ModeInfo *mi)
243 mgears_configuration *bp;
244 int wire = MI_IS_WIREFRAME(mi);
248 bp = &bps[MI_SCREEN(mi)];
250 bp->glx_context = init_GL(mi);
252 reshape_mgears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
256 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
257 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
258 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
259 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
261 glEnable(GL_LIGHTING);
263 glEnable(GL_DEPTH_TEST);
264 glEnable(GL_CULL_FACE);
266 glLightfv(GL_LIGHT0, GL_POSITION, pos);
267 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
268 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
269 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
273 double spin_speed = 0.5;
274 double wander_speed = 0.01;
275 double spin_accel = 2.0;
277 bp->rot = make_rotator (do_spin ? spin_speed : 0,
278 do_spin ? spin_speed : 0,
279 do_spin ? spin_speed : 0,
281 do_wander ? wander_speed : 0,
282 False /* don't randomize */
284 bp->trackball = gltrackball_init (True);
292 mgears_handle_event (ModeInfo *mi, XEvent *event)
294 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
296 if (gltrackball_event_handler (event, bp->trackball,
297 MI_WIDTH (mi), MI_HEIGHT (mi),
300 else if (event->xany.type == KeyPress)
304 XLookupString (&event->xkey, &c, 1, &keysym, 0);
305 if (c == '+' || c == '=' ||
306 keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
312 else if (c == '-' || c == '_' ||
313 keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
315 if (MI_COUNT(mi) <= 13)
321 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
324 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
327 MI_COUNT(mi) = 13 + (2 * (random() % 10));
337 draw_mgears (ModeInfo *mi)
339 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
340 Display *dpy = MI_DISPLAY(mi);
341 Window window = MI_WINDOW(mi);
344 if (!bp->glx_context)
347 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
349 glShadeModel(GL_SMOOTH);
351 glEnable(GL_DEPTH_TEST);
352 glEnable(GL_NORMALIZE);
353 glEnable(GL_CULL_FACE);
355 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
359 glScalef(1.1, 1.1, 1.1);
363 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
364 glTranslatef ((x - 0.5) * 4,
368 gltrackball_rotate (bp->trackball);
370 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
372 /* add a little rotation for -no-spin mode */
376 glRotatef (x * 360, 1.0, 0.0, 0.0);
377 glRotatef (y * 360, 0.0, 1.0, 0.0);
378 glRotatef (z * 360, 0.0, 0.0, 1.0);
381 mi->polygon_count = 0;
383 glScalef (1.5, 1.5, 1.5);
388 glScalef (.5, .5, .5);
389 glTranslatef (0, -bp->gears[0].g.r * bp->ngears, 0);
392 for (i = 0; i < bp->ngears; i++)
394 mogear *mg = &bp->gears[i];
399 glRotatef (mg->pos_th * 180 / M_PI, 0, 0, 1); /* rotation on ring */
400 glTranslatef (bp->ring_r, 0, 0); /* position on ring */
401 glRotatef (mg->pos_thz * 180 / M_PI, 0, 1, 0); /* twist a bit */
405 glRotatef (bp->roll_th * 180 / M_PI, 0, 1, 0);
406 bp->roll_th += speed * 0.0005;
409 glTranslatef (0, i * 2 * g->r, 0);
411 glRotatef (g->th * 180 / M_PI, 0, 0, 1);
413 glCallList (g->dlist);
414 mi->polygon_count += g->polygons;
422 for (i = 0; i < bp->ngears; i++)
424 mogear *mg = &bp->gears[i];
425 mg->g.th += speed * (M_PI / 100) * (i & 1 ? 1 : -1);
429 if (mi->fps_p) do_fps (mi);
432 glXSwapBuffers(dpy, window);
435 XSCREENSAVER_MODULE_2 ("MoebiusGears", moebiusgears, mgears)