From http://www.jwz.org/xscreensaver/xscreensaver-5.31.tar.gz
[xscreensaver] / hacks / glx / moebiusgears.c
1 /* moebiusgears, Copyright (c) 2007-2014 Jamie Zawinski <jwz@jwz.org>
2  *
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 
9  * implied warranty.
10  */
11
12 #define DEFAULTS        "*delay:        30000       \n" \
13                         "*count:        17          \n" \
14                         "*showFPS:      False       \n" \
15                         "*wireframe:    False       \n" \
16
17 # define refresh_mgears 0
18 # define release_mgears 0
19 #undef countof
20 #define countof(x) (sizeof((x))/sizeof((*x)))
21
22 #include "xlockmore.h"
23 #include "involute.h"
24 #include "normals.h"
25 #include "rotator.h"
26 #include "gltrackball.h"
27 #include <ctype.h>
28
29 #ifdef USE_GL /* whole file */
30
31
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"
37
38 typedef struct {
39
40   gear g;
41   GLfloat pos_th;  /* position on ring of gear system */
42   GLfloat pos_thz; /* rotation out of plane of gear system */
43 } mogear;
44
45 typedef struct {
46   GLXContext *glx_context;
47   rotator *rot;
48   trackball_state *trackball;
49   Bool button_down_p;
50
51   int ngears;
52   mogear *gears;
53   GLfloat ring_r;  /* radius of gear system */
54   GLfloat roll_th;
55
56 } mgears_configuration;
57
58 static mgears_configuration *bps = NULL;
59
60 static Bool do_spin;
61 static GLfloat speed;
62 static Bool do_wander;
63 static Bool do_roll;
64 static int teeth_arg;
65
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      },
75 };
76
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},
83 };
84
85 ENTRYPOINT ModeSpecOpt mgears_opts = {countof(opts), opts, countof(vars), vars, NULL};
86
87
88 /* Window management, etc
89  */
90 ENTRYPOINT void
91 reshape_mgears (ModeInfo *mi, int width, int height)
92 {
93   GLfloat h = (GLfloat) height / (GLfloat) width;
94
95   glViewport (0, 0, (GLint) width, (GLint) height);
96
97   glMatrixMode(GL_PROJECTION);
98   glLoadIdentity();
99   gluPerspective (30.0, 1/h, 1.0, 100.0);
100
101   glMatrixMode(GL_MODELVIEW);
102   glLoadIdentity();
103   gluLookAt( 0.0, 0.0, 30.0,
104              0.0, 0.0, 0.0,
105              0.0, 1.0, 0.0);
106
107   glClear(GL_COLOR_BUFFER_BIT);
108 }
109
110
111 static void
112 reset_mgears (ModeInfo *mi)
113 {
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;
119   int i, nubs, size;
120
121   if (! (total_gears & 1)) 
122     total_gears++;              /* must be odd or gears intersect */
123
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...
126   */
127   if (! (teeth_arg & 1)) teeth_arg++;
128   if (teeth_arg < 7) teeth_arg = 7;
129
130   if (total_gears < 13) /* gear mesh angle is too steep with less */
131     total_gears = 13;
132
133   thick = 0.2;
134   nubs = (random() & 3) ? 0 : (random() % teeth_arg) / 2;
135
136   slope = 0;
137
138   /* Sloping gears are incompatible with "-roll" ... */
139   /* slope= -M_PI * 2 / total_gears; */
140
141   gears_per_turn = total_gears / 2.0;
142
143   bp->ring_r = 3;
144   gear_r = M_PI * bp->ring_r / gears_per_turn;
145   tw = 0;
146   th = gear_r * 2.5 / teeth_arg;
147
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 :
152           INVOLUTE_SMALL);
153
154   /* If there are lots of teeth, use a lower density mesh. */
155   if (teeth_arg > 77)
156     size = INVOLUTE_SMALL;
157   if (teeth_arg > 45 && size >= INVOLUTE_HUGE)
158     size = INVOLUTE_MEDIUM;
159
160   if (bp->gears)
161     {
162       for (i = 0; i < bp->ngears; i++)
163         glDeleteLists (bp->gears[i].g.dlist, 1);
164       free (bp->gears);
165       bp->gears = 0;
166     }
167
168   bp->ngears = total_gears;
169
170   bp->gears = (mogear *) calloc (bp->ngears, sizeof(*bp->gears));
171   for (i = 0; i < bp->ngears; i++)
172     {
173       mogear *mg = &bp->gears[i];
174       gear *g = &mg->g;
175
176       g->r           = gear_r;
177       g->size        = size;
178       g->nteeth      = teeth_arg;
179       g->tooth_w     = tw;
180       g->tooth_h     = th;
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;
188       g->nubs        = nubs;
189       mg->pos_th     = (M_PI * 2 / gears_per_turn) * i;
190       mg->pos_thz    = (M_PI / 2 / gears_per_turn) * i;
191
192       g->th = ((i & 1)
193                ? (M_PI * 2 / g->nteeth)
194                : 0);
195
196       /* Colorize
197        */
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);
201       g->color[3] = 1.0;
202
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];
207
208       /* Now render the gear into its display list.
209        */
210       g->dlist = glGenLists (1);
211       if (! g->dlist)
212         {
213           check_gl_error ("glGenLists");
214           abort();
215         }
216
217       glNewList (g->dlist, GL_COMPILE);
218       g->polygons += draw_involute_gear (g, wire);
219       glEndList ();
220     }
221 }
222
223
224 ENTRYPOINT void 
225 init_mgears (ModeInfo *mi)
226 {
227   mgears_configuration *bp;
228   int wire = MI_IS_WIREFRAME(mi);
229
230   if (!bps) {
231     bps = (mgears_configuration *)
232       calloc (MI_NUM_SCREENS(mi), sizeof (mgears_configuration));
233     if (!bps) {
234       fprintf(stderr, "%s: out of memory\n", progname);
235       exit(1);
236     }
237   }
238
239   bp = &bps[MI_SCREEN(mi)];
240
241   bp->glx_context = init_GL(mi);
242
243   reshape_mgears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
244
245   if (!wire)
246     {
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};
251
252       glEnable(GL_LIGHTING);
253       glEnable(GL_LIGHT0);
254       glEnable(GL_DEPTH_TEST);
255       glEnable(GL_CULL_FACE);
256
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);
261     }
262
263   {
264     double spin_speed   = 0.5;
265     double wander_speed = 0.01;
266     double spin_accel   = 2.0;
267
268     bp->rot = make_rotator (do_spin ? spin_speed : 0,
269                             do_spin ? spin_speed : 0,
270                             do_spin ? spin_speed : 0,
271                             spin_accel,
272                             do_wander ? wander_speed : 0,
273                             False /* don't randomize */
274                             );
275     bp->trackball = gltrackball_init (True);
276   }
277
278   reset_mgears (mi);
279 }
280
281
282 ENTRYPOINT Bool
283 mgears_handle_event (ModeInfo *mi, XEvent *event)
284 {
285   mgears_configuration *bp = &bps[MI_SCREEN(mi)];
286
287   if (gltrackball_event_handler (event, bp->trackball,
288                                  MI_WIDTH (mi), MI_HEIGHT (mi),
289                                  &bp->button_down_p))
290     return True;
291   else if (event->xany.type == KeyPress)
292     {
293       KeySym keysym;
294       char c = 0;
295       XLookupString (&event->xkey, &c, 1, &keysym, 0);
296       if (c == '+' || c == '=' ||
297           keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
298         {
299           MI_COUNT(mi) += 2;
300           reset_mgears (mi);
301           return True;
302         }
303       else if (c == '-' || c == '_' ||
304                keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
305         {
306           if (MI_COUNT(mi) <= 13)
307             return False;
308           MI_COUNT(mi) -= 2;
309           reset_mgears (mi);
310           return True;
311         }
312       else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
313         goto DEF;
314     }
315   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
316     {
317     DEF:
318       MI_COUNT(mi) = 13 + (2 * (random() % 10));
319       reset_mgears (mi);
320       return True;
321     }
322
323   return False;
324 }
325
326
327 ENTRYPOINT void
328 draw_mgears (ModeInfo *mi)
329 {
330   mgears_configuration *bp = &bps[MI_SCREEN(mi)];
331   Display *dpy = MI_DISPLAY(mi);
332   Window window = MI_WINDOW(mi);
333   int i;
334
335   if (!bp->glx_context)
336     return;
337
338   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
339
340   glShadeModel(GL_SMOOTH);
341
342   glEnable(GL_DEPTH_TEST);
343   glEnable(GL_NORMALIZE);
344   glEnable(GL_CULL_FACE);
345
346   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
347
348   glPushMatrix ();
349
350   glScalef(1.1, 1.1, 1.1);
351
352   {
353     double x, y, z;
354     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
355     glTranslatef ((x - 0.5) * 4,
356                   (y - 0.5) * 4,
357                   (z - 0.5) * 7);
358
359     gltrackball_rotate (bp->trackball);
360
361     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
362
363     /* add a little rotation for -no-spin mode */
364     x -= 0.14;
365     y -= 0.06;
366
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);
370   }
371
372   mi->polygon_count = 0;
373
374   glScalef (1.5, 1.5, 1.5);
375
376 /*#define DEBUG*/
377
378 #ifdef DEBUG
379   glScalef (.5, .5, .5);
380   glTranslatef (0, -bp->gears[0].g.r * bp->ngears, 0);
381 #endif
382
383   for (i = 0; i < bp->ngears; i++)
384     {
385       mogear *mg = &bp->gears[i];
386       gear *g = &mg->g;
387
388       glPushMatrix();
389 #ifndef DEBUG
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 */
393
394       if (do_roll)
395         {
396           glRotatef (bp->roll_th * 180 / M_PI, 0, 1, 0);
397           bp->roll_th += speed * 0.0005;
398         }
399 #else
400       glTranslatef (0, i * 2 * g->r, 0);
401 #endif
402       glRotatef (g->th * 180 / M_PI, 0, 0, 1);
403
404       glCallList (g->dlist);
405       mi->polygon_count += g->polygons;
406       glPopMatrix ();
407     }
408
409   glPopMatrix ();
410
411 #ifndef DEBUG
412   /* spin gears */
413   for (i = 0; i < bp->ngears; i++)
414     {
415       mogear *mg = &bp->gears[i];
416       mg->g.th += speed * (M_PI / 100) * (i & 1 ? 1 : -1);
417     }
418 #endif
419
420   if (mi->fps_p) do_fps (mi);
421   glFinish();
422
423   glXSwapBuffers(dpy, window);
424 }
425
426 XSCREENSAVER_MODULE_2 ("MoebiusGears", moebiusgears, mgears)
427
428 #endif /* USE_GL */