http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / hacks / glx / skytentacles.c
1 /* Sky Tentacles, Copyright (c) 2008 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:        8           \n" \
14                         "*showFPS:      False       \n" \
15                         "*wireframe:    False       \n" \
16
17 # define refresh_tentacles 0
18 # define release_tentacles 0
19 #undef countof
20 #define countof(x) (sizeof((x))/sizeof((*x)))
21
22 #include "xlockmore.h"
23 #include "colors.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 #define DEF_SPEED       "1.0"
32 #define DEF_SMOOTH      "True"
33 #define DEF_SLICES      "32"
34 #define DEF_SEGMENTS    "32"
35 #define DEF_WIGGLINESS  "0.35"
36 #define DEF_FLEXIBILITY "0.35"
37 #define DEF_THICKNESS   "1.0"
38 #define DEF_LENGTH      "9.0"
39 #define DEF_COLOR       "#305A30"
40 #define DEF_STRIPE      "#451A30"
41 #define DEF_SUCKER      "#453E30"
42 #define DEF_DEBUG       "False"
43
44 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
45
46 typedef struct {
47   GLfloat length;     /* length of the segment coming out of this segment */
48   GLfloat th;         /* vector tilt (on yz plane) from previous segment */
49   GLfloat phi;        /* vector rotation (on xy plane) from previous segment */
50   GLfloat thickness;  /* radius of tentacle at this segment */
51   rotator *rot;       /* motion modeller */
52 } segment;
53
54 typedef struct {
55   ModeInfo *mi;
56   GLfloat x, y, z;      /* position of the base */
57   int nsegments;
58   segment *segments;
59   GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
60 } tentacle;
61
62 typedef struct {
63   GLXContext *glx_context;
64   trackball_state *trackball;
65   Bool button_down_p;
66
67   int ntentacles;
68   int tentacles_size;
69   tentacle **tentacles;
70   GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
71
72   GLuint sucker_list;
73   int sucker_polys;
74
75   Bool left_p;
76   
77
78 } tentacles_configuration;
79
80 static tentacles_configuration *tcs = NULL;
81
82 static int debug_p;
83 static GLfloat arg_speed;
84 static int smooth_p;
85 static int arg_slices;
86 static int arg_segments;
87 static GLfloat arg_thickness;
88 static GLfloat arg_length;
89 static GLfloat arg_wiggliness;
90 static GLfloat arg_flexibility;
91 static char *arg_color, *arg_stripe, *arg_sucker;
92
93 static XrmOptionDescRec opts[] = {
94   { "-speed",        ".speed",         XrmoptionSepArg, 0 },
95   { "-no-smooth",    ".smooth",        XrmoptionNoArg, "False" },
96   { "-slices",       ".slices",        XrmoptionSepArg, 0 },
97   { "-segments",     ".segments",      XrmoptionSepArg, 0 },
98   { "-thickness",    ".thickness",     XrmoptionSepArg, 0 },
99   { "-length",       ".length",        XrmoptionSepArg, 0 },
100   { "-wiggliness",   ".wiggliness",    XrmoptionSepArg, 0 },
101   { "-flexibility",  ".flexibility",   XrmoptionSepArg, 0 },
102   { "-color",        ".tentacleColor", XrmoptionSepArg, 0 },
103   { "-stripe-color", ".stripeColor",   XrmoptionSepArg, 0 },
104   { "-sucker-color", ".suckerColor",   XrmoptionSepArg, 0 },
105   { "-debug",        ".debug",         XrmoptionNoArg, "True" },
106 };
107
108 static argtype vars[] = {
109   {&arg_speed,       "speed",         "Speed",       DEF_SPEED,       t_Float},
110   {&smooth_p,        "smooth",        "Smooth",      DEF_SMOOTH,      t_Bool},
111   {&arg_slices,      "slices",        "Slices",      DEF_SLICES,      t_Int},
112   {&arg_segments,    "segments",      "Segments",    DEF_SEGMENTS,    t_Int},
113   {&arg_thickness,   "thickness",     "Thickness",   DEF_THICKNESS,   t_Float},
114   {&arg_length,      "length",        "Length",      DEF_LENGTH,      t_Float},
115   {&arg_wiggliness,  "wiggliness",    "Wiggliness",  DEF_WIGGLINESS,  t_Float},
116   {&arg_flexibility, "flexibility",   "Flexibility", DEF_FLEXIBILITY, t_Float},
117   {&arg_color,       "tentacleColor", "Color",       DEF_COLOR,       t_String},
118   {&arg_stripe,      "stripeColor",   "Color",       DEF_STRIPE,      t_String},
119   {&arg_sucker,      "suckerColor",   "Color",       DEF_SUCKER,      t_String},
120   {&debug_p,         "debug",         "Debug",       DEF_DEBUG,       t_Bool},
121 };
122
123 ENTRYPOINT ModeSpecOpt tentacles_opts = {countof(opts), opts, countof(vars), vars, NULL};
124
125
126 /* Window management, etc
127  */
128 ENTRYPOINT void
129 reshape_tentacles (ModeInfo *mi, int width, int height)
130 {
131   GLfloat h = (GLfloat) height / (GLfloat) width;
132
133   glViewport (0, 0, (GLint) width, (GLint) height);
134
135   glMatrixMode(GL_PROJECTION);
136   glLoadIdentity();
137   gluPerspective (30.0, 1/h, 1.0, 100.0);
138
139   glMatrixMode(GL_MODELVIEW);
140   glLoadIdentity();
141   gluLookAt( 0.0, 0.0, 30.0,
142              0.0, 0.0, 0.0,
143              0.0, 1.0, 0.0);
144
145   glClear(GL_COLOR_BUFFER_BIT);
146 }
147
148
149
150 static int
151 unit_torus (double ratio, int slices1, int slices2, Bool wire)
152 {
153   int i, j, k, polys = 0;
154
155   if (wire) slices1 /= 2;
156   if (wire) slices2 /= 4;
157   if (slices1 < 3) slices1 = 3;
158   if (slices2 < 3) slices2 = 3;
159
160   glFrontFace (GL_CW);
161   glBegin (wire ? GL_LINE_STRIP : GL_QUAD_STRIP);
162   for (i = 0; i < slices1; i++)
163     for (j = 0; j <= slices2; j++)
164       for (k = 0; k <= 1; k++)
165         {
166           double s = (i + k) % slices1 + 0.5;
167           double t = j % slices2;
168
169           double x = cos(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
170           double y = sin(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
171           double z = sin(s*M_PI*2/slices1);
172           glNormal3f(x, y, z);
173
174           x = (1 + ratio * cos(s*M_PI*2/slices1)) * cos(t*M_PI*2/slices2) / 2;
175           y = (1 + ratio * cos(s*M_PI*2/slices1)) * sin(t*M_PI*2/slices2) / 2;
176           z = ratio * sin(s*M_PI*2/slices1) / 2;
177           glVertex3f(x, y, z);
178           polys++;
179         }
180   glEnd();
181   return polys;
182 }
183
184
185
186
187 /* Initializes a new tentacle and stores it in the list.
188  */
189 static tentacle *
190 make_tentacle (ModeInfo *mi, int which, int total)
191 {
192   tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
193   tentacle *t = (tentacle *) calloc (1, sizeof (*t));
194   double brightness;
195   int i;
196
197   t->mi = mi;
198
199   /* position tentacles on a grid */
200   {
201     int cols = (int) (sqrt(total) + 0.5);
202     int rows = (total+cols-1) / cols;
203     int xx = which % cols;
204     int yy = which / cols;
205     double spc = arg_thickness * 0.8;
206     t->x = (cols * spc / 2) - (spc * (xx + 0.5));
207     t->y = (rows * spc / 2) - (spc * (yy + 0.5));
208     t->z = 0;
209   }
210
211   /* Brighten or darken the colors of this tentacle from the default.
212    */
213   brightness = 0.6 + frand(3.0);
214   memcpy (t->tentacle_color, tc->tentacle_color, 4 * sizeof(*t->tentacle_color));
215   memcpy (t->stripe_color,   tc->stripe_color,   4 * sizeof(*t->stripe_color));
216   memcpy (t->sucker_color,   tc->sucker_color,   4 * sizeof(*t->sucker_color));
217 # define FROB(X) \
218     t->X[0] *= brightness; if (t->X[0] > 1) t->X[0] = 1; \
219     t->X[1] *= brightness; if (t->X[1] > 1) t->X[1] = 1; \
220     t->X[2] *= brightness; if (t->X[2] > 1) t->X[2] = 1
221   FROB (tentacle_color);
222   FROB (stripe_color);
223   FROB (sucker_color);
224 # undef FROB
225
226   t->nsegments = (arg_segments) + BELLRAND(arg_segments);
227
228   t->segments = (segment *) calloc (t->nsegments+1, sizeof(*t->segments));
229   for (i = 0; i < t->nsegments; i++)
230     {
231       double spin_speed   = 0;
232       double spin_accel   = 0;
233       double wander_speed = arg_speed * (0.02 + BELLRAND(0.1));
234       t->segments[i].rot = make_rotator (spin_speed, spin_speed, spin_speed,
235                                          spin_accel, wander_speed, True);
236     }
237
238   t->segments[0].thickness = (((arg_thickness * 0.5) + 
239                                BELLRAND(arg_thickness * 0.6))
240                             / 1.0);
241
242   if (tc->tentacles_size <= tc->ntentacles)
243     {
244       tc->tentacles_size = (tc->tentacles_size * 1.2) + tc->ntentacles;
245       tc->tentacles = (tentacle **)
246         realloc (tc->tentacles, tc->tentacles_size * sizeof(*tc->tentacles));
247       if (! tc->tentacles)
248         {
249           fprintf (stderr, "%s: out of memory (%d tentacles)\n",
250                    progname, tc->tentacles_size);
251           exit (1);
252         }
253     }
254
255   tc->tentacles[tc->ntentacles++] = t;
256   return t;
257 }
258
259
260 static void
261 draw_tentacle (tentacle *t)
262 {
263   tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
264   int i;
265   Bool wire = MI_IS_WIREFRAME (t->mi);
266   XYZ ctr = { 0,0,0 };     /* current position of base of segment */
267   double cth  = 0;         /* overall orientation of current segment */
268   double cphi = 0;
269   double cth_cos  = 1, cth_sin  = 0;
270   double cphi_cos = 1, cphi_sin = 0;
271
272   XYZ *ring, *oring;       /* points around the edge (this, and previous) */
273   XYZ *norm, *onorm;       /* their normals */
274
275   /* Which portion of the radius the colored stripe takes up */
276   int indented_points = arg_slices * 0.2;
277
278   /* We do rotation way to minimize number of calls to sin/cos. */
279 # define ROT(P) do { \
280     XYZ _p = P; \
281     _p.y = ((P.y * cth_sin - P.x * cth_cos)); \
282     _p.x = ((P.y * cth_cos + P.x * cth_sin) * cphi_sin - (P.z * cphi_cos)); \
283     _p.z = ((P.y * cth_cos + P.x * cth_sin) * cphi_cos + (P.z * cphi_sin)); \
284     P = _p; \
285   } while(0)
286
287   ring  = (XYZ *) malloc (arg_slices * sizeof(*ring));
288   norm  = (XYZ *) malloc (arg_slices * sizeof(*norm));
289   oring = (XYZ *) malloc (arg_slices * sizeof(*oring));
290   onorm = (XYZ *) malloc (arg_slices * sizeof(*onorm));
291
292   if (wire)
293     glColor4fv (t->tentacle_color);
294   else
295     {
296       static const GLfloat bspec[4]  = {1.0, 1.0, 1.0, 1.0};
297       static const GLfloat bshiny    = 128.0;
298       glMaterialfv (GL_FRONT, GL_SPECULAR,            bspec);
299       glMateriali  (GL_FRONT, GL_SHININESS,           bshiny);
300     }
301
302   glPushMatrix();
303   glTranslatef (t->x, t->y, t->z);
304
305   if (debug_p)
306     {
307       if (!wire) glDisable(GL_LIGHTING);
308       glBegin(GL_LINE_LOOP);
309       for (i = 0; i < arg_slices; i++)
310         glVertex3f (arg_thickness / 2 * cos (M_PI * 2 * i / arg_slices),
311                     arg_thickness / 2 * sin (M_PI * 2 * i / arg_slices),
312                     0);
313       glEnd();
314       if (!wire) glEnable(GL_LIGHTING);
315     }
316
317   for (i = 0; i < t->nsegments; i++)
318     {
319       int j;
320       XYZ p;
321
322       for (j = 0; j < arg_slices; j++)
323         {
324           /* Construct a vertical disc at the origin, to use as the
325              base of this segment.
326            */
327           double r = t->segments[i].thickness / 2;
328           double a = M_PI * 2 * j / arg_slices;
329
330           if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
331             r *= 0.75;
332
333           ring[j].x = r * cos (a);
334           ring[j].y = 0;
335           ring[j].z = r * sin (a);
336
337           /* Then rotate the points by the angle of the current segment. */
338           ROT(ring[j]);
339
340           /* Then move the ring to the base of this segment. */
341           ring[j].x += ctr.x;
342           ring[j].y += ctr.y;
343           ring[j].z += ctr.z;
344         }
345
346
347       /* Compute the normals of the faces on this segment.  We do this
348          first so that the normals of the vertexes can be the average
349          of the normals of the faces.
350          #### Uh, except I didn't actually implement that...
351               but it would be a good idea.
352        */
353       if (i > 0)
354         for (j = 0; j <= arg_slices; j++)
355           {
356             int j0 = j     % arg_slices;
357             int j1 = (j+1) % arg_slices;
358             norm[j0] = calc_normal (oring[j0], ring[j0], ring[j1]);
359           }
360
361       /* Draw!
362        */
363
364       if (i > 0)
365         {
366           int j;
367           glFrontFace (GL_CCW);
368           glBegin (wire ? GL_LINES : smooth_p ? GL_QUAD_STRIP : GL_QUADS);
369           for (j = 0; j <= arg_slices; j++)
370             {
371               int j0 = j     % arg_slices;
372               int j1 = (j+1) % arg_slices;
373
374               if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
375                 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
376                               t->stripe_color);
377               else
378                 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
379                               t->tentacle_color);
380
381               glNormal3f (onorm[j0].x, onorm[j0].y, onorm[j0].z);
382               glVertex3f (oring[j0].x, oring[j0].y, oring[j0].z);
383               glNormal3f ( norm[j0].x,  norm[j0].y,  norm[j0].z);
384               glVertex3f ( ring[j0].x,  ring[j0].y,  ring[j0].z);
385               if (!smooth_p)
386                 {
387                   glVertex3f ( ring[j1].x,  ring[j1].y,  ring[j1].z);
388                   glVertex3f (oring[j1].x, oring[j1].y, oring[j1].z);
389                 }
390               t->mi->polygon_count++;
391             }
392           glEnd ();
393
394           if (wire)
395             {
396               glBegin (GL_LINE_LOOP);
397               for (j = 0; j < arg_slices; j++)
398                 glVertex3f (ring[j].x, ring[j].y, ring[j].z);
399               glEnd();
400             }
401
402           /* Now draw the suckers!
403            */
404           {
405             double seg_length = arg_length / t->nsegments;
406             double sucker_size = arg_thickness / 8;
407             double sucker_spacing = sucker_size * 1.5;
408             int nsuckers = seg_length / sucker_spacing;
409             double oth  = cth  - t->segments[i-1].th;
410             double ophi = cphi - t->segments[i-1].phi;
411             int k;
412
413             if (nsuckers == 0)
414               {
415                 int segs_per_sucker =
416                   (int) ((sucker_spacing / seg_length) + 0.5);
417                 nsuckers = (i % segs_per_sucker) ? 0 : 1;
418               }
419
420             glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->sucker_color);
421
422             for (k = 0; k < nsuckers; k++)
423               {
424                 double scale;
425                 XYZ p0 = ring[0];
426                 XYZ p1 = oring[0];
427                 XYZ p;
428                 p.x = p0.x + (p1.x - p0.x) * (k + 0.5) / nsuckers;
429                 p.y = p0.y + (p1.y - p0.y) * (k + 0.5) / nsuckers;
430                 p.z = p0.z + (p1.z - p0.z) * (k + 0.5) / nsuckers;
431
432                 glPushMatrix();
433                 glTranslatef (p.x, p.y, p.z);
434                 glRotatef (ophi * 180 / M_PI, 0, 1, 0);
435                 glRotatef (-oth * 180 / M_PI, 1, 0, 0);
436                 glRotatef (90, 1, 0, 0);
437
438                 { /* Not quite right: this is the slope of the outer edge
439                      if the next segment was not tilted at all...  If there
440                      is any tilt, then the angle of this wall and the 
441                      opposite wall are very different.
442                    */
443                   double slope = ((t->segments[i-1].thickness -
444                                    t->segments[i].thickness) /
445                                   t->segments[i].length);
446                   glRotatef (-45 * slope, 1, 0, 0);
447                 }
448
449                 scale = t->segments[i].thickness / arg_thickness;
450                 scale *= 0.7;
451                 glTranslatef (0, 0, -0.15 * sucker_size);
452                 glScalef (scale, scale, scale*4);
453                 {
454                   double off = sucker_size * 1.4;
455                   glPushMatrix();
456                   glTranslatef (off, 0, 0);
457                   glCallList (tc->sucker_list);
458                   t->mi->polygon_count += tc->sucker_polys;
459                   glPopMatrix();
460
461                   glPushMatrix();
462                   glTranslatef (-off, 0, 0);
463                   glCallList (tc->sucker_list);
464                   t->mi->polygon_count += tc->sucker_polys;
465                   glPopMatrix();
466                 }
467
468                 glPopMatrix();
469               }
470           }
471         }
472
473       /* Now draw the end caps.
474        */
475       if (i == 0 || i == t->nsegments-1)
476         {
477           int j;
478           GLfloat ctrz = ctr.z + ((i == 0 ? -1 : 1) *
479                                   t->segments[i].thickness / 4);
480           glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->tentacle_color);
481           glFrontFace (i == 0 ? GL_CCW : GL_CW);
482           glBegin (wire ? GL_LINES : GL_TRIANGLE_FAN);
483           glNormal3f (0, 0, (i == 0 ? -1 : 1));
484           glVertex3f (ctr.x, ctr.y, ctrz);
485           for (j = 0; j <= arg_slices; j++)
486             {
487               int jj = j % arg_slices;
488               glNormal3f (norm[jj].x, norm[jj].y, norm[jj].z);
489               glVertex3f (ring[jj].x, ring[jj].y, ring[jj].z);
490               if (wire) glVertex3f (ctr.x, ctr.y, ctrz);
491               t->mi->polygon_count++;
492             }
493           glEnd();
494         }
495
496       /* Now move to the end of this segment in preparation for the next.
497        */
498       if (i != t->nsegments-1)
499         {
500           p.x = 0;
501           p.y = t->segments[i].length;
502           p.z = 0;
503           ROT (p);
504           ctr.x += p.x;
505           ctr.y += p.y;
506           ctr.z += p.z;
507
508           /* Accumulate the current angle and rotation, to keep track of the
509              rotation of the upcoming segment.
510            */
511           cth  += t->segments[i].th;
512           cphi += t->segments[i].phi;
513
514           cth_sin  = sin (cth);
515           cth_cos  = cos (cth);
516           cphi_sin = sin (cphi);
517           cphi_cos = cos (cphi);
518
519           memcpy (oring, ring, arg_slices * sizeof(*ring));
520           memcpy (onorm, norm, arg_slices * sizeof(*norm));
521         }
522     }
523
524   glPopMatrix();
525
526   free (ring);
527   free (norm);
528   free (oring);
529   free (onorm);
530 }
531
532
533 static void
534 move_tentacle (tentacle *t)
535 {
536   /* tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)]; */
537   GLfloat len = 0;
538   double pos = 0;
539   int i, j;
540   int skip = t->nsegments * (1 - (arg_wiggliness + 0.5));
541   int tick = 0;
542   int last = 0;
543
544   for (i = 0; i < t->nsegments; i++)
545     {
546       if (++tick >= skip || i == t->nsegments-1)
547         {
548           /* randomize the motion of this segment... */
549           double x, y, z;
550           double phi_range = M_PI * 0.8 * arg_flexibility;
551           double th_range  = M_PI * 0.9 * arg_flexibility;
552           get_position (t->segments[i].rot, &x, &y, &z, True);
553           t->segments[i].phi = phi_range * (0.5 - y);
554           t->segments[i].th  = th_range  * (0.5 - z);
555           t->segments[i].length = ((0.8 + ((0.5 - x) * 0.4)) * 
556                                    (arg_length / t->nsegments));
557
558           /* ...and make the previous N segments be interpolated
559              between this one and the previous randomized one. */
560           for (j = last+1; j <= i; j++)
561             {
562               t->segments[j].phi    = (t->segments[i].phi / (i - last));
563               t->segments[j].th     = (t->segments[i].th  / (i - last));
564               t->segments[j].length = (t->segments[i].length);
565             }
566
567           tick = 0;
568           last = i;
569         }
570       len += t->segments[i].length;
571     }
572
573   /* thickness of segment is relative to current position on tentacle
574      (not just the index of the segment). */
575   for (i = 0; i < t->nsegments; i++)
576     {
577       if (i > 0)
578         {
579           double tt = (t->segments[0].thickness * (len - pos) / len);
580           if (tt < 0.001) tt = 0.001;
581           t->segments[i].thickness = tt;
582         }
583       pos += t->segments[i].length;
584     }
585 }
586
587
588
589 ENTRYPOINT Bool
590 tentacles_handle_event (ModeInfo *mi, XEvent *event)
591 {
592   tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
593
594   if (event->xany.type == ButtonPress &&
595       event->xbutton.button == Button1)
596     {
597       tc->button_down_p = True;
598       gltrackball_start (tc->trackball,
599                          event->xbutton.x, event->xbutton.y,
600                          MI_WIDTH (mi), MI_HEIGHT (mi));
601       return True;
602     }
603   else if (event->xany.type == ButtonRelease &&
604            event->xbutton.button == Button1)
605     {
606       tc->button_down_p = False;
607       return True;
608     }
609   else if (event->xany.type == ButtonPress &&
610            (event->xbutton.button == Button4 ||
611             event->xbutton.button == Button5 ||
612             event->xbutton.button == Button6 ||
613             event->xbutton.button == Button7))
614     {
615       gltrackball_mousewheel (tc->trackball, event->xbutton.button, 10,
616                               !!event->xbutton.state);
617       return True;
618     }
619   else if (event->xany.type == MotionNotify &&
620            tc->button_down_p)
621     {
622       gltrackball_track (tc->trackball,
623                          event->xmotion.x, event->xmotion.y,
624                          MI_WIDTH (mi), MI_HEIGHT (mi));
625       return True;
626     }
627
628   return False;
629 }
630
631
632 static void
633 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
634 {
635   XColor c;
636   a[3] = 1.0;  /* alpha */
637
638   if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
639     {
640       fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
641       exit (1);
642     }
643   a[0] = c.red   / 65536.0;
644   a[1] = c.green / 65536.0;
645   a[2] = c.blue  / 65536.0;
646 }
647
648
649 ENTRYPOINT void 
650 init_tentacles (ModeInfo *mi)
651 {
652   tentacles_configuration *tc;
653   int wire = MI_IS_WIREFRAME(mi);
654   int i;
655
656   if (!tcs) {
657     tcs = (tentacles_configuration *)
658       calloc (MI_NUM_SCREENS(mi), sizeof (tentacles_configuration));
659     if (!tcs) {
660       fprintf(stderr, "%s: out of memory\n", progname);
661       exit(1);
662     }
663
664     tc = &tcs[MI_SCREEN(mi)];
665   }
666
667   tc = &tcs[MI_SCREEN(mi)];
668
669   tc->glx_context = init_GL(mi);
670
671   reshape_tentacles (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
672
673   if (!wire)
674     {
675       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
676       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
677       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
678       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
679
680       glEnable(GL_LIGHTING);
681       glEnable(GL_LIGHT0);
682       glEnable(GL_DEPTH_TEST);
683       glEnable(GL_CULL_FACE);
684
685       glLightfv(GL_LIGHT0, GL_POSITION, pos);
686       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
687       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
688       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
689     }
690
691   tc->trackball = gltrackball_init ();
692
693   tc->left_p = !(random() % 5);
694
695   if (arg_segments    < 2) arg_segments    = 2;
696   if (arg_slices      < 3) arg_slices      = 3;
697   if (arg_thickness < 0.1) arg_thickness   = 0.1;
698   if (arg_wiggliness  < 0) arg_wiggliness  = 0;
699   if (arg_wiggliness  > 1) arg_wiggliness  = 1;
700   if (arg_flexibility < 0) arg_flexibility = 0;
701   if (arg_flexibility > 1) arg_flexibility = 1;
702
703   parse_color (mi, "tentacleColor", arg_color,  tc->tentacle_color);
704   parse_color (mi, "stripeColor",   arg_stripe, tc->stripe_color);
705   parse_color (mi, "suckerColor",   arg_sucker, tc->sucker_color);
706
707   for (i = 0; i < MI_COUNT(mi); i++)
708     move_tentacle (make_tentacle (mi, i, MI_COUNT(mi)));
709
710   tc->sucker_list = glGenLists (1);
711   glNewList (tc->sucker_list, GL_COMPILE);
712   { GLfloat s = arg_thickness / 5; glScalef (s, s, s); }
713   tc->sucker_polys = unit_torus (0.5, 
714                                  MIN(8,  arg_slices/6),
715                                  MIN(12, arg_slices/3), 
716                                  MI_IS_WIREFRAME(mi));
717   glEndList();
718 }
719
720
721 ENTRYPOINT void
722 draw_tentacles (ModeInfo *mi)
723 {
724   tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
725   int wire = MI_IS_WIREFRAME(mi);
726   Display *dpy = MI_DISPLAY(mi);
727   Window window = MI_WINDOW(mi);
728   int i;
729
730   if (!tc->glx_context)
731     return;
732
733   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
734
735   glShadeModel(GL_SMOOTH);
736
737   glEnable(GL_DEPTH_TEST);
738   glEnable(GL_NORMALIZE);
739   glEnable(GL_CULL_FACE);
740
741   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
742
743   glPushMatrix ();
744
745
746 # if 1
747   glScalef (3, 3, 3);
748 # else
749   glPushMatrix();
750   { GLfloat s = 8.7/1600; glScalef(s,s,s); }
751   glTranslatef(-800,-514,0);
752   if (!wire) glDisable(GL_LIGHTING);
753   glBegin(GL_LINE_LOOP);
754   glVertex3f(0,0,0);
755   glVertex3f(0,1028,0);
756   glVertex3f(1600,1028,0);
757   glVertex3f(1600,0,0);
758   glEnd();
759   if (!wire) glEnable(GL_LIGHTING);
760   glPopMatrix();
761 # endif
762
763   gltrackball_rotate (tc->trackball);
764
765   mi->polygon_count = 0;
766
767   if (debug_p)
768     {
769       if (!wire) glDisable(GL_LIGHTING);
770       glBegin(GL_LINES);
771       glVertex3f(-0.5, 0, 0); glVertex3f(0.5, 0, 0);
772       glVertex3f(0, -0.5, 0); glVertex3f(0, 0.5, 0);
773       glEnd();
774       if (!wire) glEnable(GL_LIGHTING);
775     }
776   else if (tc->left_p)
777     {
778       glRotatef ( 45, 0, 1, 0);         /* upper left */
779       glRotatef ( 45, 1, 0, 0);
780       glRotatef (-70, 0, 0, 1);
781       glTranslatef (0, -2, -4.5);
782     }
783   else
784     {
785       glRotatef (-45, 0, 1, 0);         /* upper right */
786       glRotatef ( 45, 1, 0, 0);
787       glRotatef ( 70, 0, 0, 1);
788       glTranslatef (0, -2, -4.5);
789     }
790
791   if (!tc->button_down_p)
792     for (i = 0; i < tc->ntentacles; i++)
793       move_tentacle (tc->tentacles[i]);
794
795   for (i = 0; i < tc->ntentacles; i++)
796     draw_tentacle (tc->tentacles[i]);
797
798   glPopMatrix ();
799
800   if (mi->fps_p) do_fps (mi);
801   glFinish();
802
803   glXSwapBuffers(dpy, window);
804 }
805
806 XSCREENSAVER_MODULE_2 ("SkyTentacles", skytentacles, tentacles)
807
808 #endif /* USE_GL */