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 refresh_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;
96 glViewport (0, 0, (GLint) width, (GLint) height);
98 glMatrixMode(GL_PROJECTION);
100 gluPerspective (30.0, 1/h, 1.0, 100.0);
102 glMatrixMode(GL_MODELVIEW);
104 gluLookAt( 0.0, 0.0, 30.0,
108 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
110 int o = (int) current_device_rotation();
111 if (o != 0 && o != 180 && o != -180)
112 glScalef (1/h, 1/h, 1/h);
116 glClear(GL_COLOR_BUFFER_BIT);
121 reset_mgears (ModeInfo *mi)
123 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
124 int wire = MI_IS_WIREFRAME(mi);
125 int total_gears = MI_COUNT(mi);
126 double gears_per_turn;
127 double gear_r, tw, th, thick, slope;
130 if (! (total_gears & 1))
131 total_gears++; /* must be odd or gears intersect */
133 /* Number of teeth must be odd if number of gears is odd, or teeth don't
134 mesh when the loop closes. And since number of gears must be odd...
136 if (! (teeth_arg & 1)) teeth_arg++;
137 if (teeth_arg < 7) teeth_arg = 7;
139 if (total_gears < 13) /* gear mesh angle is too steep with less */
143 nubs = (random() & 3) ? 0 : (random() % teeth_arg) / 2;
147 /* Sloping gears are incompatible with "-roll" ... */
148 /* slope= -M_PI * 2 / total_gears; */
150 gears_per_turn = total_gears / 2.0;
153 gear_r = M_PI * bp->ring_r / gears_per_turn;
155 th = gear_r * 2.5 / teeth_arg;
157 /* If the gears are small, use a lower density mesh. */
158 size = (gear_r > 0.60 ? INVOLUTE_HUGE :
159 gear_r > 0.32 ? INVOLUTE_LARGE :
160 gear_r > 0.13 ? INVOLUTE_MEDIUM :
163 /* If there are lots of teeth, use a lower density mesh. */
165 size = INVOLUTE_SMALL;
166 if (teeth_arg > 45 && size >= INVOLUTE_HUGE)
167 size = INVOLUTE_MEDIUM;
171 for (i = 0; i < bp->ngears; i++)
172 glDeleteLists (bp->gears[i].g.dlist, 1);
177 bp->ngears = total_gears;
179 bp->gears = (mogear *) calloc (bp->ngears, sizeof(*bp->gears));
180 for (i = 0; i < bp->ngears; i++)
182 mogear *mg = &bp->gears[i];
187 g->nteeth = teeth_arg;
190 g->tooth_slope = slope;
191 g->thickness = g->r * thick;
192 g->thickness2 = g->thickness * 0.1;
193 g->thickness3 = g->thickness;
194 g->inner_r = g->r * 0.80;
195 g->inner_r2 = g->r * 0.60;
196 g->inner_r3 = g->r * 0.55;
198 mg->pos_th = (M_PI * 2 / gears_per_turn) * i;
199 mg->pos_thz = (M_PI / 2 / gears_per_turn) * i;
202 ? (M_PI * 2 / g->nteeth)
207 g->color[0] = 0.7 + frand(0.3);
208 g->color[1] = 0.7 + frand(0.3);
209 g->color[2] = 0.7 + frand(0.3);
212 g->color2[0] = g->color[0] * 0.85;
213 g->color2[1] = g->color[1] * 0.85;
214 g->color2[2] = g->color[2] * 0.85;
215 g->color2[3] = g->color[3];
217 /* Now render the gear into its display list.
219 g->dlist = glGenLists (1);
222 check_gl_error ("glGenLists");
226 glNewList (g->dlist, GL_COMPILE);
227 g->polygons += draw_involute_gear (g, wire);
234 init_mgears (ModeInfo *mi)
236 mgears_configuration *bp;
237 int wire = MI_IS_WIREFRAME(mi);
240 bps = (mgears_configuration *)
241 calloc (MI_NUM_SCREENS(mi), sizeof (mgears_configuration));
243 fprintf(stderr, "%s: out of memory\n", progname);
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)