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 reset_mgears (ModeInfo *mi)
114 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
115 int wire = MI_IS_WIREFRAME(mi);
116 int total_gears = MI_COUNT(mi);
117 double gears_per_turn;
118 double gear_r, tw, th, thick, slope;
121 if (! (total_gears & 1))
122 total_gears++; /* must be odd or gears intersect */
124 /* Number of teeth must be odd if number of gears is odd, or teeth don't
125 mesh when the loop closes. And since number of gears must be odd...
127 if (! (teeth_arg & 1)) teeth_arg++;
128 if (teeth_arg < 7) teeth_arg = 7;
130 if (total_gears < 13) /* gear mesh angle is too steep with less */
134 nubs = (random() & 3) ? 0 : (random() % teeth_arg) / 2;
138 /* Sloping gears are incompatible with "-roll" ... */
139 /* slope= -M_PI * 2 / total_gears; */
141 gears_per_turn = total_gears / 2.0;
144 gear_r = M_PI * bp->ring_r / gears_per_turn;
146 th = gear_r * 2.5 / teeth_arg;
148 /* If the gears are small, use a lower density mesh. */
149 size = (gear_r > 0.60 ? INVOLUTE_HUGE :
150 gear_r > 0.32 ? INVOLUTE_LARGE :
151 gear_r > 0.13 ? INVOLUTE_MEDIUM :
154 /* If there are lots of teeth, use a lower density mesh. */
156 size = INVOLUTE_SMALL;
157 if (teeth_arg > 45 && size >= INVOLUTE_HUGE)
158 size = INVOLUTE_MEDIUM;
162 for (i = 0; i < bp->ngears; i++)
163 glDeleteLists (bp->gears[i].g.dlist, 1);
168 bp->ngears = total_gears;
170 bp->gears = (mogear *) calloc (bp->ngears, sizeof(*bp->gears));
171 for (i = 0; i < bp->ngears; i++)
173 mogear *mg = &bp->gears[i];
178 g->nteeth = teeth_arg;
181 g->tooth_slope = slope;
182 g->thickness = g->r * thick;
183 g->thickness2 = g->thickness * 0.1;
184 g->thickness3 = g->thickness;
185 g->inner_r = g->r * 0.80;
186 g->inner_r2 = g->r * 0.60;
187 g->inner_r3 = g->r * 0.55;
189 mg->pos_th = (M_PI * 2 / gears_per_turn) * i;
190 mg->pos_thz = (M_PI / 2 / gears_per_turn) * i;
193 ? (M_PI * 2 / g->nteeth)
198 g->color[0] = 0.7 + frand(0.3);
199 g->color[1] = 0.7 + frand(0.3);
200 g->color[2] = 0.7 + frand(0.3);
203 g->color2[0] = g->color[0] * 0.85;
204 g->color2[1] = g->color[1] * 0.85;
205 g->color2[2] = g->color[2] * 0.85;
206 g->color2[3] = g->color[3];
208 /* Now render the gear into its display list.
210 g->dlist = glGenLists (1);
213 check_gl_error ("glGenLists");
217 glNewList (g->dlist, GL_COMPILE);
218 g->polygons += draw_involute_gear (g, wire);
225 init_mgears (ModeInfo *mi)
227 mgears_configuration *bp;
228 int wire = MI_IS_WIREFRAME(mi);
231 bps = (mgears_configuration *)
232 calloc (MI_NUM_SCREENS(mi), sizeof (mgears_configuration));
234 fprintf(stderr, "%s: out of memory\n", progname);
239 bp = &bps[MI_SCREEN(mi)];
241 bp->glx_context = init_GL(mi);
243 reshape_mgears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
247 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
248 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
249 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
250 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
252 glEnable(GL_LIGHTING);
254 glEnable(GL_DEPTH_TEST);
255 glEnable(GL_CULL_FACE);
257 glLightfv(GL_LIGHT0, GL_POSITION, pos);
258 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
259 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
260 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
264 double spin_speed = 0.5;
265 double wander_speed = 0.01;
266 double spin_accel = 2.0;
268 bp->rot = make_rotator (do_spin ? spin_speed : 0,
269 do_spin ? spin_speed : 0,
270 do_spin ? spin_speed : 0,
272 do_wander ? wander_speed : 0,
273 False /* don't randomize */
275 bp->trackball = gltrackball_init (True);
283 mgears_handle_event (ModeInfo *mi, XEvent *event)
285 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
287 if (gltrackball_event_handler (event, bp->trackball,
288 MI_WIDTH (mi), MI_HEIGHT (mi),
291 else if (event->xany.type == KeyPress)
295 XLookupString (&event->xkey, &c, 1, &keysym, 0);
296 if (c == '+' || c == '=' ||
297 keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
303 else if (c == '-' || c == '_' ||
304 keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
306 if (MI_COUNT(mi) <= 13)
312 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
315 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
318 MI_COUNT(mi) = 13 + (2 * (random() % 10));
328 draw_mgears (ModeInfo *mi)
330 mgears_configuration *bp = &bps[MI_SCREEN(mi)];
331 Display *dpy = MI_DISPLAY(mi);
332 Window window = MI_WINDOW(mi);
335 if (!bp->glx_context)
338 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
340 glShadeModel(GL_SMOOTH);
342 glEnable(GL_DEPTH_TEST);
343 glEnable(GL_NORMALIZE);
344 glEnable(GL_CULL_FACE);
346 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
350 glScalef(1.1, 1.1, 1.1);
354 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
355 glTranslatef ((x - 0.5) * 4,
359 gltrackball_rotate (bp->trackball);
361 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
363 /* add a little rotation for -no-spin mode */
367 glRotatef (x * 360, 1.0, 0.0, 0.0);
368 glRotatef (y * 360, 0.0, 1.0, 0.0);
369 glRotatef (z * 360, 0.0, 0.0, 1.0);
372 mi->polygon_count = 0;
374 glScalef (1.5, 1.5, 1.5);
379 glScalef (.5, .5, .5);
380 glTranslatef (0, -bp->gears[0].g.r * bp->ngears, 0);
383 for (i = 0; i < bp->ngears; i++)
385 mogear *mg = &bp->gears[i];
390 glRotatef (mg->pos_th * 180 / M_PI, 0, 0, 1); /* rotation on ring */
391 glTranslatef (bp->ring_r, 0, 0); /* position on ring */
392 glRotatef (mg->pos_thz * 180 / M_PI, 0, 1, 0); /* twist a bit */
396 glRotatef (bp->roll_th * 180 / M_PI, 0, 1, 0);
397 bp->roll_th += speed * 0.0005;
400 glTranslatef (0, i * 2 * g->r, 0);
402 glRotatef (g->th * 180 / M_PI, 0, 0, 1);
404 glCallList (g->dlist);
405 mi->polygon_count += g->polygons;
413 for (i = 0; i < bp->ngears; i++)
415 mogear *mg = &bp->gears[i];
416 mg->g.th += speed * (M_PI / 100) * (i & 1 ? 1 : -1);
420 if (mi->fps_p) do_fps (mi);
423 glXSwapBuffers(dpy, window);
426 XSCREENSAVER_MODULE_2 ("MoebiusGears", moebiusgears, mgears)