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" \
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 init_mgears (ModeInfo *mi)
114 mgears_configuration *bp;
115 int wire = MI_IS_WIREFRAME(mi);
119 bps = (mgears_configuration *)
120 calloc (MI_NUM_SCREENS(mi), sizeof (mgears_configuration));
122 fprintf(stderr, "%s: out of memory\n", progname);
127 bp = &bps[MI_SCREEN(mi)];
129 bp->glx_context = init_GL(mi);
131 reshape_mgears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
135 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
136 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
137 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
138 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
140 glEnable(GL_LIGHTING);
142 glEnable(GL_DEPTH_TEST);
143 glEnable(GL_CULL_FACE);
145 glLightfv(GL_LIGHT0, GL_POSITION, pos);
146 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
147 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
148 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
153 double spin_speed = 0.5;
154 double wander_speed = 0.01;
155 double spin_accel = 2.0;
157 bp->rot = make_rotator (do_spin ? spin_speed : 0,
158 do_spin ? spin_speed : 0,
159 do_spin ? spin_speed : 0,
161 do_wander ? wander_speed : 0,
162 False /* don't randomize */
164 bp->trackball = gltrackball_init (True);
168 int total_gears = MI_COUNT(mi);
169 double gears_per_turn;
170 double gear_r, tw, th, thick, slope;
173 if (! (total_gears & 1))
174 total_gears++; /* must be odd or gears intersect */
176 /* Number of teeth must be odd if number of gears is odd, or teeth don't
177 mesh when the loop closes. And since number of gears must be odd...
179 if (! (teeth_arg & 1)) teeth_arg++;
180 if (teeth_arg < 7) teeth_arg = 7;
182 if (total_gears < 13) /* gear mesh angle is too steep with less */
186 nubs = (random() & 3) ? 0 : (random() % teeth_arg) / 2;
190 /* Sloping gears are incompatible with "-roll" ... */
191 /* slope= -M_PI * 2 / total_gears; */
193 gears_per_turn = total_gears / 2.0;
196 gear_r = M_PI * bp->ring_r / gears_per_turn;
198 th = gear_r * 2.5 / teeth_arg;
200 /* If the gears are small, use a lower density mesh. */
201 size = (gear_r > 0.32 ? INVOLUTE_LARGE :
202 gear_r > 0.13 ? INVOLUTE_MEDIUM :
205 /* If there are lots of teeth, use a lower density mesh. */
207 size = INVOLUTE_SMALL;
208 if (teeth_arg > 45 && size == INVOLUTE_LARGE)
209 size = INVOLUTE_MEDIUM;
213 for (i = 0; i < bp->ngears; i++)
214 glDeleteLists (bp->gears[i].g.dlist, 1);
218 bp->ngears = total_gears;
220 bp->gears = (mogear *) calloc (bp->ngears, sizeof(*bp->gears));
221 for (i = 0; i < bp->ngears; i++)
223 mogear *mg = &bp->gears[i];
228 g->nteeth = teeth_arg;
231 g->tooth_slope = slope;
232 g->thickness = g->r * thick;
233 g->thickness2 = g->thickness * 0.1;
234 g->thickness3 = g->thickness;
235 g->inner_r = g->r * 0.80;
236 g->inner_r2 = g->r * 0.60;
237 g->inner_r3 = g->r * 0.55;
239 mg->pos_th = (M_PI * 2 / gears_per_turn) * i;
240 mg->pos_thz = (M_PI / 2 / gears_per_turn) * i;
243 ? (M_PI * 2 / g->nteeth)
248 g->color[0] = 0.7 + frand(0.3);
249 g->color[1] = 0.7 + frand(0.3);
250 g->color[2] = 0.7 + frand(0.3);
253 g->color2[0] = g->color[0] * 0.85;
254 g->color2[1] = g->color[1] * 0.85;
255 g->color2[2] = g->color[2] * 0.85;
256 g->color2[3] = g->color[3];
258 /* Now render the gear into its display list.
260 g->dlist = glGenLists (1);
263 check_gl_error ("glGenLists");
267 glNewList (g->dlist, GL_COMPILE);
268 g->polygons += draw_involute_gear (g, wire);
276 mgears_handle_event (ModeInfo *mi, XEvent *event)
278 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
280 if (gltrackball_event_handler (event, bp->trackball,
281 MI_WIDTH (mi), MI_HEIGHT (mi),
284 else if (event->xany.type == KeyPress)
288 XLookupString (&event->xkey, &c, 1, &keysym, 0);
289 if (c == '+' || c == '=' ||
290 keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
296 else if (c == '-' || c == '_' ||
297 keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
299 if (MI_COUNT(mi) <= 13)
305 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
308 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
311 MI_COUNT(mi) = 13 + (2 * (random() % 10));
321 draw_mgears (ModeInfo *mi)
323 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
324 Display *dpy = MI_DISPLAY(mi);
325 Window window = MI_WINDOW(mi);
328 if (!bp->glx_context)
331 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
333 glShadeModel(GL_SMOOTH);
335 glEnable(GL_DEPTH_TEST);
336 glEnable(GL_NORMALIZE);
337 glEnable(GL_CULL_FACE);
339 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
343 glScalef(1.1, 1.1, 1.1);
347 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
348 glTranslatef ((x - 0.5) * 4,
352 gltrackball_rotate (bp->trackball);
354 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
356 /* add a little rotation for -no-spin mode */
360 glRotatef (x * 360, 1.0, 0.0, 0.0);
361 glRotatef (y * 360, 0.0, 1.0, 0.0);
362 glRotatef (z * 360, 0.0, 0.0, 1.0);
365 mi->polygon_count = 0;
367 glScalef (1.5, 1.5, 1.5);
372 glScalef (.5, .5, .5);
373 glTranslatef (0, -bp->gears[0].g.r * bp->ngears, 0);
376 for (i = 0; i < bp->ngears; i++)
378 mogear *mg = &bp->gears[i];
383 glRotatef (mg->pos_th * 180 / M_PI, 0, 0, 1); /* rotation on ring */
384 glTranslatef (bp->ring_r, 0, 0); /* position on ring */
385 glRotatef (mg->pos_thz * 180 / M_PI, 0, 1, 0); /* twist a bit */
389 glRotatef (bp->roll_th * 180 / M_PI, 0, 1, 0);
390 bp->roll_th += speed * 0.0005;
393 glTranslatef (0, i * 2 * g->r, 0);
395 glRotatef (g->th * 180 / M_PI, 0, 0, 1);
397 glCallList (g->dlist);
398 mi->polygon_count += g->polygons;
406 for (i = 0; i < bp->ngears; i++)
408 mogear *mg = &bp->gears[i];
409 mg->g.th += speed * (M_PI / 100) * (i & 1 ? 1 : -1);
413 if (mi->fps_p) do_fps (mi);
416 glXSwapBuffers(dpy, window);
419 XSCREENSAVER_MODULE_2 ("MoebiusGears", moebiusgears, mgears)